@astryxdesign/core 0.7.1-canary.ff40b78 → 0.7.1
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/CHANGELOG.md +116 -0
- package/dist/Button/Button.d.ts +1 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +5 -2
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +191 -32
- package/dist/Chat/ChatComposerInput.d.ts +9 -0
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +5 -0
- package/dist/Chat/ChatMessage.d.ts +11 -0
- package/dist/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.js +18 -9
- package/dist/Chat/useTriggerMenu.d.ts +9 -2
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +222 -50
- package/dist/CodeBlock/CodeBlock.d.ts +10 -1
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +158 -38
- package/dist/CodeBlock/tokenizer.d.ts +39 -0
- package/dist/CodeBlock/tokenizer.d.ts.map +1 -1
- package/dist/CodeBlock/tokenizer.js +108 -0
- package/dist/CommandPalette/CommandPalette.d.ts +9 -5
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +38 -20
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts +4 -3
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.js +4 -3
- package/dist/CommandPalette/CommandPaletteInput.d.ts +5 -4
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +35 -18
- package/dist/CommandPalette/CommandPaletteItem.d.ts +11 -6
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +8 -9
- package/dist/CommandPalette/CommandPaletteList.d.ts +4 -2
- package/dist/CommandPalette/CommandPaletteList.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteList.js +10 -5
- package/dist/ComplexSelector/ComplexSelector.d.ts +8 -5
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +12 -3
- package/dist/ContextMenu/ContextMenu.d.ts +3 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +62 -48
- package/dist/DateInput/DateInput.js +1 -1
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +12 -4
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +12 -2
- package/dist/Dialog/Dialog.d.ts +1 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +16 -5
- package/dist/Dialog/DialogHeader.d.ts +1 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +7 -7
- package/dist/Divider/Divider.d.ts +1 -0
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +5 -4
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +161 -65
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +7 -5
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +12 -5
- package/dist/Field/InputClearButton.d.ts +8 -4
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +23 -7
- package/dist/FormLayout/FormLayout.d.ts.map +1 -1
- package/dist/FormLayout/FormLayout.js +1 -0
- package/dist/Grid/Grid.d.ts +13 -4
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +7 -1
- package/dist/HoverCard/useHoverCard.d.ts +1 -1
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +67 -49
- package/dist/InputGroup/InputGroup.d.ts +2 -4
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +2 -4
- package/dist/InputGroup/groupStyles.d.ts +10 -2
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +19 -2
- package/dist/Layer/useLayerDismissal.d.ts +9 -9
- package/dist/Layer/useLayerDismissal.js +9 -9
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +25 -4
- package/dist/MobileNav/MobileNav.d.ts +7 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +45 -2
- package/dist/MultiSelector/MultiSelector.d.ts +7 -4
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +24 -3
- package/dist/NumberInput/NumberInput.js +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +6 -0
- package/dist/Resizable/ResizeHandle.d.ts +1 -1
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +83 -15
- package/dist/Resizable/useResizable.d.ts +23 -0
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +78 -0
- package/dist/Selector/Selector.d.ts +6 -3
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +28 -6
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +10 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +18 -7
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +16 -4
- package/dist/Toolbar/Toolbar.d.ts +2 -1
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +3 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +6 -3
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +7 -1
- package/dist/astryx.css +22 -0
- package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
- package/dist/hooks/useScrollOverflow.js +55 -12
- package/dist/hooks/useTreeFocus.d.ts +21 -0
- package/dist/hooks/useTreeFocus.d.ts.map +1 -1
- package/dist/hooks/useTreeFocus.js +40 -2
- 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/reset.a11y.chromium.spec.js +6 -1
- package/dist/reset.until-found.a11y.browser.spec.d.ts +2 -0
- package/dist/reset.until-found.a11y.browser.spec.d.ts.map +1 -0
- package/dist/reset.until-found.a11y.browser.spec.js +71 -0
- package/dist/theme/syntax/SyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/SyntaxTheme.js +43 -2
- package/dist/theme/syntax/defineSyntaxTheme.d.ts +18 -5
- package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +38 -9
- package/dist/theme/syntax/index.d.ts +1 -1
- package/dist/theme/syntax/index.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.d.ts +12 -2
- package/dist/theme/syntax/tokens.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.js +25 -4
- package/dist/utils/plainDate.d.ts +12 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +63 -10
- package/locales/en.json +13 -1
- package/locales/pseudo.json +9 -0
- package/package.json +4 -4
- package/src/AlertDialog/AlertDialog.doc.mjs +6 -0
- package/src/AlertDialog/AlertDialog.spec.md +205 -0
- package/src/AlertDialog/AlertDialog.test.tsx +3 -14
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +115 -0
- package/src/AlertDialog/__tests__/AlertDialog.a11y.renders.tsx +52 -0
- package/src/AlertDialog/__tests__/AlertDialog.a11y.states.ts +68 -0
- package/src/AlertDialog/__tests__/AlertDialog.a11y.test.tsx +144 -0
- package/src/Button/Button.test.tsx +37 -0
- package/src/Button/Button.tsx +5 -1
- package/src/Calendar/Calendar.doc.mjs +234 -60
- package/src/Calendar/Calendar.spec.md +209 -0
- package/src/Calendar/Calendar.test.tsx +559 -20
- package/src/Calendar/Calendar.tsx +258 -41
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +126 -0
- package/src/Calendar/__tests__/Calendar.a11y.known-failures.ts +12 -0
- package/src/Calendar/__tests__/Calendar.a11y.renders.tsx +57 -0
- package/src/Calendar/__tests__/Calendar.a11y.states.ts +139 -0
- package/src/Calendar/__tests__/Calendar.a11y.test.tsx +137 -0
- package/src/Carousel/Carousel.test.tsx +54 -71
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +192 -0
- package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +27 -0
- package/src/Carousel/__tests__/Carousel.a11y.renders.tsx +39 -0
- package/src/Carousel/__tests__/Carousel.a11y.states.ts +139 -0
- package/src/Carousel/__tests__/Carousel.a11y.test.tsx +127 -0
- package/src/Chat/ChatComposerInput.doc.mjs +1 -1
- package/src/Chat/ChatComposerInput.spec.md +94 -55
- package/src/Chat/ChatComposerInput.test.tsx +202 -1
- package/src/Chat/ChatComposerInput.tsx +13 -0
- package/src/Chat/ChatMessage.doc.mjs +31 -16
- package/src/Chat/ChatMessage.spec.md +265 -0
- package/src/Chat/ChatMessage.test.tsx +246 -0
- package/src/Chat/ChatMessage.tsx +30 -7
- package/src/Chat/ChatSystemMessage.spec.md +36 -37
- package/src/Chat/ChatSystemMessage.test.tsx +4 -4
- package/src/Chat/useTriggerMenu.test.tsx +994 -0
- package/src/Chat/useTriggerMenu.tsx +278 -46
- package/src/CodeBlock/CodeBlock.doc.mjs +7 -0
- package/src/CodeBlock/CodeBlock.spec.md +40 -41
- package/src/CodeBlock/CodeBlock.test.tsx +89 -9
- package/src/CodeBlock/CodeBlock.tsx +205 -25
- package/src/CodeBlock/__tests__/CodeBlockOverflowStop.a11y.chromium.spec.ts +125 -0
- package/src/CodeBlock/__tests__/DiffRowPaint.a11y.chromium.spec.ts +93 -0
- package/src/CodeBlock/tokenizer.test.ts +112 -0
- package/src/CodeBlock/tokenizer.ts +106 -0
- package/src/Collapsible/CollapsibleGroup.spec.md +17 -8
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.renders.tsx +45 -0
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.states.ts +139 -0
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.test.tsx +135 -0
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +128 -0
- package/src/CommandPalette/CommandPalette.doc.mjs +126 -23
- package/src/CommandPalette/CommandPalette.spec.md +100 -49
- package/src/CommandPalette/CommandPalette.test.tsx +241 -2
- package/src/CommandPalette/CommandPalette.tsx +47 -21
- package/src/CommandPalette/CommandPaletteEmpty.tsx +4 -3
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +61 -4
- package/src/CommandPalette/CommandPaletteInput.spec.md +215 -0
- package/src/CommandPalette/CommandPaletteInput.test.tsx +375 -51
- package/src/CommandPalette/CommandPaletteInput.tsx +74 -21
- package/src/CommandPalette/CommandPaletteItem.doc.mjs +73 -22
- package/src/CommandPalette/CommandPaletteItem.spec.md +223 -0
- package/src/CommandPalette/CommandPaletteItem.test.tsx +200 -5
- package/src/CommandPalette/CommandPaletteItem.tsx +16 -11
- package/src/CommandPalette/CommandPaletteList.doc.mjs +3 -3
- package/src/CommandPalette/CommandPaletteList.spec.md +41 -36
- package/src/CommandPalette/CommandPaletteList.test.tsx +80 -0
- package/src/CommandPalette/CommandPaletteList.tsx +10 -5
- package/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts +599 -0
- package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -8
- package/src/ComplexSelector/ComplexSelector.spec.md +14 -9
- package/src/ComplexSelector/ComplexSelector.test.tsx +64 -16
- package/src/ComplexSelector/ComplexSelector.tsx +37 -8
- package/src/ContextMenu/ContextMenu.doc.mjs +6 -0
- package/src/ContextMenu/ContextMenu.spec.md +51 -53
- package/src/ContextMenu/ContextMenu.test.tsx +132 -1
- package/src/ContextMenu/ContextMenu.tsx +78 -48
- package/src/DateInput/DateInput.doc.mjs +52 -0
- package/src/DateInput/DateInput.spec.md +27 -15
- package/src/DateInput/DateInput.test.tsx +38 -0
- package/src/DateInput/DateInput.tsx +1 -1
- package/src/DateInput/DateInputTouch.test.tsx +22 -0
- package/src/DateInput/MonthScroller.tsx +12 -4
- package/src/DateRangeInput/DateRangeInput.test.tsx +49 -0
- package/src/DateRangeInput/DateRangeInput.tsx +20 -2
- package/src/Dialog/Dialog.test.tsx +27 -1
- package/src/Dialog/Dialog.tsx +16 -5
- package/src/Dialog/DialogHeader.test.tsx +27 -2
- package/src/Dialog/DialogHeader.tsx +7 -8
- package/src/Dialog/__tests__/Dialog.a11y.chromium.spec.ts +21 -14
- package/src/Divider/Divider.doc.mjs +5 -3
- package/src/Divider/Divider.spec.md +140 -54
- package/src/Divider/Divider.test.tsx +15 -0
- package/src/Divider/Divider.tsx +6 -4
- package/src/DropdownMenu/DropdownMenu.doc.mjs +2 -2
- package/src/DropdownMenu/DropdownMenu.spec.md +121 -45
- package/src/DropdownMenu/DropdownMenu.test.tsx +288 -23
- package/src/DropdownMenu/DropdownMenu.tsx +212 -81
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +38 -2
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +32 -18
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +146 -1
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +136 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.known-failures.ts +12 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.renders.tsx +67 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.states.ts +141 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.test.tsx +138 -0
- package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +40 -5
- package/src/Field/InputClearButton.test.tsx +29 -4
- package/src/Field/InputClearButton.tsx +26 -8
- package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +21 -14
- package/src/FormLayout/FormLayout.tsx +1 -0
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +1 -1
- package/src/Grid/Grid.doc.mjs +14 -11
- package/src/Grid/Grid.spec.md +50 -32
- package/src/Grid/Grid.tsx +13 -4
- package/src/HoverCard/HoverCard.test.tsx +46 -1
- package/src/HoverCard/useHoverCard.tsx +68 -48
- package/src/InputGroup/InputGroup.doc.mjs +11 -1
- package/src/InputGroup/InputGroup.tsx +2 -4
- package/src/InputGroup/groupStyles.ts +40 -2
- package/src/Layer/useLayerDismissal.ts +9 -9
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +142 -0
- package/src/Link/__tests__/Link.a11y.renders.tsx +97 -0
- package/src/Link/__tests__/Link.a11y.states.ts +104 -0
- package/src/Link/__tests__/Link.a11y.test.tsx +127 -0
- package/src/Markdown/Markdown.doc.mjs +1 -1
- package/src/Markdown/Markdown.fr23.perf.test.ts +161 -40
- package/src/Markdown/Markdown.spec.md +45 -23
- package/src/Markdown/Markdown.test.tsx +38 -0
- package/src/Markdown/Markdown.tsx +24 -2
- package/src/Markdown/plugins/semanticFence.test.tsx +3 -2
- package/src/MobileNav/MobileNav.tsx +50 -2
- package/src/MobileNav/MobileNavFocus.test.tsx +553 -0
- package/src/MoreMenu/MoreMenu.spec.md +18 -7
- package/src/MoreMenu/MoreMenu.test.tsx +38 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +8 -8
- package/src/MultiSelector/MultiSelector.spec.md +1 -1
- package/src/MultiSelector/MultiSelector.test.tsx +188 -21
- package/src/MultiSelector/MultiSelector.tsx +50 -11
- package/src/NumberInput/NumberInput.test.tsx +24 -0
- package/src/NumberInput/NumberInput.tsx +1 -1
- package/src/NumberInput/__tests__/NumberInput.a11y.chromium.spec.ts +22 -15
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +78 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +6 -0
- package/src/Resizable/Resizable.spec.md +41 -9
- package/src/Resizable/ResizeHandle.test.tsx +565 -2
- package/src/Resizable/ResizeHandle.tsx +126 -21
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +119 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.known-failures.ts +39 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.renders.tsx +80 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.states.ts +197 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.test.tsx +123 -0
- package/src/Resizable/useResizable.ts +95 -0
- package/src/Selector/Selector.doc.mjs +7 -6
- package/src/Selector/Selector.spec.md +37 -24
- package/src/Selector/Selector.test.tsx +176 -10
- package/src/Selector/Selector.tsx +59 -14
- package/src/Selector/__tests__/Combobox.a11y.known-failures.ts +27 -0
- package/src/Selector/__tests__/Combobox.a11y.states.ts +145 -0
- package/src/Selector/__tests__/Selector.combobox.a11y.chromium.spec.ts +108 -0
- package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +126 -0
- package/src/SideNav/SideNav.test.tsx +35 -0
- package/src/SideNav/SideNav.tsx +16 -2
- package/src/Slider/Slider.spec.md +43 -17
- package/src/Slider/Slider.test.tsx +134 -0
- package/src/Slider/Slider.tsx +29 -6
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +112 -0
- package/src/Slider/__tests__/Slider.a11y.known-failures.ts +12 -0
- package/src/Slider/__tests__/Slider.a11y.renders.tsx +42 -0
- package/src/Slider/__tests__/Slider.a11y.states.ts +106 -0
- package/src/Slider/__tests__/Slider.a11y.test.tsx +105 -0
- package/src/Switch/__tests__/Switch.a11y.chromium.spec.ts +25 -18
- package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +19 -12
- package/src/TextInput/__tests__/TextInput.a11y.chromium.spec.ts +21 -14
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +18 -11
- package/src/Tokenizer/Tokenizer.test.tsx +87 -0
- package/src/Tokenizer/Tokenizer.tsx +31 -3
- package/src/Toolbar/Toolbar.test.tsx +131 -4
- package/src/Toolbar/Toolbar.tsx +3 -1
- package/src/TreeList/TreeList.spec.md +33 -8
- package/src/TreeList/TreeList.test.tsx +72 -0
- package/src/TreeList/TreeList.tsx +10 -6
- package/src/TreeList/TreeListItem.tsx +5 -0
- package/src/TreeList/__tests__/TreeList.a11y.chromium.spec.ts +125 -0
- package/src/TreeList/__tests__/TreeList.a11y.renders.tsx +44 -0
- package/src/TreeList/__tests__/TreeList.a11y.states.ts +164 -0
- package/src/TreeList/__tests__/TreeList.a11y.test.tsx +138 -0
- package/src/TreeList/__tests__/TreeListRowNames.a11y.chromium.spec.ts +82 -0
- package/src/hooks/useScrollOverflow.test.tsx +222 -0
- package/src/hooks/useScrollOverflow.ts +55 -13
- package/src/hooks/useTreeFocus.test.tsx +37 -0
- package/src/hooks/useTreeFocus.ts +55 -2
- package/src/i18n/generated-locales/en.generated.ts +3 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +3 -0
- package/src/reset.a11y.chromium.spec.ts +9 -1
- package/src/reset.css +8 -3
- package/src/reset.until-found.a11y.browser.spec.ts +75 -0
- package/src/reset.until-found.test.ts +32 -0
- package/src/stylexValidation.source-build.test.mjs +42 -0
- package/src/theme/syntax/SyntaxTheme.tsx +46 -5
- package/src/theme/syntax/defineSyntaxTheme.diff.test.ts +91 -0
- package/src/theme/syntax/defineSyntaxTheme.ts +75 -15
- package/src/theme/syntax/index.ts +5 -1
- package/src/theme/syntax/tokens.ts +32 -2
- package/src/theme/syntax/useSyntaxTheme.diff.test.tsx +40 -0
- package/src/utils/plainDate.test.ts +147 -0
- package/src/utils/plainDate.ts +75 -10
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @file ContextMenu.tsx
|
|
6
|
-
* @input Uses React, StyleX, useLayer,
|
|
7
|
-
* shared menu-presentation resolver
|
|
6
|
+
* @input Uses React, StyleX, useLayer, shared layer dismissal, BottomSheet,
|
|
7
|
+
* useListFocus, and the shared menu-presentation resolver
|
|
8
8
|
* @output Exports ContextMenu with cursor-popover and touch-sheet presentations
|
|
9
9
|
* @position Core implementation; consumed by index.ts
|
|
10
10
|
*
|
|
@@ -50,6 +50,8 @@ import {Button} from '../Button';
|
|
|
50
50
|
import {Heading} from '../Heading';
|
|
51
51
|
import {Icon} from '../Icon';
|
|
52
52
|
import {useLayer} from '../Layer/useLayer';
|
|
53
|
+
import {LayerDepthProvider} from '../Layer/LayerDepthContext';
|
|
54
|
+
import {useLayerDismissal} from '../Layer/useLayerDismissal';
|
|
53
55
|
import {MenuBottomSheetActionList} from '../DropdownMenu/MenuBottomSheetActionList';
|
|
54
56
|
import {renderDropdownItems} from '../DropdownMenu/renderDropdownItems';
|
|
55
57
|
import {
|
|
@@ -77,7 +79,7 @@ import {
|
|
|
77
79
|
easeVars,
|
|
78
80
|
shadowVars,
|
|
79
81
|
} from '../theme/tokens.stylex';
|
|
80
|
-
import {mergeProps,
|
|
82
|
+
import {mergeProps, rtlStyles} from '../utils';
|
|
81
83
|
import type {BaseProps} from '../BaseProps';
|
|
82
84
|
import type {StyleXStyles} from '../theme/types';
|
|
83
85
|
import {themeProps} from '../utils/themeProps';
|
|
@@ -313,6 +315,11 @@ export function ContextMenu({
|
|
|
313
315
|
style,
|
|
314
316
|
xstyle,
|
|
315
317
|
triggerXstyle,
|
|
318
|
+
onContextMenu: onContextMenuProp,
|
|
319
|
+
onTouchStart: onTouchStartProp,
|
|
320
|
+
onTouchMove: onTouchMoveProp,
|
|
321
|
+
onTouchEnd: onTouchEndProp,
|
|
322
|
+
onTouchCancel: onTouchCancelProp,
|
|
316
323
|
'data-testid': testId,
|
|
317
324
|
...rest
|
|
318
325
|
}: ContextMenuProps) {
|
|
@@ -346,7 +353,6 @@ export function ContextMenu({
|
|
|
346
353
|
// Element focused before the menu opened, restored when it closes so focus
|
|
347
354
|
// does not fall to <body> after Escape or outside-click dismissal.
|
|
348
355
|
const triggerFocusRef = useRef<HTMLElement | null>(null);
|
|
349
|
-
const sheetHeadingRef = useRef<HTMLHeadingElement>(null);
|
|
350
356
|
|
|
351
357
|
const [isOpen, setIsOpen] = useState(false);
|
|
352
358
|
const [submenuPath, setSubmenuPath] = useState<ContextMenuItemData[]>([]);
|
|
@@ -406,15 +412,14 @@ export function ContextMenu({
|
|
|
406
412
|
[closeMenu],
|
|
407
413
|
);
|
|
408
414
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}, [isOpen, submenuPath.length]);
|
|
415
|
+
const setSheetHeadingRef = useCallback(
|
|
416
|
+
(node: HTMLHeadingElement | null) => {
|
|
417
|
+
if (node && isOpen && submenuPath.length > 0) {
|
|
418
|
+
node.focus({preventScroll: true});
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
[isOpen, submenuPath.length],
|
|
422
|
+
);
|
|
418
423
|
|
|
419
424
|
const {
|
|
420
425
|
listRef,
|
|
@@ -429,7 +434,14 @@ export function ContextMenu({
|
|
|
429
434
|
// Menus wrap from the last row to the first and back.
|
|
430
435
|
wrap: true,
|
|
431
436
|
hasPaging: true,
|
|
432
|
-
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Join the shared stack so an Escape press dismisses exactly one top-most
|
|
440
|
+
// surface. BottomSheet owns its own dismissal path.
|
|
441
|
+
useLayerDismissal({
|
|
442
|
+
isActive: isOpen && !usesBottomSheet,
|
|
443
|
+
onDismiss: closeMenu,
|
|
444
|
+
getContainer: () => listRef.current,
|
|
433
445
|
});
|
|
434
446
|
|
|
435
447
|
// Typeahead over the enabled menu items, matched on each row's LABEL alone.
|
|
@@ -465,32 +477,6 @@ export function ContextMenu({
|
|
|
465
477
|
};
|
|
466
478
|
}, [isOpen, closeMenu, listRef, usesBottomSheet]);
|
|
467
479
|
|
|
468
|
-
// Dismiss on Escape from anywhere while open. The menu div's own onKeyDown
|
|
469
|
-
// only fires when focus is inside the menu; a document-level listener is
|
|
470
|
-
// kept as a reliable fallback Escape path (e.g. if focus has moved out of
|
|
471
|
-
// the menu). Guards against IME composition-cancel.
|
|
472
|
-
useEffect(() => {
|
|
473
|
-
if (!isOpen || usesBottomSheet) {
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
const handleEscape = (e: KeyboardEvent) => {
|
|
477
|
-
if (e.key !== 'Escape') {
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
if (isImeKeyEvent(e)) {
|
|
481
|
-
// Ignore Escape that is committing/cancelling an IME composition;
|
|
482
|
-
// see utils/ime.ts for why.
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
e.preventDefault();
|
|
486
|
-
closeMenu();
|
|
487
|
-
};
|
|
488
|
-
document.addEventListener('keydown', handleEscape);
|
|
489
|
-
return () => {
|
|
490
|
-
document.removeEventListener('keydown', handleEscape);
|
|
491
|
-
};
|
|
492
|
-
}, [isOpen, closeMenu, usesBottomSheet]);
|
|
493
|
-
|
|
494
480
|
const listKeyDown = useCallback(
|
|
495
481
|
(e: React.KeyboardEvent) => {
|
|
496
482
|
// A submenu flyout renders inline inside this menu; its key events bubble
|
|
@@ -601,7 +587,12 @@ export function ContextMenu({
|
|
|
601
587
|
// held long enough (see useLongPress for timer/move-cancel/cleanup logic).
|
|
602
588
|
// The hook also returns the imperative start/moveTo/cancel API the menu press
|
|
603
589
|
// model drives; only its four touch handlers belong on the trigger element.
|
|
604
|
-
const
|
|
590
|
+
const {
|
|
591
|
+
onTouchStart: handleLongPressTouchStart,
|
|
592
|
+
onTouchMove: handleLongPressTouchMove,
|
|
593
|
+
onTouchEnd: handleLongPressTouchEnd,
|
|
594
|
+
onTouchCancel: handleLongPressTouchCancel,
|
|
595
|
+
} = useLongPress({
|
|
605
596
|
disabled: isDisabled,
|
|
606
597
|
onLongPress: useCallback(
|
|
607
598
|
(point: {x: number; y: number}) => {
|
|
@@ -616,6 +607,45 @@ export function ContextMenu({
|
|
|
616
607
|
),
|
|
617
608
|
});
|
|
618
609
|
|
|
610
|
+
// ContextMenu owns invocation and long-press cleanup. Consumer handlers are
|
|
611
|
+
// notified too, but preventing their default does not cancel component-owned
|
|
612
|
+
// behavior that the public API does not promise as cancelable.
|
|
613
|
+
const handleTriggerContextMenu = useCallback(
|
|
614
|
+
(event: React.MouseEvent<HTMLElement>) => {
|
|
615
|
+
handleContextMenu(event);
|
|
616
|
+
onContextMenuProp?.(event);
|
|
617
|
+
},
|
|
618
|
+
[handleContextMenu, onContextMenuProp],
|
|
619
|
+
);
|
|
620
|
+
const handleTriggerTouchStart = useCallback(
|
|
621
|
+
(event: React.TouchEvent<HTMLElement>) => {
|
|
622
|
+
handleLongPressTouchStart(event);
|
|
623
|
+
onTouchStartProp?.(event);
|
|
624
|
+
},
|
|
625
|
+
[handleLongPressTouchStart, onTouchStartProp],
|
|
626
|
+
);
|
|
627
|
+
const handleTriggerTouchMove = useCallback(
|
|
628
|
+
(event: React.TouchEvent<HTMLElement>) => {
|
|
629
|
+
handleLongPressTouchMove(event);
|
|
630
|
+
onTouchMoveProp?.(event);
|
|
631
|
+
},
|
|
632
|
+
[handleLongPressTouchMove, onTouchMoveProp],
|
|
633
|
+
);
|
|
634
|
+
const handleTriggerTouchEnd = useCallback(
|
|
635
|
+
(event: React.TouchEvent<HTMLElement>) => {
|
|
636
|
+
handleLongPressTouchEnd();
|
|
637
|
+
onTouchEndProp?.(event);
|
|
638
|
+
},
|
|
639
|
+
[handleLongPressTouchEnd, onTouchEndProp],
|
|
640
|
+
);
|
|
641
|
+
const handleTriggerTouchCancel = useCallback(
|
|
642
|
+
(event: React.TouchEvent<HTMLElement>) => {
|
|
643
|
+
handleLongPressTouchCancel();
|
|
644
|
+
onTouchCancelProp?.(event);
|
|
645
|
+
},
|
|
646
|
+
[handleLongPressTouchCancel, onTouchCancelProp],
|
|
647
|
+
);
|
|
648
|
+
|
|
619
649
|
const popoverXstyle = menuWidth
|
|
620
650
|
? styles.popoverCustomWidth(menuWidth)
|
|
621
651
|
: styles.popover;
|
|
@@ -702,7 +732,7 @@ export function ContextMenu({
|
|
|
702
732
|
/>
|
|
703
733
|
)}
|
|
704
734
|
<Heading
|
|
705
|
-
ref={
|
|
735
|
+
ref={setSheetHeadingRef}
|
|
706
736
|
level={3}
|
|
707
737
|
tabIndex={-1}
|
|
708
738
|
xstyle={[
|
|
@@ -731,11 +761,11 @@ export function ContextMenu({
|
|
|
731
761
|
<TriggerElement
|
|
732
762
|
ref={useMergedRefs(ref, triggerRef)}
|
|
733
763
|
{...triggerProps}
|
|
734
|
-
onContextMenu={
|
|
735
|
-
onTouchStart={
|
|
736
|
-
onTouchMove={
|
|
737
|
-
onTouchEnd={
|
|
738
|
-
onTouchCancel={
|
|
764
|
+
onContextMenu={handleTriggerContextMenu}
|
|
765
|
+
onTouchStart={handleTriggerTouchStart}
|
|
766
|
+
onTouchMove={handleTriggerTouchMove}
|
|
767
|
+
onTouchEnd={handleTriggerTouchEnd}
|
|
768
|
+
onTouchCancel={handleTriggerTouchCancel}
|
|
739
769
|
data-testid={testId}
|
|
740
770
|
{...stylex.props(
|
|
741
771
|
styles.trigger,
|
|
@@ -768,7 +798,7 @@ export function ContextMenu({
|
|
|
768
798
|
</LazyMenuBottomSheet>
|
|
769
799
|
</Suspense>
|
|
770
800
|
) : (
|
|
771
|
-
layer.render(renderedMenu
|
|
801
|
+
layer.render(<LayerDepthProvider>{renderedMenu}</LayerDepthProvider>, {
|
|
772
802
|
placement: 'below',
|
|
773
803
|
alignment: 'start',
|
|
774
804
|
xstyle: [popoverXstyle, layerAnimations.below],
|
|
@@ -182,12 +182,36 @@ export const docs = {
|
|
|
182
182
|
description:
|
|
183
183
|
'StyleX styles for the Field box around the label, control, and status. containerClassName and containerStyle take a class name and inline styles for the same box. No effect (development warning) where no Field box renders: inside InputGroup (style the group) and in a horizontal-labels FormLayout.',
|
|
184
184
|
},
|
|
185
|
+
{
|
|
186
|
+
name: 'containerClassName',
|
|
187
|
+
type: 'string',
|
|
188
|
+
description:
|
|
189
|
+
'Class name for the Field box. Same targeting as containerXstyle.',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'containerStyle',
|
|
193
|
+
type: 'CSSProperties',
|
|
194
|
+
description:
|
|
195
|
+
'Inline styles for the Field box. Same targeting as containerXstyle.',
|
|
196
|
+
},
|
|
185
197
|
{
|
|
186
198
|
name: 'controlXstyle',
|
|
187
199
|
type: 'StyleXStyles',
|
|
188
200
|
description:
|
|
189
201
|
'StyleX styles for the visual control box: the bordered element that holds the text, icons, and end controls, never the native input. controlClassName and controlStyle take a class name and inline styles for the same box. Replaces root xstyle, className, and style, which this component does not accept.',
|
|
190
202
|
},
|
|
203
|
+
{
|
|
204
|
+
name: 'controlClassName',
|
|
205
|
+
type: 'string',
|
|
206
|
+
description:
|
|
207
|
+
'Class name for the visual control box. Same targeting as controlXstyle.',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'controlStyle',
|
|
211
|
+
type: 'CSSProperties',
|
|
212
|
+
description:
|
|
213
|
+
'Inline styles for the visual control box. Same targeting as controlXstyle.',
|
|
214
|
+
},
|
|
191
215
|
],
|
|
192
216
|
theming: {
|
|
193
217
|
targets: [
|
|
@@ -503,12 +527,32 @@ export const docsZh = {
|
|
|
503
527
|
description:
|
|
504
528
|
'作用于包含标签、控件和状态的 Field 容器的 StyleX 样式;containerClassName 与 containerStyle 以类名和内联样式作用于同一容器。没有 Field 容器时(InputGroup 内或 horizontal-labels FormLayout 中)不生效,并在开发环境发出警告。',
|
|
505
529
|
},
|
|
530
|
+
{
|
|
531
|
+
name: 'containerClassName',
|
|
532
|
+
type: 'string',
|
|
533
|
+
description: 'Field 容器的类名,作用目标同 containerXstyle。',
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: 'containerStyle',
|
|
537
|
+
type: 'CSSProperties',
|
|
538
|
+
description: 'Field 容器的内联样式,作用目标同 containerXstyle。',
|
|
539
|
+
},
|
|
506
540
|
{
|
|
507
541
|
name: 'controlXstyle',
|
|
508
542
|
type: 'StyleXStyles',
|
|
509
543
|
description:
|
|
510
544
|
'作用于可见控件框(带边框、包含文本、图标和末端控件的元素,而非原生 input)的 StyleX 样式;controlClassName 与 controlStyle 以类名和内联样式作用于同一元素。本组件不接受根级 xstyle、className 和 style。',
|
|
511
545
|
},
|
|
546
|
+
{
|
|
547
|
+
name: 'controlClassName',
|
|
548
|
+
type: 'string',
|
|
549
|
+
description: '可见控件框的类名,作用目标同 controlXstyle。',
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
name: 'controlStyle',
|
|
553
|
+
type: 'CSSProperties',
|
|
554
|
+
description: '可见控件框的内联样式,作用目标同 controlXstyle。',
|
|
555
|
+
},
|
|
512
556
|
],
|
|
513
557
|
theming: {
|
|
514
558
|
targets: [
|
|
@@ -615,7 +659,15 @@ export const docsDense = {
|
|
|
615
659
|
"deprecated, use presentation: 'touch'='adaptive-native', 'always'='native', 'never'='adaptive-bottom-sheet'. still works as released; presentation wins if both set.",
|
|
616
660
|
containerXstyle:
|
|
617
661
|
'Field box (label+control+status); also containerClassName/containerStyle. no effect in InputGroup or horizontal-labels FormLayout',
|
|
662
|
+
containerClassName:
|
|
663
|
+
'class name for the Field box; same target as containerXstyle',
|
|
664
|
+
containerStyle:
|
|
665
|
+
'inline styles for the Field box; same target as containerXstyle',
|
|
618
666
|
controlXstyle:
|
|
619
667
|
'visual control box (border/bg/icons), never the native input; also controlClassName/controlStyle. no root xstyle/className/style',
|
|
668
|
+
controlClassName:
|
|
669
|
+
'class name for the visual control box; same target as controlXstyle',
|
|
670
|
+
controlStyle:
|
|
671
|
+
'inline styles for the visual control box; same target as controlXstyle',
|
|
620
672
|
},
|
|
621
673
|
};
|
|
@@ -13,6 +13,7 @@ review_triggers: [public-api, behavior, layout, theming, accessibility]
|
|
|
13
13
|
verified_by:
|
|
14
14
|
[
|
|
15
15
|
packages/core/src/DateInput/DateInput.test.tsx,
|
|
16
|
+
packages/core/src/DateInput/DateInput.fr10.test.tsx,
|
|
16
17
|
packages/core/src/DateInput/DateInputTouch.test.tsx,
|
|
17
18
|
packages/core/src/DateInput/NativeDateField.test.tsx,
|
|
18
19
|
packages/core/src/DateInput/Presentation.test.tsx,
|
|
@@ -48,7 +49,7 @@ compatibility promise, ownership boundary, or visual direction.
|
|
|
48
49
|
| Builder impact | Existing native DateInput `changeAction` handlers now run after `onChange` and receive optimistic busy feedback. No API or migration work is required. |
|
|
49
50
|
| Compatibility/readiness | Compatible patch correction under the current input-field family rule. Released defaults and controlled ownership are preserved; exact-head browser evidence remains audit evidence rather than product authority. |
|
|
50
51
|
| Review checks | Reject a surface-mapping change, lost constraint, callback-order change, unlabeled field, unreachable disabled reason, changed close behavior, or a theming target placed on the wrong owner. |
|
|
51
|
-
| Governing rules | `spec:AST-043/FR1–FR5`; `family:input-fields/FR1–FR6, FR8–
|
|
52
|
+
| Governing rules | `spec:AST-043/FR1–FR5`; `family:input-fields/FR1–FR6, FR8–FR10`; `family:overlay-dismissal/FR1–FR7`, including its recorded BottomSheet adoption gap; the three architecture records linked above. |
|
|
52
53
|
|
|
53
54
|
This table is a review projection; the body below is authoritative only after this
|
|
54
55
|
record becomes `current`.
|
|
@@ -211,6 +212,15 @@ approved policy.
|
|
|
211
212
|
- **AR7 — Directionality.** DateInput's own calendar and clear controls exchange logical
|
|
212
213
|
inline sides in RTL while preserving their labels and meanings; Calendar navigation
|
|
213
214
|
remains owned and verified by `component:Calendar`.
|
|
215
|
+
- **AR8 — The month scroller is an APG grid** (`spec:AST-020/FR15`), as one
|
|
216
|
+
composite control: each mounted month pane of the touch calendar's scroller
|
|
217
|
+
is a `role="grid"` named for its month, with rows of `role="gridcell"` days,
|
|
218
|
+
at most one day per pane holding a Tab stop,
|
|
219
|
+
`aria-selected` on the chosen day; ArrowLeft and ArrowRight move by a day
|
|
220
|
+
following the reading direction, ArrowUp and ArrowDown by a week, Home and End
|
|
221
|
+
to the ends of the week, PageUp and PageDown by a month, and Enter or Space
|
|
222
|
+
picks the focused day. Implementation gap: PageUp/PageDown do not page by a
|
|
223
|
+
month.
|
|
214
224
|
|
|
215
225
|
## Design relationships
|
|
216
226
|
|
|
@@ -249,14 +259,15 @@ approved policy.
|
|
|
249
259
|
|
|
250
260
|
The `date-input` target owns the composite field surface. The
|
|
251
261
|
`date-input-toggle-icon` target reflects collapsed and expanded states on the icon
|
|
252
|
-
itself. The
|
|
253
|
-
|
|
262
|
+
itself. The shared `input-clear-icon` target owns clear-glyph theming; DateInput adds no
|
|
263
|
+
clear-glyph target of its own.
|
|
254
264
|
|
|
255
265
|
## Family and system relationships
|
|
256
266
|
|
|
257
267
|
- `family:input-fields` owns stable field sizing, end-control space, grouped
|
|
258
|
-
adaptation, disabled reasons, loading, Transition Actions, status placement,
|
|
259
|
-
the iOS-only focus-zoom floor; DateInput
|
|
268
|
+
adaptation, disabled reasons, loading, Transition Actions, status placement,
|
|
269
|
+
base-prop and part-styling targets, and the iOS-only focus-zoom floor; DateInput
|
|
270
|
+
adopts every listed concept.
|
|
260
271
|
- `family:overlay-dismissal` owns topmost Popover and BottomSheet dismissal. DateInput's
|
|
261
272
|
Popover path uses the shared owner; its bottom-sheet path inherits BottomSheet's
|
|
262
273
|
recorded adoption gap. DateInput owns only selection, open-state requests, and focus
|
|
@@ -271,16 +282,17 @@ the shared `input-clear-icon` target owns current clear-glyph theming.
|
|
|
271
282
|
|
|
272
283
|
## Verification map
|
|
273
284
|
|
|
274
|
-
| Contract | Verification
|
|
275
|
-
| ---------------------------------------------------- |
|
|
276
|
-
| FR1, FR5–FR8, FR10–FR12; AR1–AR4 | `DateInput.test.tsx`
|
|
277
|
-
| FR2–FR4 | `Presentation.test.tsx`, `NativeDateField.test.tsx`, `DateInputTouch.test.tsx`
|
|
278
|
-
| FR5–FR7; AR1, AR3–AR5 | `NativeDateField.test.tsx`
|
|
279
|
-
| FR1, FR4–FR7, FR9, FR11–FR12; AR1, AR4, AR6; PR1–PR2 | `DateInputTouch.test.tsx`
|
|
280
|
-
| FR2–FR3, FR6 | `DateInput.doc.mjs`, Storybook controls, `scripts/check-knowledge.mjs`
|
|
281
|
-
| Theming anatomy | `DateInput.test.tsx`, `themingTargets.test.ts`, generated probe theme
|
|
282
|
-
| FR8; AR2 | `apps/storybook/stories/DateInput.stories.tsx`, component-scoped axe
|
|
283
|
-
| AR7 | `apps/storybook/rtl-audit/targets.json`, component RTL audit
|
|
285
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
286
|
+
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
|
|
287
|
+
| FR1, FR5–FR8, FR10–FR12; AR1–AR4 | `DateInput.test.tsx` | empty, valued, typing, invalid, constrained, open, disabled, busy, clear, status, grouped | Value, callbacks, ARIA, disabled guard, focus, or shared-field composition drifts | `audit:DateInput/pointer-field` |
|
|
288
|
+
| FR2–FR4 | `Presentation.test.tsx`, `NativeDateField.test.tsx`, `DateInputTouch.test.tsx` | every presentation, omitted props, every deprecated mapping, dual props, fine/coarse, touchscreen laptop | A value resolves to the wrong surface, width affects modality, or legacy behavior wins incorrectly | `audit:DateInput/presentation` |
|
|
289
|
+
| FR5–FR7; AR1, AR3–AR5 | `NativeDateField.test.tsx` | empty, valued, focused, constrained, disabled, clear, formatted, locale | Native commit, overlay, label, constraint, or focus reconciliation drifts | `audit:DateInput/native` |
|
|
290
|
+
| FR1, FR4–FR7, FR9, FR11–FR12; AR1, AR4, AR6; PR1–PR2 | `DateInputTouch.test.tsx` | closed/open, month grid, selection correction, wheels, bounds, keyboard, swipe, mouse drag, clear, busy | Touch value parity, bounded rendering, focus, semantics, or gesture ownership drifts | `audit:DateInput/touch` |
|
|
291
|
+
| FR2–FR3, FR6 | `DateInput.doc.mjs`, Storybook controls, `scripts/check-knowledge.mjs` | five presentation values, deprecated prop, named formats, English and Chinese docs | Source, docs, types, or default mappings disagree | `audit:DateInput/docs-api` |
|
|
292
|
+
| Theming anatomy | `DateInput.test.tsx`, `themingTargets.test.ts`, generated probe theme | root size/status/disabled, toggle state, shared clear target | Target class or state is missing, undocumented, or placed on the wrong element | `audit:DateInput/theming` |
|
|
293
|
+
| FR8; AR2 | `apps/storybook/stories/DateInput.stories.tsx`, component-scoped axe | open Calendar dialog plus closed field states | Important popup semantics stay unreachable to the browser audit | `audit:DateInput/a11y` |
|
|
294
|
+
| AR7 | `apps/storybook/rtl-audit/targets.json`, component RTL audit | calendar and clear controls in LTR and RTL | DateInput-owned controls fail to exchange logical inline order | `audit:DateInput/rtl` |
|
|
295
|
+
| AR8 | `DateInputTouch.test.tsx` calendar-surface suite ("mounts a window of months around the visible one, not the whole century" for the named grids, "moves keyboard focus by date, across the month boundary", "moves a week at a time with the vertical arrows", "leaves exactly one day per month tab-reachable", "gives a spilled date no tab stop in the pane that borrows it", "disables days outside min/max and refuses to commit them", "follows the reading direction under RTL: ArrowLeft moves to the next day", "moves keyboard focus by date, across the month boundary" (the real day, not the spill copy)); ArrowUp, Home/End, Page and activation keys under the Grid accessibility contract (`internal/a11y-spec`, pending) | LTR and RTL; month boundary; selected day; today | A day loses its gridcell role or selection, two days in one pane hold a Tab stop, or an arrow, Home/End, Page or activation key stops moving or picking | `audit:DateInput/accessibility` |
|
|
284
296
|
|
|
285
297
|
## Decision log
|
|
286
298
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* SYNC: When DateInput.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import {useState} from 'react';
|
|
12
13
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
13
14
|
import {
|
|
14
15
|
render,
|
|
@@ -27,6 +28,7 @@ import {InputGroupText} from '../InputGroup/InputGroupText';
|
|
|
27
28
|
import {defineTheme} from '../theme/defineTheme';
|
|
28
29
|
import {generateThemeCSS} from '../theme/generateThemeRules';
|
|
29
30
|
import {InternationalizationProvider} from '../i18n';
|
|
31
|
+
import type {ISODateString} from '../utils';
|
|
30
32
|
|
|
31
33
|
function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
|
|
32
34
|
const {prose, component} = generateThemeCSS(theme);
|
|
@@ -593,6 +595,42 @@ describe('DateInput', () => {
|
|
|
593
595
|
// Full popover interaction is tested in the browser via Storybook.
|
|
594
596
|
|
|
595
597
|
describe('hasClear', () => {
|
|
598
|
+
it('shows no selected calendar days after clearing', async () => {
|
|
599
|
+
const user = userEvent.setup();
|
|
600
|
+
|
|
601
|
+
function ControlledDateInput() {
|
|
602
|
+
const [value, setValue] = useState<ISODateString | undefined>(
|
|
603
|
+
'2026-01-01',
|
|
604
|
+
);
|
|
605
|
+
return (
|
|
606
|
+
<DateInput
|
|
607
|
+
label="Date"
|
|
608
|
+
value={value}
|
|
609
|
+
onChange={setValue}
|
|
610
|
+
min="2026-01-01"
|
|
611
|
+
max="2026-01-31"
|
|
612
|
+
presentation="popover"
|
|
613
|
+
hasClear
|
|
614
|
+
/>
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
render(<ControlledDateInput />);
|
|
619
|
+
await user.click(getButton('Open calendar'));
|
|
620
|
+
await user.click(getButton(/January 15, 2026/));
|
|
621
|
+
expect(
|
|
622
|
+
screen.getAllByRole('gridcell', {selected: true, hidden: true}),
|
|
623
|
+
).toHaveLength(1);
|
|
624
|
+
|
|
625
|
+
await user.click(getButton('Clear Date'));
|
|
626
|
+
expect(screen.getByRole('combobox')).toHaveValue('');
|
|
627
|
+
await user.click(getButton('Open calendar'));
|
|
628
|
+
|
|
629
|
+
expect(
|
|
630
|
+
screen.queryAllByRole('gridcell', {selected: true, hidden: true}),
|
|
631
|
+
).toHaveLength(0);
|
|
632
|
+
});
|
|
633
|
+
|
|
596
634
|
it('shows clear button when hasClear is true and value exists', () => {
|
|
597
635
|
render(
|
|
598
636
|
<DateInput
|
|
@@ -1118,6 +1118,28 @@ describe('DateInput — calendar surface', () => {
|
|
|
1118
1118
|
last.focus();
|
|
1119
1119
|
fireEvent.keyDown(last, {key: 'ArrowRight'});
|
|
1120
1120
|
expect(document.activeElement).toHaveAttribute('data-date', '2026-04-01');
|
|
1121
|
+
// The real April 1, not its dimmed spill copy at the foot of the March
|
|
1122
|
+
// pane (which is disabled and selects nothing).
|
|
1123
|
+
expect(pane('April 2026')).toContainElement(
|
|
1124
|
+
document.activeElement as HTMLElement,
|
|
1125
|
+
);
|
|
1126
|
+
expect(document.activeElement).not.toHaveAttribute('aria-disabled');
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
it('follows the reading direction under RTL: ArrowLeft moves to the next day', () => {
|
|
1130
|
+
renderAndOpen(
|
|
1131
|
+
<InternationalizationProvider locale="en-US" dir="rtl">
|
|
1132
|
+
<Controlled initial="2026-03-10" />
|
|
1133
|
+
</InternationalizationProvider>,
|
|
1134
|
+
);
|
|
1135
|
+
const day = within(pane('March 2026')).getByRole('button', {
|
|
1136
|
+
name: /March 10, 2026/,
|
|
1137
|
+
});
|
|
1138
|
+
day.focus();
|
|
1139
|
+
fireEvent.keyDown(day, {key: 'ArrowLeft'});
|
|
1140
|
+
expect(document.activeElement).toHaveAttribute('data-date', '2026-03-11');
|
|
1141
|
+
fireEvent.keyDown(document.activeElement!, {key: 'ArrowRight'});
|
|
1142
|
+
expect(document.activeElement).toHaveAttribute('data-date', '2026-03-10');
|
|
1121
1143
|
});
|
|
1122
1144
|
|
|
1123
1145
|
it('moves a week at a time with the vertical arrows', () => {
|
|
@@ -637,12 +637,16 @@ export function MonthScroller({
|
|
|
637
637
|
// Column within the displayed week, which is what Home/End move to —
|
|
638
638
|
// not the day of the month.
|
|
639
639
|
const column = (plainDateDayOfWeek(date) - weekStartsOn + 7) % 7;
|
|
640
|
+
// The horizontal arrows follow the reading direction (APG grid): under
|
|
641
|
+
// RTL the next day sits to the left.
|
|
642
|
+
const forward = isRTL ? 'ArrowLeft' : 'ArrowRight';
|
|
643
|
+
const backward = isRTL ? 'ArrowRight' : 'ArrowLeft';
|
|
640
644
|
switch (event.key) {
|
|
641
|
-
case
|
|
645
|
+
case backward:
|
|
642
646
|
event.preventDefault();
|
|
643
647
|
moveFocus(date, -1);
|
|
644
648
|
break;
|
|
645
|
-
case
|
|
649
|
+
case forward:
|
|
646
650
|
event.preventDefault();
|
|
647
651
|
moveFocus(date, 1);
|
|
648
652
|
break;
|
|
@@ -666,7 +670,7 @@ export function MonthScroller({
|
|
|
666
670
|
break;
|
|
667
671
|
}
|
|
668
672
|
},
|
|
669
|
-
[moveFocus, weekStartsOn],
|
|
673
|
+
[isRTL, moveFocus, weekStartsOn],
|
|
670
674
|
);
|
|
671
675
|
|
|
672
676
|
// The focused day may have been in an unmounted pane a frame ago; focus it
|
|
@@ -677,8 +681,11 @@ export function MonthScroller({
|
|
|
677
681
|
if (!shouldRestoreFocusRef.current || focusedISO == null) {
|
|
678
682
|
return;
|
|
679
683
|
}
|
|
684
|
+
// A date can be mounted twice: as a real day in its own pane and as a
|
|
685
|
+
// dimmed, disabled spill copy at the edge of a neighbouring pane. Only
|
|
686
|
+
// the real day may take focus, or Enter and Space would select nothing.
|
|
680
687
|
const target = scrollerRef.current?.querySelector<HTMLElement>(
|
|
681
|
-
`[data-date="${focusedISO}"]`,
|
|
688
|
+
`[data-date="${focusedISO}"]:not([data-outside])`,
|
|
682
689
|
);
|
|
683
690
|
if (target != null) {
|
|
684
691
|
shouldRestoreFocusRef.current = false;
|
|
@@ -835,6 +842,7 @@ function MonthPane({
|
|
|
835
842
|
<button
|
|
836
843
|
type="button"
|
|
837
844
|
data-date={day.iso}
|
|
845
|
+
data-outside={day.isOutside ? '' : undefined}
|
|
838
846
|
tabIndex={day.iso === tabbableISO ? 0 : -1}
|
|
839
847
|
aria-label={plainDateFormat(
|
|
840
848
|
day.date,
|
|
@@ -320,6 +320,55 @@ describe('DateRangeInput', () => {
|
|
|
320
320
|
).toHaveLength(0);
|
|
321
321
|
});
|
|
322
322
|
|
|
323
|
+
it('returns focus to the trigger when the clear button is activated by keyboard', async () => {
|
|
324
|
+
const user = userEvent.setup();
|
|
325
|
+
function ControlledDateRangeInput() {
|
|
326
|
+
const [controlledValue, setControlledValue] =
|
|
327
|
+
useState<DateRange | null>({
|
|
328
|
+
start: '2026-01-01',
|
|
329
|
+
end: '2026-01-03',
|
|
330
|
+
});
|
|
331
|
+
return (
|
|
332
|
+
<DateRangeInput
|
|
333
|
+
label="Range"
|
|
334
|
+
value={controlledValue}
|
|
335
|
+
onChange={setControlledValue}
|
|
336
|
+
/>
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
render(<ControlledDateRangeInput />);
|
|
340
|
+
|
|
341
|
+
getButton('Clear Range').focus();
|
|
342
|
+
await user.keyboard('{Enter}');
|
|
343
|
+
|
|
344
|
+
expect(queryButton('Clear Range')).not.toBeInTheDocument();
|
|
345
|
+
expect(getButton(/^Range:/)).toHaveFocus();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('does not move focus to the trigger when the clear button is clicked', async () => {
|
|
349
|
+
const user = userEvent.setup();
|
|
350
|
+
function ControlledDateRangeInput() {
|
|
351
|
+
const [controlledValue, setControlledValue] =
|
|
352
|
+
useState<DateRange | null>({
|
|
353
|
+
start: '2026-01-01',
|
|
354
|
+
end: '2026-01-03',
|
|
355
|
+
});
|
|
356
|
+
return (
|
|
357
|
+
<DateRangeInput
|
|
358
|
+
label="Range"
|
|
359
|
+
value={controlledValue}
|
|
360
|
+
onChange={setControlledValue}
|
|
361
|
+
/>
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
render(<ControlledDateRangeInput />);
|
|
365
|
+
|
|
366
|
+
await user.click(getButton('Clear Range'));
|
|
367
|
+
|
|
368
|
+
expect(queryButton('Clear Range')).not.toBeInTheDocument();
|
|
369
|
+
expect(getButton(/^Range:/)).not.toHaveFocus();
|
|
370
|
+
});
|
|
371
|
+
|
|
323
372
|
it('shows clear button when hasClear is true and value exists', () => {
|
|
324
373
|
const range: DateRange = {
|
|
325
374
|
start: '2026-03-15',
|
|
@@ -16,7 +16,14 @@
|
|
|
16
16
|
* - /packages/cli/assets/templates/blocks/components/DateRangeInput/ (showcase blocks)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
useId,
|
|
21
|
+
useCallback,
|
|
22
|
+
useMemo,
|
|
23
|
+
useOptimistic,
|
|
24
|
+
useRef,
|
|
25
|
+
useTransition,
|
|
26
|
+
} from 'react';
|
|
20
27
|
import * as stylex from '@stylexjs/stylex';
|
|
21
28
|
import {
|
|
22
29
|
plainDateFromISO,
|
|
@@ -68,6 +75,7 @@ import {
|
|
|
68
75
|
import type {SizeValue} from '../utils/types';
|
|
69
76
|
import {useSize} from '../SizeContext/SizeContext';
|
|
70
77
|
import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
|
|
78
|
+
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
71
79
|
import {useResolvedRequired} from '../hooks/useResolvedRequired';
|
|
72
80
|
import {themeProps} from '../utils/themeProps';
|
|
73
81
|
import {focusOutlineStyles} from '../utils/focusOutline.stylex';
|
|
@@ -624,10 +632,20 @@ export function DateRangeInput({
|
|
|
624
632
|
[fireChange, popover],
|
|
625
633
|
);
|
|
626
634
|
|
|
635
|
+
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
636
|
+
const triggerButtonRef = useMergedRefs<HTMLButtonElement>(triggerRef, ref);
|
|
637
|
+
|
|
627
638
|
const handleClear = useCallback(
|
|
628
639
|
(e: React.MouseEvent) => {
|
|
629
640
|
e.stopPropagation();
|
|
630
641
|
fireChange(null);
|
|
642
|
+
// A keyboard activation (detail 0) leaves focus on the clear button,
|
|
643
|
+
// which unmounts once the range is null and would drop focus to <body>;
|
|
644
|
+
// hand it to the trigger. A pointer press never focuses the button, so
|
|
645
|
+
// there is no focus to repair.
|
|
646
|
+
if (e.detail === 0) {
|
|
647
|
+
triggerRef.current?.focus();
|
|
648
|
+
}
|
|
631
649
|
},
|
|
632
650
|
[fireChange],
|
|
633
651
|
);
|
|
@@ -713,7 +731,7 @@ export function DateRangeInput({
|
|
|
713
731
|
</button>
|
|
714
732
|
<button
|
|
715
733
|
{...controlProps}
|
|
716
|
-
ref={
|
|
734
|
+
ref={triggerButtonRef}
|
|
717
735
|
id={id}
|
|
718
736
|
type="button"
|
|
719
737
|
onClick={chainEventHandlers(handleToggle, onClickProp)}
|