@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
schema_version: 3
|
|
3
|
-
template_version:
|
|
3
|
+
template_version: 7
|
|
4
4
|
kind: component
|
|
5
5
|
id: component:CommandPalette
|
|
6
6
|
authority: draft
|
|
@@ -18,6 +18,7 @@ verified_by:
|
|
|
18
18
|
packages/core/src/CommandPalette/CommandPaletteItem.test.tsx,
|
|
19
19
|
packages/core/src/CommandPalette/CommandPaletteGroup.test.tsx,
|
|
20
20
|
packages/core/src/CommandPalette/CommandPaletteFooter.test.tsx,
|
|
21
|
+
packages/core/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts,
|
|
21
22
|
packages/core/src/CommandPalette/__tests__/CommandPaletteFooter.a11y.chromium.spec.ts,
|
|
22
23
|
packages/core/src/theme/themingTargets.test.ts,
|
|
23
24
|
scripts/check-knowledge.mjs,
|
|
@@ -32,24 +33,43 @@ architecture:
|
|
|
32
33
|
architecture:public-component-api,
|
|
33
34
|
]
|
|
34
35
|
contributing: []
|
|
35
|
-
system_specs: []
|
|
36
|
+
system_specs: [spec:AST-056]
|
|
36
37
|
---
|
|
37
38
|
|
|
38
39
|
# CommandPalette component contract
|
|
39
40
|
|
|
41
|
+
## Contract at a glance
|
|
42
|
+
|
|
43
|
+
| Area | Contract |
|
|
44
|
+
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| Public contract | Prop and visual APIs are unchanged. The result surface exposes listbox semantics only with selectable results; the query input has a real default name; rich Empty content remains ordinary. |
|
|
46
|
+
| Behavior | Modal Escape delegates to Dialog; query-field Home/End and IME remain native; result changes reset stale highlight; Empty announcements mirror rendered content. |
|
|
47
|
+
| End-user impact | Nested dismissal and native text entry are preserved; assistive technology receives the visible empty message, an independent query-field name, and only existing active options. |
|
|
48
|
+
| Builder impact | None; no migration or new caller choice. |
|
|
49
|
+
| Compatibility/readiness | Visual output is preserved. Empty-result ARIA, query-field accessible naming, empty-state announcement, and highlight reset behavior are corrected and verified. |
|
|
50
|
+
| Review checks | Reject local modal Escape handling, IME routing, Home/End result navigation, empty listboxes, stale active descendants, placeholder-only names, mismatched Empty announcements, or gaps. |
|
|
51
|
+
| Governing rules | `family:overlay-dismissal/FR1`, `FR2`, `FR4`, `FR5`; `spec:AST-056/FR3`, `AR1`, `AR2`; existing public component and delegated-owner contracts. |
|
|
52
|
+
|
|
53
|
+
This table is a review projection; the body below is authoritative.
|
|
54
|
+
|
|
40
55
|
## Intent
|
|
41
56
|
|
|
42
|
-
CommandPalette presents searchable commands inside a Dialog.
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
CommandPalette presents searchable commands inside a Dialog. It owns search and
|
|
58
|
+
selection orchestration plus the aggregate consumer anatomy while delegating its
|
|
59
|
+
surface and modal dismissal ordering to Dialog.
|
|
45
60
|
|
|
46
61
|
## Compatibility and migration
|
|
47
62
|
|
|
48
|
-
- Released default preserved: `yes`
|
|
49
|
-
- Compatibility class:
|
|
50
|
-
|
|
63
|
+
- Released default preserved: `yes` for props and visual output
|
|
64
|
+
- Compatibility class: behavioral and accessibility correction; empty states
|
|
65
|
+
conditionally omit listbox, expanded, controls, and active-descendant ARIA
|
|
66
|
+
attributes until selectable results exist; the default query accessible name
|
|
67
|
+
is independent of its placeholder; empty-search announcements mirror the
|
|
68
|
+
rendered content; public props, styling, targets, and visible defaults remain
|
|
69
|
+
unchanged
|
|
51
70
|
- Controlled/uncontrolled behavior: unchanged
|
|
52
|
-
- Migration decision:
|
|
71
|
+
- Migration decision: adopts `family:overlay-dismissal/FR1`, `FR2`, `FR4`, and
|
|
72
|
+
`FR5` through Dialog; no caller migration
|
|
53
73
|
|
|
54
74
|
Consumer migration instructions belong in consumer docs and release notes.
|
|
55
75
|
|
|
@@ -57,9 +77,9 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
57
77
|
|
|
58
78
|
**Owns**
|
|
59
79
|
|
|
60
|
-
- Search orchestration and the current Input, List, Item, Group,
|
|
61
|
-
Empty, and Footer parts and targets.
|
|
62
|
-
-
|
|
80
|
+
- Search orchestration and composition of the current Input, List, Item, Group,
|
|
81
|
+
Group heading, Empty, and Footer parts and targets.
|
|
82
|
+
- Composition of the native Query field rendered inside the default Input; the field's semantics and native passthrough are owned by `component:CommandPaletteInput`.
|
|
63
83
|
|
|
64
84
|
**Does not own / non-goals**
|
|
65
85
|
|
|
@@ -67,6 +87,8 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
67
87
|
badges — owned by their respective components.
|
|
68
88
|
- Caller-provided replacement Input, Footer, item content, or trailing input
|
|
69
89
|
content — owned by the product callsite.
|
|
90
|
+
- The Item option's passthrough, pointer, disabled, selected, highlighted, and
|
|
91
|
+
standalone-scroll semantics — owned by `component:CommandPaletteItem`.
|
|
70
92
|
- A `command-palette` root target — no such current target exists, and this
|
|
71
93
|
factual backfill does not invent one.
|
|
72
94
|
- Shared layer hosting, positioning, or lifecycle rules beyond the current
|
|
@@ -81,12 +103,16 @@ documented in `CommandPalette.doc.mjs` and its subcomponent docs.
|
|
|
81
103
|
|
|
82
104
|
## Behavioral and layout contract
|
|
83
105
|
|
|
84
|
-
| ID | Candidate invariant | Basis
|
|
85
|
-
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
-
| FR1 | The current default render places Input and Footer around a
|
|
87
|
-
| FR2 | Input, List, Item, Group, Group heading, Empty, and Footer carry the seven current `command-palette-*` targets documented below; no `command-palette` root target exists. | Current source and target docs
|
|
88
|
-
| FR3 | The default Input delegates its Search glyph to Icon and pending Loading spinner to Spinner; the default Footer delegates keyboard shortcuts to Kbd; the containing surface delegates to Dialog. | Current source and component docs
|
|
89
|
-
| FR4 | Query field is a distinct native text field inside Input and currently has no separate public target. The `command-palette-input` target is on the surrounding search region, not the native field. | Current source and target docs
|
|
106
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
107
|
+
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
108
|
+
| FR1 | The current default render places Input and Footer around a result surface inside a delegated Dialog. That surface is a List listbox while Items exist and an ordinary Empty container otherwise. | Current source, docs, tests, and ARIA required-child rule | Verified current anatomy and valid rich Empty semantics |
|
|
109
|
+
| FR2 | Input, List, Item, Group, Group heading, Empty, and Footer carry the seven current `command-palette-*` targets documented below; no `command-palette` root target exists. | Current source and target docs | Verified current inventory and focused placement coverage |
|
|
110
|
+
| FR3 | The default Input delegates its Search glyph to Icon and pending Loading spinner to Spinner; the default Footer delegates keyboard shortcuts to Kbd; the containing surface delegates to Dialog. | Current source and component docs | Verified current composition; no ownership change |
|
|
111
|
+
| FR4 | Query field is a distinct native text field inside Input and currently has no separate public target. The `command-palette-input` target is on the surrounding search region, not the native field. | Current source and target docs | Verified current reachability; long-term theming intent remains unsettled |
|
|
112
|
+
| FR5 | A modal palette leaves Escape unclaimed for Dialog's shared dismissal stack so a nested member handles the first press; inline previews close locally because they do not register as layers. | `family:overlay-dismissal/FR1`, `FR2`, `FR4`, `FR5` | Verified by nested and inline integration tests |
|
|
113
|
+
| FR6 | The editable query field keeps Home and End for native caret movement, uses PageUp and PageDown for first/last result navigation, and does not route active IME composition keys to commands. | WAI-ARIA editable combobox convention; objective IME integrity | Verified by user-event navigation and composition-key tests |
|
|
114
|
+
| FR7 | A committed result set resets highlight before exposure; the query input emits active-descendant only for an index that exists in the current selectable set. | WAI-ARIA active-descendant integrity | Verified across populated, empty, and repopulated results |
|
|
115
|
+
| FR8 | Empty search announcements mirror rendered `emptySearchText`, including rich caller content; the query input's default accessible name is independent of its placeholder. | `spec:AST-056/FR3`, `AR1`, `AR2` | Verified by live-region and accessible-name tests |
|
|
90
116
|
|
|
91
117
|
### Allowed variation
|
|
92
118
|
|
|
@@ -99,14 +125,14 @@ documented in `CommandPalette.doc.mjs` and its subcomponent docs.
|
|
|
99
125
|
|
|
100
126
|
### Representative states
|
|
101
127
|
|
|
102
|
-
| State | Required invariant
|
|
103
|
-
| ---------------------- |
|
|
104
|
-
| Ungrouped results | List contains Item instances.
|
|
105
|
-
| Grouped results | List contains Group, Group heading, and Item instances.
|
|
106
|
-
| Empty bootstrap/search |
|
|
107
|
-
| Pending search | Default Input may contain Loading spinner.
|
|
108
|
-
| Default slots | Input and Footer render their documented defaults.
|
|
109
|
-
| Caller-replaced slots | The slot content replaces the corresponding default.
|
|
128
|
+
| State | Required invariant | Allowed variation |
|
|
129
|
+
| ---------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
130
|
+
| Ungrouped results | List contains Item instances. | Item count and caller-rendered content. |
|
|
131
|
+
| Grouped results | List contains Group, Group heading, and Item instances. | Group names, count, ordering, and item content. |
|
|
132
|
+
| Empty bootstrap/search | Empty replaces listbox semantics; search-empty text is announced as rendered. | Caller-provided rich content, including ordinary controls. |
|
|
133
|
+
| Pending search | Default Input may contain Loading spinner. | Spinner is absent when no search is pending. |
|
|
134
|
+
| Default slots | Input and Footer render their documented defaults. | Footer shortcut text and translated labels. |
|
|
135
|
+
| Caller-replaced slots | The slot content replaces the corresponding default. | Replacement structure remains caller-owned. |
|
|
110
136
|
|
|
111
137
|
### Transformation and precedence order
|
|
112
138
|
|
|
@@ -118,8 +144,27 @@ documented in `CommandPalette.doc.mjs` and its subcomponent docs.
|
|
|
118
144
|
|
|
119
145
|
## Accessibility contract
|
|
120
146
|
|
|
121
|
-
|
|
122
|
-
|
|
147
|
+
- **AR1 — Named composite.** The containing Dialog, combobox, populated listbox,
|
|
148
|
+
groups, options, busy status, empty state, and keyboard guidance preserve their
|
|
149
|
+
owned accessible names and roles. When no selectable results exist, the result
|
|
150
|
+
surface and combobox omit listbox, expanded, controls, and active-descendant
|
|
151
|
+
semantics so rich Empty content stays ordinary non-nested content.
|
|
152
|
+
- **AR2 — Topmost Escape.** A modal palette delegates unclaimed Escape presses to
|
|
153
|
+
Dialog's shared dismissal stack. A nested registered surface handles the first
|
|
154
|
+
press; the host palette remains open until it becomes topmost.
|
|
155
|
+
- **AR3 — Inline preview.** Documentation-only inline rendering remains outside
|
|
156
|
+
the shared layer stack and preserves its local Escape close behavior.
|
|
157
|
+
- **AR4 — Editable query keys.** Home and End preserve native caret movement;
|
|
158
|
+
PageUp and PageDown navigate to the first and last results; active IME
|
|
159
|
+
composition keys remain owned by the input method.
|
|
160
|
+
- **AR5 — Rendered empty message.** A search dead end announces the text rendered
|
|
161
|
+
by `emptySearchText`, including text produced by caller components and links.
|
|
162
|
+
- **AR6 — Query-field name.** The default query field name is a localized
|
|
163
|
+
"Search commands" label independent of its placeholder; caller `label` and
|
|
164
|
+
native `aria-label` overrides remain supported.
|
|
165
|
+
- **AR7 — Existing active option.** Result changes reset highlight, and
|
|
166
|
+
`aria-activedescendant` is present only when the indexed option exists in the
|
|
167
|
+
current selectable set.
|
|
123
168
|
|
|
124
169
|
## Design relationships
|
|
125
170
|
|
|
@@ -185,27 +230,36 @@ current audit gap and does not authorize a new target.
|
|
|
185
230
|
mapping, delegation, and factual `none` dispositions.
|
|
186
231
|
- `architecture:public-component-api` owns the stable props and composition
|
|
187
232
|
surface; this documentation adds no API.
|
|
188
|
-
- `architecture:interaction-modality` owns
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
233
|
+
- `architecture:interaction-modality/INV4` owns supported-modality path
|
|
234
|
+
reachability; the exact query-key details follow objective editable-combobox
|
|
235
|
+
and IME composition standards.
|
|
236
|
+
- `spec:AST-056/FR3`, `AR1`, and `AR2` own rendered empty-search announcement
|
|
237
|
+
parity and an accessible query-field name independent of placeholder text.
|
|
238
|
+
- `family:overlay-dismissal` owns topmost Escape and close-request ordering;
|
|
239
|
+
CommandPalette adopts it through Dialog while preserving local inline-preview
|
|
240
|
+
closing because inline mode does not register as a layer.
|
|
241
|
+
- `architecture:layer-runtime` records that the modal path delegates Escape to
|
|
242
|
+
Dialog's shared dismissal registration while the unregistered inline preview
|
|
243
|
+
preserves local closing.
|
|
194
244
|
|
|
195
245
|
## Verification map
|
|
196
246
|
|
|
197
|
-
| Contract | Verification
|
|
198
|
-
| ------------------- |
|
|
199
|
-
| FR1
|
|
200
|
-
| FR2 |
|
|
201
|
-
| FR3 |
|
|
202
|
-
| FR4 | `CommandPaletteInput.test.tsx` and source inspection
|
|
203
|
-
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
247
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
248
|
+
| ------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
|
|
249
|
+
| FR1, AR1 | CommandPalette root and subcomponent suites plus exact-head Chromium evidence | Default, grouped, empty, pending, rich Empty, and replaced slots | Exposing an empty listbox, leaving empty Input popup attributes, or nesting rich Empty controls under option semantics fails focused role and hosted axe assertions. | `audit:CommandPalette/anatomy` |
|
|
250
|
+
| FR2 | Focused subcomponent suites and `themingTargets.test.ts` | Seven local targets at their owned elements | Removing or moving a current target fails a focused placement assertion or the global inventory. | `audit:CommandPalette/theming` |
|
|
251
|
+
| FR3 | `CommandPaletteInput.test.tsx`, `CommandPaletteFooter.test.tsx`, and browser evidence | Default Search glyph, pending spinner, and Footer Kbd | Replacing or removing the delegated Icon, Spinner, or Kbd instance fails a composed-owner assertion and changes the exact-head evidence inventory. | `audit:CommandPalette/theming` |
|
|
252
|
+
| FR4 | `CommandPaletteInput.test.tsx` and source inspection | Native query field with idle and pending Input | Removing the field fails combobox tests; adding a field target requires an explicit map update. | `audit:CommandPalette/anatomy` |
|
|
253
|
+
| FR5, AR2, AR3 | `CommandPalette.test.tsx` | Modal host with nested layer; inline preview | Restoring local modal Escape handling closes the host instead of the nested surface; removing the inline branch loses preview closing. | `audit:CommandPalette/interaction` |
|
|
254
|
+
| FR6, AR4 | `CommandPalette.test.tsx` and `CommandPaletteInput.test.tsx` | Home, End, PageDown, Enter, and active IME composition | Routing Home/End to results or forwarding a composing command key fails user-event and native composition assertions. | `audit:CommandPalette/interaction` |
|
|
255
|
+
| FR7, AR7 | `CommandPalette.test.tsx` and `CommandPaletteInput.test.tsx` | Populated, empty, and repopulated result sets | Preserving an out-of-range highlight or emitting its nonexistent active descendant fails transition and direct bounds assertions. | `audit:CommandPalette/a11y` |
|
|
256
|
+
| FR8, AR5, AR6 | `CommandPalette.test.tsx`, `CommandPaletteInput.test.tsx`, and i18n checks | Rich caller Empty content; default and caller query names | Announcing catalog copy over rendered caller content or naming the query only through placeholder text fails live-region, name, and catalog assertions. | `audit:CommandPalette/a11y` |
|
|
257
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` and exact-head Chromium evidence | Canonical anatomy and seven current local targets | Missing, extra, prefixed, stale, alias-backed, or visually absent mappings fail repository validation or browser sensor counts. | `audit:CommandPalette/theming` |
|
|
258
|
+
|
|
259
|
+
Focused target-placement assertions cover all seven local targets. The composed
|
|
260
|
+
Input and Footer tests also pin the delegated Search Icon, pending Spinner, and
|
|
261
|
+
four default Kbd shortcuts; exact-head browser evidence verifies the same owned
|
|
262
|
+
anatomy across light, dark, LTR, RTL, and narrow rendering.
|
|
209
263
|
|
|
210
264
|
## Decision log
|
|
211
265
|
|
|
@@ -216,9 +270,6 @@ layer, API, or theming decision.
|
|
|
216
270
|
|
|
217
271
|
- **OQ1 — Should Query field gain a stable public theming target?** (`human-api`)
|
|
218
272
|
Its current lack of direct reachability is an audit gap, not settled intent.
|
|
219
|
-
- **OQ2 — Should focused tests pin the default Search glyph and pending Loading
|
|
220
|
-
spinner?** (`checkable`) Their presence is currently source-inspected rather
|
|
221
|
-
than asserted.
|
|
222
273
|
|
|
223
274
|
## Content boundary
|
|
224
275
|
|
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
11
11
|
import {render, screen, waitFor, fireEvent, act} from '@testing-library/react';
|
|
12
|
+
import userEvent from '@testing-library/user-event';
|
|
12
13
|
import {useState} from 'react';
|
|
13
14
|
import {CommandPalette} from './CommandPalette';
|
|
15
|
+
import {CommandPaletteInput} from './CommandPaletteInput';
|
|
16
|
+
import {useLayerDismissal} from '../Layer/useLayerDismissal';
|
|
14
17
|
import {createStaticSource} from '@astryxdesign/core/Typeahead';
|
|
15
18
|
import type {SearchSource, SearchableItem} from '@astryxdesign/core/Typeahead';
|
|
16
19
|
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
@@ -27,6 +30,11 @@ const groupedSource = createStaticSource([
|
|
|
27
30
|
|
|
28
31
|
const emptySource = createStaticSource([]);
|
|
29
32
|
|
|
33
|
+
function NestedDismissible({onDismiss}: {onDismiss: () => void}) {
|
|
34
|
+
useLayerDismissal({isActive: true, onDismiss});
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
// Mock showModal and close since jsdom doesn't implement them
|
|
31
39
|
beforeEach(() => {
|
|
32
40
|
HTMLDialogElement.prototype.showModal = vi.fn(function (
|
|
@@ -198,6 +206,31 @@ describe('CommandPalette', () => {
|
|
|
198
206
|
});
|
|
199
207
|
});
|
|
200
208
|
|
|
209
|
+
it('keeps actionable empty content outside listbox semantics', async () => {
|
|
210
|
+
const {container} = render(
|
|
211
|
+
<CommandPalette
|
|
212
|
+
isOpen={true}
|
|
213
|
+
onOpenChange={() => {}}
|
|
214
|
+
searchSource={emptySource}
|
|
215
|
+
emptyBootstrapText={<a href="/commands/new">Create a command</a>}
|
|
216
|
+
/>,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
expect(
|
|
220
|
+
await screen.findByRole('link', {name: 'Create a command'}),
|
|
221
|
+
).toHaveAttribute('href', '/commands/new');
|
|
222
|
+
expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
|
|
223
|
+
expect(screen.queryByRole('option')).not.toBeInTheDocument();
|
|
224
|
+
expect(
|
|
225
|
+
container.querySelectorAll('.astryx-command-palette-empty'),
|
|
226
|
+
).toHaveLength(1);
|
|
227
|
+
expect(screen.getByRole('combobox')).toHaveAttribute(
|
|
228
|
+
'aria-expanded',
|
|
229
|
+
'false',
|
|
230
|
+
);
|
|
231
|
+
expect(screen.getByRole('combobox')).not.toHaveAttribute('aria-controls');
|
|
232
|
+
});
|
|
233
|
+
|
|
201
234
|
it('shows default emptyBootstrapText when not provided', async () => {
|
|
202
235
|
render(
|
|
203
236
|
<CommandPalette
|
|
@@ -211,6 +244,40 @@ describe('CommandPalette', () => {
|
|
|
211
244
|
});
|
|
212
245
|
});
|
|
213
246
|
|
|
247
|
+
it('clears a stale highlight across empty and repopulated results', async () => {
|
|
248
|
+
render(
|
|
249
|
+
<CommandPalette
|
|
250
|
+
isOpen={true}
|
|
251
|
+
onOpenChange={() => {}}
|
|
252
|
+
searchSource={simpleSource}
|
|
253
|
+
/>,
|
|
254
|
+
);
|
|
255
|
+
await waitFor(() =>
|
|
256
|
+
expect(screen.getByText('Settings')).toBeInTheDocument(),
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const input = screen.getByRole('combobox');
|
|
260
|
+
fireEvent.keyDown(input, {key: 'ArrowDown'});
|
|
261
|
+
fireEvent.keyDown(input, {key: 'ArrowDown'});
|
|
262
|
+
expect(input.getAttribute('aria-activedescendant')).toMatch(/-item-1$/);
|
|
263
|
+
|
|
264
|
+
fireEvent.change(input, {target: {value: 'zzz'}});
|
|
265
|
+
await waitFor(() => expect(screen.queryByRole('listbox')).toBeNull());
|
|
266
|
+
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
267
|
+
|
|
268
|
+
fireEvent.change(input, {target: {value: 'hom'}});
|
|
269
|
+
await waitFor(() =>
|
|
270
|
+
expect(screen.getByRole('option', {name: 'Home'})).toBeInTheDocument(),
|
|
271
|
+
);
|
|
272
|
+
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
273
|
+
|
|
274
|
+
fireEvent.keyDown(input, {key: 'ArrowDown'});
|
|
275
|
+
expect(input).toHaveAttribute(
|
|
276
|
+
'aria-activedescendant',
|
|
277
|
+
screen.getByRole('option', {name: 'Home'}).id,
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
|
|
214
281
|
it('calls onOpenChange(false) when Escape is pressed', () => {
|
|
215
282
|
const handleOpenChange = vi.fn();
|
|
216
283
|
render(
|
|
@@ -230,6 +297,46 @@ describe('CommandPalette', () => {
|
|
|
230
297
|
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
231
298
|
});
|
|
232
299
|
|
|
300
|
+
it('lets the topmost nested layer handle Escape before the palette', () => {
|
|
301
|
+
const handleOpenChange = vi.fn();
|
|
302
|
+
const handleNestedDismiss = vi.fn();
|
|
303
|
+
render(
|
|
304
|
+
<CommandPalette
|
|
305
|
+
isOpen={true}
|
|
306
|
+
onOpenChange={handleOpenChange}
|
|
307
|
+
searchSource={simpleSource}
|
|
308
|
+
input={
|
|
309
|
+
<>
|
|
310
|
+
<CommandPaletteInput />
|
|
311
|
+
<NestedDismissible onDismiss={handleNestedDismiss} />
|
|
312
|
+
</>
|
|
313
|
+
}
|
|
314
|
+
/>,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
fireEvent.keyDown(screen.getByRole('combobox'), {key: 'Escape'});
|
|
318
|
+
|
|
319
|
+
expect(handleNestedDismiss).toHaveBeenCalledOnce();
|
|
320
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('preserves local Escape closing for inline previews', () => {
|
|
324
|
+
const handleOpenChange = vi.fn();
|
|
325
|
+
render(
|
|
326
|
+
<CommandPalette
|
|
327
|
+
isOpen={true}
|
|
328
|
+
isInline
|
|
329
|
+
onOpenChange={handleOpenChange}
|
|
330
|
+
searchSource={simpleSource}
|
|
331
|
+
/>,
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
fireEvent.keyDown(screen.getByRole('combobox'), {key: 'Escape'});
|
|
335
|
+
|
|
336
|
+
expect(handleOpenChange).toHaveBeenCalledOnce();
|
|
337
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
338
|
+
});
|
|
339
|
+
|
|
233
340
|
it('keeps the empty state mounted while a no-result search is pending', async () => {
|
|
234
341
|
// A source whose searches resolve only when we release them, so we can
|
|
235
342
|
// observe the render output while a search transition is in flight.
|
|
@@ -357,6 +464,52 @@ describe('CommandPalette', () => {
|
|
|
357
464
|
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
358
465
|
});
|
|
359
466
|
|
|
467
|
+
it('keeps Home and End on the query field and uses Page keys for results', async () => {
|
|
468
|
+
const user = userEvent.setup();
|
|
469
|
+
const handleOpenChange = vi.fn();
|
|
470
|
+
const handleValueChange = vi.fn();
|
|
471
|
+
render(
|
|
472
|
+
<CommandPalette
|
|
473
|
+
isOpen={true}
|
|
474
|
+
onOpenChange={handleOpenChange}
|
|
475
|
+
onValueChange={handleValueChange}
|
|
476
|
+
searchSource={simpleSource}
|
|
477
|
+
/>,
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const input = screen.getByRole<HTMLInputElement>('combobox');
|
|
481
|
+
await waitFor(() =>
|
|
482
|
+
expect(screen.getByText('Settings')).toBeInTheDocument(),
|
|
483
|
+
);
|
|
484
|
+
await user.click(input);
|
|
485
|
+
await user.type(input, 'e');
|
|
486
|
+
await waitFor(() =>
|
|
487
|
+
expect(screen.getByText('Settings')).toBeInTheDocument(),
|
|
488
|
+
);
|
|
489
|
+
const homeOption = screen.getByText('Home').closest('[role="option"]');
|
|
490
|
+
const settingsOption = screen
|
|
491
|
+
.getByText('Settings')
|
|
492
|
+
.closest('[role="option"]');
|
|
493
|
+
|
|
494
|
+
expect(input).toHaveValue('e');
|
|
495
|
+
expect(input.selectionStart).toBe(1);
|
|
496
|
+
await user.keyboard('{Home}');
|
|
497
|
+
expect(input.selectionStart).toBe(0);
|
|
498
|
+
await user.keyboard('{End}');
|
|
499
|
+
expect(input.selectionStart).toBe(1);
|
|
500
|
+
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
501
|
+
|
|
502
|
+
await user.keyboard('{PageDown}');
|
|
503
|
+
expect(input.getAttribute('aria-activedescendant')).toBe(
|
|
504
|
+
settingsOption?.id,
|
|
505
|
+
);
|
|
506
|
+
await user.keyboard('{PageUp}');
|
|
507
|
+
expect(input.getAttribute('aria-activedescendant')).toBe(homeOption?.id);
|
|
508
|
+
await user.keyboard('{Enter}');
|
|
509
|
+
expect(handleValueChange).toHaveBeenCalledWith('home');
|
|
510
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
511
|
+
});
|
|
512
|
+
|
|
360
513
|
it('highlights on hover without scrolling and scrolls once per key (#6077)', async () => {
|
|
361
514
|
// Hover must be handled by the root's delegated list handler (routed to
|
|
362
515
|
// useCombobox's hover-aware path), and the shared useHighlightedOptionScroll effect
|
|
@@ -453,19 +606,105 @@ describe('CommandPalette', () => {
|
|
|
453
606
|
});
|
|
454
607
|
});
|
|
455
608
|
|
|
456
|
-
it('announces the empty state
|
|
609
|
+
it('announces the rendered default empty state when nothing matches', async () => {
|
|
610
|
+
render(
|
|
611
|
+
<CommandPalette
|
|
612
|
+
isOpen={true}
|
|
613
|
+
onOpenChange={() => {}}
|
|
614
|
+
searchSource={simpleSource}
|
|
615
|
+
/>,
|
|
616
|
+
);
|
|
617
|
+
await waitFor(() => expect(screen.getByText('Home')).toBeInTheDocument());
|
|
618
|
+
fireEvent.change(screen.getByRole('combobox'), {target: {value: 'zzz'}});
|
|
619
|
+
await waitFor(() => {
|
|
620
|
+
expect(politeRegion()).toHaveTextContent(/^No results$/);
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('announces rich emptySearchText exactly as rendered', async () => {
|
|
457
625
|
render(
|
|
458
626
|
<CommandPalette
|
|
459
627
|
isOpen={true}
|
|
460
628
|
onOpenChange={() => {}}
|
|
461
629
|
searchSource={simpleSource}
|
|
630
|
+
emptySearchText={
|
|
631
|
+
<span>
|
|
632
|
+
Nothing like that here. <a href="/commands/new">Add a command</a>
|
|
633
|
+
</span>
|
|
634
|
+
}
|
|
462
635
|
/>,
|
|
463
636
|
);
|
|
464
637
|
await waitFor(() => expect(screen.getByText('Home')).toBeInTheDocument());
|
|
465
638
|
fireEvent.change(screen.getByRole('combobox'), {target: {value: 'zzz'}});
|
|
639
|
+
|
|
640
|
+
expect(
|
|
641
|
+
await screen.findByRole('link', {name: 'Add a command'}),
|
|
642
|
+
).toHaveAttribute('href', '/commands/new');
|
|
466
643
|
await waitFor(() => {
|
|
467
|
-
expect(politeRegion()).toHaveTextContent(
|
|
644
|
+
expect(politeRegion()).toHaveTextContent(
|
|
645
|
+
/^Nothing like that here\. Add a command$/,
|
|
646
|
+
);
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
it('waits for a narrowed search to settle before announcing no results', async () => {
|
|
651
|
+
const resolvers: ((items: SearchableItem[]) => void)[] = [];
|
|
652
|
+
const source: SearchSource = {
|
|
653
|
+
bootstrap: () => [
|
|
654
|
+
{id: 'home', label: 'Home'},
|
|
655
|
+
{id: 'settings', label: 'Settings'},
|
|
656
|
+
],
|
|
657
|
+
async search(): Promise<SearchableItem[]> {
|
|
658
|
+
return new Promise<SearchableItem[]>(resolve => {
|
|
659
|
+
resolvers.push(resolve);
|
|
660
|
+
});
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
const spoken: string[] = [];
|
|
664
|
+
const observer = new MutationObserver(() => {
|
|
665
|
+
const text = politeRegion()?.textContent ?? '';
|
|
666
|
+
if (text !== '' && spoken.at(-1) !== text) {
|
|
667
|
+
spoken.push(text);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
observer.observe(document.body, {
|
|
671
|
+
childList: true,
|
|
672
|
+
subtree: true,
|
|
673
|
+
characterData: true,
|
|
468
674
|
});
|
|
675
|
+
try {
|
|
676
|
+
render(
|
|
677
|
+
<CommandPalette
|
|
678
|
+
isOpen={true}
|
|
679
|
+
onOpenChange={() => {}}
|
|
680
|
+
searchSource={source}
|
|
681
|
+
/>,
|
|
682
|
+
);
|
|
683
|
+
await waitFor(() =>
|
|
684
|
+
expect(screen.getByText('Home')).toBeInTheDocument(),
|
|
685
|
+
);
|
|
686
|
+
const input = screen.getByRole('combobox');
|
|
687
|
+
|
|
688
|
+
fireEvent.change(input, {target: {value: 'e'}});
|
|
689
|
+
await act(async () =>
|
|
690
|
+
resolvers.shift()?.(source.bootstrap() as SearchableItem[]),
|
|
691
|
+
);
|
|
692
|
+
await waitFor(() => expect(spoken.at(-1)).toBe('2 results'));
|
|
693
|
+
|
|
694
|
+
// Optimistic narrowing empties the list while the source is still
|
|
695
|
+
// answering; the source then finds one match.
|
|
696
|
+
fireEvent.change(input, {target: {value: 'ez'}});
|
|
697
|
+
await act(async () => {
|
|
698
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
699
|
+
});
|
|
700
|
+
await act(async () =>
|
|
701
|
+
resolvers.shift()?.([{id: 'settings', label: 'Settings'}]),
|
|
702
|
+
);
|
|
703
|
+
await waitFor(() => expect(spoken.at(-1)).toBe('1 result'));
|
|
704
|
+
expect(spoken).not.toContain('No results');
|
|
705
|
+
} finally {
|
|
706
|
+
observer.disconnect();
|
|
707
|
+
}
|
|
469
708
|
});
|
|
470
709
|
|
|
471
710
|
it('announces loading politely while a search is in flight', async () => {
|