@astryxdesign/core 0.7.5 → 0.7.6-canary.5cd7a15
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/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +7 -1
- 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 +12 -4
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +101 -15
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +8 -3
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +1 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +23 -12
- package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
- package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
- 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 +1 -0
- package/dist/Drawer/Drawer.d.ts.map +1 -1
- package/dist/Drawer/Drawer.js +7 -2
- 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/NumberInput/numberParser.d.ts +3 -0
- package/dist/NumberInput/numberParser.d.ts.map +1 -1
- package/dist/NumberInput/numberParser.js +7 -44
- 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/TimeInput/TimeInput.d.ts +6 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +21 -14
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +5 -4
- 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/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +60 -20
- 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 +15 -1
- package/dist/gestures/GestureArena.d.ts +6 -0
- package/dist/gestures/GestureArena.d.ts.map +1 -1
- package/dist/gestures/GestureArena.js +39 -0
- package/dist/gestures/GestureHost.d.ts +352 -0
- package/dist/gestures/GestureHost.d.ts.map +1 -0
- package/dist/gestures/GestureHost.js +1773 -0
- package/dist/gestures/GestureRecognizer.d.ts +10 -0
- package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/GestureRecognizer.js +16 -0
- package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
- package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/LongPressGestureRecognizer.js +3 -0
- package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
- package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/PanGestureRecognizer.js +3 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
- package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/PinchGestureRecognizer.js +128 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
- package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/SwipeGestureRecognizer.js +148 -0
- package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
- package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/TapGestureRecognizer.js +3 -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/haptics.d.ts +65 -0
- package/dist/gestures/haptics.d.ts.map +1 -0
- package/dist/gestures/haptics.js +182 -0
- package/dist/gestures/nativeScroll.d.ts +42 -0
- package/dist/gestures/nativeScroll.d.ts.map +1 -0
- package/dist/gestures/nativeScroll.js +228 -0
- package/dist/gestures/touchAction.d.ts +60 -0
- package/dist/gestures/touchAction.d.ts.map +1 -0
- package/dist/gestures/touchAction.js +157 -0
- package/dist/gestures/touchActionRule.d.ts +34 -0
- package/dist/gestures/touchActionRule.d.ts.map +1 -0
- package/dist/gestures/touchActionRule.js +140 -0
- package/dist/gestures/types.d.ts +18 -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/plainDate.d.ts +2 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +46 -3
- 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/primaryClick.d.ts +11 -0
- package/dist/utils/primaryClick.d.ts.map +1 -0
- package/dist/utils/primaryClick.js +25 -0
- package/dist/utils/timeFormat.d.ts +47 -0
- package/dist/utils/timeFormat.d.ts.map +1 -0
- package/dist/utils/timeFormat.js +240 -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/dist/utils/unicodeDigits.d.ts +20 -0
- package/dist/utils/unicodeDigits.d.ts.map +1 -0
- package/dist/utils/unicodeDigits.js +70 -0
- package/locales/en.json +15 -3
- 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/Badge/Badge.doc.mjs +1 -1
- package/src/Badge/Badge.tsx +19 -2
- package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -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/ComplexSelector/ComplexSelector.test.tsx +37 -0
- package/src/ContextMenu/ContextMenu.test.tsx +35 -0
- package/src/DateInput/DateInput.test.tsx +115 -3
- package/src/DateInput/DateInput.tsx +29 -14
- package/src/DateInput/DateInputTouch.test.tsx +76 -0
- package/src/DateInput/MonthScroller.tsx +106 -20
- package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
- package/src/DateInput/TouchDateField.tsx +14 -3
- 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.doc.mjs +10 -8
- package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
- package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
- package/src/DateTimeInput/DateTimeInput.tsx +46 -24
- package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
- package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
- 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 +35 -0
- package/src/Drawer/Drawer.tsx +13 -2
- package/src/Drawer/DrawerHeader.spec.md +181 -0
- package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -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.spec.md +205 -0
- 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/NumberInput/numberParser.ts +7 -45
- 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/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
- 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 +58 -4
- 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/TimeInput/NativeTimeInput.test.tsx +17 -0
- package/src/TimeInput/Presentation.test.tsx +21 -0
- package/src/TimeInput/TimeInput.doc.mjs +8 -8
- package/src/TimeInput/TimeInput.spec.md +45 -48
- package/src/TimeInput/TimeInput.test.tsx +76 -0
- package/src/TimeInput/TimeInput.tsx +36 -18
- package/src/TimeInput/TouchTimeField.tsx +4 -6
- 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 +14 -20
- package/src/Toast/Toast.tsx +104 -36
- package/src/Toast/ToastViewport.test.tsx +719 -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/GestureArena.ts +35 -0
- package/src/gestures/GestureHost.ts +2065 -0
- package/src/gestures/GestureRecognizer.ts +18 -0
- package/src/gestures/LongPressGestureRecognizer.ts +4 -0
- package/src/gestures/PanGestureRecognizer.ts +4 -0
- package/src/gestures/PinchGestureRecognizer.ts +148 -0
- package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
- package/src/gestures/SwipeGestureRecognizer.ts +197 -0
- package/src/gestures/TapGestureRecognizer.ts +4 -0
- package/src/gestures/VelocityTracker.ts +10 -3
- package/src/gestures/__tests__/GestureHost.test.ts +2087 -0
- 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__/arena.test.ts +5 -0
- package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
- package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
- package/src/gestures/__tests__/browser/fixture.ts +307 -0
- package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
- package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
- package/src/gestures/__tests__/haptics.test.ts +187 -0
- package/src/gestures/__tests__/harness.ts +21 -2
- package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
- package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
- package/src/gestures/haptics.ts +203 -0
- package/src/gestures/nativeScroll.ts +277 -0
- package/src/gestures/touchAction.ts +191 -0
- package/src/gestures/touchActionRule.ts +157 -0
- package/src/gestures/types.ts +30 -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/plainDate.test.ts +90 -0
- package/src/utils/plainDate.ts +58 -3
- 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/primaryClick.test.ts +38 -0
- package/src/utils/primaryClick.ts +28 -0
- package/src/utils/timeFormat.test.ts +204 -0
- package/src/utils/timeFormat.ts +314 -0
- package/src/utils/touchPresentation.stylex.ts +43 -0
- package/src/utils/touchPresentation.test.ts +140 -1
- package/src/utils/touchPresentation.ts +5 -3
- package/src/utils/unicodeDigits.test.ts +44 -0
- package/src/utils/unicodeDigits.ts +71 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 3
|
|
3
|
+
template_version: 7
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:ListItem
|
|
6
|
+
authority: current
|
|
7
|
+
archive_reason: null
|
|
8
|
+
superseded_by: null
|
|
9
|
+
approved_by: cixzhang
|
|
10
|
+
approved_at: 2026-10-31
|
|
11
|
+
owners: [cixzhang]
|
|
12
|
+
review_triggers: [public-api, behavior, layout, theming]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/List/List.test.tsx,
|
|
16
|
+
packages/core/src/theme/themingTargets.test.ts,
|
|
17
|
+
scripts/check-knowledge.mjs,
|
|
18
|
+
]
|
|
19
|
+
modules: []
|
|
20
|
+
families: [family:navigation-destinations]
|
|
21
|
+
design_specs: []
|
|
22
|
+
architecture:
|
|
23
|
+
[
|
|
24
|
+
architecture:component-style-authoring,
|
|
25
|
+
architecture:component-theming-surface,
|
|
26
|
+
architecture:public-component-api,
|
|
27
|
+
]
|
|
28
|
+
contributing: []
|
|
29
|
+
system_specs: [spec:AST-057, spec:AST-103]
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
# ListItem component contract
|
|
33
|
+
|
|
34
|
+
## Contract at a glance
|
|
35
|
+
|
|
36
|
+
| Area | Contract |
|
|
37
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
38
|
+
| Public contract | `ListItem` with `label`, `description`, `startContent`, `endContent`, `onClick`, `interactiveRef`, `href`, `target`, `rel`, `align`, `isDisabled`, `isSelected`, `swipeActions`, `swipeBehavior`, `xstyle`, `className`, `style`, and `ref`; the row carries `component:List`'s `list-item` target. `itemKey` (`spec:AST-103` DEC-4) is specified but not shipped. |
|
|
39
|
+
| Behavior | One `<li>` row rendered by `component:Item`, which receives every row prop. Inside a List the row takes the list's density, dividers, edge treatment, and marker; outside a List it uses Item's defaults and draws no marker. |
|
|
40
|
+
| End-user impact | Each list entry reads as one list item, with the list's bullet, number, or task checkbox before it and the same interaction Item gives any row. |
|
|
41
|
+
| Builder impact | None. |
|
|
42
|
+
| Compatibility/readiness | Records shipped behavior; no public change. Two implementation gaps, listed under Current-state impact. |
|
|
43
|
+
| Review checks | Reject a row that is not an `<li>`, a ListItem-level edge, density, or divider prop, a marker drawn outside a marked List, or a dropped Item prop. |
|
|
44
|
+
| Governing rules | `component:Item` (`align` for start and end content); `component:List`; `family:navigation-destinations`; `spec:AST-057`; `spec:AST-103`; `architecture:component-theming-surface`; `architecture:component-style-authoring` INV7. |
|
|
45
|
+
|
|
46
|
+
This table is a review projection; the body below is authoritative.
|
|
47
|
+
|
|
48
|
+
## Intent
|
|
49
|
+
|
|
50
|
+
ListItem is the row a List renders for each entry. It owns turning a row into
|
|
51
|
+
a list item: the `<li>` element, what it reads from its List, the marker drawn
|
|
52
|
+
before it, and how it applies `component:List`'s `list-item` target. Everything a row does as a row comes
|
|
53
|
+
from `component:Item`. Consumer usage lives in `ListItem.doc.mjs`.
|
|
54
|
+
|
|
55
|
+
## Compatibility and migration
|
|
56
|
+
|
|
57
|
+
- Released default preserved: `yes`
|
|
58
|
+
- Compatibility class: records shipped behavior; no public change
|
|
59
|
+
- Controlled/uncontrolled behavior: not applicable; `isSelected` and
|
|
60
|
+
`isDisabled` are caller-owned
|
|
61
|
+
- Migration decision: none
|
|
62
|
+
|
|
63
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
64
|
+
|
|
65
|
+
## Current-state impact
|
|
66
|
+
|
|
67
|
+
- **Markers are extra elements.** A bullet marker is drawn as two decorative
|
|
68
|
+
`span` elements, and a number marker as one `span` carrying a counter
|
|
69
|
+
pseudo-element, beside the row's content. `architecture:component-style-authoring`
|
|
70
|
+
INV7 carries decoration with no semantic role on a pseudo-element rather than
|
|
71
|
+
an added element.
|
|
72
|
+
- **ListItem has no `itemKey` yet.** `spec:AST-103` DEC-4 and `component:List`
|
|
73
|
+
FR12 have each reorderable row name its item through an `itemKey` prop;
|
|
74
|
+
ListItem does not accept one yet.
|
|
75
|
+
|
|
76
|
+
## Ownership boundary
|
|
77
|
+
|
|
78
|
+
**Owns**
|
|
79
|
+
|
|
80
|
+
- Rendering the row as an `<li>` through `component:Item` and passing it every
|
|
81
|
+
row prop (FR1).
|
|
82
|
+
- Reading density, dividers, edge treatment, and marker from the enclosing
|
|
83
|
+
List, and the defaults outside one (FR2, FR3).
|
|
84
|
+
- Drawing the marker or a task item's read-only checkbox (FR4).
|
|
85
|
+
- Applying `component:List`'s `list-item` target to the row (FR5).
|
|
86
|
+
|
|
87
|
+
**Does not own / non-goals**
|
|
88
|
+
|
|
89
|
+
- `align`, the start and end content's alignment — owned by `component:Item`; swipe actions — owned by
|
|
90
|
+
`spec:AST-057`; link destinations — owned by
|
|
91
|
+
`family:navigation-destinations`, of which ListItem is a member.
|
|
92
|
+
- Item's label, description, slots, invisible button, delegated activation,
|
|
93
|
+
and disabled and selected semantics — Item's shipped behavior, uncontracted
|
|
94
|
+
here.
|
|
95
|
+
- What each edge treatment does to row geometry and the row inset, the touch
|
|
96
|
+
target, press paint, row motion, and row menus — owned by `component:List`.
|
|
97
|
+
- The `ListMarkerScope` that sets a marker or task state for Markdown's nested
|
|
98
|
+
lists — internal and uncontracted here.
|
|
99
|
+
- Whole number markers and text alignment for Markdown lists — owned by
|
|
100
|
+
`spec:AST-061` FR11 and `component:List` DEC-5.
|
|
101
|
+
|
|
102
|
+
## Public concepts
|
|
103
|
+
|
|
104
|
+
No component-local concept beyond the consumer props; row props are
|
|
105
|
+
`component:Item`'s.
|
|
106
|
+
|
|
107
|
+
## Behavioral and layout contract
|
|
108
|
+
|
|
109
|
+
| ID | Invariant | Basis |
|
|
110
|
+
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
111
|
+
| FR1 | ListItem renders `component:Item` as an `<li>` and passes `label`, `description`, `startContent`, `endContent`, `onClick`, `interactiveRef`, `href`, `target`, `rel`, `align`, `isDisabled`, `isSelected`, `swipeActions`, `swipeBehavior`, and `ref` to it unchanged, with other DOM and data props reaching the row. | Current source and tests |
|
|
112
|
+
| FR2 | Inside a List, the row takes the list's `density`, its `hasDividers` setting, which draws a divider between rows with no extra element and square corners, and its `edge` treatment. | Current source and tests |
|
|
113
|
+
| FR3 | Outside a List, the row uses `balanced` density, no dividers, the `padded` treatment, and no marker, keeping Item's inline inset. | Current source and tests |
|
|
114
|
+
| FR4 | In a List whose `listStyle` is `disc`, `circle`, or `decimal`, the row draws that marker before its content: a disc or circle shape, or a counter number. A task state set for the row draws a read-only, labelled checkbox in the marker's place. Each marker and the task checkbox sit on the label's first line. A List whose `listStyle` is `none` draws neither. Other markers, set internally for Markdown's nested lists, are uncontracted here. | Current source and tests |
|
|
115
|
+
| FR5 | The row carries `component:List`'s `list-item` target, merged with the caller's `className` and `style`; `xstyle` applies after ListItem's own styles. | Current source |
|
|
116
|
+
|
|
117
|
+
### Allowed variation
|
|
118
|
+
|
|
119
|
+
- **AV1 — Row content.** Label, description, and slot content vary.
|
|
120
|
+
- **AV2 — Theme styling.** Themes restyle `list-item`.
|
|
121
|
+
|
|
122
|
+
### Representative states
|
|
123
|
+
|
|
124
|
+
| State | Required invariant | Allowed variation |
|
|
125
|
+
| -------------------- | ------------------------------------------------ | ----------------- |
|
|
126
|
+
| Static row in a List | One `<li>`; no button or anchor. | Content. |
|
|
127
|
+
| Interactive row | Item's invisible button or anchor; one tab stop. | Content. |
|
|
128
|
+
| Divided list | A divider between rows; square corners. | Density. |
|
|
129
|
+
| Numbered list | A counter number before each row. | Number style. |
|
|
130
|
+
| Task row | A read-only checkbox in the marker's place. | Checked state. |
|
|
131
|
+
| Row outside a List | Item's inline inset; no marker. | Content. |
|
|
132
|
+
|
|
133
|
+
### Transformation and precedence order
|
|
134
|
+
|
|
135
|
+
- **ORD1 — Marker.** No marker when the List's `listStyle` is `none`; otherwise
|
|
136
|
+
a task checkbox when the row has a task state, then the marker.
|
|
137
|
+
- **ORD2 — Styling.** ListItem's own styles, then the caller's `xstyle`.
|
|
138
|
+
|
|
139
|
+
### Performance and resources
|
|
140
|
+
|
|
141
|
+
- No resource rule.
|
|
142
|
+
|
|
143
|
+
## Accessibility contract
|
|
144
|
+
|
|
145
|
+
- **AR1 — List semantics.** Inside a List, each row is an `<li>` in the List's
|
|
146
|
+
`<ul>` or `<ol>` (FR1).
|
|
147
|
+
- **AR2 — Row semantics.** Item's interaction, disabled, and selected semantics
|
|
148
|
+
apply unchanged; they are Item's shipped behavior, uncontracted here.
|
|
149
|
+
- **AR3 — Task state.** A task row's checkbox is read-only and labelled (FR4).
|
|
150
|
+
|
|
151
|
+
## Design relationships
|
|
152
|
+
|
|
153
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
154
|
+
| ---------------- | ----------------------------------------------- | ------------------------ | -------------- | ------------------ |
|
|
155
|
+
| Row | Presents one list entry through Item. | `component:Item` | Prominent | FR1, FR5 |
|
|
156
|
+
| Marker | Shows the list's bullet, number, or task state. | Current source | Supporting | FR4 |
|
|
157
|
+
|
|
158
|
+
No anatomy-theming map is recorded yet; the block is optional during
|
|
159
|
+
migration, so the map is deferred.
|
|
160
|
+
|
|
161
|
+
## Family and system relationships
|
|
162
|
+
|
|
163
|
+
- `component:Item` owns `align`, the start and end content's alignment; its
|
|
164
|
+
other row behavior is
|
|
165
|
+
uncontracted. `spec:AST-057` owns swipe actions and
|
|
166
|
+
`family:navigation-destinations` owns link destinations.
|
|
167
|
+
- `component:List` owns the edge treatment and its row geometry and inset;
|
|
168
|
+
its record leaves the density, divider, and marker-style settings
|
|
169
|
+
uncontracted, and FR2 and FR4 here cover what a row does with them.
|
|
170
|
+
- `architecture:component-theming-surface` owns target qualification;
|
|
171
|
+
`component:List` owns the `list-item` target, and FR5 covers how the row
|
|
172
|
+
applies it.
|
|
173
|
+
|
|
174
|
+
## Verification map
|
|
175
|
+
|
|
176
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
177
|
+
| -------- | -------------------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------ | ------------------------------ |
|
|
178
|
+
| FR1, AR1 | `List.test.tsx` row element, align, data attribute, and swipe pass-through tests | Static and interactive rows | A row that is not an `<li>`, or a dropped prop, fails. | `audit:ListItem/behavior` |
|
|
179
|
+
| FR2 | `List.test.tsx` density, divider, radius, and edge treatment tests | Each density; dividers on and off; each edge | A row that ignores its List's settings fails. | `audit:ListItem/layout` |
|
|
180
|
+
| FR3 | `List.test.tsx` "keeps the inset of a ListItem outside a List" | Row outside a List | A row outside a List that loses its inset fails. | `audit:ListItem/layout` |
|
|
181
|
+
| FR4, AR3 | `List.test.tsx` marker and ListMarkerScope tests | Presence of a marker, none, scope marker, task | A missing, extra, or unlabelled marker fails. | `audit:ListItem/anatomy` |
|
|
182
|
+
| AR2 | `List.test.tsx` invisible button, anchor, disabled, and selection tests | Click, href, disabled, selected | Lost row semantics fail. | `audit:ListItem/accessibility` |
|
|
183
|
+
|
|
184
|
+
Read from source, with no focused test yet:
|
|
185
|
+
|
|
186
|
+
- FR1: `interactiveRef`, `rel`, and `ref` reaching the Item row.
|
|
187
|
+
- FR2: the divider border and square corners.
|
|
188
|
+
- FR4: each marker and the task checkbox sitting on the label's first line.
|
|
189
|
+
- FR3: `balanced` density, no dividers, and no marker outside a List.
|
|
190
|
+
- FR4: the shape of each bullet marker (the disc's fill, the circle's ring),
|
|
191
|
+
and the number marker and its counter.
|
|
192
|
+
- FR5: the `list-item` target, and `xstyle` applying after ListItem's styles.
|
|
193
|
+
|
|
194
|
+
## Decision log
|
|
195
|
+
|
|
196
|
+
No component-local decision is recorded.
|
|
197
|
+
|
|
198
|
+
## Open questions
|
|
199
|
+
|
|
200
|
+
None.
|
|
201
|
+
|
|
202
|
+
## Content boundary
|
|
203
|
+
|
|
204
|
+
This file does not duplicate consumer prop tables, Item's row behavior, or
|
|
205
|
+
List's edge and geometry rules. It links to their owners.
|
package/src/List/ListItem.tsx
CHANGED
|
@@ -39,9 +39,9 @@ import {
|
|
|
39
39
|
} from '../theme/tokens.stylex';
|
|
40
40
|
import type {BaseProps} from '../BaseProps';
|
|
41
41
|
import {ListContext, type ListMarker} from './ListContext';
|
|
42
|
-
import {CheckboxInput} from '../CheckboxInput';
|
|
43
|
-
import {mergeProps} from '../utils';
|
|
44
|
-
import {Item} from '../Item';
|
|
42
|
+
import {CheckboxInput} from '../CheckboxInput/CheckboxInput';
|
|
43
|
+
import {mergeProps} from '../utils/mergeProps';
|
|
44
|
+
import {Item} from '../Item/Item';
|
|
45
45
|
import {ItemMarkerColumnsContext} from '../Item/ItemMarkerColumnsContext';
|
|
46
46
|
import type {ItemAlign, ItemSwipeActions, ItemSwipeBehavior} from '../Item';
|
|
47
47
|
import {themeProps} from '../utils/themeProps';
|
|
@@ -54,6 +54,21 @@ describe('MobileNav', () => {
|
|
|
54
54
|
expect(dialog).not.toHaveAttribute('open');
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
it('stays open when a file picker inside it is dismissed', () => {
|
|
58
|
+
const handleClose = vi.fn();
|
|
59
|
+
render(
|
|
60
|
+
<MobileNav isOpen={true} onOpenChange={handleClose}>
|
|
61
|
+
<input type="file" aria-label="Picture" />
|
|
62
|
+
</MobileNav>,
|
|
63
|
+
);
|
|
64
|
+
// A dismissed picker fires a bubbling `cancel` at the input; it is not a
|
|
65
|
+
// close request for the navigation.
|
|
66
|
+
const fromPicker = new Event('cancel', {bubbles: true, cancelable: true});
|
|
67
|
+
screen.getByLabelText('Picture').dispatchEvent(fromPicker);
|
|
68
|
+
expect(handleClose).not.toHaveBeenCalled();
|
|
69
|
+
expect(fromPicker.defaultPrevented).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
57
72
|
it('calls onOpenChange(false) on native cancel event (Escape)', () => {
|
|
58
73
|
const handleClose = vi.fn();
|
|
59
74
|
render(
|
|
@@ -225,7 +225,10 @@ const styles = stylex.create({
|
|
|
225
225
|
display: 'flex',
|
|
226
226
|
alignItems: 'center',
|
|
227
227
|
justifyContent: 'space-between',
|
|
228
|
-
|
|
228
|
+
// At least one bar high, and taller for header content that needs it (a
|
|
229
|
+
// stacked title and search, or a title wrapped at a larger text size), so
|
|
230
|
+
// it never runs into the first item below.
|
|
231
|
+
minHeight: spacingVars['--spacing-12'],
|
|
229
232
|
paddingInline: spacingVars['--spacing-2'],
|
|
230
233
|
flexShrink: 0,
|
|
231
234
|
borderBlockEndWidth: borderVars['--border-width'],
|
|
@@ -648,6 +651,11 @@ export function MobileNav({
|
|
|
648
651
|
// only, and never while an IME composition is in progress.
|
|
649
652
|
const handleCancel = useCallback(
|
|
650
653
|
(event: React.SyntheticEvent<HTMLDialogElement>) => {
|
|
654
|
+
// A cancel that bubbles up from inside (a dismissed file picker) is not
|
|
655
|
+
// a close request; the platform's is fired at the dialog itself.
|
|
656
|
+
if (event.target !== event.currentTarget) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
651
659
|
event.preventDefault();
|
|
652
660
|
if (!shouldDismissOnCloseRequest()) {
|
|
653
661
|
return;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file MobileNavHeader.a11y.chromium.spec.ts
|
|
5
|
+
* @input Built MobileNav stories in a phone context in real Chromium
|
|
6
|
+
* @output Browser evidence that the header row is at least one bar high and
|
|
7
|
+
* grows with taller header content: a stacked title and search field stay
|
|
8
|
+
* inside the row, and the first navigation item starts below it
|
|
9
|
+
* @position Chromium proof for MobileNav's header row (`styles.header` in
|
|
10
|
+
* MobileNav.tsx, `minHeight`); jsdom has no layout to measure it
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {expect, test, type Page} from '@playwright/test';
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_STORYBOOK_DIR,
|
|
16
|
+
serveStorybook,
|
|
17
|
+
type StaticServer,
|
|
18
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
19
|
+
|
|
20
|
+
let storybook: StaticServer;
|
|
21
|
+
test.beforeAll(async () => {
|
|
22
|
+
storybook = await serveStorybook(
|
|
23
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
test.afterAll(async () => {
|
|
27
|
+
await storybook?.close();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test.use({
|
|
31
|
+
hasTouch: true,
|
|
32
|
+
isMobile: true,
|
|
33
|
+
viewport: {width: 390, height: 844},
|
|
34
|
+
deviceScaleFactor: 1,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/** Opens a story's drawer and returns the boxes of its header row and first item. */
|
|
38
|
+
async function openAndMeasure(page: Page, story: string, firstItem: string) {
|
|
39
|
+
await page.goto(
|
|
40
|
+
`${storybook.origin}/iframe.html?id=core-mobilenav--${story}&viewMode=story`,
|
|
41
|
+
);
|
|
42
|
+
await page.getByRole('button', {name: 'Open Navigation'}).click();
|
|
43
|
+
const nav = page.getByRole('dialog');
|
|
44
|
+
await expect(nav).toBeVisible();
|
|
45
|
+
const close = nav.getByRole('button', {name: 'Close navigation'});
|
|
46
|
+
// The Close button sits in the header row.
|
|
47
|
+
const header = close.locator('xpath=..');
|
|
48
|
+
const headerBox = await header.boundingBox();
|
|
49
|
+
const itemBox = await nav
|
|
50
|
+
.getByRole('link', {name: firstItem})
|
|
51
|
+
.first()
|
|
52
|
+
.boundingBox();
|
|
53
|
+
if (headerBox == null || itemBox == null) {
|
|
54
|
+
throw new Error('the drawer did not render');
|
|
55
|
+
}
|
|
56
|
+
return {nav, header, headerBox, itemBox};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
test('a one-line header keeps the one-bar row', async ({page}) => {
|
|
60
|
+
const {headerBox, itemBox} = await openAndMeasure(
|
|
61
|
+
page,
|
|
62
|
+
'default',
|
|
63
|
+
'Dashboard',
|
|
64
|
+
);
|
|
65
|
+
// --spacing-12 at the default root size, as before.
|
|
66
|
+
expect(headerBox.height).toBeCloseTo(48, 0);
|
|
67
|
+
expect(itemBox.y).toBeGreaterThanOrEqual(headerBox.y + headerBox.height);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('a taller header grows its row, and the first item starts below it', async ({
|
|
71
|
+
page,
|
|
72
|
+
}) => {
|
|
73
|
+
const {nav, headerBox, itemBox} = await openAndMeasure(
|
|
74
|
+
page,
|
|
75
|
+
'tall-header',
|
|
76
|
+
'Dashboard',
|
|
77
|
+
);
|
|
78
|
+
expect(headerBox.height).toBeGreaterThan(48);
|
|
79
|
+
const search = await nav.getByRole('textbox', {name: 'Search'}).boundingBox();
|
|
80
|
+
if (search == null) {
|
|
81
|
+
throw new Error('the search field did not render');
|
|
82
|
+
}
|
|
83
|
+
// The search field is inside the row, and nothing overlaps the first item.
|
|
84
|
+
expect(search.y).toBeGreaterThanOrEqual(headerBox.y);
|
|
85
|
+
expect(search.y + search.height).toBeLessThanOrEqual(
|
|
86
|
+
headerBox.y + headerBox.height,
|
|
87
|
+
);
|
|
88
|
+
expect(itemBox.y).toBeGreaterThanOrEqual(headerBox.y + headerBox.height);
|
|
89
|
+
});
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* SYNC: When MoreMenu.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import {createRef} from 'react';
|
|
12
13
|
import {describe, it, expect, vi, beforeAll, beforeEach} from 'vitest';
|
|
13
14
|
import {render, screen, fireEvent, waitFor} from '@testing-library/react';
|
|
14
15
|
import userEvent from '@testing-library/user-event';
|
|
@@ -256,6 +257,16 @@ describe('MoreMenu', () => {
|
|
|
256
257
|
expect(handleEdit).not.toHaveBeenCalled();
|
|
257
258
|
});
|
|
258
259
|
|
|
260
|
+
it('forwards its ref to the bottom-sheet trigger', () => {
|
|
261
|
+
const ref = createRef<HTMLButtonElement>();
|
|
262
|
+
render(
|
|
263
|
+
<MoreMenu items={defaultItems} ref={ref} presentation="bottom-sheet" />,
|
|
264
|
+
);
|
|
265
|
+
expect(ref.current).toBe(
|
|
266
|
+
screen.getByRole('button', {name: 'More options'}),
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
|
|
259
270
|
it('supports forwardRef', () => {
|
|
260
271
|
const ref = vi.fn();
|
|
261
272
|
render(<MoreMenu items={defaultItems} ref={ref} />);
|
|
@@ -182,10 +182,13 @@ and `aria-controls` for the listbox, the listbox carries `aria-label` from
|
|
|
182
182
|
focused element that owns arrow, Enter, Escape, and typeahead keys while open,
|
|
183
183
|
so an anchor that cannot take focus still leaves a keyboard path.
|
|
184
184
|
|
|
185
|
-
The create row is a `role="option"` with `aria-selected="false"` and no
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
The create row is a `role="option"` with `aria-selected="false"` and no checkbox
|
|
186
|
+
indicator. When it is the only result, the polite live region announces its
|
|
187
|
+
label rather than the empty-search message, so what is announced matches what is
|
|
188
|
+
on screen; picking it announces the creation. The result count and the create
|
|
189
|
+
row's label come from the search's own `useAnnounce({hasReplace: true})`
|
|
190
|
+
instance, so a newer one replaces one not yet spoken (`spec:AST-086` FR9), and
|
|
191
|
+
selection messages keep their own instance.
|
|
189
192
|
|
|
190
193
|
## Design relationships
|
|
191
194
|
|
|
@@ -9,7 +9,15 @@
|
|
|
9
9
|
* SYNC: When MultiSelector.tsx API changes, update these tests.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
describe,
|
|
14
|
+
it,
|
|
15
|
+
expect,
|
|
16
|
+
vi,
|
|
17
|
+
beforeAll,
|
|
18
|
+
beforeEach,
|
|
19
|
+
afterEach,
|
|
20
|
+
} from 'vitest';
|
|
13
21
|
import {
|
|
14
22
|
act,
|
|
15
23
|
render,
|
|
@@ -65,6 +73,15 @@ function politeRegion(): HTMLElement | null {
|
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
// Mock showPopover and hidePopover methods since they're not implemented in jsdom
|
|
76
|
+
// The bottom-sheet presentation renders BottomSheet through React.lazy. Its
|
|
77
|
+
// first import transforms the BottomSheet module tree, which on a loaded
|
|
78
|
+
// runner takes longer than a query's 1 s wait, so the first sheet test timed
|
|
79
|
+
// out before the dialog existed. Load the module once up front: lazy() then
|
|
80
|
+
// resolves from the module cache and every sheet test waits only on the open.
|
|
81
|
+
beforeAll(async () => {
|
|
82
|
+
await import('../BottomSheet/BottomSheet');
|
|
83
|
+
});
|
|
84
|
+
|
|
68
85
|
beforeEach(() => {
|
|
69
86
|
__resetInteractionModalityForTest();
|
|
70
87
|
HTMLDialogElement.prototype.showModal = vi.fn(function (
|
|
@@ -128,6 +145,13 @@ function expectSilentBusySpinner() {
|
|
|
128
145
|
expect(screen.queryByRole('status', {name: 'Loading'})).toBeNull();
|
|
129
146
|
}
|
|
130
147
|
|
|
148
|
+
// The bottom sheet is loaded on demand (SelectorBottomSheet's lazy import).
|
|
149
|
+
// Loading its module is not part of an open, so the file waits for it once
|
|
150
|
+
// here, and each test's open waits only for the open.
|
|
151
|
+
beforeAll(async () => {
|
|
152
|
+
await import('../BottomSheet/BottomSheet');
|
|
153
|
+
});
|
|
154
|
+
|
|
131
155
|
describe('MultiSelector', () => {
|
|
132
156
|
const defaultOptions = ['Apple', 'Banana', 'Orange'];
|
|
133
157
|
|
|
@@ -1388,6 +1412,58 @@ describe('MultiSelector', () => {
|
|
|
1388
1412
|
expect(within(listbox).getByText('Add a fruit first')).toBeInTheDocument();
|
|
1389
1413
|
});
|
|
1390
1414
|
|
|
1415
|
+
describe('count trigger text', () => {
|
|
1416
|
+
it('shows the default count in the catalog message', () => {
|
|
1417
|
+
render(
|
|
1418
|
+
<MultiSelector
|
|
1419
|
+
label="Fruit"
|
|
1420
|
+
options={defaultOptions}
|
|
1421
|
+
value={['Apple', 'Banana']}
|
|
1422
|
+
onChange={() => {}}
|
|
1423
|
+
triggerDisplay="count"
|
|
1424
|
+
/>,
|
|
1425
|
+
);
|
|
1426
|
+
expect(screen.getByText('2 selected')).toBeInTheDocument();
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
it('translates the default count, with plural forms, under another locale', () => {
|
|
1430
|
+
render(
|
|
1431
|
+
<InternationalizationProvider
|
|
1432
|
+
locale="fr"
|
|
1433
|
+
overrides={{
|
|
1434
|
+
fr: {
|
|
1435
|
+
'@astryx.multiSelector.selectedCount':
|
|
1436
|
+
'{count, number} {count, plural, one {sélectionné} other {sélectionnés}}',
|
|
1437
|
+
},
|
|
1438
|
+
}}>
|
|
1439
|
+
<MultiSelector
|
|
1440
|
+
label="Fruit"
|
|
1441
|
+
options={defaultOptions}
|
|
1442
|
+
value={['Apple', 'Banana']}
|
|
1443
|
+
onChange={() => {}}
|
|
1444
|
+
triggerDisplay="count"
|
|
1445
|
+
/>
|
|
1446
|
+
</InternationalizationProvider>,
|
|
1447
|
+
);
|
|
1448
|
+
expect(screen.getByText('2 sélectionnés')).toBeInTheDocument();
|
|
1449
|
+
expect(screen.queryByText('2 selected')).not.toBeInTheDocument();
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
it('still lets formatValue replace the count text', () => {
|
|
1453
|
+
render(
|
|
1454
|
+
<MultiSelector
|
|
1455
|
+
label="Fruit"
|
|
1456
|
+
options={defaultOptions}
|
|
1457
|
+
value={['Apple', 'Banana']}
|
|
1458
|
+
onChange={() => {}}
|
|
1459
|
+
triggerDisplay="count"
|
|
1460
|
+
formatValue={items => `${items.length} fruits`}
|
|
1461
|
+
/>,
|
|
1462
|
+
);
|
|
1463
|
+
expect(screen.getByText('2 fruits')).toBeInTheDocument();
|
|
1464
|
+
});
|
|
1465
|
+
});
|
|
1466
|
+
|
|
1391
1467
|
describe('result announcements', () => {
|
|
1392
1468
|
it('announces the match count politely while searching', async () => {
|
|
1393
1469
|
const user = userEvent.setup();
|
|
@@ -3436,6 +3512,47 @@ describe('MultiSelector trigger render prop', () => {
|
|
|
3436
3512
|
expect(screen.queryByText('Labels')).toBeNull();
|
|
3437
3513
|
});
|
|
3438
3514
|
|
|
3515
|
+
// spec:AST-055 AR3: a light dismiss returns focus to the caller's control,
|
|
3516
|
+
// even when the press that closed the list focused another field.
|
|
3517
|
+
it('returns focus to the caller control after a light dismiss onto another field', async () => {
|
|
3518
|
+
const user = userEvent.setup();
|
|
3519
|
+
render(
|
|
3520
|
+
<>
|
|
3521
|
+
<MultiSelector
|
|
3522
|
+
label="Labels"
|
|
3523
|
+
options={OPTIONS}
|
|
3524
|
+
value={[]}
|
|
3525
|
+
onChange={() => {}}
|
|
3526
|
+
renderTrigger={props => (
|
|
3527
|
+
<button type="button" {...props}>
|
|
3528
|
+
Pick labels
|
|
3529
|
+
</button>
|
|
3530
|
+
)}
|
|
3531
|
+
/>
|
|
3532
|
+
<input aria-label="Notes" />
|
|
3533
|
+
</>,
|
|
3534
|
+
);
|
|
3535
|
+
const opener = screen.getByRole('button', {name: 'Pick labels'});
|
|
3536
|
+
await user.click(opener);
|
|
3537
|
+
expect(opener).toHaveAttribute('aria-expanded', 'true');
|
|
3538
|
+
|
|
3539
|
+
act(() => {
|
|
3540
|
+
screen.getByRole('textbox', {name: 'Notes'}).focus();
|
|
3541
|
+
});
|
|
3542
|
+
const popover = document.querySelector('[popover]') as HTMLElement;
|
|
3543
|
+
act(() => {
|
|
3544
|
+
popover.dispatchEvent(
|
|
3545
|
+
Object.assign(new Event('toggle'), {
|
|
3546
|
+
oldState: 'open',
|
|
3547
|
+
newState: 'closed',
|
|
3548
|
+
}),
|
|
3549
|
+
);
|
|
3550
|
+
});
|
|
3551
|
+
|
|
3552
|
+
expect(opener).toHaveAttribute('aria-expanded', 'false');
|
|
3553
|
+
expect(opener).toHaveFocus();
|
|
3554
|
+
});
|
|
3555
|
+
|
|
3439
3556
|
it('opens against the caller control and names the listbox by label', async () => {
|
|
3440
3557
|
const user = userEvent.setup();
|
|
3441
3558
|
render(
|
|
@@ -905,7 +905,7 @@ export interface MultiSelectorProps<
|
|
|
905
905
|
* Not called when nothing is selected — the placeholder shows instead — and
|
|
906
906
|
* not called for triggerDisplay 'badges', which renders Badge elements
|
|
907
907
|
* rather than text.
|
|
908
|
-
* @default
|
|
908
|
+
* @default the translated `@astryx.multiSelector.selectedCount` message ("2 selected") for 'count', "A, B, C, +N" for 'labels'
|
|
909
909
|
*/
|
|
910
910
|
formatValue?: (items: MultiSelectorSelectedItem[]) => string;
|
|
911
911
|
|
|
@@ -1450,6 +1450,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
1450
1450
|
onHide: handleLayerHide,
|
|
1451
1451
|
onShow: handleLayerShow,
|
|
1452
1452
|
triggerRef,
|
|
1453
|
+
isFocusReturnAlways: hasExternalTrigger,
|
|
1453
1454
|
popoverOptions: {
|
|
1454
1455
|
hasLightDismiss: true,
|
|
1455
1456
|
hasCloseButton: false,
|
|
@@ -1890,7 +1891,9 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
1890
1891
|
return (
|
|
1891
1892
|
<span {...stylex.props(styles.triggerText)}>
|
|
1892
1893
|
{formatValue?.(selectedItems) ??
|
|
1893
|
-
|
|
1894
|
+
t('@astryx.multiSelector.selectedCount', {
|
|
1895
|
+
count: optimisticValue.length,
|
|
1896
|
+
})}
|
|
1894
1897
|
</span>
|
|
1895
1898
|
);
|
|
1896
1899
|
|
|
@@ -1933,6 +1936,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
1933
1936
|
placeholder,
|
|
1934
1937
|
formatValue,
|
|
1935
1938
|
maxBadges,
|
|
1939
|
+
t,
|
|
1936
1940
|
]);
|
|
1937
1941
|
|
|
1938
1942
|
// Render search input
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import {cleanup, render, screen, within} from '@testing-library/react';
|
|
11
11
|
import userEvent from '@testing-library/user-event';
|
|
12
|
-
import {describe, expect, it} from 'vitest';
|
|
12
|
+
import {beforeAll, describe, expect, it} from 'vitest';
|
|
13
13
|
import {
|
|
14
14
|
expectAccessibilitySpec,
|
|
15
15
|
LISTBOX_PATTERN,
|
|
@@ -23,6 +23,13 @@ import {installListboxDialogStubs} from '../../Selector/__tests__/Listbox.a11y.d
|
|
|
23
23
|
|
|
24
24
|
installListboxDialogStubs();
|
|
25
25
|
|
|
26
|
+
// The bottom sheet is loaded on demand (SelectorBottomSheet's lazy import).
|
|
27
|
+
// Loading its module is not part of an open, so the file waits for it once
|
|
28
|
+
// here, and each scenario's open waits only for the open.
|
|
29
|
+
beforeAll(async () => {
|
|
30
|
+
await import('../../BottomSheet/BottomSheet');
|
|
31
|
+
});
|
|
32
|
+
|
|
26
33
|
const scenarios = LISTBOX_SCENARIOS.filter(
|
|
27
34
|
scenario => scenario.component === 'MultiSelector',
|
|
28
35
|
);
|
|
@@ -44,7 +44,7 @@ system_specs: [spec:AST-005/DEC-1, spec:AST-020, spec:AST-085]
|
|
|
44
44
|
| Behavior | A `role="menu"` list of `menuitem` rows inside a heading popover, with arrow, Home, End, typeahead, Enter, Space, and Escape support. Selecting an enabled item runs it and, where the host heading supplies a close action, closes the heading menu. |
|
|
45
45
|
| End-user impact | Users switch products or accounts from the heading menu by pointer or keyboard; disabled rows are skipped by keyboard and typeahead. |
|
|
46
46
|
| Builder impact | None. |
|
|
47
|
-
| Compatibility/readiness | Records shipped behavior; no public change.
|
|
47
|
+
| Compatibility/readiness | Records shipped behavior; no public change. Five implementation gaps (see Current-state impact). |
|
|
48
48
|
| Review checks | Reject losing `menu`/`menuitem` roles to caller props, keyboard focus landing on a disabled item, or a caller `onKeyDown` that silently disables the keyboard model. |
|
|
49
49
|
| Governing rules | `family:navigation-destinations` FR7; `architecture:interaction-modality`; `architecture:component-theming-surface`; `architecture:layer-runtime`. |
|
|
50
50
|
|
|
@@ -81,10 +81,6 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
81
81
|
`role="menu"` sits directly inside the heading's own `role="menu"` wrapper.
|
|
82
82
|
WAI-ARIA 1.2 allows a `menu` to own only `menuitem`-family items and groups,
|
|
83
83
|
so assistive technology meets a menu nested in a menu.
|
|
84
|
-
- **Inside a heading, arrow keys move twice.** Inside SideNavHeading or
|
|
85
|
-
TopNavHeading, the heading's own list handler also moves focus over every
|
|
86
|
-
`menuitem`, so one ArrowDown moves two items, and in a two-item menu ArrowDown
|
|
87
|
-
never moves focus. The APG menu pattern moves focus one item per arrow press.
|
|
88
84
|
- **The menu has no name of its own.** NavHeadingMenu's `menu` carries no
|
|
89
85
|
accessible name unless the caller passes `aria-label`; inside a heading only
|
|
90
86
|
the heading's wrapper `menu` is named. The APG menu pattern, which
|