@astryxdesign/core 0.7.6-canary.ec128c4 → 0.7.6-canary.f73623d
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/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +183 -5
- package/dist/AppShell/observeMainOverflow.d.ts +9 -0
- package/dist/AppShell/observeMainOverflow.d.ts.map +1 -0
- package/dist/AppShell/observeMainOverflow.js +125 -0
- package/dist/Carousel/Carousel.d.ts +3 -1
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +12 -3
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +41 -4
- package/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +11 -7
- package/dist/DateInput/usePointerDragScroll.d.ts +10 -1
- package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -1
- package/dist/DateInput/usePointerDragScroll.js +25 -9
- package/dist/DateInput/useScrollSettle.d.ts +3 -1
- package/dist/DateInput/useScrollSettle.d.ts.map +1 -1
- package/dist/DateInput/useScrollSettle.js +37 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +28 -19
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +19 -6
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +27 -6
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +8 -3
- package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.js +1 -1
- package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenuItem.js +5 -29
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +23 -2
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +27 -0
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +32 -12
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +10 -3
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +37 -30
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +11 -3
- package/dist/SideNav/SideNavItem.d.ts +2 -2
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +18 -7
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +1 -1
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +1 -1
- package/dist/Stepper/Step.d.ts.map +1 -1
- package/dist/Stepper/Step.js +26 -6
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +10 -3
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +36 -10
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +46 -58
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +19 -3
- package/dist/ToggleButton/ToggleButtonGroup.d.ts +7 -1
- package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButtonGroup.js +16 -2
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +37 -5
- package/dist/Token/TokenLink.d.ts.map +1 -1
- package/dist/Token/TokenLink.js +14 -2
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +15 -4
- package/dist/Tokenizer/TokenizerClearAllMessageContext.d.ts +2 -0
- package/dist/Tokenizer/TokenizerClearAllMessageContext.d.ts.map +1 -0
- package/dist/Tokenizer/TokenizerClearAllMessageContext.js +17 -0
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +27 -4
- package/dist/Typeahead/BaseTypeahead.d.ts +4 -4
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +43 -7
- package/dist/astryx.css +10 -3
- package/dist/gestures/GestureArena.d.ts +26 -1
- package/dist/gestures/GestureArena.d.ts.map +1 -1
- package/dist/gestures/GestureArena.js +71 -9
- package/dist/gestures/GestureHost.d.ts +2 -3
- package/dist/gestures/GestureHost.d.ts.map +1 -1
- package/dist/gestures/GestureHost.js +7 -7
- package/dist/gestures/GestureRecognizer.d.ts +3 -1
- package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
- package/dist/gestures/GestureRecognizer.js +3 -1
- package/dist/gestures/PressTracker.d.ts +112 -0
- package/dist/gestures/PressTracker.d.ts.map +1 -0
- package/dist/gestures/PressTracker.js +267 -0
- package/dist/gestures/index.d.ts +37 -0
- package/dist/gestures/index.d.ts.map +1 -0
- package/dist/gestures/index.js +25 -0
- package/dist/gestures/useGestureRecognizer.d.ts +56 -0
- package/dist/gestures/useGestureRecognizer.d.ts.map +1 -0
- package/dist/gestures/useGestureRecognizer.js +226 -0
- package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -1
- package/dist/hooks/scrollKeyboardDelegation.js +7 -3
- package/dist/hooks/useScrollOverflow.d.ts +2 -1
- package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
- package/dist/hooks/useScrollOverflow.js +56 -10
- package/dist/i18n/generated-locales/af-ZA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/af-ZA.generated.js +0 -5
- package/dist/i18n/generated-locales/ar-SA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ar-SA.generated.js +0 -5
- package/dist/i18n/generated-locales/ca-ES.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ca-ES.generated.js +0 -5
- package/dist/i18n/generated-locales/cs-CZ.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/cs-CZ.generated.js +0 -5
- package/dist/i18n/generated-locales/da-DK.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/da-DK.generated.js +0 -5
- package/dist/i18n/generated-locales/de-DE.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/de-DE.generated.js +0 -5
- package/dist/i18n/generated-locales/el-GR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/el-GR.generated.js +0 -5
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +3 -5
- package/dist/i18n/generated-locales/es-ES.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/es-ES.generated.js +0 -5
- package/dist/i18n/generated-locales/fi-FI.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/fi-FI.generated.js +0 -5
- package/dist/i18n/generated-locales/fr-FR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/fr-FR.generated.js +0 -5
- package/dist/i18n/generated-locales/he-IL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/he-IL.generated.js +0 -5
- package/dist/i18n/generated-locales/hu-HU.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/hu-HU.generated.js +0 -5
- package/dist/i18n/generated-locales/it-IT.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/it-IT.generated.js +0 -5
- package/dist/i18n/generated-locales/ja-JP.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ja-JP.generated.js +0 -5
- package/dist/i18n/generated-locales/ko-KR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ko-KR.generated.js +0 -5
- package/dist/i18n/generated-locales/nl-NL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/nl-NL.generated.js +0 -5
- package/dist/i18n/generated-locales/no-NO.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/no-NO.generated.js +0 -5
- package/dist/i18n/generated-locales/pl-PL.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pl-PL.generated.js +0 -5
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +3 -5
- package/dist/i18n/generated-locales/pt-BR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pt-BR.generated.js +0 -5
- package/dist/i18n/generated-locales/pt-PT.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pt-PT.generated.js +0 -5
- package/dist/i18n/generated-locales/ro-RO.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ro-RO.generated.js +0 -5
- package/dist/i18n/generated-locales/ru-RU.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/ru-RU.generated.js +0 -5
- package/dist/i18n/generated-locales/sr-SP.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/sr-SP.generated.js +0 -5
- package/dist/i18n/generated-locales/sv-SE.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/sv-SE.generated.js +0 -5
- package/dist/i18n/generated-locales/tr-TR.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/tr-TR.generated.js +0 -5
- package/dist/i18n/generated-locales/uk-UA.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/uk-UA.generated.js +0 -5
- package/dist/i18n/generated-locales/vi-VN.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/vi-VN.generated.js +0 -5
- package/dist/i18n/generated-locales/zh-CN.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/zh-CN.generated.js +0 -5
- package/dist/i18n/generated-locales/zh-TW.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/zh-TW.generated.js +0 -5
- package/dist/motion/animator.d.ts +76 -7
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +589 -99
- package/dist/motion/coast.d.ts +4 -1
- package/dist/motion/coast.d.ts.map +1 -1
- package/dist/motion/coast.js +4 -2
- package/dist/motion/reducedMotion.d.ts.map +1 -1
- package/dist/motion/reducedMotion.js +6 -2
- package/dist/motion/reducedMotionPlan.d.ts +7 -0
- package/dist/motion/reducedMotionPlan.d.ts.map +1 -1
- package/dist/motion/reducedMotionPlan.js +14 -1
- package/dist/motion/resolveMotion.d.ts +42 -0
- package/dist/motion/resolveMotion.d.ts.map +1 -0
- package/dist/motion/resolveMotion.js +43 -0
- package/dist/motion/roleValue.d.ts +2 -1
- package/dist/motion/roleValue.d.ts.map +1 -1
- package/dist/motion/roleValue.js +2 -1
- package/dist/motion/uikit/timing.d.ts +7 -1
- package/dist/motion/uikit/timing.d.ts.map +1 -1
- package/dist/motion/uikit/timing.js +19 -1
- package/dist/theme/defineTheme.d.ts +19 -5
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +63 -3
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/motionKnobs.d.ts +56 -0
- package/dist/theme/motionKnobs.d.ts.map +1 -0
- package/dist/theme/motionKnobs.js +48 -0
- package/dist/theme/resolveThemeValues.d.ts +11 -4
- package/dist/theme/resolveThemeValues.d.ts.map +1 -1
- package/dist/theme/resolveThemeValues.js +40 -4
- package/dist/theme/themeAdaptations.d.ts +7 -3
- package/dist/theme/themeAdaptations.d.ts.map +1 -1
- package/dist/theme/themeAdaptations.js +36 -7
- package/dist/theme/themeMotion.d.ts +96 -0
- package/dist/theme/themeMotion.d.ts.map +1 -0
- package/dist/theme/themeMotion.js +177 -0
- package/dist/theme/tokenReferences.d.ts +31 -0
- package/dist/theme/tokenReferences.d.ts.map +1 -0
- package/dist/theme/tokenReferences.js +132 -0
- package/dist/theme/tokens.d.ts +1 -0
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +9 -129
- package/dist/theme/tokens.stylex.d.ts +12 -0
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +12 -0
- package/dist/utils/focusOutline.stylex.d.ts +6 -0
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.js +7 -0
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +17 -8
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +20 -1
- package/dist/utils/sharedMutationObserver.d.ts +19 -11
- package/dist/utils/sharedMutationObserver.d.ts.map +1 -1
- package/dist/utils/sharedMutationObserver.js +66 -26
- package/dist/utils/touchPresentation.d.ts +2 -2
- package/dist/utils/touchPresentation.d.ts.map +1 -1
- package/dist/utils/touchPresentation.js +8 -5
- package/dist/utils/touchQueries.d.ts +13 -0
- package/dist/utils/touchQueries.d.ts.map +1 -0
- package/dist/utils/touchQueries.js +15 -0
- package/dist/utils/useFocusAcrossSwap.d.ts +19 -0
- package/dist/utils/useFocusAcrossSwap.d.ts.map +1 -0
- package/dist/utils/useFocusAcrossSwap.js +41 -0
- package/locales/af-ZA.json +0 -21
- package/locales/ar-SA.json +0 -21
- package/locales/ca-ES.json +0 -21
- package/locales/cs-CZ.json +0 -21
- package/locales/da-DK.json +0 -21
- package/locales/de-DE.json +0 -21
- package/locales/el-GR.json +0 -21
- package/locales/en.json +12 -20
- package/locales/es-ES.json +0 -21
- package/locales/fi-FI.json +0 -21
- package/locales/fr-FR.json +0 -21
- package/locales/he-IL.json +0 -21
- package/locales/hu-HU.json +0 -21
- package/locales/it-IT.json +0 -21
- package/locales/ja-JP.json +0 -21
- package/locales/ko-KR.json +0 -21
- package/locales/nl-NL.json +0 -21
- package/locales/no-NO.json +0 -21
- package/locales/pl-PL.json +0 -21
- package/locales/pseudo.json +8 -14
- package/locales/pt-BR.json +0 -21
- package/locales/pt-PT.json +0 -21
- package/locales/ro-RO.json +0 -21
- package/locales/ru-RU.json +0 -21
- package/locales/sr-SP.json +0 -21
- package/locales/sv-SE.json +0 -21
- package/locales/tr-TR.json +0 -21
- package/locales/uk-UA.json +0 -21
- package/locales/vi-VN.json +0 -21
- package/locales/zh-CN.json +0 -21
- package/locales/zh-TW.json +0 -21
- package/package.json +4 -4
- package/src/AppShell/AppShell.doc.mjs +1 -1
- package/src/AppShell/AppShell.spec.md +9 -12
- package/src/AppShell/AppShell.test.tsx +164 -2
- package/src/AppShell/AppShell.tsx +225 -5
- package/src/AppShell/__tests__/AppShellMainKeyboard.a11y.browser.spec.ts +534 -0
- package/src/AppShell/observeMainOverflow.test.ts +173 -0
- package/src/AppShell/observeMainOverflow.ts +143 -0
- package/src/Carousel/Carousel.doc.mjs +1 -1
- package/src/Carousel/Carousel.spec.md +9 -9
- package/src/Carousel/Carousel.test.tsx +124 -3
- package/src/Carousel/Carousel.tsx +10 -2
- package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +1 -16
- package/src/Chat/ChatComposer.spec.md +75 -59
- package/src/Chat/ChatComposerTokenElement.spec.md +45 -33
- package/src/Chat/ChatDictationButton.spec.md +126 -81
- package/src/Chat/ChatLayout.spec.md +41 -36
- package/src/Chat/ChatLayoutScrollButton.spec.md +87 -103
- package/src/Chat/ChatSendButton.spec.md +78 -67
- package/src/CodeBlock/__tests__/CodeBlockCopyClearance.a11y.chromium.spec.ts +38 -3
- package/src/ComplexSelector/ComplexSelector.test.tsx +111 -0
- package/src/ComplexSelector/ComplexSelector.tsx +41 -4
- package/src/ContextMenu/ContextMenu.spec.md +84 -56
- package/src/ContextMenu/ContextMenuItem.spec.md +132 -0
- package/src/DateInput/DateInputTouch.test.tsx +44 -0
- package/src/DateInput/Wheel.tsx +9 -6
- package/src/DateInput/usePointerDragScroll.ts +34 -10
- package/src/DateInput/useScrollSettle.ts +38 -7
- package/src/DropdownMenu/DropdownMenu.spec.md +198 -218
- package/src/DropdownMenu/DropdownMenu.test.tsx +117 -0
- package/src/DropdownMenu/DropdownMenu.tsx +33 -18
- package/src/DropdownMenu/DropdownMenuCheckboxItem.spec.md +192 -0
- package/src/DropdownMenu/DropdownMenuDivider.spec.md +25 -31
- package/src/DropdownMenu/DropdownMenuGroup.spec.md +145 -0
- package/src/DropdownMenu/DropdownMenuItem.spec.md +48 -42
- package/src/DropdownMenu/DropdownMenuRadioGroup.spec.md +34 -37
- package/src/DropdownMenu/DropdownMenuRadioItem.spec.md +54 -53
- package/src/DropdownMenu/DropdownMenuSubMenu.spec.md +47 -41
- package/src/EmptyState/EmptyState.spec.md +35 -29
- package/src/Item/Item.test.tsx +60 -0
- package/src/Item/Item.tsx +14 -7
- package/src/Layer/useLayer.test.tsx +78 -3
- package/src/Layer/useLayer.tsx +27 -6
- package/src/Link/Link.spec.md +3 -2
- package/src/Link/Link.tsx +11 -0
- package/src/Link/__tests__/Link.truncation.a11y.chromium.spec.ts +58 -0
- package/src/Markdown/plugins/headingLinks.a11y.chromium.spec.ts +1 -1
- package/src/NavMenu/NavHeadingMenuItem.tsx +5 -39
- package/src/NumberInput/NumberInput.test.tsx +68 -7
- package/src/NumberInput/NumberInput.tsx +31 -1
- package/src/Outline/Outline.spec.md +165 -69
- package/src/Outline/Outline.test.tsx +67 -2
- package/src/Outline/Outline.tsx +23 -0
- package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +3 -4
- package/src/OverflowList/OverflowList.spec.md +102 -55
- package/src/Pagination/Pagination.spec.md +158 -84
- package/src/Popover/Popover.test.tsx +84 -0
- package/src/Popover/Popover.tsx +34 -14
- package/src/Popover/__tests__/PopoverSecondDocument.a11y.chromium.spec.ts +70 -8
- package/src/Popover/usePopover.tsx +10 -3
- package/src/PowerSearch/PowerSearch.test.tsx +24 -0
- package/src/PowerSearch/PowerSearch.tsx +42 -31
- package/src/RadioList/RadioList.spec.md +1 -1
- package/src/RadioList/RadioList.test.tsx +84 -0
- package/src/RadioList/RadioListItem.tsx +11 -2
- package/src/Resizable/Resizable.spec.md +144 -124
- package/src/SideNav/SideNav.test.tsx +124 -4
- package/src/SideNav/SideNavItem.spec.md +14 -24
- package/src/SideNav/SideNavItem.tsx +27 -10
- package/src/Skeleton/Skeleton.tsx +3 -1
- package/src/Spinner/Spinner.tsx +3 -1
- package/src/Stepper/Step.spec.md +36 -42
- package/src/Stepper/Step.tsx +33 -6
- package/src/Stepper/Stepper.doc.mjs +1 -1
- package/src/Stepper/Stepper.test.tsx +69 -7
- package/src/Switch/Switch.spec.md +1 -1
- package/src/Switch/Switch.test.tsx +66 -1
- package/src/Switch/Switch.tsx +11 -3
- package/src/Table/Table.spec.md +1 -1
- package/src/Table/Table.test.tsx +32 -0
- package/src/Table/Table.tsx +43 -11
- package/src/Table/__tests__/Table.a11y.states.ts +1 -5
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +73 -144
- package/src/Table/plugins/sortable/useTableSortable.tsx +44 -73
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +9 -21
- package/src/TimeInput/TimeInput.spec.md +13 -13
- package/src/Toast/Toast.spec.md +2 -5
- package/src/Toast/ToastViewport.test.tsx +79 -0
- package/src/Toast/ToastViewport.tsx +21 -3
- package/src/ToggleButton/ToggleButton.test.tsx +27 -0
- package/src/ToggleButton/ToggleButtonGroup.tsx +20 -1
- package/src/Token/Token.doc.mjs +2 -2
- package/src/Token/Token.spec.md +41 -51
- package/src/Token/Token.test.tsx +67 -0
- package/src/Token/Token.tsx +60 -5
- package/src/Token/TokenLink.tsx +21 -7
- package/src/Tokenizer/Tokenizer.spec.md +13 -1
- package/src/Tokenizer/Tokenizer.test.tsx +27 -0
- package/src/Tokenizer/Tokenizer.tsx +24 -10
- package/src/Tokenizer/TokenizerBottomSheet.test.tsx +8 -0
- package/src/Tokenizer/TokenizerClearAllMessageContext.ts +21 -0
- package/src/TreeList/TreeList.test.tsx +26 -0
- package/src/TreeList/TreeListItem.tsx +26 -4
- package/src/Typeahead/BaseTypeahead.doc.mjs +3 -3
- package/src/Typeahead/BaseTypeahead.spec.md +45 -45
- package/src/Typeahead/BaseTypeahead.tsx +48 -9
- package/src/Typeahead/Typeahead.test.tsx +120 -0
- package/src/__tests__/internalModulesNotExperimental.test.ts +88 -0
- package/src/__tests__/pressState.ts +8 -4
- package/src/gestures/GestureArena.ts +76 -11
- package/src/gestures/GestureHost.ts +6 -7
- package/src/gestures/GestureRecognizer.ts +3 -1
- package/src/gestures/PressTracker.ts +386 -0
- package/src/gestures/README.md +420 -0
- package/src/gestures/__tests__/GestureHost.test.ts +50 -1
- package/src/gestures/__tests__/PressTracker.test.ts +275 -0
- package/src/gestures/__tests__/arena.test.ts +35 -0
- package/src/gestures/__tests__/harness.ts +31 -2
- package/src/gestures/__tests__/pressTrackerFactory.test.ts +59 -0
- package/src/gestures/__tests__/useGestureRecognizer.test.tsx +570 -0
- package/src/gestures/index.ts +79 -0
- package/src/gestures/useGestureRecognizer.ts +305 -0
- package/src/hooks/scrollKeyboardDelegation.test.ts +33 -0
- package/src/hooks/scrollKeyboardDelegation.ts +9 -3
- package/src/hooks/useScrollOverflow.doc.mjs +1 -1
- package/src/hooks/useScrollOverflow.test.tsx +102 -1
- package/src/hooks/useScrollOverflow.ts +59 -10
- package/src/i18n/generated-locales/af-ZA.generated.ts +0 -7
- package/src/i18n/generated-locales/ar-SA.generated.ts +0 -6
- package/src/i18n/generated-locales/ca-ES.generated.ts +0 -7
- package/src/i18n/generated-locales/cs-CZ.generated.ts +0 -6
- package/src/i18n/generated-locales/da-DK.generated.ts +0 -6
- package/src/i18n/generated-locales/de-DE.generated.ts +0 -6
- package/src/i18n/generated-locales/el-GR.generated.ts +0 -7
- package/src/i18n/generated-locales/en.generated.ts +3 -6
- package/src/i18n/generated-locales/es-ES.generated.ts +0 -7
- package/src/i18n/generated-locales/fi-FI.generated.ts +0 -6
- package/src/i18n/generated-locales/fr-FR.generated.ts +0 -7
- package/src/i18n/generated-locales/he-IL.generated.ts +0 -6
- package/src/i18n/generated-locales/hu-HU.generated.ts +0 -6
- package/src/i18n/generated-locales/it-IT.generated.ts +0 -7
- package/src/i18n/generated-locales/ja-JP.generated.ts +0 -6
- package/src/i18n/generated-locales/ko-KR.generated.ts +0 -6
- package/src/i18n/generated-locales/nl-NL.generated.ts +0 -6
- package/src/i18n/generated-locales/no-NO.generated.ts +0 -6
- package/src/i18n/generated-locales/pl-PL.generated.ts +0 -7
- package/src/i18n/generated-locales/pseudo.generated.ts +3 -6
- package/src/i18n/generated-locales/pt-BR.generated.ts +0 -7
- package/src/i18n/generated-locales/pt-PT.generated.ts +0 -7
- package/src/i18n/generated-locales/ro-RO.generated.ts +0 -6
- package/src/i18n/generated-locales/ru-RU.generated.ts +0 -7
- package/src/i18n/generated-locales/sr-SP.generated.ts +0 -6
- package/src/i18n/generated-locales/sv-SE.generated.ts +0 -6
- package/src/i18n/generated-locales/tr-TR.generated.ts +0 -7
- package/src/i18n/generated-locales/uk-UA.generated.ts +0 -7
- package/src/i18n/generated-locales/vi-VN.generated.ts +0 -6
- package/src/i18n/generated-locales/zh-CN.generated.ts +0 -6
- package/src/i18n/generated-locales/zh-TW.generated.ts +0 -6
- package/src/motion/MEASUREMENTS.md +2 -1
- package/src/motion/README.md +102 -32
- package/src/motion/__tests__/animator.interaction.browser.spec.ts +843 -0
- package/src/motion/__tests__/motionBundle.ts +14 -0
- package/src/motion/animator.test.ts +978 -33
- package/src/motion/animator.ts +790 -104
- package/src/motion/coast.ts +10 -3
- package/src/motion/reducedMotion.ts +12 -2
- package/src/motion/reducedMotionPlan.test.ts +26 -0
- package/src/motion/reducedMotionPlan.ts +20 -1
- package/src/motion/resolveMotion.test.ts +123 -0
- package/src/motion/resolveMotion.ts +68 -0
- package/src/motion/roleValue.ts +2 -1
- package/src/motion/roles.test.ts +21 -4
- package/src/motion/uikit/tableImports.test.ts +293 -0
- package/src/motion/uikit/timing.ts +15 -1
- package/src/motion/uikit/values.test.ts +23 -12
- package/src/theme/Theme.doc.mjs +1 -1
- package/src/theme/defineTheme.ts +116 -7
- package/src/theme/expandMotionScale.test.ts +1 -1
- package/src/theme/index.ts +5 -0
- package/src/theme/motionKnobs.ts +82 -0
- package/src/theme/resolveThemeValues.ts +68 -6
- package/src/theme/themeAdaptations.ts +52 -12
- package/src/theme/themeMotion.test.ts +620 -0
- package/src/theme/themeMotion.ts +221 -0
- package/src/theme/tokenReferences.ts +149 -0
- package/src/theme/tokens.stylex.ts +10 -0
- package/src/theme/tokens.ts +15 -158
- package/src/utils/focusOutline.stylex.ts +28 -0
- package/src/utils/interactionOverlay.stylex.ts +12 -3
- package/src/utils/pressFeedback.test.ts +138 -0
- package/src/utils/pressFeedback.ts +34 -11
- package/src/utils/pressGesture.test.ts +9 -0
- package/src/utils/sharedMutationObserver.test.ts +54 -1
- package/src/utils/sharedMutationObserver.ts +87 -29
- package/src/utils/touchPresentation.ts +8 -5
- package/src/utils/touchQueries.ts +15 -0
- package/src/utils/useFocusAcrossSwap.ts +55 -0
package/src/Token/Token.spec.md
CHANGED
|
@@ -41,7 +41,7 @@ system_specs: [spec:AST-005, spec:AST-009, spec:AST-085]
|
|
|
41
41
|
| Behavior | `href` renders a link, `onClick` (or an enclosing trigger context) renders a container whose inner button wraps the label, and `onRemove` adds a separate remove button beside, never inside, them. |
|
|
42
42
|
| End-user impact | Pointer users can activate the whole chip; keyboard and screen-reader users reach the chip's action and its remove action as separate, named controls. |
|
|
43
43
|
| Builder impact | None; the record describes shipped behavior. |
|
|
44
|
-
| Compatibility/readiness | Released behavior recorded as current; no API, default, or output changes.
|
|
44
|
+
| Compatibility/readiness | Released behavior recorded as current; no API, default, or output changes. Four implementation gaps are listed under Current-state impact. |
|
|
45
45
|
| Review checks | Reject a remove button nested in the link or the inner button, a remove target smaller than 24 by 24 CSS pixels, a native `disabled` attribute, or a token that navigates around the shared destination rule. |
|
|
46
46
|
| Governing rules | `family:navigation-destinations`; `spec:AST-005`; `spec:AST-085/FR1,FR2,FR5`; `architecture:public-component-api/INV5,INV6,INV8`; `architecture:component-theming-surface/INV6,INV14`. |
|
|
47
47
|
|
|
@@ -66,10 +66,6 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
66
66
|
|
|
67
67
|
## Current-state impact
|
|
68
68
|
|
|
69
|
-
- **Disabled link token (FR5, AR5).** A disabled token with `href` and no
|
|
70
|
-
`onClick` renders a static `span`: it loses its tab stop and exposes no
|
|
71
|
-
disabled state, where `spec:AST-085` FR1 and FR5 keep a disabled control
|
|
72
|
-
focusable and announced.
|
|
73
69
|
- **`onClick` with `href` (FR3).** When a token renders as a link, a supplied
|
|
74
70
|
`onClick` is not attached or called, where
|
|
75
71
|
`architecture:public-component-api` INV5 preserves accepted event inputs.
|
|
@@ -77,10 +73,6 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
77
73
|
static, clickable, or link-and-remove token puts `aria-label` on a `span` that
|
|
78
74
|
has no role, where WAI-ARIA 1.2 prohibits naming the generic role. The
|
|
79
75
|
visually hidden label text still reaches assistive technology.
|
|
80
|
-
- **Disabled state not reflected on the target (FR5).** No structure reflects
|
|
81
|
-
`isDisabled` on the `token` target, so a theme cannot style the disabled state
|
|
82
|
-
through it, where `architecture:component-theming-surface` INV6 keeps state on
|
|
83
|
-
the owning target.
|
|
84
76
|
- **Two focus indicators on Remove (AR4).** In clickable and link-and-remove
|
|
85
77
|
tokens, keyboard focus on the Remove button draws the token's outline and the
|
|
86
78
|
button's own, where `architecture:interaction-modality` INV3 allows one
|
|
@@ -116,29 +108,29 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
116
108
|
|
|
117
109
|
## Public concepts
|
|
118
110
|
|
|
119
|
-
| Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior
|
|
120
|
-
| ---------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------- | ----------------- | --------- |
|
|
121
|
-
| Structure | static; clickable; link; link-and-remove | Which element owns the token's own action (FR2–FR4). | Resolved from `href`, `onClick`, `isDisabled`, and an enclosing trigger context | static | `component:Token` | Stable | A disabled link token stays a focusable control that exposes its disabled state (FR5)
|
|
122
|
-
| Size | `sm`, `md`, `lg` | Selects the block size. | All structures | `md` | `component:Token` | Stable | TypeScript rejects other values.
|
|
123
|
-
| Tone (`color`) | `default`, `red`, `orange`, `yellow`, `green`, `teal`, `cyan`, `blue`, `purple`, `pink`, `gray`; theme additions | Selects fill and text color. | All structures | `default` | `component:Token` | Stable | A theme-added value is reflected on `token` and receives no component fill or color; TypeScript rejects unregistered values.
|
|
124
|
-
| Removable | present when `onRemove` is supplied; absent | Adds the Remove button after the label and end content. | All structures | absent | `component:Token` | Stable | —
|
|
125
|
-
| Label visibility | visible; visually hidden | Hides the label visually while keeping it as the token's name. | All structures | visible | `component:Token` | Stable | —
|
|
126
|
-
| Disabled | enabled; disabled | Blocks every token and remove action. | All structures | enabled | `component:Token` | Stable | —
|
|
111
|
+
| Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
|
|
112
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------- | ----------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
113
|
+
| Structure | static; clickable; link; link-and-remove | Which element owns the token's own action (FR2–FR4). | Resolved from `href`, `onClick`, `isDisabled`, and an enclosing trigger context | static | `component:Token` | Stable | A disabled link token stays a focusable control that exposes its disabled state (FR5). |
|
|
114
|
+
| Size | `sm`, `md`, `lg` | Selects the block size. | All structures | `md` | `component:Token` | Stable | TypeScript rejects other values. |
|
|
115
|
+
| Tone (`color`) | `default`, `red`, `orange`, `yellow`, `green`, `teal`, `cyan`, `blue`, `purple`, `pink`, `gray`; theme additions | Selects fill and text color. | All structures | `default` | `component:Token` | Stable | A theme-added value is reflected on `token` and receives no component fill or color; TypeScript rejects unregistered values. |
|
|
116
|
+
| Removable | present when `onRemove` is supplied; absent | Adds the Remove button after the label and end content. | All structures | absent | `component:Token` | Stable | — |
|
|
117
|
+
| Label visibility | visible; visually hidden | Hides the label visually while keeping it as the token's name. | All structures | visible | `component:Token` | Stable | — |
|
|
118
|
+
| Disabled | enabled; disabled | Blocks every token and remove action. | All structures | enabled | `component:Token` | Stable | — |
|
|
127
119
|
|
|
128
120
|
## Behavioral and layout contract
|
|
129
121
|
|
|
130
|
-
| ID | Invariant
|
|
131
|
-
| ---- |
|
|
132
|
-
| FR1 | Content renders in logical inline order: Icon, Label, End content, Remove button. The root is inline-flex with `--spacing-1` gaps, inline padding `--spacing-2`, `--radius-inner` corners, `--text-supporting-size` type at `--font-weight-medium`, and a minimum block size of `--size-element-sm`, `-md`, or `-lg` minus 8px for `sm`, `md`, and `lg`. That size grows with a set text size under `spec:AST-102` FR4; today's fixed size is a gap.
|
|
133
|
-
| FR2 | With no `href`, `onClick`, or enclosing trigger context, the token is a static `span` holding its content directly.
|
|
134
|
-
| FR3 | With an enabled `href` and no `onRemove`, the token root is the shared link component carrying the destination; it takes the root ref, passthrough props, and its own focus-visible outline. A supplied `onClick`, as an accepted event input, reaches the element that owns the contract (`architecture:public-component-api` INV5). Implementation gap: in both link structures `onClick` is not attached or called (Current-state impact).
|
|
135
|
-
| FR4 | With `onClick`, or an enclosing trigger context that resolves the token to a button, the root is a `span` container holding an inner `button` that wraps only the Label and calls `onClick`; a click on the container outside any nested button or link also calls `onClick`. With an enabled `href` and `onRemove`, the container instead holds an inner link wrapping only the Label; a plain click on the surface outside the Remove button follows that link, and a Cmd/Ctrl-click or middle click on the surface opens it in a new tab. A caller `onMouseUp` on the link-and-remove container runs before its delegation.
|
|
136
|
-
| FR5 | A disabled token renders at half opacity with pointer events off and fires neither `onClick` nor `onRemove` from pointer, Enter, or Space; inner and Remove buttons stay focusable with `aria-disabled="true"` and never receive native `disabled`. A disabled control keeps its tab stop and exposes its disabled state (`spec:AST-085` FR1, FR5), and the disabled state is reflected on the owning target (`architecture:component-theming-surface` INV6).
|
|
137
|
-
| FR6 | `onRemove` renders a `type="button"` Remove button holding a close glyph; a click stops propagation before `onRemove` runs, so it never triggers the token's own action or link. The button is a sibling of the inner button or link, never nested in it.
|
|
138
|
-
| FR7 | The label never wraps: the root is capped at its container's inline size and clips overflow. In the static and link structures the Label ends in an ellipsis; in the clickable and link-and-remove structures the inner control clips the Label without an ellipsis. `isLabelHidden` keeps the Label in the DOM with a visually hidden clip treatment.
|
|
139
|
-
| FR8 | `color` defaults to `default`, painting `--color-neutral` with primary text; every other built-in tone paints its tinted background with its matching colored text. The `token` target reflects the resolved `color` and `size` for every value, including a theme-added color, which receives no component fill or text color until a theme paints it.
|
|
140
|
-
| FR9 | Clickable and link structures paint the shared hover overlay under a hover-capable pointer and the shared pressed overlay for mouse and touch presses; the static structure paints neither.
|
|
141
|
-
| FR10 | `ref` reaches the root element of every structure. Supported DOM, ARIA, data, and event props reach the root; `data-testid` reaches the root; `xstyle`, `className`, and `style` merge in that order after component styles.
|
|
122
|
+
| ID | Invariant | Basis |
|
|
123
|
+
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
124
|
+
| FR1 | Content renders in logical inline order: Icon, Label, End content, Remove button. The root is inline-flex with `--spacing-1` gaps, inline padding `--spacing-2`, `--radius-inner` corners, `--text-supporting-size` type at `--font-weight-medium`, and a minimum block size of `--size-element-sm`, `-md`, or `-lg` minus 8px for `sm`, `md`, and `lg`. That size grows with a set text size under `spec:AST-102` FR4; today's fixed size is a gap. | Current source and tests |
|
|
125
|
+
| FR2 | With no `href`, `onClick`, or enclosing trigger context, the token is a static `span` holding its content directly. | Current source and tests |
|
|
126
|
+
| FR3 | With an enabled `href` and no `onRemove`, the token root is the shared link component carrying the destination; it takes the root ref, passthrough props, and its own focus-visible outline. A supplied `onClick`, as an accepted event input, reaches the element that owns the contract (`architecture:public-component-api` INV5). Implementation gap: in both link structures `onClick` is not attached or called (Current-state impact). | Current source and tests |
|
|
127
|
+
| FR4 | With `onClick`, or an enclosing trigger context that resolves the token to a button, the root is a `span` container holding an inner `button` that wraps only the Label and calls `onClick`; a click on the container outside any nested button or link also calls `onClick`. With an enabled `href` and `onRemove`, the container instead holds an inner link wrapping only the Label; a plain click on the surface outside the Remove button follows that link, and a Cmd/Ctrl-click or middle click on the surface opens it in a new tab. A caller `onMouseUp` on the link-and-remove container runs before its delegation. | Current source and tests |
|
|
128
|
+
| FR5 | A disabled token renders at half opacity with pointer events off and fires neither `onClick` nor `onRemove` from pointer, Enter, or Space; inner and Remove buttons stay focusable with `aria-disabled="true"` and never receive native `disabled`. A disabled control keeps its tab stop and exposes its disabled state (`spec:AST-085` FR1, FR5), and the disabled state is reflected on the owning target (`architecture:component-theming-surface` INV6). A disabled token with `href` and no `onClick` or trigger context renders a focusable link with `aria-disabled="true"` and no `href`, and every structure reflects the disabled state on the `token` target. | Current source and tests |
|
|
129
|
+
| FR6 | `onRemove` renders a `type="button"` Remove button holding a close glyph; a click stops propagation before `onRemove` runs, so it never triggers the token's own action or link. The button is a sibling of the inner button or link, never nested in it. | Current source and tests |
|
|
130
|
+
| FR7 | The label never wraps: the root is capped at its container's inline size and clips overflow. In the static and link structures the Label ends in an ellipsis; in the clickable and link-and-remove structures the inner control clips the Label without an ellipsis. `isLabelHidden` keeps the Label in the DOM with a visually hidden clip treatment. | Current source and tests |
|
|
131
|
+
| FR8 | `color` defaults to `default`, painting `--color-neutral` with primary text; every other built-in tone paints its tinted background with its matching colored text. The `token` target reflects the resolved `color` and `size` for every value, including a theme-added color, which receives no component fill or text color until a theme paints it. | Current source and tests |
|
|
132
|
+
| FR9 | Clickable and link structures paint the shared hover overlay under a hover-capable pointer and the shared pressed overlay for mouse and touch presses; the static structure paints neither. | Current source |
|
|
133
|
+
| FR10 | `ref` reaches the root element of every structure. Supported DOM, ARIA, data, and event props reach the root; `data-testid` reaches the root; `xstyle`, `className`, and `style` merge in that order after component styles. | Current source and tests |
|
|
142
134
|
|
|
143
135
|
### Allowed variation
|
|
144
136
|
|
|
@@ -163,11 +155,11 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
163
155
|
### Transformation and precedence order
|
|
164
156
|
|
|
165
157
|
- **ORD1 — Structure.** Enabled `href` → `onClick` → enclosing trigger context →
|
|
166
|
-
static. `onRemove` with an
|
|
167
|
-
container. A disabled `href`
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
- **ORD2 — Root attributes.** Component class and `data-color`/`data-size` →
|
|
158
|
+
disabled `href` → static. `onRemove` with an `href` that wins selects the
|
|
159
|
+
link-and-remove container. A disabled `href` yields to `onClick` and a
|
|
160
|
+
trigger context; otherwise it keeps the link structure, rendered disabled
|
|
161
|
+
(FR5).
|
|
162
|
+
- **ORD2 — Root attributes.** Component class and `data-color`/`data-size`/`data-disabled` →
|
|
171
163
|
component styles → `xstyle` → `className` → `style` → caller props → `data-testid`, hidden-label
|
|
172
164
|
`aria-label`, and `aria-description`. A hidden label's `aria-label` replaces
|
|
173
165
|
a caller `aria-label`.
|
|
@@ -204,9 +196,7 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
204
196
|
those structures shows the container outline and the button's own.
|
|
205
197
|
- **AR5 — Disabled.** A disabled token's controls stay focusable, expose
|
|
206
198
|
`aria-disabled="true"`, and ignore pointer, Enter, and Space (`spec:AST-085`
|
|
207
|
-
FR1, FR2, FR5)
|
|
208
|
-
disabled link token renders as a static `span` with no tab stop and no
|
|
209
|
-
disabled state.
|
|
199
|
+
FR1, FR2, FR5), the disabled link included.
|
|
210
200
|
- **AR6 — Remove target size.** The Remove button's glyph box is 16 by 16 CSS
|
|
211
201
|
pixels and its hit area extends 4 pixels on every side to 24 by 24 at `md`
|
|
212
202
|
and `lg`, meeting WCAG 2.2 2.5.8, while a press on the adjacent label still
|
|
@@ -224,7 +214,7 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
224
214
|
| End content | Optional trailing content such as a count or status dot. | Current source and docs | Supporting | FR1 |
|
|
225
215
|
| Remove button | A trailing close control that dismisses the token. | Current source and docs | Supporting | FR6, AR1, AR2, AR6 |
|
|
226
216
|
|
|
227
|
-
Theming: the root is the single `token` target and reflects `color` and
|
|
217
|
+
Theming: the root is the single `token` target and reflects `color`, `size`, and the disabled state.
|
|
228
218
|
The Label, End content, and Icon add no targets; the Remove button inherits the
|
|
229
219
|
token's text color.
|
|
230
220
|
|
|
@@ -234,7 +224,7 @@ token's text color.
|
|
|
234
224
|
for both link structures, including delegated surface clicks; Token adopts
|
|
235
225
|
them through the shared link component and clickable-container delegation.
|
|
236
226
|
- `spec:AST-085` owns focusable disabled controls; FR5 and AR5 record Token's
|
|
237
|
-
conformance
|
|
227
|
+
conformance.
|
|
238
228
|
- `architecture:public-component-api` owns ref, passthrough, and styling-input
|
|
239
229
|
rules; FR3 records one gap against INV5.
|
|
240
230
|
- `architecture:component-theming-surface` owns the `token` target and the
|
|
@@ -244,18 +234,18 @@ token's text color.
|
|
|
244
234
|
|
|
245
235
|
## Verification map
|
|
246
236
|
|
|
247
|
-
| Contract | Verification | Representative states
|
|
248
|
-
| ------------------ | -------------------------------------------------------------------------------------- |
|
|
249
|
-
| FR2, FR10 | `Token.test.tsx` render, ref, and pass-through suites | Static, clickable, and link roots; caller props
|
|
250
|
-
| FR1, FR7 | `Token.stories.tsx` size, icon, and end-content fixtures in a browser | Sizes, icon and end content
|
|
251
|
-
| FR3, FR4, FR6, AR1 | `Token.test.tsx` link, clickable, and link-with-remove suites | Link, clickable, link and remove, modified and middle clicks
|
|
252
|
-
| FR5, AR5 | `Token.test.tsx` disabled tests | Disabled clickable with remove
|
|
253
|
-
| AR2, AR3, AR7 | `Token.test.tsx` accessibility suite | Remove name, hidden label, description
|
|
254
|
-
| AR6 | `TokenRemoveTarget.a11y.chromium.spec.ts` | Pre-set filter tokens (default size), LTR and RTL
|
|
255
|
-
| FR8 | `Token.test.tsx` custom-color test; `extensibleAxes.test.ts`; `themingTargets.test.ts` | Theme-added color; map wiring
|
|
256
|
-
| FR9, AR4 | `Token.stories.tsx` clickable and link fixtures in a browser | Hover, press, keyboard focus on the token
|
|
257
|
-
| PR1 | Source review | Every structure
|
|
258
|
-
| AR2, AR3, AR5, AR7 | Accessibility-tree evidence (`spec:AST-009` FR3) | Every structure; hidden label; description; disabled
|
|
237
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
238
|
+
| ------------------ | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
|
|
239
|
+
| FR2, FR10 | `Token.test.tsx` render, ref, and pass-through suites | Static, clickable, and link roots; caller props | A wrong root element, a detached ref, or a dropped caller prop fails DOM assertions. Gap: no test covers the ref on the link-and-remove container. | `audit:Token/behavior` |
|
|
240
|
+
| FR1, FR7 | `Token.stories.tsx` size, icon, and end-content fixtures in a browser | Sizes, icon and end content | Gap: the size and overflow unit tests assert presence only, and no story renders a long label; slot order and block size are visible only in the rendered fixtures, and the ellipsis has no check. | `audit:Token/layout` |
|
|
241
|
+
| FR3, FR4, FR6, AR1 | `Token.test.tsx` link, clickable, and link-with-remove suites | Link, clickable, link and remove, modified and middle clicks | Nesting the Remove button, firing the token action from Remove, or losing delegation fails. Gap: no test asserts that `onClick` is dropped with `href`. | `audit:Token/behavior` |
|
|
242
|
+
| FR5, AR5 | `Token.test.tsx` disabled tests | Disabled clickable with remove; disabled static; disabled link with and without remove | A native `disabled`, a lost tab stop, a disabled link with a destination, an unreflected disabled state, or a fired callback fails. | `audit:Token/a11y` |
|
|
243
|
+
| AR2, AR3, AR7 | `Token.test.tsx` accessibility suite | Remove name, hidden label, description | A missing remove name, lost hidden label, or missing description fails. Gap: no test asserts the link structure's content-derived name. | `audit:Token/a11y` |
|
|
244
|
+
| AR6 | `TokenRemoveTarget.a11y.chromium.spec.ts` | Pre-set filter tokens (default size), LTR and RTL | A remove target that misses its X edge or swallows a label press fails in real Chromium. Gap: the `sm` clipping is not covered. | `audit:Token/a11y` |
|
|
245
|
+
| FR8 | `Token.test.tsx` custom-color test; `extensibleAxes.test.ts`; `themingTargets.test.ts` | Theme-added color; map wiring | Dropping the `color` reflection fails. Gap: no test asserts `data-size` or any tone's paint; those are visible only in `Token.stories.tsx`. | `audit:Token/theming` |
|
|
246
|
+
| FR9, AR4 | `Token.stories.tsx` clickable and link fixtures in a browser | Hover, press, keyboard focus on the token | Gap: no automated check covers overlays or indicators, and no story renders a clickable or link token with Remove, so the double indicator on Remove has no fixture. | `audit:Token/design` |
|
|
247
|
+
| PR1 | Source review | Every structure | Gap: no automated check fails if Token adds a per-token listener, observer, or timer. | `audit:Token/behavior` |
|
|
248
|
+
| AR2, AR3, AR5, AR7 | Accessibility-tree evidence (`spec:AST-009` FR3) | Every structure; hidden label; description; disabled | Gap: no accessibility-tree check proves the computed names, the hidden-label name, the description, or the disabled state; the unit tests assert DOM attributes only. | `audit:Token/a11y` |
|
|
259
249
|
|
|
260
250
|
## Decision log
|
|
261
251
|
|
package/src/Token/Token.test.tsx
CHANGED
|
@@ -256,6 +256,73 @@ describe('Token accessibility', () => {
|
|
|
256
256
|
).toBeInTheDocument();
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
+
it('reflects the disabled state on the token target in every structure (theming-surface INV6)', () => {
|
|
260
|
+
render(
|
|
261
|
+
<>
|
|
262
|
+
<Token label="Static" isDisabled data-testid="static" />
|
|
263
|
+
<Token
|
|
264
|
+
label="Clickable"
|
|
265
|
+
onClick={() => {}}
|
|
266
|
+
isDisabled
|
|
267
|
+
data-testid="clickable"
|
|
268
|
+
/>
|
|
269
|
+
<Token label="Link" href="/docs" isDisabled data-testid="link" />
|
|
270
|
+
<Token
|
|
271
|
+
label="Link with remove"
|
|
272
|
+
href="/docs"
|
|
273
|
+
onRemove={() => {}}
|
|
274
|
+
isDisabled
|
|
275
|
+
data-testid="link-and-remove"
|
|
276
|
+
/>
|
|
277
|
+
<Token label="Enabled" href="/docs" data-testid="enabled" />
|
|
278
|
+
</>,
|
|
279
|
+
);
|
|
280
|
+
for (const id of ['static', 'clickable', 'link', 'link-and-remove']) {
|
|
281
|
+
expect(screen.getByTestId(id), id).toHaveAttribute(
|
|
282
|
+
'data-disabled',
|
|
283
|
+
'disabled',
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
expect(screen.getByTestId('enabled')).not.toHaveAttribute('data-disabled');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('keeps a disabled link token a focusable link with no destination (spec:AST-085 FR1, FR5)', async () => {
|
|
290
|
+
const user = userEvent.setup();
|
|
291
|
+
const onRemove = vi.fn();
|
|
292
|
+
render(
|
|
293
|
+
<>
|
|
294
|
+
<Token label="Docs" href="/docs" isDisabled />
|
|
295
|
+
<Token label="Guide" href="/guide" isDisabled onRemove={onRemove} />
|
|
296
|
+
</>,
|
|
297
|
+
);
|
|
298
|
+
for (const name of ['Docs', 'Guide']) {
|
|
299
|
+
const link = screen.getByRole('link', {name});
|
|
300
|
+
expect(link, name).toHaveAttribute('aria-disabled', 'true');
|
|
301
|
+
expect(link, name).not.toHaveAttribute('href');
|
|
302
|
+
}
|
|
303
|
+
await user.tab();
|
|
304
|
+
expect(screen.getByRole('link', {name: 'Docs'})).toHaveFocus();
|
|
305
|
+
await user.tab();
|
|
306
|
+
expect(screen.getByRole('link', {name: 'Guide'})).toHaveFocus();
|
|
307
|
+
await user.tab();
|
|
308
|
+
await user.keyboard('{Enter}');
|
|
309
|
+
expect(onRemove).not.toHaveBeenCalled();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("passes other keys to a disabled link token's onKeyDown, but not Enter or Space", async () => {
|
|
313
|
+
const user = userEvent.setup();
|
|
314
|
+
const onKeyDown = vi.fn();
|
|
315
|
+
render(
|
|
316
|
+
<Token label="Docs" href="/docs" isDisabled onKeyDown={onKeyDown} />,
|
|
317
|
+
);
|
|
318
|
+
await user.tab();
|
|
319
|
+
await user.keyboard('{ArrowRight}');
|
|
320
|
+
expect(onKeyDown).toHaveBeenCalledTimes(1);
|
|
321
|
+
expect(onKeyDown.mock.calls[0][0].key).toBe('ArrowRight');
|
|
322
|
+
await user.keyboard('{Enter} ');
|
|
323
|
+
expect(onKeyDown).toHaveBeenCalledTimes(1);
|
|
324
|
+
});
|
|
325
|
+
|
|
259
326
|
it('keeps both buttons focusable and inert when isDisabled is true', async () => {
|
|
260
327
|
const user = userEvent.setup();
|
|
261
328
|
const handleClick = vi.fn();
|
package/src/Token/Token.tsx
CHANGED
|
@@ -348,7 +348,46 @@ export function Token({
|
|
|
348
348
|
...(description != null ? {'aria-description': description} : {}),
|
|
349
349
|
};
|
|
350
350
|
|
|
351
|
-
|
|
351
|
+
// A disabled link keeps a focusable link with aria-disabled and no
|
|
352
|
+
// destination, as a disabled Link does (spec:AST-085 FR1, FR5;
|
|
353
|
+
// component:Link FR5). useInteractiveRole reports no link role once
|
|
354
|
+
// disabled, so the structure is chosen here.
|
|
355
|
+
const isDisabledLink = isDisabled && href != null && effectiveOnClick == null;
|
|
356
|
+
|
|
357
|
+
if (isDisabledLink && onRemove == null) {
|
|
358
|
+
// A disabled link is an <a> with no href, as Link renders one: nothing can
|
|
359
|
+
// navigate, it keeps a Tab stop and the link role (spec:AST-085 FR1, FR5).
|
|
360
|
+
// The router LinkComponent is skipped, since a custom router link may
|
|
361
|
+
// require a live href.
|
|
362
|
+
const DisabledAnchor = 'a';
|
|
363
|
+
return (
|
|
364
|
+
<DisabledAnchor
|
|
365
|
+
ref={ref as React.Ref<HTMLAnchorElement>}
|
|
366
|
+
role="link"
|
|
367
|
+
aria-disabled={true}
|
|
368
|
+
tabIndex={0}
|
|
369
|
+
{...mergeProps(
|
|
370
|
+
themeProps('token', {color, size, disabled: 'disabled'}),
|
|
371
|
+
focusOutlineProps.focusVisible(
|
|
372
|
+
styles.base,
|
|
373
|
+
sizeStyles[size],
|
|
374
|
+
colorStyles[color],
|
|
375
|
+
styles.interactive,
|
|
376
|
+
styles.disabled,
|
|
377
|
+
xstyle,
|
|
378
|
+
),
|
|
379
|
+
className,
|
|
380
|
+
style,
|
|
381
|
+
)}
|
|
382
|
+
{...rest}
|
|
383
|
+
{...disabledInteractionProps(rest.onKeyDown)}
|
|
384
|
+
{...sharedProps}>
|
|
385
|
+
{content}
|
|
386
|
+
</DisabledAnchor>
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (role === 'link' || isDisabledLink) {
|
|
352
391
|
if (onRemove == null) {
|
|
353
392
|
return (
|
|
354
393
|
<LinkComponent
|
|
@@ -356,7 +395,11 @@ export function Token({
|
|
|
356
395
|
href={href as string}
|
|
357
396
|
{...pressable}
|
|
358
397
|
{...mergeProps(
|
|
359
|
-
themeProps('token', {
|
|
398
|
+
themeProps('token', {
|
|
399
|
+
color,
|
|
400
|
+
size,
|
|
401
|
+
disabled: isDisabled ? 'disabled' : null,
|
|
402
|
+
}),
|
|
360
403
|
focusOutlineProps.focusVisible(
|
|
361
404
|
styles.base,
|
|
362
405
|
sizeStyles[size],
|
|
@@ -405,7 +448,11 @@ export function Token({
|
|
|
405
448
|
</span>
|
|
406
449
|
}
|
|
407
450
|
{...mergeProps(
|
|
408
|
-
themeProps('token', {
|
|
451
|
+
themeProps('token', {
|
|
452
|
+
color,
|
|
453
|
+
size,
|
|
454
|
+
disabled: isDisabled ? 'disabled' : null,
|
|
455
|
+
}),
|
|
409
456
|
focusOutlineProps.focusWithin(
|
|
410
457
|
styles.base,
|
|
411
458
|
sizeStyles[size],
|
|
@@ -439,7 +486,11 @@ export function Token({
|
|
|
439
486
|
onClick={isDisabled ? undefined : handleContainerClick}
|
|
440
487
|
{...pressable}
|
|
441
488
|
{...mergeProps(
|
|
442
|
-
themeProps('token', {
|
|
489
|
+
themeProps('token', {
|
|
490
|
+
color,
|
|
491
|
+
size,
|
|
492
|
+
disabled: isDisabled ? 'disabled' : null,
|
|
493
|
+
}),
|
|
443
494
|
focusOutlineProps.focusWithin(
|
|
444
495
|
styles.base,
|
|
445
496
|
sizeStyles[size],
|
|
@@ -479,7 +530,11 @@ export function Token({
|
|
|
479
530
|
<span
|
|
480
531
|
ref={ref}
|
|
481
532
|
{...mergeProps(
|
|
482
|
-
themeProps('token', {
|
|
533
|
+
themeProps('token', {
|
|
534
|
+
color,
|
|
535
|
+
size,
|
|
536
|
+
disabled: isDisabled ? 'disabled' : null,
|
|
537
|
+
}),
|
|
483
538
|
stylex.props(
|
|
484
539
|
styles.base,
|
|
485
540
|
sizeStyles[size],
|
package/src/Token/TokenLink.tsx
CHANGED
|
@@ -32,6 +32,7 @@ import type {LinkComponentType} from '../Link/types';
|
|
|
32
32
|
import {composeEventHandlers} from '../utils';
|
|
33
33
|
|
|
34
34
|
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
35
|
+
import {disabledInteractionProps} from '../utils/disabledInteraction';
|
|
35
36
|
export interface TokenLinkProps extends HTMLAttributes<HTMLElement> {
|
|
36
37
|
/** Ref forwarded to the container element. */
|
|
37
38
|
ref?: Ref<HTMLElement>;
|
|
@@ -100,13 +101,26 @@ export function TokenLink({
|
|
|
100
101
|
: composeEventHandlers(onMouseUpProp, onMouseUp)
|
|
101
102
|
}>
|
|
102
103
|
{icon}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
{isDisabled ? (
|
|
105
|
+
// A disabled link stays a focusable link with no destination, as a
|
|
106
|
+
// disabled Link does (spec:AST-085 FR1, FR5; component:Link FR5).
|
|
107
|
+
<a
|
|
108
|
+
ref={linkRef as Ref<HTMLAnchorElement>}
|
|
109
|
+
role="link"
|
|
110
|
+
aria-disabled={true}
|
|
111
|
+
tabIndex={0}
|
|
112
|
+
{...linkStyleProps}
|
|
113
|
+
{...disabledInteractionProps()}>
|
|
114
|
+
{labelContent}
|
|
115
|
+
</a>
|
|
116
|
+
) : (
|
|
117
|
+
<LinkComponent
|
|
118
|
+
ref={linkRef as Ref<HTMLAnchorElement>}
|
|
119
|
+
href={href}
|
|
120
|
+
{...linkStyleProps}>
|
|
121
|
+
{labelContent}
|
|
122
|
+
</LinkComponent>
|
|
123
|
+
)}
|
|
110
124
|
{endContent}
|
|
111
125
|
{removeButton}
|
|
112
126
|
</span>
|
|
@@ -16,6 +16,7 @@ verified_by:
|
|
|
16
16
|
packages/core/src/Tokenizer/Tokenizer.ssr.test.tsx,
|
|
17
17
|
packages/core/src/Tokenizer/TokenizerBottomSheet.test.tsx,
|
|
18
18
|
packages/core/src/Tokenizer/Tokenizer.props-type.test.tsx,
|
|
19
|
+
packages/core/src/PowerSearch/PowerSearch.test.tsx,
|
|
19
20
|
packages/core/src/internal/tokenizer-presentation.test.ts,
|
|
20
21
|
packages/core/src/Typeahead/Typeahead.test.tsx,
|
|
21
22
|
packages/core/src/Layer/useLayer.test.tsx,
|
|
@@ -325,7 +326,7 @@ presentation, which has no inline combobox, suggestion menu, or end lane.
|
|
|
325
326
|
|
|
326
327
|
## Accessibility contract
|
|
327
328
|
|
|
328
|
-
AR1–AR5 describe the `popover` presentation; AR6 describes the sheet
|
|
329
|
+
AR1–AR5 and AR7 describe the `popover` presentation; AR6 describes the sheet
|
|
329
330
|
presentation.
|
|
330
331
|
|
|
331
332
|
- **AR1 — Modality equivalence.** The same input or token/control produces the
|
|
@@ -382,6 +383,16 @@ presentation.
|
|
|
382
383
|
through `spec:AST-020/FR1`). After the sheet closes focus returns to the
|
|
383
384
|
trigger and the field's announcements follow the same FR14 route from
|
|
384
385
|
wherever the field renders.
|
|
386
|
+
- **AR7 — Clear all is announced.** In the `popover` presentation, the field's
|
|
387
|
+
Clear all, which empties every token at once and moves focus to the input,
|
|
388
|
+
announces "All items removed" (`@astryx.tokenizer.allRemoved`, the words of
|
|
389
|
+
the sheet's Clear all in AR6) the same way the field announces a token added
|
|
390
|
+
or removed: politely, through the shared channel (`spec:AST-086/FR2`, FR5).
|
|
391
|
+
It is announced once per Clear all (`spec:AST-086/FR6`); a Clear all
|
|
392
|
+
confirmed in the sheet is announced by the sheet alone (AR6), and the field
|
|
393
|
+
stays silent for it (WCAG 2.2 SC 4.1.3 through `spec:AST-020/FR9`). A
|
|
394
|
+
component that composes Tokenizer and words its own Clear all announces its
|
|
395
|
+
own words instead (`component:PowerSearch`).
|
|
385
396
|
|
|
386
397
|
## Design relationships
|
|
387
398
|
|
|
@@ -462,6 +473,7 @@ become Tokenizer policy.
|
|
|
462
473
|
| FR13–FR15, AR5 | Focused Tokenizer/BaseTypeahead tests and exact-head Chromium interaction evidence | pointer/keyboard commit, first/middle/last choice, rejected/deferred control, loading, disabled, max, exhaustion, Escape, Tab, outside focus | Selected values remain available, order changes, focus leaves the combobox, active descendant names no option, stale work reopens, or a terminal state stays interactive. | `audit:Tokenizer/multi-select` |
|
|
463
474
|
| PR1–PR5 | Instrumented reads, pending-frame cleanup tests, and first-frame Chromium capture | open, close before frame, reopen opposite side, resize while open | Render-time/repeated reads, observer/pointer tracking, uncancelled frame, or first-frame jump occurs. | `audit:Tokenizer/performance` |
|
|
464
475
|
| AR1–AR5 | Existing interaction suites plus focused pointer/keyboard/browser checks; native-disabled assertions pending conformance (`spec:AST-085`) | add/edit, focus/refocus, overflow, disabled, dismissal, retained bootstrap selection | Modality changes placement, semantics/focus regress, active descendant becomes invalid, or visible placement is wrong before fallback. | `audit:Tokenizer/accessibility` |
|
|
476
|
+
| AR7 | Focused Tokenizer tests, inline and sheet, and the PowerSearch test | inline Clear all; a localized catalog; a Clear all confirmed in the sheet counted across every live region; PowerSearch's own words | The inline Clear all is silent, speaks words other than its catalog message, a sheet Clear all is announced twice, or PowerSearch speaks Tokenizer's words. | `audit:Tokenizer/accessibility` |
|
|
465
477
|
| FR16–FR24, AR6, PR6 | Focused Tokenizer sheet tests with the compact-touch query stubbed both ways, a server-render-then-hydrate test, plus real-engine touch evidence | default popover, forced sheet, adaptive both ways, `adaptive` server-rendered as the combobox and hydrated without a recoverable error, then the trigger on a coarse-pointer client with the body still active, with `hasAutoFocus` the autofocused input's focus on the trigger after the swap and no `onBlur`/`onFocus` reported, the trigger's box against the hydrating field's at 390 and 414px, base props and ref on the trigger, the trigger's description in the inline input's order — a disabled trigger with `disabledMessage`, `description` and a status exposing the caller's ids, the description, the status message and the reason before the count and the tokens —, inert-prop warning, pending search, in-sheet announcements of toggles, busy start, result count and the rendered empty outcome, the confirmed Clear all announced once the confirmation has closed, a below-threshold query, a rejecting source, focus on the panel at open, the query cleared by a swipe, scrim, or Escape close, focus after a presentation change with the sheet closed, focus after the create row, focus after a confirmed Clear all, a spent Clear all focusable with `aria-disabled` and no action, focus after a settled search removes the focused row, no busy feedback or busy-start announcement on open with an instant source, only the search field — no list, no message, no announcement — for an empty query with nothing to list (a search-only source, nothing selected), and the list once a query finds items, element counts under a fine and a coarse pointer at rest and open (Chromium, Experimental stories: adaptive 154/120 at rest — the closed trigger in place of the inline field, a different element that stays under the fine count — and 157/165 open at the query "e" with no footer, the coarse total reconciled as rest − the re-hosted surfaces host + the dialog of 47 by part, forced sheet 114/114 and 165/165; the two owner exceptions DEC-3 records beyond the FR60 allowance, with before and after counts), no confirmation dialog in the DOM while none is pending, the query kept across a disabled-forced close, `aria-setsize`/`aria-posinset` on each row's `listitem` and the next batch on focus reaching the last rendered row, open/toggle/reopen order, create, batches, limit, Clear all, disabled while open, focus return | A field without `experimental` changes, a toggle moves a row, Clear all skips confirmation, the limit disables checked rows, or the popover bundle carries the sheet. | `audit:Tokenizer/sheet` |
|
|
466
478
|
| Record structure | `scripts/check-knowledge.mjs` | Template-v3 current record and current-only frontmatter links | Invalid metadata, authority, or relationship references fail repository validation. | `audit:Tokenizer/knowledge` |
|
|
467
479
|
|
|
@@ -1403,6 +1403,33 @@ describe('Tokenizer', () => {
|
|
|
1403
1403
|
});
|
|
1404
1404
|
});
|
|
1405
1405
|
describe('announcements', () => {
|
|
1406
|
+
it("announces Clear all politely, in the sheet's words", async () => {
|
|
1407
|
+
const onChange = vi.fn();
|
|
1408
|
+
render(
|
|
1409
|
+
<InternationalizationProvider
|
|
1410
|
+
locale="fr"
|
|
1411
|
+
overrides={{
|
|
1412
|
+
fr: {'@astryx.tokenizer.allRemoved': 'Tout a été retiré'},
|
|
1413
|
+
}}>
|
|
1414
|
+
<Tokenizer
|
|
1415
|
+
label="Members"
|
|
1416
|
+
searchSource={userSource}
|
|
1417
|
+
value={[users[0], users[1]]}
|
|
1418
|
+
onChange={onChange}
|
|
1419
|
+
hasClear
|
|
1420
|
+
/>
|
|
1421
|
+
</InternationalizationProvider>,
|
|
1422
|
+
);
|
|
1423
|
+
fireEvent.click(screen.getByRole('button', {name: /clear/i}));
|
|
1424
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
1425
|
+
[],
|
|
1426
|
+
expect.objectContaining({type: 'remove'}),
|
|
1427
|
+
);
|
|
1428
|
+
await waitFor(() => {
|
|
1429
|
+
expect(politeRegion()?.textContent).toBe('Tout a été retiré');
|
|
1430
|
+
});
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1406
1433
|
it('announces removal politely on Backspace with an empty input', async () => {
|
|
1407
1434
|
const onChange = vi.fn();
|
|
1408
1435
|
render(
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
import React, {
|
|
28
28
|
lazy,
|
|
29
29
|
Suspense,
|
|
30
|
+
use,
|
|
30
31
|
useCallback,
|
|
31
32
|
useEffect,
|
|
32
33
|
useId,
|
|
@@ -51,6 +52,7 @@ import {
|
|
|
51
52
|
useFieldInputStyling,
|
|
52
53
|
} from '../Field/fieldParts';
|
|
53
54
|
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
55
|
+
import {TokenizerClearAllMessageContext} from './TokenizerClearAllMessageContext';
|
|
54
56
|
import {useBusyAnnouncement} from '../hooks/useBusyAnnouncement';
|
|
55
57
|
import {useAdaptivePresentation} from '../hooks/useAdaptivePresentation';
|
|
56
58
|
import {useIsHydrating} from '../hooks/useIsHydrating';
|
|
@@ -1223,15 +1225,27 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
1223
1225
|
);
|
|
1224
1226
|
|
|
1225
1227
|
// Handle clearing all items
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1228
|
+
// A composing component that words its own Clear all (PowerSearch) provides
|
|
1229
|
+
// its words; otherwise the field's own (component:Tokenizer AR7).
|
|
1230
|
+
const clearAllMessage =
|
|
1231
|
+
use(TokenizerClearAllMessageContext) ?? t('@astryx.tokenizer.allRemoved');
|
|
1232
|
+
const handleClearAll = useCallback(
|
|
1233
|
+
(options?: {silent?: boolean}) => {
|
|
1234
|
+
if (isDisabled || value.length === 0) {
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
// Report the last item as removed (convention)
|
|
1238
|
+
const lastItem = value[value.length - 1];
|
|
1239
|
+
onChange([], {item: lastItem, type: 'remove'});
|
|
1240
|
+
// Focus moves to the input, so the emptied selection is announced, in
|
|
1241
|
+
// the sheet's words (WCAG 4.1.3). The sheet announces its own Clear all.
|
|
1242
|
+
if (!options?.silent) {
|
|
1243
|
+
announce(clearAllMessage);
|
|
1244
|
+
}
|
|
1245
|
+
inputRef.current?.focus();
|
|
1246
|
+
},
|
|
1247
|
+
[value, onChange, isDisabled, announce, clearAllMessage],
|
|
1248
|
+
);
|
|
1235
1249
|
|
|
1236
1250
|
// Handle backspace on empty input — remove last token
|
|
1237
1251
|
const handleKeyDown = useCallback(
|
|
@@ -1636,7 +1650,7 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
1636
1650
|
emptySearchText={emptySearchText}
|
|
1637
1651
|
onAdd={handleAdd}
|
|
1638
1652
|
onRemove={handleRemove}
|
|
1639
|
-
onClearAll={handleClearAll}
|
|
1653
|
+
onClearAll={() => handleClearAll({silent: true})}
|
|
1640
1654
|
onChangeQuery={handleSheetQueryChange}
|
|
1641
1655
|
/>
|
|
1642
1656
|
)}
|
|
@@ -808,6 +808,14 @@ describe('Tokenizer sheet presentation (single-sheet touch flow)', () => {
|
|
|
808
808
|
),
|
|
809
809
|
).toHaveTextContent('All items removed'),
|
|
810
810
|
);
|
|
811
|
+
// Exactly one live region says it: the field's own region stays silent
|
|
812
|
+
// for a Clear all the sheet already announced.
|
|
813
|
+
const saying = Array.from(
|
|
814
|
+
document.querySelectorAll(
|
|
815
|
+
'[aria-live], [role="status"], [role="alert"]',
|
|
816
|
+
),
|
|
817
|
+
).filter(region => region.textContent?.includes('All items removed'));
|
|
818
|
+
expect(saying).toHaveLength(1);
|
|
811
819
|
} finally {
|
|
812
820
|
modalSpy.mockRestore();
|
|
813
821
|
}
|