@redsift/ds-mcp-server 12.3.1-muiv6-alpha.2

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 (258) hide show
  1. package/README.md +203 -0
  2. package/consumer-instructions/.cursorrules +80 -0
  3. package/consumer-instructions/.windsurfrules +80 -0
  4. package/consumer-instructions/CLAUDE.md +87 -0
  5. package/consumer-instructions/redsift-design-system.instructions.md +87 -0
  6. package/data/demos/patterns/crossfiltered-datagrid-page/example.tsx +750 -0
  7. package/data/demos/patterns/crossfiltered-datagrid-page/with-empty-state.tsx +111 -0
  8. package/data/demos/patterns/crossfiltered-datagrid-page/with-error.tsx +122 -0
  9. package/data/demos/patterns/crossfiltered-datagrid-page/with-loading.tsx +88 -0
  10. package/data/demos/patterns/datagrid-page/example.tsx +521 -0
  11. package/data/demos/patterns/datagrid-page/with-empty-state.tsx +80 -0
  12. package/data/demos/patterns/datagrid-page/with-error.tsx +96 -0
  13. package/data/demos/patterns/datagrid-page/with-loading.tsx +57 -0
  14. package/data/demos/patterns/drilldown-datagrid-page/example.tsx +715 -0
  15. package/data/demos/patterns/drilldown-datagrid-page/with-empty-state.tsx +113 -0
  16. package/data/demos/patterns/drilldown-datagrid-page/with-error.tsx +125 -0
  17. package/data/demos/patterns/drilldown-datagrid-page/with-loading.tsx +90 -0
  18. package/data/demos/patterns/server-datagrid-page/example.tsx +757 -0
  19. package/data/demos/patterns/server-datagrid-page/with-empty-state.tsx +107 -0
  20. package/data/demos/patterns/server-datagrid-page/with-error.tsx +107 -0
  21. package/data/demos/patterns/server-datagrid-page/with-loading.tsx +63 -0
  22. package/data/docs/components/charts/Arc.json +179 -0
  23. package/data/docs/components/charts/Arcs.json +104 -0
  24. package/data/docs/components/charts/Axis.json +269 -0
  25. package/data/docs/components/charts/Bar.json +236 -0
  26. package/data/docs/components/charts/BarChart.json +852 -0
  27. package/data/docs/components/charts/BarChartBars.json +18 -0
  28. package/data/docs/components/charts/BarChartGroupedTooltip.json +9 -0
  29. package/data/docs/components/charts/BarChartOverlay.json +34 -0
  30. package/data/docs/components/charts/BarChartSection.json +94 -0
  31. package/data/docs/components/charts/BaseBarChart.json +852 -0
  32. package/data/docs/components/charts/ChartContainerTitle.json +574 -0
  33. package/data/docs/components/charts/DataPoint.json +160 -0
  34. package/data/docs/components/charts/Dot.json +191 -0
  35. package/data/docs/components/charts/EmptyBarChart.json +852 -0
  36. package/data/docs/components/charts/EmptyLineChart.json +753 -0
  37. package/data/docs/components/charts/EmptyPieChart.json +826 -0
  38. package/data/docs/components/charts/EmptyScatterPlot.json +802 -0
  39. package/data/docs/components/charts/Legend.json +510 -0
  40. package/data/docs/components/charts/LegendItem.json +128 -0
  41. package/data/docs/components/charts/Line.json +69 -0
  42. package/data/docs/components/charts/LineChart.json +753 -0
  43. package/data/docs/components/charts/LoadingBarChart.json +852 -0
  44. package/data/docs/components/charts/LoadingLineChart.json +753 -0
  45. package/data/docs/components/charts/LoadingPieChart.json +826 -0
  46. package/data/docs/components/charts/LoadingScatterPlot.json +802 -0
  47. package/data/docs/components/charts/PieChart.json +826 -0
  48. package/data/docs/components/charts/RenderedLineChart.json +753 -0
  49. package/data/docs/components/charts/RenderedLinearBarChart.json +823 -0
  50. package/data/docs/components/charts/RenderedOrdinalBarChart.json +852 -0
  51. package/data/docs/components/charts/RenderedPieChart.json +826 -0
  52. package/data/docs/components/charts/RenderedScatterPlot.json +802 -0
  53. package/data/docs/components/charts/ScatterPlot.json +802 -0
  54. package/data/docs/components/charts/getAxisType.json +9 -0
  55. package/data/docs/components/charts/getComponentPosition.json +9 -0
  56. package/data/docs/components/dashboard/ChartEmptyState.json +42 -0
  57. package/data/docs/components/dashboard/Dashboard.json +26 -0
  58. package/data/docs/components/dashboard/DataCard.json +300 -0
  59. package/data/docs/components/dashboard/DataCardBody.json +431 -0
  60. package/data/docs/components/dashboard/DataCardHeader.json +304 -0
  61. package/data/docs/components/dashboard/DataCardListbox.json +529 -0
  62. package/data/docs/components/dashboard/DataRow.json +539 -0
  63. package/data/docs/components/dashboard/DrilldownItem.json +342 -0
  64. package/data/docs/components/dashboard/FilterableBarChart.json +83 -0
  65. package/data/docs/components/dashboard/FilterableDataGrid.json +83 -0
  66. package/data/docs/components/dashboard/FilterablePieChart.json +83 -0
  67. package/data/docs/components/dashboard/FilterableScatterPlot.json +83 -0
  68. package/data/docs/components/dashboard/PdfDocument.json +58 -0
  69. package/data/docs/components/dashboard/PdfExportButton.json +458 -0
  70. package/data/docs/components/dashboard/TimeSeriesBarChart.json +172 -0
  71. package/data/docs/components/dashboard/WithFilters.json +83 -0
  72. package/data/docs/components/design-system/ActiveDescendantListbox.json +521 -0
  73. package/data/docs/components/design-system/Alert.json +349 -0
  74. package/data/docs/components/design-system/AppBar.json +64 -0
  75. package/data/docs/components/design-system/AppContainer.json +67 -0
  76. package/data/docs/components/design-system/AppContent.json +566 -0
  77. package/data/docs/components/design-system/AppSidePanel.json +87 -0
  78. package/data/docs/components/design-system/Badge.json +293 -0
  79. package/data/docs/components/design-system/BaseBreadcrumbs.json +298 -0
  80. package/data/docs/components/design-system/BaseGrid.json +543 -0
  81. package/data/docs/components/design-system/BaseSkeleton.json +338 -0
  82. package/data/docs/components/design-system/BreadcrumbItem.json +231 -0
  83. package/data/docs/components/design-system/Breadcrumbs.json +298 -0
  84. package/data/docs/components/design-system/Button.json +402 -0
  85. package/data/docs/components/design-system/ButtonGroup.json +415 -0
  86. package/data/docs/components/design-system/ButtonLink.json +568 -0
  87. package/data/docs/components/design-system/Card.json +328 -0
  88. package/data/docs/components/design-system/CardActions.json +431 -0
  89. package/data/docs/components/design-system/CardBody.json +431 -0
  90. package/data/docs/components/design-system/CardHeader.json +428 -0
  91. package/data/docs/components/design-system/Checkbox.json +426 -0
  92. package/data/docs/components/design-system/CheckboxGroup.json +382 -0
  93. package/data/docs/components/design-system/ConditionalWrapper.json +40 -0
  94. package/data/docs/components/design-system/DetailedCard.json +401 -0
  95. package/data/docs/components/design-system/DetailedCardCollapsibleSectionItems.json +29 -0
  96. package/data/docs/components/design-system/DetailedCardHeader.json +37 -0
  97. package/data/docs/components/design-system/DetailedCardSection.json +90 -0
  98. package/data/docs/components/design-system/DetailedCardSectionItem.json +109 -0
  99. package/data/docs/components/design-system/Flexbox.json +523 -0
  100. package/data/docs/components/design-system/FocusWithinGroup.json +9 -0
  101. package/data/docs/components/design-system/Grid.json +543 -0
  102. package/data/docs/components/design-system/GridItem.json +388 -0
  103. package/data/docs/components/design-system/Heading.json +390 -0
  104. package/data/docs/components/design-system/Icon.json +325 -0
  105. package/data/docs/components/design-system/IconButton.json +371 -0
  106. package/data/docs/components/design-system/IconButtonLink.json +588 -0
  107. package/data/docs/components/design-system/Item.json +554 -0
  108. package/data/docs/components/design-system/Link.json +552 -0
  109. package/data/docs/components/design-system/LinkButton.json +397 -0
  110. package/data/docs/components/design-system/Listbox.json +529 -0
  111. package/data/docs/components/design-system/Number.json +773 -0
  112. package/data/docs/components/design-system/NumberField.json +594 -0
  113. package/data/docs/components/design-system/Pill.json +378 -0
  114. package/data/docs/components/design-system/ProgressBar.json +121 -0
  115. package/data/docs/components/design-system/RadarSvgLinearGradient.json +9 -0
  116. package/data/docs/components/design-system/Radio.json +415 -0
  117. package/data/docs/components/design-system/RadioGroup.json +382 -0
  118. package/data/docs/components/design-system/RenderedListboxItem.json +18 -0
  119. package/data/docs/components/design-system/RovingTabindexListbox.json +521 -0
  120. package/data/docs/components/design-system/Shield.json +360 -0
  121. package/data/docs/components/design-system/SideNavigationMenu.json +144 -0
  122. package/data/docs/components/design-system/SideNavigationMenuBar.json +89 -0
  123. package/data/docs/components/design-system/SideNavigationMenuItem.json +319 -0
  124. package/data/docs/components/design-system/Skeleton.json +338 -0
  125. package/data/docs/components/design-system/SkeletonCircle.json +338 -0
  126. package/data/docs/components/design-system/SkeletonText.json +371 -0
  127. package/data/docs/components/design-system/Spinner.json +291 -0
  128. package/data/docs/components/design-system/Switch.json +415 -0
  129. package/data/docs/components/design-system/SwitchGroup.json +382 -0
  130. package/data/docs/components/design-system/Text.json +418 -0
  131. package/data/docs/components/design-system/TextArea.json +501 -0
  132. package/data/docs/components/design-system/TextField.json +539 -0
  133. package/data/docs/components/design-system/sizeToDimension.json +9 -0
  134. package/data/docs/components/pickers/BaseCombobox.json +320 -0
  135. package/data/docs/components/pickers/BaseComboboxContent.json +453 -0
  136. package/data/docs/components/pickers/BaseMenuButton.json +240 -0
  137. package/data/docs/components/pickers/BaseMenuButtonContent.json +442 -0
  138. package/data/docs/components/pickers/BaseSelect.json +258 -0
  139. package/data/docs/components/pickers/Combobox.json +320 -0
  140. package/data/docs/components/pickers/ComboboxContent.json +453 -0
  141. package/data/docs/components/pickers/ComboboxContentFooter.json +431 -0
  142. package/data/docs/components/pickers/ComboboxContentHeader.json +431 -0
  143. package/data/docs/components/pickers/ComboboxContentListbox.json +541 -0
  144. package/data/docs/components/pickers/ComboboxTrigger.json +336 -0
  145. package/data/docs/components/pickers/Item.json +554 -0
  146. package/data/docs/components/pickers/MenuButton.json +240 -0
  147. package/data/docs/components/pickers/MenuButtonContent.json +442 -0
  148. package/data/docs/components/pickers/MenuButtonContentFooter.json +431 -0
  149. package/data/docs/components/pickers/MenuButtonContentHeader.json +431 -0
  150. package/data/docs/components/pickers/MenuButtonContentMenu.json +523 -0
  151. package/data/docs/components/pickers/MenuButtonTrigger.json +287 -0
  152. package/data/docs/components/pickers/Select.json +258 -0
  153. package/data/docs/components/pickers/SelectContent.json +442 -0
  154. package/data/docs/components/pickers/SelectTrigger.json +298 -0
  155. package/data/docs/components/popovers/BaseDialog.json +114 -0
  156. package/data/docs/components/popovers/BaseDialogContent.json +21 -0
  157. package/data/docs/components/popovers/BasePopover.json +171 -0
  158. package/data/docs/components/popovers/BaseToggletip.json +184 -0
  159. package/data/docs/components/popovers/BaseTooltip.json +121 -0
  160. package/data/docs/components/popovers/Button.json +402 -0
  161. package/data/docs/components/popovers/ButtonLink.json +568 -0
  162. package/data/docs/components/popovers/Dialog.json +114 -0
  163. package/data/docs/components/popovers/DialogContent.json +21 -0
  164. package/data/docs/components/popovers/DialogContentActions.json +442 -0
  165. package/data/docs/components/popovers/DialogContentBody.json +442 -0
  166. package/data/docs/components/popovers/DialogContentHeader.json +76 -0
  167. package/data/docs/components/popovers/DialogTrigger.json +276 -0
  168. package/data/docs/components/popovers/IconButton.json +371 -0
  169. package/data/docs/components/popovers/IconButtonLink.json +588 -0
  170. package/data/docs/components/popovers/Link.json +552 -0
  171. package/data/docs/components/popovers/LinkButton.json +397 -0
  172. package/data/docs/components/popovers/Popover.json +171 -0
  173. package/data/docs/components/popovers/PopoverContent.json +442 -0
  174. package/data/docs/components/popovers/PopoverTrigger.json +276 -0
  175. package/data/docs/components/popovers/Toast.json +145 -0
  176. package/data/docs/components/popovers/ToastContainer.json +122 -0
  177. package/data/docs/components/popovers/Toggletip.json +184 -0
  178. package/data/docs/components/popovers/ToggletipContent.json +402 -0
  179. package/data/docs/components/popovers/ToggletipTrigger.json +276 -0
  180. package/data/docs/components/popovers/Tooltip.json +121 -0
  181. package/data/docs/components/popovers/TooltipContent.json +402 -0
  182. package/data/docs/components/popovers/TooltipTrigger.json +276 -0
  183. package/data/docs/components/products/Dialog.json +106 -0
  184. package/data/docs/components/products/MenuButton.json +232 -0
  185. package/data/docs/components/products/PulsedRadarImage.json +9 -0
  186. package/data/docs/components/products/RadarButton.json +402 -0
  187. package/data/docs/components/products/RadarItem.json +554 -0
  188. package/data/docs/components/table/ControlledPagination.json +9 -0
  189. package/data/docs/components/table/DataGrid.json +93 -0
  190. package/data/docs/components/table/GridToolbarFilterSemanticField.json +69 -0
  191. package/data/docs/components/table/ServerSideControlledPagination.json +9 -0
  192. package/data/docs/components/table/StatefulDataGrid.json +117 -0
  193. package/data/docs/components/table/TextCell.json +118 -0
  194. package/data/docs/components/table/Toolbar.json +145 -0
  195. package/data/docs/components/table/ToolbarWrapper.json +9 -0
  196. package/data/docs/components-index.json +1206 -0
  197. package/data/docs/components.json +55694 -0
  198. package/data/docs/llms-full.txt +8012 -0
  199. package/data/docs/llms.txt +234 -0
  200. package/data/docs/patterns-catalog.md +359 -0
  201. package/data/docs/patterns.json +712 -0
  202. package/data/metadata.json +4 -0
  203. package/data/patterns/crossfiltered-datagrid-page.mdx +386 -0
  204. package/data/patterns/datagrid-page.mdx +214 -0
  205. package/data/patterns/drilldown-datagrid-page.mdx +291 -0
  206. package/data/patterns/server-datagrid-page.mdx +301 -0
  207. package/data/tokens/properties/components/dark-components.json +1108 -0
  208. package/data/tokens/properties/components/light-components.json +1108 -0
  209. package/data/tokens/properties/core/border-radius.json +3 -0
  210. package/data/tokens/properties/core/color.json +280 -0
  211. package/data/tokens/properties/core/layout.json +14 -0
  212. package/data/tokens/properties/core/typography.json +199 -0
  213. package/data/tokens/redsift-design-tokens.css +1391 -0
  214. package/dist/data-store.d.ts +47 -0
  215. package/dist/data-store.d.ts.map +1 -0
  216. package/dist/data-store.js +152 -0
  217. package/dist/data-store.js.map +1 -0
  218. package/dist/index.d.ts +16 -0
  219. package/dist/index.d.ts.map +1 -0
  220. package/dist/index.js +50 -0
  221. package/dist/index.js.map +1 -0
  222. package/dist/init.d.ts +14 -0
  223. package/dist/init.d.ts.map +1 -0
  224. package/dist/init.js +137 -0
  225. package/dist/init.js.map +1 -0
  226. package/dist/paths.d.ts +30 -0
  227. package/dist/paths.d.ts.map +1 -0
  228. package/dist/paths.js +53 -0
  229. package/dist/paths.js.map +1 -0
  230. package/dist/pattern-store.d.ts +41 -0
  231. package/dist/pattern-store.d.ts.map +1 -0
  232. package/dist/pattern-store.js +177 -0
  233. package/dist/pattern-store.js.map +1 -0
  234. package/dist/prompts.d.ts +14 -0
  235. package/dist/prompts.d.ts.map +1 -0
  236. package/dist/prompts.js +762 -0
  237. package/dist/prompts.js.map +1 -0
  238. package/dist/resources.d.ts +14 -0
  239. package/dist/resources.d.ts.map +1 -0
  240. package/dist/resources.js +482 -0
  241. package/dist/resources.js.map +1 -0
  242. package/dist/scaffold.d.ts +31 -0
  243. package/dist/scaffold.d.ts.map +1 -0
  244. package/dist/scaffold.js +239 -0
  245. package/dist/scaffold.js.map +1 -0
  246. package/dist/token-store.d.ts +70 -0
  247. package/dist/token-store.d.ts.map +1 -0
  248. package/dist/token-store.js +196 -0
  249. package/dist/token-store.js.map +1 -0
  250. package/dist/tools.d.ts +15 -0
  251. package/dist/tools.d.ts.map +1 -0
  252. package/dist/tools.js +491 -0
  253. package/dist/tools.js.map +1 -0
  254. package/dist/types.d.ts +108 -0
  255. package/dist/types.d.ts.map +1 -0
  256. package/dist/types.js +17 -0
  257. package/dist/types.js.map +1 -0
  258. package/package.json +39 -0
@@ -0,0 +1,291 @@
1
+ import { Link } from '@redsift/design-system';
2
+ import { DemoBlock } from '../../components/DemoBlock';
3
+ import { CodeBlock } from '../../components/CodeBlock';
4
+ import { FeatureTable } from '../../components/FeatureTable';
5
+ import { RouterLink } from '../../components/RouterLink';
6
+
7
+ ## Introduction
8
+
9
+ The Drilldown Datagrid Page extends the standard [Datagrid Page](/patterns/datagrid-page) with summary **DataCards** placed above the grid. Each DataCard contains **DrilldownItems** that display aggregate counts for a field (e.g. status, category). Clicking a DrilldownItem adds an `isAnyOf` filter to the DataGrid's filter panel — a one-click shortcut for filtering by that value.
10
+
11
+ This is a **one-way** interaction: DrilldownItem clicks push filters into the grid, but the DataCard counts always reflect the **full** (unfiltered) dataset. If you need DataCard counts to recalculate as filters change, use the [Cross-filtered Datagrid Page](/patterns/crossfiltered-datagrid-page) instead.
12
+
13
+ When the user sets an `is` or `isNot` filter via the DataGrid filter panel (instead of clicking a DrilldownItem), the DataCard selection **clears** — all DrilldownItems are deselected. Clicking a DrilldownItem after that overwrites the incompatible filter with a fresh `isAnyOf` item.
14
+
15
+ ## When to Use
16
+
17
+ - You want **summary KPI cards** above a filterable table
18
+ - Clicking a card value should **add a filter** to the datagrid as a convenience shortcut
19
+ - DataCard counts should always show totals from the **full** dataset, regardless of active filters
20
+ - The dataset is small enough to load entirely (under ~1,000 rows)
21
+
22
+ When **not** to use:
23
+
24
+ - DataCard counts should **recalculate** when filters change — use [Cross-filtered Datagrid Page](/patterns/crossfiltered-datagrid-page) instead
25
+ - No summary cards are needed — use [Datagrid Page](/patterns/datagrid-page) instead
26
+ - The dataset is too large to load entirely — combine with [Server Datagrid Page](/patterns/server-datagrid-page) patterns
27
+
28
+ ## Anatomy
29
+
30
+ 1. **DataCard Row** — `Flexbox` with 1–N `DataCard` components, each containing a `DataCard.Header` and either `DataCard.Listbox` (for multi-select fields) or `DataCard.Body` (for boolean toggles) with `DrilldownItem` children
31
+ 2. **Toolbar** — `Flexbox` wrapping MUI's `GridToolbarContainer` with `GridToolbarFilterButton`, `GridToolbarColumnsButton`, `GridToolbarDensitySelector`, `GridToolbarExport`, and `GridToolbarQuickFilter`
32
+ 3. **DataGrid** — the table itself with `filterModel` + `onFilterModelChange` controlled state
33
+ 4. **Bulk Action Bar** — a `Flexbox` row shown when `selectionModel.length > 0`, containing a `Pill` with the selection count and action `Button` components
34
+
35
+ ## Features
36
+
37
+ <FeatureTable
38
+ features={[
39
+ {
40
+ feature: 'DataCard aggregation',
41
+ required: true,
42
+ description: (
43
+ <>
44
+ <code>DataCard</code> components showing aggregate counts per field from the full dataset
45
+ </>
46
+ ),
47
+ },
48
+ {
49
+ feature: 'DrilldownItem filter push',
50
+ required: true,
51
+ description: (
52
+ <>
53
+ Clicking a <code>DrilldownItem</code> adds an <code>isAnyOf</code> filter to the DataGrid's{' '}
54
+ <code>filterModel</code>
55
+ </>
56
+ ),
57
+ },
58
+ {
59
+ feature: 'Listbox multi-select',
60
+ required: true,
61
+ description: (
62
+ <>
63
+ <code>DataCard.Listbox</code> with <code>{'selectionMode="multiple"'}</code> and controlled{' '}
64
+ <code>values</code> for multi-value fields
65
+ </>
66
+ ),
67
+ },
68
+ {
69
+ feature: (
70
+ <>
71
+ <code>is</code>/<code>isNot</code> clearing
72
+ </>
73
+ ),
74
+ required: true,
75
+ description: (
76
+ <>
77
+ When filter panel has <code>is</code>/<code>isNot</code> for a card's field, the DataCard selection clears
78
+ </>
79
+ ),
80
+ },
81
+ {
82
+ feature: 'Column definitions',
83
+ required: true,
84
+ description: (
85
+ <>
86
+ <code>GridColDef[]</code> with custom types: <code>rsString</code>, <code>rsNumber</code>,{' '}
87
+ <code>rsSingleSelect</code>, <code>rsMultipleSelect</code>
88
+ </>
89
+ ),
90
+ },
91
+ {
92
+ feature: 'Toolbar',
93
+ required: true,
94
+ description: 'Built-in toolbar with filter, columns, density, export, and quick search controls',
95
+ },
96
+ {
97
+ feature: 'Custom cell renderers',
98
+ required: true,
99
+ description: (
100
+ <>
101
+ <code>renderCell</code> returning <code>TextCell</code>, <code>Pill</code>, <code>Icon</code>,{' '}
102
+ <code>IconButtonLink</code>
103
+ </>
104
+ ),
105
+ },
106
+ {
107
+ feature: 'Boolean toggle',
108
+ required: false,
109
+ description: (
110
+ <>
111
+ <code>DataCard.Body</code> with <code>DrilldownItem</code> <code>onClick</code> for boolean fields
112
+ (active/inactive)
113
+ </>
114
+ ),
115
+ },
116
+ {
117
+ feature: 'Checkbox selection',
118
+ required: false,
119
+ description: (
120
+ <>
121
+ <code>checkboxSelection</code> + <code>rowSelectionModel</code>
122
+ </>
123
+ ),
124
+ },
125
+ { feature: 'Bulk action bar', required: false, description: 'Contextual buttons shown when rows are selected' },
126
+ ]}
127
+ />
128
+
129
+ ### Components
130
+
131
+ {/* prettier-ignore */}
132
+ - <Link href="/table/datagrid" as={RouterLink}>DataGrid</Link> — the table itself (from `@redsift/table`)
133
+ - <Link href="/dashboard/data-card" as={RouterLink}>DataCard</Link> — summary card container with colored border (from `@redsift/dashboard`)
134
+ - <Link href="/dashboard/data-card" as={RouterLink}>DataCard.Header</Link> — card header with icon and title
135
+ - <Link href="/dashboard/data-card" as={RouterLink}>DataCard.Body</Link> — static row container for DataRow or DrilldownItem
136
+ - <Link href="/dashboard/data-card" as={RouterLink}>DataCard.Listbox</Link> — multi-select container synced with DataGrid filters
137
+ - <Link href="/dashboard/drilldown-item" as={RouterLink}>DrilldownItem</Link> — numeric value with legend and filter icon (from `@redsift/dashboard`)
138
+ - <Link href="/layout/flexbox" as={RouterLink}>Flexbox</Link> — page and toolbar layout
139
+ - <Link href="/forms/button" as={RouterLink}>Button</Link> — toolbar and bulk action buttons
140
+ - <Link href="/forms/icon-button-link" as={RouterLink}>IconButtonLink</Link> — action column navigation links
141
+ - <Link href="/data-display/pill" as={RouterLink}>Pill</Link> — status badges in cells and count indicators
142
+ - <Link href="/typography/text" as={RouterLink}>Text</Link> — cell content and labels
143
+ - <Link href="/media-and-icons/icon" as={RouterLink}>Icon</Link> — toolbar icons and cell indicators
144
+
145
+ ## State Management
146
+
147
+ <CodeBlock codeString={`import { useCallback, useMemo, useState } from 'react';
148
+ import { GridFilterModel, GridSelectionModel } from '@mui/x-data-grid-pro';
149
+
150
+ // Filter state — controlled, shared between DataCards and DataGrid
151
+ const [filterModel, setFilterModel] = useState<GridFilterModel>({ items: [] });
152
+
153
+ // Selection state
154
+ const [selectionModel, setSelectionModel] = useState<GridSelectionModel>([]);
155
+
156
+ // Aggregate counts — always computed from the FULL dataset (one-way)
157
+ const counts = useAggregateCounts(allRows);
158
+
159
+ // Derive DataCard selections from filterModel (returns [] for is/isNot)
160
+ const statusSelection = getSelectedFromFilterModel(filterModel, 'status');
161
+ const categorySelection = getSelectedFromFilterModel(filterModel, 'category');
162
+
163
+ // DataCard change handler — always writes isAnyOf filters
164
+ const handleStatusChange = useCallback(
165
+ (values: string[]) => setFilterModel((prev) => updateFilterModel(prev, 'status', values)),
166
+ [],
167
+ );
168
+ `} />
169
+
170
+ ## Filter Helpers
171
+
172
+ <CodeBlock codeString={`// Read selected values for a field — only syncs with isAnyOf.
173
+ // Returns [] for is/isNot operators (clears the DataCard).
174
+ function getSelectedFromFilterModel(filterModel: GridFilterModel, fieldName: string): string[] {
175
+ const item = filterModel.items.find((i) => i.field === fieldName);
176
+ if (!item || item.value === undefined) return [];
177
+ if (item.operator === 'isAnyOf') {
178
+ return Array.isArray(item.value) ? item.value : [item.value];
179
+ }
180
+ return []; // is, isNot, or other → clear card
181
+ }
182
+
183
+ // Write an isAnyOf filter — always creates a fresh item, never preserves
184
+ // an incompatible operator. Empty values removes the filter entirely.
185
+ function updateFilterModel(
186
+ filterModel: GridFilterModel,
187
+ fieldName: string,
188
+ values: string[],
189
+ ): GridFilterModel {
190
+ const items = filterModel.items.filter((i) => i.field !== fieldName);
191
+ if (values.length > 0) {
192
+ items.push({
193
+ field: fieldName,
194
+ id: Math.floor(Math.random() \* 100000),
195
+ operator: 'isAnyOf',
196
+ value: values,
197
+ });
198
+ }
199
+ return { ...filterModel, items };
200
+ }
201
+
202
+ // Boolean filter helper — uses the 'is' operator with 'true'/'false'.
203
+ function updateBooleanFilter(
204
+ filterModel: GridFilterModel,
205
+ fieldName: string,
206
+ value: boolean | null,
207
+ ): GridFilterModel {
208
+ const items = filterModel.items.filter((i) => i.field !== fieldName);
209
+ if (value !== null) {
210
+ items.push({
211
+ field: fieldName,
212
+ id: Math.floor(Math.random() \* 100000),
213
+ operator: 'is',
214
+ value: String(value),
215
+ });
216
+ }
217
+ return { ...filterModel, items };
218
+ }
219
+ `} />
220
+
221
+ ## Data Contract
222
+
223
+ <CodeBlock codeString={`// Row type — same as Datagrid Page. Every row MUST have a unique 'id' field.
224
+ type Row = {
225
+ id: string;
226
+ label: string;
227
+ active: boolean;
228
+ status: 'active' | 'warning' | 'critical' | 'expired' | 'revoked';
229
+ category: 'primary' | 'secondary' | 'tertiary';
230
+ score: number;
231
+ expiry: string;
232
+ tags: string[];
233
+ };
234
+
235
+ // Aggregate counts computed from the full dataset
236
+ type AggregateCounts = {
237
+ status: Record<string, number>;
238
+ category: Record<string, number>;
239
+ activeCount: number;
240
+ inactiveCount: number;
241
+ };
242
+ `} />
243
+
244
+ ## Example
245
+
246
+ The same 27-row dataset as the Datagrid Page, with three DataCards above: **Status** (5 DrilldownItems), **Category** (3 DrilldownItems), and **Active** (boolean toggle with 2 DrilldownItems). Clicking a DrilldownItem adds an `isAnyOf` filter to the grid. The Status and Category cards use `DataCard.Listbox` with controlled `values` for multi-select. The Active card uses `DataCard.Body` with `onClick` toggles. DataCard counts always show the full 27-row totals.
247
+
248
+ <DemoBlock withThemeSwitcher path="patterns/drilldown-datagrid-page/example" />
249
+
250
+ ## Loading State
251
+
252
+ DataCards with `isLoading={true}` (hides headers, shows skeleton placeholders) alongside a DataGrid with `loading={true}` and `rows={[]}`. Use this state while fetching initial data.
253
+
254
+ <DemoBlock withThemeSwitcher path="patterns/drilldown-datagrid-page/with-loading" />
255
+
256
+ ## Empty State
257
+
258
+ DataCards showing zero counts on all DrilldownItems, with the DataGrid displaying a custom `NoRowsOverlay` with "No results match your filters" and guidance to adjust filters.
259
+
260
+ <DemoBlock withThemeSwitcher path="patterns/drilldown-datagrid-page/with-empty-state" />
261
+
262
+ ## Error State
263
+
264
+ An error banner (`role="alert"`) above the DataCards and DataGrid with a retry button. DataCards switch to loading state during retry. The page layout remains intact.
265
+
266
+ <DemoBlock withThemeSwitcher path="patterns/drilldown-datagrid-page/with-error" />
267
+
268
+ ## Implementation Checklist
269
+
270
+ 1. **Define your row type** — Create a TypeScript `type Row` per the Data Contract section. Every row must have a unique `id` field.
271
+ 2. **Compute aggregate counts** — Write a `useAggregateCounts(allRows)` hook that counts occurrences per field value from the full dataset. This never recalculates when filters change.
272
+ 3. **Build DataCards** — Create a `DataCard` for each summarized field with `DataCard.Header` (icon + title) and either `DataCard.Listbox` (multi-select) or `DataCard.Body` (boolean toggle) containing `DrilldownItem` children.
273
+ 4. **Write filter helpers** — Implement `getSelectedFromFilterModel` (reads `isAnyOf` values, returns `[]` for `is`/`isNot`) and `updateFilterModel` (always writes `isAnyOf`, removes when empty).
274
+ 5. **Wire DataCard handlers** — Connect `DataCard.Listbox` `onChange` to `updateFilterModel`. For boolean cards, connect `DrilldownItem` `onClick` to `updateBooleanFilter`.
275
+ 6. **Render the DataGrid** — Pass `filterModel` and `onFilterModelChange={setFilterModel}` so the filter panel stays synced with DataCard selections.
276
+ 7. **Handle edge cases** — Add loading (DataCards `isLoading` + DataGrid `loading`), empty (zero counts + `NoRowsOverlay`), and error states (banner + retry).
277
+
278
+ ## Keyboard & Accessibility
279
+
280
+ - **DrilldownItem** renders as a `<button>` — focusable via **Tab**, activates with **Enter** or **Space**
281
+ - **DataCard.Listbox** supports **Arrow Up/Down** to navigate between items and **Space** to toggle selection
282
+ - When a DrilldownItem is selected, it receives `aria-selected="true"` from the Listbox
283
+ - DataGrid keyboard navigation works unchanged (arrows, Page Up/Down, Space for checkboxes)
284
+ - Error banners use `role="alert"` and `aria-live="polite"` for screen reader announcement
285
+
286
+ ## Related Patterns
287
+
288
+ {/* prettier-ignore */}
289
+ - <Link href="/patterns/crossfiltered-datagrid-page" as={RouterLink}>Cross-filtered Datagrid Page</Link> — use when DataCard counts should recalculate as filters change (two-way sync)
290
+ - <Link href="/patterns/datagrid-page" as={RouterLink}>Datagrid Page</Link> — use when no summary cards are needed
291
+ - <Link href="/patterns/server-datagrid-page" as={RouterLink}>Server Datagrid Page</Link> — use when the dataset is too large to load entirely
@@ -0,0 +1,301 @@
1
+ import { Link } from '@redsift/design-system';
2
+ import { DemoBlock } from '../../components/DemoBlock';
3
+ import { CodeBlock } from '../../components/CodeBlock';
4
+ import { FeatureTable } from '../../components/FeatureTable';
5
+ import { RouterLink } from '../../components/RouterLink';
6
+
7
+ ## Introduction
8
+
9
+ The Server Datagrid Page is a variant of the standard [Datagrid Page](/patterns/datagrid-page) where **all filtering, sorting, and pagination are handled server-side**. Instead of loading every row into the browser, only the current page of results is fetched from the server, and every user interaction (changing page, sorting a column, adding a filter) triggers a new request.
10
+
11
+ Use this pattern when the dataset is too large to load entirely — thousands or millions of records — or when the canonical sort/filter logic must live on the backend (e.g. ElasticSearch queries, SQL `ORDER BY`).
12
+
13
+ The DataGrid is configured with `paginationMode="server"`, `sortingMode="server"`, and `filterMode="server"`. A controlled `rowCount` tells the grid the total number of matching records so it can render the correct number of pagination pages. The `loading` prop activates the built-in loading overlay during fetches.
14
+
15
+ ## When to Use
16
+
17
+ - The dataset has **thousands or millions of records** that cannot be loaded into the browser
18
+ - Filtering, sorting, or pagination logic must live on the **backend** (ElasticSearch, SQL, API)
19
+ - You need the server to return the **total matching count** alongside the current page of results
20
+ - Filter changes should be **debounced** to avoid excessive requests during rapid typing
21
+
22
+ When **not** to use:
23
+
24
+ - The dataset is small enough to load entirely (under ~1,000 rows) — use [Datagrid Page](/patterns/datagrid-page) instead
25
+ - All data is available upfront and filtering can happen in the browser — use [Datagrid Page](/patterns/datagrid-page) instead
26
+
27
+ ## Anatomy
28
+
29
+ 1. **Toolbar** — `Flexbox` wrapping MUI's `GridToolbarContainer` with `GridToolbarFilterButton`, `GridToolbarColumnsButton`, `GridToolbarDensitySelector`, `GridToolbarExport`, and `GridToolbarQuickFilter`
30
+ 2. **DataGrid** — the table configured with `paginationMode="server"`, `sortingMode="server"`, `filterMode="server"`, `loading`, and `rowCount`
31
+ 3. **Bulk Action Bar** — a `Flexbox` row shown when `selectionModel.length > 0`, containing a `Pill` with the selection count and action `Button` components
32
+
33
+ ## Features
34
+
35
+ <FeatureTable
36
+ features={[
37
+ {
38
+ feature: 'Server-side pagination',
39
+ required: true,
40
+ description: (
41
+ <>
42
+ <code>{'paginationMode="server"'}</code> with controlled <code>page</code>, <code>pageSize</code>,{' '}
43
+ <code>rowCount</code>
44
+ </>
45
+ ),
46
+ },
47
+ {
48
+ feature: 'Server-side sorting',
49
+ required: true,
50
+ description: (
51
+ <>
52
+ <code>{'sortingMode="server"'}</code> with controlled <code>sortModel</code> + <code>onSortModelChange</code>
53
+ </>
54
+ ),
55
+ },
56
+ {
57
+ feature: 'Server-side filtering',
58
+ required: true,
59
+ description: (
60
+ <>
61
+ <code>{'filterMode="server"'}</code> with controlled <code>filterModel</code> +{' '}
62
+ <code>onFilterModelChange</code>
63
+ </>
64
+ ),
65
+ },
66
+ {
67
+ feature: 'Loading overlay',
68
+ required: true,
69
+ description: (
70
+ <>
71
+ <code>loading</code> prop set to <code>true</code> during fetches
72
+ </>
73
+ ),
74
+ },
75
+ {
76
+ feature: 'Row count',
77
+ required: true,
78
+ description: (
79
+ <>
80
+ <code>rowCount</code> prop set to total matching records from the server response
81
+ </>
82
+ ),
83
+ },
84
+ {
85
+ feature: 'Debounced filter changes',
86
+ required: true,
87
+ description: (
88
+ <>
89
+ 300ms debounce on <code>onFilterModelChange</code> to avoid excessive requests
90
+ </>
91
+ ),
92
+ },
93
+ {
94
+ feature: 'Column definitions',
95
+ required: true,
96
+ description: (
97
+ <>
98
+ <code>GridColDef[]</code> with custom types: <code>rsString</code>, <code>rsNumber</code>,{' '}
99
+ <code>rsSingleSelect</code>, <code>rsMultipleSelect</code>
100
+ </>
101
+ ),
102
+ },
103
+ {
104
+ feature: 'Toolbar',
105
+ required: true,
106
+ description: 'Built-in toolbar with filter, columns, density, export, and quick search',
107
+ },
108
+ {
109
+ feature: 'Custom cell renderers',
110
+ required: true,
111
+ description: (
112
+ <>
113
+ <code>renderCell</code> returning <code>TextCell</code>, <code>Pill</code>, <code>Icon</code>,{' '}
114
+ <code>IconButtonLink</code>
115
+ </>
116
+ ),
117
+ },
118
+ {
119
+ feature: 'Checkbox selection',
120
+ required: false,
121
+ description: (
122
+ <>
123
+ <code>checkboxSelection</code> + <code>rowSelectionModel</code>
124
+ </>
125
+ ),
126
+ },
127
+ { feature: 'Bulk action bar', required: false, description: 'Contextual buttons shown when rows are selected' },
128
+ {
129
+ feature: 'Quick search',
130
+ required: false,
131
+ description: (
132
+ <>
133
+ <code>GridToolbarQuickFilter</code> with server-side text search
134
+ </>
135
+ ),
136
+ },
137
+ ]}
138
+ />
139
+
140
+ ### Components
141
+
142
+ {/* prettier-ignore */}
143
+ - <Link href="/table/datagrid" as={RouterLink}>DataGrid</Link> — the table itself (from `@redsift/table`)
144
+ - <Link href="/layout/flexbox" as={RouterLink}>Flexbox</Link> — page and toolbar layout
145
+ - <Link href="/forms/button" as={RouterLink}>Button</Link> — toolbar and bulk action buttons
146
+ - <Link href="/forms/icon-button" as={RouterLink}>IconButton</Link> — toolbar icon controls (columns, filters, density, export, reset, save)
147
+ - <Link href="/forms/icon-button-link" as={RouterLink}>IconButtonLink</Link> — action column navigation links
148
+ - <Link href="/data-display/pill" as={RouterLink}>Pill</Link> — status badges in cells and count indicators
149
+ - <Link href="/typography/text" as={RouterLink}>Text</Link> — cell content, labels, and search placeholder
150
+ - <Link href="/media-and-icons/icon" as={RouterLink}>Icon</Link> — toolbar icons and cell indicators
151
+ - <Link href="/data-display/badge" as={RouterLink}>Badge</Link> — active filter count on the filter button
152
+
153
+ ## State Management
154
+
155
+ <CodeBlock codeString={`import { useCallback, useEffect, useRef, useState } from 'react';
156
+ import { GridFilterModel, GridSelectionModel, GridSortModel } from '@mui/x-data-grid-pro';
157
+
158
+ // Data state — rows returned from the server for the current page
159
+ const [rows, setRows] = useState<Row[]>([]);
160
+ const [totalRows, setTotalRows] = useState(0);
161
+ const [loading, setLoading] = useState(true);
162
+
163
+ // Pagination state
164
+ const [page, setPage] = useState(0);
165
+ const [pageSize, setPageSize] = useState(10);
166
+
167
+ // Sort & filter state (controlled — sent to server on change)
168
+ const [sortModel, setSortModel] = useState<GridSortModel>([]);
169
+ const [filterModel, setFilterModel] = useState<GridFilterModel>({ items: [] });
170
+
171
+ // Selection state
172
+ const [selectionModel, setSelectionModel] = useState<GridSelectionModel>([]);
173
+
174
+ // Quick search
175
+ const [quickFilterText, setQuickFilterText] = useState('');
176
+
177
+ // Debounce ref for filter changes
178
+ const debounceRef = useRef<ReturnType<typeof setTimeout>>();
179
+
180
+ // Fetch data whenever page, pageSize, sortModel, or filterModel changes
181
+ const fetchData = useCallback(async () => {
182
+ setLoading(true);
183
+ const result = await yourFetchFunction({ page, pageSize, sortModel, filterModel, quickFilterText });
184
+ setRows(result.rows);
185
+ setTotalRows(result.totalRows);
186
+ setLoading(false);
187
+ }, [page, pageSize, sortModel, filterModel, quickFilterText]);
188
+
189
+ useEffect(() => { fetchData(); }, [fetchData]);
190
+
191
+ // Debounced filter handler (300ms)
192
+ const handleFilterChange = useCallback((model: GridFilterModel) => {
193
+ clearTimeout(debounceRef.current);
194
+ debounceRef.current = setTimeout(() => {
195
+ setFilterModel(model);
196
+ setPage(0); // Reset to first page on filter change
197
+ }, 300);
198
+ }, []);
199
+ `} />
200
+
201
+ ## Data Contract
202
+
203
+ <CodeBlock codeString={`// Row type — adapt fields to your domain
204
+ // Every row MUST have a unique 'id' field (string or number)
205
+ type Row = {
206
+ id: string;
207
+ label: string;
208
+ active: boolean;
209
+ status: 'active' | 'warning' | 'critical' | 'expired';
210
+ category: string;
211
+ score: number;
212
+ expiry: string; // ISO 8601 date string
213
+ tags: string[];
214
+ };
215
+
216
+ // Fetch function parameters — sent to the server on every interaction
217
+ type FetchParams = {
218
+ page: number; // 0-based page index
219
+ pageSize: number; // rows per page (10, 25, 50)
220
+ sortModel: GridSortModel;
221
+ filterModel: GridFilterModel;
222
+ quickFilterText: string;
223
+ };
224
+
225
+ // Fetch function response — returned by the server
226
+ type FetchResult = {
227
+ rows: Row[]; // only the current page of results
228
+ totalRows: number; // total matching records (for pagination)
229
+ };
230
+
231
+ // Fetch function signature
232
+ async function fetchData(params: FetchParams): Promise<FetchResult> {
233
+ // Implement: translate params to your API/ElasticSearch query
234
+ // Return: { rows, totalRows }
235
+ }
236
+ `} />
237
+
238
+ ## Example
239
+
240
+ The same 27-row dataset as the client-side Datagrid Page, but every interaction is routed through a mock `fakeFetch` function that simulates a 400 ms network round-trip. The mock applies filtering (string, number, date, single-select, multi-select, and boolean operators), sorting, and pagination server-side, then returns only the requested page slice alongside the total matching row count. Filter changes are debounced at 300 ms so rapid typing doesn't fire excessive requests.
241
+
242
+ <DemoBlock withThemeSwitcher path="patterns/server-datagrid-page/example" />
243
+
244
+ ## Loading State
245
+
246
+ The initial loading state before the first fetch completes — DataGrid rendered with `loading={true}` and `rows={[]}`. The built-in loading overlay displays a skeleton while the server responds.
247
+
248
+ <DemoBlock withThemeSwitcher path="patterns/server-datagrid-page/with-loading" />
249
+
250
+ ## Empty State
251
+
252
+ Server returns `{ rows: [], totalRows: 0 }` after filtering yields no results. The DataGrid shows a custom `NoRowsOverlay` with "No results match your filters" and a button to clear all filters.
253
+
254
+ <DemoBlock withThemeSwitcher path="patterns/server-datagrid-page/with-empty-state" />
255
+
256
+ ## Error State
257
+
258
+ The fetch function rejects with an error. The component shows an error banner above the DataGrid with a retry button, keeping the page layout intact.
259
+
260
+ <DemoBlock withThemeSwitcher path="patterns/server-datagrid-page/with-error" />
261
+
262
+ ## Implementation Checklist
263
+
264
+ 1. **Define your row type** — Create a TypeScript `type Row` per the Data Contract section. Every row must have a unique `id` field.
265
+ 2. **Create your fetch function** — Implement an async function matching the `FetchParams → FetchResult` contract. This is where you call your API or ElasticSearch. If using React Query, wrap it in a `useQuery` hook with `[page, pageSize, sortModel, filterModel, quickFilterText]` as the query key.
266
+ 3. **Define column definitions** — Create a `GridColDef[]` array. Use RS custom column types (`rsString`, `rsNumber`, `rsSingleSelect`, `rsMultipleSelect`) for built-in filter operators. Add `renderCell` for rich content.
267
+ 4. **Create the toolbar** — Build a `CustomToolbar` component using `GridToolbarContainer` with filter, columns, density, export, and quick search controls.
268
+ 5. **Set up state** — Add all hooks from the State Management section: `rows`, `totalRows`, `loading`, `page`, `pageSize`, `sortModel`, `filterModel`, `selectionModel`, `quickFilterText`, and the debounce ref.
269
+ 6. **Wire the fetch effect** — Use `useEffect` to call your fetch function whenever `page`, `pageSize`, `sortModel`, `filterModel`, or `quickFilterText` changes.
270
+ 7. **Configure the DataGrid** — Pass `paginationMode="server"`, `sortingMode="server"`, `filterMode="server"`, `loading`, `rowCount={totalRows}`, and all controlled state handlers (`onPageChange`, `onPageSizeChange`, `onSortModelChange`, `onFilterModelChange`).
271
+ 8. **Debounce filter changes** — Use a 300ms `setTimeout` in `onFilterModelChange` to avoid excessive requests during rapid typing. Reset to page 0 on filter change.
272
+ 9. **Add bulk action bar** — Conditionally render when `selectionModel.length > 0`.
273
+ 10. **Handle edge cases** — Add loading state (`loading` prop), empty state (custom `NoRowsOverlay`), and error state (conditional render with retry).
274
+ 11. **Verify** — Test page navigation, sort toggling, filter operators, debounced quick search, loading overlay, empty results, and error recovery.
275
+
276
+ ## Keyboard & Accessibility
277
+
278
+ The MUI DataGrid provides extensive built-in keyboard support:
279
+
280
+ - **Arrow keys** navigate between cells
281
+ - **Page Up / Page Down** move between pages
282
+ - **Home / End** jump to first/last cell in a row
283
+ - **Enter** activates cell editing or expands a row
284
+ - **Space** toggles checkbox selection on the focused row
285
+ - **Tab** moves focus between the toolbar and the grid
286
+
287
+ As an implementor, ensure:
288
+
289
+ - Every `IconButtonLink` in the actions column has an accessible `aria-label` (e.g. `aria-label="View details"`)
290
+ - Status `Pill` components use meaningful color contrast — don't rely on color alone
291
+ - Bulk action buttons have descriptive labels (e.g. "Delete 3 selected" instead of just "Delete")
292
+ - The loading state is announced to screen readers (`aria-busy="true"` on the container)
293
+ - Error messages are in a live region (`aria-live="polite"`) so screen readers announce them
294
+ - Custom `NoRowsOverlay` content is announced to screen readers
295
+
296
+ ## Related Patterns
297
+
298
+ {/* prettier-ignore */}
299
+ - <Link href="/patterns/datagrid-page" as={RouterLink}>Datagrid Page</Link> — use when all data can be loaded upfront and filtering/sorting can happen client-side
300
+ - <Link href="/patterns/drilldown-datagrid-page" as={RouterLink}>Drilldown Datagrid Page</Link> — use when you want summary DataCards above the grid with one-way filter push
301
+ - <Link href="/patterns/crossfiltered-datagrid-page" as={RouterLink}>Cross-filtered Datagrid Page</Link> — use when DataCard counts should recalculate as filters change (two-way sync)