@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,312 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file MonthScrollerSteps.interaction.browser.spec.ts
|
|
5
|
+
* @input The built "Default" DateInput story, shown in a bottom sheet, the
|
|
6
|
+
* arrows clicked from the page, real mouse and trackpad input and the
|
|
7
|
+
* interaction harness
|
|
8
|
+
* @output Interaction proofs for the month scroller's steps: after the month
|
|
9
|
+
* and year wheels close, a trackpad scroll still pages to the next month and
|
|
10
|
+
* Reset from four months on still rests on today's month; an arrow step
|
|
11
|
+
* through a stalled frame moves exactly one month; under a finger, a swipe
|
|
12
|
+
* that crosses the middle of a month as it lifts keeps scroll snapping on
|
|
13
|
+
* and rests on the next month.
|
|
14
|
+
* (A second arrow step late in the first one's scroll, which WebKit cuts
|
|
15
|
+
* short, is proven in MonthScrollerSettle.test.tsx: a loaded page's frames
|
|
16
|
+
* are too far apart to aim a click at a share of a 300 ms scroll.)
|
|
17
|
+
* @position Interaction tests of MonthScroller.tsx
|
|
18
|
+
* (`*.interaction.browser.spec.ts`, run by `pnpm test:interaction`)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {expect, test, type Locator, type Page} from '@playwright/test';
|
|
22
|
+
import {
|
|
23
|
+
blockMainThread,
|
|
24
|
+
drag,
|
|
25
|
+
openStory,
|
|
26
|
+
touchDriver,
|
|
27
|
+
withStorybook,
|
|
28
|
+
} from '@astryxdesign/interaction-harness';
|
|
29
|
+
|
|
30
|
+
const storybook = withStorybook(test);
|
|
31
|
+
const STORY = 'core-dateinput--default';
|
|
32
|
+
const ARGS = {presentation: 'bottom-sheet'};
|
|
33
|
+
|
|
34
|
+
/** Waits until the scroller's offset has not moved for ten frames. */
|
|
35
|
+
async function waitForRest(viewport: Locator): Promise<number> {
|
|
36
|
+
return viewport.evaluate(
|
|
37
|
+
async element =>
|
|
38
|
+
new Promise<number>(resolve => {
|
|
39
|
+
let last = element.scrollLeft;
|
|
40
|
+
let still = 0;
|
|
41
|
+
const check = () => {
|
|
42
|
+
const now = element.scrollLeft;
|
|
43
|
+
still = now === last ? still + 1 : 0;
|
|
44
|
+
last = now;
|
|
45
|
+
if (still >= 10) {
|
|
46
|
+
resolve(now);
|
|
47
|
+
} else {
|
|
48
|
+
requestAnimationFrame(check);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
requestAnimationFrame(check);
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The month the scroller rests on, as the pane's own grid names it. */
|
|
57
|
+
/**
|
|
58
|
+
* Waits until a month's grid sits flush with the scroller's edge, the offset
|
|
59
|
+
* unchanged, for ten frames: the end of the scroller's own settle
|
|
60
|
+
* correction, however long the page takes to run it. (The panes' width can
|
|
61
|
+
* be fractional, so the grid's own box is the measure, not the offset.)
|
|
62
|
+
*/
|
|
63
|
+
async function waitForRestOnMonth(viewport: Locator): Promise<number> {
|
|
64
|
+
return viewport.evaluate(
|
|
65
|
+
async element =>
|
|
66
|
+
new Promise<number>(resolve => {
|
|
67
|
+
let last = element.scrollLeft;
|
|
68
|
+
let still = 0;
|
|
69
|
+
const flush = () => {
|
|
70
|
+
const edge = element.getBoundingClientRect().left;
|
|
71
|
+
return [
|
|
72
|
+
...element.querySelectorAll<HTMLElement>('[role="grid"]'),
|
|
73
|
+
].some(
|
|
74
|
+
grid => Math.abs(grid.getBoundingClientRect().left - edge) < 1,
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
const check = () => {
|
|
78
|
+
const now = element.scrollLeft;
|
|
79
|
+
still = now === last && flush() ? still + 1 : 0;
|
|
80
|
+
last = now;
|
|
81
|
+
if (still >= 10) {
|
|
82
|
+
resolve(now);
|
|
83
|
+
} else {
|
|
84
|
+
requestAnimationFrame(check);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
requestAnimationFrame(check);
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function monthAtRest(viewport: Locator): Promise<string> {
|
|
93
|
+
return viewport.evaluate(element => {
|
|
94
|
+
const box = element.getBoundingClientRect();
|
|
95
|
+
const middle = box.left + box.width / 2;
|
|
96
|
+
for (const grid of element.querySelectorAll<HTMLElement>('[role="grid"]')) {
|
|
97
|
+
const r = grid.getBoundingClientRect();
|
|
98
|
+
if (r.left <= middle && r.right >= middle) {
|
|
99
|
+
return grid.getAttribute('aria-label') ?? '';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return '';
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function openCalendar(page: Page) {
|
|
107
|
+
await openStory(page, storybook(), STORY, ARGS);
|
|
108
|
+
await page
|
|
109
|
+
.getByRole('button', {name: /calendar/i})
|
|
110
|
+
.first()
|
|
111
|
+
.click();
|
|
112
|
+
const viewport = page.locator('[data-scroller="months"]');
|
|
113
|
+
await expect(viewport).toBeVisible();
|
|
114
|
+
await waitForRest(viewport);
|
|
115
|
+
return viewport;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
test.use({hasTouch: false, isMobile: false});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Waits until the scroller comes to rest, or until it has gone more than
|
|
122
|
+
* `limit` past `from`: a scroller that runs on by itself never rests, and
|
|
123
|
+
* the proof should fail where it ran, not at the test's timeout.
|
|
124
|
+
*/
|
|
125
|
+
async function waitForRestOrRun(
|
|
126
|
+
viewport: Locator,
|
|
127
|
+
from: number,
|
|
128
|
+
limit: number,
|
|
129
|
+
): Promise<number> {
|
|
130
|
+
return viewport.evaluate(
|
|
131
|
+
async (element, {from, limit}) =>
|
|
132
|
+
new Promise<number>(resolve => {
|
|
133
|
+
let last = element.scrollLeft;
|
|
134
|
+
let still = 0;
|
|
135
|
+
const check = () => {
|
|
136
|
+
const now = element.scrollLeft;
|
|
137
|
+
still = now === last ? still + 1 : 0;
|
|
138
|
+
last = now;
|
|
139
|
+
if (still >= 10 || Math.abs(now - from) > limit) {
|
|
140
|
+
resolve(now);
|
|
141
|
+
} else {
|
|
142
|
+
requestAnimationFrame(check);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
requestAnimationFrame(check);
|
|
146
|
+
}),
|
|
147
|
+
{from, limit},
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* A stalled frame right after an arrow (a 300 ms block of the main thread)
|
|
153
|
+
* let the re-center that follows the step land after the scroll had ended,
|
|
154
|
+
* and WebKit's re-snap after it then paged on by itself, a month every few
|
|
155
|
+
* frames. The step moves exactly one month.
|
|
156
|
+
*/
|
|
157
|
+
test('an arrow step through a stalled frame moves exactly one month', async ({
|
|
158
|
+
page,
|
|
159
|
+
}) => {
|
|
160
|
+
const viewport = await openCalendar(page);
|
|
161
|
+
const title = page.getByRole('button', {name: /Choose month and year/});
|
|
162
|
+
const from = await viewport.evaluate(element => element.scrollLeft);
|
|
163
|
+
const width = await viewport.evaluate(element => element.clientWidth);
|
|
164
|
+
const box = await page
|
|
165
|
+
.getByRole('button', {name: 'Next month'})
|
|
166
|
+
.boundingBox();
|
|
167
|
+
if (box == null) {
|
|
168
|
+
throw new Error('no Next month button');
|
|
169
|
+
}
|
|
170
|
+
await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
|
|
171
|
+
await blockMainThread(page, 300);
|
|
172
|
+
const at = await waitForRestOrRun(viewport, from, 2 * width);
|
|
173
|
+
expect(Math.round((at - from) / width)).toBe(1);
|
|
174
|
+
await waitForRestOnMonth(viewport);
|
|
175
|
+
expect(
|
|
176
|
+
Math.round(((await viewport.evaluate(e => e.scrollLeft)) - from) / width),
|
|
177
|
+
).toBe(1);
|
|
178
|
+
await expect(title).toHaveAccessibleName(
|
|
179
|
+
new RegExp(await monthAtRest(viewport)),
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Closing the month and year wheels re-asserts the month shown, a steer that
|
|
185
|
+
* moves nothing. A trackpad scroll after it is the user's: it pages to the
|
|
186
|
+
* next month and stays there, the title following.
|
|
187
|
+
*/
|
|
188
|
+
test('after the month and year wheels close, a trackpad scroll to the next month stays there', async ({
|
|
189
|
+
page,
|
|
190
|
+
}) => {
|
|
191
|
+
const viewport = await openCalendar(page);
|
|
192
|
+
const toggle = page.getByRole('button', {name: /Choose month and year/});
|
|
193
|
+
const start = await monthAtRest(viewport);
|
|
194
|
+
expect(start).not.toBe('');
|
|
195
|
+
const toggleBox = await toggle.boundingBox();
|
|
196
|
+
if (toggleBox == null) {
|
|
197
|
+
throw new Error('no month and year toggle');
|
|
198
|
+
}
|
|
199
|
+
const clickToggle = async () =>
|
|
200
|
+
page.mouse.click(
|
|
201
|
+
toggleBox.x + toggleBox.width / 2,
|
|
202
|
+
toggleBox.y + toggleBox.height / 2,
|
|
203
|
+
);
|
|
204
|
+
await clickToggle();
|
|
205
|
+
await expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
|
206
|
+
await clickToggle();
|
|
207
|
+
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
|
208
|
+
await waitForRest(viewport);
|
|
209
|
+
|
|
210
|
+
const box = await viewport.boundingBox();
|
|
211
|
+
if (box == null) {
|
|
212
|
+
throw new Error('no month scroller');
|
|
213
|
+
}
|
|
214
|
+
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
|
215
|
+
// A horizontal trackpad scroll of 70 % of a month, as one wheel event: a
|
|
216
|
+
// burst of small ones reaches a loaded page far enough apart to count as
|
|
217
|
+
// separate gestures, each snapping back on its own.
|
|
218
|
+
await page.mouse.wheel(box.width * 0.7, 0);
|
|
219
|
+
// Past the scroller's own settle correction too, which ends on a month's
|
|
220
|
+
// boundary.
|
|
221
|
+
await waitForRestOnMonth(viewport);
|
|
222
|
+
const next = await monthAtRest(viewport);
|
|
223
|
+
expect(next).not.toBe(start);
|
|
224
|
+
await expect(toggle).toHaveAccessibleName(new RegExp(next));
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Reset from four months on is a far jump, mounting today's month before the
|
|
229
|
+
* scroller moves. After the month and year wheels close, whose re-assert is a
|
|
230
|
+
* steer that moved nothing, the jump still rests on today's month, the title
|
|
231
|
+
* with it.
|
|
232
|
+
*/
|
|
233
|
+
test('after the month and year wheels close, Reset from four months on rests on today’s month', async ({
|
|
234
|
+
page,
|
|
235
|
+
}) => {
|
|
236
|
+
const viewport = await openCalendar(page);
|
|
237
|
+
const toggle = page.getByRole('button', {name: /Choose month and year/});
|
|
238
|
+
const today = await monthAtRest(viewport);
|
|
239
|
+
expect(today).not.toBe('');
|
|
240
|
+
const clickCentre = async (name: string | RegExp) => {
|
|
241
|
+
const box = await page.getByRole('button', {name}).boundingBox();
|
|
242
|
+
if (box == null) {
|
|
243
|
+
throw new Error(`no ${String(name)} button`);
|
|
244
|
+
}
|
|
245
|
+
await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
|
|
246
|
+
};
|
|
247
|
+
for (let step = 0; step < 4; step++) {
|
|
248
|
+
await clickCentre('Next month');
|
|
249
|
+
await waitForRest(viewport);
|
|
250
|
+
}
|
|
251
|
+
expect(await monthAtRest(viewport)).not.toBe(today);
|
|
252
|
+
await clickCentre(/Choose month and year/);
|
|
253
|
+
await expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
|
254
|
+
await clickCentre(/Choose month and year/);
|
|
255
|
+
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
|
256
|
+
await waitForRest(viewport);
|
|
257
|
+
|
|
258
|
+
await clickCentre('Reset');
|
|
259
|
+
// Past the scroller's own settle correction too, which ends on a month's
|
|
260
|
+
// boundary.
|
|
261
|
+
await waitForRestOnMonth(viewport);
|
|
262
|
+
expect(await monthAtRest(viewport)).toBe(today);
|
|
263
|
+
await expect(toggle).toHaveAccessibleName(new RegExp(today));
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test.describe('under a finger', () => {
|
|
267
|
+
test.use({hasTouch: true, isMobile: true});
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* A swipe whose last move crosses the middle of the month makes the
|
|
271
|
+
* scroller re-center as the finger lifts. The finger drives that scroll, so
|
|
272
|
+
* the re-center leaves scroll snapping on (turning it off drops the snap
|
|
273
|
+
* target iOS tracks for the gesture, and the swipe coasted past the month),
|
|
274
|
+
* and the swipe comes to rest on the next month.
|
|
275
|
+
*/
|
|
276
|
+
test('a swipe that crosses the middle of a month as it lifts keeps snapping on and rests on the next month', async ({
|
|
277
|
+
page,
|
|
278
|
+
}) => {
|
|
279
|
+
const viewport = await openCalendar(page);
|
|
280
|
+
const start = await monthAtRest(viewport);
|
|
281
|
+
const box = await viewport.boundingBox();
|
|
282
|
+
if (box == null) {
|
|
283
|
+
throw new Error('no month scroller');
|
|
284
|
+
}
|
|
285
|
+
// Every change of the scroller's inline scroll-snap-type from here on.
|
|
286
|
+
await viewport.evaluate(element => {
|
|
287
|
+
const changes: string[] = [];
|
|
288
|
+
(window as unknown as {snapChanges: string[]}).snapChanges = changes;
|
|
289
|
+
new MutationObserver(() => {
|
|
290
|
+
changes.push((element as HTMLElement).style.scrollSnapType || 'on');
|
|
291
|
+
}).observe(element, {attributes: true, attributeFilter: ['style']});
|
|
292
|
+
});
|
|
293
|
+
const driver = await touchDriver(page);
|
|
294
|
+
const y = box.y + box.height / 2;
|
|
295
|
+
const from = {x: box.x + box.width * 0.8, y};
|
|
296
|
+
// 55 % of a month toward the next one, the middle crossed by the last
|
|
297
|
+
// move, lifting a quarter frame after it.
|
|
298
|
+
await driver.play(
|
|
299
|
+
drag(from, {x: from.x - box.width * 0.55, y}, {durationMs: 300}),
|
|
300
|
+
);
|
|
301
|
+
await waitForRestOnMonth(viewport);
|
|
302
|
+
const rest = await monthAtRest(viewport);
|
|
303
|
+
expect(rest).not.toBe('');
|
|
304
|
+
expect(rest).not.toBe(start);
|
|
305
|
+
// Snapping was never turned off while the finger drove the scroll.
|
|
306
|
+
expect(
|
|
307
|
+
await page.evaluate(
|
|
308
|
+
() => (window as unknown as {snapChanges: string[]}).snapChanges,
|
|
309
|
+
),
|
|
310
|
+
).not.toContain('none');
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -18,9 +18,9 @@ function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
|
|
|
18
18
|
const {prose, component} = generateThemeCSS(theme);
|
|
19
19
|
return [prose, component].filter(Boolean).join('\n\n');
|
|
20
20
|
}
|
|
21
|
-
// getButton/queryButton instead of getByRole('button', {name}): the
|
|
22
|
-
// popover
|
|
23
|
-
//
|
|
21
|
+
// getButton/queryButton instead of getByRole('button', {name}): the open
|
|
22
|
+
// popover holds a two-month Calendar (~85 role=button nodes), which makes
|
|
23
|
+
// every role+name query compute ~85 accessible names through jsdom's
|
|
24
24
|
// slow getComputedStyle (~450ms per query). See fastRoleQueries.ts.
|
|
25
25
|
import {getButton, queryButton} from '../__tests__/fastRoleQueries';
|
|
26
26
|
import * as stylex from '@stylexjs/stylex';
|
|
@@ -767,6 +767,49 @@ describe('DateRangeInput', () => {
|
|
|
767
767
|
});
|
|
768
768
|
});
|
|
769
769
|
|
|
770
|
+
describe('DateRangeInput calendar while the popover is closed', () => {
|
|
771
|
+
const calendarGrid = () => document.querySelector('[role="grid"]');
|
|
772
|
+
|
|
773
|
+
it('renders no calendar until the popover opens, and none after it closes', () => {
|
|
774
|
+
render(
|
|
775
|
+
<DateRangeInput
|
|
776
|
+
label="Range"
|
|
777
|
+
value={{start: '2026-01-05', end: '2026-01-09'}}
|
|
778
|
+
onChange={() => {}}
|
|
779
|
+
/>,
|
|
780
|
+
);
|
|
781
|
+
const toggle = getButton('Open calendar');
|
|
782
|
+
expect(calendarGrid()).toBeNull();
|
|
783
|
+
|
|
784
|
+
fireEvent.click(toggle);
|
|
785
|
+
expect(calendarGrid()).not.toBeNull();
|
|
786
|
+
|
|
787
|
+
fireEvent.click(toggle);
|
|
788
|
+
expect(calendarGrid()).toBeNull();
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it('moves focus into the popover on each open from the toggle', async () => {
|
|
792
|
+
render(
|
|
793
|
+
<DateRangeInput
|
|
794
|
+
label="Range"
|
|
795
|
+
value={{start: '2026-01-05', end: '2026-01-09'}}
|
|
796
|
+
onChange={() => {}}
|
|
797
|
+
/>,
|
|
798
|
+
);
|
|
799
|
+
const toggle = getButton('Open calendar');
|
|
800
|
+
|
|
801
|
+
for (let i = 0; i < 2; i++) {
|
|
802
|
+
fireEvent.click(toggle);
|
|
803
|
+
// The first calendar control takes focus, not the dialog surface.
|
|
804
|
+
await waitFor(() => {
|
|
805
|
+
expect(getButton('Previous month')).toHaveFocus();
|
|
806
|
+
});
|
|
807
|
+
fireEvent.click(toggle);
|
|
808
|
+
expect(calendarGrid()).toBeNull();
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
|
|
770
813
|
describe('DateRangeInput statusVariant forwarding', () => {
|
|
771
814
|
it('defaults to attached (status renders with data-variant="attached")', () => {
|
|
772
815
|
const {container} = render(
|
|
@@ -851,18 +851,22 @@ export function DateRangeInput({
|
|
|
851
851
|
})}
|
|
852
852
|
</div>
|
|
853
853
|
)}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
854
|
+
{/* The layer renders its children open or closed, so mount the
|
|
855
|
+
calendar only while it is open. */}
|
|
856
|
+
{popover.isOpen && (
|
|
857
|
+
<Calendar
|
|
858
|
+
mode="range"
|
|
859
|
+
value={optimisticValue}
|
|
860
|
+
onChange={handleRangeSelect}
|
|
861
|
+
min={min}
|
|
862
|
+
max={max}
|
|
863
|
+
dateConstraints={dateConstraints}
|
|
864
|
+
maxRangeSpan={maxRangeSpan}
|
|
865
|
+
minRangeSpan={minRangeSpan}
|
|
866
|
+
numberOfMonths={numberOfMonths}
|
|
867
|
+
weekStartsOn={weekStartsOn}
|
|
868
|
+
/>
|
|
869
|
+
)}
|
|
866
870
|
</div>,
|
|
867
871
|
{placement: 'below', alignment: 'start'},
|
|
868
872
|
)}
|
|
@@ -3,11 +3,11 @@ schema_version: 3
|
|
|
3
3
|
template_version: 7
|
|
4
4
|
kind: component
|
|
5
5
|
id: component:DateTimeInput
|
|
6
|
-
authority:
|
|
6
|
+
authority: current
|
|
7
7
|
archive_reason: null
|
|
8
8
|
superseded_by: null
|
|
9
|
-
approved_by:
|
|
10
|
-
approved_at:
|
|
9
|
+
approved_by: cixzhang
|
|
10
|
+
approved_at: 2026-10-30
|
|
11
11
|
owners: [cixzhang]
|
|
12
12
|
review_triggers: [public-api, behavior, layout, theming, accessibility]
|
|
13
13
|
verified_by:
|
|
@@ -16,30 +16,37 @@ verified_by:
|
|
|
16
16
|
packages/core/src/DateTimeInput/DateTimeInputTouch.test.tsx,
|
|
17
17
|
packages/core/src/DateTimeInput/NativePickerSegments.test.tsx,
|
|
18
18
|
packages/core/src/DateTimeInput/Presentation.test.tsx,
|
|
19
|
+
packages/core/src/DateTimeInput/DateTimeInput.fr10.test.tsx,
|
|
20
|
+
packages/core/src/__tests__/BusyOwnerAnnouncement.test.tsx,
|
|
19
21
|
packages/core/src/theme/themingTargets.test.ts,
|
|
20
22
|
scripts/check-knowledge.mjs,
|
|
21
23
|
]
|
|
22
24
|
modules: []
|
|
23
25
|
families: [family:input-fields, family:overlay-dismissal]
|
|
24
26
|
design_specs: []
|
|
25
|
-
architecture:
|
|
27
|
+
architecture:
|
|
28
|
+
[
|
|
29
|
+
architecture:component-theming-surface,
|
|
30
|
+
architecture:internationalization,
|
|
31
|
+
architecture:public-component-api,
|
|
32
|
+
]
|
|
26
33
|
contributing: []
|
|
27
|
-
system_specs: [spec:AST-043]
|
|
34
|
+
system_specs: [spec:AST-043, spec:AST-085, spec:AST-086, spec:AST-102]
|
|
28
35
|
---
|
|
29
36
|
|
|
30
37
|
# DateTimeInput component contract
|
|
31
38
|
|
|
32
39
|
## Contract at a glance
|
|
33
40
|
|
|
34
|
-
| Area | Contract
|
|
35
|
-
| ----------------------- |
|
|
36
|
-
| Public contract | One controlled ISO date-time value presented through coordinated date and time segments; `presentation` selects Astryx, native, or adaptive picker surfaces.
|
|
37
|
-
| Behavior | Date and time changes emit one combined value; clear emits `undefined`; controlled empty state remains empty in every rendered picker.
|
|
38
|
-
| End-user impact | People selecting a date and time see one value reflected consistently in the closed field and the active picker, including after clearing.
|
|
39
|
-
| Builder impact | None.
|
|
40
|
-
| Compatibility/readiness |
|
|
41
|
-
| Review checks | Reject competing picker selection, date/time output that is not one valid combined value, presentation precedence drift, lost per-segment fallback, or broken segment accessibility.
|
|
42
|
-
| Governing rules | `spec:AST-043`, `family:input-fields`, `family:overlay-dismissal`, and objective platform accessibility standards.
|
|
41
|
+
| Area | Contract |
|
|
42
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| Public contract | One controlled ISO date-time value presented through coordinated date and time segments; `presentation` selects Astryx, native, or adaptive picker surfaces. |
|
|
44
|
+
| Behavior | Date and time changes emit one combined value; clear emits `undefined`; controlled empty state remains empty in every rendered picker. |
|
|
45
|
+
| End-user impact | People selecting a date and time see one value reflected consistently in the closed field and the active picker, including after clearing. |
|
|
46
|
+
| Builder impact | None. |
|
|
47
|
+
| Compatibility/readiness | Records shipped behavior; no API, default, or DOM change. The presentation matrix and deprecated `nativePicker` mapping follow `spec:AST-043`. Fixed segment height, locale time text and parsing, busy paint, and caller handlers while disabled are listed under Current-state impact. |
|
|
48
|
+
| Review checks | Reject competing picker selection, date/time output that is not one valid combined value, presentation precedence drift, lost per-segment fallback, or broken segment accessibility. |
|
|
49
|
+
| Governing rules | `spec:AST-043`, `family:input-fields`, `family:overlay-dismissal`, `spec:AST-085`, `spec:AST-086` FR8, and objective platform accessibility standards. |
|
|
43
50
|
|
|
44
51
|
This table is a review projection; the body below is authoritative.
|
|
45
52
|
|
|
@@ -71,13 +78,16 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
71
78
|
- Applying component-local date-time bounds, date constraints, time precision,
|
|
72
79
|
typed-time stepping, and optional preset-time behavior.
|
|
73
80
|
- Emitting `onChange` before an optional optimistic `changeAction`.
|
|
81
|
+
- The disabled-reason Tooltip composition, which `family:input-fields` assigns
|
|
82
|
+
to the member.
|
|
74
83
|
|
|
75
84
|
**Does not own / non-goals**
|
|
76
85
|
|
|
77
|
-
- Field label, description, status,
|
|
78
|
-
owned by `
|
|
86
|
+
- Field label, description, status, and clear-button presentation —
|
|
87
|
+
owned by Field under `family:input-fields` (shared owner section) and
|
|
88
|
+
`component:FieldStatus`.
|
|
79
89
|
- Calendar-grid rendering and date-cell interaction — owned by
|
|
80
|
-
|
|
90
|
+
Calendar.
|
|
81
91
|
- Popover and sheet hosting or dismissal — owned by the composed layer components
|
|
82
92
|
and `family:overlay-dismissal`.
|
|
83
93
|
- Cross-component picker-presentation vocabulary and deprecated-prop mapping —
|
|
@@ -95,18 +105,17 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
95
105
|
|
|
96
106
|
## Behavioral and layout contract
|
|
97
107
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
| FR7 | A preset-time listbox MUST remain optional, preserve typed entry, expose one selected option independently from keyboard highlight, and limit offered values to the selected date's effective minimum and maximum. | Public docs, current source, focused interaction tests | Verified observation; owner approval pending |
|
|
108
|
+
| ID | Invariant | Basis |
|
|
109
|
+
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
|
|
110
|
+
| FR1 | DateTimeInput MUST reflect one controlled date-time value across its visible segments and active picker, and MUST NOT reveal a competing selected date after the parent accepts a clear. | Public docs, controlled component seam, current source, focused tests |
|
|
111
|
+
| FR2 | Every committed date or time change MUST emit one valid combined ISO date-time value; clearing MUST emit `undefined`; changing time while no date is committed MUST emit nothing. | Public types, docs, current source, focused tests |
|
|
112
|
+
| FR3 | `presentation`, deprecated `nativePicker`, precedence, pointer adaptation, forced surfaces, and per-segment native fallbacks MUST follow `spec:AST-043` FR1–FR4. | `spec:AST-043` |
|
|
113
|
+
| FR4 | When `changeAction` is present, every value-change path MUST call `onChange` first, show the proposed controlled value optimistically, and contribute to one busy state until the controlled value accepts or replaces it. | `family:input-fields/FR6` |
|
|
114
|
+
| FR5 | The two pointer segments share one row while each retains its intrinsic basis; when the container is narrower, flex wrapping moves them into separate full-width rows. Each segment is the shared element height for its size, as a minimum that grows with a set text size under `spec:AST-102` FR4. Implementation gap: the height is fixed at that size. | Public docs, current source, focused layout tests; `spec:AST-102` FR4 |
|
|
115
|
+
| FR6 | Date-time bounds and date constraints MUST prevent invalid commits. Native controls MAY receive platform hints, but JavaScript validation remains the final commit boundary; calendar-only options MAY be unavailable on native surfaces. | Public docs, current source, focused presentation tests |
|
|
116
|
+
| FR7 | A preset-time listbox MUST remain optional, preserve typed entry, expose one selected option independently from keyboard highlight, and limit offered values to the selected date's effective minimum and maximum. Time text and typed times MUST follow the active locale's digits, separator, and day-period words (`architecture:internationalization` INV1a, INV8). Implementation gap: time text, preset options, and the stepped-time announcement use ASCII digits, a `:` separator, and a fixed English "AM"/"PM", and the typed time parser takes no locale. | Public docs, current source, focused interaction tests; `architecture:internationalization` INV1a, INV8 |
|
|
117
|
+
| FR8 | Base props and `ref` MUST reach the date segment's input; a caller `aria-describedby` joins the component's own descriptions, and a caller DOM handler runs after the field's own handler. `container*` reaches the Field box and `control*` the segment row; root `xstyle`, `className`, and `style` are not accepted. | `family:input-fields` FR10; `DateTimeInput.fr10.test.tsx` |
|
|
118
|
+
| FR9 | A disabled field MUST keep both segments focusable with `aria-disabled` and no native `disabled`, block every value-changing path, keep the calendar button's tab stop, and paint the keyboard focus ring. Explicit `isLoading` makes every surface inert the same way; a pending `changeAction` does so only on the popover surface, and the bottom sheet and any field with a native date segment keep accepting edits. The busy start is announced once. Implementation gap: a caller `onKeyDown` and other DOM handlers on the date segment still run while disabled (`spec:AST-085` FR2). Implementation gap: a busy field that is not disabled takes the disabled paint (half opacity), on every surface while `isLoading` and on the popover while a `changeAction` is pending, against `spec:AST-085` FR6. | `spec:AST-085` FR1, FR2, FR6; `spec:AST-086` FR8; current source and tests |
|
|
110
119
|
|
|
111
120
|
### Allowed variation
|
|
112
121
|
|
|
@@ -119,13 +128,13 @@ intentional decision.
|
|
|
119
128
|
|
|
120
129
|
### Representative states
|
|
121
130
|
|
|
122
|
-
| State | Required invariant
|
|
123
|
-
| --------------------------- |
|
|
124
|
-
| Empty | Both segments and every active picker expose no committed selection.
|
|
125
|
-
| Date-time value | Date segment, time segment, and active picker reflect the same committed value.
|
|
126
|
-
| Cleared after picker change | Closed field is empty and reopening the picker reveals no selected date or retained committed time.
|
|
127
|
-
| Busy | Proposed value remains visible; mutation paths are unavailable;
|
|
128
|
-
| Focusable disabled | Both segments
|
|
131
|
+
| State | Required invariant | Allowed variation |
|
|
132
|
+
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
|
|
133
|
+
| Empty | Both segments and every active picker expose no committed selection. | Placeholder and selected surface. |
|
|
134
|
+
| Date-time value | Date segment, time segment, and active picker reflect the same committed value. | Locale, hour format, and time precision. |
|
|
135
|
+
| Cleared after picker change | Closed field is empty and reopening the picker reveals no selected date or retained committed time. | Picker focus date may remain navigational. |
|
|
136
|
+
| Busy | Proposed value remains visible; both editable segments expose busy. With explicit `isLoading`, mutation paths are unavailable on every surface; with a pending `changeAction`, only on the popover surface. | Explicit loading or pending action source. |
|
|
137
|
+
| Focusable disabled | Both segments stay focusable with `aria-disabled` while every value-changing path stays blocked. | Disabled-reason text. |
|
|
129
138
|
|
|
130
139
|
### Transformation and precedence order
|
|
131
140
|
|
|
@@ -220,24 +229,38 @@ leading clock glyph uses `date-time-input-clock-icon`.
|
|
|
220
229
|
|
|
221
230
|
## Verification map
|
|
222
231
|
|
|
223
|
-
| Contract | Verification
|
|
224
|
-
| ------------------- |
|
|
225
|
-
| FR1–FR2 | `DateTimeInput.test.tsx`
|
|
226
|
-
| FR3, FR6 | `Presentation.test.tsx`, `NativePickerSegments.test.tsx`, `DateTimeInputTouch.test.tsx`
|
|
227
|
-
| FR4, AR4
|
|
228
|
-
| FR5 | `DateTimeInput.test.tsx`
|
|
229
|
-
| FR7, AR1–AR3 | `DateTimeInput.test.tsx`, `DateTimeInputTouch.test.tsx`
|
|
230
|
-
|
|
|
232
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
233
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
|
|
234
|
+
| FR1–FR2 | `DateTimeInput.test.tsx` | empty, calendar selection, typed date/time, clear | A stale Calendar selection, partial output, or invalid mutation makes a public-state test fail. | `audit:DateTimeInput/behavior` |
|
|
235
|
+
| FR3, FR6 | `Presentation.test.tsx`, `NativePickerSegments.test.tsx`, `DateTimeInputTouch.test.tsx` | forced, adaptive, legacy, native fallback, constrained commit | A substituted surface, precedence drift, or invalid commit makes a presentation test fail. | `audit:DateTimeInput/surfaces` |
|
|
236
|
+
| FR4, FR9, AR4 | `DateTimeInput.test.tsx`, `DateTimeInputTouch.test.tsx`; `BusyOwnerAnnouncement.test.tsx` DateTimeInput case | pending, loading, disabled reason | Mutation during busy/disabled state or stale optimistic output makes a state test fail. Evidence gap: no test asserts that caller handlers still run while disabled. | `audit:DateTimeInput/state` |
|
|
237
|
+
| FR5 | `DateTimeInput.test.tsx` | intrinsic row, narrow-container wrap classes | Removing the intrinsic basis or wrap styles makes the class-level layout assertions fail; rendered geometry remains review evidence. Evidence gap: no test asserts segment heights or text growth. | `audit:DateTimeInput/layout` |
|
|
238
|
+
| FR7, AR1–AR3 | `DateTimeInput.test.tsx`, `DateTimeInputTouch.test.tsx` | popovers, time options, keyboard/focus | Wrong role, name, state, keyboard behavior, or focus behavior makes a focused interaction test fail. Evidence gap: no test asserts locale-specific time text or parsing. | `audit:DateTimeInput/interaction` |
|
|
239
|
+
| FR8 | `DateTimeInput.fr10.test.tsx` | each surface; caller id, describedby, onClick, onKeyDown | A base prop off the date input, a dropped caller description, or styling on the wrong part fails. | `audit:DateTimeInput/api` |
|
|
240
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs`, `themingTargets.test.ts` | root, segments, icons, listbox, options | Missing, undocumented, or wrongly placed targets fail knowledge or target checks. | `audit:DateTimeInput/theming` |
|
|
241
|
+
|
|
242
|
+
## Current-state impact
|
|
243
|
+
|
|
244
|
+
- **Fixed height (FR5).** Segments hold the fixed shared element height instead
|
|
245
|
+
of growing with a set text size, against `spec:AST-102` FR4.
|
|
246
|
+
- **Time text and parsing (FR7).** Time text, preset options, and the stepped
|
|
247
|
+
announcement use ASCII digits, a `:` separator, and English "AM"/"PM", and
|
|
248
|
+
typed times parse without the locale, against
|
|
249
|
+
`architecture:internationalization` INV1a and INV8.
|
|
250
|
+
- **Busy paint (FR9).** A busy field that is not disabled takes the disabled
|
|
251
|
+
half-opacity paint, against `spec:AST-085` FR6.
|
|
252
|
+
- **Caller handlers while disabled (FR9).** Caller DOM handlers on the date
|
|
253
|
+
segment, such as `onKeyDown`, still run while disabled, against
|
|
254
|
+
`spec:AST-085` FR2.
|
|
231
255
|
|
|
232
256
|
## Decision log
|
|
233
257
|
|
|
234
|
-
No component-local decision
|
|
235
|
-
|
|
258
|
+
No component-local decision. Cross-component picker decisions live in
|
|
259
|
+
`spec:AST-043`.
|
|
236
260
|
|
|
237
261
|
## Open questions
|
|
238
262
|
|
|
239
|
-
None.
|
|
240
|
-
rows without changing inherited current authority.
|
|
263
|
+
None.
|
|
241
264
|
|
|
242
265
|
## Content boundary
|
|
243
266
|
|