@12-apps/ui 1.0.0 → 1.3.0
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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { useConfirmAction } from './ConfirmAction.hooks';
|
|
4
|
+
import { ConfirmActionDialog } from './ConfirmAction.parts';
|
|
5
|
+
import type { ConfirmActionProps } from './ConfirmAction.types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wraps any trigger in a confirmation popup.
|
|
9
|
+
*
|
|
10
|
+
* The trigger is a render prop rather than a fixed element, so the same guard
|
|
11
|
+
* covers a button, a dropdown item, an icon or a table row action without this
|
|
12
|
+
* component knowing what any of them look like. The guarded handler runs ONLY
|
|
13
|
+
* after the operator confirms:
|
|
14
|
+
*
|
|
15
|
+
* ```tsx
|
|
16
|
+
* <ConfirmAction
|
|
17
|
+
* title="Excluir a categoria?"
|
|
18
|
+
* entityName={row.name}
|
|
19
|
+
* description="Ela vai para a lixeira e pode ser restaurada de lá."
|
|
20
|
+
* confirmText="Excluir"
|
|
21
|
+
* onConfirm={() => deleteCategoryAction({ tenantSlug, id: row.id })}
|
|
22
|
+
* >
|
|
23
|
+
* {(request) => <Button color="danger" onClick={request}>Excluir</Button>}
|
|
24
|
+
* </ConfirmAction>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* For the common case — an existing button whose `onClick` is already the
|
|
28
|
+
* destructive write — reach for {@link withConfirmation} instead, which needs
|
|
29
|
+
* no restructuring of the call site.
|
|
30
|
+
*/
|
|
31
|
+
export function ConfirmAction({
|
|
32
|
+
onConfirm,
|
|
33
|
+
children,
|
|
34
|
+
...options
|
|
35
|
+
}: ConfirmActionProps): React.ReactElement {
|
|
36
|
+
const state = useConfirmAction(onConfirm, options.errorText);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
{children(state.request, { pending: state.pending })}
|
|
41
|
+
<ConfirmActionDialog state={state} {...options} />
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
ConfirmAction.displayName = 'ConfirmAction';
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* How much the confirmation should shout. `destructive` paints the dialog and
|
|
5
|
+
* its confirm button in the error colour and moves the initial focus onto
|
|
6
|
+
* Cancel, so a stray Enter cannot complete a delete.
|
|
7
|
+
*/
|
|
8
|
+
export type ConfirmTone = 'destructive' | 'default';
|
|
9
|
+
|
|
10
|
+
/** Which button owns focus when the dialog opens. */
|
|
11
|
+
export type ConfirmInitialFocus = 'confirm' | 'cancel';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Everything the confirmation popup needs to say. `title` and `confirmText` are
|
|
15
|
+
* required on purpose: a confirm whose button reads "OK" tells the operator
|
|
16
|
+
* nothing about what is about to happen, so the verb is never defaulted.
|
|
17
|
+
*/
|
|
18
|
+
export interface ConfirmOptions {
|
|
19
|
+
/** The question, e.g. `Excluir a categoria?`. */
|
|
20
|
+
title: string;
|
|
21
|
+
/**
|
|
22
|
+
* The consequence, in the operator's words. Prefer saying whether the action
|
|
23
|
+
* can be undone — the recycle bin recovers a soft delete, nothing recovers a
|
|
24
|
+
* purge. Falls back to a neutral sentence built from `entityName`.
|
|
25
|
+
*/
|
|
26
|
+
description?: ReactNode;
|
|
27
|
+
/** The specific row being acted on, echoed into the dialog so it names a thing. */
|
|
28
|
+
entityName?: string;
|
|
29
|
+
/** The confirm button's label. Use the VERB ("Excluir"), never "OK". */
|
|
30
|
+
confirmText: string;
|
|
31
|
+
/** The cancel button's label. Defaults to `Cancelar`. */
|
|
32
|
+
cancelText?: string;
|
|
33
|
+
/**
|
|
34
|
+
* When set, the confirm button stays disabled until the operator types this
|
|
35
|
+
* exact string. For the handful of actions with no recovery path at all
|
|
36
|
+
* (permanent purge, suspending a live store), a click is too cheap.
|
|
37
|
+
*/
|
|
38
|
+
typeToConfirm?: string;
|
|
39
|
+
/** Label above the type-to-confirm field. Defaults to a sentence naming the text. */
|
|
40
|
+
typeToConfirmLabel?: string;
|
|
41
|
+
/** Defaults to `destructive` — this component exists for destructive actions. */
|
|
42
|
+
tone?: ConfirmTone;
|
|
43
|
+
/** Defaults to `cancel` for the destructive tone, `confirm` otherwise. */
|
|
44
|
+
initialFocus?: ConfirmInitialFocus;
|
|
45
|
+
/** Shown inside the dialog when the action rejects; the dialog stays open. */
|
|
46
|
+
errorText?: string;
|
|
47
|
+
/** Root test id; the dialog's parts derive theirs from it. */
|
|
48
|
+
dataTestId?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The action being guarded. A returned promise keeps the dialog open in its
|
|
53
|
+
* pending state until it settles, so the operator sees the write finish and
|
|
54
|
+
* cannot fire it twice.
|
|
55
|
+
*
|
|
56
|
+
* It takes no arguments deliberately: the click that opened the popup is long
|
|
57
|
+
* over by the time the operator confirms, and holding its synthetic event
|
|
58
|
+
* across that gap hands the handler a target that may no longer be in the
|
|
59
|
+
* document.
|
|
60
|
+
*/
|
|
61
|
+
export type ConfirmHandler = () => void | Promise<unknown>;
|
|
62
|
+
|
|
63
|
+
/** The confirmation state machine returned by `useConfirmAction`. */
|
|
64
|
+
export interface UseConfirmActionResult {
|
|
65
|
+
/** True while the popup is open. */
|
|
66
|
+
open: boolean;
|
|
67
|
+
/** True while the confirmed action is in flight. */
|
|
68
|
+
pending: boolean;
|
|
69
|
+
/** The failure message from the last attempt, or null. */
|
|
70
|
+
error: string | null;
|
|
71
|
+
/** Open the popup (this is what the trigger's click does). */
|
|
72
|
+
request: () => void;
|
|
73
|
+
/** Dismiss without running anything. No-op while pending. */
|
|
74
|
+
cancel: () => void;
|
|
75
|
+
/** Run the guarded action. No-op while pending. */
|
|
76
|
+
confirm: () => void;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* `<ConfirmAction>`'s props: the options, the guarded action, and a render prop
|
|
81
|
+
* for the trigger. The render prop receives the opener so any element can be
|
|
82
|
+
* the trigger — a button, a menu item, an icon.
|
|
83
|
+
*/
|
|
84
|
+
export interface ConfirmActionProps extends ConfirmOptions {
|
|
85
|
+
onConfirm: ConfirmHandler;
|
|
86
|
+
children: (request: () => void, state: { pending: boolean }) => ReactNode;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The minimum a component must accept to be wrappable by `withConfirmation`:
|
|
91
|
+
* some click handler. The signature is deliberately at its widest so that any
|
|
92
|
+
* button-shaped component qualifies — `MouseEventHandler<HTMLButtonElement>`,
|
|
93
|
+
* `MouseEventHandler<HTMLAnchorElement>` and a bare `() => void` all satisfy it,
|
|
94
|
+
* where naming one concrete element type would exclude the others.
|
|
95
|
+
*/
|
|
96
|
+
export interface ConfirmableProps {
|
|
97
|
+
onClick?: (...args: never[]) => unknown;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The prop `withConfirmation` adds to the wrapped component. Pass the options
|
|
102
|
+
* to guard the click; pass `false` (or nothing, when the HOC has no defaults)
|
|
103
|
+
* to fall through to the plain component — the escape hatch for the same button
|
|
104
|
+
* used in a context where the action is harmless.
|
|
105
|
+
*/
|
|
106
|
+
export interface WithConfirmationProps {
|
|
107
|
+
confirm?: Partial<ConfirmOptions> | false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The component `withConfirmation` returns. */
|
|
111
|
+
export type ConfirmableComponent<P> = ComponentType<P & WithConfirmationProps>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Button } from '../../form/Button';
|
|
2
|
+
import type { ButtonProps } from '../../form/Button/Button.types';
|
|
3
|
+
|
|
4
|
+
import { withConfirmation } from './with-confirmation';
|
|
5
|
+
import type { ConfirmableComponent } from './ConfirmAction.types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The library `Button`, already wrapped — the shape most call sites want.
|
|
9
|
+
*
|
|
10
|
+
* Adding the guard to an existing button is then an import swap plus the
|
|
11
|
+
* `confirm` prop; the button's own props (colour, size, icon) are untouched.
|
|
12
|
+
* The baked-in defaults are only the wording and tone every destructive
|
|
13
|
+
* confirmation shares, and each instance can still override them.
|
|
14
|
+
*/
|
|
15
|
+
export const ConfirmButton: ConfirmableComponent<ButtonProps> = withConfirmation<ButtonProps>(
|
|
16
|
+
Button,
|
|
17
|
+
{ cancelText: 'Cancelar', tone: 'destructive' },
|
|
18
|
+
);
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The safety contract, asserted at the surface an operator actually touches.
|
|
3
|
+
*
|
|
4
|
+
* Every case here is a way the guard could fail OPEN — the write leaving before
|
|
5
|
+
* anyone confirmed it, or leaving twice. A confirmation that merely renders is
|
|
6
|
+
* worth nothing; what matters is that nothing happens until the confirm button
|
|
7
|
+
* is pressed, and that it then happens exactly once.
|
|
8
|
+
*/
|
|
9
|
+
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
10
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
11
|
+
|
|
12
|
+
import { Button } from '../../../form/Button';
|
|
13
|
+
import { ConfirmAction, withConfirmation } from '../index';
|
|
14
|
+
|
|
15
|
+
afterEach(cleanup);
|
|
16
|
+
|
|
17
|
+
const OPTIONS = {
|
|
18
|
+
title: 'Excluir a categoria?',
|
|
19
|
+
description: 'Ela vai para a lixeira e pode ser restaurada de lá.',
|
|
20
|
+
confirmText: 'Excluir',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** The dialog's buttons, by the test ids `AlertDialog` derives from ours. */
|
|
24
|
+
const confirmButton = (): HTMLElement => screen.getByTestId('confirm-action-confirm-button');
|
|
25
|
+
const cancelButton = (): HTMLElement => screen.getByTestId('confirm-action-cancel-button');
|
|
26
|
+
const trigger = (): HTMLElement => screen.getByRole('button', { name: 'Excluir' });
|
|
27
|
+
|
|
28
|
+
function renderConfirmAction(
|
|
29
|
+
onConfirm: () => void | Promise<unknown>,
|
|
30
|
+
options: Record<string, unknown> = {},
|
|
31
|
+
): void {
|
|
32
|
+
render(
|
|
33
|
+
<ConfirmAction {...OPTIONS} {...options} onConfirm={onConfirm}>
|
|
34
|
+
{(request) => (
|
|
35
|
+
<button type="button" onClick={request}>
|
|
36
|
+
Excluir
|
|
37
|
+
</button>
|
|
38
|
+
)}
|
|
39
|
+
</ConfirmAction>,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe('ConfirmAction', () => {
|
|
44
|
+
it('opens the popup without running the action', () => {
|
|
45
|
+
const onConfirm = vi.fn();
|
|
46
|
+
renderConfirmAction(onConfirm);
|
|
47
|
+
|
|
48
|
+
fireEvent.click(trigger());
|
|
49
|
+
|
|
50
|
+
expect(screen.getByText('Excluir a categoria?')).toBeInTheDocument();
|
|
51
|
+
expect(onConfirm).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('runs nothing when the operator cancels', async () => {
|
|
55
|
+
const onConfirm = vi.fn();
|
|
56
|
+
renderConfirmAction(onConfirm);
|
|
57
|
+
|
|
58
|
+
fireEvent.click(trigger());
|
|
59
|
+
fireEvent.click(cancelButton());
|
|
60
|
+
|
|
61
|
+
expect(onConfirm).not.toHaveBeenCalled();
|
|
62
|
+
await waitFor(() => {
|
|
63
|
+
expect(screen.queryByText('Excluir a categoria?')).not.toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('runs the action once the operator confirms', async () => {
|
|
68
|
+
const onConfirm = vi.fn();
|
|
69
|
+
renderConfirmAction(onConfirm);
|
|
70
|
+
|
|
71
|
+
fireEvent.click(trigger());
|
|
72
|
+
fireEvent.click(confirmButton());
|
|
73
|
+
|
|
74
|
+
expect(onConfirm).toHaveBeenCalledTimes(1);
|
|
75
|
+
await waitFor(() => {
|
|
76
|
+
expect(screen.queryByText('Excluir a categoria?')).not.toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('sends one write when the confirm button is double-clicked', () => {
|
|
81
|
+
// The write is held open so both clicks land inside the in-flight window —
|
|
82
|
+
// the exact race a bare onClick would turn into two DELETEs. The resolver
|
|
83
|
+
// lives on a container because the flakiness gate forbids reassigning a
|
|
84
|
+
// closed-over binding from inside a stub.
|
|
85
|
+
const inFlight: { resolve: () => void } = { resolve: () => undefined };
|
|
86
|
+
const onConfirm = vi.fn(
|
|
87
|
+
() =>
|
|
88
|
+
new Promise<void>((resolve) => {
|
|
89
|
+
inFlight.resolve = resolve;
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
92
|
+
renderConfirmAction(onConfirm);
|
|
93
|
+
|
|
94
|
+
fireEvent.click(trigger());
|
|
95
|
+
fireEvent.click(confirmButton());
|
|
96
|
+
fireEvent.click(confirmButton());
|
|
97
|
+
|
|
98
|
+
expect(onConfirm).toHaveBeenCalledTimes(1);
|
|
99
|
+
inFlight.resolve();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('disables the confirm while the write is in flight', async () => {
|
|
103
|
+
const inFlight: { resolve: () => void } = { resolve: () => undefined };
|
|
104
|
+
const onConfirm = vi.fn(
|
|
105
|
+
() =>
|
|
106
|
+
new Promise<void>((resolve) => {
|
|
107
|
+
inFlight.resolve = resolve;
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
renderConfirmAction(onConfirm);
|
|
111
|
+
|
|
112
|
+
fireEvent.click(trigger());
|
|
113
|
+
fireEvent.click(confirmButton());
|
|
114
|
+
|
|
115
|
+
await waitFor(() => {
|
|
116
|
+
expect(confirmButton()).toBeDisabled();
|
|
117
|
+
});
|
|
118
|
+
inFlight.resolve();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('keeps the popup open, carrying the error, when the action fails', async () => {
|
|
122
|
+
// The server's sentence is the one the operator can act on, so a rejection
|
|
123
|
+
// carrying a message beats the generic fallback.
|
|
124
|
+
const onConfirm = vi.fn(() =>
|
|
125
|
+
Promise.reject(new Error('Esta categoria ainda tem produtos vinculados.')),
|
|
126
|
+
);
|
|
127
|
+
renderConfirmAction(onConfirm);
|
|
128
|
+
|
|
129
|
+
fireEvent.click(trigger());
|
|
130
|
+
fireEvent.click(confirmButton());
|
|
131
|
+
|
|
132
|
+
await waitFor(() => {
|
|
133
|
+
expect(screen.getByTestId('confirm-action-error')).toHaveTextContent(
|
|
134
|
+
'Esta categoria ainda tem produtos vinculados.',
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
expect(screen.getByText('Excluir a categoria?')).toBeInTheDocument();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('falls back to the configured wording when the rejection says nothing', async () => {
|
|
141
|
+
const onConfirm = vi.fn(() => Promise.reject(new Error('')));
|
|
142
|
+
renderConfirmAction(onConfirm, { errorText: 'Não foi possível excluir a categoria.' });
|
|
143
|
+
|
|
144
|
+
fireEvent.click(trigger());
|
|
145
|
+
fireEvent.click(confirmButton());
|
|
146
|
+
|
|
147
|
+
await waitFor(() => {
|
|
148
|
+
expect(screen.getByTestId('confirm-action-error')).toHaveTextContent(
|
|
149
|
+
'Não foi possível excluir a categoria.',
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('recovers from a failure: a second confirm can still run', async () => {
|
|
155
|
+
// `busy` must be released on the failure path too, or the popup is left
|
|
156
|
+
// holding an error it can never be retried out of.
|
|
157
|
+
const attempts = { count: 0 };
|
|
158
|
+
const onConfirm = vi.fn(() => {
|
|
159
|
+
attempts.count += 1;
|
|
160
|
+
return attempts.count === 1 ? Promise.reject(new Error('rede')) : Promise.resolve();
|
|
161
|
+
});
|
|
162
|
+
renderConfirmAction(onConfirm);
|
|
163
|
+
|
|
164
|
+
fireEvent.click(trigger());
|
|
165
|
+
fireEvent.click(confirmButton());
|
|
166
|
+
await waitFor(() => {
|
|
167
|
+
expect(screen.getByTestId('confirm-action-error')).toBeInTheDocument();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
fireEvent.click(confirmButton());
|
|
171
|
+
expect(onConfirm).toHaveBeenCalledTimes(2);
|
|
172
|
+
await waitFor(() => {
|
|
173
|
+
expect(screen.queryByText('Excluir a categoria?')).not.toBeInTheDocument();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('announces as an alertdialog named by its title and described by its body', () => {
|
|
178
|
+
// `alertdialog`, not `dialog`: this interrupts what the operator was doing
|
|
179
|
+
// and will not go away until answered, which is what makes a screen reader
|
|
180
|
+
// read the message out on open instead of just the dialog's name. MUI
|
|
181
|
+
// hardcodes role="dialog", so the override has to keep working.
|
|
182
|
+
renderConfirmAction(vi.fn());
|
|
183
|
+
fireEvent.click(trigger());
|
|
184
|
+
|
|
185
|
+
const dialog = screen.getByRole('alertdialog');
|
|
186
|
+
expect(dialog).toHaveAccessibleName('Excluir a categoria?');
|
|
187
|
+
expect(dialog).toHaveAccessibleDescription(
|
|
188
|
+
/Ela vai para a lixeira e pode ser restaurada de lá./,
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('opens with focus on Cancel, so a stray Enter cannot delete', async () => {
|
|
193
|
+
renderConfirmAction(vi.fn());
|
|
194
|
+
|
|
195
|
+
fireEvent.click(trigger());
|
|
196
|
+
|
|
197
|
+
await waitFor(() => {
|
|
198
|
+
expect(cancelButton()).toHaveFocus();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('holds the confirm disabled until the exact text is typed', async () => {
|
|
203
|
+
const onConfirm = vi.fn();
|
|
204
|
+
renderConfirmAction(onConfirm, { typeToConfirm: 'Bebidas' });
|
|
205
|
+
|
|
206
|
+
fireEvent.click(trigger());
|
|
207
|
+
const field = screen.getByTestId('confirm-action-type-to-confirm');
|
|
208
|
+
expect(confirmButton()).toBeDisabled();
|
|
209
|
+
|
|
210
|
+
fireEvent.change(field, { target: { value: 'Bebida' } });
|
|
211
|
+
expect(confirmButton()).toBeDisabled();
|
|
212
|
+
|
|
213
|
+
fireEvent.change(field, { target: { value: 'Bebidas' } });
|
|
214
|
+
expect(confirmButton()).toBeEnabled();
|
|
215
|
+
|
|
216
|
+
fireEvent.click(confirmButton());
|
|
217
|
+
expect(onConfirm).toHaveBeenCalledTimes(1);
|
|
218
|
+
await waitFor(() => {
|
|
219
|
+
expect(screen.queryByText('Excluir a categoria?')).not.toBeInTheDocument();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('starts each open from an empty type-to-confirm field', async () => {
|
|
224
|
+
renderConfirmAction(vi.fn(), { typeToConfirm: 'Bebidas' });
|
|
225
|
+
|
|
226
|
+
fireEvent.click(trigger());
|
|
227
|
+
fireEvent.change(screen.getByTestId('confirm-action-type-to-confirm'), {
|
|
228
|
+
target: { value: 'Bebidas' },
|
|
229
|
+
});
|
|
230
|
+
fireEvent.click(cancelButton());
|
|
231
|
+
await waitFor(() => {
|
|
232
|
+
expect(screen.queryByText('Excluir a categoria?')).not.toBeInTheDocument();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
fireEvent.click(trigger());
|
|
236
|
+
expect(screen.getByTestId('confirm-action-type-to-confirm')).toHaveValue('');
|
|
237
|
+
expect(confirmButton()).toBeDisabled();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('withConfirmation', () => {
|
|
242
|
+
const ConfirmButton = withConfirmation(Button);
|
|
243
|
+
|
|
244
|
+
it("guards the wrapped component's own onClick", async () => {
|
|
245
|
+
const onClick = vi.fn();
|
|
246
|
+
render(
|
|
247
|
+
<ConfirmButton onClick={onClick} confirm={OPTIONS}>
|
|
248
|
+
Excluir
|
|
249
|
+
</ConfirmButton>,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
fireEvent.click(trigger());
|
|
253
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
254
|
+
|
|
255
|
+
fireEvent.click(confirmButton());
|
|
256
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
257
|
+
await waitFor(() => {
|
|
258
|
+
expect(screen.queryByText('Excluir a categoria?')).not.toBeInTheDocument();
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('falls through to the plain component when confirm is false', () => {
|
|
263
|
+
const onClick = vi.fn();
|
|
264
|
+
render(
|
|
265
|
+
<ConfirmButton onClick={onClick} confirm={false}>
|
|
266
|
+
Excluir
|
|
267
|
+
</ConfirmButton>,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
fireEvent.click(trigger());
|
|
271
|
+
|
|
272
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
273
|
+
// A COUNT rather than an absence assertion: the popup was never rendered
|
|
274
|
+
// here, so there is no prior presence to wait for — which is exactly the
|
|
275
|
+
// distinction the flakiness rule exists to enforce.
|
|
276
|
+
expect(screen.queryAllByTestId('confirm-action')).toHaveLength(0);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('does not leak its own confirm prop onto the DOM node', () => {
|
|
280
|
+
render(
|
|
281
|
+
<ConfirmButton onClick={vi.fn()} confirm={OPTIONS}>
|
|
282
|
+
Excluir
|
|
283
|
+
</ConfirmButton>,
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
expect(trigger()).not.toHaveAttribute('confirm');
|
|
287
|
+
});
|
|
288
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { ConfirmAction } from './ConfirmAction';
|
|
2
|
+
export { ConfirmButton } from './ConfirmButton';
|
|
3
|
+
export { DEFAULT_ERROR_TEXT, useConfirmAction } from './ConfirmAction.hooks';
|
|
4
|
+
export { ConfirmActionDialog } from './ConfirmAction.parts';
|
|
5
|
+
export { withConfirmation } from './with-confirmation';
|
|
6
|
+
export type {
|
|
7
|
+
ConfirmActionProps,
|
|
8
|
+
ConfirmHandler,
|
|
9
|
+
ConfirmInitialFocus,
|
|
10
|
+
ConfirmOptions,
|
|
11
|
+
ConfirmTone,
|
|
12
|
+
ConfirmableComponent,
|
|
13
|
+
ConfirmableProps,
|
|
14
|
+
UseConfirmActionResult,
|
|
15
|
+
WithConfirmationProps,
|
|
16
|
+
} from './ConfirmAction.types';
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ComponentType } from 'react';
|
|
3
|
+
|
|
4
|
+
import { useConfirmAction } from './ConfirmAction.hooks';
|
|
5
|
+
import { ConfirmActionDialog } from './ConfirmAction.parts';
|
|
6
|
+
import type {
|
|
7
|
+
ConfirmHandler,
|
|
8
|
+
ConfirmOptions,
|
|
9
|
+
ConfirmableComponent,
|
|
10
|
+
ConfirmableProps,
|
|
11
|
+
WithConfirmationProps,
|
|
12
|
+
} from './ConfirmAction.types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Merge the HOC's baked-in defaults with the per-instance `confirm` prop.
|
|
16
|
+
* Returns null when the instance opts out (`confirm={false}`) or when neither
|
|
17
|
+
* side supplies the two fields a confirmation cannot be written without — the
|
|
18
|
+
* question and the verb. A null result means "render the plain component",
|
|
19
|
+
* which keeps the wrapped export usable in harmless contexts.
|
|
20
|
+
*/
|
|
21
|
+
function resolveOptions(
|
|
22
|
+
defaults: Partial<ConfirmOptions> | undefined,
|
|
23
|
+
instance: Partial<ConfirmOptions> | false | undefined,
|
|
24
|
+
): ConfirmOptions | null {
|
|
25
|
+
if (instance === false) return null;
|
|
26
|
+
const merged = { ...defaults, ...instance };
|
|
27
|
+
if (!merged.title || !merged.confirmText) return null;
|
|
28
|
+
return { ...merged, title: merged.title, confirmText: merged.confirmText };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Adapt the wrapped component's `onClick` into a {@link ConfirmHandler}.
|
|
33
|
+
*
|
|
34
|
+
* The click argument is dropped (see `ConfirmHandler`) and the return value is
|
|
35
|
+
* narrowed: a promise is handed back so the popup can hold its pending state
|
|
36
|
+
* until the write settles, anything else becomes `undefined`. A component with
|
|
37
|
+
* no `onClick` has nothing to guard, so confirming is a no-op.
|
|
38
|
+
*/
|
|
39
|
+
function useGuardedHandler(onClick: ConfirmableProps['onClick']): ConfirmHandler {
|
|
40
|
+
return React.useCallback((): void | Promise<unknown> => {
|
|
41
|
+
const result = onClick?.();
|
|
42
|
+
return result instanceof Promise ? result : undefined;
|
|
43
|
+
}, [onClick]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Give any click-driven component a confirmation popup, without restructuring
|
|
48
|
+
* the call site.
|
|
49
|
+
*
|
|
50
|
+
* The wrapped component's own `onClick` becomes the GUARDED action: clicking
|
|
51
|
+
* now opens the popup, and the handler runs only if the operator confirms. It
|
|
52
|
+
* inherits the whole contract from `useConfirmAction` — cancel sends nothing, a
|
|
53
|
+
* returned promise holds the popup in its pending state so a double-click
|
|
54
|
+
* cannot fire two writes, and a rejection keeps the popup open carrying the
|
|
55
|
+
* error.
|
|
56
|
+
*
|
|
57
|
+
* ```tsx
|
|
58
|
+
* const ConfirmButton = withConfirmation(Button);
|
|
59
|
+
*
|
|
60
|
+
* <ConfirmButton
|
|
61
|
+
* color="danger"
|
|
62
|
+
* onClick={() => deleteCategoryAction({ tenantSlug, id: row.id })}
|
|
63
|
+
* confirm={{
|
|
64
|
+
* title: "Excluir a categoria?",
|
|
65
|
+
* entityName: row.name,
|
|
66
|
+
* description: "Ela vai para a lixeira e pode ser restaurada de lá.",
|
|
67
|
+
* confirmText: "Excluir",
|
|
68
|
+
* }}
|
|
69
|
+
* >
|
|
70
|
+
* Excluir
|
|
71
|
+
* </ConfirmButton>
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* Bake the wording in once when the same guard repeats — `withConfirmation(Button,
|
|
75
|
+
* { confirmText: "Excluir" })` — and let each instance override what differs.
|
|
76
|
+
* `confirm={false}` falls through to the plain component.
|
|
77
|
+
*
|
|
78
|
+
* Refs are not forwarded: the HOC renders a fragment (trigger + popup), so there
|
|
79
|
+
* is no single node to hand back. Wrap a component that needs a ref at its own
|
|
80
|
+
* call site with {@link ConfirmAction} instead.
|
|
81
|
+
*/
|
|
82
|
+
export function withConfirmation<P extends ConfirmableProps>(
|
|
83
|
+
Wrapped: ComponentType<P>,
|
|
84
|
+
defaults?: Partial<ConfirmOptions>,
|
|
85
|
+
): ConfirmableComponent<P> {
|
|
86
|
+
function WithConfirmation(props: P & WithConfirmationProps): React.ReactElement {
|
|
87
|
+
const { confirm, onClick } = props;
|
|
88
|
+
const options = resolveOptions(defaults, confirm);
|
|
89
|
+
const state = useConfirmAction(useGuardedHandler(onClick), options?.errorText);
|
|
90
|
+
|
|
91
|
+
// `confirm` is ours and must not reach the DOM — an unknown attribute on a
|
|
92
|
+
// <button> is a React warning at every render. The cast is the standard HOC
|
|
93
|
+
// one: TypeScript cannot prove `Omit<P & X, 'confirm'>` is still a `P`.
|
|
94
|
+
const { confirm: _confirm, ...rest } = props;
|
|
95
|
+
const forwarded = rest as unknown as P;
|
|
96
|
+
|
|
97
|
+
if (!options) return <Wrapped {...forwarded} />;
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<>
|
|
101
|
+
<Wrapped {...forwarded} onClick={state.request} />
|
|
102
|
+
<ConfirmActionDialog state={state} {...options} />
|
|
103
|
+
</>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
WithConfirmation.displayName = `withConfirmation(${
|
|
108
|
+
Wrapped.displayName ?? Wrapped.name ?? 'Component'
|
|
109
|
+
})`;
|
|
110
|
+
|
|
111
|
+
return WithConfirmation;
|
|
112
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SxProps, Theme } from '@mui/material';
|
|
2
2
|
import { alpha, keyframes } from '@mui/material';
|
|
3
3
|
|
|
4
|
+
import { dynamicViewportHeight } from '../../../utils/viewport';
|
|
4
5
|
import type { DialogProps } from './Dialog.types';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -133,7 +134,8 @@ export function variantStylesOf(theme: Theme, opts: VariantStyleOptions): SxProp
|
|
|
133
134
|
borderRadius: 0,
|
|
134
135
|
margin: 0,
|
|
135
136
|
width: '100vw',
|
|
136
|
-
|
|
137
|
+
// `100dvh`, not `100vh` — see `dynamicViewportHeight`.
|
|
138
|
+
...dynamicViewportHeight('height'),
|
|
137
139
|
maxWidth: 'none',
|
|
138
140
|
maxHeight: 'none',
|
|
139
141
|
...decorations,
|
|
@@ -144,7 +146,7 @@ export function variantStylesOf(theme: Theme, opts: VariantStyleOptions): SxProp
|
|
|
144
146
|
borderRadius: `${radius}px 0 0 ${radius}px`,
|
|
145
147
|
margin: 0,
|
|
146
148
|
width: maxWidthOf(opts.size),
|
|
147
|
-
height
|
|
149
|
+
...dynamicViewportHeight('height'),
|
|
148
150
|
maxHeight: 'none',
|
|
149
151
|
position: 'absolute' as const,
|
|
150
152
|
right: 0,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
2
2
|
import {
|
|
3
3
|
Backdrop,
|
|
4
4
|
Box,
|
|
@@ -199,6 +199,30 @@ export const DialogHeader: React.FC<DialogHeaderProps> = ({
|
|
|
199
199
|
);
|
|
200
200
|
};
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Top padding for a body that sits directly under the title.
|
|
204
|
+
*
|
|
205
|
+
* MUI zeroes it there (`.MuiDialogTitle-root + &` on a non-`dividers` content),
|
|
206
|
+
* assuming the title's own bottom padding is the whole gap. That assumption
|
|
207
|
+
* breaks for a body opening with an outlined field: a filled field floats its
|
|
208
|
+
* label ABOVE the input's border box, and `DialogContent` is `overflow-y: auto`
|
|
209
|
+
* — a clipping box — so the label gets sliced in half (FUT-544: "Tipo" in the
|
|
210
|
+
* "Novo insumo" dialog).
|
|
211
|
+
*
|
|
212
|
+
* Reserve the overhang rather than restore the full 24px: the title already
|
|
213
|
+
* contributes its bottom padding, so a shrunk label's `translate(…, -9px)` is
|
|
214
|
+
* all that is missing. 12px clears it and leaves the gap visually tight.
|
|
215
|
+
*/
|
|
216
|
+
const TITLED_BODY_PADDING_TOP = 1.5;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* `.MuiDialogTitle-root + &` is `(0,2,0)`-specific and a plain `sx` entry is
|
|
220
|
+
* `(0,1,0)`, so the padding above only survives the cascade by naming the class
|
|
221
|
+
* again — `(0,3,0)`. Scoped to the title-following case so an untitled dialog
|
|
222
|
+
* keeps MUI's roomier default.
|
|
223
|
+
*/
|
|
224
|
+
const TITLED_BODY_SELECTOR = '.MuiDialogTitle-root + &.MuiDialogContent-root';
|
|
225
|
+
|
|
202
226
|
export const DialogContent: React.FC<DialogContentProps> = ({
|
|
203
227
|
children,
|
|
204
228
|
dividers = false,
|
|
@@ -211,6 +235,7 @@ export const DialogContent: React.FC<DialogContentProps> = ({
|
|
|
211
235
|
dividers={dividers}
|
|
212
236
|
sx={{
|
|
213
237
|
padding: dense ? 1.5 : 3,
|
|
238
|
+
[TITLED_BODY_SELECTOR]: { paddingTop: TITLED_BODY_PADDING_TOP },
|
|
214
239
|
'&.MuiDialogContent-dividers': {
|
|
215
240
|
borderTop: dividers ? '1px solid' : 'none',
|
|
216
241
|
borderBottom: dividers ? '1px solid' : 'none',
|