@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
@@ -9,18 +9,41 @@ import { useFormContext } from './FormContext';
9
9
  export interface SubmitButtonProps {
10
10
  /** Button label. */
11
11
  children: React.ReactNode;
12
+ /** Test id; defaults to `total-form-submit`. Override when a page renders more than one form. */
13
+ dataTestId?: string;
12
14
  }
13
15
 
16
+ /**
17
+ * The height of a form input control (see `Input` — `.MuiInputBase-root`), so a
18
+ * submit button placed inline with fields lines up with them instead of sitting
19
+ * shorter. Kept in sync with that value by hand.
20
+ */
21
+ const FIELD_HEIGHT = 56;
22
+
14
23
  /**
15
24
  * Submit control wired to {@link useFormContext}. Wraps the existing `@12-apps/ui`
16
25
  * `Button` as a `type="submit"` button (testid `total-form-submit`) and
17
26
  * reflects the form's `submitting` state via `loading`/`disabled`.
27
+ *
28
+ * It matches the form field height and never wraps its label, so it aligns with
29
+ * an adjacent input on the same row (and stays full-width when the row stacks).
18
30
  */
19
- export function SubmitButton({ children }: SubmitButtonProps): React.ReactElement {
31
+ export function SubmitButton({
32
+ children,
33
+ dataTestId = 'total-form-submit',
34
+ }: SubmitButtonProps): React.ReactElement {
20
35
  const { submitting } = useFormContext();
21
36
 
22
37
  return (
23
- <Button type="submit" dataTestId="total-form-submit" loading={submitting} disabled={submitting}>
38
+ <Button
39
+ type="submit"
40
+ dataTestId={dataTestId}
41
+ loading={submitting}
42
+ disabled={submitting}
43
+ // `Button` replaces (not merges) its size styles with this `sx`, so restate
44
+ // the md padding/font here alongside the field-height + no-wrap alignment.
45
+ sx={{ height: FIELD_HEIGHT, px: 2, fontSize: '1rem', flexShrink: 0, whiteSpace: 'nowrap' }}
46
+ >
24
47
  {children}
25
48
  </Button>
26
49
  );
@@ -1,9 +1,10 @@
1
1
  import { describe, it, expect, vi } from "vitest";
2
2
  import { fireEvent, render, screen, waitFor } from "@testing-library/react";
3
- import { email, required } from "@12-apps/shared-helpers/forms";
3
+ import { email, required } from "@12-apps/forms-core";
4
4
 
5
5
  import { FormContainer, Fields, FormErrorSnackbar, SubmitButton } from "./index";
6
6
  import type { FormSubmitHelpers } from "./index";
7
+ import type { CepAddress } from "../CepField";
7
8
 
8
9
  interface LoginValues extends Record<string, string> {
9
10
  email: string;
@@ -95,3 +96,119 @@ describe("total-form (RTL)", () => {
95
96
  });
96
97
  });
97
98
  });
99
+
100
+ /**
101
+ * `Fields.CepField` autofill ownership (FUT-306). The adapter writes resolved
102
+ * address parts into sibling fields, and the rule that keeps it honest runs in
103
+ * BOTH directions: it must never overwrite what the user typed, and it must
104
+ * never leave one CEP's address sitting under a different CEP.
105
+ */
106
+ interface AddressValues extends Record<string, string> {
107
+ postalCode: string;
108
+ addressLine1: string;
109
+ neighborhood: string;
110
+ city: string;
111
+ state: string;
112
+ }
113
+
114
+ const CEP_FILLS = {
115
+ street: "addressLine1",
116
+ neighborhood: "neighborhood",
117
+ city: "city",
118
+ state: "state",
119
+ } as const;
120
+
121
+ const PAULISTA: CepAddress = {
122
+ cep: "01310100",
123
+ street: "Avenida Paulista",
124
+ neighborhood: "Bela Vista",
125
+ city: "São Paulo",
126
+ state: "SP",
127
+ };
128
+
129
+ /** A "CEP geral" — a small município's catch-all, which has no logradouro. */
130
+ const CEP_GERAL: CepAddress = {
131
+ cep: "13800000",
132
+ street: null,
133
+ neighborhood: null,
134
+ city: "Mogi Mirim",
135
+ state: "SP",
136
+ };
137
+
138
+ function renderAddressForm(lookup: (cep: string) => Promise<CepAddress | null>) {
139
+ return render(
140
+ <FormContainer<AddressValues>
141
+ initialValues={{
142
+ postalCode: "",
143
+ addressLine1: "",
144
+ neighborhood: "",
145
+ city: "",
146
+ state: "",
147
+ }}
148
+ onSubmit={() => undefined}
149
+ >
150
+ <Fields.CepField name="postalCode" lookup={lookup} fills={CEP_FILLS} debounceMs={0} />
151
+ <Fields.TextField name="addressLine1" label="Endereço" />
152
+ <Fields.TextField name="neighborhood" label="Bairro" />
153
+ <Fields.TextField name="city" label="Cidade" />
154
+ </FormContainer>,
155
+ );
156
+ }
157
+
158
+ const cepField = (name: string): HTMLInputElement =>
159
+ screen.getByTestId(`total-form-field-${name}`) as HTMLInputElement;
160
+
161
+ const setCep = (value: string): void => {
162
+ fireEvent.change(cepField("postalCode"), { target: { value } });
163
+ };
164
+
165
+ describe("Fields.CepField autofill ownership", () => {
166
+ it("fills the mapped address fields from a resolved CEP", async () => {
167
+ renderAddressForm(async () => PAULISTA);
168
+
169
+ setCep("01310100");
170
+
171
+ await waitFor(() => expect(cepField("addressLine1").value).toBe("Avenida Paulista"));
172
+ expect(cepField("neighborhood").value).toBe("Bela Vista");
173
+ expect(cepField("city").value).toBe("São Paulo");
174
+ });
175
+
176
+ it("clears parts the replacement CEP has no value for", async () => {
177
+ renderAddressForm(async (cep) => (cep === "01310100" ? PAULISTA : CEP_GERAL));
178
+
179
+ setCep("01310100");
180
+ await waitFor(() => expect(cepField("addressLine1").value).toBe("Avenida Paulista"));
181
+
182
+ setCep("13800000");
183
+
184
+ // The new CEP names no logradouro or bairro, so the old ones must go —
185
+ // otherwise Avenida Paulista would sit under a Mogi Mirim CEP.
186
+ await waitFor(() => expect(cepField("city").value).toBe("Mogi Mirim"));
187
+ expect(cepField("addressLine1").value).toBe("");
188
+ expect(cepField("neighborhood").value).toBe("");
189
+ });
190
+
191
+ it("undoes its own autofill when a CEP stops resolving", async () => {
192
+ let hit = true;
193
+ renderAddressForm(async () => (hit ? PAULISTA : null));
194
+
195
+ setCep("01310100");
196
+ await waitFor(() => expect(cepField("city").value).toBe("São Paulo"));
197
+
198
+ hit = false;
199
+ setCep("99999999");
200
+
201
+ await waitFor(() => expect(cepField("city").value).toBe(""));
202
+ expect(cepField("addressLine1").value).toBe("");
203
+ });
204
+
205
+ it("leaves a field the user typed themselves alone", async () => {
206
+ renderAddressForm(async () => PAULISTA);
207
+
208
+ fireEvent.change(cepField("addressLine1"), { target: { value: "Rua que eu digitei" } });
209
+ setCep("01310100");
210
+
211
+ await waitFor(() => expect(cepField("city").value).toBe("São Paulo"));
212
+ expect(cepField("addressLine1").value).toBe("Rua que eu digitei");
213
+ });
214
+ });
@@ -50,7 +50,7 @@ The Accordion component provides expandable/collapsible content sections with sm
50
50
 
51
51
  ```tsx
52
52
  import { Accordion, AccordionSummary, AccordionDetails } from '@mui/material';
53
- import { ExpandMore } from '@mui/icons-material';
53
+ import ExpandMore from '@mui/icons-material/ExpandMore';
54
54
 
55
55
  function BasicAccordion() {
56
56
  return (
@@ -1,4 +1,8 @@
1
- import { ExpandMore, Help,Info, Security, Settings } from '@mui/icons-material';
1
+ import ExpandMore from '@mui/icons-material/ExpandMore';
2
+ import Help from '@mui/icons-material/Help';
3
+ import Info from '@mui/icons-material/Info';
4
+ import Security from '@mui/icons-material/Security';
5
+ import Settings from '@mui/icons-material/Settings';
2
6
  import { Box, Button, Stack,Typography } from '@mui/material';
3
7
  import type { Meta, StoryObj } from '@storybook/react-vite';
4
8
  import { useState } from 'react';
@@ -0,0 +1,80 @@
1
+ import { alpha } from '@mui/material';
2
+ import type { Theme } from '@mui/material';
3
+
4
+ import type { AccordionVariant } from './Accordion.types';
5
+
6
+ const BASE_TRANSITION = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
7
+
8
+ // Deliberately unannotated: the inferred union of literals assigns to MUI's
9
+ // `sx`, whereas a `CSSObject` return type does not survive being spread into it.
10
+ export const accordionVariantStyles = (theme: Theme, variant: AccordionVariant) => {
11
+ switch (variant) {
12
+ case 'glass':
13
+ return {
14
+ backgroundColor: alpha(theme.palette.background.paper, 0.08),
15
+ backdropFilter: 'blur(24px) saturate(180%)',
16
+ border: `1px solid ${alpha(theme.palette.primary.main, 0.15)}`,
17
+ borderRadius: theme.spacing(1.5),
18
+ transition: BASE_TRANSITION,
19
+ '&:before': {
20
+ display: 'none',
21
+ },
22
+ '&:hover': {
23
+ backgroundColor: alpha(theme.palette.background.paper, 0.12),
24
+ border: `1px solid ${alpha(theme.palette.primary.main, 0.25)}`,
25
+ transform: 'translateY(-1px)',
26
+ boxShadow: `0 8px 32px ${alpha(theme.palette.primary.main, 0.12)}`,
27
+ },
28
+ '&.Mui-expanded': {
29
+ backgroundColor: alpha(theme.palette.background.paper, 0.15),
30
+ border: `1px solid ${alpha(theme.palette.primary.main, 0.3)}`,
31
+ },
32
+ };
33
+ case 'bordered':
34
+ return {
35
+ border: `1px solid ${theme.palette.divider}`,
36
+ borderRadius: theme.spacing(1),
37
+ transition: BASE_TRANSITION,
38
+ '&:before': {
39
+ display: 'none',
40
+ },
41
+ '&:not(:last-child)': {
42
+ marginBottom: theme.spacing(1),
43
+ },
44
+ '&:hover': {
45
+ borderColor: theme.palette.primary.main,
46
+ boxShadow: `0 0 0 1px ${alpha(theme.palette.primary.main, 0.1)}`,
47
+ },
48
+ '&.Mui-expanded': {
49
+ borderColor: theme.palette.primary.main,
50
+ },
51
+ };
52
+ case 'separated':
53
+ return {
54
+ boxShadow: theme.shadows[2],
55
+ borderRadius: theme.spacing(1.5),
56
+ transition: BASE_TRANSITION,
57
+ '&:before': {
58
+ display: 'none',
59
+ },
60
+ '&:not(:last-child)': {
61
+ marginBottom: theme.spacing(2),
62
+ },
63
+ '&:hover': {
64
+ boxShadow: theme.shadows[4],
65
+ transform: 'translateY(-2px)',
66
+ },
67
+ '&.Mui-expanded': {
68
+ boxShadow: theme.shadows[6],
69
+ transform: 'translateY(-1px)',
70
+ },
71
+ };
72
+ default:
73
+ return {
74
+ transition: BASE_TRANSITION,
75
+ '&:hover': {
76
+ backgroundColor: alpha(theme.palette.action.hover, 0.04),
77
+ },
78
+ };
79
+ }
80
+ };
@@ -1,4 +1,5 @@
1
- import { ExpandMore, Settings } from '@mui/icons-material';
1
+ import ExpandMore from '@mui/icons-material/ExpandMore';
2
+ import Settings from '@mui/icons-material/Settings';
2
3
  import { Box, Button, Stack,Typography } from '@mui/material';
3
4
  import type { Meta, StoryObj } from '@storybook/react-vite';
4
5
  import { useState } from 'react';
@@ -215,18 +216,22 @@ export const KeyboardNavigation: Story = {
215
216
  const firstSummary = canvas.getByTestId('first-summary');
216
217
  const secondSummary = canvas.getByTestId('second-summary');
217
218
 
218
- // Focus first summary
219
- firstSummary.focus();
220
- await expect(firstSummary).toHaveFocus();
219
+ // Tab in from the body rather than calling .focus() — the old first
220
+ // assertion only proved the DOM honours focus().
221
+ await userEvent.tab();
222
+ await waitFor(() => expect(firstSummary).toHaveFocus());
221
223
 
222
224
  // Tab to second summary
223
225
  await userEvent.tab();
224
- await expect(secondSummary).toHaveFocus();
226
+ await waitFor(() => expect(secondSummary).toHaveFocus());
225
227
  });
226
228
 
227
229
  await step('Enter key to expand accordion', async () => {
228
230
  const firstSummary = canvas.getByTestId('first-summary');
229
- firstSummary.focus();
231
+
232
+ // Focus is on the second summary from the previous step; shift-tab back.
233
+ await userEvent.tab({ shift: true });
234
+ await waitFor(() => expect(firstSummary).toHaveFocus());
230
235
 
231
236
  await userEvent.keyboard('{Enter}');
232
237
 
@@ -239,12 +244,15 @@ export const KeyboardNavigation: Story = {
239
244
  await step('Tab into expanded content', async () => {
240
245
  const firstButton = canvas.getByTestId('first-button');
241
246
  await userEvent.tab();
242
- await expect(firstButton).toHaveFocus();
247
+ await waitFor(() => expect(firstButton).toHaveFocus());
243
248
  });
244
249
 
245
250
  await step('Space key to expand accordion', async () => {
246
251
  const secondSummary = canvas.getByTestId('second-summary');
247
- secondSummary.focus();
252
+
253
+ // Continue the journey forward from the button inside the first panel.
254
+ await userEvent.tab();
255
+ await waitFor(() => expect(secondSummary).toHaveFocus());
248
256
 
249
257
  await userEvent.keyboard(' ');
250
258
 
@@ -351,14 +359,14 @@ export const FocusManagement: Story = {
351
359
 
352
360
  await step('Initial focus state', async () => {
353
361
  const externalTrigger = canvas.getByTestId('external-trigger');
354
- externalTrigger.focus();
355
- await expect(externalTrigger).toHaveFocus();
362
+ await userEvent.tab();
363
+ await waitFor(() => expect(externalTrigger).toHaveFocus());
356
364
  });
357
365
 
358
366
  await step('Focus on accordion summary', async () => {
359
367
  const summary = canvas.getByTestId('focus-summary');
360
- summary.focus();
361
- await expect(summary).toHaveFocus();
368
+ await userEvent.tab();
369
+ await waitFor(() => expect(summary).toHaveFocus());
362
370
  });
363
371
 
364
372
  await step('Expand accordion and check focus flow', async () => {
@@ -375,9 +383,6 @@ export const FocusManagement: Story = {
375
383
  { timeout: 1000 },
376
384
  );
377
385
 
378
- // Small delay to ensure DOM is stable after animation
379
- await new Promise((resolve) => setTimeout(resolve, 150));
380
-
381
386
  // Tab through the expanded content with proper focus waiting
382
387
  await userEvent.tab();
383
388
  await waitFor(
@@ -438,15 +443,8 @@ export const FocusManagement: Story = {
438
443
  await step('Focus restoration after collapse', async () => {
439
444
  const summary = canvas.getByTestId('focus-summary');
440
445
 
441
- // First ensure summary has focus before clicking
442
- summary.focus();
443
- await waitFor(
444
- () => {
445
- expect(summary).toHaveFocus();
446
- },
447
- { timeout: 500 },
448
- );
449
-
446
+ // No pre-focus: clicking the summary focuses it, so calling .focus()
447
+ // first only asserted that focus() works.
450
448
  // Click to collapse
451
449
  await userEvent.click(summary);
452
450
 
@@ -527,8 +525,9 @@ export const VisualStates: Story = {
527
525
  await expect(styles.pointerEvents).toBe('none');
528
526
 
529
527
  // Content should remain hidden since it can't be clicked
530
- const details = canvas.queryByText('This should not be accessible');
531
- await expect(details).not.toBeVisible();
528
+ await waitFor(() =>
529
+ expect(canvas.queryByText('This should not be accessible')).not.toBeVisible(),
530
+ );
532
531
  });
533
532
 
534
533
  await step('Expand animation test', async () => {
@@ -811,8 +810,6 @@ export const FormInteraction: Story = {
811
810
  variant="contained"
812
811
  onClick={() => {
813
812
  // Form submission handler
814
- // eslint-disable-next-line no-console
815
- console.log('Form submitted:', formData);
816
813
  }}
817
814
  >
818
815
  Submit
@@ -1319,44 +1316,29 @@ export const PerformanceTest: Story = {
1319
1316
 
1320
1317
  await step('Measure initial render time', async () => {
1321
1318
 
1322
- const startTime = performance.now();
1319
+ // No wall-clock budget: getAllByTestId queries DOM that has already
1320
+ // rendered, so timing around it measured query time, and any threshold is
1321
+ // really a bet on how loaded the runner is. The count is the real check.
1323
1322
  const accordions = canvas.getAllByTestId(/^perf-accordion-\d+$/);
1324
-
1325
- const endTime = performance.now();
1326
-
1327
- const renderTime = endTime - startTime;
1328
- // eslint-disable-next-line no-console
1329
- console.log(`Render time for ${accordions.length} accordions: ${renderTime}ms`);
1330
-
1331
1323
  await expect(accordions.length).toBe(20);
1332
- // Assert reasonable render time (lenient threshold for CI/CD variability)
1333
- await expect(renderTime).toBeLessThan(500);
1334
1324
  });
1335
1325
 
1336
1326
  await step('Test rapid expand/collapse performance', async () => {
1337
1327
 
1338
- const startTime = performance.now();
1339
-
1340
- // Rapidly expand/collapse multiple accordions
1328
+ // Expand then collapse each accordion, waiting on the state the click is
1329
+ // supposed to produce rather than sleeping 50ms and hoping it landed.
1341
1330
  for (let i = 0; i < 5; i++) {
1342
1331
  const accordion = canvas.getByTestId(`perf-accordion-${i}`);
1343
1332
  const summary = accordion.querySelector('.MuiAccordionSummary-root');
1344
1333
 
1345
1334
  if (summary) {
1346
1335
  await userEvent.click(summary);
1336
+ await waitFor(() => expect(summary).toHaveAttribute('aria-expanded', 'true'));
1347
1337
 
1348
- await new Promise((resolve) => setTimeout(resolve, 50));
1349
1338
  await userEvent.click(summary);
1339
+ await waitFor(() => expect(summary).toHaveAttribute('aria-expanded', 'false'));
1350
1340
  }
1351
1341
  }
1352
-
1353
- const endTime = performance.now();
1354
- const interactionTime = endTime - startTime;
1355
- // eslint-disable-next-line no-console
1356
- console.log(`Interaction time for 5 accordions: ${interactionTime}ms`);
1357
-
1358
- // Verify no performance issues (lenient threshold for CI/CD variability)
1359
- await expect(interactionTime).toBeLessThan(3000);
1360
1342
  });
1361
1343
  },
1362
1344
  };
@@ -8,6 +8,7 @@ import {
8
8
  useTheme} from '@mui/material';
9
9
  import React from 'react';
10
10
 
11
+ import { accordionVariantStyles } from './Accordion.styles';
11
12
  import type {
12
13
  AccordionActionsProps,
13
14
  AccordionDetailsProps,
@@ -27,80 +28,6 @@ export const Accordion: React.FC<AccordionProps> = ({
27
28
  }) => {
28
29
  const theme = useTheme();
29
30
 
30
- const getVariantStyles = () => {
31
- const baseTransition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
32
-
33
- switch (variant) {
34
- case 'glass':
35
- return {
36
- backgroundColor: alpha(theme.palette.background.paper, 0.08),
37
- backdropFilter: 'blur(24px) saturate(180%)',
38
- border: `1px solid ${alpha(theme.palette.primary.main, 0.15)}`,
39
- borderRadius: theme.spacing(1.5),
40
- transition: baseTransition,
41
- '&:before': {
42
- display: 'none',
43
- },
44
- '&:hover': {
45
- backgroundColor: alpha(theme.palette.background.paper, 0.12),
46
- border: `1px solid ${alpha(theme.palette.primary.main, 0.25)}`,
47
- transform: 'translateY(-1px)',
48
- boxShadow: `0 8px 32px ${alpha(theme.palette.primary.main, 0.12)}`,
49
- },
50
- '&.Mui-expanded': {
51
- backgroundColor: alpha(theme.palette.background.paper, 0.15),
52
- border: `1px solid ${alpha(theme.palette.primary.main, 0.3)}`,
53
- },
54
- };
55
- case 'bordered':
56
- return {
57
- border: `1px solid ${theme.palette.divider}`,
58
- borderRadius: theme.spacing(1),
59
- transition: baseTransition,
60
- '&:before': {
61
- display: 'none',
62
- },
63
- '&:not(:last-child)': {
64
- marginBottom: theme.spacing(1),
65
- },
66
- '&:hover': {
67
- borderColor: theme.palette.primary.main,
68
- boxShadow: `0 0 0 1px ${alpha(theme.palette.primary.main, 0.1)}`,
69
- },
70
- '&.Mui-expanded': {
71
- borderColor: theme.palette.primary.main,
72
- },
73
- };
74
- case 'separated':
75
- return {
76
- boxShadow: theme.shadows[2],
77
- borderRadius: theme.spacing(1.5),
78
- transition: baseTransition,
79
- '&:before': {
80
- display: 'none',
81
- },
82
- '&:not(:last-child)': {
83
- marginBottom: theme.spacing(2),
84
- },
85
- '&:hover': {
86
- boxShadow: theme.shadows[4],
87
- transform: 'translateY(-2px)',
88
- },
89
- '&.Mui-expanded': {
90
- boxShadow: theme.shadows[6],
91
- transform: 'translateY(-1px)',
92
- },
93
- };
94
- default:
95
- return {
96
- transition: baseTransition,
97
- '&:hover': {
98
- backgroundColor: alpha(theme.palette.action.hover, 0.04),
99
- },
100
- };
101
- }
102
- };
103
-
104
31
  return (
105
32
  <MuiAccordion
106
33
  disabled={disabled}
@@ -109,7 +36,7 @@ export const Accordion: React.FC<AccordionProps> = ({
109
36
  onChange={onChange}
110
37
  data-testid={testId}
111
38
  sx={{
112
- ...getVariantStyles(),
39
+ ...accordionVariantStyles(theme, variant),
113
40
  ...sx,
114
41
  }}
115
42
  {...props}
@@ -0,0 +1,30 @@
1
+ import type { CardProps } from './Card.types';
2
+
3
+ type CardDefaultedKeys =
4
+ | 'variant'
5
+ | 'interactive'
6
+ | 'glow'
7
+ | 'pulse'
8
+ | 'borderRadius'
9
+ | 'loading';
10
+
11
+ type ResolvedCardProps = CardProps & Required<Pick<CardProps, CardDefaultedKeys>>;
12
+
13
+ const CARD_DEFAULTS: Pick<CardProps, CardDefaultedKeys> = {
14
+ variant: 'elevated',
15
+ interactive: false,
16
+ glow: false,
17
+ pulse: false,
18
+ borderRadius: 'md',
19
+ loading: false,
20
+ };
21
+
22
+ // Strips explicitly-undefined props before the merge, so `glow={undefined}` still
23
+ // falls back to the default exactly as a destructuring default would.
24
+ const definedProps = (props: CardProps): Partial<CardProps> =>
25
+ Object.fromEntries(
26
+ Object.entries(props).filter(([, value]) => value !== undefined),
27
+ ) as Partial<CardProps>;
28
+
29
+ export const resolveCardProps = (props: CardProps): ResolvedCardProps =>
30
+ ({ ...CARD_DEFAULTS, ...definedProps(props) }) as ResolvedCardProps;
@@ -1,14 +1,12 @@
1
- import {
2
- Edit,
3
- Email,
4
- Favorite,
5
- LocationOn,
6
- MoreVert,
7
- Phone,
8
- Share,
9
- ShoppingCart,
10
- Star,
11
- } from '@mui/icons-material';
1
+ import Edit from '@mui/icons-material/Edit';
2
+ import Email from '@mui/icons-material/Email';
3
+ import Favorite from '@mui/icons-material/Favorite';
4
+ import LocationOn from '@mui/icons-material/LocationOn';
5
+ import MoreVert from '@mui/icons-material/MoreVert';
6
+ import Phone from '@mui/icons-material/Phone';
7
+ import Share from '@mui/icons-material/Share';
8
+ import ShoppingCart from '@mui/icons-material/ShoppingCart';
9
+ import Star from '@mui/icons-material/Star';
12
10
  import { Avatar, Box, Button, Chip, Grid,IconButton, Stack, Typography } from '@mui/material';
13
11
  import type { Meta, StoryObj } from '@storybook/react-vite';
14
12