From a626daa156a81e656442afb536b93f5d63712588 Mon Sep 17 00:00:00 2001 From: David RENARD Date: Sat, 9 May 2026 09:25:09 +0200 Subject: [PATCH] docs: propose vanilla Stimulus interaction model --- docs/architecture.md | 10 + ...0004-vanilla-stimulus-interaction-model.md | 348 ++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 docs/decisions/0004-vanilla-stimulus-interaction-model.md diff --git a/docs/architecture.md b/docs/architecture.md index d576809..e2ccbbe 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -40,6 +40,16 @@ The backend renders datatable HTML fragments, and the Stimulus controller update The frontend controller must not duplicate cell rendering logic in JavaScript. +### Stimulus interaction model + +The bundle uses a vanilla Stimulus controller to orchestrate datatable interactions. + +The controller is responsible for Ajax requests, loading state, error state, pagination, sorting, search and page size changes. + +It must not render business cells manually in JavaScript. Cell and row rendering remains a Twig/server-side responsibility. + +The controller receives server-rendered HTML fragments and updates the relevant DOM targets. + ## Ajax controller The Ajax controller exposes generic endpoints used by the frontend controller. diff --git a/docs/decisions/0004-vanilla-stimulus-interaction-model.md b/docs/decisions/0004-vanilla-stimulus-interaction-model.md new file mode 100644 index 0000000..ecdea0f --- /dev/null +++ b/docs/decisions/0004-vanilla-stimulus-interaction-model.md @@ -0,0 +1,348 @@ +# 0004 - Vanilla Stimulus interaction model + +## Status + +Proposed + +## Context + +The bundle must provide Ajax-powered datatables without depending on jQuery or DataTables.net. + +The rendering strategy is Twig-first: the backend renders HTML fragments and the frontend updates these fragments through a Stimulus controller. + +The Stimulus controller must remain lightweight and focused on interactions, not business rendering. + +## Decision + +The bundle will provide a vanilla Stimulus controller responsible for orchestrating datatable interactions. + +The controller will: + +- read configuration from HTML data attributes; +- submit Ajax requests with the current table state; +- update server-rendered HTML fragments; +- manage loading and error states; +- bind sorting, pagination, search and page size interactions. + +The controller will not: + +- build table rows manually from raw JSON data; +- render business cells in JavaScript; +- depend on jQuery; +- depend on DataTables.net; +- depend on Bootstrap JavaScript. + +## Controller name + +The initial controller name should be: + +```text +zhortein-datatable +``` + +Usage example: + +```html +
+ +
+``` + +## Required values + +The controller should support these Stimulus values: + +```js +static values = { + name: String, + dataUrl: String, + page: Number, + pageSize: Number, + search: String, + sort: String, + direction: String, + debounceDelay: Number, +}; +``` + +Initial defaults: + +- `page`: `1` +- `pageSize`: `25` +- `search`: empty string +- `sort`: empty string +- `direction`: `asc` +- `debounceDelay`: `300` + +## Targets + +The controller should support these targets: + +```js +static targets = [ + 'body', + 'pagination', + 'summary', + 'searchInput', + 'pageSize', + 'error', + 'loading', +]; +``` + +Expected responsibilities: + +- `body`: receives rendered table rows. +- `pagination`: receives rendered pagination HTML. +- `summary`: receives rendered count/summary text or HTML. +- `searchInput`: global search input. +- `pageSize`: page size selector. +- `error`: error container. +- `loading`: loading indicator. + +## Ajax request method + +The initial implementation should use `GET` requests. + +Reasons: + +- table state is naturally query-like; +- URLs are easier to debug; +- it supports browser/devtool inspection; +- it keeps requests simple. + +A later version may support `POST` for complex filters or large payloads. + +## Ajax request parameters + +The controller should send the following query parameters: + +```text +page +pageSize +search +sort +direction +``` + +Example: + +```text +/_zhortein/datatable/users/data?page=2&pageSize=25&search=admin&sort=e.email&direction=asc +``` + +The backend must validate and normalize these values. + +## Expected Ajax response + +The response should contain server-rendered HTML fragments: + +```json +{ + "body": "...", + "pagination": "", + "summary": "Showing 26 to 50 of 183 entries", + "page": 2, + "pageSize": 25, + "totalItems": 183, + "totalPages": 8 +} +``` + +The controller should update only the provided fragments. + +Missing optional fragments should not break the controller. + +## Loading state + +Before sending a request, the controller should: + +- mark the root element as busy; +- show loading target if present; +- hide the error target if present; +- prevent duplicate concurrent updates where possible. + +Expected markup behavior: + +```html +
+ ... +
+``` + +The controller may also add a CSS class: + +```text +is-loading +``` + +## Error state + +On Ajax failure, the controller should: + +- keep the current table content if possible; +- show a generic error message; +- expose a safe message only; +- log technical details to `console.error()` in development-friendly fashion. + +The default user-facing message should be translatable server-side when rendered in Twig. + +The frontend must not display raw exception traces. + +## Search behavior + +The global search input should trigger a refresh with debounce. + +Expected behavior: + +- user types in the search field; +- controller waits `debounceDelay`; +- page is reset to `1`; +- table refreshes. + +Example markup: + +```html + +``` + +## Sorting behavior + +Sortable headers should trigger sorting through Stimulus action parameters. + +Example markup: + +```html + +``` + +Expected behavior: + +- clicking a new field sorts ascending; +- clicking the current field toggles direction; +- page is reset to `1`; +- table refreshes. + +Multi-column sorting is out of scope for the first implementation. + +## Pagination behavior + +Pagination links/buttons should use Stimulus action parameters. + +Example markup: + +```html + +``` + +Expected behavior: + +- controller updates the current page; +- table refreshes; +- disabled buttons should not trigger requests. + +## Page size behavior + +The page size selector should trigger a refresh and reset the page to `1`. + +Example markup: + +```html + +``` + +## Progressive enhancement + +The generated HTML should remain understandable without JavaScript. + +The initial implementation may require JavaScript for Ajax navigation, but the markup should not be opaque or fully client-rendered. + +A later version may add non-JavaScript fallback links if needed. + +## Browser history + +The first implementation should not update browser history automatically. + +Reasons: + +- keep the controller simple; +- avoid unexpected back-button behavior; +- wait for real usage feedback. + +A later version may support optional URL synchronization. + +## Accessibility + +The controller and templates should preserve accessibility: + +- use real buttons for actions; +- use `aria-busy` during loading; +- preserve focus when possible; +- expose current page in pagination markup; +- avoid clickable non-interactive elements; +- keep table semantics valid. + +## Events + +The controller may dispatch custom events for extension points. + +Initial event names: + +```text +zhortein-datatable:before-load +zhortein-datatable:after-load +zhortein-datatable:error +``` + +These events should be documented when implemented. + +## Consequences + +This model keeps JavaScript small and focused. + +It preserves Symfony/Twig as the rendering authority. + +It avoids recreating DataTables.net in custom JavaScript. + +The trade-off is that Ajax responses contain HTML fragments rather than only raw data. + +For business datatables, this is acceptable and consistent with the Bootstrap-first, Symfony-first goal. + +## Follow-up tasks + +- Implement the Stimulus controller skeleton. +- Define exact data attributes in Twig templates. +- Implement Ajax endpoint returning HTML fragments. +- Add JavaScript unit tests if a test runner is introduced. +- Add functional tests for rendered attributes.