@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
@@ -0,0 +1,255 @@
1
+ import CompleteIcon from '@mui/icons-material/CheckCircle';
2
+ import CloseIcon from '@mui/icons-material/Close';
3
+ import PrevIcon from '@mui/icons-material/NavigateBefore';
4
+ import NextIcon from '@mui/icons-material/NavigateNext';
5
+ import RestartIcon from '@mui/icons-material/Refresh';
6
+ import {
7
+ alpha,
8
+ Box,
9
+ Button,
10
+ IconButton,
11
+ Stack,
12
+ styled,
13
+ Typography } from '@mui/material';
14
+ import React, { } from 'react';
15
+
16
+ import type { TutorialOverlayProps } from './TutorialOverlay.types';
17
+
18
+ // Animation keyframes
19
+
20
+ const StepIndicator = styled(Box)(({ theme }) => ({
21
+ display: 'flex',
22
+ gap: theme.spacing(1),
23
+ marginTop: theme.spacing(2),
24
+ marginBottom: theme.spacing(2) }));
25
+
26
+
27
+ const StepDot = styled(Box, {
28
+ shouldForwardProp: (prop) => prop !== 'active' && prop !== 'completed' })<{ active?: boolean; completed?: boolean }>(({ theme, active, completed }) => ({
29
+ width: 8,
30
+ height: 8,
31
+ borderRadius: '50%',
32
+ background: completed
33
+ ? theme.palette.success.main
34
+ : active
35
+ ? theme.palette.primary.main
36
+ : alpha(theme.palette.text.primary, 0.3),
37
+ transition: 'all 0.3s ease',
38
+ ...(active && {
39
+ transform: 'scale(1.5)' }) }));
40
+
41
+ // Title, body copy, the step dots and the navigation row.
42
+
43
+ // The step's controls: skip/restart on the left, previous/next on the right.
44
+ const TutorialStepNav: React.FC<{
45
+ currentStep: number;
46
+ stepCount: number;
47
+ isLastStep: boolean;
48
+ allowSkip: boolean;
49
+ requiresActionBeforeNext?: boolean;
50
+ onNext: () => void;
51
+ onPrev: () => void;
52
+ onSkip: () => void;
53
+ onRestart: () => void;
54
+ }> = ({
55
+ currentStep,
56
+ stepCount,
57
+ isLastStep,
58
+ allowSkip,
59
+ requiresActionBeforeNext,
60
+ onNext,
61
+ onPrev,
62
+ onSkip,
63
+ onRestart }) => (
64
+ <Stack
65
+ direction="row"
66
+ spacing={1}
67
+ justifyContent="space-between"
68
+ data-testid="tutorial-navigation"
69
+ >
70
+ <Stack direction="row" spacing={1}>
71
+ {allowSkip && (
72
+ <Button size="small" onClick={onSkip} variant="text">
73
+ Skip
74
+ </Button>
75
+ )}
76
+ {stepCount > 1 && (
77
+ <IconButton
78
+ size="small"
79
+ onClick={onRestart}
80
+ disabled={currentStep === 0}
81
+ title="Restart"
82
+ >
83
+ <RestartIcon fontSize="small" />
84
+ </IconButton>
85
+ )}
86
+ </Stack>
87
+
88
+ <Stack direction="row" spacing={1}>
89
+ {stepCount > 1 && currentStep > 0 && (
90
+ <Button
91
+ size="small"
92
+ startIcon={<PrevIcon />}
93
+ onClick={onPrev}
94
+ disabled={currentStep === 0}
95
+ data-testid="tutorial-prev-button"
96
+ >
97
+ Previous
98
+ </Button>
99
+ )}
100
+
101
+ {!isLastStep ? (
102
+ <Button
103
+ variant="contained"
104
+ size="small"
105
+ endIcon={<NextIcon />}
106
+ onClick={onNext}
107
+ disabled={requiresActionBeforeNext}
108
+ title={requiresActionBeforeNext ? 'Complete the required action first' : ''}
109
+ data-testid="tutorial-next-button"
110
+ >
111
+ Next
112
+ </Button>
113
+ ) : (
114
+ <Button
115
+ variant="contained"
116
+ size="small"
117
+ endIcon={<CompleteIcon />}
118
+ onClick={onNext}
119
+ sx={{
120
+ background: 'linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%)' }}
121
+ data-testid="tutorial-finish-button"
122
+ >
123
+ {stepCount === 1 ? 'Complete' : 'Finish'}
124
+ </Button>
125
+ )}
126
+ </Stack>
127
+ </Stack>
128
+ );
129
+
130
+ const StepDots: React.FC<{ currentStep: number; stepCount: number }> = ({
131
+ currentStep,
132
+ stepCount }) => (
133
+ <StepIndicator data-testid="tutorial-step-indicators">
134
+ {Array.from({ length: stepCount }).map((_, index) => (
135
+ <StepDot
136
+ key={index}
137
+ active={index === currentStep}
138
+ completed={index < currentStep}
139
+ data-testid={`tutorial-indicator-${index}`}
140
+ />
141
+ ))}
142
+ </StepIndicator>
143
+ );
144
+
145
+ // Title, and the close button when skipping is allowed.
146
+ const TutorialStepHeader: React.FC<{
147
+ title: React.ReactNode;
148
+ currentStep: number;
149
+ allowSkip: boolean;
150
+ onSkip: () => void;
151
+ }> = ({ title, currentStep, allowSkip, onSkip }) => (
152
+ <Box
153
+ sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}
154
+ >
155
+ <Typography
156
+ id={`tutorial-title-${currentStep}`}
157
+ variant="h6"
158
+ fontWeight="bold"
159
+ sx={{ flex: 1 }}
160
+ data-testid="tutorial-step-title"
161
+ >
162
+ {title}
163
+ </Typography>
164
+ {allowSkip && (
165
+ <IconButton
166
+ size="small"
167
+ onClick={onSkip}
168
+ sx={{ ml: 1, mt: -1, mr: -1 }}
169
+ data-testid="tutorial-close-button"
170
+ >
171
+ <CloseIcon fontSize="small" />
172
+ </IconButton>
173
+ )}
174
+ </Box>
175
+ );
176
+
177
+ export const TutorialStepBody: React.FC<{
178
+ step: TutorialOverlayProps['steps'][number];
179
+ currentStep: number;
180
+ stepCount: number;
181
+ progress: number;
182
+ isLastStep: boolean;
183
+ allowSkip: boolean;
184
+ showProgress: boolean;
185
+ requiresActionBeforeNext?: boolean;
186
+ onNext: () => void;
187
+ onPrev: () => void;
188
+ onSkip: () => void;
189
+ onRestart: () => void;
190
+ }> = ({
191
+ step,
192
+ currentStep,
193
+ stepCount,
194
+ progress: _progress,
195
+ isLastStep,
196
+ allowSkip,
197
+ showProgress,
198
+ requiresActionBeforeNext,
199
+ onNext,
200
+ onPrev,
201
+ onSkip,
202
+ onRestart }) => (
203
+ <Stack spacing={2}>
204
+ <TutorialStepHeader
205
+ title={step.title}
206
+ currentStep={currentStep}
207
+ allowSkip={allowSkip}
208
+ onSkip={onSkip}
209
+ />
210
+
211
+ <Typography
212
+ id={`tutorial-content-${currentStep}`}
213
+ variant="body2"
214
+ color="text.secondary"
215
+ data-testid="tutorial-step-content"
216
+ >
217
+ {step.content}
218
+ </Typography>
219
+
220
+ {showProgress && (
221
+ <Typography variant="caption" color="text.secondary" align="center">
222
+ {currentStep + 1} of {stepCount}
223
+ </Typography>
224
+ )}
225
+
226
+ {step.action && (
227
+ <Button
228
+ variant="contained"
229
+ size="small"
230
+ onClick={step.action.onClick}
231
+ sx={{
232
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
233
+ color: 'white' }}
234
+ >
235
+ {step.action.label}
236
+ </Button>
237
+ )}
238
+
239
+ <StepDots currentStep={currentStep} stepCount={stepCount} />
240
+
241
+ <TutorialStepNav
242
+ currentStep={currentStep}
243
+ stepCount={stepCount}
244
+ isLastStep={isLastStep}
245
+ allowSkip={allowSkip}
246
+ requiresActionBeforeNext={requiresActionBeforeNext}
247
+ onNext={onNext}
248
+ onPrev={onPrev}
249
+ onSkip={onSkip}
250
+ onRestart={onRestart}
251
+ />
252
+ </Stack>
253
+ );
254
+
255
+ // Main component
@@ -1,3 +1,4 @@
1
+ export * from './ConfirmAction';
1
2
  export * from './Dialog';
2
3
  export * from './Modal';
3
4
  export * from './Sonner';
@@ -0,0 +1,58 @@
1
+ import type { AddressAutocompleteProps, AddressOptionValue } from './AddressAutocomplete.types';
2
+
3
+ // Below this the field clears its options rather than querying Places.
4
+ export const MIN_QUERY_LENGTH = 2;
5
+
6
+ type DefaultedKeys =
7
+ | 'variant'
8
+ | 'label'
9
+ | 'placeholder'
10
+ | 'floating'
11
+ | 'error'
12
+ | 'disabled'
13
+ | 'required'
14
+ | 'fullWidth'
15
+ | 'defaultValue'
16
+ | 'getCurrentLocation';
17
+
18
+ export type ResolvedAddressProps = AddressAutocompleteProps &
19
+ Required<Pick<AddressAutocompleteProps, DefaultedKeys>>;
20
+
21
+ const ADDRESS_AUTOCOMPLETE_DEFAULTS: Pick<AddressAutocompleteProps, DefaultedKeys> = {
22
+ variant: 'outlined',
23
+ label: 'Address',
24
+ placeholder: 'Enter an address',
25
+ floating: false,
26
+ error: false,
27
+ disabled: false,
28
+ required: false,
29
+ fullWidth: true,
30
+ defaultValue: '',
31
+ getCurrentLocation: false,
32
+ };
33
+
34
+ // Strips explicitly-undefined props before the merge, so `prop={undefined}` still
35
+ // falls back to the default exactly as a destructuring default would.
36
+ const definedProps = (props: AddressAutocompleteProps): Partial<AddressAutocompleteProps> =>
37
+ Object.fromEntries(
38
+ Object.entries(props).filter(([, value]) => value !== undefined),
39
+ ) as Partial<AddressAutocompleteProps>;
40
+
41
+ // Applied as a merge rather than destructuring defaults, which would otherwise
42
+ // put ten branches into the component's own complexity budget.
43
+ export const resolveAddressProps = (props: AddressAutocompleteProps): ResolvedAddressProps =>
44
+ ({ ...ADDRESS_AUTOCOMPLETE_DEFAULTS, ...definedProps(props) }) as ResolvedAddressProps;
45
+
46
+ export const addressOptionLabel = (option: AddressOptionValue): string => {
47
+ if (typeof option === 'string') return option;
48
+ return option.description || '';
49
+ };
50
+
51
+ // A string only ever equals the same string, so a mixed pair is never a match.
52
+ export const isSameAddressOption = (
53
+ option: AddressOptionValue,
54
+ value: AddressOptionValue,
55
+ ): boolean => {
56
+ if (typeof option === 'string' || typeof value === 'string') return option === value;
57
+ return option.place_id === value.place_id;
58
+ };
@@ -0,0 +1,93 @@
1
+ import type React from 'react';
2
+ import { useCallback, useState } from 'react';
3
+
4
+ import type {
5
+ AddressAutocompleteProps,
6
+ AddressDetails,
7
+ AddressOptionValue,
8
+ } from './AddressAutocomplete.types';
9
+ import { useAddressPredictions } from './useAddressPredictions';
10
+ import { useAddressSelection } from './useAddressSelection';
11
+ import { useCurrentLocation } from './useCurrentLocation';
12
+ import { useGoogleMapsAPI } from './useGoogleMapsAPI';
13
+
14
+ export interface AddressAutocompleteState {
15
+ inputValue: string;
16
+ options: AddressOptionValue[];
17
+ loading: boolean;
18
+ apiError: string | null;
19
+ mapsError: string | null;
20
+ isLoaded: boolean;
21
+ handleInputChange: (event: React.SyntheticEvent, value: string) => void;
22
+ handleSelect: (event: React.SyntheticEvent, value: AddressOptionValue | null) => void;
23
+ handleGetCurrentLocation: () => void;
24
+ }
25
+
26
+ // Owns the state the three Places concerns share — the field's text, whether a
27
+ // request is in flight, and the last error — then hands each concern the slice it
28
+ // writes to.
29
+ export const useAddressAutocomplete = ({
30
+ defaultValue,
31
+ googleMapsApiKey,
32
+ restrictions,
33
+ onSelect,
34
+ }: {
35
+ defaultValue: string;
36
+ googleMapsApiKey: string;
37
+ restrictions: AddressAutocompleteProps['restrictions'];
38
+ onSelect: (address: AddressDetails) => void;
39
+ }): AddressAutocompleteState => {
40
+ const [inputValue, setInputValue] = useState(defaultValue);
41
+ const [loading, setLoading] = useState(false);
42
+ const [apiError, setApiError] = useState<string | null>(null);
43
+
44
+ const { isLoaded, error: mapsError, services, useMockData } = useGoogleMapsAPI(googleMapsApiKey);
45
+
46
+ // A resolved address both notifies the caller and becomes the field's text.
47
+ const applyAddress = useCallback(
48
+ (address: AddressDetails) => {
49
+ onSelect(address);
50
+ setInputValue(address.formatted);
51
+ },
52
+ [onSelect],
53
+ );
54
+
55
+ const { options, handleInputChange } = useAddressPredictions({
56
+ isLoaded,
57
+ services,
58
+ restrictions,
59
+ useMockData,
60
+ setInputValue,
61
+ setLoading,
62
+ setApiError,
63
+ });
64
+
65
+ const handleSelect = useAddressSelection({
66
+ services,
67
+ useMockData,
68
+ setLoading,
69
+ setApiError,
70
+ onSelect,
71
+ applyAddress,
72
+ });
73
+
74
+ const handleGetCurrentLocation = useCurrentLocation({
75
+ services,
76
+ useMockData,
77
+ setLoading,
78
+ setApiError,
79
+ applyAddress,
80
+ });
81
+
82
+ return {
83
+ inputValue,
84
+ options,
85
+ loading,
86
+ apiError,
87
+ mapsError,
88
+ isLoaded,
89
+ handleInputChange,
90
+ handleSelect,
91
+ handleGetCurrentLocation,
92
+ };
93
+ };
@@ -0,0 +1,57 @@
1
+ import type { AddressDetails } from './AddressAutocomplete.types';
2
+
3
+ // The shape shared by google.maps.GeocoderAddressComponent and the canned mock
4
+ // components: enough to read a part out by type.
5
+ interface AddressComponentLike {
6
+ long_name: string;
7
+ types: string[];
8
+ }
9
+
10
+ interface LatLngLike {
11
+ lat: () => number;
12
+ lng: () => number;
13
+ }
14
+
15
+ // Google reports address parts as an unordered list tagged by type, so every part
16
+ // has to be looked up rather than indexed.
17
+ const componentReader =
18
+ (components: readonly AddressComponentLike[]) =>
19
+ (type: string): string =>
20
+ components.find((component) => component.types.includes(type))?.long_name || '';
21
+
22
+ // Places and Geocoder results both make the whole geometry chain optional.
23
+ export const coordinatesOf = (geometry?: { location?: LatLngLike }): AddressDetails['coordinates'] => ({
24
+ lat: geometry?.location?.lat() || 0,
25
+ lng: geometry?.location?.lng() || 0,
26
+ });
27
+
28
+ // The four ways an address can arrive — a mock or live prediction, a mock or live
29
+ // reverse geocode — all land here, so they agree on how a place becomes an
30
+ // AddressDetails.
31
+ export const toAddressDetails = ({
32
+ components,
33
+ formatted,
34
+ coordinates,
35
+ }: {
36
+ components: readonly AddressComponentLike[];
37
+ formatted: string;
38
+ coordinates: AddressDetails['coordinates'];
39
+ }): AddressDetails => {
40
+ const read = componentReader(components);
41
+ const street = `${read('street_number')} ${read('route')}`.trim();
42
+
43
+ return {
44
+ formatted,
45
+ // A street number on its own is not an address; fall back to the route name.
46
+ street: street || read('route'),
47
+ city: read('locality') || read('administrative_area_level_2'),
48
+ state: read('administrative_area_level_1'),
49
+ country: read('country'),
50
+ postalCode: read('postal_code'),
51
+ coordinates,
52
+ };
53
+ };
54
+
55
+ // freeSolo mode hands back whatever was typed, with no components to read.
56
+ export const freeSoloAddress = (value: string): AddressDetails =>
57
+ toAddressDetails({ components: [], formatted: value, coordinates: { lat: 0, lng: 0 } });
@@ -1,4 +1,6 @@
1
- import { Business, Home, LocalShipping } from '@mui/icons-material';
1
+ import Business from '@mui/icons-material/Business';
2
+ import Home from '@mui/icons-material/Home';
3
+ import LocalShipping from '@mui/icons-material/LocalShipping';
2
4
  import { Alert,Box, Paper, Stack, Typography } from '@mui/material';
3
5
  import type { Meta, StoryObj } from '@storybook/react-vite';
4
6
  import React from 'react';
@@ -222,7 +224,7 @@ const WithValidationComponent = () => {
222
224
  } else {
223
225
  setError(false);
224
226
  setHelperText('');
225
- // eslint-disable-next-line no-console
227
+ // eslint-disable-next-line no-console -- demo output for the Storybook docs page
226
228
  console.log(`Address confirmed: ${address.formatted}`);
227
229
  }
228
230
  };
@@ -180,7 +180,7 @@ export const KeyboardNavigation: Story = {
180
180
  googleMapsApiKey: 'demo-key',
181
181
  onSelect: fn(),
182
182
  },
183
- play: async ({ canvasElement }) => {
183
+ play: async ({ canvasElement, args }) => {
184
184
  const canvas = within(canvasElement);
185
185
 
186
186
  try {
@@ -188,9 +188,9 @@ export const KeyboardNavigation: Story = {
188
188
  const input = await canvas.findByPlaceholderText('Use Tab and Enter...');
189
189
  await expect(input).not.toBeDisabled();
190
190
 
191
- // Focus the input
192
- input.focus();
193
- await expect(input).toHaveFocus();
191
+ // Focus the input the way a user would
192
+ await userEvent.click(input);
193
+ await waitFor(() => expect(input).toHaveFocus());
194
194
 
195
195
  // Type to trigger suggestions
196
196
  await userEvent.type(input, 'main');
@@ -215,8 +215,9 @@ export const KeyboardNavigation: Story = {
215
215
  // Try to select with Enter key
216
216
  await userEvent.keyboard('{Enter}');
217
217
 
218
- // Wait a moment for any selection to process
219
- await new Promise((resolve) => setTimeout(resolve, 100));
218
+ // Selection resolves the place asynchronously, so wait on the callback
219
+ // rather than on the clock.
220
+ await waitFor(() => expect(args.onSelect).toHaveBeenCalled());
220
221
 
221
222
  // Test Escape key closes suggestions
222
223
  await userEvent.clear(input);
@@ -313,16 +314,17 @@ export const FocusManagementTest: Story = {
313
314
  const input = await canvas.findByPlaceholderText('Test focus behavior...');
314
315
  await expect(input).not.toBeDisabled();
315
316
 
316
- // Test focus
317
- input.focus();
318
- await expect(input).toHaveFocus();
317
+ // Clicking focuses the field and tabbing away releases it. The DOM
318
+ // focus()/blur() calls this used to make asserted the browser's own API
319
+ // rather than the component's focus handling.
320
+ await userEvent.click(input);
321
+ await waitFor(() => expect(input).toHaveFocus());
319
322
 
320
- // Test blur
321
- input.blur();
322
- await expect(input).not.toHaveFocus();
323
+ await userEvent.tab();
324
+ await waitFor(() => expect(input).not.toHaveFocus());
323
325
 
324
326
  // Focus again and type
325
- input.focus();
327
+ await userEvent.click(input);
326
328
  await userEvent.type(input, 'Focus test');
327
329
  await expect(input).toHaveValue('Focus test');
328
330
 
@@ -462,7 +464,7 @@ export const VisualStatesTest: Story = {
462
464
 
463
465
  // Test focus state
464
466
  await userEvent.click(input);
465
- await expect(input).toHaveFocus();
467
+ await waitFor(() => expect(input).toHaveFocus());
466
468
 
467
469
  // Test typing state
468
470
  await userEvent.type(input, 'Testing');
@@ -549,8 +551,10 @@ export const EdgeCasesTest: Story = {
549
551
 
550
552
  // Test empty input doesn't trigger suggestions
551
553
  await expect(input).toHaveValue('');
552
- const initialSuggestion = body.queryByTestId('address-suggestion-0');
553
- expect(initialSuggestion).not.toBeInTheDocument();
554
+ await waitFor(() => {
555
+ const initialSuggestion = body.queryByTestId('address-suggestion-0');
556
+ expect(initialSuggestion).not.toBeInTheDocument();
557
+ });
554
558
 
555
559
  // Test minimum characters requirement (less than 3 chars)
556
560
  await userEvent.type(input, 'ab');