@astryxdesign/core 0.7.5 → 0.7.6-canary.bb18c81
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/dist/AlertDialog/AlertDialog.d.ts +1 -1
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +5 -5
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +12 -3
- package/dist/BottomSheet/BottomSheet.js +1 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +2 -1
- package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +30 -3
- package/dist/Button/Button.js +4 -4
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +6 -5
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +4 -1
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +101 -15
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +4 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +34 -9
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +5 -4
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +2 -1
- package/dist/Drawer/Drawer.d.ts.map +1 -1
- package/dist/Drawer/Drawer.js +5 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +4 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +10 -1
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
- package/dist/DropdownMenu/lazyMenuBottomSheet.js +16 -2
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
- package/dist/DropdownMenu/renderDropdownItems.js +21 -16
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +2 -1
- package/dist/Field/FieldLabel.d.ts +1 -1
- package/dist/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +4 -4
- package/dist/Field/InputClearButton.js +3 -3
- package/dist/Field/PanelSearchInput.js +2 -2
- package/dist/FieldStatus/FieldStatus.js +2 -2
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +7 -1
- package/dist/Heading/Heading.js +1 -1
- package/dist/Icon/Icon.js +1 -1
- package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckIndicator.js +2 -1
- package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
- package/dist/Indicator/CheckboxIndicator.js +3 -1
- package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
- package/dist/Indicator/RadioIndicator.js +3 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +2 -1
- package/dist/Item/ItemSwipeLayer.js +1 -1
- package/dist/Layout/Layout.js +1 -1
- package/dist/Layout/LayoutContent.js +1 -1
- package/dist/Layout/LayoutFooter.js +1 -1
- package/dist/Layout/LayoutHeader.js +1 -1
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +5 -0
- package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
- package/dist/List/List.js +1 -1
- package/dist/List/ListItem.js +3 -3
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +7 -2
- package/dist/MultiSelector/MultiSelector.d.ts +1 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +5 -2
- package/dist/NumberInput/NumberInput.d.ts +3 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +3 -3
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +16 -12
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +28 -2
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
- package/dist/Selector/useSelectorPresentation.d.ts +8 -1
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
- package/dist/Selector/useSelectorPresentation.js +20 -4
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +7 -1
- package/dist/Spinner/Spinner.js +2 -2
- package/dist/Stack/Stack.js +1 -1
- package/dist/Stack/StackItem.js +1 -1
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +16 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
- package/dist/Text/Text.js +1 -1
- package/dist/Text/useTruncation.d.ts.map +1 -1
- package/dist/Text/useTruncation.js +23 -5
- package/dist/TextArea/TextArea.d.ts +1 -1
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +13 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +3 -3
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +2 -2
- package/dist/Timestamp/TimestampHoverCard.js +4 -4
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +82 -51
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +13 -2
- package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +2 -1
- package/dist/Typeahead/Typeahead.d.ts +1 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +9 -1
- package/dist/astryx.css +14 -1
- package/dist/gestures/PinchGestureRecognizer.d.ts +30 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/PinchGestureRecognizer.js +125 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +70 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.js +220 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts +36 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/SwipeGestureRecognizer.js +145 -0
- package/dist/gestures/VelocityTracker.d.ts +6 -2
- package/dist/gestures/VelocityTracker.d.ts.map +1 -1
- package/dist/gestures/VelocityTracker.js +7 -3
- package/dist/gestures/types.d.ts +11 -7
- package/dist/gestures/types.d.ts.map +1 -1
- package/dist/gestures/types.js +22 -2
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
- package/dist/hooks/useAdaptivePresentation.js +4 -1
- package/dist/hooks/useMenuHover.d.ts.map +1 -1
- package/dist/hooks/useMenuHover.js +12 -1
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +3 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +3 -0
- package/dist/motion/animator.d.ts +7 -1
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +119 -27
- package/dist/theme/defineTheme.js +1 -1
- package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
- package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
- package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
- package/dist/theme/domainTokens/index.d.ts +5 -4
- package/dist/theme/domainTokens/index.d.ts.map +1 -1
- package/dist/theme/domainTokens/index.js +5 -9
- package/dist/utils/focusReturn.d.ts +17 -1
- package/dist/utils/focusReturn.d.ts.map +1 -1
- package/dist/utils/focusReturn.js +11 -4
- package/dist/utils/getNodeText.d.ts +8 -0
- package/dist/utils/getNodeText.d.ts.map +1 -0
- package/dist/utils/getNodeText.js +37 -0
- package/dist/utils/interactionModality.d.ts +11 -2
- package/dist/utils/interactionModality.d.ts.map +1 -1
- package/dist/utils/interactionModality.js +58 -5
- package/dist/utils/presentation.d.ts +50 -0
- package/dist/utils/presentation.d.ts.map +1 -0
- package/dist/utils/presentation.js +125 -0
- package/dist/utils/touchPresentation.d.ts.map +1 -1
- package/dist/utils/touchPresentation.js +5 -3
- package/dist/utils/touchPresentation.stylex.d.ts +15 -0
- package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
- package/dist/utils/touchPresentation.stylex.js +33 -0
- package/locales/en.json +13 -1
- package/locales/pseudo.json +9 -0
- package/package.json +5 -4
- package/src/AlertDialog/AlertDialog.tsx +6 -5
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
- package/src/Avatar/Avatar.spec.md +22 -22
- package/src/Avatar/Avatar.tsx +18 -1
- package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
- package/src/BottomSheet/BottomSheet.test.tsx +46 -0
- package/src/BottomSheet/BottomSheet.tsx +1 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
- package/src/BottomSheet/useSheetGestures.test.ts +52 -0
- package/src/BottomSheet/useSheetGestures.ts +37 -6
- package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
- package/src/Button/Button.tsx +4 -4
- package/src/Calendar/Calendar.spec.md +87 -48
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
- package/src/CheckboxInput/CheckboxInput.tsx +6 -5
- package/src/Collapsible/Collapsible.spec.md +2 -0
- package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
- package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
- package/src/ContextMenu/ContextMenu.test.tsx +35 -0
- package/src/DateInput/DateInput.test.tsx +53 -2
- package/src/DateInput/DateInput.tsx +15 -11
- package/src/DateInput/MonthScroller.tsx +106 -20
- package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
- package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
- package/src/DateRangeInput/DateRangeInput.tsx +16 -12
- package/src/DateTimeInput/DateTimeInput.spec.md +71 -48
- package/src/DateTimeInput/DateTimeInput.test.tsx +61 -0
- package/src/DateTimeInput/DateTimeInput.tsx +15 -11
- package/src/Dialog/Dialog.test.tsx +216 -2
- package/src/Dialog/Dialog.tsx +42 -9
- package/src/Dialog/DialogHeader.tsx +5 -4
- package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
- package/src/Divider/Divider.tsx +2 -1
- package/src/Drawer/Drawer.spec.md +111 -80
- package/src/Drawer/Drawer.test.tsx +22 -0
- package/src/Drawer/Drawer.tsx +5 -0
- package/src/Drawer/DrawerHeader.spec.md +181 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
- package/src/DropdownMenu/DropdownMenu.tsx +7 -1
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
- package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
- package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +53 -0
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +23 -6
- package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
- package/src/EmptyState/EmptyState.tsx +2 -1
- package/src/Field/FieldLabel.tsx +5 -4
- package/src/Field/InputClearButton.tsx +3 -3
- package/src/Field/PanelSearchInput.tsx +2 -2
- package/src/FieldStatus/FieldStatus.tsx +2 -2
- package/src/FileInput/FileInput.test.tsx +21 -0
- package/src/FileInput/FileInput.tsx +4 -1
- package/src/FormLayout/FormLayout.spec.md +7 -8
- package/src/Heading/Heading.tsx +1 -1
- package/src/Icon/Icon.tsx +1 -1
- package/src/Indicator/CheckIndicator.spec.md +47 -42
- package/src/Indicator/CheckIndicator.tsx +2 -1
- package/src/Indicator/CheckboxIndicator.spec.md +40 -37
- package/src/Indicator/CheckboxIndicator.tsx +3 -1
- package/src/Indicator/Indicator.spec.md +188 -0
- package/src/Indicator/RadioIndicator.tsx +3 -1
- package/src/Item/Item.tsx +2 -1
- package/src/Item/ItemSwipeLayer.tsx +1 -1
- package/src/Layout/Layout.spec.md +127 -131
- package/src/Layout/Layout.tsx +1 -1
- package/src/Layout/LayoutContent.spec.md +205 -0
- package/src/Layout/LayoutContent.tsx +1 -1
- package/src/Layout/LayoutFooter.spec.md +190 -0
- package/src/Layout/LayoutFooter.tsx +1 -1
- package/src/Layout/LayoutHeader.spec.md +193 -0
- package/src/Layout/LayoutHeader.tsx +1 -1
- package/src/Layout/LayoutPanel.spec.md +194 -0
- package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
- package/src/Lightbox/Lightbox.test.tsx +18 -0
- package/src/Lightbox/Lightbox.tsx +5 -0
- package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
- package/src/List/List.tsx +1 -1
- package/src/List/ListItem.tsx +3 -3
- package/src/MobileNav/MobileNav.test.tsx +15 -0
- package/src/MobileNav/MobileNav.tsx +9 -1
- package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
- package/src/MoreMenu/MoreMenu.test.tsx +11 -0
- package/src/MultiSelector/MultiSelector.spec.md +7 -4
- package/src/MultiSelector/MultiSelector.test.tsx +118 -1
- package/src/MultiSelector/MultiSelector.tsx +6 -2
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
- package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
- package/src/NumberInput/NumberInput.doc.mjs +13 -7
- package/src/NumberInput/NumberInput.spec.md +59 -37
- package/src/NumberInput/NumberInput.tsx +13 -3
- package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
- package/src/ProgressBar/ProgressBar.test.tsx +108 -32
- package/src/ProgressBar/ProgressBar.tsx +58 -10
- package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
- package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
- package/src/Resizable/useResizable.test.ts +43 -0
- package/src/Resizable/useResizable.ts +32 -9
- package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
- package/src/Selector/Selector.test.tsx +90 -1
- package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
- package/src/Selector/useSelectorPresentation.ts +31 -3
- package/src/SideNav/SideNav.test.tsx +27 -0
- package/src/Slider/Slider.test.tsx +12 -0
- package/src/Slider/Slider.tsx +4 -1
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
- package/src/Spinner/Spinner.tsx +2 -2
- package/src/Stack/Stack.tsx +1 -1
- package/src/Stack/StackItem.tsx +1 -1
- package/src/TabList/TabList.test.tsx +52 -1
- package/src/TabList/TabList.tsx +16 -1
- package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
- package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
- package/src/Text/Text.tsx +1 -1
- package/src/Text/useTruncation.test.tsx +128 -1
- package/src/Text/useTruncation.ts +23 -5
- package/src/TextArea/TextArea.spec.md +10 -13
- package/src/TextArea/TextArea.test.tsx +60 -0
- package/src/TextArea/TextArea.tsx +15 -1
- package/src/TextInput/TextInput.test.tsx +21 -0
- package/src/TextInput/TextInput.tsx +3 -1
- package/src/Timestamp/Timestamp.spec.md +6 -12
- package/src/Timestamp/Timestamp.test.tsx +30 -5
- package/src/Timestamp/Timestamp.tsx +6 -2
- package/src/Timestamp/TimestampHoverCard.tsx +4 -4
- package/src/Toast/Toast.spec.md +7 -10
- package/src/Toast/ToastViewport.test.tsx +575 -1
- package/src/Toast/ToastViewport.tsx +100 -50
- package/src/Tokenizer/Tokenizer.spec.md +5 -3
- package/src/Tokenizer/Tokenizer.test.tsx +68 -0
- package/src/Tokenizer/Tokenizer.tsx +14 -2
- package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
- package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
- package/src/Toolbar/Toolbar.spec.md +5 -6
- package/src/Toolbar/Toolbar.test.tsx +30 -0
- package/src/Toolbar/Toolbar.tsx +2 -1
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
- package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
- package/src/TopNav/TopNav.test.tsx +27 -0
- package/src/Typeahead/Typeahead.test.tsx +40 -0
- package/src/Typeahead/Typeahead.tsx +12 -1
- package/src/gestures/PinchGestureRecognizer.ts +144 -0
- package/src/gestures/ScreenEdgePanGestureRecognizer.ts +252 -0
- package/src/gestures/SwipeGestureRecognizer.ts +193 -0
- package/src/gestures/VelocityTracker.ts +10 -3
- package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
- package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
- package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
- package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
- package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +188 -0
- package/src/gestures/__tests__/harness.ts +21 -2
- package/src/gestures/types.ts +23 -9
- package/src/hooks/useAdaptivePresentation.ts +4 -1
- package/src/hooks/useMenuHover.ts +15 -1
- package/src/i18n/generated-locales/en.generated.ts +4 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +4 -0
- package/src/motion/README.md +11 -1
- package/src/motion/animator.test.ts +151 -3
- package/src/motion/animator.ts +149 -33
- package/src/theme/defineTheme.ts +1 -1
- package/src/theme/derivedVarRegistry.test.ts +4 -0
- package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
- package/src/theme/domainTokens/index.ts +6 -10
- package/src/utils/focusReturn.ts +17 -6
- package/src/utils/getNodeText.test.ts +24 -0
- package/src/utils/getNodeText.ts +37 -0
- package/src/utils/interactionModality.test.ts +45 -1
- package/src/utils/interactionModality.ts +64 -5
- package/src/utils/presentation.test.ts +380 -0
- package/src/utils/presentation.ts +180 -0
- package/src/utils/pressPaintPriority.test.ts +80 -0
- package/src/utils/touchPresentation.stylex.ts +43 -0
- package/src/utils/touchPresentation.test.ts +140 -1
- package/src/utils/touchPresentation.ts +5 -3
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 3
|
|
3
|
+
template_version: 7
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:DrawerHeader
|
|
6
|
+
authority: current
|
|
7
|
+
archive_reason: null
|
|
8
|
+
superseded_by: null
|
|
9
|
+
approved_by: cixzhang
|
|
10
|
+
approved_at: 2026-10-30
|
|
11
|
+
owners: [cixzhang, imdreamrunner]
|
|
12
|
+
review_triggers: [public-api, behavior, layout, theming, accessibility]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/Drawer/DrawerHeader.test.tsx,
|
|
16
|
+
apps/storybook/stories/DrawerHeader.stories.tsx,
|
|
17
|
+
scripts/check-knowledge.mjs,
|
|
18
|
+
]
|
|
19
|
+
modules: []
|
|
20
|
+
families: [family:layout-regions]
|
|
21
|
+
design_specs: []
|
|
22
|
+
architecture:
|
|
23
|
+
[architecture:component-theming-surface, architecture:container-padding]
|
|
24
|
+
contributing: []
|
|
25
|
+
system_specs: []
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# DrawerHeader component contract
|
|
29
|
+
|
|
30
|
+
## Contract at a glance
|
|
31
|
+
|
|
32
|
+
| Area | Contract |
|
|
33
|
+
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| Public contract | `DrawerHeader` with `title`, `subtitle`, `onOpenChange`, `startContent`, `endContent`, `endContentEdgeCompensation` (`inline`, `block`, `all`), and `hasDivider`; five `drawer-header` targets. |
|
|
35
|
+
| Behavior | A Layout header row for a Drawer: optional start content, an h2 title with optional subtitle, optional end content, and a close button only when `onOpenChange` is given. |
|
|
36
|
+
| End-user impact | Users see the drawer's title and, when the caller provides one, a named close button; focus is not moved when the header mounts. |
|
|
37
|
+
| Builder impact | None. |
|
|
38
|
+
| Compatibility/readiness | Records shipped behavior; no public change. |
|
|
39
|
+
| Review checks | Reject a close button rendered without `onOpenChange`, a header that names the Drawer or moves focus on mount, or a title that is not an h2. |
|
|
40
|
+
| Governing rules | `component:Drawer` (the panel and naming); `family:layout-regions`; `architecture:component-theming-surface`. |
|
|
41
|
+
|
|
42
|
+
This table is a review projection; the body below is authoritative.
|
|
43
|
+
|
|
44
|
+
## Intent
|
|
45
|
+
|
|
46
|
+
DrawerHeader is the optional title row a caller composes in a Drawer's Layout
|
|
47
|
+
header slot. It owns the row's parts, its close button, and its targets.
|
|
48
|
+
Consumer usage lives in `DrawerHeader.doc.mjs`.
|
|
49
|
+
|
|
50
|
+
## Compatibility and migration
|
|
51
|
+
|
|
52
|
+
- Released default preserved: `yes`
|
|
53
|
+
- Compatibility class: records shipped behavior; no public change
|
|
54
|
+
- Controlled/uncontrolled behavior: the close button calls the caller's
|
|
55
|
+
`onOpenChange(false)` and owns no state
|
|
56
|
+
- Migration decision: none
|
|
57
|
+
|
|
58
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
59
|
+
|
|
60
|
+
## Ownership boundary
|
|
61
|
+
|
|
62
|
+
**Owns**
|
|
63
|
+
|
|
64
|
+
- The header row, its slots, title semantics, close button, end-content edge
|
|
65
|
+
compensation, and five targets (FR1–FR5).
|
|
66
|
+
|
|
67
|
+
**Does not own / non-goals**
|
|
68
|
+
|
|
69
|
+
- The Drawer's name, focus entry, and open state — owned by `component:Drawer`.
|
|
70
|
+
- The header region's padding, divider default, and place in the Layout — owned
|
|
71
|
+
by `family:layout-regions` through `LayoutHeader`.
|
|
72
|
+
- The close Button — owned by `component:Button`.
|
|
73
|
+
|
|
74
|
+
## Public concepts
|
|
75
|
+
|
|
76
|
+
| Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
|
|
77
|
+
| ------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------- | ------------------------ | --------- | -------------------------------- |
|
|
78
|
+
| End-content compensation | `inline`, `block`, `all`, omitted | Which edges of the end slot pull outward to align a trailing control with the container edge. | End slot. | Both axes when `onOpenChange` is set, none otherwise | `component:DrawerHeader` | Stable | TypeScript rejects other values. |
|
|
79
|
+
|
|
80
|
+
## Behavioral and layout contract
|
|
81
|
+
|
|
82
|
+
| ID | Invariant | Basis |
|
|
83
|
+
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
84
|
+
| FR1 | DrawerHeader renders inside a LayoutHeader that receives `ref`, `hasDivider`, `xstyle`, `className`, `style`, and other DOM props. Its row carries `drawer-header` and shows, in order, start content, the title block, and the end slot. | Current source and tests |
|
|
85
|
+
| FR2 | The title renders as an h2; a renderable `subtitle` (including `0`) renders below it as secondary small body text, and an empty one renders nothing. Start and end slots render when their content is renderable, including `0`. | Current source and tests |
|
|
86
|
+
| FR3 | With `onOpenChange`, the end slot ends with an icon-only ghost Button named and tooltipped by `@astryx.dialog.close` that calls `onOpenChange(false)`; without it there is no close button. | Current source and tests |
|
|
87
|
+
| FR4 | The end slot pulls outward by `--spacing-2` on the block axis, the inline-end edge, or both, per `endContentEdgeCompensation`; when omitted, both apply only if there is a close button. | Current source |
|
|
88
|
+
| FR5 | The start slot, title block, end slot, and close glyph carry `drawer-header-start-content`, `drawer-header-title-block`, `drawer-header-end-content`, and `drawer-header-close-icon`. | Current source and tests |
|
|
89
|
+
| FR6 | Mounting the header does not move focus, and the header does not name the Drawer. | Current source and tests |
|
|
90
|
+
|
|
91
|
+
### Allowed variation
|
|
92
|
+
|
|
93
|
+
- **AV1 — Slots.** Start, end, and subtitle content vary or are absent.
|
|
94
|
+
|
|
95
|
+
### Representative states
|
|
96
|
+
|
|
97
|
+
| State | Required invariant | Allowed variation |
|
|
98
|
+
| ------------------- | ------------------------------------------------- | ----------------- |
|
|
99
|
+
| Title only | h2 title; no close button. | — |
|
|
100
|
+
| With `onOpenChange` | Named close button at the end; edges compensated. | End content. |
|
|
101
|
+
| Start and end slots | Order: start, title block, end. | Content. |
|
|
102
|
+
|
|
103
|
+
### Transformation and precedence order
|
|
104
|
+
|
|
105
|
+
- **ORD1 — Compensation.** `endContentEdgeCompensation`, then the close-button
|
|
106
|
+
default (FR4).
|
|
107
|
+
|
|
108
|
+
### Performance and resources
|
|
109
|
+
|
|
110
|
+
- No resource rule.
|
|
111
|
+
|
|
112
|
+
## Accessibility contract
|
|
113
|
+
|
|
114
|
+
- **AR1 — Heading.** The title is an h2.
|
|
115
|
+
- **AR2 — Close name.** FR3's translated name and tooltip.
|
|
116
|
+
- **AR3 — Focus.** FR6.
|
|
117
|
+
|
|
118
|
+
## Design relationships
|
|
119
|
+
|
|
120
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
121
|
+
| ---------------- | --------------------------------------------- | ------------------------------ | -------------- | ------------------ |
|
|
122
|
+
| Header row | Start, title block, end in one row. | Current source and public docs | Supporting | FR1 |
|
|
123
|
+
| Start content | Leading caller content. | Current source and public docs | Supporting | FR2, FR5 |
|
|
124
|
+
| Title block | Title and optional subtitle. | Current source and public docs | Prominent | FR2, FR5 |
|
|
125
|
+
| End content | Trailing caller content and the close button. | Current source and public docs | Supporting | FR3–FR5 |
|
|
126
|
+
| Close icon | Close glyph inside the button. | Current source and public docs | Supporting | FR3, FR5 |
|
|
127
|
+
|
|
128
|
+
### Theming anatomy
|
|
129
|
+
|
|
130
|
+
<!-- anatomy-theming:v1 -->
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"Header row": {"target": "drawer-header"},
|
|
135
|
+
"Start content": {"target": "drawer-header-start-content"},
|
|
136
|
+
"Title block": {"target": "drawer-header-title-block"},
|
|
137
|
+
"End content": {"target": "drawer-header-end-content"},
|
|
138
|
+
"Close icon": {"target": "drawer-header-close-icon"}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Family and system relationships
|
|
143
|
+
|
|
144
|
+
- `component:Drawer` hosts the header in its Layout and owns naming and focus.
|
|
145
|
+
- `family:layout-regions` owns the header region it renders into.
|
|
146
|
+
|
|
147
|
+
## Verification map
|
|
148
|
+
|
|
149
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
150
|
+
| ------------------- | -------------------------------------------- | ---------------------------------------- | -------------------------------------------------- | ---------------------------------- |
|
|
151
|
+
| FR1, FR2, AR1 | `DrawerHeader.test.tsx` title and slot tests | Title, subtitle, `0` values, empty slots | Wrong heading level or dropped content fails. | `audit:DrawerHeader/anatomy` |
|
|
152
|
+
| FR3, AR2 | `DrawerHeader.test.tsx` close-button tests | With and without `onOpenChange` | A button without a callback, or no name, fails. | `audit:DrawerHeader/accessibility` |
|
|
153
|
+
| FR5 | `DrawerHeader.test.tsx` theming target test | All slots | A missing target fails. | `audit:DrawerHeader/theming` |
|
|
154
|
+
| FR6, AR3 | `DrawerHeader.test.tsx` focus test | Mount | Focus moved on mount fails. | `audit:DrawerHeader/accessibility` |
|
|
155
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Consumer anatomy and current targets | Missing, extra, or stale mappings fail validation. | `audit:DrawerHeader/theming` |
|
|
156
|
+
|
|
157
|
+
FR4 is read from source; no focused test checks it yet.
|
|
158
|
+
|
|
159
|
+
Read from source, with no focused test yet:
|
|
160
|
+
|
|
161
|
+
- FR1: `ref`, `hasDivider`, `xstyle`, `className`, `style`, and other DOM
|
|
162
|
+
props reaching the LayoutHeader.
|
|
163
|
+
- FR3: the close button's tooltip.
|
|
164
|
+
- FR4: the end slot's edge compensation.
|
|
165
|
+
- FR6: the header not naming the Drawer.
|
|
166
|
+
|
|
167
|
+
## Decision log
|
|
168
|
+
|
|
169
|
+
No component-local decision is recorded.
|
|
170
|
+
|
|
171
|
+
## Open questions
|
|
172
|
+
|
|
173
|
+
- How do `endContentEdgeCompensation`'s `inline`, `block`, and `all` axis
|
|
174
|
+
values map onto `endContentEdge` treatment values
|
|
175
|
+
(`architecture:container-padding`), and when does the old prop enter its
|
|
176
|
+
compatibility window? Today the prop stays supported as released.
|
|
177
|
+
|
|
178
|
+
## Content boundary
|
|
179
|
+
|
|
180
|
+
This file does not duplicate consumer prop tables, examples, or Drawer
|
|
181
|
+
behavior. It links to their owners.
|
|
@@ -127,6 +127,47 @@ describe('DropdownMenu', () => {
|
|
|
127
127
|
).not.toBeInTheDocument();
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
it("forwards the button's ref object to the popover trigger", () => {
|
|
131
|
+
const ref = createRef<HTMLButtonElement>();
|
|
132
|
+
render(
|
|
133
|
+
<DropdownMenu
|
|
134
|
+
button={{label: 'Project actions', ref}}
|
|
135
|
+
items={[{label: 'Edit project', onClick: vi.fn()}]}
|
|
136
|
+
/>,
|
|
137
|
+
);
|
|
138
|
+
expect(ref.current).toBe(
|
|
139
|
+
screen.getByRole('button', {name: /Project actions/}),
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("forwards the button's ref object to the bottom-sheet trigger", () => {
|
|
144
|
+
const ref = createRef<HTMLButtonElement>();
|
|
145
|
+
render(
|
|
146
|
+
<DropdownMenu
|
|
147
|
+
button={{label: 'Project actions', ref}}
|
|
148
|
+
presentation="bottom-sheet"
|
|
149
|
+
items={[{label: 'Edit project', onClick: vi.fn()}]}
|
|
150
|
+
/>,
|
|
151
|
+
);
|
|
152
|
+
expect(ref.current).toBe(
|
|
153
|
+
screen.getByRole('button', {name: /Project actions/}),
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("forwards the button's callback ref to the bottom-sheet trigger", () => {
|
|
158
|
+
const ref = vi.fn();
|
|
159
|
+
render(
|
|
160
|
+
<DropdownMenu
|
|
161
|
+
button={{label: 'Project actions', ref}}
|
|
162
|
+
presentation="bottom-sheet"
|
|
163
|
+
items={[{label: 'Edit project', onClick: vi.fn()}]}
|
|
164
|
+
/>,
|
|
165
|
+
);
|
|
166
|
+
expect(ref).toHaveBeenCalledWith(
|
|
167
|
+
screen.getByRole('button', {name: /Project actions/}),
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
|
|
130
171
|
it('renders data-driven actions in a bottom sheet when requested', async () => {
|
|
131
172
|
const user = userEvent.setup();
|
|
132
173
|
const onEdit = vi.fn();
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
type DropdownMenuSurfaceValue,
|
|
68
68
|
} from './DropdownMenuContext';
|
|
69
69
|
import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
|
|
70
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
70
71
|
import {useListFocus} from '../hooks/useListFocus';
|
|
71
72
|
import {useMenuPress} from '../hooks/useMenuPress';
|
|
72
73
|
import type {MenuPressPointerType} from '../hooks/menuPressGesture';
|
|
@@ -621,6 +622,11 @@ function DropdownMenuBottomSheet({
|
|
|
621
622
|
const button = buttonFromProps ?? {label: t(DEFAULT_BUTTON_I18N_KEY)};
|
|
622
623
|
const backLabel = t('@astryx.dropdownMenu.back');
|
|
623
624
|
const buttonRef = useRef<HTMLElement>(null);
|
|
625
|
+
// The caller's `button.ref` rides along with the sheet's own trigger ref.
|
|
626
|
+
const triggerRef = useMergedRefs<HTMLButtonElement>(
|
|
627
|
+
buttonRef as React.Ref<HTMLButtonElement>,
|
|
628
|
+
button.ref,
|
|
629
|
+
);
|
|
624
630
|
const triggerId = useId();
|
|
625
631
|
const sheetId = useId();
|
|
626
632
|
const actionListRef = useRef<HTMLDivElement>(null);
|
|
@@ -806,7 +812,7 @@ function DropdownMenuBottomSheet({
|
|
|
806
812
|
) : (
|
|
807
813
|
<Button
|
|
808
814
|
{...button}
|
|
809
|
-
ref={
|
|
815
|
+
ref={triggerRef}
|
|
810
816
|
xstyle={[
|
|
811
817
|
isOpen && styles.triggerOpen,
|
|
812
818
|
button.xstyle,
|
|
@@ -647,6 +647,40 @@ describe('DropdownMenu data-driven submenus', () => {
|
|
|
647
647
|
expect(onMove).toHaveBeenCalledWith('b');
|
|
648
648
|
});
|
|
649
649
|
|
|
650
|
+
it('renders a submenu for an item with nested items inside a section', async () => {
|
|
651
|
+
const user = userEvent.setup();
|
|
652
|
+
const onWork = vi.fn();
|
|
653
|
+
render(
|
|
654
|
+
<DropdownMenu
|
|
655
|
+
button={{label: 'Share'}}
|
|
656
|
+
items={[
|
|
657
|
+
{
|
|
658
|
+
type: 'section',
|
|
659
|
+
title: 'Send',
|
|
660
|
+
items: [
|
|
661
|
+
{label: 'Copy link', onClick: () => {}},
|
|
662
|
+
{
|
|
663
|
+
label: 'Email',
|
|
664
|
+
items: [
|
|
665
|
+
{label: 'Work', onClick: onWork},
|
|
666
|
+
{label: 'Personal', onClick: () => {}},
|
|
667
|
+
],
|
|
668
|
+
},
|
|
669
|
+
],
|
|
670
|
+
},
|
|
671
|
+
]}
|
|
672
|
+
/>,
|
|
673
|
+
);
|
|
674
|
+
await user.click(screen.getByRole('button', {name: /Share/}));
|
|
675
|
+
const trigger = screen.getByRole('menuitem', {name: /Email/, hidden: true});
|
|
676
|
+
expect(trigger).toHaveAttribute('aria-haspopup', 'menu');
|
|
677
|
+
await user.click(trigger);
|
|
678
|
+
await user.click(
|
|
679
|
+
await screen.findByRole('menuitem', {name: 'Work', hidden: true}),
|
|
680
|
+
);
|
|
681
|
+
expect(onWork).toHaveBeenCalled();
|
|
682
|
+
});
|
|
683
|
+
|
|
650
684
|
it('keyboard-reaches an item positioned after a submenu row', async () => {
|
|
651
685
|
// Regression: the submenu flyout renders inline inside the root menu, so
|
|
652
686
|
// the root's item query swept in the (hidden) flyout items. Arrow nav then
|
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
|
|
12
12
|
import React, {useRef, type ReactElement} from 'react';
|
|
13
13
|
import * as stylex from '@stylexjs/stylex';
|
|
14
|
-
import {Divider} from '../Divider';
|
|
15
|
-
import {Heading} from '../Heading';
|
|
16
|
-
import {Icon, renderIconSlot} from '../Icon';
|
|
17
|
-
import {List
|
|
14
|
+
import {Divider} from '../Divider/Divider';
|
|
15
|
+
import {Heading} from '../Heading/Heading';
|
|
16
|
+
import {Icon, renderIconSlot} from '../Icon/Icon';
|
|
17
|
+
import {List} from '../List/List';
|
|
18
|
+
import {ListItem} from '../List/ListItem';
|
|
18
19
|
import {useMenuPress} from '../hooks/useMenuPress';
|
|
19
20
|
import {colorVars, spacingVars} from '../theme/tokens.stylex';
|
|
20
|
-
import {rtlStyles} from '../utils';
|
|
21
|
+
import {rtlStyles} from '../utils/rtlStyles';
|
|
21
22
|
import {getInteractionModality} from '../utils/interactionModality';
|
|
22
23
|
import type {DropdownMenuItemData, DropdownMenuOption} from './DropdownMenu';
|
|
23
24
|
import {isModifiedClick} from './menuItemRoles';
|
|
@@ -120,16 +120,28 @@ async function runState(
|
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
// Checking coverage runs every state again, which on a loaded runner nears
|
|
124
|
+
// the per-test timeout as one test. It runs as one serial part per state,
|
|
125
|
+
// sharing their results, then one check over all of them.
|
|
126
|
+
test.describe('every menu expectation is exercised by a DropdownMenu state', () => {
|
|
127
|
+
test.describe.configure({mode: 'serial'});
|
|
127
128
|
const results: BindingResult[] = [];
|
|
129
|
+
|
|
128
130
|
for (const state of MENU_BINDING_STATES) {
|
|
129
|
-
|
|
131
|
+
test(`runs ${state.id}`, async ({page}) => {
|
|
132
|
+
const cdp = await page.context().newCDPSession(page);
|
|
133
|
+
results.push(await runState(page, cdp, state));
|
|
134
|
+
});
|
|
130
135
|
}
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
|
|
137
|
+
test('no expectation is left unexercised', () => {
|
|
138
|
+
// Every part ran in this worker; this test alone proves nothing.
|
|
139
|
+
expect(results.map(result => result.state).sort()).toEqual(
|
|
140
|
+
MENU_BINDING_STATES.map(state => state.id).sort(),
|
|
141
|
+
);
|
|
142
|
+
expect(neverExercised(results)).toEqual([]);
|
|
143
|
+
expect(unmatchedKnownFailures(MENU_KNOWN_FAILURES, results)).toEqual([]);
|
|
144
|
+
});
|
|
133
145
|
});
|
|
134
146
|
|
|
135
147
|
for (const state of MENU_BINDING_STATES) {
|
|
@@ -107,18 +107,30 @@ async function runState(
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
// Checking coverage runs every state again, which on a loaded runner nears
|
|
111
|
+
// the per-test timeout as one test. It runs as one serial part per state,
|
|
112
|
+
// sharing their results, then one check over all of them.
|
|
113
|
+
test.describe('every menu-button expectation is exercised by a DropdownMenu or MoreMenu state', () => {
|
|
114
|
+
test.describe.configure({mode: 'serial'});
|
|
114
115
|
const results: BindingResult[] = [];
|
|
116
|
+
|
|
115
117
|
for (const state of MENU_BUTTON_BINDING_STATES) {
|
|
116
|
-
|
|
118
|
+
test(`runs ${state.id}`, async ({page}) => {
|
|
119
|
+
const cdp = await page.context().newCDPSession(page);
|
|
120
|
+
results.push(await runState(page, cdp, state));
|
|
121
|
+
});
|
|
117
122
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
|
|
124
|
+
test('no expectation is left unexercised', () => {
|
|
125
|
+
// Every part ran in this worker; this test alone proves nothing.
|
|
126
|
+
expect(results.map(result => result.state).sort()).toEqual(
|
|
127
|
+
MENU_BUTTON_BINDING_STATES.map(state => state.id).sort(),
|
|
128
|
+
);
|
|
129
|
+
expect(neverExercised(results)).toEqual([]);
|
|
130
|
+
expect(unmatchedKnownFailures(MENU_BUTTON_KNOWN_FAILURES, results)).toEqual(
|
|
131
|
+
[],
|
|
132
|
+
);
|
|
133
|
+
});
|
|
122
134
|
});
|
|
123
135
|
|
|
124
136
|
for (const state of MENU_BUTTON_BINDING_STATES) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file lazyMenuBottomSheet.test.ts
|
|
5
|
+
* @input lazyMenuBottomSheet's action-list preload, with the list's module
|
|
6
|
+
* failing to load once
|
|
7
|
+
* @output A failed preload is no unhandled rejection, and is not kept: the
|
|
8
|
+
* next load asks the module loader again
|
|
9
|
+
* @position Unit test of ./lazyMenuBottomSheet; the hydration case is in
|
|
10
|
+
* DropdownMenu.ssr.test.tsx
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {afterEach, describe, expect, it, vi} from 'vitest';
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
vi.doUnmock('./MenuBottomSheetActionList');
|
|
17
|
+
vi.resetModules();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('preloadMenuBottomSheetActionList', () => {
|
|
21
|
+
it('drops a failed preload without an unhandled rejection, and asks the module loader again on the next call', async () => {
|
|
22
|
+
vi.resetModules();
|
|
23
|
+
let attempts = 0;
|
|
24
|
+
vi.doMock('./MenuBottomSheetActionList', () => {
|
|
25
|
+
attempts += 1;
|
|
26
|
+
if (attempts === 1) {
|
|
27
|
+
throw new Error('the connection dropped');
|
|
28
|
+
}
|
|
29
|
+
return {MenuBottomSheetActionList: () => null};
|
|
30
|
+
});
|
|
31
|
+
const unhandled: unknown[] = [];
|
|
32
|
+
const onUnhandled = (reason: unknown) => unhandled.push(reason);
|
|
33
|
+
process.on('unhandledRejection', onUnhandled);
|
|
34
|
+
try {
|
|
35
|
+
const {preloadMenuBottomSheetActionList} =
|
|
36
|
+
await import('./lazyMenuBottomSheet');
|
|
37
|
+
preloadMenuBottomSheetActionList();
|
|
38
|
+
await vi.waitFor(() => expect(attempts).toBe(1));
|
|
39
|
+
// An unhandled rejection is reported after the rejecting task's
|
|
40
|
+
// microtasks: give it two macrotasks.
|
|
41
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
42
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
43
|
+
expect(unhandled).toEqual([]);
|
|
44
|
+
|
|
45
|
+
preloadMenuBottomSheetActionList();
|
|
46
|
+
await vi.waitFor(() => expect(attempts).toBe(2));
|
|
47
|
+
const loaded = await import('./MenuBottomSheetActionList');
|
|
48
|
+
expect(typeof loaded.MenuBottomSheetActionList).toBe('function');
|
|
49
|
+
} finally {
|
|
50
|
+
process.off('unhandledRejection', onUnhandled);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
* of pages whose dropdowns only open as anchored popovers. Each wrapper keeps
|
|
16
16
|
* the wrapped component's props and renders nothing until its module loads.
|
|
17
17
|
*
|
|
18
|
+
* The action list preloads at rest; a failed load is not kept, so the next
|
|
19
|
+
* preload or open asks the module loader again (a loader that retries then
|
|
20
|
+
* loads it; a browser does not fetch a native module again once its import
|
|
21
|
+
* failed).
|
|
22
|
+
*
|
|
18
23
|
* Neither wrapper renders on the server or in the render that hydrates server
|
|
19
24
|
* HTML (spec:AST-013 FR9): a lazy component that has not loaded yet would
|
|
20
25
|
* leave server markup incomplete and abort hydration. The closed sheet has no
|
|
@@ -47,16 +52,28 @@ const LazyMenuBottomSheet = lazy(async () =>
|
|
|
47
52
|
let actionListModule: typeof ActionListModule | null = null;
|
|
48
53
|
let actionListLoad: Promise<typeof ActionListModule> | null = null;
|
|
49
54
|
async function loadActionList(): Promise<typeof ActionListModule> {
|
|
50
|
-
actionListLoad ??= import('./MenuBottomSheetActionList').then(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
actionListLoad ??= import('./MenuBottomSheetActionList').then(
|
|
56
|
+
module => {
|
|
57
|
+
actionListModule = module;
|
|
58
|
+
return module;
|
|
59
|
+
},
|
|
60
|
+
(error: unknown) => {
|
|
61
|
+
// A failed load (a dropped connection) is not kept: the next caller
|
|
62
|
+
// asks the module loader again.
|
|
63
|
+
actionListLoad = null;
|
|
64
|
+
throw error;
|
|
65
|
+
},
|
|
66
|
+
);
|
|
54
67
|
return actionListLoad;
|
|
55
68
|
}
|
|
56
69
|
|
|
57
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Starts loading the action list; a sheet menu calls it at rest. A preload
|
|
72
|
+
* that fails is dropped, never an unhandled rejection: the first open asks the
|
|
73
|
+
* module loader again.
|
|
74
|
+
*/
|
|
58
75
|
export function preloadMenuBottomSheetActionList(): void {
|
|
59
|
-
|
|
76
|
+
loadActionList().catch(() => {});
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
const LazyMenuBottomSheetActionList = lazy(async () =>
|
|
@@ -68,6 +68,28 @@ function renderLeafItem(
|
|
|
68
68
|
return <DropdownMenuItem key={getItemKey(item, index)} {...itemProps} />;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* A data item that declares nested `items` becomes a submenu (data-mode
|
|
73
|
+
* parity with the compound DropdownMenuSubMenu API), at the top level and
|
|
74
|
+
* inside a section alike; otherwise it's a leaf. renderDropdownItems owns the
|
|
75
|
+
* recursion and hands the rendered children to DropdownMenuSubMenu, so
|
|
76
|
+
* DropdownMenuSubMenu never imports this module back (no import cycle).
|
|
77
|
+
*/
|
|
78
|
+
function renderItem(item: DropdownMenuItemData, index: number): ReactElement {
|
|
79
|
+
if (item.items && item.items.length > 0) {
|
|
80
|
+
return (
|
|
81
|
+
<DropdownMenuSubMenu
|
|
82
|
+
key={getItemKey(item, index)}
|
|
83
|
+
icon={item.icon}
|
|
84
|
+
label={item.label}
|
|
85
|
+
isDisabled={item.isDisabled}>
|
|
86
|
+
{renderDropdownItems(item.items)}
|
|
87
|
+
</DropdownMenuSubMenu>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return renderLeafItem(item, index);
|
|
91
|
+
}
|
|
92
|
+
|
|
71
93
|
/**
|
|
72
94
|
* Converts data-driven items into DropdownMenuItem components,
|
|
73
95
|
* so both modes share the same rendering and keyboard navigation path.
|
|
@@ -96,28 +118,11 @@ export function renderDropdownItems(items: DropdownMenuOption[]): ReactNode {
|
|
|
96
118
|
{option.title}
|
|
97
119
|
</div>
|
|
98
120
|
)}
|
|
99
|
-
{option.items.map(
|
|
121
|
+
{option.items.map(renderItem)}
|
|
100
122
|
</div>,
|
|
101
123
|
);
|
|
102
124
|
} else if (!('type' in option)) {
|
|
103
|
-
|
|
104
|
-
// parity with the compound DropdownMenuSubMenu API); otherwise it's a
|
|
105
|
-
// leaf. renderDropdownItems owns the recursion and hands the rendered
|
|
106
|
-
// children to DropdownMenuSubMenu — so DropdownMenuSubMenu never imports
|
|
107
|
-
// this module back (no import cycle).
|
|
108
|
-
if (option.items && option.items.length > 0) {
|
|
109
|
-
elements.push(
|
|
110
|
-
<DropdownMenuSubMenu
|
|
111
|
-
key={getItemKey(option, i)}
|
|
112
|
-
icon={option.icon}
|
|
113
|
-
label={option.label}
|
|
114
|
-
isDisabled={option.isDisabled}>
|
|
115
|
-
{renderDropdownItems(option.items)}
|
|
116
|
-
</DropdownMenuSubMenu>,
|
|
117
|
-
);
|
|
118
|
-
} else {
|
|
119
|
-
elements.push(renderLeafItem(option, i));
|
|
120
|
-
}
|
|
125
|
+
elements.push(renderItem(option, i));
|
|
121
126
|
}
|
|
122
127
|
}
|
|
123
128
|
|
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
} from '../theme/tokens.stylex';
|
|
25
25
|
import type {BaseProps} from '../BaseProps';
|
|
26
26
|
import {AnnounceRenderedText} from '../hooks/AnnounceRenderedText';
|
|
27
|
-
import {isRenderable
|
|
27
|
+
import {isRenderable} from '../utils/isRenderable';
|
|
28
|
+
import {mergeProps} from '../utils/mergeProps';
|
|
28
29
|
import {themeProps} from '../utils/themeProps';
|
|
29
30
|
|
|
30
31
|
const styles = stylex.create({
|
package/src/Field/FieldLabel.tsx
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import {use, useMemo, useRef, type ReactNode, type RefObject} from 'react';
|
|
20
20
|
import * as stylex from '@stylexjs/stylex';
|
|
21
21
|
import type {BaseProps} from '../BaseProps';
|
|
22
|
-
import {mergeProps} from '../utils';
|
|
22
|
+
import {mergeProps} from '../utils/mergeProps';
|
|
23
23
|
|
|
24
24
|
import {
|
|
25
25
|
colorVars,
|
|
@@ -28,9 +28,10 @@ import {
|
|
|
28
28
|
typographyVars,
|
|
29
29
|
typeScaleVars,
|
|
30
30
|
} from '../theme/tokens.stylex';
|
|
31
|
-
import {Icon, renderIconSlot
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
31
|
+
import {Icon, renderIconSlot} from '../Icon/Icon';
|
|
32
|
+
import type {IconType} from '../Icon';
|
|
33
|
+
import {Tooltip} from '../Tooltip/Tooltip';
|
|
34
|
+
import {useTranslator} from '../i18n/useTranslator';
|
|
34
35
|
import {themeProps} from '../utils/themeProps';
|
|
35
36
|
import {useInputContainer} from '../hooks/useInputContainer';
|
|
36
37
|
import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
import type {ReactNode} from 'react';
|
|
20
20
|
import * as stylex from '@stylexjs/stylex';
|
|
21
|
-
import {Button} from '../Button';
|
|
22
|
-
import {Icon} from '../Icon';
|
|
23
|
-
import {useTranslator} from '../i18n';
|
|
21
|
+
import {Button} from '../Button/Button';
|
|
22
|
+
import {Icon} from '../Icon/Icon';
|
|
23
|
+
import {useTranslator} from '../i18n/useTranslator';
|
|
24
24
|
import {themeProps} from '../utils/themeProps';
|
|
25
25
|
|
|
26
26
|
const styles = stylex.create({
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
import {useCallback, useState, type Ref} from 'react';
|
|
43
43
|
import * as stylex from '@stylexjs/stylex';
|
|
44
44
|
import type {StyleXStyles} from '@stylexjs/stylex';
|
|
45
|
-
import {Icon} from '../Icon';
|
|
45
|
+
import {Icon} from '../Icon/Icon';
|
|
46
46
|
import {InputClearButton} from './InputClearButton';
|
|
47
|
-
import {mergeProps} from '../utils';
|
|
47
|
+
import {mergeProps} from '../utils/mergeProps';
|
|
48
48
|
import {
|
|
49
49
|
getInteractionModality,
|
|
50
50
|
useInteractionModalityTracking,
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import React from 'react';
|
|
21
21
|
import * as stylex from '@stylexjs/stylex';
|
|
22
22
|
import type {BaseProps} from '../BaseProps';
|
|
23
|
-
import {mergeProps} from '../utils';
|
|
23
|
+
import {mergeProps} from '../utils/mergeProps';
|
|
24
24
|
import {
|
|
25
25
|
colorVars,
|
|
26
26
|
radiusVars,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import type {InputStatusType} from '../Field/types';
|
|
32
32
|
import {useEntryAnimation} from '../hooks/useEntryAnimation';
|
|
33
33
|
import {themeProps} from '../utils/themeProps';
|
|
34
|
-
import {Icon} from '../Icon';
|
|
34
|
+
import {Icon} from '../Icon/Icon';
|
|
35
35
|
import type {IconName} from '../Icon';
|
|
36
36
|
import type {FieldStatusVariantMap} from './index';
|
|
37
37
|
import {useFieldStatusAnnouncement} from './useFieldStatusAnnouncement';
|