@12-apps/ui 1.0.0 → 1.3.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.
Files changed (448) hide show
  1. package/components-catalog.md +14 -0
  2. package/package.json +42 -27
  3. package/src/__tests__/data-grid.test.tsx +125 -0
  4. package/src/__tests__/package-wiring.test.ts +12 -8
  5. package/src/__tests__/social-login-button.test.tsx +5 -5
  6. package/src/__tests__/viewport.test.ts +50 -0
  7. package/src/charts/index.ts +8 -0
  8. package/src/components/data-display/Alert/Alert.md +1 -1
  9. package/src/components/data-display/Alert/Alert.stories.tsx +5 -7
  10. package/src/components/data-display/Alert/Alert.styles.tsx +255 -0
  11. package/src/components/data-display/Alert/Alert.test.stories.tsx +11 -13
  12. package/src/components/data-display/Alert/Alert.tsx +135 -328
  13. package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +3 -1
  14. package/src/components/data-display/AlertDialog/AlertDialog.tsx +191 -78
  15. package/src/components/data-display/AlertDialog/AlertDialog.types.ts +8 -1
  16. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +5 -5
  17. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +44 -35
  18. package/src/components/data-display/Avatar/Avatar.stories.tsx +4 -1
  19. package/src/components/data-display/Avatar/Avatar.test.stories.tsx +5 -3
  20. package/src/components/data-display/Avatar/Avatar.tsx +154 -450
  21. package/src/components/data-display/Avatar/Avatar.view.tsx +396 -0
  22. package/src/components/data-display/Badge/Badge.animations.ts +67 -0
  23. package/src/components/data-display/Badge/Badge.helpers.ts +74 -0
  24. package/src/components/data-display/Badge/Badge.stories.tsx +14 -16
  25. package/src/components/data-display/Badge/Badge.styles.ts +261 -0
  26. package/src/components/data-display/Badge/Badge.test.stories.tsx +32 -31
  27. package/src/components/data-display/Badge/Badge.tsx +151 -557
  28. package/src/components/data-display/Badge/Badge.variants.ts +106 -0
  29. package/src/components/data-display/Badge/BadgeContent.tsx +107 -0
  30. package/src/components/data-display/Banner/Banner.stories.tsx +5 -1
  31. package/src/components/data-display/Banner/Banner.styles.ts +120 -0
  32. package/src/components/data-display/Banner/Banner.tsx +179 -224
  33. package/src/components/data-display/Chart/Chart.md +48 -1
  34. package/src/components/data-display/Chart/Chart.tsx +82 -500
  35. package/src/components/data-display/Chart/Chart.types.ts +2 -0
  36. package/src/components/data-display/Chart/ChartComposables.tsx +121 -0
  37. package/src/components/data-display/Chart/ChartSpec.types.ts +70 -0
  38. package/src/components/data-display/Chart/SpecChart.stories.tsx +150 -0
  39. package/src/components/data-display/Chart/SpecChart.test.stories.tsx +56 -0
  40. package/src/components/data-display/Chart/SpecChart.tsx +53 -0
  41. package/src/components/data-display/Chart/__tests__/SpecChart.test.tsx +60 -0
  42. package/src/components/data-display/Chart/__tests__/chart-format.test.ts +43 -0
  43. package/src/components/data-display/Chart/__tests__/chart-spec.test.ts +119 -0
  44. package/src/components/data-display/Chart/chart-format.ts +26 -0
  45. package/src/components/data-display/Chart/chart-internals.ts +148 -0
  46. package/src/components/data-display/Chart/chart-renderers.tsx +338 -0
  47. package/src/components/data-display/Chart/chart-spec-registry.ts +74 -0
  48. package/src/components/data-display/Chart/chart-spec.ts +171 -0
  49. package/src/components/data-display/Chart/index.ts +30 -0
  50. package/src/components/data-display/Chip/Chip.helpers.tsx +131 -0
  51. package/src/components/data-display/Chip/Chip.stories.tsx +2 -1
  52. package/src/components/data-display/Chip/Chip.test.stories.tsx +11 -8
  53. package/src/components/data-display/Chip/Chip.tsx +31 -94
  54. package/src/components/data-display/DataGrid/DataGrid.body.tsx +141 -0
  55. package/src/components/data-display/DataGrid/DataGrid.cells.tsx +238 -0
  56. package/src/components/data-display/DataGrid/DataGrid.model.ts +315 -0
  57. package/src/components/data-display/DataGrid/DataGrid.rows.ts +112 -0
  58. package/src/components/data-display/DataGrid/DataGrid.states.tsx +102 -0
  59. package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +6 -12
  60. package/src/components/data-display/DataGrid/DataGrid.tsx +129 -725
  61. package/src/components/data-display/DataViews/DataViewsGrid.tsx +187 -0
  62. package/src/components/data-display/DataViews/DataViewsTableBase.tsx +400 -0
  63. package/src/components/data-display/DataViews/ManageViewsDialog.tsx +140 -0
  64. package/src/components/data-display/DataViews/SaveViewModal.tsx +289 -0
  65. package/src/components/data-display/DataViews/__tests__/DataViewsGrid.test.tsx +271 -0
  66. package/src/components/data-display/DataViews/__tests__/DataViewsTableBase.test.tsx +134 -0
  67. package/src/components/data-display/DataViews/__tests__/SaveViewModal.test.tsx +83 -0
  68. package/src/components/data-display/DataViews/__tests__/data-views-filter.test.ts +115 -0
  69. package/src/components/data-display/DataViews/__tests__/data-views-preview.test.ts +53 -0
  70. package/src/components/data-display/DataViews/__tests__/range-pill.test.tsx +114 -0
  71. package/src/components/data-display/DataViews/base-card.stories.tsx +136 -0
  72. package/src/components/data-display/DataViews/base-card.tsx +278 -0
  73. package/src/components/data-display/DataViews/data-views-filter-panel.tsx +184 -0
  74. package/src/components/data-display/DataViews/data-views-filter.ts +138 -0
  75. package/src/components/data-display/DataViews/data-views-grid-helpers.tsx +248 -0
  76. package/src/components/data-display/DataViews/data-views-grid-parts.tsx +400 -0
  77. package/src/components/data-display/DataViews/data-views-inline-bar.tsx +257 -0
  78. package/src/components/data-display/DataViews/data-views-layout-context.tsx +176 -0
  79. package/src/components/data-display/DataViews/data-views-pagination.tsx +31 -0
  80. package/src/components/data-display/DataViews/data-views-preview.ts +53 -0
  81. package/src/components/data-display/DataViews/data-views-range-pill.tsx +277 -0
  82. package/src/components/data-display/DataViews/data-views-table-parts.tsx +150 -0
  83. package/src/components/data-display/DataViews/data-views-toolbar.tsx +149 -0
  84. package/src/components/data-display/DataViews/data-views-types.ts +312 -0
  85. package/src/components/data-display/DataViews/data-views-view-mutations.ts +68 -0
  86. package/src/components/data-display/DataViews/index.ts +33 -0
  87. package/src/components/data-display/DataViews/use-data-views-state.ts +349 -0
  88. package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.hooks.ts +104 -0
  89. package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.tsx +68 -118
  90. package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +1 -1
  91. package/src/components/data-display/DescriptionItem/DescriptionItem.tsx +52 -34
  92. package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +6 -6
  93. package/src/components/data-display/EmptyState/EmptyState.tsx +218 -135
  94. package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +2 -2
  95. package/src/components/data-display/ErrorState/ErrorState.tsx +83 -55
  96. package/src/components/data-display/HoverCard/HoverCard.stories.tsx +3 -1
  97. package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +5 -5
  98. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.hooks.ts +99 -0
  99. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.styles.ts +87 -0
  100. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.tsx +28 -155
  101. package/src/components/data-display/LazyImage/LazyImage.hooks.ts +203 -0
  102. package/src/components/data-display/LazyImage/LazyImage.md +1 -1
  103. package/src/components/data-display/LazyImage/LazyImage.stories.tsx +3 -3
  104. package/src/components/data-display/LazyImage/LazyImage.tsx +144 -243
  105. package/src/components/data-display/Lightbox/Lightbox.constants.ts +53 -0
  106. package/src/components/data-display/Lightbox/Lightbox.helpers.ts +39 -0
  107. package/src/components/data-display/Lightbox/Lightbox.hooks.ts +259 -0
  108. package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +28 -35
  109. package/src/components/data-display/Lightbox/Lightbox.tsx +98 -658
  110. package/src/components/data-display/Lightbox/LightboxControls.tsx +152 -0
  111. package/src/components/data-display/Lightbox/LightboxCurrentMedia.tsx +50 -0
  112. package/src/components/data-display/Lightbox/LightboxMedia.tsx +76 -0
  113. package/src/components/data-display/Lightbox/LightboxOverlay.tsx +83 -0
  114. package/src/components/data-display/Lightbox/LightboxStage.tsx +97 -0
  115. package/src/components/data-display/Lightbox/LightboxThumbnails.tsx +83 -0
  116. package/src/components/data-display/Lightbox/useLightbox.ts +80 -0
  117. package/src/components/data-display/LoadingState/LoadingState.helpers.ts +52 -0
  118. package/src/components/data-display/LoadingState/LoadingState.tsx +117 -87
  119. package/src/components/data-display/MapPreview/MapControlBar.tsx +116 -0
  120. package/src/components/data-display/MapPreview/MapDecorations.tsx +155 -0
  121. package/src/components/data-display/MapPreview/MapLayers.tsx +70 -0
  122. package/src/components/data-display/MapPreview/MapMarkers.tsx +84 -0
  123. package/src/components/data-display/MapPreview/MapOverlays.tsx +68 -0
  124. package/src/components/data-display/MapPreview/MapPreview.constants.ts +19 -0
  125. package/src/components/data-display/MapPreview/MapPreview.helpers.ts +63 -0
  126. package/src/components/data-display/MapPreview/MapPreview.hooks.ts +265 -0
  127. package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +14 -10
  128. package/src/components/data-display/MapPreview/MapPreview.tsx +52 -882
  129. package/src/components/data-display/MapPreview/MapPreview.types.ts +8 -1
  130. package/src/components/data-display/MapPreview/MapSearchBar.tsx +64 -0
  131. package/src/components/data-display/MapPreview/MapTiles.tsx +70 -0
  132. package/src/components/data-display/MapPreview/StaticMapView.tsx +63 -0
  133. package/src/components/data-display/MapPreview/mapProjection.ts +52 -0
  134. package/src/components/data-display/MapPreview/mapSurface.ts +37 -0
  135. package/src/components/data-display/MapPreview/useMapPreview.ts +62 -0
  136. package/src/components/data-display/Popover/Popover.test.stories.tsx +13 -19
  137. package/src/components/data-display/Progress/Progress.helpers.ts +37 -0
  138. package/src/components/data-display/Progress/Progress.styles.ts +101 -0
  139. package/src/components/data-display/Progress/Progress.tsx +185 -213
  140. package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +1 -1
  141. package/src/components/data-display/SectionOnboarding/SectionOnboarding.tsx +1 -1
  142. package/src/components/data-display/StatCard/StatCard.md +117 -0
  143. package/src/components/data-display/StatCard/StatCard.stories.tsx +167 -0
  144. package/src/components/data-display/StatCard/StatCard.test.stories.tsx +240 -0
  145. package/src/components/data-display/StatCard/StatCard.tsx +236 -0
  146. package/src/components/data-display/StatCard/StatCard.types.ts +62 -0
  147. package/src/components/data-display/StatCard/index.ts +7 -0
  148. package/src/components/data-display/Stepper/Stepper.test.stories.tsx +12 -19
  149. package/src/components/data-display/Stepper/Stepper.tsx +1 -1
  150. package/src/components/data-display/Table/Table.helpers.ts +31 -0
  151. package/src/components/data-display/Table/Table.hooks.ts +161 -0
  152. package/src/components/data-display/Table/Table.stories.tsx +3 -1
  153. package/src/components/data-display/Table/Table.styles.ts +223 -0
  154. package/src/components/data-display/Table/Table.test.stories.tsx +4 -4
  155. package/src/components/data-display/Table/Table.tsx +291 -748
  156. package/src/components/data-display/Table/TableParts.hooks.tsx +112 -0
  157. package/src/components/data-display/Table/TableParts.tsx +298 -0
  158. package/src/components/data-display/Table/TableStates.tsx +21 -0
  159. package/src/components/data-display/Timeline/Timeline.stories.tsx +10 -12
  160. package/src/components/data-display/Timeline/Timeline.test.stories.tsx +30 -44
  161. package/src/components/data-display/Timeline/Timeline.tsx +23 -284
  162. package/src/components/data-display/Timeline/TimelineRow.tsx +346 -0
  163. package/src/components/data-display/TimingDiagram/TimingDiagram.tsx +38 -311
  164. package/src/components/data-display/TimingDiagram/TimingDiagram.views.tsx +346 -0
  165. package/src/components/data-display/Tooltip/Tooltip.stories.tsx +4 -1
  166. package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +44 -58
  167. package/src/components/data-display/Tooltip/Tooltip.tsx +67 -62
  168. package/src/components/data-display/index.ts +1 -0
  169. package/src/components/feedback/ConfirmAction/ConfirmAction.hooks.ts +96 -0
  170. package/src/components/feedback/ConfirmAction/ConfirmAction.md +140 -0
  171. package/src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx +154 -0
  172. package/src/components/feedback/ConfirmAction/ConfirmAction.stories.tsx +146 -0
  173. package/src/components/feedback/ConfirmAction/ConfirmAction.test.stories.tsx +114 -0
  174. package/src/components/feedback/ConfirmAction/ConfirmAction.tsx +46 -0
  175. package/src/components/feedback/ConfirmAction/ConfirmAction.types.ts +111 -0
  176. package/src/components/feedback/ConfirmAction/ConfirmButton.tsx +18 -0
  177. package/src/components/feedback/ConfirmAction/__tests__/confirm-action.test.tsx +288 -0
  178. package/src/components/feedback/ConfirmAction/index.ts +16 -0
  179. package/src/components/feedback/ConfirmAction/with-confirmation.tsx +112 -0
  180. package/src/components/feedback/Dialog/Dialog.styles.ts +4 -2
  181. package/src/components/feedback/Dialog/Dialog.tsx +26 -1
  182. package/src/components/feedback/Dialog/__tests__/dialog-content-spacing.test.tsx +66 -0
  183. package/src/components/feedback/Modal/Modal.md +1 -1
  184. package/src/components/feedback/Modal/Modal.stories.tsx +2 -2
  185. package/src/components/feedback/Sonner/Sonner.tsx +104 -288
  186. package/src/components/feedback/Sonner/Sonner.types.ts +7 -0
  187. package/src/components/feedback/Sonner/SonnerToast.tsx +216 -0
  188. package/src/components/feedback/StackedModal/StackedModal.hooks.ts +226 -0
  189. package/src/components/feedback/StackedModal/StackedModal.parts.tsx +311 -0
  190. package/src/components/feedback/StackedModal/StackedModal.stories.tsx +13 -15
  191. package/src/components/feedback/StackedModal/StackedModal.styles.ts +193 -0
  192. package/src/components/feedback/StackedModal/StackedModal.tsx +100 -619
  193. package/src/components/feedback/StackedModal/StackedModal.types.ts +17 -3
  194. package/src/components/feedback/StackedModal/__tests__/stacked-modal-viewport.test.tsx +104 -0
  195. package/src/components/feedback/StackedModal/modal-stack.tsx +119 -0
  196. package/src/components/feedback/Toast/Toast.tsx +92 -81
  197. package/src/components/feedback/TutorialOverlay/TutorialOverlay.geometry.ts +65 -0
  198. package/src/components/feedback/TutorialOverlay/TutorialOverlay.hooks.ts +185 -0
  199. package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +2 -2
  200. package/src/components/feedback/TutorialOverlay/TutorialOverlay.tsx +156 -414
  201. package/src/components/feedback/TutorialOverlay/TutorialStepBody.tsx +255 -0
  202. package/src/components/feedback/index.ts +1 -0
  203. package/src/components/form/AddressAutocomplete/AddressAutocomplete.helpers.ts +58 -0
  204. package/src/components/form/AddressAutocomplete/AddressAutocomplete.hooks.ts +93 -0
  205. package/src/components/form/AddressAutocomplete/AddressAutocomplete.mapper.ts +57 -0
  206. package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +4 -2
  207. package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +20 -16
  208. package/src/components/form/AddressAutocomplete/AddressAutocomplete.tsx +26 -829
  209. package/src/components/form/AddressAutocomplete/AddressAutocomplete.types.ts +11 -0
  210. package/src/components/form/AddressAutocomplete/AddressAutocompleteField.tsx +86 -0
  211. package/src/components/form/AddressAutocomplete/AddressFallbackField.tsx +64 -0
  212. package/src/components/form/AddressAutocomplete/AddressInputField.tsx +80 -0
  213. package/src/components/form/AddressAutocomplete/AddressOption.tsx +83 -0
  214. package/src/components/form/AddressAutocomplete/AddressSuggestionsPaper.tsx +24 -0
  215. package/src/components/form/AddressAutocomplete/AddressTextField.tsx +43 -0
  216. package/src/components/form/AddressAutocomplete/mockPlaces.ts +208 -0
  217. package/src/components/form/AddressAutocomplete/useAddressPredictions.ts +136 -0
  218. package/src/components/form/AddressAutocomplete/useAddressSelection.ts +113 -0
  219. package/src/components/form/AddressAutocomplete/useCurrentLocation.ts +116 -0
  220. package/src/components/form/AddressAutocomplete/useGoogleMapsAPI.ts +75 -0
  221. package/src/components/form/Button/Button.helpers.ts +36 -0
  222. package/src/components/form/Button/Button.stories.tsx +4 -1
  223. package/src/components/form/Button/Button.styles.ts +225 -0
  224. package/src/components/form/Button/Button.test.stories.tsx +25 -21
  225. package/src/components/form/Button/Button.tsx +45 -252
  226. package/src/components/form/Calendar/Calendar.dates.ts +100 -0
  227. package/src/components/form/Calendar/Calendar.helpers.ts +32 -0
  228. package/src/components/form/Calendar/Calendar.hooks.ts +278 -0
  229. package/src/components/form/Calendar/Calendar.keyboard.ts +32 -0
  230. package/src/components/form/Calendar/Calendar.selection.ts +156 -0
  231. package/src/components/form/Calendar/Calendar.test.stories.tsx +33 -34
  232. package/src/components/form/Calendar/Calendar.tsx +63 -551
  233. package/src/components/form/Calendar/Calendar.types.ts +16 -0
  234. package/src/components/form/Calendar/CalendarDay.tsx +127 -0
  235. package/src/components/form/Calendar/CalendarDayCell.tsx +52 -0
  236. package/src/components/form/Calendar/CalendarMonth.tsx +128 -0
  237. package/src/components/form/CepField/CepField.md +138 -0
  238. package/src/components/form/CepField/CepField.stories.tsx +138 -0
  239. package/src/components/form/CepField/CepField.test.stories.tsx +281 -0
  240. package/src/components/form/CepField/CepField.tsx +211 -0
  241. package/src/components/form/CepField/CepField.types.ts +73 -0
  242. package/src/components/form/CepField/index.ts +2 -0
  243. package/src/components/form/Checkbox/Checkbox.helpers.ts +28 -0
  244. package/src/components/form/Checkbox/Checkbox.tsx +19 -16
  245. package/src/components/form/CodeEditor/CodeEditor.helpers.ts +45 -0
  246. package/src/components/form/CodeEditor/CodeEditor.hooks.ts +104 -0
  247. package/src/components/form/CodeEditor/CodeEditor.monaco.ts +109 -0
  248. package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +17 -18
  249. package/src/components/form/CodeEditor/CodeEditor.tsx +81 -330
  250. package/src/components/form/CodeEditor/CodeEditorToolbar.tsx +168 -0
  251. package/src/components/form/Command/Command.hooks.ts +207 -0
  252. package/src/components/form/Command/Command.parts.tsx +58 -0
  253. package/src/components/form/Command/Command.styles.ts +83 -0
  254. package/src/components/form/Command/Command.tsx +61 -270
  255. package/src/components/form/CreatableSelect/CreatableSelect.md +1 -1
  256. package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +1 -1
  257. package/src/components/form/CreatableSelect/CreatableSelect.tsx +152 -71
  258. package/src/components/form/CreatableSelect/CreatableSelect.types.ts +13 -0
  259. package/src/components/form/HeaderButton/HeaderButton.tsx +80 -0
  260. package/src/components/form/HeaderButton/index.ts +2 -0
  261. package/src/components/form/Input/Input.stories.tsx +5 -1
  262. package/src/components/form/Input/Input.styles.ts +176 -0
  263. package/src/components/form/Input/Input.test.stories.tsx +30 -23
  264. package/src/components/form/Input/Input.tsx +71 -211
  265. package/src/components/form/Input/Input.types.ts +11 -0
  266. package/src/components/form/InputOTP/InputOTP.test.stories.tsx +9 -17
  267. package/src/components/form/Menubar/Menubar.test.stories.tsx +4 -8
  268. package/src/components/form/PasswordStrength/PasswordStrength.helpers.tsx +117 -0
  269. package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +3 -3
  270. package/src/components/form/PasswordStrength/PasswordStrength.tsx +202 -255
  271. package/src/components/form/PhoneInput/PhoneInput.hooks.ts +134 -0
  272. package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +4 -4
  273. package/src/components/form/PhoneInput/PhoneInput.tsx +279 -292
  274. package/src/components/form/PhoneInput/countries.ts +54 -0
  275. package/src/components/form/PhoneInput/phone.ts +47 -0
  276. package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +9 -9
  277. package/src/components/form/RichTextEditor/RichTextEditor.dom.ts +124 -0
  278. package/src/components/form/RichTextEditor/RichTextEditor.format.ts +61 -0
  279. package/src/components/form/RichTextEditor/RichTextEditor.hooks.ts +142 -0
  280. package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +5 -12
  281. package/src/components/form/RichTextEditor/RichTextEditor.tsx +98 -410
  282. package/src/components/form/RichTextEditor/RichTextEditorToolbar.tsx +187 -0
  283. package/src/components/form/SearchPalette/SearchPalette.parts.tsx +22 -0
  284. package/src/components/form/SearchPalette/SearchPalette.tsx +13 -9
  285. package/src/components/form/Select/Select.test.stories.tsx +35 -27
  286. package/src/components/form/Slider/Slider.test.stories.tsx +9 -11
  287. package/src/components/form/Switch/Switch.md +2 -1
  288. package/src/components/form/Switch/Switch.test.stories.tsx +6 -6
  289. package/src/components/form/Textarea/Textarea.styles.ts +143 -0
  290. package/src/components/form/Textarea/Textarea.test.stories.tsx +20 -26
  291. package/src/components/form/Textarea/Textarea.tsx +114 -535
  292. package/src/components/form/Textarea/TextareaRichEditor.tsx +377 -0
  293. package/src/components/form/Toggle/Toggle.styles.ts +211 -0
  294. package/src/components/form/Toggle/Toggle.test.stories.tsx +19 -27
  295. package/src/components/form/Toggle/Toggle.tsx +10 -185
  296. package/src/components/form/UploadButton/UploadButton.hooks.ts +323 -0
  297. package/src/components/form/UploadButton/UploadButton.parts.tsx +210 -0
  298. package/src/components/form/UploadButton/UploadButton.stories.tsx +2 -1
  299. package/src/components/form/UploadButton/UploadButton.test.stories.tsx +123 -4
  300. package/src/components/form/UploadButton/UploadButton.tsx +76 -305
  301. package/src/components/form/UploadButton/UploadButton.types.ts +16 -0
  302. package/src/components/form/index.ts +1 -0
  303. package/src/components/form/total-form/Fields/CepField.tsx +143 -0
  304. package/src/components/form/total-form/Fields/ToggleField.tsx +61 -0
  305. package/src/components/form/total-form/Fields/index.ts +4 -0
  306. package/src/components/form/total-form/FormContainer.tsx +3 -3
  307. package/src/components/form/total-form/SubmitButton.tsx +25 -2
  308. package/src/components/form/total-form/total-form.test.tsx +118 -1
  309. package/src/components/layout/Accordion/Accordion.md +1 -1
  310. package/src/components/layout/Accordion/Accordion.stories.tsx +5 -1
  311. package/src/components/layout/Accordion/Accordion.styles.ts +80 -0
  312. package/src/components/layout/Accordion/Accordion.test.stories.tsx +32 -50
  313. package/src/components/layout/Accordion/Accordion.tsx +2 -75
  314. package/src/components/layout/Card/Card.helpers.ts +30 -0
  315. package/src/components/layout/Card/Card.stories.tsx +9 -11
  316. package/src/components/layout/Card/Card.styles.ts +182 -0
  317. package/src/components/layout/Card/Card.test.stories.tsx +46 -46
  318. package/src/components/layout/Card/Card.tsx +24 -241
  319. package/src/components/layout/CardGrid/CardGrid.md +96 -0
  320. package/src/components/layout/CardGrid/CardGrid.tsx +140 -0
  321. package/src/components/layout/CardGrid/__tests__/CardGrid.test.tsx +92 -0
  322. package/src/components/layout/CardGrid/index.ts +2 -0
  323. package/src/components/layout/Collapsible/Collapsible.helpers.ts +83 -0
  324. package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +7 -3
  325. package/src/components/layout/Collapsible/Collapsible.tsx +111 -107
  326. package/src/components/layout/Container/Container.test.stories.tsx +11 -16
  327. package/src/components/layout/ContentToolbar/ColumnsMenu.tsx +1 -1
  328. package/src/components/layout/ContentToolbar/ContentToolbar.tsx +120 -44
  329. package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +9 -0
  330. package/src/components/layout/ContentToolbar/FilterTrigger.tsx +1 -1
  331. package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +58 -43
  332. package/src/components/layout/ContentToolbar/MultiSelectTriggers.tsx +139 -0
  333. package/src/components/layout/ContentToolbar/SavedViewsMenu.tsx +10 -3
  334. package/src/components/layout/ContentToolbar/SortByDropdown.tsx +35 -11
  335. package/src/components/layout/ContentToolbar/ViewSelector.tsx +5 -6
  336. package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +21 -0
  337. package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +27 -1
  338. package/src/components/layout/Dashboard/Dashboard.tsx +27 -8
  339. package/src/components/layout/Dashboard/DashboardParts.tsx +12 -19
  340. package/src/components/layout/Dashboard/index.ts +2 -1
  341. package/src/components/layout/Drawer/Drawer.stories.tsx +8 -10
  342. package/src/components/layout/Drawer/Drawer.test.stories.tsx +18 -14
  343. package/src/components/layout/Drawer/Drawer.tsx +67 -53
  344. package/src/components/layout/Resizable/Resizable.helpers.ts +133 -0
  345. package/src/components/layout/Resizable/Resizable.hooks.ts +68 -0
  346. package/src/components/layout/Resizable/Resizable.tsx +38 -153
  347. package/src/components/layout/ScrollArea/ScrollArea.helpers.ts +51 -0
  348. package/src/components/layout/ScrollArea/ScrollArea.styles.ts +248 -0
  349. package/src/components/layout/ScrollArea/ScrollArea.tsx +183 -374
  350. package/src/components/layout/ScrollArea/ScrollAreaContent.tsx +70 -0
  351. package/src/components/layout/ScrollArea/ScrollViewport.tsx +90 -0
  352. package/src/components/layout/Separator/Separator.styles.ts +128 -0
  353. package/src/components/layout/Separator/Separator.test.stories.tsx +3 -12
  354. package/src/components/layout/Separator/Separator.tsx +6 -132
  355. package/src/components/layout/SettingsLayout/SettingsLayout.parts.tsx +2 -1
  356. package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +6 -8
  357. package/src/components/layout/Sidebar/Sidebar.stories.tsx +10 -12
  358. package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +35 -37
  359. package/src/components/layout/Skeleton/Skeleton.helpers.ts +33 -0
  360. package/src/components/layout/Skeleton/Skeleton.styles.ts +114 -0
  361. package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +3 -3
  362. package/src/components/layout/Skeleton/Skeleton.tsx +67 -155
  363. package/src/components/layout/TableFilter/TableFilter.tsx +2 -1
  364. package/src/components/navigation/Breadcrumbs/BreadcrumbEntry.tsx +305 -0
  365. package/src/components/navigation/Breadcrumbs/Breadcrumbs.md +2 -1
  366. package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +12 -14
  367. package/src/components/navigation/Breadcrumbs/Breadcrumbs.styles.tsx +212 -0
  368. package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +62 -61
  369. package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +116 -429
  370. package/src/components/navigation/CommandPalette/CommandPalette.helpers.ts +82 -0
  371. package/src/components/navigation/CommandPalette/CommandPalette.hooks.ts +202 -0
  372. package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +3 -1
  373. package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +3 -5
  374. package/src/components/navigation/CommandPalette/CommandPalette.tsx +108 -341
  375. package/src/components/navigation/CommandPalette/CommandPaletteResults.tsx +182 -0
  376. package/src/components/navigation/CommandPalette/ShortcutChip.tsx +18 -0
  377. package/src/components/navigation/ContextMenu/ContextMenu.md +3 -1
  378. package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +4 -1
  379. package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +19 -24
  380. package/src/components/navigation/ContextMenu/ContextMenu.tsx +70 -54
  381. package/src/components/navigation/DropdownMenu/DropdownMenu.md +3 -1
  382. package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +17 -19
  383. package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +26 -26
  384. package/src/components/navigation/DropdownMenu/DropdownMenu.tsx +189 -111
  385. package/src/components/navigation/NavigationMenu/NavigationMegaSection.tsx +66 -0
  386. package/src/components/navigation/NavigationMenu/NavigationMenu.md +7 -2
  387. package/src/components/navigation/NavigationMenu/NavigationMenu.shell.tsx +218 -0
  388. package/src/components/navigation/NavigationMenu/NavigationMenu.styles.ts +223 -0
  389. package/src/components/navigation/NavigationMenu/NavigationMenu.tsx +119 -808
  390. package/src/components/navigation/NavigationMenu/NavigationMenuItem.hooks.ts +126 -0
  391. package/src/components/navigation/NavigationMenu/NavigationMenuItem.tsx +352 -0
  392. package/src/components/navigation/NavigationMenu/NavigationStandardBody.tsx +148 -0
  393. package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +15 -17
  394. package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +27 -23
  395. package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +11 -13
  396. package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +9 -11
  397. package/src/components/navigation/Pagination/Pagination.helpers.tsx +85 -0
  398. package/src/components/navigation/Pagination/Pagination.md +2 -1
  399. package/src/components/navigation/Pagination/Pagination.stories.tsx +4 -6
  400. package/src/components/navigation/Pagination/Pagination.styles.ts +135 -0
  401. package/src/components/navigation/Pagination/Pagination.tsx +178 -313
  402. package/src/components/navigation/Pagination/PaginationParts.tsx +81 -0
  403. package/src/components/navigation/Tabs/Tabs.md +3 -1
  404. package/src/components/navigation/Tabs/Tabs.stories.tsx +21 -23
  405. package/src/components/navigation/Tabs/Tabs.styles.ts +163 -0
  406. package/src/components/navigation/Tabs/Tabs.test.stories.tsx +37 -37
  407. package/src/components/navigation/Tabs/Tabs.tsx +192 -361
  408. package/src/components/navigation/Tabs/TabsPanel.tsx +100 -0
  409. package/src/components/typography/Blockquote/Blockquote.tsx +1 -1
  410. package/src/components/typography/Code/Code.test.stories.tsx +5 -5
  411. package/src/components/typography/Code/Code.tsx +74 -49
  412. package/src/components/typography/Heading/Heading.test.stories.tsx +8 -8
  413. package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +5 -12
  414. package/src/components/typography/Text/Text.tsx +84 -77
  415. package/src/components/utility/AnimatedIcon/AnimatedIcon.md +4 -4
  416. package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +19 -21
  417. package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +19 -12
  418. package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +9 -9
  419. package/src/components/utility/InfiniteScroll/InfiniteScroll.hooks.ts +118 -0
  420. package/src/components/utility/InfiniteScroll/InfiniteScroll.parts.tsx +54 -0
  421. package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +12 -9
  422. package/src/components/utility/InfiniteScroll/InfiniteScroll.tsx +29 -148
  423. package/src/components/utility/InstallPrompt/InstallPrompt.helpers.ts +124 -0
  424. package/src/components/utility/InstallPrompt/InstallPrompt.md +113 -0
  425. package/src/components/utility/InstallPrompt/InstallPrompt.stories.tsx +118 -0
  426. package/src/components/utility/InstallPrompt/InstallPrompt.styles.ts +50 -0
  427. package/src/components/utility/InstallPrompt/InstallPrompt.test.stories.tsx +83 -0
  428. package/src/components/utility/InstallPrompt/InstallPrompt.tsx +192 -0
  429. package/src/components/utility/InstallPrompt/InstallPrompt.types.ts +78 -0
  430. package/src/components/utility/InstallPrompt/__tests__/InstallPrompt.ios.test.tsx +65 -0
  431. package/src/components/utility/InstallPrompt/__tests__/usePwaInstall.test.tsx +271 -0
  432. package/src/components/utility/InstallPrompt/index.ts +10 -0
  433. package/src/components/utility/InstallPrompt/usePwaInstall.ts +171 -0
  434. package/src/components/utility/Transition/Transition.helpers.ts +40 -0
  435. package/src/components/utility/Transition/Transition.tsx +17 -68
  436. package/src/components/utility/VirtualList/VirtualList.hooks.ts +319 -0
  437. package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +49 -38
  438. package/src/components/utility/VirtualList/VirtualList.tsx +110 -315
  439. package/src/components/utility/WorkflowStep/WorkflowStep.helpers.ts +151 -0
  440. package/src/components/utility/WorkflowStep/WorkflowStep.parts.tsx +371 -0
  441. package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +9 -11
  442. package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +7 -7
  443. package/src/components/utility/WorkflowStep/WorkflowStep.tsx +51 -449
  444. package/src/components/utility/WorkflowStep/WorkflowStepRow.tsx +96 -0
  445. package/src/components/utility/index.ts +1 -0
  446. package/src/mui/Link.ts +2 -0
  447. package/src/utils/index.ts +2 -1
  448. package/src/utils/viewport.ts +65 -0
@@ -1,4 +1,6 @@
1
- import { Close, Error, Info } from '@mui/icons-material';
1
+ import Close from '@mui/icons-material/Close';
2
+ import Error from '@mui/icons-material/Error';
3
+ import Info from '@mui/icons-material/Info';
2
4
  import type { ButtonProps } from '@mui/material';
3
5
  import {
4
6
  alpha,
@@ -54,8 +56,17 @@ const StyledDialog = styled(Dialog, {
54
56
  border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
55
57
  }),
56
58
 
59
+ // The error tint is LAYERED over the paper, not used as the background
60
+ // itself: `alpha(error, 0.05)` alone leaves the panel 95% transparent, so
61
+ // the page behind it showed straight through the question and its buttons.
62
+ // A confirm nobody can read is the one dialog that must never be hard to
63
+ // read. Same tint, opaque base.
57
64
  ...(customVariant === 'destructive' && {
58
- backgroundColor: alpha(theme.palette.error.main, 0.05),
65
+ backgroundColor: theme.palette.background.paper,
66
+ backgroundImage: `linear-gradient(${alpha(theme.palette.error.main, 0.05)}, ${alpha(
67
+ theme.palette.error.main,
68
+ 0.05,
69
+ )})`,
59
70
  border: `1px solid ${alpha(theme.palette.error.main, 0.2)}`,
60
71
  }),
61
72
 
@@ -141,6 +152,155 @@ const CloseButton = styled(IconButton)(({ theme }) => ({
141
152
  color: theme.palette.grey[500],
142
153
  }));
143
154
 
155
+ /**
156
+ * Per-variant lookups, hoisted out of the render function. They depend on
157
+ * nothing but their arguments, so keeping them at module scope means they are
158
+ * not rebuilt on every render — and it keeps the component's own branch count
159
+ * inside the repo's complexity budget.
160
+ */
161
+ const variantIcon = (
162
+ variant: AlertDialogProps['variant'],
163
+ icon: AlertDialogProps['icon'],
164
+ ): React.ReactNode => {
165
+ if (icon) return icon;
166
+ return variant === 'destructive' ? <Error color="error" /> : <Info color="primary" />;
167
+ };
168
+
169
+ /**
170
+ * Ids for the two elements that give the dialog its accessible name and
171
+ * description. Derived from the test id so they are stable and unique per
172
+ * dialog without a generated id that would change on every render.
173
+ */
174
+ const titleId = (dataTestId: string): string => `${dataTestId}-title`;
175
+ const descriptionId = (dataTestId: string): string => `${dataTestId}-description`;
176
+
177
+ /**
178
+ * `alertdialog`, not `dialog`.
179
+ *
180
+ * This component interrupts what the operator was doing and will not go away
181
+ * until they answer — which is exactly the distinction the two roles draw, and
182
+ * it is what makes a screen reader announce the message itself on open rather
183
+ * than just the dialog's name. MUI hardcodes `role="dialog"` on the paper, so
184
+ * it has to be overridden through the paper slot.
185
+ */
186
+ const ALERT_ROLE = 'alertdialog';
187
+
188
+ /**
189
+ * The paper slot's a11y props: the role, plus the name and description wired to
190
+ * the elements that actually carry them, so the announcement is the QUESTION
191
+ * rather than the word "dialog".
192
+ *
193
+ * Both references are conditional — pointing `aria-labelledby` at an element
194
+ * that was never rendered leaves a dangling id, which screen readers treat as
195
+ * no name at all, i.e. worse than omitting it.
196
+ */
197
+ function ariaSlotProps(
198
+ dataTestId: string,
199
+ title: AlertDialogProps['title'],
200
+ description: AlertDialogProps['description'],
201
+ ): { paper: Record<string, string> } {
202
+ return {
203
+ paper: {
204
+ role: ALERT_ROLE,
205
+ ...(title ? { 'aria-labelledby': titleId(dataTestId) } : {}),
206
+ ...(description ? { 'aria-describedby': descriptionId(dataTestId) } : {}),
207
+ },
208
+ };
209
+ }
210
+
211
+ /** Both variants render a filled confirm; only the colour distinguishes them. */
212
+ const CONFIRM_BUTTON_VARIANT: ButtonProps['variant'] = 'contained';
213
+ const confirmButtonColor = (variant: AlertDialogProps['variant']): 'error' | 'primary' =>
214
+ variant === 'destructive' ? 'error' : 'primary';
215
+
216
+ /** Title row: the variant icon (unless explicitly suppressed with `null`) + the title. */
217
+ function AlertDialogHeader({
218
+ title,
219
+ icon,
220
+ variant,
221
+ dataTestId,
222
+ }: {
223
+ title: AlertDialogProps['title'];
224
+ icon: AlertDialogProps['icon'];
225
+ variant: AlertDialogProps['variant'];
226
+ dataTestId: string;
227
+ }): React.ReactElement | null {
228
+ if (!title) return null;
229
+ return (
230
+ <StyledDialogTitle id={titleId(dataTestId)} data-testid={`${dataTestId}-title`}>
231
+ {icon !== null && (
232
+ <span data-testid={`${dataTestId}-icon`}>{variantIcon(variant, icon)}</span>
233
+ )}
234
+ <Typography variant="h6" component="span">
235
+ {title}
236
+ </Typography>
237
+ </StyledDialogTitle>
238
+ );
239
+ }
240
+
241
+ /** Action row: the optional cancel button and the (variant-coloured) confirm. */
242
+ function AlertDialogFooter({
243
+ variant,
244
+ cancelText = 'Cancel',
245
+ confirmText = 'Confirm',
246
+ showCancel = true,
247
+ loading = false,
248
+ confirmDisabled = false,
249
+ initialFocus = 'confirm',
250
+ onCancel,
251
+ onConfirm,
252
+ dataTestId,
253
+ }: Pick<
254
+ AlertDialogProps,
255
+ | 'variant'
256
+ | 'cancelText'
257
+ | 'confirmText'
258
+ | 'showCancel'
259
+ | 'loading'
260
+ | 'confirmDisabled'
261
+ | 'initialFocus'
262
+ > & {
263
+ onCancel: () => void;
264
+ onConfirm: () => void;
265
+ dataTestId: string;
266
+ }): React.ReactElement {
267
+ return (
268
+ <StyledDialogActions data-testid={`${dataTestId}-actions`}>
269
+ {showCancel && (
270
+ <Button
271
+ onClick={onCancel}
272
+ variant="outlined"
273
+ color="inherit"
274
+ disabled={loading}
275
+ autoFocus={initialFocus === 'cancel'}
276
+ data-testid={`${dataTestId}-cancel-button`}
277
+ >
278
+ {cancelText}
279
+ </Button>
280
+ )}
281
+ <Button
282
+ onClick={onConfirm}
283
+ variant={CONFIRM_BUTTON_VARIANT}
284
+ color={confirmButtonColor(variant)}
285
+ disabled={confirmDisabled || loading}
286
+ startIcon={
287
+ loading ? (
288
+ <CircularProgress
289
+ size={16}
290
+ color="inherit"
291
+ data-testid={`${dataTestId}-loading-spinner`}
292
+ />
293
+ ) : undefined
294
+ }
295
+ autoFocus={initialFocus !== 'cancel'}
296
+ data-testid={`${dataTestId}-confirm-button`}
297
+ >
298
+ {confirmText}
299
+ </Button>
300
+ </StyledDialogActions>
301
+ );
302
+ }
303
+
144
304
  export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
145
305
  ({
146
306
  variant = 'default',
@@ -149,57 +309,24 @@ export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
149
309
  title,
150
310
  description,
151
311
  icon,
152
- cancelText = 'Cancel',
153
- confirmText = 'Confirm',
312
+ cancelText,
313
+ confirmText,
154
314
  onCancel,
155
315
  onConfirm,
156
- showCancel = true,
157
- loading = false,
158
- confirmDisabled = false,
316
+ showCancel,
317
+ loading,
318
+ confirmDisabled,
319
+ initialFocus,
159
320
  children,
160
321
  onClose,
161
322
  'data-testid': dataTestId = 'alert-dialog',
162
323
  ...props
163
324
  }, ref) => {
164
-
165
325
  const handleCancel = () => {
166
326
  onCancel?.();
167
327
  onClose?.({}, 'backdropClick');
168
328
  };
169
329
 
170
- const handleConfirm = () => {
171
- onConfirm?.();
172
- };
173
-
174
- const getVariantIcon = () => {
175
- if (icon) return icon;
176
-
177
- switch (variant) {
178
- case 'destructive':
179
- return <Error color="error" />;
180
- default:
181
- return <Info color="primary" />;
182
- }
183
- };
184
-
185
- const getConfirmButtonVariant = (): ButtonProps['variant'] => {
186
- switch (variant) {
187
- case 'destructive':
188
- return 'contained';
189
- default:
190
- return 'contained';
191
- }
192
- };
193
-
194
- const getConfirmButtonColor = () => {
195
- switch (variant) {
196
- case 'destructive':
197
- return 'error';
198
- default:
199
- return 'primary';
200
- }
201
- };
202
-
203
330
  return (
204
331
  <StyledDialog
205
332
  ref={ref}
@@ -208,6 +335,7 @@ export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
208
335
  pulse={pulse}
209
336
  onClose={onClose}
210
337
  data-testid={dataTestId}
338
+ slotProps={ariaSlotProps(dataTestId, title, description)}
211
339
  {...props}
212
340
  >
213
341
  <CloseButton
@@ -219,52 +347,37 @@ export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
219
347
  <Close />
220
348
  </CloseButton>
221
349
 
222
- {title && (
223
- <StyledDialogTitle data-testid={`${dataTestId}-title`}>
224
- {(icon !== null) && (
225
- <span data-testid={`${dataTestId}-icon`}>
226
- {getVariantIcon()}
227
- </span>
228
- )}
229
- <Typography variant="h6" component="span">
230
- {title}
231
- </Typography>
232
- </StyledDialogTitle>
233
- )}
350
+ <AlertDialogHeader
351
+ title={title}
352
+ icon={icon}
353
+ variant={variant}
354
+ dataTestId={dataTestId}
355
+ />
234
356
 
235
357
  <StyledDialogContent data-testid={`${dataTestId}-content`}>
236
358
  {description && (
237
- <DialogContentText data-testid={`${dataTestId}-description`}>
359
+ <DialogContentText
360
+ id={descriptionId(dataTestId)}
361
+ data-testid={`${dataTestId}-description`}
362
+ >
238
363
  {description}
239
364
  </DialogContentText>
240
365
  )}
241
366
  {children}
242
367
  </StyledDialogContent>
243
368
 
244
- <StyledDialogActions data-testid={`${dataTestId}-actions`}>
245
- {showCancel && (
246
- <Button
247
- onClick={handleCancel}
248
- variant="outlined"
249
- color="inherit"
250
- disabled={loading}
251
- data-testid={`${dataTestId}-cancel-button`}
252
- >
253
- {cancelText}
254
- </Button>
255
- )}
256
- <Button
257
- onClick={handleConfirm}
258
- variant={getConfirmButtonVariant()}
259
- color={getConfirmButtonColor()}
260
- disabled={confirmDisabled || loading}
261
- startIcon={loading ? <CircularProgress size={16} color="inherit" data-testid={`${dataTestId}-loading-spinner`} /> : undefined}
262
- autoFocus
263
- data-testid={`${dataTestId}-confirm-button`}
264
- >
265
- {confirmText}
266
- </Button>
267
- </StyledDialogActions>
369
+ <AlertDialogFooter
370
+ variant={variant}
371
+ cancelText={cancelText}
372
+ confirmText={confirmText}
373
+ showCancel={showCancel}
374
+ loading={loading}
375
+ confirmDisabled={confirmDisabled}
376
+ initialFocus={initialFocus}
377
+ onCancel={handleCancel}
378
+ onConfirm={() => onConfirm?.()}
379
+ dataTestId={dataTestId}
380
+ />
268
381
  </StyledDialog>
269
382
  );
270
383
  }
@@ -25,7 +25,7 @@ export interface AlertDialogProps extends Omit<MuiDialogProps, 'variant'> {
25
25
  /**
26
26
  * Description text
27
27
  */
28
- description?: string;
28
+ description?: React.ReactNode;
29
29
 
30
30
  /**
31
31
  * Icon to display in the dialog
@@ -67,6 +67,13 @@ export interface AlertDialogProps extends Omit<MuiDialogProps, 'variant'> {
67
67
  */
68
68
  confirmDisabled?: boolean;
69
69
 
70
+ /**
71
+ * Which button owns focus when the dialog opens. Defaults to `confirm`, which
72
+ * is right for an acknowledgement; a destructive dialog should pass `cancel`
73
+ * so a stray Enter on the trigger cannot complete the action.
74
+ */
75
+ initialFocus?: 'confirm' | 'cancel';
76
+
70
77
  /**
71
78
  * Optional data-testid attribute for testing
72
79
  */
@@ -1,6 +1,6 @@
1
1
  import { Typography } from '@mui/material';
2
2
  import type { Meta, StoryObj } from '@storybook/react-vite';
3
- import { expect, fn, userEvent, within } from 'storybook/test';
3
+ import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
4
4
 
5
5
  import { EmptyState } from '../EmptyState/EmptyState';
6
6
 
@@ -66,7 +66,7 @@ export const LoadingStateTest: Story = {
66
66
 
67
67
  await step('Verify children content is NOT rendered', async () => {
68
68
  const content = canvas.queryByTestId('success-content');
69
- await expect(content).not.toBeInTheDocument();
69
+ await waitFor(() => expect(content).not.toBeInTheDocument());
70
70
  });
71
71
 
72
72
  await step('Verify loading component is rendered', async () => {
@@ -94,7 +94,7 @@ export const ErrorStateTest: Story = {
94
94
 
95
95
  await step('Verify children content is NOT rendered', async () => {
96
96
  const content = canvas.queryByTestId('success-content');
97
- await expect(content).not.toBeInTheDocument();
97
+ await waitFor(() => expect(content).not.toBeInTheDocument());
98
98
  });
99
99
 
100
100
  await step('Verify error component is rendered', async () => {
@@ -127,7 +127,7 @@ export const EmptyStateTest: Story = {
127
127
 
128
128
  await step('Verify children content is NOT rendered', async () => {
129
129
  const content = canvas.queryByTestId('success-content');
130
- await expect(content).not.toBeInTheDocument();
130
+ await waitFor(() => expect(content).not.toBeInTheDocument());
131
131
  });
132
132
 
133
133
  await step('Verify empty state component is rendered', async () => {
@@ -158,7 +158,7 @@ export const PriorityTest: Story = {
158
158
  await expect(loading).toBeInTheDocument();
159
159
 
160
160
  const error = canvas.queryByTestId('error-state');
161
- await expect(error).not.toBeInTheDocument();
161
+ await waitFor(() => expect(error).not.toBeInTheDocument());
162
162
  });
163
163
  },
164
164
  };
@@ -38,6 +38,37 @@ import type { AsyncStateContainerProps } from './AsyncStateContainer.types';
38
38
  * <DataGrid data={data} />
39
39
  * </AsyncStateContainer>
40
40
  */
41
+ // The four states render the same wrapper, differing only in data-state and
42
+ // what goes inside it.
43
+ const StateBox: React.FC<{
44
+ state: 'loading' | 'error' | 'empty' | 'success';
45
+ className?: string;
46
+ dataTestId?: string;
47
+ children: React.ReactNode;
48
+ }> = ({ state, className, dataTestId, children }) => (
49
+ <Box
50
+ className={className}
51
+ data-testid={dataTestId || 'async-state-container'}
52
+ data-state={state}
53
+ >
54
+ {children}
55
+ </Box>
56
+ );
57
+
58
+ const errorMessageOf = (error: unknown): string => {
59
+ if (!error) return '';
60
+ if (typeof error === 'string') return error;
61
+ if (error instanceof Error) return error.message;
62
+ return 'An unexpected error occurred';
63
+ };
64
+
65
+ // Each state takes a ready-made node, else a render function, else the default.
66
+ const pickNode = (
67
+ node: React.ReactNode,
68
+ render: (() => React.ReactNode) | undefined,
69
+ fallback: React.ReactNode,
70
+ ): React.ReactNode => node || render?.() || fallback;
71
+
41
72
  export const AsyncStateContainer: React.FC<AsyncStateContainerProps> = React.memo(
42
73
  ({
43
74
  isLoading = false,
@@ -53,60 +84,38 @@ export const AsyncStateContainer: React.FC<AsyncStateContainerProps> = React.mem
53
84
  className,
54
85
  dataTestId,
55
86
  }) => {
56
- // Helper to extract error message from various error types
57
- const getErrorMessage = (): string => {
58
- if (!error) return '';
59
- if (typeof error === 'string') return error;
60
- if (error instanceof Error) return error.message;
61
- return 'An unexpected error occurred';
62
- };
87
+ const box = { className, dataTestId };
63
88
 
64
89
  // Priority: loading > error > empty > children
65
90
  if (isLoading) {
66
91
  return (
67
- <Box
68
- className={className}
69
- data-testid={dataTestId || 'async-state-container'}
70
- data-state="loading"
71
- >
72
- {loadingComponent || renderLoading?.() || <LoadingState />}
73
- </Box>
92
+ <StateBox state="loading" {...box}>
93
+ {pickNode(loadingComponent, renderLoading, <LoadingState />)}
94
+ </StateBox>
74
95
  );
75
96
  }
76
97
 
77
98
  if (error) {
78
- const errorMessage = getErrorMessage();
99
+ const message = errorMessageOf(error);
79
100
  return (
80
- <Box
81
- className={className}
82
- data-testid={dataTestId || 'async-state-container'}
83
- data-state="error"
84
- >
85
- {errorComponent || renderError?.(errorMessage) || <ErrorState message={errorMessage} />}
86
- </Box>
101
+ <StateBox state="error" {...box}>
102
+ {errorComponent || renderError?.(message) || <ErrorState message={message} />}
103
+ </StateBox>
87
104
  );
88
105
  }
89
106
 
90
107
  if (isEmpty) {
91
108
  return (
92
- <Box
93
- className={className}
94
- data-testid={dataTestId || 'async-state-container'}
95
- data-state="empty"
96
- >
97
- {emptyComponent || renderEmpty?.() || <EmptyState title="No data available" />}
98
- </Box>
109
+ <StateBox state="empty" {...box}>
110
+ {pickNode(emptyComponent, renderEmpty, <EmptyState title="No data available" />)}
111
+ </StateBox>
99
112
  );
100
113
  }
101
114
 
102
115
  return (
103
- <Box
104
- className={className}
105
- data-testid={dataTestId || 'async-state-container'}
106
- data-state="success"
107
- >
116
+ <StateBox state="success" {...box}>
108
117
  {children}
109
- </Box>
118
+ </StateBox>
110
119
  );
111
120
  },
112
121
  );
@@ -1,4 +1,7 @@
1
- import { Email, Notifications, Phone, Settings } from '@mui/icons-material';
1
+ import Email from '@mui/icons-material/Email';
2
+ import Notifications from '@mui/icons-material/Notifications';
3
+ import Phone from '@mui/icons-material/Phone';
4
+ import Settings from '@mui/icons-material/Settings';
2
5
  import { Badge, Box, Paper, Stack, Typography } from '@mui/material';
3
6
  import type { Meta, StoryObj } from '@storybook/react-vite';
4
7
  import React, { useState } from 'react';
@@ -1,4 +1,4 @@
1
- import { Person } from '@mui/icons-material';
1
+ import Person from '@mui/icons-material/Person';
2
2
  import { Box,Stack } from '@mui/material';
3
3
  import type { Meta, StoryObj } from '@storybook/react-vite';
4
4
  import React from 'react';
@@ -494,8 +494,10 @@ export const AvatarGroupTest: Story = {
494
494
  await expect(avatar3).toBeInTheDocument();
495
495
 
496
496
  // Avatar 4 and 5 should not be in the document (hidden by max=3)
497
- await expect(canvas.queryByTestId('group-avatar-4')).not.toBeInTheDocument();
498
- await expect(canvas.queryByTestId('group-avatar-5')).not.toBeInTheDocument();
497
+ await waitFor(() => {
498
+ expect(canvas.queryByTestId('group-avatar-4')).not.toBeInTheDocument();
499
+ expect(canvas.queryByTestId('group-avatar-5')).not.toBeInTheDocument();
500
+ });
499
501
  });
500
502
 
501
503
  await step('Verify overflow indicator', async () => {