laif-ds 1.0.0-app-data-grid.65 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CHANGELOG.md +10 -249
- package/dist/agent-docs/adoption-report.json +87 -146
- package/dist/agent-docs/components/AppBottomNav.md +173 -41
- package/dist/agent-docs/components/AppCard.md +21 -25
- package/dist/agent-docs/components/AppEditor.md +13 -18
- package/dist/agent-docs/components/AppSelect.md +0 -50
- package/dist/agent-docs/components/AppSidebar.md +504 -11
- package/dist/agent-docs/components/DataTable.md +3 -34
- package/dist/agent-docs/components/DatePicker.md +7 -7
- package/dist/agent-docs/components/FileUploader.md +12 -12
- package/dist/agent-docs/components/Sidebar.md +72 -1
- package/dist/agent-docs/components-list.md +3 -45
- package/dist/agent-docs/manifest.json +72 -215
- package/dist/components/design-tokens.js +2 -202
- package/dist/components/nav-tokens.js +41 -0
- package/dist/components/ui/app-bottom-nav.js +124 -125
- package/dist/components/ui/app-select.js +222 -332
- package/dist/components/ui/app-sidebar-mobile.js +119 -104
- package/dist/components/ui/app-sidebar.js +516 -313
- package/dist/components/ui/button.js +1 -2
- package/dist/components/ui/nav-badge.js +66 -0
- package/dist/components/ui/sidebar.js +272 -207
- package/dist/components/ui/tables/data-cross-table/data-cross-table.js +6 -6
- package/dist/components/ui/tables/data-table/data-table-constants.js +16 -36
- package/dist/components/ui/tables/data-table/data-table-i18n.js +1 -135
- package/dist/css-for-template.css +0 -27
- package/dist/index.d.ts +180 -5361
- package/dist/index.js +406 -473
- package/dist/styles.css +1 -2
- package/dist/styles.v3.css +1 -1
- package/package.json +7 -25
- package/dist/agent-docs/components/AppDataGrid.md +0 -2467
- package/dist/agent-docs/components/GridFilterPanel.md +0 -112
- package/dist/components/ui/app-data-grid/app-data-grid.js +0 -851
- package/dist/components/ui/app-data-grid/engine/use-grid-column-resize.js +0 -110
- package/dist/components/ui/app-data-grid/engine/use-grid-engine.js +0 -586
- package/dist/components/ui/app-data-grid/engine/use-grid-infinite-loader.js +0 -50
- package/dist/components/ui/app-data-grid/engine/use-grid-virtualizer.js +0 -90
- package/dist/components/ui/app-data-grid/grid-aggregate.js +0 -5
- package/dist/components/ui/app-data-grid/grid-column-registry.js +0 -28
- package/dist/components/ui/app-data-grid/grid-columns.js +0 -204
- package/dist/components/ui/app-data-grid/grid-filter-compile.js +0 -33
- package/dist/components/ui/app-data-grid/grid-filter-operators.js +0 -113
- package/dist/components/ui/app-data-grid/grid-grouping.js +0 -104
- package/dist/components/ui/app-data-grid/grid-header-matrix.js +0 -112
- package/dist/components/ui/app-data-grid/grid-i18n.js +0 -207
- package/dist/components/ui/app-data-grid/grid-mount-count.js +0 -8
- package/dist/components/ui/app-data-grid/grid-reducer.js +0 -200
- package/dist/components/ui/app-data-grid/grid-result-scope.js +0 -35
- package/dist/components/ui/app-data-grid/grid-tokens.js +0 -169
- package/dist/components/ui/app-data-grid/grid-view-preset.js +0 -33
- package/dist/components/ui/app-data-grid/parts/grid-actions-row.js +0 -86
- package/dist/components/ui/app-data-grid/parts/grid-cell-text.js +0 -67
- package/dist/components/ui/app-data-grid/parts/grid-column-registry-panel.js +0 -320
- package/dist/components/ui/app-data-grid/parts/grid-density-control.js +0 -88
- package/dist/components/ui/app-data-grid/parts/grid-filter-chips.js +0 -224
- package/dist/components/ui/app-data-grid/parts/grid-filter-editor.js +0 -197
- package/dist/components/ui/app-data-grid/parts/grid-filter-panel.js +0 -386
- package/dist/components/ui/app-data-grid/parts/grid-filter-row.js +0 -273
- package/dist/components/ui/app-data-grid/parts/grid-fullscreen-toggle.js +0 -29
- package/dist/components/ui/app-data-grid/parts/grid-group-row.js +0 -135
- package/dist/components/ui/app-data-grid/parts/grid-header.js +0 -338
- package/dist/components/ui/app-data-grid/parts/grid-pagination-control.js +0 -218
- package/dist/components/ui/app-data-grid/parts/grid-row.js +0 -181
- package/dist/components/ui/app-data-grid/parts/grid-scroll-area.js +0 -186
- package/dist/components/ui/app-data-grid/parts/grid-selection-bar.js +0 -195
- package/dist/components/ui/app-data-grid/parts/grid-status-surface.js +0 -66
- package/dist/components/ui/app-data-grid/parts/grid-table.js +0 -482
- package/dist/components/ui/app-data-grid/parts/grid-toolbar-slots.js +0 -192
- package/dist/components/ui/app-data-grid/use-grid-filters.js +0 -173
- /package/dist/agent-docs/components/{TruncatedCell.md → truncated-cell.md} +0 -0
package/dist/CHANGELOG.md
CHANGED
|
@@ -5,263 +5,24 @@ All notable technical changes to the laif-ds core will be documented in this fil
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [laif-ds@1.0.1]
|
|
9
9
|
|
|
10
10
|
### 🚀 Added
|
|
11
11
|
|
|
12
|
-
- **
|
|
12
|
+
- **AppSidebar**: New `expandOnHover` prop expands the sidebar as a hover/focus overlay (page content doesn't shift) when collapsed to `collapsible="icon"`; a Pin control in the header promotes that to a permanent expansion, controllable via `pinned` / `defaultPinned` / `onPinnedChange`
|
|
13
|
+
- **AppSidebar / AppBottomNav**: `NavItem`, `NavSubItem`, `NavGroup` and `BottomNavItem` gained `badge` (`number | string | true`) and `badgeLabel` props for a count, short text or dot indicator; `AppBottomNav` also gained `menuBadge` for a dot on the Menu cell. The badge degrades per surface — a pill when the sidebar is expanded, a dot with the value in the tooltip when collapsed to icons, a superscript on `AppBottomNav`'s glyph (text degrades to a dot there) — and never aggregates child counts into a parent. `SidebarMenuBadge` is now deprecated in favor of `NavItem.badge`
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
- **AppDataGrid**: `GridDensityControl` and `GridDensityControlProps` are now **public exports** of `src/components/ui/app-data-grid/index.ts`. It is the same control the toolbar used to render, unchanged: a `role="group"` with the three i18n labels and the `sr-only` full words. It exists as public surface because `adg-c12` removed the grid's own switcher — without this export the file would have had no importer left (dead code) and every consumer wanting the switch would have hand-rolled three buttons, losing the group semantics and the labels. Mount it in `toolbar.actions` to put it back where it was, or anywhere outside the table. ⚠️ **If you also pass a fixed `viewportHeight`, derive it from the LIVE density**: `gridViewportHeight` multiplies `gridRowHeight[density]` by the row count, so a constant `"default"` beside rows rendered taller promises ten rows and fits eight, and the `<tfoot>` covers rows the paginator announces — a mistake that was not possible while the control lived inside the grid
|
|
16
|
-
- **AppDataGrid**: `AppDataGrid/Completa → ModalitaFiltri`, the scene the delivery is signed on, now turns on the mockup capabilities that were off while a prop already governed them, and **marks in a dashed orange frame every piece the library does not render** — in three declared provenances, asserted BOTH ways so a frame in the wrong category goes red rather than merely colouring wrong: `controllo` (outside `[data-app-data-grid]`: the deviations note, the filter-mode switch, the body-state switch, the chip row, the density control), `slot` (inside it, but consumer content: `toolbar.actions`, `selection.summary`) and `sonda` (the two `data-scena-*` probes, which were empty zero-height `div`s — their attributes existed and their box did not). Turned on: the **visible status row** with its megaphone (`statusRowVisible`, deviation 20 of the plan, which was `aperto` — closed by one prop, with no component change, since the wrapper was already implemented and the scene simply did not pass it); the **applied-filters chip row** (`GridFilterChips`, deviation 10 — mounted as a framed sibling because there is no slot between the toolbar and the table band, which SHOWS the deviation without closing it: whether the grid or the consumer should render it stays a product decision); and the **four body states** of the mockup's single shell — ready / empty / error with a wired «Riprova» / loading. ⚠️ **The frames carry no background and no `dark:` variant, and that is a measurement**: the first draft used `bg-orange-50/50 dark:bg-orange-950/20`, and in Tailwind v4 `dark:` is a `prefers-color-scheme` media query, not the class Storybook's theme selector toggles — with the Storybook theme on LIGHT and the OS on dark the frame received `oklab(0.266 … / 0.2)`, a dark brown wash over a `rgb(250, 250, 250)` body. Verified after the fix with the real theme switcher (page background `rgb(250,250,250)` → `rgb(10,10,10)`): one single colour triple across all frames in both themes
|
|
17
|
-
- **AppDataGrid**: New data grid, exported from `src/components/organisms/index.ts` together with `AppDataGridProps` — native `<table>` semantics with single and multi-column sorting (capped by `maxSortedColumns`), column widths with drag resize and left/right pinning over a measured sticky band, multi-level headers with a derived `id`/`headers` chain, opt-in virtualization, an infinite source with skeleton rows, client-side row grouping with per-column `aggregate` totals rendered in `<tfoot>`, density and full screen, page-scope selection with bulk actions that carry the count in their name, saved views applied in one tick through `preset`/`onPresetApplied`, and filters with two homes — `filterMode: "panel" | "row"`, default `"panel"` — fed by the one badge constructor, so the overlay inventory and the zero row produce the same state; the fifteen requirements behind it are tracked in one place, each next to the command that measures it, in the «Registro dei requisiti» section of `docs/analysis/datatable-v2/PIANO-COMPLETAMENTO.md`, and six of them carry a signed limit, in three different shapes. **Three are partial**: R6 ships the always-reachable horizontal scrollbar but no arrow controls (`grep -c 'scrollLeft' src/components/ui/app-data-grid/parts/grid-scroll-area.tsx` returns 0), R12 ships multi-level headers but not moving or grouping columns from the header, and R14 has no prop at all (open-at-first-paint is a story default, not a contract). **Two ship in a different shape than the wording asked for**: R8 as a port — `GridViewStore` plus `createInMemoryGridViewStore`, no persistence and no server adapter — and R9 as a `Collapsible` rather than a `DropdownMenu`. **And one is half-closable here by nature**: the editorial half of R3 — deciding which filters deserve to live outside the table, scene by scene — is a choice of whoever mounts the grid, so the design system makes it possible without being able to take it. The same six are listed in the «I SEI LIMITI FIRMATI» block at the top of `src/components/ui/app-data-grid/app-data-grid.tsx`
|
|
18
|
-
- **AppDataGrid**: New Storybook story `AppDataGrid/Completa` (`src/components/stories/app-data-grid-completa.stories.tsx`) replays the mockup scene with the real engine. **Fourteen leaf columns** over three header levels — twelve of data (Commessa, Ragione sociale, Referente, Settore, Stato, Avanzamento, Q1, Q2, Q3, Q4, Totale, Consegna) plus the two service columns, the checkbox column and the row menu — measured on the rendered table as `aria-colcount="14"` in both scenes, the same count the mockup makes in `COMPLETE_LEAF_COLUMNS`, where `sel` and `azioni` are leaves in their own right. Five cell renderers declared through `cell`: status badges, an initials avatar **derived** from the referent (in the mockup the initials were a field of the record), a progress bar with its percentage, `it-IT` amounts and `gg/mm/aaaa` dates — none of which the grid renders on its own, all of which are the call-site's, and all of which the previous shape of this story was missing. Group rows whose aggregates come from `aggregatedCell` and a `<tfoot>` whose row count and average come from `aggregate`; single-level grouping on the sector; the `columns` and `actions` toolbar slots («Colonne», «Esporta»); the search copy overridden per story by passing a whole `i18n` object; a selection bar with three actions and its own `summary` render; and a mute row-menu column (`header: ""` plus `headerLabel: "Azioni"`) — every piece coming from props and none from a story shell. It is split into two exports, `ModalitaFiltri` (paginated, no groups) and `Raggruppata` (grouped, no pager), because `paginated` does not exist on the type branch that accepts `groupBy` (`grep -n 'paginated?: never' src/components/ui/app-data-grid/app-data-grid.types.ts` returns 982); an on-screen note (`data-testid="nota-scostamenti"`) names what the target will not have — the mockup's «Seleziona tutte le 128 righe» link, since selection is page-scope only and promotion to the whole result set is not even writable (`GridSelectionSlots.mode` is a one-member union), the two filter forms left out by decision 9 («frase in prosa» and «domande pronte»), that split scene, and the group label that falls outside the pinned column — plus deviation 12, which is not an absence: the scene starts with zero applied filters where the mockup starts with six and a «Filtri (6)» badge, a bench choice made so that every count in the scene stays derivable from the corpus. The deviations from the mockup are tabulated one by one, each with its proposed outcome, in the «Scostamenti dal mockup» section of `docs/analysis/datatable-v2/PIANO-COMPLETAMENTO.md`: **thirty-seven rows, twenty-two `accettato`, seven `aperto`, seven closed by a task and one half-closed** — counts DERIVED from the table by a command, not typed by hand: `awk -F'|' '/^\| # \| Scostamento \|/{t=1;next} t&&!/^\|/{t=0} t&&/^\| *[0-9]+ *\|/{e=$(NF-1);gsub(/^ +| +$|`/,"",e);c[e]++;n++} END{print n; for(k in c) print k, c[k]}' docs/analysis/datatable-v2/PIANO-COMPLETAMENTO.md`
|
|
19
|
-
- **AppDataGrid**: ⚠️ **What the remounted scene does NOT close, and nothing here claims it does.** **Seven** of the **thirty-seven** tabulated deviations stay `aperto` — rows **10, 13, 14, 20, 22, 27, 37** of the table in `docs/analysis/datatable-v2/PIANO-COMPLETAMENTO.md`. ⚠️ **The numbers and the row list are DERIVED from that table by a command, never typed**, because a previous wording of this entry enumerated ten items, called them nine, put an `accettato` row among them and dropped an `aperto` one: `awk -F'|' '/^\| # \| Scostamento \|/{t=1;next} t&&!/^\|/{t=0} t&&/^\| *[0-9]+ *\|/{n=$2;gsub(/ /,"",n);e=$(NF-1);gsub(/^ +| +$|`/,"",e);tot++;if(e=="aperto")printf "%s ",n} END{printf "\\nof %d\\n",tot}' docs/analysis/datatable-v2/PIANO-COMPLETAMENTO.md`. The split is **five of the component** (13, 14, 20, 22, 27), **one of governance** (10, the chip row) and **one of toolbar composition** (37, opened by `adg-r14`: with the «N filters out of view» indicator gone the row still wraps, by **7px**). ⚠️ **The prose below describes an earlier, larger set** — rows 15, 17, 19, 24, 25 and 34 have since been closed by `adg-m1`/`adg-m2`/`adg-m3`/`adg-m6`, and the table is the authority, not this paragraph. **The component ones still open**: the group label falls in the grouping column instead of the pinned one and reads «Edilizia 78 righe» rather than «Edilizia (78)» (two rows, placement and form); «Schermo intero» has no visible label, only an `aria-label`; the search keeps the primitive's stacked geometry — label above, full-width field, no inline lens — and only its **copy** was closed; the scope figure «Righe 1-10 di 128» is not rendered, and — this is the correction that matters — it is **not** a prop the story forgot: `resultTotal`exists only on`GridClientProps` (`app-data-grid.types.ts:1088`), the branch that declares `paginated?: never`(line 982), so passing it next to`paginated`is`error TS2322`, measured with `tsc --noEmit`on the real file; the status line is`sr-only`; the selection bar's «N righe selezionate su M in pagina» is a fixed template literal that `summary`cannot replace; the two trailing skeleton rows do not exist, and chasing them with`loading`would delete every data row; the viewport is **64px shorter than its content** with three header levels (measured on the rendered scene:`scrollHeight - clientHeight`= 64 = 2 ×`designTokens.table.headerHeight`), because `gridViewportHeight`counts one header level and`GridViewportHeightInput` has no input for how many there are — the consumer-composed viewport height of Fase 9 is still a pact the grid does not keep for you; the order of the toolbar slots is fixed in JSX; and the paginated grid announces **`aria-rowcount="10"`** over 128 rows where the mockup announces 133, which the component's own source already records as a defect of its own and which the story cannot fix for the same TS2322 reason. **The one governance row** is the chip row §11.3-A, which no signed decision covers and which the mockup itself asks to renegotiate — it had disappeared from this entry entirely. **The one story row** is that the scene opens with every `aria-sort`at`none`where the mockup opens sorted by Settore then Totale; it stays open because nobody has written *why*, and the decision comes before the code. ⚠️ Two of the thirteen — the toolbar order and the missing initial sort — carry **no written reason anywhere**, and the table records that instead of inventing one. ⚠️ The filter trigger reading «Filtri» with no count is **not** in this list: it is row 18 and it is`accettato`, with its reason in the table
|
|
20
|
-
- **AppDataGrid**: ⚠️ **NO LONGER TRUE as of the AUDIT 2026-09-09 fixes below — kept here because a consumer who read it needs to know it changed.** The whole lane surface now leaves the package barrel: `src/components/organisms/index.ts` re-exports **all 114** names of `src/components/ui/app-data-grid/index.ts` (verified with the TypeScript AST, not a grep: 0 missing, 0 collisions), so every prop listed below can now be annotated and not merely passed. What follows is the original entry, left for the record — Part of the grid's surface was reachable only from `src/components/ui/app-data-grid/index.ts` and not yet from the package barrel — `grep -c '<name>' src/components/organisms/index.ts` returns 0 for `GridColumnAggregate`, `GridFilterMode`, `GridFilterType`, `GridSelectionSlots`, `GridSelectionAction`, `GridInitialOpenGroups`, the `GridFilterControl` family (`GridFilterControl`, `GridFilterControlProps`, `GridFilterControlRegistry`) and the two runtime values `createGridCustomFilterBadge` and `GRID_CUSTOM_FILTER_COLUMN_PREFIX`, so the props typed by them (`aggregate`, `filterMode`, `filterType`, `selection`, `filterControls`, `initialOpenGroups`) can be passed but not annotated by a consumer of the package
|
|
21
|
-
|
|
22
|
-
### 🗑️ Removed
|
|
23
|
-
|
|
24
|
-
- **AppDataGrid**: ⚠️ **BREAKING — the density switcher is gone from the grid, and `toolbar.density` is gone from the type.** `AppDataGrid` no longer renders any density control in any configuration: the key was **deleted** from `GridToolbarSlots` rather than moved to the opt-in family, so `toolbar={{ density: {} }}` is now `error TS2353: Object literal may only specify known properties, and 'density' does not exist in type 'GridToolbarSlots'` — measured on `tsc -p tsconfig.probe.json`, not guessed. Row height is set by the `density` prop, **default `"default"` (standard, 36px), unchanged**. ⚠️ **This RETRACTS the P6 rule for that one key**: the «always rendered» family drops from three to two (`search`, `fullscreen`), for which decision 26 still holds in full. The retired argument was «a table whose density changes only if the call-site remembered a boolean is a table whose density, in practice, never changes» — a rule about the end user's experience, imposed on every call-site; density is view configuration, and whoever mounts the table is who knows whether its rows should be tight or roomy. ⚠️ **Two consequences that are not cosmetic.** First, `changeDensity` was the ONLY caller of the `view/density` dispatch, so with the control gone **omitting the `density` prop freezes density at `"default"` for good** — the reducer slice is now written only by `applyPreset`; density is de facto always controlled. Second, `onDensityChange` had no caller left at all, which would have made it a public, documented prop the component never invokes: it is now fired on **preset application** (`onDensityChange?.(preset.view.density)`), a gap that existed before this change and was masked by the toolbar control. ⚠️ **Declared limit, not fixed here:** on a grid with `density` passed, a preset's density cannot win — the render picks the prop — so the preset _announces_ its density without imposing it; who should win is an open product decision. ⚠️ **The type probes were REWRITTEN, not disabled**, and the distinction is the point: `ProbeShellSlotsDensitaBooleano` and `ProbeShellSlotsDensitaRenderVuoto` would have stayed red — hence the gate green — but for the WRONG reason (unknown property instead of incompatible value), i.e. they would have kept printing «N probe su N» while measuring nothing about decision 26. They are replaced by one `ProbeShellSlotsDensitaChiaveInesistente`, which must use an **inline literal** because `GridToolbarSlots` has only optional keys and an extra property arriving from a typed variable stays assignable — excess-property checking applies to fresh literals only. Its negative proof was **executed**: re-adding `density?: GridToolbarAlwaysSlot` turns `scripts/check-grouping-server-unrepresentable.mjs` red on that probe by name, and reverting turns it green (16 probes / 16). The gate itself grew one field for this — the expected type name, defaulting to `"AppDataGridProps"` — because a `TS2353` names `GridToolbarSlots` instead, and the hard-coded string would have reported a falling probe as compiling
|
|
15
|
+
### 🔧 Changed
|
|
25
16
|
|
|
26
|
-
- **
|
|
17
|
+
- **AppBottomNav**: Cell height increased from 55px to 67px — icons are now `size="sm"` (20px, up from 16px) and vertical padding is `py-2.5` (up from `py-1.5`), bringing the cell further past the 44px touch-target floor
|
|
18
|
+
- **AppBottomNav**: `menuIconName` renamed to `menuIcon`, now accepting `IconName | (() => React.ReactNode)` so the Menu cell can render a custom glyph (e.g. a brand mark) in addition to a lucide icon name
|
|
27
19
|
|
|
28
20
|
### 🐛 Fixed
|
|
29
21
|
|
|
30
|
-
- **
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
filtro le righe passavano da 10 a 0 **entro 60ms**. Ora la griglia tiene **due slice**: quella **viva**, che
|
|
35
|
-
gli input mostrano (la searchbar è controllata da `state.query.search`, gli editor leggono
|
|
36
|
-
`state.query.filters`), e quella **confermata**, che filtra le righe e viene emessa — commit ed emissione
|
|
37
|
-
sono **lo stesso evento**, così la tabella comincia a filtrare con lo stesso scatto di query che il consumer
|
|
38
|
-
riceve. Misurato dopo: sei battute lasciano il conteggio righe **fermo** (un solo valore distinto su sei
|
|
39
|
-
campioni) e l'input mostra già il testo intero; alla chiusura della finestra le righe si muovono una volta.
|
|
40
|
-
⚠️ **TanStack riceve lo scatto INTERO** — ordinamento, filtro globale e paginazione insieme — perché
|
|
41
|
-
`query/search` e `query/filters` azzerano `pageIndex` e un misto mostrerebbe la pagina 0 dell'insieme non
|
|
42
|
-
filtrato per un frame. ⚠️ **NUOVA PROP `debounceTime` SUL RAMO BASE** (default 200, `0` disattiva), perché
|
|
43
|
-
`serverConfig.serverDebounceTime` vive sul solo ramo server e una griglia client ora ne ha bisogno; con
|
|
44
|
-
entrambe vince `debounceTime`. ⚠️ **`useGridFilters` ESPONE `committedBadges`**, additivo: si passa `badges`
|
|
45
|
-
alla griglia (gli editor devono restare vivi) e si filtra con `committedBadges`. Chi resta su `badges` non
|
|
46
|
-
si rompe — semplicemente non ottiene il differimento, ed è il modo di fallire che è stato **scelto**: rendere
|
|
47
|
-
`badges` il valore confermato avrebbe dato il differimento a tutti senza toccare una riga, ma avrebbe fatto
|
|
48
|
-
**scattare la digitazione in produzione** a chi non passasse la bozza agli editor. ⚠️ **E DENTRO IL HOOK LA
|
|
49
|
-
DIVISIONE È PER GESTO, non per nome del mutatore** — `updateBadge` (un valore che si digita o si trascina) è
|
|
50
|
-
continuo, `addBadge` è **discreto**, perché è anche il modo in cui un badge nasce da uno switch, da un
|
|
51
|
-
`AppSelect` o da un chip: la prima stesura lo aveva classificato continuo e, misurato, lo switch di
|
|
52
|
-
`FiltriSoloFuori` non annunciava più il proprio filtro entro il frame. Il prezzo dichiarato è che un filtro
|
|
53
|
-
di **testo nuovo** conferma sul primo carattere e coalesce il resto — due conferme per parola invece di una;
|
|
54
|
-
la ricerca globale, che non passa dal hook, resta a una. **Quattro spec riscritte** una per una sul contratto
|
|
55
|
-
nuovo, nessuna allentata (`filters-lifted` — che dichiarava a parole «il debounce è affare del consumer» —,
|
|
56
|
-
`riga-zero`, `completa/FiltriSoloFuori` e `gruppi-iniziali`, dove «una battuta, una emissione» diventa un
|
|
57
|
-
delta esatto di 1 e la prova sulla risemina si affila), più **due story nuove** che misurano il caso client
|
|
58
|
-
e la sua via d'uscita
|
|
59
|
-
- **AppDataGrid — ⚠️ CAMBIO DI COMPORTAMENTO PER OGNI CONSUMER IN MODALITÀ SERVER, anche chi non passa
|
|
60
|
-
niente.** `serverConfig.serverDebounceTime` **è implementata**, con **default 200ms**, e le due azioni
|
|
61
|
-
continue — `query/search` e `query/filters` — vengono **coalescite**: una parola di sei lettere nella
|
|
62
|
-
searchbar è ora **una** chiamata a `onQueryChange` invece di sei, col payload finale. Fino a oggi quel
|
|
63
|
-
campo era dichiarato nel tipo, il suo TSDoc diceva «NON IMPLEMENTATO» e **nessuna riga lo leggeva**: chi lo
|
|
64
|
-
passava otteneva silenziosamente niente. Misurato nel browser prima e dopo, non dedotto: sei battute
|
|
65
|
-
ravvicinate danno `data-emissions` fermo durante la raffica e **+1** allo scadere della finestra, mentre con
|
|
66
|
-
`serverDebounceTime: 0` — che è lo stesso ramo sincrono di prima — le stesse sei battute danno **+6**.
|
|
67
|
-
⚠️ **SOLO L'EMISSIONE È DIFFERITA, MAI LO STATO**: la tabella si aggiorna sulla battuta (la searchbar è
|
|
68
|
-
controllata da `state.query.search`, e in modalità client il filtro locale resta istantaneo), quindi il
|
|
69
|
-
campo compra meno richieste e non una tabella più lenta — misurato leggendo il valore dell'`<input>` nello
|
|
70
|
-
stesso istante in cui il contatore è ancora a zero. ⚠️ **LE AZIONI DISCRETE EMETTONO SUBITO E ANNULLANO LA
|
|
71
|
-
COALESCENZA IN SOSPESO** invece di accodarsi (ordinamento, pagina, dimensione pagina, load-more, preset):
|
|
72
|
-
altrimenti un gesto metterebbe **due** richieste in volo, che è il difetto che il debounce dovrebbe ridurre
|
|
73
|
-
— misurato strumentando `setTimeout`/`clearTimeout`, il timer della battuta risulta cancellato e mai
|
|
74
|
-
scaduto e il contatore sale di uno al click, portando **dentro** la ricerca battuta nel frattempo.
|
|
75
|
-
⚠️ **RITARDO ZERO SENZA EMETTITORE**: il ramo client dichiara `onQueryChange?: never`, quindi in client non
|
|
76
|
-
c'è niente da coalescere e il numero di emissioni per gesto **non cambia** (misurato: tre battute, tre
|
|
77
|
-
emissioni immediate). ⚠️ **DUE LIMITI DICHIARATI**: coalescere riduce le richieste ma **non ne sequenzia le
|
|
78
|
-
risposte**, che restano affare del consumer; e con i filtri **SOLLEVATI** il canale è `onFiltersChange`, la
|
|
79
|
-
callback del consumer chiamata in modo sincrono dentro l'azione, che **non** passa da `applyAndEmit` e
|
|
80
|
-
quindi resta una chiamata per tasto. ⚠️ **E LA PROP È OMONIMA MA NON GEMELLA** di quella del `DataTable`
|
|
81
|
-
congelato, dove (default 750) ritarda solo la comparsa dell'overlay di caricamento e non coalesce nessuna
|
|
82
|
-
richiesta — `DataTable.md` afferma il contrario ed è falso su quel componente. **Cinque story nuove** in
|
|
83
|
-
`AppDataGrid/Emettitore (P2)`: la searchbar, il default, l'azione discreta, `0`, e la **riga dei filtri** —
|
|
84
|
-
quest'ultima perché ricerca e filtri sono due rami diversi di `isContinuousQueryAction` e una correzione
|
|
85
|
-
che ne coprisse uno solo resterebbe verde su tutto il resto. La falsificazione è stata eseguita sul regime
|
|
86
|
-
a ritardo zero, che è byte-per-byte il ramo di prima: là le due asserzioni discriminanti leggono 6 invece
|
|
87
|
-
di 0 e 1
|
|
88
|
-
- **AppDataGrid (story, non il componente)**: nella scena `AppDataGrid/Completa → DueCaricamenti` il server
|
|
89
|
-
finto **risponde dopo mezzo secondo** invece che nello stesso commit, quindi il caricamento si può
|
|
90
|
-
finalmente guardare. ⚠️ **Nessuna riga del componente cambia**: è il banco della story. Il difetto era che
|
|
91
|
-
la scena aveva due regimi e in **nessuno dei due** un caricamento durava un tempo osservabile — con lo
|
|
92
|
-
switch «Blocca le risposte» attivo non finisce mai, e con lo switch spento rispondeva **sincrono**
|
|
93
|
-
(`setCaricaInfinita(true)` e la consegna nello stesso gesto), quindi gli scheletri nascevano e morivano
|
|
94
|
-
nello stesso commit. Misurato nel browser sulla story viva, non dedotto dalla costante: **615ms** di
|
|
95
|
-
scheletri sulla griglia infinita (dieci scheletri accodati, righe 40 → 50) e **514ms** sulla paginata (dieci
|
|
96
|
-
scheletri, righe a **0** durante l'attesa — la semantica firmata «accoda contro sostituisce» resta intatta).
|
|
97
|
-
Congelare a metà volo **annulla** la risposta in corso invece di lasciarla arrivare a switch chiuso
|
|
98
|
-
(misurato: scheletri ancora a schermo dopo 1500ms, cioè tre volte il ritardo), e nessun timer sopravvive
|
|
99
|
-
allo smontaggio (misurato strumentando `setTimeout`/`clearTimeout` e facendo cambiare story a Storybook nel
|
|
100
|
-
medesimo documento: il timer pendente risulta cancellato e mai scaduto). ⚠️ **REVOCA metà di una frase
|
|
101
|
-
firmata nel docblock della scena** — «non è un `delayMs`: un ritardo rende la play una gara coi tempi,
|
|
102
|
-
mentre un server che non risponde è uno stato stabile che si misura». La seconda metà vale ancora, ed è il
|
|
103
|
-
motivo per cui il congelamento resta e per cui le due misure firmate si prendono tutte in quel regime; la
|
|
104
|
-
prima era una conclusione troppo larga. Il ritardo attraversa la **sola** fase 3 della play (lo sblocco),
|
|
105
|
-
dove le attese portano ora un `timeout` dichiarato, derivato dalla costante con un margine di 6×, invece
|
|
106
|
-
del default di `waitFor`
|
|
107
|
-
- **AppDataGrid**: con più di una riga di intestazione, **fra le righe dell'header non passa più un pixel
|
|
108
|
-
del corpo che scorre**. Segnalato con uno screenshot e riprodotto prima di essere corretto. Le righe di
|
|
109
|
-
header intermedie portavano `border-d-border border-b` sulla `<tr>`: sotto `border-collapse: collapse`
|
|
110
|
-
quel bordo viene collassato nella griglia dei bordi della **tabella** — che non è sticky — quindi scorre
|
|
111
|
-
via col corpo, e la striscia da 1px che occupava non è coperta da niente, né dalla riga sopra, né da
|
|
112
|
-
quella sotto, né dal fondo del `<thead>`. ⚠️ **Misurato, non dedotto** (Chrome, `devicePixelRatio` 2,
|
|
113
|
-
scena `AppDataGrid/Header multilivello (P5) → Manuale`, `scrollTop: 137.5`, corpo dipinto `#ff0000`,
|
|
114
|
-
lettura del pixel sul PNG del compositor): **670 campioni rossi su 888 a ciascuno dei due confini interni
|
|
115
|
-
del `<thead>`** e zero dentro le righe; dopo il fix, **zero in tutto il blocco**. Un fondo sul `<thead>`
|
|
116
|
-
non è il rimedio, ed è misurato anche questo: con `thead { background: #00ff00 !important }` il verde non
|
|
117
|
-
appare **mai** al confine, che resta rosso — il fondo di un row-group non copre quella striscia. Il filo
|
|
118
|
-
fra due livelli è ora `box-shadow: inset 0 -1px 0 0 var(--d-border)` (`gridHeaderRowSeparatorShadow`), la
|
|
119
|
-
stessa forma con cui la Fase 2 aveva già risolto la separazione header→corpo e quella del footer, e vive
|
|
120
|
-
in **due** posti per necessità: sulla `<tr>`, l'unico elemento che si estende su tutta la larghezza della
|
|
121
|
-
tabella colonna fantasma inclusa, e — composto attraverso `gridPinnedOffsetStyle`, mai come una seconda
|
|
122
|
-
dichiarazione — sulle celle **opache** di quelle righe, perché il `bg-d-background` di una cella pinnata
|
|
123
|
-
dipinge sopra l'ombra della riga (misurato: con la sola ombra della `<tr>` il filo correva da css x 73
|
|
124
|
-
invece che da 25). ⚠️ **Effetto collaterale visibile nel contratto delle sonde**: una cella-etichetta di
|
|
125
|
-
gruppo pinnata di una riga intermedia porta ora **un `box-shadow` layer in più** (4 invece di 3 a
|
|
126
|
-
sinistra, 3 invece di 2 a destra), con la geometria nuova `0px -1px 0px 0px inset` — le due spec che
|
|
127
|
-
contano gli strati sono state aggiornate nominando il quarto, non allentando il conteggio. ⚠️ **E chiude
|
|
128
|
-
una domanda che il repo dichiarava aperta per iscritto** (`parts/grid-filter-row.tsx`): un `box-shadow` su
|
|
129
|
-
una `<tr>` sotto `border-collapse` **si rende**. Story di regressione:
|
|
130
|
-
`AppDataGrid/Header multilivello (P5) → Fra due righe di header non passa il corpo che scorre`, i cui tre
|
|
131
|
-
predicati sono stati falsificati contro il regime pre-correzione (bordi `1px`, ombra di riga `none`, e le
|
|
132
|
-
due celle-segnaposto delle spunte senza filo)
|
|
133
|
-
|
|
134
|
-
> **AUDIT 2026-09-09.** Le voci di questo blocco chiudono i rilievi confermati dell'audit di
|
|
135
|
-
> `AppDataGrid` (`docs/analysis/datatable-v2/AUDIT-BUG-2026-09-09.md`: 8 corsie di lettura, 4
|
|
136
|
-
> verificatori avversari, 47 rilievi confermati su 53). ⚠️ **Il contesto che le rende leggibili:** la
|
|
137
|
-
> suite era **verde** prima dell'audit — 730 test su 102 file — quindi nessuno di questi difetti era
|
|
138
|
-
> una regressione recente: erano zone che nessuna asserzione guardava. Dopo: **765 test su 110 file**
|
|
139
|
-
> (766 da quando la voce qui sopra ha aggiunto la sua story di regressione), e ogni fix ha una story che
|
|
140
|
-
> cade se il fix viene revocato.
|
|
141
|
-
|
|
142
|
-
- **AppDataGrid — ⚠️ BREAKING per chi confronta il payload carattere per carattere.** Il valore di un
|
|
143
|
-
filtro **numerico** parte come `number` e non più come `string` (`{importo: {operator: "gt", value: 100}}`,
|
|
144
|
-
non `"100"`), e il valore di un filtro **data** è composto dai getter LOCALI — `"YYYY-MM-DD"` per
|
|
145
|
-
`date`, `"YYYY-MM-DDTHH:mm:ss"` per `datetime` — invece di `toISOString()`: nessuno `Z`, nessun
|
|
146
|
-
istante UTC.
|
|
147
|
-
⚠️ **DUE PRECISAZIONI, aggiunte dopo la verifica avversaria perché la prima stesura di questa voce
|
|
148
|
-
prometteva più di quanto consegna.** (1) **Il numero vale per l'editor SINGOLO, non per il compositore
|
|
149
|
-
«da…a…»**: `GridFilterRangeSide.value` è tipata `string` in `app-data-grid.types.ts`, quindi un
|
|
150
|
-
intervallo numerico spedisce ancora due stringhe. Sono due forme dello stesso valore nello stesso
|
|
151
|
-
modulo, ed è un debito aperto: allargare quel tipo cambia il contratto di `onApply`, quindi non è stato
|
|
152
|
-
deciso qui. (2) **La forma `datetime` NON è la parità letterale con la tabella congelata**, che appende
|
|
153
|
-
sei decimali — `const microseconds = "000000"` in
|
|
154
|
-
`data-table/components/data-table-filter-inputs.tsx`. La griglia emette `"YYYY-MM-DDTHH:mm:ss"` senza
|
|
155
|
-
di essi. Il difetto corretto è il **fuso** — non c'è più uno `Z` né un giorno sbagliato — non la
|
|
156
|
-
coincidenza carattere per carattere, e un backend che facesse parsing rigido dei microsecondi va
|
|
157
|
-
verificato. Erano entrambi scarti dalla parità col payload della `DataTable` congelata che il
|
|
158
|
-
documento promette. Nel verso opposto, un valore di sola data nella forma che la congelata produce
|
|
159
|
-
(`"2026-09-09"`) viene riletto come mezzanotte **locale** e non più UTC, così il picker non mostra
|
|
160
|
-
più il giorno prima. Il campo numerico svuotato emette `undefined` e non `0`: `Number("")` avrebbe
|
|
161
|
-
reso una cancellazione un filtro «maggiore di 0» che nessuno ha scritto
|
|
162
|
-
- **AppDataGrid**: il chip di un filtro **booleano** porta il valore tradotto — «Chiusa Uguale a: Vero»
|
|
163
|
-
/ «: Falso» — dove prima «è vero» perdeva il valore (il chip era testualmente indistinguibile da un
|
|
164
|
-
badge senza valore) e «è falso» stampava il letterale non tradotto. Gli operatori senza valore si
|
|
165
|
-
riconoscono ora dall'**operatore** (`GRID_VALUELESS_OPERATORS`, la stessa sorgente che usa
|
|
166
|
-
`grid-filter-compile`) e non dal valore, quindi «è vuoto» resta nudo
|
|
167
|
-
- **AppDataGrid**: l'editor a lista non fa più cadere il select sul segnaposto quando il valore del
|
|
168
|
-
badge non è più fra le opzioni della colonna: rende un'`<option>` sintetica marcata
|
|
169
|
-
`data-grid-filter-editor-unknown` — **nuovo aggancio DOM** — così un filtro attivo resta visibile
|
|
170
|
-
invece di sembrare vuoto
|
|
171
|
-
- **AppDataGrid**: la **sorgente infinita** non si congela più quando un filtro viene applicato e poi
|
|
172
|
-
rimosso: la chiave della guardia si azzera a ogni ripartenza, dove prima una firma che TORNAVA a un
|
|
173
|
-
valore già consumato lasciava la lista ferma per sempre. E una griglia infinita montata **senza box
|
|
174
|
-
di layout** (`display:none`, un pannello chiuso) non incatena più l'intera sorgente credendosi a
|
|
175
|
-
fondo lista: non chiede nessuna pagina e la chiede all'apertura del pannello. ⚠️ Conseguenza per il
|
|
176
|
-
consumer: la prima pagina di una griglia dentro un pannello chiuso arriva **all'apertura**, non al
|
|
177
|
-
montaggio. Il ritorno della vista in cima dopo un cambio di query non è più confinato alla modalità
|
|
178
|
-
infinita: vale per ogni griglia, paginata compresa
|
|
179
|
-
- **AppDataGrid**: il **raggruppamento** su una colonna con valori assenti non collassa più i livelli.
|
|
180
|
-
`keyOf(null)` vale `""`, e la guardia del separatore di percorso scattava una seconda volta al
|
|
181
|
-
livello successivo **sostituendo** il percorso invece di concatenarlo: due gruppi diversi si
|
|
182
|
-
fondevano, il conteggio raddoppiava e l'aggregato sommava la stessa riga N volte — un numero
|
|
183
|
-
sbagliato mostrato all'utente. Un gruppo con chiave assente si rende ora con «—» come etichetta
|
|
184
|
-
(la sua chiave resta la stringa vuota), e `initialOpenGroups` riconosce un percorso il cui antenato
|
|
185
|
-
ha la chiave assente. ⚠️ **Limite che resta aperto e va dichiarato:** `null`, `undefined` e la
|
|
186
|
-
stringa vuota condividono la chiave `""`, quindi tre assenze semanticamente diverse confluiscono in
|
|
187
|
-
un solo gruppo. Con `groupLabelPlacement="first-column"` la prima colonna non perde più il proprio
|
|
188
|
-
`aggregatedCell`
|
|
189
|
-
- **AppDataGrid**: in modalità infinita l'annuncio «Altre righe caricate» era diventato **permanente**
|
|
190
|
-
dopo il primo caricamento, coprendo le quattro clausole della riga di stato (selezione, conteggio,
|
|
191
|
-
ordinamento, filtri) nella regione live e — con `statusRowVisible` — anche a schermo. Ora è uno
|
|
192
|
-
scatto: torna alla riga di stato al primo cambio di stato dopo l'atterraggio della pagina
|
|
193
|
-
- **AppDataGrid**: a portata di selezione **promossa** la barra non sparisce più cambiando pagina, così
|
|
194
|
-
il ritorno alla pagina e le azioni di massa restano raggiungibili; e la conferma di un'azione
|
|
195
|
-
distruttiva non sopravvive più allo svuotamento della selezione per riarmarsi alla spunta successiva
|
|
196
|
-
(prima solo un array `selection.actions` RICOSTRUITO a ogni render la annullava: con `useMemo` o una
|
|
197
|
-
costante di modulo restava armata). ⚠️ Il contratto «a selezione vuota la barra non si rende
|
|
198
|
-
affatto» ha ora **una** eccezione dichiarata: con la promozione offerta e la portata `"all"` viva la
|
|
199
|
-
barra resta resa a zero spunte, perché è l'unica superficie che governa quella portata — quindi
|
|
200
|
-
`[data-grid-selection-count]` può valere `0`
|
|
201
|
-
- **AppDataGrid**: `readGridViewPreset` rifiuta un payload v1 **troncato** con
|
|
202
|
-
`GridViewPresetVersionError` invece di validare solo `version` e lasciare esplodere il motore su
|
|
203
|
-
`query.sorting.map` dentro un effetto — cioè una schermata bianca invece di un errore dichiarato
|
|
204
|
-
- **AppDataGrid (a11y)**: una colonna con `header` vuoto e senza `headerLabel` prende il proprio `id`
|
|
205
|
-
come nome accessibile. Prima l'`aria-label` vuoto veniva **saltato** da accname e il nome del
|
|
206
|
-
`columnheader` si ricomponeva dai contenuti del `<th>`, **larghezza della maniglia di resize
|
|
207
|
-
compresa**: il nome accessibile della colonna era un numero di pixel
|
|
208
|
-
- **AppSelect (a11y, WCAG 4.1.2)**: il trigger `role="combobox"` senza `label` ricade sul proprio
|
|
209
|
-
`placeholder` come nome accessibile. La catena è `aria-labelledby` → `aria-label` → `placeholder`,
|
|
210
|
-
**mai il valore corrente**. ⚠️ Perché conta oltre `AppSelect`: i **dieci** `<AppSelect>` che la
|
|
211
|
-
`DataTable` congelata rende non passano né `label` né prop ARIA, e dopo il passaggio del trigger a
|
|
212
|
-
`role="combobox"` + `tabIndex` erano diventati dieci combobox focalizzabili **senza alcun nome**, in
|
|
213
|
-
diciotto repo consumer. La cartella congelata non è modificabile (`check-datatable-freeze.mjs`),
|
|
214
|
-
quindi il ripiego nel primitivo condiviso è l'unico punto raggiungibile e li copre tutti e dieci.
|
|
215
|
-
⚠️ **Chi asseriva `aria-label` assente in quella forma va aggiornato** (è il caso della story
|
|
216
|
-
`SenzaEtichettaNomeAccessibile`, misurata rossa e corretta)
|
|
217
|
-
- **AppDataGrid**: `rowMenu` che ritorna un elenco **vuoto** non rende più il grilletto «⋮», come il
|
|
218
|
-
contratto dichiarato nel tipo diceva già
|
|
219
|
-
- **AppDataGrid**: `initialState.pagination.pageIndex` è **onorato**. `GridPagination` lo dichiara
|
|
220
|
-
obbligatorio, quindi ogni call-site è costretto a scriverlo, e la griglia lo scartava montando
|
|
221
|
-
sempre a pagina 1: il paginatore stampava «1 / N» sulla terza pagina di dati. E il **termine di
|
|
222
|
-
ricerca iniziale** non si perde più: il motore leggeva `initialState.searchbarFilter` al primo
|
|
223
|
-
livello, mentre il builder e la tabella congelata lo scrivono dentro `initialState.filters`
|
|
224
|
-
- **AppDataGrid**: `onFiltersChange` e l'incremento di `emissions` escono ora **fuori** dall'updater
|
|
225
|
-
di `setState`, così il contratto osservabile è «una mutazione = un annuncio» anche sotto
|
|
226
|
-
`StrictMode`, dove prima era doppio
|
|
227
|
-
|
|
228
|
-
### 🐛 Fixed — quello che l'audit ha trovato e che NON è chiuso
|
|
229
|
-
|
|
230
|
-
- **AppDataGrid**: ⚠️ **`serverConfig.serverDebounceTime` è dichiarato nel tipo, promesso dal proprio
|
|
231
|
-
TSDoc («Millisecondi di attesa prima di emettere») e non letto da nessuna riga di codice**:
|
|
232
|
-
l'emettitore è sincrono, quindi sei battute in una searchbar sono sei richieste HTTP senza garanzia
|
|
233
|
-
d'ordine. Non chiuso qui perché le due strade sono decisioni: implementarlo nell'emettitore cambia
|
|
234
|
-
il contratto di `onQueryChange`, e togliere il campo dal tipo è un breaking change per diciotto
|
|
235
|
-
repo. Oggi il debounce lo deve fare il consumer
|
|
236
|
-
- **AppDataGrid**: ⚠️ **con i filtri SOLLEVATI la prima pagina della lista nuova è chiesta col
|
|
237
|
-
contatore della lista vecchia** (misurato: `pageIndex: 3` per un insieme in cui nessuna riga è stata
|
|
238
|
-
consegnata), quindi un consumer che deriva `offset = pageIndex * pageSize` salta le prime pagine del
|
|
239
|
-
risultato filtrato. **Tentato e revocato**, e la ragione va letta prima di ritentare: azzerare
|
|
240
|
-
`requestCount` in `effectiveQuery` fa andare rossa `FiltriSollevatiConSorgenteInfinita` con
|
|
241
|
-
«expected 30 to be 60» — una scena verde nella baseline il cui harness dichiara il disegno opposto,
|
|
242
|
-
cioè che la ripartenza la fa il **consumer** in `onFiltersChange` e la griglia deve poi emettere un
|
|
243
|
-
indice che **avanza**. I due contratti sono incompatibili e la griglia non può distinguerli: chi
|
|
244
|
-
possiede il segnale di ripartenza è una decisione di prodotto. Scritta per intero sul commento di
|
|
245
|
-
`effectiveQuery` in `engine/use-grid-engine.ts`, e inchiodata dalla story
|
|
246
|
-
`DEBITO motore-1 — col chip la pagina chiesta è quella stantia, non la zero`
|
|
247
|
-
- **AppDataGrid**: ⚠️ con `groupBy` + `resultTotal` gli `aria-rowindex` **sfondano** `aria-rowcount`
|
|
248
|
-
(misurato: indici 1..6 contro un totale di 3), perché il conteggio torna a contare le sole righe di
|
|
249
|
-
dati mentre gli indici contano anche le righe di gruppo. Il rimedio è in `app-data-grid.tsx`
|
|
250
|
-
- **AppDataGrid**: ⚠️ tre rilievi in `parts/grid-filter-panel.tsx` restano **non corretti per scelta**:
|
|
251
|
-
il file è nel perimetro congelato e il pannello è deprecato e non montato da nessuna story —
|
|
252
|
-
`adg-r18` ha già registrato che «la sua superficie non va migrata, va lasciata morire»
|
|
253
|
-
- **AppDataGrid**: ⚠️ `check-datatable-shared-surface` resta **rosso**, e non è chiudibile da codice:
|
|
254
|
-
il gate confronta il merge-base con l'albero di lavoro, quindi il rimedio che nomina — separare la
|
|
255
|
-
corsia dai primitivi condivisi — richiede **git**, cioè due commit o due PR
|
|
256
|
-
|
|
257
|
-
### 🔧 Changed
|
|
258
|
-
|
|
259
|
-
- **AppDataGrid**: The **25 «intermediate» story files** — every one except the three showcase files (`AppDataGrid/Mockup`, `AppDataGrid/Completa`, `AppDataGrid/Confronto`) — moved from `title: "AppDataGrid/<name>"` to `title: "CI-CD/AppDataGrid/<name>"`, so the regression specs collect under one `CI-CD` sidebar node and the showcase keeps `AppDataGrid/` to itself. ⚠️ **Each one also pins `id:` in its own meta, and that is the whole point of the change being safe.** A CSF component id is DERIVED from the title (`toId`), so the retitle alone would have moved the URL of every story in those files — and those URLs are cited OUTSIDE the source: 12 files under `.laif/tasks/to-validate` (tasks awaiting human signature, naming the page to open for a criterion not yet signed) and 11 append-only run logs, which are not rewritable, so any id change would leave permanently dead links in the audit trail. `id?: ComponentId` is the documented clause for exactly this (`node_modules/storybook/dist/csf/index.d.ts:876`, «used for URLs. By default is inferred from sanitizing the title»); Storybook in use is 10.0.8. ⚠️ **MEASURED on Storybook's own `/index.json`, before and after — not deduced from the source**: `appdatagrid-*` stories 319 → **319**; ids that disappeared (broken URLs) **0**; unexpected new ids **0**; stories whose title changed at an **unchanged id** **305** (the remaining 14 are the three showcase files); total index 725 → **725**. Citations to intermediate specs that no longer resolve: **2 before, the same 2 after** — `atomo-di-filtro-c7--inventario` and `selezione-c6--barra-di-selezione` were ALREADY dangling before the rename, so the retitle broke nothing. `npm run spec` stays **102 files / 725 tests, 0 failed** (it filters by file path, not by title) and all **14** `scripts/check-*.mjs` gates exit 0 (they read paths and sources; none reads a title). ⚠️ **The id values were COPIED from the index, never retyped**: one of the twenty-five is `appdatagrid-densità-e-schermo-intero-p6`, accent included — retyping it would have normalised the accent and silently broken a URL that works today. ⚠️ **Accepted consequence: title and URL now diverge for good** — a future title rename will silently keep the pinned id, which is why every one of the 25 metas carries the reason inline and points at the «Il nodo CI-CD» section of `docs/analysis/datatable-v2/PIANO-COMPLETAMENTO.md`. ⚠️ **The seven `Manuale` stories move too, and it is stated rather than hidden**: they are the deliberately play-less bench for human task validation, and in CSF a story cannot override its meta's title, so keeping them under `AppDataGrid/` would have meant splitting files — not done. Their URLs are unchanged, so every citation by id still resolves; only their sidebar location moved
|
|
260
|
-
- **AppDataGrid**: ⚠️ **What the reorganisation deliberately did NOT do, after an audit that measured it.** All 25 intermediate files were audited one by one (25 recon passes, then three adversarial skeptics on every proposal to cut) and **none is deletable**: 23 came back «keep» outright and the 2 «reduce» proposals were refuted **3 votes out of 3**. The reason is structural and measurable — 24 of the 25 are the only place in the repo where at least one public prop, DOM selector or exported function is exercised (`infinite`, `filterControls`, `validateGridColumnTree`, `readGridViewPreset`, `th[scope="row"]`, the `data-grid-column-registry-*` family) — and, decisively, **deleting a story turns nothing red**: `npm run check` does not run the plays, and `spec` is chained to neither `check` nor `pr-checks.yml` for two reasons the repo itself already states in `package.json` (`_comment:spec`). The lane has also already paid for one pruning (`dtv2-f1-potatura-e-spacchettamento-story` deleted ten stories and with them the source three later tasks needed). Still open and NOT addressed here: the «show/hide specs» switch (`tags: ["spec"]` plus a `sidebar.filters` filter in `.storybook/manager.ts`), which must go in a separate PR because `scripts/check-datatable-freeze.mjs` scopes lane purity on `NEW_TABLE_PATHS`, which covers the story folder but not `.storybook/`
|
|
261
|
-
- **DataTable**: Deprecated **in the documentation only**, in favour of `AppDataGrid` — the entry is in `src/components/documents/components-list.md` under «Deprecated Components» and nothing is removed: `DataTable` is still exported and every existing call-site keeps compiling, `grep -rn '@deprecated' src/components/ui/tables/data-table/ | wc -l` returns 0 on purpose (a JSDoc tag would trip the consumer lint rules that ban deprecated APIs), the two components import nothing from each other and can be mounted on the same page, and `scripts/codemod/rename-map.json` records name by name which old export maps to which new one and which parts of the new surface have no old equivalent
|
|
262
|
-
- **AppDataGrid**: `AppDataGrid.mockup.stories.tsx` stays published in Storybook as `AppDataGrid/Mockup`, frozen: it is the reference the delivery is signed against, and it is not public API — what leaves it is the stale «this mockup is not a component» banner (verifiable by difference: `grep -c 'non è un componente' src/components/stories/AppDataGrid.mockup.stories.tsx` went 1 → 0), not the story, because the delivery is signed by opening the two target scenes — `?path=/story/appdatagrid-completa--modalita-filtri` and `?path=/story/appdatagrid-completa--raggruppata` — next to `?path=/story/appdatagrid-mockup--modalita-filtri` from the Storybook manager, both of them, since groups and pager cannot be represented together and the mockup's single scene therefore has two counterparts
|
|
263
|
-
- **AppDataGrid**: ⚠️ **Known limitation, open** — the two-value operator composer opens from the «Modifica valore…» menu item but **not** from the operator radio path of the filter inventory: the closing `DropdownMenu`'s `DismissableLayer` swallows the `Popover` that mounts in the same frame, and on the radio path the dismissal window outlives a single `requestAnimationFrame` because changing the group's value adds a render before the unmount (tried in four shapes, `modal={false}` included). The only known remedy is a 200ms `setTimeout`, which this lane's «zero setTimeout» convention rejects. Measured and written up in `src/components/documents/components/AppDataGrid.md` and at the `modal={false}` comment in `src/components/ui/app-data-grid/parts/grid-filter-editor.tsx`
|
|
264
|
-
- **Gantt**: Confirmed `react-window` and `@types/react-window` stay as direct dependencies — `VariableSizeList`/`ListOnItemsRenderedProps` are imported directly by `src/components/ui/gantt/types/GanttContext.tsx`, `components/Chart/Chart.tsx`, `components/Chart/Scale/Scale.tsx`, `components/Chart/Tree/Tree.tsx`, `components/Chart/Bars/Bars.tsx` and `components/Gantt/Gantt.tsx`; the only other match in `src/` is a textual mention inside a mock message in `message-list.stories.tsx`, not an import
|
|
22
|
+
- **AppSidebar**: On mobile, `navigationFooter` now anchors to the bottom of the panel instead of trailing immediately after the last `navigation` group, matching its desktop placement whenever the navigation list is short enough to leave room
|
|
23
|
+
- **AppSidebar**: The mobile panel's header is no longer offset ~16px from center under the floating close button — centered `headerContent` (e.g. a logo) is now centered correctly
|
|
24
|
+
- **AppSidebar**: Collapsible submenus now animate open/closed instead of snapping instantly (skipped under `prefers-reduced-motion`), and collapsed rows are removed from the tab order and accessibility tree instead of staying mounted
|
|
25
|
+
- **Sidebar**: The panel's edge border (default, non-floating/inset variant) now resolves to the intended `border-d-sidebar-border` token instead of Tailwind v4's `currentColor` default, which rendered near-white on dark themes and near-black on light ones
|
|
265
26
|
|
|
266
27
|
## [laif-ds@1.0.0]
|
|
267
28
|
|