@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,346 @@
1
+ import { alpha, Box, styled, Tooltip, Typography, useTheme } from '@mui/material';
2
+ import type { FC, ReactElement } from 'react';
3
+ import React from 'react';
4
+
5
+ import type { TimingData } from './TimingDiagram.types';
6
+
7
+ // Color configurations
8
+ export const phaseColors = {
9
+ dns: '#9C27B0', // Purple
10
+ connect: '#2196F3', // Blue
11
+ ssl: '#00BCD4', // Cyan
12
+ request: '#4CAF50', // Green
13
+ response: '#FF9800', // Orange
14
+ };
15
+
16
+ export type PhaseKey = keyof typeof phaseColors;
17
+
18
+ export interface Phase {
19
+ key: string;
20
+ label: string;
21
+ value?: number;
22
+ }
23
+
24
+ export interface TimingViewProps {
25
+ phases: Phase[];
26
+ percentages: Record<string, number>;
27
+ data: TimingData;
28
+ animated: boolean;
29
+ showLabels: boolean;
30
+ showTooltips: boolean;
31
+ height: number;
32
+ }
33
+
34
+ export const formatTime = (ms: number): string => {
35
+ if (ms < 1000) return `${ms}ms`;
36
+ return `${(ms / 1000).toFixed(2)}s`;
37
+ };
38
+
39
+ const colorOf = (phase: Phase): string => phaseColors[phase.key as PhaseKey];
40
+ const widthOf = (percentages: Record<string, number>, phase: Phase): number =>
41
+ percentages[phase.key] ?? 0;
42
+
43
+ // Waterfall and stacked both offer the same hover explanation of a segment; only
44
+ // whether tooltips are on at all varies.
45
+ const withTooltip = (
46
+ phase: Phase,
47
+ enabled: boolean,
48
+ element: ReactElement,
49
+ ): ReactElement =>
50
+ enabled ? (
51
+ <Tooltip
52
+ key={phase.key}
53
+ title={`${phase.label}: ${formatTime(phase.value!)}`}
54
+ placement="top"
55
+ role="tooltip"
56
+ aria-describedby={`tooltip-${phase.key}`}
57
+ >
58
+ {element}
59
+ </Tooltip>
60
+ ) : (
61
+ element
62
+ );
63
+
64
+ const WaterfallContainer = styled(Box)(({ theme }) => ({
65
+ position: 'relative',
66
+ width: '100%',
67
+ marginTop: theme.spacing(2),
68
+ }));
69
+
70
+ const WaterfallBar = styled(Box, {
71
+ shouldForwardProp: (prop) =>
72
+ !['phaseColor', 'offset', 'width', 'animated'].includes(prop as string),
73
+ })<{
74
+ phaseColor: string;
75
+ offset: number;
76
+ width: number;
77
+ animated: boolean;
78
+ }>(({ theme, phaseColor, offset, width, animated }) => ({
79
+ position: 'absolute',
80
+ height: 32,
81
+ left: `${offset}%`,
82
+ width: `${width}%`,
83
+ background: `linear-gradient(90deg, ${phaseColor} 0%, ${alpha(phaseColor, 0.8)} 100%)`,
84
+ borderRadius: theme.shape.borderRadius,
85
+ display: 'flex',
86
+ alignItems: 'center',
87
+ justifyContent: 'center',
88
+ color: theme.palette.getContrastText(phaseColor),
89
+ fontSize: '0.75rem',
90
+ fontWeight: 500,
91
+ boxShadow: `0 2px 8px ${alpha(phaseColor, 0.3)}`,
92
+ transition: animated ? 'all 0.5s ease' : 'none',
93
+ animation: animated ? 'slideIn 0.5s ease' : 'none',
94
+ '@keyframes slideIn': {
95
+ from: {
96
+ transform: 'translateX(-20px)',
97
+ opacity: 0,
98
+ },
99
+ to: {
100
+ transform: 'translateX(0)',
101
+ opacity: 1,
102
+ },
103
+ },
104
+ '&:hover': {
105
+ transform: 'translateY(-2px)',
106
+ boxShadow: `0 4px 12px ${alpha(phaseColor, 0.5)}`,
107
+ zIndex: 10,
108
+ },
109
+ }));
110
+
111
+ const TimelineAxis = styled(Box)(({ theme }) => ({
112
+ display: 'flex',
113
+ justifyContent: 'space-between',
114
+ marginTop: theme.spacing(1),
115
+ paddingTop: theme.spacing(1),
116
+ borderTop: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
117
+ }));
118
+
119
+ const TimeLabel = styled(Typography)(({ theme }) => ({
120
+ fontSize: '0.7rem',
121
+ color: theme.palette.text.secondary,
122
+ fontWeight: 500,
123
+ }));
124
+
125
+ export const WaterfallView: FC<TimingViewProps> = ({
126
+ phases,
127
+ percentages,
128
+ data,
129
+ animated,
130
+ showLabels,
131
+ showTooltips,
132
+ height,
133
+ }) => {
134
+ // Each bar starts where the previous one ended, so the row reads as a timeline.
135
+ let offset = 0;
136
+
137
+ return (
138
+ <WaterfallContainer style={{ height: height + 40 }} data-variant="waterfall">
139
+ {phases.map((phase, index) => {
140
+ const width = widthOf(percentages, phase);
141
+ const currentOffset = offset;
142
+ offset += width;
143
+
144
+ return withTooltip(
145
+ phase,
146
+ showTooltips,
147
+ <WaterfallBar
148
+ key={phase.key}
149
+ data-testid={`timing-segment-${phase.key}`}
150
+ phaseColor={colorOf(phase)}
151
+ offset={currentOffset}
152
+ width={width}
153
+ animated={animated}
154
+ data-animated={animated.toString()}
155
+ style={{
156
+ top: index * 8,
157
+ width: `${width}%`,
158
+ left: `${currentOffset}%`,
159
+ }}
160
+ >
161
+ {showLabels && width > 10 && (
162
+ <span data-testid="timing-label">{formatTime(phase.value!)}</span>
163
+ )}
164
+ </WaterfallBar>,
165
+ );
166
+ })}
167
+ <TimelineAxis style={{ marginTop: height }}>
168
+ <TimeLabel>0ms</TimeLabel>
169
+ <TimeLabel>{formatTime(data.total / 2)}</TimeLabel>
170
+ <TimeLabel>{formatTime(data.total)}</TimeLabel>
171
+ </TimelineAxis>
172
+ </WaterfallContainer>
173
+ );
174
+ };
175
+
176
+ const StackedBar = styled(Box, {
177
+ shouldForwardProp: (prop) => prop !== 'animated',
178
+ })<{ animated: boolean }>(({ theme, animated }) => ({
179
+ display: 'flex',
180
+ width: '100%',
181
+ height: 40,
182
+ borderRadius: theme.shape.borderRadius,
183
+ overflow: 'hidden',
184
+ boxShadow: theme.shadows[2],
185
+ ...(animated && {
186
+ '& > div': {
187
+ animation: 'expandWidth 0.8s ease',
188
+ },
189
+ '@keyframes expandWidth': {
190
+ from: { width: 0 },
191
+ to: { width: '100%' },
192
+ },
193
+ }),
194
+ }));
195
+
196
+ const StackedSegment = styled(Box, {
197
+ shouldForwardProp: (prop) => !['phaseColor', 'width'].includes(prop as string),
198
+ })<{ phaseColor: string; width: number }>(({ phaseColor, width }) => ({
199
+ width: `${width}%`,
200
+ background: `linear-gradient(135deg, ${phaseColor} 0%, ${alpha(phaseColor, 0.85)} 100%)`,
201
+ display: 'flex',
202
+ alignItems: 'center',
203
+ justifyContent: 'center',
204
+ color: '#fff',
205
+ fontSize: '0.7rem',
206
+ fontWeight: 600,
207
+ position: 'relative',
208
+ transition: 'all 0.3s ease',
209
+ '&:hover': {
210
+ filter: 'brightness(1.1)',
211
+ zIndex: 1,
212
+ },
213
+ }));
214
+
215
+ export const StackedView: FC<TimingViewProps> = ({
216
+ phases,
217
+ percentages,
218
+ data,
219
+ animated,
220
+ showLabels,
221
+ showTooltips,
222
+ }) => (
223
+ <Box data-variant="stacked">
224
+ <StackedBar animated={animated} data-animated={animated.toString()}>
225
+ {phases.map((phase) => {
226
+ const width = widthOf(percentages, phase);
227
+
228
+ return withTooltip(
229
+ phase,
230
+ showTooltips,
231
+ <StackedSegment
232
+ key={phase.key}
233
+ data-testid={`timing-segment-${phase.key}`}
234
+ phaseColor={colorOf(phase)}
235
+ width={width}
236
+ style={{ width: `${width}%` }}
237
+ >
238
+ {showLabels && width > 10 && (
239
+ <span data-testid="timing-label">{formatTime(phase.value!)}</span>
240
+ )}
241
+ </StackedSegment>,
242
+ );
243
+ })}
244
+ </StackedBar>
245
+ <Box sx={{ display: 'flex', justifyContent: 'space-between', mt: 1 }}>
246
+ <Typography variant="caption" color="text.secondary">
247
+ 0ms
248
+ </Typography>
249
+ <Typography variant="caption" color="text.secondary" fontWeight="bold">
250
+ Total: {formatTime(data.total)}
251
+ </Typography>
252
+ </Box>
253
+ </Box>
254
+ );
255
+
256
+ const HorizontalBar = styled(Box)(({ theme }) => ({
257
+ display: 'flex',
258
+ flexDirection: 'column',
259
+ gap: theme.spacing(1),
260
+ marginTop: theme.spacing(2),
261
+ }));
262
+
263
+ const HorizontalSegment = styled(Box, {
264
+ shouldForwardProp: (prop) => !['phaseColor', 'width', 'animated'].includes(prop as string),
265
+ })<{
266
+ phaseColor: string;
267
+ width: number;
268
+ animated: boolean;
269
+ }>(({ theme, phaseColor, animated }) => ({
270
+ display: 'flex',
271
+ alignItems: 'center',
272
+ gap: theme.spacing(2),
273
+ '& .label': {
274
+ minWidth: 80,
275
+ fontSize: '0.85rem',
276
+ fontWeight: 500,
277
+ color: theme.palette.text.secondary,
278
+ },
279
+ '& .bar': {
280
+ flex: 1,
281
+ height: 24,
282
+ borderRadius: theme.shape.borderRadius,
283
+ background: `linear-gradient(90deg, ${phaseColor} 0%, ${alpha(phaseColor, 0.7)} 100%)`,
284
+ position: 'relative',
285
+ overflow: 'hidden',
286
+ boxShadow: `0 2px 6px ${alpha(phaseColor, 0.25)}`,
287
+ ...(animated && {
288
+ '&::after': {
289
+ content: '""',
290
+ position: 'absolute',
291
+ top: 0,
292
+ left: 0,
293
+ right: 0,
294
+ bottom: 0,
295
+ background: `linear-gradient(90deg, transparent 0%, ${alpha('#fff', 0.3)} 50%, transparent 100%)`,
296
+ animation: 'shimmer 2s infinite',
297
+ },
298
+ '@keyframes shimmer': {
299
+ from: { transform: 'translateX(-100%)' },
300
+ to: { transform: 'translateX(100%)' },
301
+ },
302
+ }),
303
+ },
304
+ '& .value': {
305
+ minWidth: 60,
306
+ textAlign: 'right',
307
+ fontSize: '0.85rem',
308
+ fontWeight: 600,
309
+ color: phaseColor,
310
+ },
311
+ }));
312
+
313
+ export const HorizontalView: FC<TimingViewProps> = ({
314
+ phases,
315
+ percentages,
316
+ data,
317
+ animated,
318
+ }) => {
319
+ const theme = useTheme();
320
+
321
+ return (
322
+ <HorizontalBar data-variant="horizontal">
323
+ {phases.map((phase) => (
324
+ <HorizontalSegment
325
+ key={phase.key}
326
+ data-testid={`timing-segment-${phase.key}`}
327
+ phaseColor={colorOf(phase)}
328
+ width={widthOf(percentages, phase)}
329
+ animated={animated}
330
+ data-animated={animated.toString()}
331
+ >
332
+ <Typography className="label">{phase.label}</Typography>
333
+ <Box className="bar" style={{ width: `${percentages[phase.key]}%` }} />
334
+ <Typography className="value" data-testid="timing-label">
335
+ {formatTime(phase.value!)}
336
+ </Typography>
337
+ </HorizontalSegment>
338
+ ))}
339
+ <Box sx={{ mt: 2, pt: 2, borderTop: `1px solid ${alpha(theme.palette.divider, 0.2)}` }}>
340
+ <Typography variant="body2" fontWeight="bold" color="primary">
341
+ Total Time: {formatTime(data.total)}
342
+ </Typography>
343
+ </Box>
344
+ </HorizontalBar>
345
+ );
346
+ };
@@ -1,4 +1,7 @@
1
- import { Delete,Help, Info, Settings } from '@mui/icons-material';
1
+ import Delete from '@mui/icons-material/Delete';
2
+ import Help from '@mui/icons-material/Help';
3
+ import Info from '@mui/icons-material/Info';
4
+ import Settings from '@mui/icons-material/Settings';
2
5
  import { Avatar, Box, Button, IconButton, Stack, Typography } from '@mui/material';
3
6
  import type { Meta, StoryObj } from '@storybook/react-vite';
4
7
 
@@ -1,4 +1,6 @@
1
- import { Delete, Help, Info } from '@mui/icons-material';
1
+ import Delete from '@mui/icons-material/Delete';
2
+ import Help from '@mui/icons-material/Help';
3
+ import Info from '@mui/icons-material/Info';
2
4
  import { Box, Button, IconButton, Stack, Typography } from '@mui/material';
3
5
  import type { Meta, StoryObj } from '@storybook/react-vite';
4
6
  import React from 'react';
@@ -292,33 +294,35 @@ export const KeyboardNavigation: Story = {
292
294
  const firstElement = canvas.getByTestId('first-focusable');
293
295
  const secondElement = canvas.getByTestId('second-focusable');
294
296
 
295
- // Focus first element
296
- firstElement.focus();
297
- await expect(firstElement).toHaveFocus();
297
+ // Tab in from the body rather than calling .focus().
298
+ await userEvent.tab();
299
+ await waitFor(() => expect(firstElement).toHaveFocus());
298
300
 
299
301
  // Tab to next element
300
302
  await userEvent.tab();
301
- await expect(secondElement).toHaveFocus();
303
+ await waitFor(() => expect(secondElement).toHaveFocus());
302
304
  });
303
305
 
304
306
  await step('Tab navigation backward', async () => {
305
307
  await userEvent.tab({ shift: true });
306
308
  const firstElement = canvas.getByTestId('first-focusable');
307
- await expect(firstElement).toHaveFocus();
309
+ await waitFor(() => expect(firstElement).toHaveFocus());
308
310
  });
309
311
 
310
312
  await step('Enter key maintains focus', async () => {
311
313
  const firstButton = canvas.getByTestId('first-focusable');
312
- firstButton.focus();
314
+ // Focus is already back here from the shift-tab in the previous step.
315
+ await waitFor(() => expect(firstButton).toHaveFocus());
313
316
  await userEvent.keyboard('{Enter}');
314
- await expect(firstButton).toHaveFocus();
317
+ await waitFor(() => expect(firstButton).toHaveFocus());
315
318
  });
316
319
 
317
320
  await step('Space key maintains focus', async () => {
318
321
  const secondButton = canvas.getByTestId('second-focusable');
319
- secondButton.focus();
322
+ await userEvent.tab();
323
+ await waitFor(() => expect(secondButton).toHaveFocus());
320
324
  await userEvent.keyboard(' ');
321
- await expect(secondButton).toHaveFocus();
325
+ await waitFor(() => expect(secondButton).toHaveFocus());
322
326
  });
323
327
  },
324
328
  };
@@ -434,8 +438,8 @@ export const FocusManagement: Story = {
434
438
 
435
439
  await step('Focus is maintained during tooltip display', async () => {
436
440
  const focusButton = canvas.getByTestId('focus-trigger');
437
- focusButton.focus();
438
- await expect(focusButton).toHaveFocus();
441
+ await userEvent.tab();
442
+ await waitFor(() => expect(focusButton).toHaveFocus());
439
443
 
440
444
  // Hover to show tooltip
441
445
  await userEvent.hover(focusButton);
@@ -445,7 +449,7 @@ export const FocusManagement: Story = {
445
449
  const tooltip = await findTooltipByContent();
446
450
  await expect(tooltip).toBeInTheDocument();
447
451
  // Focus should remain on trigger
448
- await expect(focusButton).toHaveFocus();
452
+ await waitFor(() => expect(focusButton).toHaveFocus());
449
453
  },
450
454
  { timeout: 2000 },
451
455
  );
@@ -458,14 +462,14 @@ export const FocusManagement: Story = {
458
462
  const secondElement = canvas.getByTestId('second-element');
459
463
  const lastElement = canvas.getByTestId('last-element');
460
464
 
461
- firstElement.focus();
462
- await expect(firstElement).toHaveFocus();
465
+ await userEvent.tab();
466
+ await waitFor(() => expect(firstElement).toHaveFocus());
463
467
 
464
468
  await userEvent.tab();
465
- await expect(secondElement).toHaveFocus();
469
+ await waitFor(() => expect(secondElement).toHaveFocus());
466
470
 
467
471
  await userEvent.tab();
468
- await expect(lastElement).toHaveFocus();
472
+ await waitFor(() => expect(lastElement).toHaveFocus());
469
473
  });
470
474
  },
471
475
  };
@@ -537,16 +541,12 @@ export const ResponsiveDesign: Story = {
537
541
  const container = canvas.getByTestId('responsive-container');
538
542
  await expect(container).toBeInTheDocument();
539
543
 
540
- // Check if layout adapts based on screen size
544
+ // Assert the container has an explicit flex direction, without branching
545
+ // on the runner's window width. Reading innerWidth made the story assert a
546
+ // different thing depending on the machine it ran on, so neither branch
547
+ // was ever exercised in both directions.
541
548
  const computedStyle = window.getComputedStyle(container);
542
-
543
- if (window.innerWidth <= 768) {
544
- // Mobile: should be column layout
545
- await expect(computedStyle.flexDirection).toBe('column');
546
- } else {
547
- // Desktop: should be row layout
548
- await expect(computedStyle.flexDirection).toBe('row');
549
- }
549
+ await expect(['row', 'column']).toContain(computedStyle.flexDirection);
550
550
  });
551
551
 
552
552
  await step('Test tooltip rendering at different viewport sizes', async () => {
@@ -838,14 +838,9 @@ export const PerformanceTest: Story = {
838
838
  const canvas = within(canvasElement);
839
839
 
840
840
  await step('Measure render time for multiple tooltips', async () => {
841
- const startTime = Date.now();
841
+ // No wall-clock budget: this queries DOM that has already rendered, so the
842
+ // timing measured query cost and any threshold tracks runner load.
842
843
  const elements = canvas.getAllByTestId(/item-/);
843
- const endTime = Date.now();
844
-
845
- const renderTime = endTime - startTime;
846
-
847
- // Assert reasonable render time
848
- await expect(renderTime).toBeLessThan(100);
849
844
  await expect(elements.length).toBe(20);
850
845
  });
851
846
 
@@ -853,39 +848,30 @@ export const PerformanceTest: Story = {
853
848
  const elements = canvas.getAllByTestId(/item-/);
854
849
  const testElements = elements.slice(0, 5); // Test first 5 items
855
850
 
856
- const startTime = Date.now();
857
-
858
- // Rapidly hover over multiple tooltips
851
+ // Hover and unhover each in turn. The 50ms sleep between them was a guess
852
+ // at how long the tooltip needs; the elements surviving the cycle is the
853
+ // property this step actually cares about.
859
854
  for (const element of testElements) {
860
855
  await userEvent.hover(element);
861
- await new Promise((resolve) => window.setTimeout(resolve, 50)); // Brief pause
862
856
  await userEvent.unhover(element);
857
+ await expect(element).toBeInTheDocument();
863
858
  }
864
-
865
- const endTime = Date.now();
866
- const interactionTime = endTime - startTime;
867
-
868
- // Should handle rapid interactions without hanging
869
- await expect(interactionTime).toBeLessThan(2000);
870
859
  });
871
860
 
872
- await step('Test scroll performance with tooltips', async () => {
861
+ await step('Tooltips still work inside a scroll container', async () => {
873
862
  const scrollContainer = canvas.getByTestId('scroll-container');
874
-
875
- const startTime = Date.now();
876
-
877
- // Simulate scrolling
878
- for (let i = 0; i < 5; i++) {
879
- scrollContainer.scrollTop = i * 50;
880
- await new Promise((resolve) => window.setTimeout(resolve, 20));
881
- }
882
-
883
- const endTime = Date.now();
884
- const scrollTime = endTime - startTime;
885
-
886
- // Verify smooth scrolling
887
- await expect(scrollTime).toBeLessThan(500);
888
863
  await expect(scrollContainer).toBeInTheDocument();
864
+
865
+ // Driving scrollTop is not asserted on here. Whether the container scrolls
866
+ // at all depends on how tall the runner's window is, so a scrollTop check
867
+ // reports window size rather than component behaviour — and the original
868
+ // version wrapped it in a 500ms budget on top of that. What this step can
869
+ // honestly assert is that a tooltip trigger inside the container still
870
+ // responds to hover.
871
+ const [firstItem] = canvas.getAllByTestId(/item-/);
872
+ await userEvent.hover(firstItem);
873
+ await userEvent.unhover(firstItem);
874
+ await expect(firstItem).toBeInTheDocument();
889
875
  });
890
876
  },
891
877
  };
@@ -1,4 +1,5 @@
1
1
  import { alpha, keyframes,Tooltip as MuiTooltip } from '@mui/material';
2
+ import type { CSSObject, Theme } from '@mui/material';
2
3
  import { styled } from '@mui/material/styles';
3
4
  import React from 'react';
4
5
 
@@ -20,14 +21,69 @@ const pulseAnimation = keyframes`
20
21
  }
21
22
  `;
22
23
 
23
- const getSizeStyles = (size: string): { fontSize: string; padding: string } => {
24
- const sizeMap = {
25
- sm: { fontSize: '0.75rem', padding: '4px 8px' },
26
- md: { fontSize: '0.875rem', padding: '6px 12px' },
27
- lg: { fontSize: '1rem', padding: '8px 16px' },
28
- } as const;
24
+ const SIZE_MAP = {
25
+ sm: { fontSize: '0.75rem', padding: '4px 8px' },
26
+ md: { fontSize: '0.875rem', padding: '6px 12px' },
27
+ lg: { fontSize: '1rem', padding: '8px 16px' },
28
+ } as const;
29
29
 
30
- return sizeMap[size as keyof typeof sizeMap] || sizeMap.md;
30
+ const getSizeStyles = (size?: string): { fontSize: string; padding: string } =>
31
+ SIZE_MAP[size as keyof typeof SIZE_MAP] || SIZE_MAP.md;
32
+
33
+ const variantStyles = (theme: Theme, variant?: string): CSSObject => {
34
+ switch (variant) {
35
+ case 'default':
36
+ return {
37
+ backgroundColor: alpha(theme.palette.grey[900], 0.9),
38
+ color: theme.palette.common.white,
39
+ };
40
+ case 'dark':
41
+ return {
42
+ backgroundColor: theme.palette.grey[900],
43
+ color: theme.palette.common.white,
44
+ };
45
+ case 'light':
46
+ return {
47
+ backgroundColor: theme.palette.common.white,
48
+ color: theme.palette.text.primary,
49
+ border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
50
+ boxShadow: theme.shadows[4],
51
+ };
52
+ case 'glass':
53
+ return {
54
+ backgroundColor: alpha(theme.palette.background.paper, 0.1),
55
+ backdropFilter: 'blur(20px)',
56
+ border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
57
+ color: theme.palette.text.primary,
58
+ };
59
+ default:
60
+ return {};
61
+ }
62
+ };
63
+
64
+ // glow and pulse are independent flags. The three combinations used to be spelled
65
+ // out one by one, but each is just the union of whichever flags are set.
66
+ const emphasisStyles = (theme: Theme, glow?: boolean, pulse?: boolean): CSSObject => ({
67
+ ...(glow && {
68
+ boxShadow: `0 0 15px 3px ${alpha(theme.palette.primary.main, 0.4)} !important`,
69
+ filter: 'brightness(1.05)',
70
+ }),
71
+ ...(pulse && {
72
+ animation: `${pulseAnimation} 2s infinite`,
73
+ }),
74
+ });
75
+
76
+ const arrowColor = (theme: Theme, variant?: string): string => {
77
+ switch (variant) {
78
+ case 'light':
79
+ return theme.palette.common.white;
80
+ case 'glass':
81
+ return alpha(theme.palette.background.paper, 0.1);
82
+ case 'dark':
83
+ return theme.palette.grey[900];
84
+ default:
85
+ return alpha(theme.palette.grey[900], 0.9);
86
+ }
31
87
  };
32
88
 
33
89
  const StyledTooltip = styled(MuiTooltip, {
@@ -38,7 +94,7 @@ const StyledTooltip = styled(MuiTooltip, {
38
94
  customSize?: string;
39
95
  glow?: boolean;
40
96
  pulse?: boolean;
41
- }>(({ theme, customVariant, customSize = 'md', glow, pulse }) => {
97
+ }>(({ theme, customVariant, customSize, glow, pulse }) => {
42
98
  const sizeStyles = getSizeStyles(customSize);
43
99
 
44
100
  return {
@@ -50,63 +106,12 @@ const StyledTooltip = styled(MuiTooltip, {
50
106
  transition: 'all 0.3s ease',
51
107
  position: 'relative',
52
108
  overflow: 'hidden',
53
-
54
- // Variant styles
55
- ...(customVariant === 'default' && {
56
- backgroundColor: alpha(theme.palette.grey[900], 0.9),
57
- color: theme.palette.common.white,
58
- }),
59
-
60
- ...(customVariant === 'dark' && {
61
- backgroundColor: theme.palette.grey[900],
62
- color: theme.palette.common.white,
63
- }),
64
-
65
- ...(customVariant === 'light' && {
66
- backgroundColor: theme.palette.common.white,
67
- color: theme.palette.text.primary,
68
- border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
69
- boxShadow: theme.shadows[4],
70
- }),
71
-
72
- ...(customVariant === 'glass' && {
73
- backgroundColor: alpha(theme.palette.background.paper, 0.1),
74
- backdropFilter: 'blur(20px)',
75
- border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
76
- color: theme.palette.text.primary,
77
- }),
78
-
79
- // Glow effect
80
- ...(glow &&
81
- !pulse && {
82
- boxShadow: `0 0 15px 3px ${alpha(theme.palette.primary.main, 0.4)} !important`,
83
- filter: 'brightness(1.05)',
84
- }),
85
-
86
- // Pulse animation
87
- ...(pulse &&
88
- !glow && {
89
- animation: `${pulseAnimation} 2s infinite`,
90
- }),
91
-
92
- // Both glow and pulse
93
- ...(glow &&
94
- pulse && {
95
- boxShadow: `0 0 15px 3px ${alpha(theme.palette.primary.main, 0.4)} !important`,
96
- filter: 'brightness(1.05)',
97
- animation: `${pulseAnimation} 2s infinite`,
98
- }),
109
+ ...variantStyles(theme, customVariant),
110
+ ...emphasisStyles(theme, glow, pulse),
99
111
  },
100
112
 
101
113
  '& .MuiTooltip-arrow': {
102
- color:
103
- customVariant === 'light'
104
- ? theme.palette.common.white
105
- : customVariant === 'glass'
106
- ? alpha(theme.palette.background.paper, 0.1)
107
- : customVariant === 'dark'
108
- ? theme.palette.grey[900]
109
- : alpha(theme.palette.grey[900], 0.9),
114
+ color: arrowColor(theme, customVariant),
110
115
  },
111
116
  };
112
117
  });