@elliemae/ds-data-table 3.70.0-next.3 → 3.70.0-next.4

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.
Files changed (43) hide show
  1. package/dist/cjs/TruncatedTooltipText.js +94 -0
  2. package/dist/cjs/TruncatedTooltipText.js.map +7 -0
  3. package/dist/cjs/exported-related/EditableCell.js +1 -1
  4. package/dist/cjs/exported-related/EditableCell.js.map +2 -2
  5. package/dist/cjs/parts/Cells/Cell.js +2 -2
  6. package/dist/cjs/parts/Cells/Cell.js.map +2 -2
  7. package/dist/cjs/parts/Cells/CellFactory.js +2 -2
  8. package/dist/cjs/parts/Cells/CellFactory.js.map +2 -2
  9. package/dist/cjs/parts/Headers/HeaderCellTitle.js +2 -2
  10. package/dist/cjs/parts/Headers/HeaderCellTitle.js.map +2 -2
  11. package/dist/cjs/react-desc-prop-types.js +2 -2
  12. package/dist/cjs/react-desc-prop-types.js.map +1 -1
  13. package/dist/esm/TruncatedTooltipText.js +68 -0
  14. package/dist/esm/TruncatedTooltipText.js.map +7 -0
  15. package/dist/esm/exported-related/EditableCell.js +1 -1
  16. package/dist/esm/exported-related/EditableCell.js.map +2 -2
  17. package/dist/esm/parts/Cells/Cell.js +2 -2
  18. package/dist/esm/parts/Cells/Cell.js.map +2 -2
  19. package/dist/esm/parts/Cells/CellFactory.js +2 -2
  20. package/dist/esm/parts/Cells/CellFactory.js.map +2 -2
  21. package/dist/esm/parts/Headers/HeaderCellTitle.js +2 -2
  22. package/dist/esm/parts/Headers/HeaderCellTitle.js.map +2 -2
  23. package/dist/esm/react-desc-prop-types.js +2 -2
  24. package/dist/esm/react-desc-prop-types.js.map +1 -1
  25. package/dist/types/TruncatedTooltipText.d.ts +9 -0
  26. package/dist/types/tests/DSDataTable.get-owner-props-arguments-slots.test.d.ts +1 -0
  27. package/dist/types/tests/callbacks/editableCell.events.test.d.ts +1 -0
  28. package/dist/types/tests/playwright/DSDataTable.slot-contracts-dynamic.test.playwright.d.ts +1 -0
  29. package/dist/types/tests/playwright/DSDataTableDropIndicatorTestRenderer.d.ts +1 -0
  30. package/dist/types/tests/render/cellStyle.test.d.ts +1 -0
  31. package/package.json +33 -33
  32. package/skills/ds-data-table-boundaries/SKILL.md +363 -0
  33. package/skills/ds-data-table-columns/SKILL.md +273 -0
  34. package/skills/ds-data-table-expandable/SKILL.md +235 -0
  35. package/skills/ds-data-table-feedback/SKILL.md +190 -0
  36. package/skills/ds-data-table-filtering/SKILL.md +322 -0
  37. package/skills/ds-data-table-health-check/SKILL.md +172 -0
  38. package/skills/ds-data-table-migration/SKILL.md +201 -0
  39. package/skills/ds-data-table-pagination/SKILL.md +182 -0
  40. package/skills/ds-data-table-row-variants/SKILL.md +260 -0
  41. package/skills/ds-data-table-selection/SKILL.md +449 -0
  42. package/skills/ds-data-table-setup/SKILL.md +229 -0
  43. package/skills/ds-data-table-slots/SKILL.md +257 -0
@@ -0,0 +1,322 @@
1
+ ---
2
+ name: ds-data-table-filtering
3
+ description: >
4
+ V2 filter system for @elliemae/ds-data-table. FILTER_TYPES V2 values (MULTI_SELECT_V2,
5
+ SELECT_V2, etc.), column-level filter config, filterOptions for multi-select, controlled
6
+ filters + onFiltersChange handler, applyOutOfTheBoxFilters, withFilterBar, filterBarProps,
7
+ composable DSFilterBar + PillsFromDataTableFilters. V1 filter strings are a11y-unsafe tech
8
+ debt — migrate to V2 whenever found in existing code, never use in new implementations.
9
+ type: core
10
+ library: ds-data-table
11
+ library_version: '3.60.0'
12
+ requires:
13
+ - ds-data-table-setup
14
+ - ds-filter-bar-setup
15
+ sources:
16
+ - '@elliemae/ds-data-table:dist/types/exported-related/FilterTypes.d.ts'
17
+ - '@elliemae/ds-data-table:dist/types/react-desc-prop-types.d.ts'
18
+ ---
19
+
20
+ ## Setup
21
+
22
+ ```jsx
23
+ import { DataTable, FILTER_TYPES, applyOutOfTheBoxFilters } from '@elliemae/ds-data-table';
24
+
25
+ const columns = [
26
+ { Header: 'ID', accessor: 'id', filter: FILTER_TYPES.MULTI_SELECT_V2 },
27
+ {
28
+ Header: 'Position',
29
+ accessor: 'position',
30
+ filter: FILTER_TYPES.MULTI_SELECT_V2,
31
+ filterOptions: [
32
+ { dsId: '1', type: 'option', value: 'React dev', label: 'React dev' },
33
+ { dsId: '2', type: 'option', value: 'Fullstack dev', label: 'Fullstack dev' },
34
+ ],
35
+ },
36
+ ];
37
+
38
+ // In your state manager — adapt to Redux/Zustand/Jotai
39
+ const [filters, setFilters] = /* state manager slice */;
40
+ const [filteredData, setFilteredData] = /* state manager slice */;
41
+
42
+ // onFiltersChange MUST be memoized
43
+ const onFiltersChange = useCallback((newFilters) => {
44
+ dispatch(setFilters(newFilters));
45
+ dispatch(setFilteredData(applyOutOfTheBoxFilters(originalData, newFilters)));
46
+ }, [originalData]);
47
+
48
+ <DataTable
49
+ columns={columns}
50
+ data={filteredData}
51
+ height={500}
52
+ withFilterBar
53
+ filters={filters}
54
+ onFiltersChange={onFiltersChange}
55
+ />
56
+ ```
57
+
58
+ ## Core Patterns
59
+
60
+ ### Available V2 filter types
61
+
62
+ ```js
63
+ import { FILTER_TYPES } from '@elliemae/ds-data-table';
64
+
65
+ // V2 types — always use these
66
+ FILTER_TYPES.MULTI_SELECT_V2; // multi-select dropdown
67
+ FILTER_TYPES.SELECT_V2; // single-select dropdown
68
+ FILTER_TYPES.CREATABLE_SELECT_V2; // single-select with user-creatable options
69
+ FILTER_TYPES.CREATABLE_MULTI_SELECT_V2; // multi-select with user-creatable options
70
+ FILTER_TYPES.SINGLE_DATE_V2; // single date picker
71
+ FILTER_TYPES.DATE_RANGE_V2; // date range picker
72
+ FILTER_TYPES.DATE_SWITCHER_V2; // date / date-range toggle
73
+ FILTER_TYPES.NUMBER_RANGE_V2; // numeric range input
74
+ FILTER_TYPES.FREE_TEXT_SEARCH_V2; // text input
75
+ FILTER_TYPES.CURRENCY_RANGE_V2; // currency range input
76
+
77
+ // V1 types — DO NOT USE, migrate on sight
78
+ FILTER_TYPES.MULTI_SELECT; // ← a11y-unsafe legacy
79
+ FILTER_TYPES.SELECT; // ← a11y-unsafe legacy
80
+ FILTER_TYPES.CREATABLE_SELECT; // ← a11y-unsafe legacy
81
+ FILTER_TYPES.CREATABLE_MULTI_SELECT; // ← a11y-unsafe legacy
82
+ FILTER_TYPES.SINGLE_DATE; // ← a11y-unsafe legacy
83
+ FILTER_TYPES.DATE_RANGE; // ← a11y-unsafe legacy
84
+ FILTER_TYPES.DATE_SWITCHER; // ← a11y-unsafe legacy
85
+ FILTER_TYPES.NUMBER_RANGE; // ← a11y-unsafe legacy
86
+ FILTER_TYPES.FREE_TEXT_SEARCH; // ← a11y-unsafe legacy
87
+ FILTER_TYPES.CURRENCY_RANGE; // ← a11y-unsafe legacy
88
+ ```
89
+
90
+ ### Composable filter bar with external DSFilterBar
91
+
92
+ The composable pattern pairs `PillsFromDataTableFilters` (this package) with `DSFilterBar` from `@elliemae/ds-filter-bar`. The `@elliemae/ds-filter-bar` assembly has its own a11y requirements (focus management, screen reader announcements) that are outside the scope of this skill.
93
+
94
+ **Before implementing the filter bar assembly:**
95
+
96
+ 1. Load the `ds-filter-bar-setup` skill from `@elliemae/ds-filter-bar` — run `npx @tanstack/intent install` in the project or look for a `skills/` directory inside the package. That skill covers the full assembly: `useFilterBarPrevNextFocus`, `runAfterRemoval`, `ScreenReaderOnly` announcements, `DSFilterBarMenuButton` wiring, and all a11y requirements.
97
+ 2. If no skill is found, ask the human to share a correct implementation example from the Dimsum storybook. Do not approximate the filter bar assembly from incomplete information — omitting the focus management hooks breaks keyboard accessibility.
98
+
99
+ The DataTable side of the wiring:
100
+
101
+ ```jsx
102
+ import { DataTable, PillsFromDataTableFilters, applyOutOfTheBoxFilters } from '@elliemae/ds-data-table';
103
+ import {} from /* see @elliemae/ds-filter-bar skill or storybook */ '@elliemae/ds-filter-bar';
104
+ import { useCallback, useRef } from 'react';
105
+
106
+ const onFiltersChange = useCallback(
107
+ (newFilters) => {
108
+ dispatch(setFilters(newFilters));
109
+ dispatch(setFilteredData(applyOutOfTheBoxFilters(originalData, newFilters)));
110
+ },
111
+ [originalData],
112
+ );
113
+
114
+ // fallbackRef is shared with the DSFilterBar assembly (passed as fallbackRef to
115
+ // useFilterBarPrevNextFocus). When the last filter pill is removed, the filter bar
116
+ // has nowhere left to move focus — this ref tells it to land on the first column header.
117
+ const fallbackRef = useRef(null);
118
+
119
+ // dsDatatableHeadTh injects fallbackRef into the first focusable column header,
120
+ // completing the focus chain: last pill removed → focus moves to table header
121
+ const dsDatatableHeadTh = useCallback(({ columnId, firstFocuseableColumnHeaderId }) => {
122
+ if (columnId === firstFocuseableColumnHeaderId) return { innerRef: fallbackRef };
123
+ return {};
124
+ }, []);
125
+
126
+ {
127
+ /*
128
+ ... DSFilterBar assembly here (from @elliemae/ds-filter-bar).
129
+ Use PillsFromDataTableFilters inside DSFilterBarContent.
130
+ Follow all a11y requirements from the ds-filter-bar skill or storybook —
131
+ do not omit focus management hooks or ScreenReaderOnly announcements.
132
+ */
133
+ }
134
+
135
+ <DataTable
136
+ columns={columns}
137
+ data={filteredData}
138
+ height={500}
139
+ filters={filters}
140
+ onFiltersChange={onFiltersChange}
141
+ dsDatatableHeadTh={dsDatatableHeadTh}
142
+ />;
143
+ ```
144
+
145
+ Do not pass `withFilterBar` to DataTable when using a composable `DSFilterBar` — the filter bar is external.
146
+
147
+ ### filterBarProps for inline filter bar customization
148
+
149
+ `filterBarAddonRenderer` must be a **stable React component reference** — defined at module level or imported, never inline or via `useCallback`. Defining it inline (even with `useCallback`) creates a new component type on re-render, causing React to unmount and remount the addon on every render cycle, producing erratic behavior whenever the addon uses hooks or internal state.
150
+
151
+ The addon needs access to filter state and clear action from outside itself. Because `FilterBarAddon` is at module level, it reads what it needs at render time rather than closing over parent state — that is what makes this pattern work. Adapt to your project's state management stack:
152
+
153
+ - **Redux**: define `FilterBarAddon` at module level and connect directly with `useSelector`/`useDispatch` — no context needed
154
+ - **Zustand / Jotai**: subscribe to the relevant store or atom directly inside `FilterBarAddon`
155
+ - **React Context**: use the pattern below (last resort — prefer the state manager options above)
156
+
157
+ ```jsx
158
+ import { DSButtonV3 } from '@elliemae/ds-button-v2';
159
+ import { ScreenReaderOnly } from '@elliemae/ds-accessibility';
160
+ import { DataTable, applyOutOfTheBoxFilters } from '@elliemae/ds-data-table';
161
+ import React, { createContext, useCallback, useContext, useState, useMemo } from 'react';
162
+
163
+ const FilterBarAddonContext = createContext(null);
164
+
165
+ const FilterBarAddon = () => {
166
+ const { filters, onClearFilters } = useContext(FilterBarAddonContext);
167
+ if (filters.length === 0) return null;
168
+ return (
169
+ <DSButtonV3 buttonType="text" onClick={onClearFilters}>
170
+ Clear all filters
171
+ </DSButtonV3>
172
+ );
173
+ };
174
+
175
+ const [announcement, setAnnouncement] = useState('');
176
+
177
+ const onFiltersChange = useCallback(
178
+ (newFilters) => {
179
+ dispatch(setFilters(newFilters));
180
+ dispatch(setFilteredData(applyOutOfTheBoxFilters(originalData, newFilters)));
181
+ },
182
+ [originalData],
183
+ );
184
+
185
+ const handleClearFilters = useCallback(() => {
186
+ onFiltersChange([]);
187
+ setAnnouncement('All filters cleared');
188
+ }, [onFiltersChange]);
189
+
190
+ const ctxValue = useMemo(() => ({ filters, onClearFilters: handleClearFilters }), [filters, handleClearFilters]);
191
+
192
+ <FilterBarAddonContext.Provider value={ctxValue}>
193
+ <ScreenReaderOnly aria-live="polite">{announcement}</ScreenReaderOnly>
194
+ <DataTable
195
+ columns={columns}
196
+ data={filteredData}
197
+ height={500}
198
+ withFilterBar
199
+ filters={filters}
200
+ filterBarProps={{ filterBarAddonRenderer: FilterBarAddon }}
201
+ onFiltersChange={onFiltersChange}
202
+ />
203
+ </FilterBarAddonContext.Provider>;
204
+ ```
205
+
206
+ ## Common Mistakes
207
+
208
+ ### CRITICAL Using V1 filter string literals in new or existing code
209
+
210
+ Wrong:
211
+
212
+ ```jsx
213
+ { Header: 'Position', accessor: 'position', filter: FILTER_TYPES.MULTI_SELECT }
214
+ { Header: 'Status', accessor: 'status', filter: FILTER_TYPES.SELECT }
215
+ ```
216
+
217
+ Correct:
218
+
219
+ ```jsx
220
+ { Header: 'Position', accessor: 'position', filter: FILTER_TYPES.MULTI_SELECT_V2 }
221
+ { Header: 'Status', accessor: 'status', filter: FILTER_TYPES.SELECT_V2 }
222
+ ```
223
+
224
+ V1 filter types are not a11y safe. When found in existing code, migrate all instances — not just the one in scope.
225
+
226
+ Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-datatable-features-filtersv2--multi-select-filter-vs-multi-select-v-2
227
+
228
+ ---
229
+
230
+ ### HIGH withFilterBar without the filters + onFiltersChange controlled pair
231
+
232
+ Wrong:
233
+
234
+ ```jsx
235
+ <DataTable columns={columns} data={rows} height={500} withFilterBar />
236
+ ```
237
+
238
+ Correct:
239
+
240
+ ```jsx
241
+ <DataTable
242
+ columns={columns}
243
+ data={filteredData}
244
+ height={500}
245
+ withFilterBar
246
+ filters={filters}
247
+ onFiltersChange={onFiltersChange}
248
+ />
249
+ ```
250
+
251
+ `withFilterBar` only renders the UI. Without `filters` and `onFiltersChange`, filter interactions produce no result.
252
+
253
+ Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-datatable-features-filtersv2--multi-select-filter-v-2
254
+
255
+ ---
256
+
257
+ ### HIGH onFiltersChange updates filter state but does not produce updated data
258
+
259
+ DataTable does not filter data internally. `onFiltersChange` receives the new filter state — the app is entirely responsible for producing the updated `data` array that reflects those filters. Storing the filters without updating `data` leaves the table showing stale rows regardless of what the user selects.
260
+
261
+ **Before writing the filtering logic, determine which filtering model applies:**
262
+
263
+ **Server-side / API-based filtering** — the standard model for any non-trivial dataset. On filter change, dispatch the new filter state and trigger an API call with the filters as query parameters. The response is the filtered dataset.
264
+
265
+ ```jsx
266
+ const onFiltersChange = useCallback((newFilters) => {
267
+ dispatch(setFilters(newFilters));
268
+ dispatch(fetchFilteredData(newFilters)); // API call with filters as query params
269
+ }, []);
270
+ ```
271
+
272
+ **Client-side filtering** — only appropriate for very small datasets or as a temporary workaround while backend filtering endpoints are being developed. Triple-check that this is actually the correct model before implementing. Filtering is by industry standards a backend concern — backends have specialized tools for it (SQL WHERE clauses, database engine algorithms optimized for exactly this problem). Implementing it on the frontend duplicates that concern on the wrong layer with inferior tools, making the flow suboptimal and fragile. Applying filters to an in-memory array is better than offering no filtering at all, but it covers a backend limitation rather than solving the underlying problem. It also introduces stale data risk: the in-memory array reflects the dataset at load time; if the source changes and the array is never refreshed, users are filtering a snapshot, not the live data. Server-side filtering inherently avoids this because every request fetches fresh data from the authoritative source. The cost compounds further when combined with sorting and pagination.
273
+
274
+ ```jsx
275
+ // Only use applyOutOfTheBoxFilters when client-side filtering is confirmed correct
276
+ const onFiltersChange = useCallback(
277
+ (newFilters) => {
278
+ dispatch(setFilters(newFilters));
279
+ dispatch(setFilteredData(applyOutOfTheBoxFilters(originalData, newFilters)));
280
+ },
281
+ [originalData],
282
+ );
283
+ ```
284
+
285
+ Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-datatable-features-filtersv2--multi-select-filter-v-2
286
+
287
+ ---
288
+
289
+ ### HIGH Non-primitive filtering props not memoized cause unnecessary re-renders
290
+
291
+ Every non-primitive prop passed to DataTable must be a stable reference. For filtering, the props that require memoization are:
292
+
293
+ | Prop | Type | Memoization |
294
+ | ------------------------------------- | --------- | -------------------------------------- |
295
+ | `onFiltersChange` | callback | `useCallback` |
296
+ | `filterBarProps` | object | `useMemo` (if constructed inline) |
297
+ | `filterOptions` on column definitions | array | `useMemo` on the columns array |
298
+ | `filterBarAddonRenderer` | component | defined at module level — never inline |
299
+
300
+ Non-stable references cause DataTable to re-render on every parent render cycle regardless of whether filtering state actually changed. `filterBarAddonRenderer` is covered in detail in the `filterBarProps` pattern above.
301
+
302
+ Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-datatable-features-filtersv2--multi-select-filter-v-2
303
+
304
+ ---
305
+
306
+ ### HIGH Tension: custom Cell renderers and filter display diverge
307
+
308
+ V2 filter types derive display values from the raw data value in each cell. Custom Cell renderers change the display layer independently — the filter system has no visibility into what the custom renderer shows. Agents adding filters to columns with custom cells often produce tables where filter pills and cell display are inconsistent.
309
+
310
+ When a column uses a custom Cell renderer, `applyOutOfTheBoxFilters` is no longer sufficient — it operates on raw data values and cannot account for the transformed presentation the renderer produces. The application is responsible for implementing its own filtering logic for those columns, using the IoC patterns DataTable exposes (`onFiltersChange` callback, `filters` controlled state). The OOB filter algorithms are designed to cover standard data-value scenarios only; anything beyond that is the application's domain.
311
+
312
+ See also: ds-data-table-migration/SKILL.md — V1 to V2 migration patterns
313
+ See also: ds-filter-bar-setup/SKILL.md — full DSFilterBar assembly with @elliemae/ds-filter-bar primitives
314
+
315
+ ---
316
+
317
+ ## When this isn't enough
318
+
319
+ If the documented patterns cannot satisfy the requirement, contact the Dimsum team:
320
+
321
+ **ICE internal:** Microsoft Teams — Dimsum channel (informal) / Jira Dimsum board (formal)
322
+ **Partners:** Your organization's Dimsum point of contact
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: ds-data-table-health-check
3
+ description: >
4
+ Audit the current state of a @elliemae/ds-data-table implementation. Use before
5
+ starting a task that depends on DataTable, mid-task to verify a foundation before
6
+ building on it, or when diagnosing unexpected behavior. Produces a complete state
7
+ report: what is correct and sound, what requires fixing, what is suboptimal, and
8
+ what gaps may affect the current task.
9
+ type: health-check
10
+ library: ds-data-table
11
+ library_version: '3.60.0'
12
+ requires:
13
+ - ds-data-table-setup
14
+ - ds-data-table-slots
15
+ - ds-data-table-selection
16
+ - ds-data-table-filtering
17
+ - ds-data-table-columns
18
+ - ds-data-table-pagination
19
+ - ds-data-table-expandable
20
+ - ds-data-table-row-variants
21
+ - ds-data-table-feedback
22
+ - ds-data-table-boundaries
23
+ ---
24
+
25
+ ## Purpose
26
+
27
+ This skill audits an existing DataTable implementation. It is not a setup guide — it does not tell you how to build a DataTable. It tells you the current state of one that already exists.
28
+
29
+ **Use it:**
30
+
31
+ - Before starting a task that heavily involves a DataTable — to understand what you are about to touch before you touch it
32
+ - Before making a DataTable load-bearing for a new feature — to verify the foundation is sound
33
+ - When something is behaving unexpectedly — to surface what the implementation gets wrong
34
+
35
+ The output is a prioritized state report. A clean report means you can proceed with confidence. A report with critical issues means fix those before doing anything else.
36
+
37
+ ---
38
+
39
+ ## Load the embedded knowledge
40
+
41
+ Load the following skills **as reference knowledge only**. You are reading their embedded understanding of correct patterns — their Common Mistakes, practically mandatory props, and OOB-first guidance become the diagnostic lens. You are not running them as tasks. Do not execute any setup or implementation step from these skills.
42
+
43
+ - `ds-data-table-setup` — required props, state management, a11y baseline, height/uniqueRowAccessor/domIdAffix requirements
44
+ - `ds-data-table-slots` — slot injection surface, OOB-first hierarchy, event listener red flags
45
+ - `ds-data-table-selection` — auto-injection model, cross-page selection, selectedControl behavior
46
+ - `ds-data-table-filtering` — V1 vs V2 filter types, withFilterBar requirements, applyOutOfTheBoxFilters scope
47
+ - `ds-data-table-columns` — column config, sort/resize callbacks, custom Cell escalation path
48
+ - `ds-data-table-pagination` — Pagination render prop vs deprecated lowercase, data slicing responsibility
49
+ - `ds-data-table-expandable` — tableRowDetails deprecation, controlled pair requirement, uniqueRowAccessor dependency
50
+ - `ds-data-table-row-variants` — getRowVariant + data format dual requirement, groupBy utility
51
+ - `ds-data-table-feedback` — height constraint for skeleton, isLoading vs isSkeleton distinction
52
+ - `ds-data-table-boundaries` — useWholeStore smell, DataTable-as-form prohibition
53
+
54
+ ---
55
+
56
+ ## Procedure
57
+
58
+ ### Step 1 — Locate and read the implementation
59
+
60
+ Find every file where `DataTable` from `@elliemae/ds-data-table` is rendered. For each instance:
61
+
62
+ - Read the full props being passed
63
+ - Read every callback handler wired to the DataTable
64
+ - Read where the state driving the DataTable lives (state manager slice, store, atoms, or — a finding in itself — `useState`)
65
+ - Read any column definitions, especially `Cell` and `Header` as component references
66
+ - Note any slot props (`dsDatatableCellsContainer`, `dsDatatableCell`, `dsDatatableRow`, `dsDatatableHeadTh`)
67
+ - Note if `renderRowActions` is present
68
+ - Note if `withFilterBar`, `filters`, `onFiltersChange` are present
69
+ - Note if `selection`, `onSelectionChange`, `selectSingle` are present
70
+ - Note if `isExpandable`, `expandedRows`, `onRowExpand` are present
71
+ - Note if `Pagination` (capital P) or `pagination` (lowercase) is present
72
+
73
+ ### Step 2 — Apply the diagnostic checks
74
+
75
+ Work through the following checks. For each finding, assign a severity:
76
+
77
+ **CRITICAL** — prevents correct function, a11y failure, or silent data corruption; fix before proceeding
78
+ **HIGH** — meaningful risk of regression, wrong behavior, or performance degradation
79
+ **MEDIUM** — suboptimal but not immediately breaking
80
+ **SOUND** — correct; note it so you know what you can rely on
81
+
82
+ #### Foundation checks (from ds-data-table-setup)
83
+
84
+ - [ ] `height` is a numeric pixel value — non-px silently defeats virtualization and scroll
85
+ - [ ] `uniqueRowAccessor` is present when selection, expand, or drag & drop is used
86
+ - [ ] `dsDataTableTableWrapper={{ 'aria-label': '...' }}` is present — absence is a WCAG 4.1.2 VPAT failure
87
+ - [ ] `domIdAffix` is an explicit string when multiple DataTable instances coexist on the page
88
+ - [ ] All DataTable-driving state lives in the project's state manager — not `useState`
89
+ - [ ] No `useEffect` watches DataTable callback-driven state to trigger secondary logic
90
+
91
+ #### Column checks (from ds-data-table-columns)
92
+
93
+ - [ ] Column `width` values are pixel numbers — non-px is unsupported
94
+ - [ ] `isResizeable` is not combined with `colsLayoutStyle='auto'` — incompatible
95
+ - [ ] `onColumnSort` is used, not deprecated `onColumnSortChange`
96
+ - [ ] `onColumnSizeChange` is used, not deprecated `onColumnResize`
97
+ - [ ] Columns using `Cell` as a React component: flag each one — was OOB or slot injection considered first?
98
+
99
+ #### Selection checks (from ds-data-table-selection)
100
+
101
+ - [ ] `selection` prop is present — DataTable auto-injects the checkbox column when truthy; `multiSelectColumn` import is for customization only
102
+ - [ ] `onSelectionChange` is present when `selection` is present — without it, selection silently does nothing
103
+ - [ ] `uniqueRowAccessor` is set — without it, selection maps to wrong rows
104
+ - [ ] Select-all cross-page: if `selectedControl === 'All'` and the dataset is paginated, is `newSelection` being merged with existing selection rather than replacing it?
105
+
106
+ #### Filtering checks (from ds-data-table-filtering)
107
+
108
+ - [ ] All `column.filter` values use `FILTER_TYPES.*_V2` — V1 strings are a11y-unsafe legacy
109
+ - [ ] `withFilterBar={true}` is accompanied by both `filters` and `onFiltersChange` — either alone silently does nothing
110
+ - [ ] `applyOutOfTheBoxFilters` is used only for client-side filtering of small datasets — not as the default model for any non-trivial dataset
111
+ - [ ] Columns with custom `Cell` renderers that also have filters: flag — `applyOutOfTheBoxFilters` operates on raw data values, not on what the custom renderer displays
112
+
113
+ #### Pagination checks (from ds-data-table-pagination)
114
+
115
+ - [ ] Capital-P `Pagination` render prop is used — lowercase `pagination` prop is deprecated
116
+ - [ ] `TablePagination` is defined at module level — not inside the component body
117
+ - [ ] `data` passed to DataTable is the current page slice — DataTable does not paginate internally
118
+ - [ ] Page index resets to 1 on filter change where applicable
119
+
120
+ #### Expandable checks (from ds-data-table-expandable)
121
+
122
+ - [ ] `tableRowDetails` is not used — deprecated; WCAG violations; propose page-level alternative
123
+ - [ ] `expandedRows` and `onRowExpand` are both present when `isExpandable` is set
124
+ - [ ] `uniqueRowAccessor` is set — required for expand state stability
125
+
126
+ #### Slot checks (from ds-data-table-slots)
127
+
128
+ - [ ] Any `dsDatatableCellsContainer` or `dsDatatableCell` callbacks: confirm they return plain props objects with no hooks inside
129
+ - [ ] Any `dsDatatableCellsContainer` or `dsDatatableCell` callbacks: are event listeners (`onClick`, `onKeyDown`) being injected? **Flag as HIGH** — an OOB callback should handle the interaction; if not, stop and surface to the human in the loop before proceeding
130
+ - [ ] All slot callbacks are wrapped in `useCallback`
131
+ - [ ] `aria-*` and `data-*` injections target `CELL_CONTAINER` (`dsDatatableCellsContainer`), not `ROW` (`dsDatatableRow`) — injecting into ROW is a silent a11y failure
132
+
133
+ #### Feedback checks (from ds-data-table-feedback)
134
+
135
+ - [ ] `isSkeleton` is preferred over `isLoading` for initial data fetch
136
+ - [ ] `noResultsButtonLabel` and `onNoResultsButtonClick` are either both present or both absent — either alone has no visible effect
137
+ - [ ] If `isSkeleton` is used, `height` is a numeric pixel value — non-px silently prevents skeleton rows from rendering
138
+
139
+ #### Boundary checks (from ds-data-table-boundaries)
140
+
141
+ - [ ] No call to `useWholeStore()` — it re-renders on any change anywhere; use CellRendererProps IoC arguments instead
142
+ - [ ] DataTable is not being used to drive full form data entry — use `@elliemae/ds-grid` for that
143
+
144
+ ---
145
+
146
+ ### Step 3 — Produce the state report
147
+
148
+ Organize findings into four sections:
149
+
150
+ **Foundation — what is correct and sound**
151
+ List what the implementation gets right. Be specific. This is what you can rely on when making changes.
152
+
153
+ **Critical issues — fix before proceeding**
154
+ Each item with the specific prop, file, and line where the problem is. Link back to the relevant skill section for the fix.
155
+
156
+ **Warnings — should address, not blocking**
157
+ Each item with context on the risk. Note whether it is a quick fix or requires design discussion.
158
+
159
+ **Awareness — no action, but good to know**
160
+ Suboptimal patterns that are not breaking but may become relevant as the task evolves.
161
+
162
+ **Gaps relevant to this task**
163
+ Based on what the current task requires, are there missing props or wiring that will be needed? Surface these now rather than mid-implementation.
164
+
165
+ ---
166
+
167
+ ## What this skill does NOT do
168
+
169
+ - It does not generate or modify code
170
+ - It does not run the setup skills as tasks
171
+ - It does not replace human judgment on trade-offs — critical issues are surfaced, not automatically fixed
172
+ - It does not test the component — use the slot contract tests and compliance tests for that