@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.
- package/components-catalog.md +14 -0
- package/package.json +42 -27
- package/src/__tests__/data-grid.test.tsx +125 -0
- package/src/__tests__/package-wiring.test.ts +12 -8
- package/src/__tests__/social-login-button.test.tsx +5 -5
- package/src/__tests__/viewport.test.ts +50 -0
- package/src/charts/index.ts +8 -0
- package/src/components/data-display/Alert/Alert.md +1 -1
- package/src/components/data-display/Alert/Alert.stories.tsx +5 -7
- package/src/components/data-display/Alert/Alert.styles.tsx +255 -0
- package/src/components/data-display/Alert/Alert.test.stories.tsx +11 -13
- package/src/components/data-display/Alert/Alert.tsx +135 -328
- package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +3 -1
- package/src/components/data-display/AlertDialog/AlertDialog.tsx +191 -78
- package/src/components/data-display/AlertDialog/AlertDialog.types.ts +8 -1
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +5 -5
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +44 -35
- package/src/components/data-display/Avatar/Avatar.stories.tsx +4 -1
- package/src/components/data-display/Avatar/Avatar.test.stories.tsx +5 -3
- package/src/components/data-display/Avatar/Avatar.tsx +154 -450
- package/src/components/data-display/Avatar/Avatar.view.tsx +396 -0
- package/src/components/data-display/Badge/Badge.animations.ts +67 -0
- package/src/components/data-display/Badge/Badge.helpers.ts +74 -0
- package/src/components/data-display/Badge/Badge.stories.tsx +14 -16
- package/src/components/data-display/Badge/Badge.styles.ts +261 -0
- package/src/components/data-display/Badge/Badge.test.stories.tsx +32 -31
- package/src/components/data-display/Badge/Badge.tsx +151 -557
- package/src/components/data-display/Badge/Badge.variants.ts +106 -0
- package/src/components/data-display/Badge/BadgeContent.tsx +107 -0
- package/src/components/data-display/Banner/Banner.stories.tsx +5 -1
- package/src/components/data-display/Banner/Banner.styles.ts +120 -0
- package/src/components/data-display/Banner/Banner.tsx +179 -224
- package/src/components/data-display/Chart/Chart.md +48 -1
- package/src/components/data-display/Chart/Chart.tsx +82 -500
- package/src/components/data-display/Chart/Chart.types.ts +2 -0
- package/src/components/data-display/Chart/ChartComposables.tsx +121 -0
- package/src/components/data-display/Chart/ChartSpec.types.ts +70 -0
- package/src/components/data-display/Chart/SpecChart.stories.tsx +150 -0
- package/src/components/data-display/Chart/SpecChart.test.stories.tsx +56 -0
- package/src/components/data-display/Chart/SpecChart.tsx +53 -0
- package/src/components/data-display/Chart/__tests__/SpecChart.test.tsx +60 -0
- package/src/components/data-display/Chart/__tests__/chart-format.test.ts +43 -0
- package/src/components/data-display/Chart/__tests__/chart-spec.test.ts +119 -0
- package/src/components/data-display/Chart/chart-format.ts +26 -0
- package/src/components/data-display/Chart/chart-internals.ts +148 -0
- package/src/components/data-display/Chart/chart-renderers.tsx +338 -0
- package/src/components/data-display/Chart/chart-spec-registry.ts +74 -0
- package/src/components/data-display/Chart/chart-spec.ts +171 -0
- package/src/components/data-display/Chart/index.ts +30 -0
- package/src/components/data-display/Chip/Chip.helpers.tsx +131 -0
- package/src/components/data-display/Chip/Chip.stories.tsx +2 -1
- package/src/components/data-display/Chip/Chip.test.stories.tsx +11 -8
- package/src/components/data-display/Chip/Chip.tsx +31 -94
- package/src/components/data-display/DataGrid/DataGrid.body.tsx +141 -0
- package/src/components/data-display/DataGrid/DataGrid.cells.tsx +238 -0
- package/src/components/data-display/DataGrid/DataGrid.model.ts +315 -0
- package/src/components/data-display/DataGrid/DataGrid.rows.ts +112 -0
- package/src/components/data-display/DataGrid/DataGrid.states.tsx +102 -0
- package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +6 -12
- package/src/components/data-display/DataGrid/DataGrid.tsx +129 -725
- package/src/components/data-display/DataViews/DataViewsGrid.tsx +187 -0
- package/src/components/data-display/DataViews/DataViewsTableBase.tsx +400 -0
- package/src/components/data-display/DataViews/ManageViewsDialog.tsx +140 -0
- package/src/components/data-display/DataViews/SaveViewModal.tsx +289 -0
- package/src/components/data-display/DataViews/__tests__/DataViewsGrid.test.tsx +271 -0
- package/src/components/data-display/DataViews/__tests__/DataViewsTableBase.test.tsx +134 -0
- package/src/components/data-display/DataViews/__tests__/SaveViewModal.test.tsx +83 -0
- package/src/components/data-display/DataViews/__tests__/data-views-filter.test.ts +115 -0
- package/src/components/data-display/DataViews/__tests__/data-views-preview.test.ts +53 -0
- package/src/components/data-display/DataViews/__tests__/range-pill.test.tsx +114 -0
- package/src/components/data-display/DataViews/base-card.stories.tsx +136 -0
- package/src/components/data-display/DataViews/base-card.tsx +278 -0
- package/src/components/data-display/DataViews/data-views-filter-panel.tsx +184 -0
- package/src/components/data-display/DataViews/data-views-filter.ts +138 -0
- package/src/components/data-display/DataViews/data-views-grid-helpers.tsx +248 -0
- package/src/components/data-display/DataViews/data-views-grid-parts.tsx +400 -0
- package/src/components/data-display/DataViews/data-views-inline-bar.tsx +257 -0
- package/src/components/data-display/DataViews/data-views-layout-context.tsx +176 -0
- package/src/components/data-display/DataViews/data-views-pagination.tsx +31 -0
- package/src/components/data-display/DataViews/data-views-preview.ts +53 -0
- package/src/components/data-display/DataViews/data-views-range-pill.tsx +277 -0
- package/src/components/data-display/DataViews/data-views-table-parts.tsx +150 -0
- package/src/components/data-display/DataViews/data-views-toolbar.tsx +149 -0
- package/src/components/data-display/DataViews/data-views-types.ts +312 -0
- package/src/components/data-display/DataViews/data-views-view-mutations.ts +68 -0
- package/src/components/data-display/DataViews/index.ts +33 -0
- package/src/components/data-display/DataViews/use-data-views-state.ts +349 -0
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.hooks.ts +104 -0
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.tsx +68 -118
- package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +1 -1
- package/src/components/data-display/DescriptionItem/DescriptionItem.tsx +52 -34
- package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +6 -6
- package/src/components/data-display/EmptyState/EmptyState.tsx +218 -135
- package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +2 -2
- package/src/components/data-display/ErrorState/ErrorState.tsx +83 -55
- package/src/components/data-display/HoverCard/HoverCard.stories.tsx +3 -1
- package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +5 -5
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.hooks.ts +99 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.styles.ts +87 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.tsx +28 -155
- package/src/components/data-display/LazyImage/LazyImage.hooks.ts +203 -0
- package/src/components/data-display/LazyImage/LazyImage.md +1 -1
- package/src/components/data-display/LazyImage/LazyImage.stories.tsx +3 -3
- package/src/components/data-display/LazyImage/LazyImage.tsx +144 -243
- package/src/components/data-display/Lightbox/Lightbox.constants.ts +53 -0
- package/src/components/data-display/Lightbox/Lightbox.helpers.ts +39 -0
- package/src/components/data-display/Lightbox/Lightbox.hooks.ts +259 -0
- package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +28 -35
- package/src/components/data-display/Lightbox/Lightbox.tsx +98 -658
- package/src/components/data-display/Lightbox/LightboxControls.tsx +152 -0
- package/src/components/data-display/Lightbox/LightboxCurrentMedia.tsx +50 -0
- package/src/components/data-display/Lightbox/LightboxMedia.tsx +76 -0
- package/src/components/data-display/Lightbox/LightboxOverlay.tsx +83 -0
- package/src/components/data-display/Lightbox/LightboxStage.tsx +97 -0
- package/src/components/data-display/Lightbox/LightboxThumbnails.tsx +83 -0
- package/src/components/data-display/Lightbox/useLightbox.ts +80 -0
- package/src/components/data-display/LoadingState/LoadingState.helpers.ts +52 -0
- package/src/components/data-display/LoadingState/LoadingState.tsx +117 -87
- package/src/components/data-display/MapPreview/MapControlBar.tsx +116 -0
- package/src/components/data-display/MapPreview/MapDecorations.tsx +155 -0
- package/src/components/data-display/MapPreview/MapLayers.tsx +70 -0
- package/src/components/data-display/MapPreview/MapMarkers.tsx +84 -0
- package/src/components/data-display/MapPreview/MapOverlays.tsx +68 -0
- package/src/components/data-display/MapPreview/MapPreview.constants.ts +19 -0
- package/src/components/data-display/MapPreview/MapPreview.helpers.ts +63 -0
- package/src/components/data-display/MapPreview/MapPreview.hooks.ts +265 -0
- package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +14 -10
- package/src/components/data-display/MapPreview/MapPreview.tsx +52 -882
- package/src/components/data-display/MapPreview/MapPreview.types.ts +8 -1
- package/src/components/data-display/MapPreview/MapSearchBar.tsx +64 -0
- package/src/components/data-display/MapPreview/MapTiles.tsx +70 -0
- package/src/components/data-display/MapPreview/StaticMapView.tsx +63 -0
- package/src/components/data-display/MapPreview/mapProjection.ts +52 -0
- package/src/components/data-display/MapPreview/mapSurface.ts +37 -0
- package/src/components/data-display/MapPreview/useMapPreview.ts +62 -0
- package/src/components/data-display/Popover/Popover.test.stories.tsx +13 -19
- package/src/components/data-display/Progress/Progress.helpers.ts +37 -0
- package/src/components/data-display/Progress/Progress.styles.ts +101 -0
- package/src/components/data-display/Progress/Progress.tsx +185 -213
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +1 -1
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.tsx +1 -1
- package/src/components/data-display/StatCard/StatCard.md +117 -0
- package/src/components/data-display/StatCard/StatCard.stories.tsx +167 -0
- package/src/components/data-display/StatCard/StatCard.test.stories.tsx +240 -0
- package/src/components/data-display/StatCard/StatCard.tsx +236 -0
- package/src/components/data-display/StatCard/StatCard.types.ts +62 -0
- package/src/components/data-display/StatCard/index.ts +7 -0
- package/src/components/data-display/Stepper/Stepper.test.stories.tsx +12 -19
- package/src/components/data-display/Stepper/Stepper.tsx +1 -1
- package/src/components/data-display/Table/Table.helpers.ts +31 -0
- package/src/components/data-display/Table/Table.hooks.ts +161 -0
- package/src/components/data-display/Table/Table.stories.tsx +3 -1
- package/src/components/data-display/Table/Table.styles.ts +223 -0
- package/src/components/data-display/Table/Table.test.stories.tsx +4 -4
- package/src/components/data-display/Table/Table.tsx +291 -748
- package/src/components/data-display/Table/TableParts.hooks.tsx +112 -0
- package/src/components/data-display/Table/TableParts.tsx +298 -0
- package/src/components/data-display/Table/TableStates.tsx +21 -0
- package/src/components/data-display/Timeline/Timeline.stories.tsx +10 -12
- package/src/components/data-display/Timeline/Timeline.test.stories.tsx +30 -44
- package/src/components/data-display/Timeline/Timeline.tsx +23 -284
- package/src/components/data-display/Timeline/TimelineRow.tsx +346 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.tsx +38 -311
- package/src/components/data-display/TimingDiagram/TimingDiagram.views.tsx +346 -0
- package/src/components/data-display/Tooltip/Tooltip.stories.tsx +4 -1
- package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +44 -58
- package/src/components/data-display/Tooltip/Tooltip.tsx +67 -62
- package/src/components/data-display/index.ts +1 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.hooks.ts +96 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.md +140 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx +154 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.stories.tsx +146 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.test.stories.tsx +114 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.tsx +46 -0
- package/src/components/feedback/ConfirmAction/ConfirmAction.types.ts +111 -0
- package/src/components/feedback/ConfirmAction/ConfirmButton.tsx +18 -0
- package/src/components/feedback/ConfirmAction/__tests__/confirm-action.test.tsx +288 -0
- package/src/components/feedback/ConfirmAction/index.ts +16 -0
- package/src/components/feedback/ConfirmAction/with-confirmation.tsx +112 -0
- package/src/components/feedback/Dialog/Dialog.styles.ts +4 -2
- package/src/components/feedback/Dialog/Dialog.tsx +26 -1
- package/src/components/feedback/Dialog/__tests__/dialog-content-spacing.test.tsx +66 -0
- package/src/components/feedback/Modal/Modal.md +1 -1
- package/src/components/feedback/Modal/Modal.stories.tsx +2 -2
- package/src/components/feedback/Sonner/Sonner.tsx +104 -288
- package/src/components/feedback/Sonner/Sonner.types.ts +7 -0
- package/src/components/feedback/Sonner/SonnerToast.tsx +216 -0
- package/src/components/feedback/StackedModal/StackedModal.hooks.ts +226 -0
- package/src/components/feedback/StackedModal/StackedModal.parts.tsx +311 -0
- package/src/components/feedback/StackedModal/StackedModal.stories.tsx +13 -15
- package/src/components/feedback/StackedModal/StackedModal.styles.ts +193 -0
- package/src/components/feedback/StackedModal/StackedModal.tsx +100 -619
- package/src/components/feedback/StackedModal/StackedModal.types.ts +17 -3
- package/src/components/feedback/StackedModal/__tests__/stacked-modal-viewport.test.tsx +104 -0
- package/src/components/feedback/StackedModal/modal-stack.tsx +119 -0
- package/src/components/feedback/Toast/Toast.tsx +92 -81
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.geometry.ts +65 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.hooks.ts +185 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +2 -2
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.tsx +156 -414
- package/src/components/feedback/TutorialOverlay/TutorialStepBody.tsx +255 -0
- package/src/components/feedback/index.ts +1 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.helpers.ts +58 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.hooks.ts +93 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.mapper.ts +57 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +4 -2
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +20 -16
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.tsx +26 -829
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.types.ts +11 -0
- package/src/components/form/AddressAutocomplete/AddressAutocompleteField.tsx +86 -0
- package/src/components/form/AddressAutocomplete/AddressFallbackField.tsx +64 -0
- package/src/components/form/AddressAutocomplete/AddressInputField.tsx +80 -0
- package/src/components/form/AddressAutocomplete/AddressOption.tsx +83 -0
- package/src/components/form/AddressAutocomplete/AddressSuggestionsPaper.tsx +24 -0
- package/src/components/form/AddressAutocomplete/AddressTextField.tsx +43 -0
- package/src/components/form/AddressAutocomplete/mockPlaces.ts +208 -0
- package/src/components/form/AddressAutocomplete/useAddressPredictions.ts +136 -0
- package/src/components/form/AddressAutocomplete/useAddressSelection.ts +113 -0
- package/src/components/form/AddressAutocomplete/useCurrentLocation.ts +116 -0
- package/src/components/form/AddressAutocomplete/useGoogleMapsAPI.ts +75 -0
- package/src/components/form/Button/Button.helpers.ts +36 -0
- package/src/components/form/Button/Button.stories.tsx +4 -1
- package/src/components/form/Button/Button.styles.ts +225 -0
- package/src/components/form/Button/Button.test.stories.tsx +25 -21
- package/src/components/form/Button/Button.tsx +45 -252
- package/src/components/form/Calendar/Calendar.dates.ts +100 -0
- package/src/components/form/Calendar/Calendar.helpers.ts +32 -0
- package/src/components/form/Calendar/Calendar.hooks.ts +278 -0
- package/src/components/form/Calendar/Calendar.keyboard.ts +32 -0
- package/src/components/form/Calendar/Calendar.selection.ts +156 -0
- package/src/components/form/Calendar/Calendar.test.stories.tsx +33 -34
- package/src/components/form/Calendar/Calendar.tsx +63 -551
- package/src/components/form/Calendar/Calendar.types.ts +16 -0
- package/src/components/form/Calendar/CalendarDay.tsx +127 -0
- package/src/components/form/Calendar/CalendarDayCell.tsx +52 -0
- package/src/components/form/Calendar/CalendarMonth.tsx +128 -0
- package/src/components/form/CepField/CepField.md +138 -0
- package/src/components/form/CepField/CepField.stories.tsx +138 -0
- package/src/components/form/CepField/CepField.test.stories.tsx +281 -0
- package/src/components/form/CepField/CepField.tsx +211 -0
- package/src/components/form/CepField/CepField.types.ts +73 -0
- package/src/components/form/CepField/index.ts +2 -0
- package/src/components/form/Checkbox/Checkbox.helpers.ts +28 -0
- package/src/components/form/Checkbox/Checkbox.tsx +19 -16
- package/src/components/form/CodeEditor/CodeEditor.helpers.ts +45 -0
- package/src/components/form/CodeEditor/CodeEditor.hooks.ts +104 -0
- package/src/components/form/CodeEditor/CodeEditor.monaco.ts +109 -0
- package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +17 -18
- package/src/components/form/CodeEditor/CodeEditor.tsx +81 -330
- package/src/components/form/CodeEditor/CodeEditorToolbar.tsx +168 -0
- package/src/components/form/Command/Command.hooks.ts +207 -0
- package/src/components/form/Command/Command.parts.tsx +58 -0
- package/src/components/form/Command/Command.styles.ts +83 -0
- package/src/components/form/Command/Command.tsx +61 -270
- package/src/components/form/CreatableSelect/CreatableSelect.md +1 -1
- package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +1 -1
- package/src/components/form/CreatableSelect/CreatableSelect.tsx +152 -71
- package/src/components/form/CreatableSelect/CreatableSelect.types.ts +13 -0
- package/src/components/form/HeaderButton/HeaderButton.tsx +80 -0
- package/src/components/form/HeaderButton/index.ts +2 -0
- package/src/components/form/Input/Input.stories.tsx +5 -1
- package/src/components/form/Input/Input.styles.ts +176 -0
- package/src/components/form/Input/Input.test.stories.tsx +30 -23
- package/src/components/form/Input/Input.tsx +71 -211
- package/src/components/form/Input/Input.types.ts +11 -0
- package/src/components/form/InputOTP/InputOTP.test.stories.tsx +9 -17
- package/src/components/form/Menubar/Menubar.test.stories.tsx +4 -8
- package/src/components/form/PasswordStrength/PasswordStrength.helpers.tsx +117 -0
- package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +3 -3
- package/src/components/form/PasswordStrength/PasswordStrength.tsx +202 -255
- package/src/components/form/PhoneInput/PhoneInput.hooks.ts +134 -0
- package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +4 -4
- package/src/components/form/PhoneInput/PhoneInput.tsx +279 -292
- package/src/components/form/PhoneInput/countries.ts +54 -0
- package/src/components/form/PhoneInput/phone.ts +47 -0
- package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +9 -9
- package/src/components/form/RichTextEditor/RichTextEditor.dom.ts +124 -0
- package/src/components/form/RichTextEditor/RichTextEditor.format.ts +61 -0
- package/src/components/form/RichTextEditor/RichTextEditor.hooks.ts +142 -0
- package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +5 -12
- package/src/components/form/RichTextEditor/RichTextEditor.tsx +98 -410
- package/src/components/form/RichTextEditor/RichTextEditorToolbar.tsx +187 -0
- package/src/components/form/SearchPalette/SearchPalette.parts.tsx +22 -0
- package/src/components/form/SearchPalette/SearchPalette.tsx +13 -9
- package/src/components/form/Select/Select.test.stories.tsx +35 -27
- package/src/components/form/Slider/Slider.test.stories.tsx +9 -11
- package/src/components/form/Switch/Switch.md +2 -1
- package/src/components/form/Switch/Switch.test.stories.tsx +6 -6
- package/src/components/form/Textarea/Textarea.styles.ts +143 -0
- package/src/components/form/Textarea/Textarea.test.stories.tsx +20 -26
- package/src/components/form/Textarea/Textarea.tsx +114 -535
- package/src/components/form/Textarea/TextareaRichEditor.tsx +377 -0
- package/src/components/form/Toggle/Toggle.styles.ts +211 -0
- package/src/components/form/Toggle/Toggle.test.stories.tsx +19 -27
- package/src/components/form/Toggle/Toggle.tsx +10 -185
- package/src/components/form/UploadButton/UploadButton.hooks.ts +323 -0
- package/src/components/form/UploadButton/UploadButton.parts.tsx +210 -0
- package/src/components/form/UploadButton/UploadButton.stories.tsx +2 -1
- package/src/components/form/UploadButton/UploadButton.test.stories.tsx +123 -4
- package/src/components/form/UploadButton/UploadButton.tsx +76 -305
- package/src/components/form/UploadButton/UploadButton.types.ts +16 -0
- package/src/components/form/index.ts +1 -0
- package/src/components/form/total-form/Fields/CepField.tsx +143 -0
- package/src/components/form/total-form/Fields/ToggleField.tsx +61 -0
- package/src/components/form/total-form/Fields/index.ts +4 -0
- package/src/components/form/total-form/FormContainer.tsx +3 -3
- package/src/components/form/total-form/SubmitButton.tsx +25 -2
- package/src/components/form/total-form/total-form.test.tsx +118 -1
- package/src/components/layout/Accordion/Accordion.md +1 -1
- package/src/components/layout/Accordion/Accordion.stories.tsx +5 -1
- package/src/components/layout/Accordion/Accordion.styles.ts +80 -0
- package/src/components/layout/Accordion/Accordion.test.stories.tsx +32 -50
- package/src/components/layout/Accordion/Accordion.tsx +2 -75
- package/src/components/layout/Card/Card.helpers.ts +30 -0
- package/src/components/layout/Card/Card.stories.tsx +9 -11
- package/src/components/layout/Card/Card.styles.ts +182 -0
- package/src/components/layout/Card/Card.test.stories.tsx +46 -46
- package/src/components/layout/Card/Card.tsx +24 -241
- package/src/components/layout/CardGrid/CardGrid.md +96 -0
- package/src/components/layout/CardGrid/CardGrid.tsx +140 -0
- package/src/components/layout/CardGrid/__tests__/CardGrid.test.tsx +92 -0
- package/src/components/layout/CardGrid/index.ts +2 -0
- package/src/components/layout/Collapsible/Collapsible.helpers.ts +83 -0
- package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +7 -3
- package/src/components/layout/Collapsible/Collapsible.tsx +111 -107
- package/src/components/layout/Container/Container.test.stories.tsx +11 -16
- package/src/components/layout/ContentToolbar/ColumnsMenu.tsx +1 -1
- package/src/components/layout/ContentToolbar/ContentToolbar.tsx +120 -44
- package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +9 -0
- package/src/components/layout/ContentToolbar/FilterTrigger.tsx +1 -1
- package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +58 -43
- package/src/components/layout/ContentToolbar/MultiSelectTriggers.tsx +139 -0
- package/src/components/layout/ContentToolbar/SavedViewsMenu.tsx +10 -3
- package/src/components/layout/ContentToolbar/SortByDropdown.tsx +35 -11
- package/src/components/layout/ContentToolbar/ViewSelector.tsx +5 -6
- package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +21 -0
- package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +27 -1
- package/src/components/layout/Dashboard/Dashboard.tsx +27 -8
- package/src/components/layout/Dashboard/DashboardParts.tsx +12 -19
- package/src/components/layout/Dashboard/index.ts +2 -1
- package/src/components/layout/Drawer/Drawer.stories.tsx +8 -10
- package/src/components/layout/Drawer/Drawer.test.stories.tsx +18 -14
- package/src/components/layout/Drawer/Drawer.tsx +67 -53
- package/src/components/layout/Resizable/Resizable.helpers.ts +133 -0
- package/src/components/layout/Resizable/Resizable.hooks.ts +68 -0
- package/src/components/layout/Resizable/Resizable.tsx +38 -153
- package/src/components/layout/ScrollArea/ScrollArea.helpers.ts +51 -0
- package/src/components/layout/ScrollArea/ScrollArea.styles.ts +248 -0
- package/src/components/layout/ScrollArea/ScrollArea.tsx +183 -374
- package/src/components/layout/ScrollArea/ScrollAreaContent.tsx +70 -0
- package/src/components/layout/ScrollArea/ScrollViewport.tsx +90 -0
- package/src/components/layout/Separator/Separator.styles.ts +128 -0
- package/src/components/layout/Separator/Separator.test.stories.tsx +3 -12
- package/src/components/layout/Separator/Separator.tsx +6 -132
- package/src/components/layout/SettingsLayout/SettingsLayout.parts.tsx +2 -1
- package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +6 -8
- package/src/components/layout/Sidebar/Sidebar.stories.tsx +10 -12
- package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +35 -37
- package/src/components/layout/Skeleton/Skeleton.helpers.ts +33 -0
- package/src/components/layout/Skeleton/Skeleton.styles.ts +114 -0
- package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +3 -3
- package/src/components/layout/Skeleton/Skeleton.tsx +67 -155
- package/src/components/layout/TableFilter/TableFilter.tsx +2 -1
- package/src/components/navigation/Breadcrumbs/BreadcrumbEntry.tsx +305 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.md +2 -1
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +12 -14
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.styles.tsx +212 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +62 -61
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +116 -429
- package/src/components/navigation/CommandPalette/CommandPalette.helpers.ts +82 -0
- package/src/components/navigation/CommandPalette/CommandPalette.hooks.ts +202 -0
- package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +3 -1
- package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +3 -5
- package/src/components/navigation/CommandPalette/CommandPalette.tsx +108 -341
- package/src/components/navigation/CommandPalette/CommandPaletteResults.tsx +182 -0
- package/src/components/navigation/CommandPalette/ShortcutChip.tsx +18 -0
- package/src/components/navigation/ContextMenu/ContextMenu.md +3 -1
- package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +4 -1
- package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +19 -24
- package/src/components/navigation/ContextMenu/ContextMenu.tsx +70 -54
- package/src/components/navigation/DropdownMenu/DropdownMenu.md +3 -1
- package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +17 -19
- package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +26 -26
- package/src/components/navigation/DropdownMenu/DropdownMenu.tsx +189 -111
- package/src/components/navigation/NavigationMenu/NavigationMegaSection.tsx +66 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.md +7 -2
- package/src/components/navigation/NavigationMenu/NavigationMenu.shell.tsx +218 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.styles.ts +223 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.tsx +119 -808
- package/src/components/navigation/NavigationMenu/NavigationMenuItem.hooks.ts +126 -0
- package/src/components/navigation/NavigationMenu/NavigationMenuItem.tsx +352 -0
- package/src/components/navigation/NavigationMenu/NavigationStandardBody.tsx +148 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +15 -17
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +27 -23
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +11 -13
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +9 -11
- package/src/components/navigation/Pagination/Pagination.helpers.tsx +85 -0
- package/src/components/navigation/Pagination/Pagination.md +2 -1
- package/src/components/navigation/Pagination/Pagination.stories.tsx +4 -6
- package/src/components/navigation/Pagination/Pagination.styles.ts +135 -0
- package/src/components/navigation/Pagination/Pagination.tsx +178 -313
- package/src/components/navigation/Pagination/PaginationParts.tsx +81 -0
- package/src/components/navigation/Tabs/Tabs.md +3 -1
- package/src/components/navigation/Tabs/Tabs.stories.tsx +21 -23
- package/src/components/navigation/Tabs/Tabs.styles.ts +163 -0
- package/src/components/navigation/Tabs/Tabs.test.stories.tsx +37 -37
- package/src/components/navigation/Tabs/Tabs.tsx +192 -361
- package/src/components/navigation/Tabs/TabsPanel.tsx +100 -0
- package/src/components/typography/Blockquote/Blockquote.tsx +1 -1
- package/src/components/typography/Code/Code.test.stories.tsx +5 -5
- package/src/components/typography/Code/Code.tsx +74 -49
- package/src/components/typography/Heading/Heading.test.stories.tsx +8 -8
- package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +5 -12
- package/src/components/typography/Text/Text.tsx +84 -77
- package/src/components/utility/AnimatedIcon/AnimatedIcon.md +4 -4
- package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +19 -21
- package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +19 -12
- package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +9 -9
- package/src/components/utility/InfiniteScroll/InfiniteScroll.hooks.ts +118 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.parts.tsx +54 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +12 -9
- package/src/components/utility/InfiniteScroll/InfiniteScroll.tsx +29 -148
- package/src/components/utility/InstallPrompt/InstallPrompt.helpers.ts +124 -0
- package/src/components/utility/InstallPrompt/InstallPrompt.md +113 -0
- package/src/components/utility/InstallPrompt/InstallPrompt.stories.tsx +118 -0
- package/src/components/utility/InstallPrompt/InstallPrompt.styles.ts +50 -0
- package/src/components/utility/InstallPrompt/InstallPrompt.test.stories.tsx +83 -0
- package/src/components/utility/InstallPrompt/InstallPrompt.tsx +192 -0
- package/src/components/utility/InstallPrompt/InstallPrompt.types.ts +78 -0
- package/src/components/utility/InstallPrompt/__tests__/InstallPrompt.ios.test.tsx +65 -0
- package/src/components/utility/InstallPrompt/__tests__/usePwaInstall.test.tsx +271 -0
- package/src/components/utility/InstallPrompt/index.ts +10 -0
- package/src/components/utility/InstallPrompt/usePwaInstall.ts +171 -0
- package/src/components/utility/Transition/Transition.helpers.ts +40 -0
- package/src/components/utility/Transition/Transition.tsx +17 -68
- package/src/components/utility/VirtualList/VirtualList.hooks.ts +319 -0
- package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +49 -38
- package/src/components/utility/VirtualList/VirtualList.tsx +110 -315
- package/src/components/utility/WorkflowStep/WorkflowStep.helpers.ts +151 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.parts.tsx +371 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +9 -11
- package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +7 -7
- package/src/components/utility/WorkflowStep/WorkflowStep.tsx +51 -449
- package/src/components/utility/WorkflowStep/WorkflowStepRow.tsx +96 -0
- package/src/components/utility/index.ts +1 -0
- package/src/mui/Link.ts +2 -0
- package/src/utils/index.ts +2 -1
- package/src/utils/viewport.ts +65 -0
|
@@ -9,18 +9,41 @@ import { useFormContext } from './FormContext';
|
|
|
9
9
|
export interface SubmitButtonProps {
|
|
10
10
|
/** Button label. */
|
|
11
11
|
children: React.ReactNode;
|
|
12
|
+
/** Test id; defaults to `total-form-submit`. Override when a page renders more than one form. */
|
|
13
|
+
dataTestId?: string;
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The height of a form input control (see `Input` — `.MuiInputBase-root`), so a
|
|
18
|
+
* submit button placed inline with fields lines up with them instead of sitting
|
|
19
|
+
* shorter. Kept in sync with that value by hand.
|
|
20
|
+
*/
|
|
21
|
+
const FIELD_HEIGHT = 56;
|
|
22
|
+
|
|
14
23
|
/**
|
|
15
24
|
* Submit control wired to {@link useFormContext}. Wraps the existing `@12-apps/ui`
|
|
16
25
|
* `Button` as a `type="submit"` button (testid `total-form-submit`) and
|
|
17
26
|
* reflects the form's `submitting` state via `loading`/`disabled`.
|
|
27
|
+
*
|
|
28
|
+
* It matches the form field height and never wraps its label, so it aligns with
|
|
29
|
+
* an adjacent input on the same row (and stays full-width when the row stacks).
|
|
18
30
|
*/
|
|
19
|
-
export function SubmitButton({
|
|
31
|
+
export function SubmitButton({
|
|
32
|
+
children,
|
|
33
|
+
dataTestId = 'total-form-submit',
|
|
34
|
+
}: SubmitButtonProps): React.ReactElement {
|
|
20
35
|
const { submitting } = useFormContext();
|
|
21
36
|
|
|
22
37
|
return (
|
|
23
|
-
<Button
|
|
38
|
+
<Button
|
|
39
|
+
type="submit"
|
|
40
|
+
dataTestId={dataTestId}
|
|
41
|
+
loading={submitting}
|
|
42
|
+
disabled={submitting}
|
|
43
|
+
// `Button` replaces (not merges) its size styles with this `sx`, so restate
|
|
44
|
+
// the md padding/font here alongside the field-height + no-wrap alignment.
|
|
45
|
+
sx={{ height: FIELD_HEIGHT, px: 2, fontSize: '1rem', flexShrink: 0, whiteSpace: 'nowrap' }}
|
|
46
|
+
>
|
|
24
47
|
{children}
|
|
25
48
|
</Button>
|
|
26
49
|
);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest";
|
|
2
2
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
-
import { email, required } from "@12-apps/
|
|
3
|
+
import { email, required } from "@12-apps/forms-core";
|
|
4
4
|
|
|
5
5
|
import { FormContainer, Fields, FormErrorSnackbar, SubmitButton } from "./index";
|
|
6
6
|
import type { FormSubmitHelpers } from "./index";
|
|
7
|
+
import type { CepAddress } from "../CepField";
|
|
7
8
|
|
|
8
9
|
interface LoginValues extends Record<string, string> {
|
|
9
10
|
email: string;
|
|
@@ -95,3 +96,119 @@ describe("total-form (RTL)", () => {
|
|
|
95
96
|
});
|
|
96
97
|
});
|
|
97
98
|
});
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* `Fields.CepField` autofill ownership (FUT-306). The adapter writes resolved
|
|
102
|
+
* address parts into sibling fields, and the rule that keeps it honest runs in
|
|
103
|
+
* BOTH directions: it must never overwrite what the user typed, and it must
|
|
104
|
+
* never leave one CEP's address sitting under a different CEP.
|
|
105
|
+
*/
|
|
106
|
+
interface AddressValues extends Record<string, string> {
|
|
107
|
+
postalCode: string;
|
|
108
|
+
addressLine1: string;
|
|
109
|
+
neighborhood: string;
|
|
110
|
+
city: string;
|
|
111
|
+
state: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const CEP_FILLS = {
|
|
115
|
+
street: "addressLine1",
|
|
116
|
+
neighborhood: "neighborhood",
|
|
117
|
+
city: "city",
|
|
118
|
+
state: "state",
|
|
119
|
+
} as const;
|
|
120
|
+
|
|
121
|
+
const PAULISTA: CepAddress = {
|
|
122
|
+
cep: "01310100",
|
|
123
|
+
street: "Avenida Paulista",
|
|
124
|
+
neighborhood: "Bela Vista",
|
|
125
|
+
city: "São Paulo",
|
|
126
|
+
state: "SP",
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/** A "CEP geral" — a small município's catch-all, which has no logradouro. */
|
|
130
|
+
const CEP_GERAL: CepAddress = {
|
|
131
|
+
cep: "13800000",
|
|
132
|
+
street: null,
|
|
133
|
+
neighborhood: null,
|
|
134
|
+
city: "Mogi Mirim",
|
|
135
|
+
state: "SP",
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
function renderAddressForm(lookup: (cep: string) => Promise<CepAddress | null>) {
|
|
139
|
+
return render(
|
|
140
|
+
<FormContainer<AddressValues>
|
|
141
|
+
initialValues={{
|
|
142
|
+
postalCode: "",
|
|
143
|
+
addressLine1: "",
|
|
144
|
+
neighborhood: "",
|
|
145
|
+
city: "",
|
|
146
|
+
state: "",
|
|
147
|
+
}}
|
|
148
|
+
onSubmit={() => undefined}
|
|
149
|
+
>
|
|
150
|
+
<Fields.CepField name="postalCode" lookup={lookup} fills={CEP_FILLS} debounceMs={0} />
|
|
151
|
+
<Fields.TextField name="addressLine1" label="Endereço" />
|
|
152
|
+
<Fields.TextField name="neighborhood" label="Bairro" />
|
|
153
|
+
<Fields.TextField name="city" label="Cidade" />
|
|
154
|
+
</FormContainer>,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const cepField = (name: string): HTMLInputElement =>
|
|
159
|
+
screen.getByTestId(`total-form-field-${name}`) as HTMLInputElement;
|
|
160
|
+
|
|
161
|
+
const setCep = (value: string): void => {
|
|
162
|
+
fireEvent.change(cepField("postalCode"), { target: { value } });
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
describe("Fields.CepField autofill ownership", () => {
|
|
166
|
+
it("fills the mapped address fields from a resolved CEP", async () => {
|
|
167
|
+
renderAddressForm(async () => PAULISTA);
|
|
168
|
+
|
|
169
|
+
setCep("01310100");
|
|
170
|
+
|
|
171
|
+
await waitFor(() => expect(cepField("addressLine1").value).toBe("Avenida Paulista"));
|
|
172
|
+
expect(cepField("neighborhood").value).toBe("Bela Vista");
|
|
173
|
+
expect(cepField("city").value).toBe("São Paulo");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("clears parts the replacement CEP has no value for", async () => {
|
|
177
|
+
renderAddressForm(async (cep) => (cep === "01310100" ? PAULISTA : CEP_GERAL));
|
|
178
|
+
|
|
179
|
+
setCep("01310100");
|
|
180
|
+
await waitFor(() => expect(cepField("addressLine1").value).toBe("Avenida Paulista"));
|
|
181
|
+
|
|
182
|
+
setCep("13800000");
|
|
183
|
+
|
|
184
|
+
// The new CEP names no logradouro or bairro, so the old ones must go —
|
|
185
|
+
// otherwise Avenida Paulista would sit under a Mogi Mirim CEP.
|
|
186
|
+
await waitFor(() => expect(cepField("city").value).toBe("Mogi Mirim"));
|
|
187
|
+
expect(cepField("addressLine1").value).toBe("");
|
|
188
|
+
expect(cepField("neighborhood").value).toBe("");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("undoes its own autofill when a CEP stops resolving", async () => {
|
|
192
|
+
let hit = true;
|
|
193
|
+
renderAddressForm(async () => (hit ? PAULISTA : null));
|
|
194
|
+
|
|
195
|
+
setCep("01310100");
|
|
196
|
+
await waitFor(() => expect(cepField("city").value).toBe("São Paulo"));
|
|
197
|
+
|
|
198
|
+
hit = false;
|
|
199
|
+
setCep("99999999");
|
|
200
|
+
|
|
201
|
+
await waitFor(() => expect(cepField("city").value).toBe(""));
|
|
202
|
+
expect(cepField("addressLine1").value).toBe("");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("leaves a field the user typed themselves alone", async () => {
|
|
206
|
+
renderAddressForm(async () => PAULISTA);
|
|
207
|
+
|
|
208
|
+
fireEvent.change(cepField("addressLine1"), { target: { value: "Rua que eu digitei" } });
|
|
209
|
+
setCep("01310100");
|
|
210
|
+
|
|
211
|
+
await waitFor(() => expect(cepField("city").value).toBe("São Paulo"));
|
|
212
|
+
expect(cepField("addressLine1").value).toBe("Rua que eu digitei");
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -50,7 +50,7 @@ The Accordion component provides expandable/collapsible content sections with sm
|
|
|
50
50
|
|
|
51
51
|
```tsx
|
|
52
52
|
import { Accordion, AccordionSummary, AccordionDetails } from '@mui/material';
|
|
53
|
-
import
|
|
53
|
+
import ExpandMore from '@mui/icons-material/ExpandMore';
|
|
54
54
|
|
|
55
55
|
function BasicAccordion() {
|
|
56
56
|
return (
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ExpandMore from '@mui/icons-material/ExpandMore';
|
|
2
|
+
import Help from '@mui/icons-material/Help';
|
|
3
|
+
import Info from '@mui/icons-material/Info';
|
|
4
|
+
import Security from '@mui/icons-material/Security';
|
|
5
|
+
import Settings from '@mui/icons-material/Settings';
|
|
2
6
|
import { Box, Button, Stack,Typography } from '@mui/material';
|
|
3
7
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
8
|
import { useState } from 'react';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { alpha } from '@mui/material';
|
|
2
|
+
import type { Theme } from '@mui/material';
|
|
3
|
+
|
|
4
|
+
import type { AccordionVariant } from './Accordion.types';
|
|
5
|
+
|
|
6
|
+
const BASE_TRANSITION = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
|
|
7
|
+
|
|
8
|
+
// Deliberately unannotated: the inferred union of literals assigns to MUI's
|
|
9
|
+
// `sx`, whereas a `CSSObject` return type does not survive being spread into it.
|
|
10
|
+
export const accordionVariantStyles = (theme: Theme, variant: AccordionVariant) => {
|
|
11
|
+
switch (variant) {
|
|
12
|
+
case 'glass':
|
|
13
|
+
return {
|
|
14
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.08),
|
|
15
|
+
backdropFilter: 'blur(24px) saturate(180%)',
|
|
16
|
+
border: `1px solid ${alpha(theme.palette.primary.main, 0.15)}`,
|
|
17
|
+
borderRadius: theme.spacing(1.5),
|
|
18
|
+
transition: BASE_TRANSITION,
|
|
19
|
+
'&:before': {
|
|
20
|
+
display: 'none',
|
|
21
|
+
},
|
|
22
|
+
'&:hover': {
|
|
23
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.12),
|
|
24
|
+
border: `1px solid ${alpha(theme.palette.primary.main, 0.25)}`,
|
|
25
|
+
transform: 'translateY(-1px)',
|
|
26
|
+
boxShadow: `0 8px 32px ${alpha(theme.palette.primary.main, 0.12)}`,
|
|
27
|
+
},
|
|
28
|
+
'&.Mui-expanded': {
|
|
29
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.15),
|
|
30
|
+
border: `1px solid ${alpha(theme.palette.primary.main, 0.3)}`,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
case 'bordered':
|
|
34
|
+
return {
|
|
35
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
36
|
+
borderRadius: theme.spacing(1),
|
|
37
|
+
transition: BASE_TRANSITION,
|
|
38
|
+
'&:before': {
|
|
39
|
+
display: 'none',
|
|
40
|
+
},
|
|
41
|
+
'&:not(:last-child)': {
|
|
42
|
+
marginBottom: theme.spacing(1),
|
|
43
|
+
},
|
|
44
|
+
'&:hover': {
|
|
45
|
+
borderColor: theme.palette.primary.main,
|
|
46
|
+
boxShadow: `0 0 0 1px ${alpha(theme.palette.primary.main, 0.1)}`,
|
|
47
|
+
},
|
|
48
|
+
'&.Mui-expanded': {
|
|
49
|
+
borderColor: theme.palette.primary.main,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
case 'separated':
|
|
53
|
+
return {
|
|
54
|
+
boxShadow: theme.shadows[2],
|
|
55
|
+
borderRadius: theme.spacing(1.5),
|
|
56
|
+
transition: BASE_TRANSITION,
|
|
57
|
+
'&:before': {
|
|
58
|
+
display: 'none',
|
|
59
|
+
},
|
|
60
|
+
'&:not(:last-child)': {
|
|
61
|
+
marginBottom: theme.spacing(2),
|
|
62
|
+
},
|
|
63
|
+
'&:hover': {
|
|
64
|
+
boxShadow: theme.shadows[4],
|
|
65
|
+
transform: 'translateY(-2px)',
|
|
66
|
+
},
|
|
67
|
+
'&.Mui-expanded': {
|
|
68
|
+
boxShadow: theme.shadows[6],
|
|
69
|
+
transform: 'translateY(-1px)',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
default:
|
|
73
|
+
return {
|
|
74
|
+
transition: BASE_TRANSITION,
|
|
75
|
+
'&:hover': {
|
|
76
|
+
backgroundColor: alpha(theme.palette.action.hover, 0.04),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ExpandMore from '@mui/icons-material/ExpandMore';
|
|
2
|
+
import Settings from '@mui/icons-material/Settings';
|
|
2
3
|
import { Box, Button, Stack,Typography } from '@mui/material';
|
|
3
4
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
5
|
import { useState } from 'react';
|
|
@@ -215,18 +216,22 @@ export const KeyboardNavigation: Story = {
|
|
|
215
216
|
const firstSummary = canvas.getByTestId('first-summary');
|
|
216
217
|
const secondSummary = canvas.getByTestId('second-summary');
|
|
217
218
|
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
await
|
|
219
|
+
// Tab in from the body rather than calling .focus() — the old first
|
|
220
|
+
// assertion only proved the DOM honours focus().
|
|
221
|
+
await userEvent.tab();
|
|
222
|
+
await waitFor(() => expect(firstSummary).toHaveFocus());
|
|
221
223
|
|
|
222
224
|
// Tab to second summary
|
|
223
225
|
await userEvent.tab();
|
|
224
|
-
await expect(secondSummary).toHaveFocus();
|
|
226
|
+
await waitFor(() => expect(secondSummary).toHaveFocus());
|
|
225
227
|
});
|
|
226
228
|
|
|
227
229
|
await step('Enter key to expand accordion', async () => {
|
|
228
230
|
const firstSummary = canvas.getByTestId('first-summary');
|
|
229
|
-
|
|
231
|
+
|
|
232
|
+
// Focus is on the second summary from the previous step; shift-tab back.
|
|
233
|
+
await userEvent.tab({ shift: true });
|
|
234
|
+
await waitFor(() => expect(firstSummary).toHaveFocus());
|
|
230
235
|
|
|
231
236
|
await userEvent.keyboard('{Enter}');
|
|
232
237
|
|
|
@@ -239,12 +244,15 @@ export const KeyboardNavigation: Story = {
|
|
|
239
244
|
await step('Tab into expanded content', async () => {
|
|
240
245
|
const firstButton = canvas.getByTestId('first-button');
|
|
241
246
|
await userEvent.tab();
|
|
242
|
-
await expect(firstButton).toHaveFocus();
|
|
247
|
+
await waitFor(() => expect(firstButton).toHaveFocus());
|
|
243
248
|
});
|
|
244
249
|
|
|
245
250
|
await step('Space key to expand accordion', async () => {
|
|
246
251
|
const secondSummary = canvas.getByTestId('second-summary');
|
|
247
|
-
|
|
252
|
+
|
|
253
|
+
// Continue the journey forward from the button inside the first panel.
|
|
254
|
+
await userEvent.tab();
|
|
255
|
+
await waitFor(() => expect(secondSummary).toHaveFocus());
|
|
248
256
|
|
|
249
257
|
await userEvent.keyboard(' ');
|
|
250
258
|
|
|
@@ -351,14 +359,14 @@ export const FocusManagement: Story = {
|
|
|
351
359
|
|
|
352
360
|
await step('Initial focus state', async () => {
|
|
353
361
|
const externalTrigger = canvas.getByTestId('external-trigger');
|
|
354
|
-
|
|
355
|
-
await expect(externalTrigger).toHaveFocus();
|
|
362
|
+
await userEvent.tab();
|
|
363
|
+
await waitFor(() => expect(externalTrigger).toHaveFocus());
|
|
356
364
|
});
|
|
357
365
|
|
|
358
366
|
await step('Focus on accordion summary', async () => {
|
|
359
367
|
const summary = canvas.getByTestId('focus-summary');
|
|
360
|
-
|
|
361
|
-
await expect(summary).toHaveFocus();
|
|
368
|
+
await userEvent.tab();
|
|
369
|
+
await waitFor(() => expect(summary).toHaveFocus());
|
|
362
370
|
});
|
|
363
371
|
|
|
364
372
|
await step('Expand accordion and check focus flow', async () => {
|
|
@@ -375,9 +383,6 @@ export const FocusManagement: Story = {
|
|
|
375
383
|
{ timeout: 1000 },
|
|
376
384
|
);
|
|
377
385
|
|
|
378
|
-
// Small delay to ensure DOM is stable after animation
|
|
379
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
380
|
-
|
|
381
386
|
// Tab through the expanded content with proper focus waiting
|
|
382
387
|
await userEvent.tab();
|
|
383
388
|
await waitFor(
|
|
@@ -438,15 +443,8 @@ export const FocusManagement: Story = {
|
|
|
438
443
|
await step('Focus restoration after collapse', async () => {
|
|
439
444
|
const summary = canvas.getByTestId('focus-summary');
|
|
440
445
|
|
|
441
|
-
//
|
|
442
|
-
|
|
443
|
-
await waitFor(
|
|
444
|
-
() => {
|
|
445
|
-
expect(summary).toHaveFocus();
|
|
446
|
-
},
|
|
447
|
-
{ timeout: 500 },
|
|
448
|
-
);
|
|
449
|
-
|
|
446
|
+
// No pre-focus: clicking the summary focuses it, so calling .focus()
|
|
447
|
+
// first only asserted that focus() works.
|
|
450
448
|
// Click to collapse
|
|
451
449
|
await userEvent.click(summary);
|
|
452
450
|
|
|
@@ -527,8 +525,9 @@ export const VisualStates: Story = {
|
|
|
527
525
|
await expect(styles.pointerEvents).toBe('none');
|
|
528
526
|
|
|
529
527
|
// Content should remain hidden since it can't be clicked
|
|
530
|
-
|
|
531
|
-
|
|
528
|
+
await waitFor(() =>
|
|
529
|
+
expect(canvas.queryByText('This should not be accessible')).not.toBeVisible(),
|
|
530
|
+
);
|
|
532
531
|
});
|
|
533
532
|
|
|
534
533
|
await step('Expand animation test', async () => {
|
|
@@ -811,8 +810,6 @@ export const FormInteraction: Story = {
|
|
|
811
810
|
variant="contained"
|
|
812
811
|
onClick={() => {
|
|
813
812
|
// Form submission handler
|
|
814
|
-
// eslint-disable-next-line no-console
|
|
815
|
-
console.log('Form submitted:', formData);
|
|
816
813
|
}}
|
|
817
814
|
>
|
|
818
815
|
Submit
|
|
@@ -1319,44 +1316,29 @@ export const PerformanceTest: Story = {
|
|
|
1319
1316
|
|
|
1320
1317
|
await step('Measure initial render time', async () => {
|
|
1321
1318
|
|
|
1322
|
-
|
|
1319
|
+
// No wall-clock budget: getAllByTestId queries DOM that has already
|
|
1320
|
+
// rendered, so timing around it measured query time, and any threshold is
|
|
1321
|
+
// really a bet on how loaded the runner is. The count is the real check.
|
|
1323
1322
|
const accordions = canvas.getAllByTestId(/^perf-accordion-\d+$/);
|
|
1324
|
-
|
|
1325
|
-
const endTime = performance.now();
|
|
1326
|
-
|
|
1327
|
-
const renderTime = endTime - startTime;
|
|
1328
|
-
// eslint-disable-next-line no-console
|
|
1329
|
-
console.log(`Render time for ${accordions.length} accordions: ${renderTime}ms`);
|
|
1330
|
-
|
|
1331
1323
|
await expect(accordions.length).toBe(20);
|
|
1332
|
-
// Assert reasonable render time (lenient threshold for CI/CD variability)
|
|
1333
|
-
await expect(renderTime).toBeLessThan(500);
|
|
1334
1324
|
});
|
|
1335
1325
|
|
|
1336
1326
|
await step('Test rapid expand/collapse performance', async () => {
|
|
1337
1327
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
// Rapidly expand/collapse multiple accordions
|
|
1328
|
+
// Expand then collapse each accordion, waiting on the state the click is
|
|
1329
|
+
// supposed to produce rather than sleeping 50ms and hoping it landed.
|
|
1341
1330
|
for (let i = 0; i < 5; i++) {
|
|
1342
1331
|
const accordion = canvas.getByTestId(`perf-accordion-${i}`);
|
|
1343
1332
|
const summary = accordion.querySelector('.MuiAccordionSummary-root');
|
|
1344
1333
|
|
|
1345
1334
|
if (summary) {
|
|
1346
1335
|
await userEvent.click(summary);
|
|
1336
|
+
await waitFor(() => expect(summary).toHaveAttribute('aria-expanded', 'true'));
|
|
1347
1337
|
|
|
1348
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1349
1338
|
await userEvent.click(summary);
|
|
1339
|
+
await waitFor(() => expect(summary).toHaveAttribute('aria-expanded', 'false'));
|
|
1350
1340
|
}
|
|
1351
1341
|
}
|
|
1352
|
-
|
|
1353
|
-
const endTime = performance.now();
|
|
1354
|
-
const interactionTime = endTime - startTime;
|
|
1355
|
-
// eslint-disable-next-line no-console
|
|
1356
|
-
console.log(`Interaction time for 5 accordions: ${interactionTime}ms`);
|
|
1357
|
-
|
|
1358
|
-
// Verify no performance issues (lenient threshold for CI/CD variability)
|
|
1359
|
-
await expect(interactionTime).toBeLessThan(3000);
|
|
1360
1342
|
});
|
|
1361
1343
|
},
|
|
1362
1344
|
};
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
useTheme} from '@mui/material';
|
|
9
9
|
import React from 'react';
|
|
10
10
|
|
|
11
|
+
import { accordionVariantStyles } from './Accordion.styles';
|
|
11
12
|
import type {
|
|
12
13
|
AccordionActionsProps,
|
|
13
14
|
AccordionDetailsProps,
|
|
@@ -27,80 +28,6 @@ export const Accordion: React.FC<AccordionProps> = ({
|
|
|
27
28
|
}) => {
|
|
28
29
|
const theme = useTheme();
|
|
29
30
|
|
|
30
|
-
const getVariantStyles = () => {
|
|
31
|
-
const baseTransition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
|
|
32
|
-
|
|
33
|
-
switch (variant) {
|
|
34
|
-
case 'glass':
|
|
35
|
-
return {
|
|
36
|
-
backgroundColor: alpha(theme.palette.background.paper, 0.08),
|
|
37
|
-
backdropFilter: 'blur(24px) saturate(180%)',
|
|
38
|
-
border: `1px solid ${alpha(theme.palette.primary.main, 0.15)}`,
|
|
39
|
-
borderRadius: theme.spacing(1.5),
|
|
40
|
-
transition: baseTransition,
|
|
41
|
-
'&:before': {
|
|
42
|
-
display: 'none',
|
|
43
|
-
},
|
|
44
|
-
'&:hover': {
|
|
45
|
-
backgroundColor: alpha(theme.palette.background.paper, 0.12),
|
|
46
|
-
border: `1px solid ${alpha(theme.palette.primary.main, 0.25)}`,
|
|
47
|
-
transform: 'translateY(-1px)',
|
|
48
|
-
boxShadow: `0 8px 32px ${alpha(theme.palette.primary.main, 0.12)}`,
|
|
49
|
-
},
|
|
50
|
-
'&.Mui-expanded': {
|
|
51
|
-
backgroundColor: alpha(theme.palette.background.paper, 0.15),
|
|
52
|
-
border: `1px solid ${alpha(theme.palette.primary.main, 0.3)}`,
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
case 'bordered':
|
|
56
|
-
return {
|
|
57
|
-
border: `1px solid ${theme.palette.divider}`,
|
|
58
|
-
borderRadius: theme.spacing(1),
|
|
59
|
-
transition: baseTransition,
|
|
60
|
-
'&:before': {
|
|
61
|
-
display: 'none',
|
|
62
|
-
},
|
|
63
|
-
'&:not(:last-child)': {
|
|
64
|
-
marginBottom: theme.spacing(1),
|
|
65
|
-
},
|
|
66
|
-
'&:hover': {
|
|
67
|
-
borderColor: theme.palette.primary.main,
|
|
68
|
-
boxShadow: `0 0 0 1px ${alpha(theme.palette.primary.main, 0.1)}`,
|
|
69
|
-
},
|
|
70
|
-
'&.Mui-expanded': {
|
|
71
|
-
borderColor: theme.palette.primary.main,
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
case 'separated':
|
|
75
|
-
return {
|
|
76
|
-
boxShadow: theme.shadows[2],
|
|
77
|
-
borderRadius: theme.spacing(1.5),
|
|
78
|
-
transition: baseTransition,
|
|
79
|
-
'&:before': {
|
|
80
|
-
display: 'none',
|
|
81
|
-
},
|
|
82
|
-
'&:not(:last-child)': {
|
|
83
|
-
marginBottom: theme.spacing(2),
|
|
84
|
-
},
|
|
85
|
-
'&:hover': {
|
|
86
|
-
boxShadow: theme.shadows[4],
|
|
87
|
-
transform: 'translateY(-2px)',
|
|
88
|
-
},
|
|
89
|
-
'&.Mui-expanded': {
|
|
90
|
-
boxShadow: theme.shadows[6],
|
|
91
|
-
transform: 'translateY(-1px)',
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
default:
|
|
95
|
-
return {
|
|
96
|
-
transition: baseTransition,
|
|
97
|
-
'&:hover': {
|
|
98
|
-
backgroundColor: alpha(theme.palette.action.hover, 0.04),
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
31
|
return (
|
|
105
32
|
<MuiAccordion
|
|
106
33
|
disabled={disabled}
|
|
@@ -109,7 +36,7 @@ export const Accordion: React.FC<AccordionProps> = ({
|
|
|
109
36
|
onChange={onChange}
|
|
110
37
|
data-testid={testId}
|
|
111
38
|
sx={{
|
|
112
|
-
...
|
|
39
|
+
...accordionVariantStyles(theme, variant),
|
|
113
40
|
...sx,
|
|
114
41
|
}}
|
|
115
42
|
{...props}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CardProps } from './Card.types';
|
|
2
|
+
|
|
3
|
+
type CardDefaultedKeys =
|
|
4
|
+
| 'variant'
|
|
5
|
+
| 'interactive'
|
|
6
|
+
| 'glow'
|
|
7
|
+
| 'pulse'
|
|
8
|
+
| 'borderRadius'
|
|
9
|
+
| 'loading';
|
|
10
|
+
|
|
11
|
+
type ResolvedCardProps = CardProps & Required<Pick<CardProps, CardDefaultedKeys>>;
|
|
12
|
+
|
|
13
|
+
const CARD_DEFAULTS: Pick<CardProps, CardDefaultedKeys> = {
|
|
14
|
+
variant: 'elevated',
|
|
15
|
+
interactive: false,
|
|
16
|
+
glow: false,
|
|
17
|
+
pulse: false,
|
|
18
|
+
borderRadius: 'md',
|
|
19
|
+
loading: false,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Strips explicitly-undefined props before the merge, so `glow={undefined}` still
|
|
23
|
+
// falls back to the default exactly as a destructuring default would.
|
|
24
|
+
const definedProps = (props: CardProps): Partial<CardProps> =>
|
|
25
|
+
Object.fromEntries(
|
|
26
|
+
Object.entries(props).filter(([, value]) => value !== undefined),
|
|
27
|
+
) as Partial<CardProps>;
|
|
28
|
+
|
|
29
|
+
export const resolveCardProps = (props: CardProps): ResolvedCardProps =>
|
|
30
|
+
({ ...CARD_DEFAULTS, ...definedProps(props) }) as ResolvedCardProps;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Star,
|
|
11
|
-
} from '@mui/icons-material';
|
|
1
|
+
import Edit from '@mui/icons-material/Edit';
|
|
2
|
+
import Email from '@mui/icons-material/Email';
|
|
3
|
+
import Favorite from '@mui/icons-material/Favorite';
|
|
4
|
+
import LocationOn from '@mui/icons-material/LocationOn';
|
|
5
|
+
import MoreVert from '@mui/icons-material/MoreVert';
|
|
6
|
+
import Phone from '@mui/icons-material/Phone';
|
|
7
|
+
import Share from '@mui/icons-material/Share';
|
|
8
|
+
import ShoppingCart from '@mui/icons-material/ShoppingCart';
|
|
9
|
+
import Star from '@mui/icons-material/Star';
|
|
12
10
|
import { Avatar, Box, Button, Chip, Grid,IconButton, Stack, Typography } from '@mui/material';
|
|
13
11
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
14
12
|
|