@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
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @position Testing; validates CommandPaletteInput.tsx implementation
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import {createRef} from 'react';
|
|
10
11
|
import {describe, it, expect, vi} from 'vitest';
|
|
11
12
|
import {render, screen, fireEvent} from '@testing-library/react';
|
|
12
13
|
import {CommandPaletteInput} from './CommandPaletteInput';
|
|
@@ -14,12 +15,47 @@ import {CommandPaletteContext} from './CommandPaletteContext';
|
|
|
14
15
|
import {Dialog} from '../Dialog';
|
|
15
16
|
import type {CommandPaletteContextValue} from './CommandPaletteContext';
|
|
16
17
|
|
|
18
|
+
function makeContext(
|
|
19
|
+
overrides: Partial<CommandPaletteContextValue> = {},
|
|
20
|
+
): CommandPaletteContextValue {
|
|
21
|
+
return {
|
|
22
|
+
search: '',
|
|
23
|
+
setSearch: vi.fn(),
|
|
24
|
+
value: '',
|
|
25
|
+
setValue: vi.fn(),
|
|
26
|
+
listId: 'list-1',
|
|
27
|
+
highlightedIndex: -1,
|
|
28
|
+
setHighlightedIndex: vi.fn(),
|
|
29
|
+
getItemId: (i: number) => `item-${i}`,
|
|
30
|
+
selectableItems: [{value: 'open', label: 'Open'}],
|
|
31
|
+
searchResults: [],
|
|
32
|
+
selectItem: vi.fn(),
|
|
33
|
+
onKeyDown: vi.fn(),
|
|
34
|
+
onClose: vi.fn(),
|
|
35
|
+
isOpen: true,
|
|
36
|
+
isBusy: false,
|
|
37
|
+
...overrides,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
17
41
|
describe('CommandPaletteInput', () => {
|
|
18
42
|
it('renders with default placeholder', () => {
|
|
19
43
|
render(<CommandPaletteInput />);
|
|
20
44
|
expect(screen.getByPlaceholderText('Search…')).toBeInTheDocument();
|
|
21
45
|
});
|
|
22
46
|
|
|
47
|
+
it('renders the local input target and delegated search icon', () => {
|
|
48
|
+
const {container} = render(<CommandPaletteInput />);
|
|
49
|
+
|
|
50
|
+
expect(container.firstElementChild).toHaveClass(
|
|
51
|
+
'astryx-command-palette-input',
|
|
52
|
+
);
|
|
53
|
+
expect(container.querySelector('.astryx-icon')).toHaveAttribute(
|
|
54
|
+
'aria-hidden',
|
|
55
|
+
'true',
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
23
59
|
it('renders with custom placeholder', () => {
|
|
24
60
|
render(<CommandPaletteInput placeholder="Type a command..." />);
|
|
25
61
|
expect(
|
|
@@ -27,40 +63,166 @@ describe('CommandPaletteInput', () => {
|
|
|
27
63
|
).toBeInTheDocument();
|
|
28
64
|
});
|
|
29
65
|
|
|
30
|
-
it('
|
|
66
|
+
it('uses searchbox semantics when no popup context exists', () => {
|
|
31
67
|
render(<CommandPaletteInput />);
|
|
32
|
-
|
|
68
|
+
const input = screen.getByRole('searchbox');
|
|
69
|
+
expect(input).not.toHaveAttribute('aria-expanded');
|
|
70
|
+
expect(input).not.toHaveAttribute('aria-controls');
|
|
71
|
+
expect(input).not.toHaveAttribute('aria-autocomplete');
|
|
33
72
|
});
|
|
34
73
|
|
|
35
|
-
it('
|
|
36
|
-
render(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
74
|
+
it('preserves standalone caller-owned popup semantics', () => {
|
|
75
|
+
render(
|
|
76
|
+
<CommandPaletteInput
|
|
77
|
+
aria-expanded="false"
|
|
78
|
+
aria-autocomplete="both"
|
|
79
|
+
aria-controls="caller-list"
|
|
80
|
+
aria-activedescendant="caller-item"
|
|
81
|
+
/>,
|
|
40
82
|
);
|
|
83
|
+
|
|
84
|
+
const input = screen.getByRole('combobox');
|
|
85
|
+
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
86
|
+
expect(input).toHaveAttribute('aria-autocomplete', 'both');
|
|
87
|
+
expect(input).toHaveAttribute('aria-controls', 'caller-list');
|
|
88
|
+
expect(input).toHaveAttribute('aria-activedescendant', 'caller-item');
|
|
41
89
|
});
|
|
42
90
|
|
|
43
|
-
it('
|
|
44
|
-
render(<CommandPaletteInput
|
|
91
|
+
it('infers caller-owned popup defaults from aria-controls alone', () => {
|
|
92
|
+
render(<CommandPaletteInput aria-controls="caller-list" />);
|
|
93
|
+
|
|
94
|
+
const input = screen.getByRole('combobox');
|
|
95
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
96
|
+
expect(input).toHaveAttribute('aria-autocomplete', 'list');
|
|
97
|
+
expect(input).toHaveAttribute('aria-controls', 'caller-list');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('preserves a caller-owned combobox role and supplies popup defaults', () => {
|
|
101
|
+
render(<CommandPaletteInput role="combobox" />);
|
|
102
|
+
|
|
45
103
|
const input = screen.getByRole('combobox');
|
|
46
|
-
expect(input).toHaveAttribute('aria-
|
|
47
|
-
|
|
104
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
105
|
+
expect(input).toHaveAttribute('aria-autocomplete', 'list');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('keeps a toggling caller popup a combobox with its own values', () => {
|
|
109
|
+
function CallerPopup({open}: {open: boolean}) {
|
|
110
|
+
return (
|
|
111
|
+
<CommandPaletteInput
|
|
112
|
+
aria-expanded={open || undefined}
|
|
113
|
+
aria-controls={open ? 'caller-list' : undefined}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const {rerender} = render(<CallerPopup open={false} />);
|
|
118
|
+
|
|
119
|
+
const closed = screen.getByRole('combobox');
|
|
120
|
+
expect(closed).not.toHaveAttribute('aria-expanded');
|
|
121
|
+
expect(closed).not.toHaveAttribute('aria-controls');
|
|
122
|
+
|
|
123
|
+
rerender(<CallerPopup open />);
|
|
124
|
+
const open = screen.getByRole('combobox');
|
|
125
|
+
expect(open).toHaveAttribute('aria-expanded', 'true');
|
|
126
|
+
expect(open).toHaveAttribute('aria-controls', 'caller-list');
|
|
127
|
+
|
|
128
|
+
rerender(<CallerPopup open={false} />);
|
|
129
|
+
expect(screen.getByRole('combobox')).not.toHaveAttribute('aria-expanded');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('omits an explicitly undefined aria-expanded beside a caller list', () => {
|
|
133
|
+
render(
|
|
134
|
+
<CommandPaletteInput
|
|
135
|
+
aria-expanded={undefined}
|
|
136
|
+
aria-controls="caller-list"
|
|
137
|
+
/>,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const input = screen.getByRole('combobox');
|
|
141
|
+
expect(input).not.toHaveAttribute('aria-expanded');
|
|
142
|
+
expect(input).toHaveAttribute('aria-controls', 'caller-list');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it.each([
|
|
146
|
+
['aria-owns', {'aria-owns': 'caller-list'}],
|
|
147
|
+
['aria-haspopup="listbox"', {'aria-haspopup': 'listbox' as const}],
|
|
148
|
+
['aria-haspopup={true}', {'aria-haspopup': true}],
|
|
149
|
+
])(
|
|
150
|
+
'keeps a field wired to its popup only through %s a combobox',
|
|
151
|
+
(_label, popupProps) => {
|
|
152
|
+
render(<CommandPaletteInput {...popupProps} />);
|
|
153
|
+
|
|
154
|
+
const input = screen.getByRole('combobox');
|
|
155
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
156
|
+
expect(input).toHaveAttribute('aria-autocomplete', 'list');
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
it.each([
|
|
161
|
+
['false', false],
|
|
162
|
+
['"false"', 'false' as const],
|
|
163
|
+
])('stays a searchbox when aria-haspopup is %s', (_label, value) => {
|
|
164
|
+
render(<CommandPaletteInput aria-haspopup={value} />);
|
|
165
|
+
|
|
166
|
+
const input = screen.getByRole('searchbox');
|
|
167
|
+
expect(input).not.toHaveAttribute('aria-expanded');
|
|
168
|
+
expect(input).not.toHaveAttribute('aria-autocomplete');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('is named "Search commands" by default, not by its placeholder', () => {
|
|
172
|
+
render(<CommandPaletteInput />);
|
|
173
|
+
const input = screen.getByRole('searchbox', {name: 'Search commands'});
|
|
48
174
|
expect(input).toHaveAttribute('placeholder', 'Search…');
|
|
49
175
|
});
|
|
50
176
|
|
|
51
|
-
it('
|
|
177
|
+
it('uses the label prop as the accessible name', () => {
|
|
178
|
+
render(<CommandPaletteInput label="Find files" />);
|
|
179
|
+
const input = screen.getByRole('searchbox', {name: 'Find files'});
|
|
180
|
+
expect(input).toHaveAttribute('placeholder', 'Search…');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('never takes its name from a custom placeholder', () => {
|
|
52
184
|
render(<CommandPaletteInput placeholder="Type a command..." />);
|
|
53
|
-
expect(
|
|
54
|
-
'
|
|
55
|
-
|
|
185
|
+
expect(
|
|
186
|
+
screen.getByRole('searchbox', {name: 'Search commands'}),
|
|
187
|
+
).toHaveAttribute('placeholder', 'Type a command...');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('owns role and popup state inside CommandPalette context, passing other ARIA through', () => {
|
|
191
|
+
render(
|
|
192
|
+
<CommandPaletteContext value={makeContext()}>
|
|
193
|
+
<CommandPaletteInput
|
|
194
|
+
role="searchbox"
|
|
195
|
+
aria-expanded={false}
|
|
196
|
+
aria-controls="caller-list"
|
|
197
|
+
aria-haspopup="dialog"
|
|
198
|
+
aria-owns="caller-owned"
|
|
199
|
+
/>
|
|
200
|
+
</CommandPaletteContext>,
|
|
201
|
+
);
|
|
202
|
+
const input = screen.getByRole('combobox');
|
|
203
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
204
|
+
expect(input).toHaveAttribute('aria-controls', 'list-1');
|
|
205
|
+
expect(input).toHaveAttribute('aria-autocomplete', 'list');
|
|
206
|
+
expect(input).toHaveAttribute('aria-haspopup', 'dialog');
|
|
207
|
+
expect(input).toHaveAttribute('aria-owns', 'caller-owned');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('keeps the default name inside CommandPalette context', () => {
|
|
211
|
+
render(
|
|
212
|
+
<CommandPaletteContext value={makeContext()}>
|
|
213
|
+
<CommandPaletteInput placeholder="Jump to…" />
|
|
214
|
+
</CommandPaletteContext>,
|
|
56
215
|
);
|
|
216
|
+
expect(
|
|
217
|
+
screen.getByRole('combobox', {name: 'Search commands'}),
|
|
218
|
+
).toBeInTheDocument();
|
|
57
219
|
});
|
|
58
220
|
|
|
59
221
|
it('lets a consumer-passed aria-label override the default', () => {
|
|
60
222
|
render(
|
|
61
223
|
<CommandPaletteInput label="Search commands" aria-label="Custom name" />,
|
|
62
224
|
);
|
|
63
|
-
expect(screen.getByRole('
|
|
225
|
+
expect(screen.getByRole('searchbox')).toHaveAttribute(
|
|
64
226
|
'aria-label',
|
|
65
227
|
'Custom name',
|
|
66
228
|
);
|
|
@@ -70,15 +232,16 @@ describe('CommandPaletteInput', () => {
|
|
|
70
232
|
const handleChange = vi.fn();
|
|
71
233
|
render(<CommandPaletteInput onValueChange={handleChange} />);
|
|
72
234
|
|
|
73
|
-
|
|
74
|
-
|
|
235
|
+
fireEvent.change(screen.getByRole('searchbox'), {
|
|
236
|
+
target: {value: 'test'},
|
|
237
|
+
});
|
|
75
238
|
|
|
76
239
|
expect(handleChange).toHaveBeenCalledWith('test');
|
|
77
240
|
});
|
|
78
241
|
|
|
79
242
|
it('displays controlled value', () => {
|
|
80
243
|
render(<CommandPaletteInput value="hello" onValueChange={() => {}} />);
|
|
81
|
-
expect(screen.getByRole('
|
|
244
|
+
expect(screen.getByRole('searchbox')).toHaveValue('hello');
|
|
82
245
|
});
|
|
83
246
|
|
|
84
247
|
it('forwards native onChange alongside onValueChange', () => {
|
|
@@ -92,46 +255,207 @@ describe('CommandPaletteInput', () => {
|
|
|
92
255
|
/>,
|
|
93
256
|
);
|
|
94
257
|
|
|
95
|
-
|
|
96
|
-
fireEvent.change(input, {target: {value: 'x'}});
|
|
258
|
+
fireEvent.change(screen.getByRole('searchbox'), {target: {value: 'x'}});
|
|
97
259
|
|
|
98
260
|
expect(handleChange).toHaveBeenCalledWith('x');
|
|
99
261
|
expect(handleNativeChange).toHaveBeenCalled();
|
|
100
262
|
});
|
|
101
263
|
|
|
102
|
-
it('
|
|
103
|
-
render(<CommandPaletteInput />);
|
|
264
|
+
it('keeps visible numeric end content in the trailing group', () => {
|
|
265
|
+
render(<CommandPaletteInput endContent={0} />);
|
|
266
|
+
const input = screen.getByRole('searchbox');
|
|
267
|
+
const trailingGroup = screen.getByText('0');
|
|
268
|
+
expect(trailingGroup.tagName).toBe('SPAN');
|
|
269
|
+
expect(input.parentElement?.lastElementChild).toBe(trailingGroup);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it.each([false, true, '', null, undefined])(
|
|
273
|
+
'omits trailing chrome for empty end content %#',
|
|
274
|
+
endContent => {
|
|
275
|
+
render(<CommandPaletteInput endContent={endContent} />);
|
|
276
|
+
expect(
|
|
277
|
+
screen.getByRole('searchbox').parentElement?.children,
|
|
278
|
+
).toHaveLength(2);
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
it('does not schedule focus when hasAutoFocus is false', async () => {
|
|
283
|
+
const focusSpy = vi.spyOn(HTMLElement.prototype, 'focus');
|
|
284
|
+
render(<CommandPaletteInput hasAutoFocus={false} />);
|
|
285
|
+
|
|
286
|
+
const input = screen.getByRole('searchbox');
|
|
287
|
+
await new Promise<void>(resolve => requestAnimationFrame(() => resolve()));
|
|
288
|
+
const inputFocusCalls = focusSpy.mock.calls.filter((_, i) => {
|
|
289
|
+
return focusSpy.mock.contexts[i] === input;
|
|
290
|
+
});
|
|
291
|
+
expect(input).not.toHaveAttribute('data-autofocus');
|
|
292
|
+
expect(inputFocusCalls).toHaveLength(0);
|
|
293
|
+
focusSpy.mockRestore();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('forwards the ref and native data attributes to the query field', () => {
|
|
297
|
+
const ref = createRef<HTMLInputElement>();
|
|
298
|
+
render(<CommandPaletteInput ref={ref} data-testid="query-field" />);
|
|
299
|
+
|
|
300
|
+
expect(ref.current).toBe(screen.getByTestId('query-field'));
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('CommandPaletteInput popup context', () => {
|
|
305
|
+
it('preserves component-owned combobox semantics against rest props', () => {
|
|
306
|
+
render(
|
|
307
|
+
<CommandPaletteContext value={makeContext({isOpen: false})}>
|
|
308
|
+
<CommandPaletteInput
|
|
309
|
+
role="button"
|
|
310
|
+
aria-expanded="true"
|
|
311
|
+
aria-autocomplete="none"
|
|
312
|
+
aria-controls="wrong-list"
|
|
313
|
+
aria-activedescendant="wrong-item"
|
|
314
|
+
/>
|
|
315
|
+
</CommandPaletteContext>,
|
|
316
|
+
);
|
|
317
|
+
|
|
104
318
|
const input = screen.getByRole('combobox');
|
|
105
|
-
expect(input).toHaveAttribute('aria-expanded', '
|
|
319
|
+
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
106
320
|
expect(input).toHaveAttribute('aria-autocomplete', 'list');
|
|
321
|
+
expect(input).toHaveAttribute('aria-controls', 'list-1');
|
|
322
|
+
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('renders the busy spinner before trailing content', () => {
|
|
326
|
+
render(
|
|
327
|
+
<CommandPaletteContext value={makeContext({isBusy: true})}>
|
|
328
|
+
<CommandPaletteInput
|
|
329
|
+
endContent={<span data-testid="trailing-content">⌘K</span>}
|
|
330
|
+
/>
|
|
331
|
+
</CommandPaletteContext>,
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
const spinner = screen.getByRole('status');
|
|
335
|
+
const trailingContent = screen.getByTestId('trailing-content');
|
|
336
|
+
expect(trailingContent.parentElement?.firstElementChild).toContainElement(
|
|
337
|
+
spinner,
|
|
338
|
+
);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('reports the contextual open state and controlled list', () => {
|
|
342
|
+
render(
|
|
343
|
+
<CommandPaletteContext value={makeContext()}>
|
|
344
|
+
<CommandPaletteInput />
|
|
345
|
+
</CommandPaletteContext>,
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
const input = screen.getByRole('combobox');
|
|
349
|
+
expect(input).toHaveAttribute('aria-expanded', 'true');
|
|
350
|
+
expect(input).toHaveAttribute('aria-controls', 'list-1');
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('exposes the highlighted option as the active descendant', () => {
|
|
354
|
+
render(
|
|
355
|
+
<CommandPaletteContext
|
|
356
|
+
value={makeContext({
|
|
357
|
+
highlightedIndex: 2,
|
|
358
|
+
selectableItems: [{value: 'a'}, {value: 'b'}, {value: 'c'}],
|
|
359
|
+
})}>
|
|
360
|
+
<CommandPaletteInput />
|
|
361
|
+
</CommandPaletteContext>,
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
expect(screen.getByRole('combobox')).toHaveAttribute(
|
|
365
|
+
'aria-activedescendant',
|
|
366
|
+
'item-2',
|
|
367
|
+
);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('reads and writes search state through context', () => {
|
|
371
|
+
const setSearch = vi.fn();
|
|
372
|
+
render(
|
|
373
|
+
<CommandPaletteContext value={makeContext({search: 'hel', setSearch})}>
|
|
374
|
+
<CommandPaletteInput />
|
|
375
|
+
</CommandPaletteContext>,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
const input = screen.getByRole('combobox');
|
|
379
|
+
expect(input).toHaveValue('hel');
|
|
380
|
+
fireEvent.change(input, {target: {value: 'help'}});
|
|
381
|
+
expect(setSearch).toHaveBeenCalledWith('help');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('gives explicit search state precedence over context', () => {
|
|
385
|
+
const setSearch = vi.fn();
|
|
386
|
+
const onValueChange = vi.fn();
|
|
387
|
+
render(
|
|
388
|
+
<CommandPaletteContext
|
|
389
|
+
value={makeContext({search: 'context', setSearch})}>
|
|
390
|
+
<CommandPaletteInput value="explicit" onValueChange={onValueChange} />
|
|
391
|
+
</CommandPaletteContext>,
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
const input = screen.getByRole('combobox');
|
|
395
|
+
expect(input).toHaveValue('explicit');
|
|
396
|
+
fireEvent.change(input, {target: {value: 'next'}});
|
|
397
|
+
expect(onValueChange).toHaveBeenCalledWith('next');
|
|
398
|
+
expect(setSearch).not.toHaveBeenCalled();
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('does not delegate a consumer-prevented keyboard event', () => {
|
|
402
|
+
const onKeyDown = vi.fn();
|
|
403
|
+
render(
|
|
404
|
+
<CommandPaletteContext value={makeContext({onKeyDown})}>
|
|
405
|
+
<CommandPaletteInput onKeyDown={event => event.preventDefault()} />
|
|
406
|
+
</CommandPaletteContext>,
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
fireEvent.keyDown(screen.getByRole('combobox'), {key: 'ArrowDown'});
|
|
410
|
+
expect(onKeyDown).not.toHaveBeenCalled();
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('omits popup ARIA while the result surface has no listbox', () => {
|
|
414
|
+
render(
|
|
415
|
+
<CommandPaletteContext value={makeContext({selectableItems: []})}>
|
|
416
|
+
<CommandPaletteInput />
|
|
417
|
+
</CommandPaletteContext>,
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const input = screen.getByRole('combobox');
|
|
421
|
+
expect(input).toHaveAttribute('aria-expanded', 'false');
|
|
422
|
+
expect(input).not.toHaveAttribute('aria-controls');
|
|
423
|
+
expect(input).not.toHaveAttribute('aria-activedescendant');
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('omits an active descendant whose highlighted index has no option', () => {
|
|
427
|
+
render(
|
|
428
|
+
<CommandPaletteContext
|
|
429
|
+
value={makeContext({
|
|
430
|
+
highlightedIndex: 1,
|
|
431
|
+
selectableItems: [{value: 'home'}],
|
|
432
|
+
})}>
|
|
433
|
+
<CommandPaletteInput />
|
|
434
|
+
</CommandPaletteContext>,
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
expect(screen.getByRole('combobox')).not.toHaveAttribute(
|
|
438
|
+
'aria-activedescendant',
|
|
439
|
+
);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('does not route IME composition keys to palette commands', () => {
|
|
443
|
+
const onKeyDown = vi.fn();
|
|
444
|
+
render(
|
|
445
|
+
<CommandPaletteContext value={makeContext({onKeyDown})}>
|
|
446
|
+
<CommandPaletteInput />
|
|
447
|
+
</CommandPaletteContext>,
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
fireEvent.keyDown(screen.getByRole('combobox'), {
|
|
451
|
+
key: 'Enter',
|
|
452
|
+
isComposing: true,
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
expect(onKeyDown).not.toHaveBeenCalled();
|
|
107
456
|
});
|
|
108
|
-
});
|
|
109
457
|
|
|
110
|
-
|
|
111
|
-
function makeContext(
|
|
112
|
-
overrides: Partial<CommandPaletteContextValue> = {},
|
|
113
|
-
): CommandPaletteContextValue {
|
|
114
|
-
return {
|
|
115
|
-
search: '',
|
|
116
|
-
setSearch: vi.fn(),
|
|
117
|
-
value: '',
|
|
118
|
-
setValue: vi.fn(),
|
|
119
|
-
listId: 'list-1',
|
|
120
|
-
highlightedIndex: -1,
|
|
121
|
-
setHighlightedIndex: vi.fn(),
|
|
122
|
-
getItemId: (i: number) => `item-${i}`,
|
|
123
|
-
selectableItems: [],
|
|
124
|
-
searchResults: [],
|
|
125
|
-
selectItem: vi.fn(),
|
|
126
|
-
onKeyDown: vi.fn(),
|
|
127
|
-
onClose: vi.fn(),
|
|
128
|
-
isOpen: true,
|
|
129
|
-
isBusy: false,
|
|
130
|
-
...overrides,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
it('does not auto-focus inside an inline dialog', () => {
|
|
458
|
+
it('does not auto-focus inside an inline dialog', async () => {
|
|
135
459
|
const focusSpy = vi.spyOn(HTMLElement.prototype, 'focus');
|
|
136
460
|
render(
|
|
137
461
|
<Dialog isOpen isInline onOpenChange={() => {}}>
|
|
@@ -142,6 +466,7 @@ describe('CommandPaletteInput dialog context', () => {
|
|
|
142
466
|
);
|
|
143
467
|
|
|
144
468
|
const input = screen.getByRole('combobox');
|
|
469
|
+
await new Promise<void>(resolve => requestAnimationFrame(() => resolve()));
|
|
145
470
|
const inputFocusCalls = focusSpy.mock.calls.filter((_, i) => {
|
|
146
471
|
return focusSpy.mock.contexts[i] === input;
|
|
147
472
|
});
|
|
@@ -156,7 +481,6 @@ describe('CommandPaletteInput dialog context', () => {
|
|
|
156
481
|
</CommandPaletteContext>,
|
|
157
482
|
);
|
|
158
483
|
|
|
159
|
-
// Auto-focus uses requestAnimationFrame — flush it
|
|
160
484
|
await vi.waitFor(() => {
|
|
161
485
|
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
162
486
|
});
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* SYNC: When modified, update these files to stay in sync:
|
|
11
11
|
* - /apps/storybook/stories/CommandPalette.stories.tsx
|
|
12
|
+
* - /apps/storybook/stories/CommandPaletteInput.stories.tsx
|
|
12
13
|
* - /packages/cli/assets/templates/blocks/components/CommandPalette/ (showcase blocks)
|
|
13
14
|
*/
|
|
14
15
|
|
|
@@ -16,7 +17,7 @@ import {useCallback, useEffect, useRef, type ReactNode} from 'react';
|
|
|
16
17
|
import * as stylex from '@stylexjs/stylex';
|
|
17
18
|
import {Icon} from '../Icon';
|
|
18
19
|
import {Spinner} from '../Spinner';
|
|
19
|
-
import {mergeProps} from '../utils';
|
|
20
|
+
import {isImeKeyEvent, isRenderable, mergeProps} from '../utils';
|
|
20
21
|
import {
|
|
21
22
|
colorVars,
|
|
22
23
|
typeScaleVars,
|
|
@@ -112,14 +113,14 @@ export interface CommandPaletteInputProps extends Omit<
|
|
|
112
113
|
|
|
113
114
|
/**
|
|
114
115
|
* Placeholder text for the input.
|
|
115
|
-
* @default 'Search
|
|
116
|
+
* @default 'Search…'
|
|
116
117
|
*/
|
|
117
118
|
placeholder?: string;
|
|
118
119
|
|
|
119
120
|
/**
|
|
120
|
-
* Accessible label for the
|
|
121
|
-
*
|
|
122
|
-
*
|
|
121
|
+
* Accessible label for the query field, announced by screen readers. The
|
|
122
|
+
* placeholder is never the field's name.
|
|
123
|
+
* @default 'Search commands'
|
|
123
124
|
*/
|
|
124
125
|
label?: string;
|
|
125
126
|
|
|
@@ -178,12 +179,42 @@ export function CommandPaletteInput({
|
|
|
178
179
|
const placeholder =
|
|
179
180
|
placeholderFromProps ?? t('@astryx.commandPalette.input.placeholder');
|
|
180
181
|
const ctx = useCommandPaletteContext();
|
|
182
|
+
// Inside CommandPalette the results surface is a listbox only while it holds
|
|
183
|
+
// selectable results; empty and pending states expose no popup.
|
|
184
|
+
const hasListbox = ctx ? ctx.selectableItems.length > 0 : true;
|
|
181
185
|
const dialogContext = useDialogContext();
|
|
182
186
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
183
187
|
|
|
184
188
|
// Use context values as fallback
|
|
185
189
|
const value = controlledValue ?? ctx?.search;
|
|
186
190
|
const handleValueChange = onValueChange ?? ctx?.setSearch;
|
|
191
|
+
const accessibleLabel =
|
|
192
|
+
props['aria-label'] ?? label ?? t('@astryx.commandPalette.input.label');
|
|
193
|
+
const hasEndContent = isRenderable(endContent);
|
|
194
|
+
// A popup attribute the caller passes is caller-owned even while its value
|
|
195
|
+
// is undefined (`aria-expanded={open || undefined}`), so the role stays
|
|
196
|
+
// stable as the caller's popup opens and closes, and a present value is
|
|
197
|
+
// passed through unchanged.
|
|
198
|
+
// `aria-haspopup` counts unless it says there is no popup.
|
|
199
|
+
const callerSets = (key: keyof typeof props) => Object.hasOwn(props, key);
|
|
200
|
+
const callerDeclaresPopup =
|
|
201
|
+
callerSets('aria-haspopup') &&
|
|
202
|
+
props['aria-haspopup'] !== false &&
|
|
203
|
+
props['aria-haspopup'] !== 'false';
|
|
204
|
+
const callerOwnsPopup =
|
|
205
|
+
!ctx &&
|
|
206
|
+
(props.role === 'combobox' ||
|
|
207
|
+
callerSets('aria-expanded') ||
|
|
208
|
+
callerSets('aria-autocomplete') ||
|
|
209
|
+
callerSets('aria-controls') ||
|
|
210
|
+
callerSets('aria-owns') ||
|
|
211
|
+
callerSets('aria-activedescendant') ||
|
|
212
|
+
callerDeclaresPopup);
|
|
213
|
+
const standaloneRole = callerSets('role')
|
|
214
|
+
? props.role
|
|
215
|
+
: callerOwnsPopup
|
|
216
|
+
? 'combobox'
|
|
217
|
+
: 'searchbox';
|
|
187
218
|
|
|
188
219
|
// When rendered inside an inline dialog, disable auto-focus by default
|
|
189
220
|
// to avoid stealing focus from the surrounding page.
|
|
@@ -202,7 +233,10 @@ export function CommandPaletteInput({
|
|
|
202
233
|
const handleKeyDown = useCallback(
|
|
203
234
|
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
204
235
|
onKeyDown?.(e);
|
|
205
|
-
if (e.defaultPrevented) {
|
|
236
|
+
if (e.defaultPrevented || isImeKeyEvent(e.nativeEvent)) {
|
|
237
|
+
// An in-progress IME composition owns Enter, Escape, and arrows. Let
|
|
238
|
+
// the input method finish or cancel composition without selecting or
|
|
239
|
+
// dismissing palette content.
|
|
206
240
|
return;
|
|
207
241
|
}
|
|
208
242
|
// Delegate to useCombobox's keyboard handler from context
|
|
@@ -223,20 +257,41 @@ export function CommandPaletteInput({
|
|
|
223
257
|
<Icon icon="search" size="sm" color="inherit" />
|
|
224
258
|
</span>
|
|
225
259
|
<input
|
|
260
|
+
{...props}
|
|
226
261
|
ref={useMergedRefs(ref, inputRef)}
|
|
262
|
+
{...stylex.props(styles.input)}
|
|
227
263
|
type="text"
|
|
228
|
-
role=
|
|
229
|
-
aria-expanded={
|
|
230
|
-
|
|
231
|
-
|
|
264
|
+
role={ctx ? 'combobox' : standaloneRole}
|
|
265
|
+
aria-expanded={
|
|
266
|
+
ctx
|
|
267
|
+
? ctx.isOpen && hasListbox
|
|
268
|
+
: callerSets('aria-expanded')
|
|
269
|
+
? props['aria-expanded']
|
|
270
|
+
: callerOwnsPopup || undefined
|
|
271
|
+
}
|
|
272
|
+
aria-autocomplete={
|
|
273
|
+
ctx
|
|
274
|
+
? 'list'
|
|
275
|
+
: callerSets('aria-autocomplete')
|
|
276
|
+
? props['aria-autocomplete']
|
|
277
|
+
: callerOwnsPopup
|
|
278
|
+
? 'list'
|
|
279
|
+
: undefined
|
|
280
|
+
}
|
|
281
|
+
aria-controls={
|
|
282
|
+
ctx ? (hasListbox ? ctx.listId : undefined) : props['aria-controls']
|
|
283
|
+
}
|
|
232
284
|
aria-activedescendant={
|
|
233
|
-
ctx
|
|
234
|
-
?
|
|
235
|
-
|
|
285
|
+
ctx
|
|
286
|
+
? hasListbox &&
|
|
287
|
+
ctx.highlightedIndex >= 0 &&
|
|
288
|
+
ctx.highlightedIndex < ctx.selectableItems.length
|
|
289
|
+
? ctx.getItemId(ctx.highlightedIndex)
|
|
290
|
+
: undefined
|
|
291
|
+
: props['aria-activedescendant']
|
|
236
292
|
}
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
aria-label={label ?? placeholder}
|
|
293
|
+
// Placeholder text is never the field's name (AST-056 AR2).
|
|
294
|
+
aria-label={accessibleLabel}
|
|
240
295
|
placeholder={placeholder}
|
|
241
296
|
value={value}
|
|
242
297
|
data-autofocus={effectiveAutoFocus || undefined}
|
|
@@ -245,19 +300,17 @@ export function CommandPaletteInput({
|
|
|
245
300
|
onChange?.(e);
|
|
246
301
|
}}
|
|
247
302
|
onKeyDown={handleKeyDown}
|
|
248
|
-
{...stylex.props(styles.input)}
|
|
249
|
-
{...props}
|
|
250
303
|
/>
|
|
251
|
-
{(ctx?.isBusy ||
|
|
304
|
+
{(ctx?.isBusy || hasEndContent) && (
|
|
252
305
|
<span {...stylex.props(styles.end)}>
|
|
253
306
|
{ctx?.isBusy && (
|
|
254
307
|
<span {...stylex.props(styles.icon, styles.spinner)}>
|
|
255
308
|
<Spinner size="sm" />
|
|
256
309
|
</span>
|
|
257
310
|
)}
|
|
258
|
-
{endContent}
|
|
311
|
+
{hasEndContent && endContent}
|
|
259
312
|
</span>
|
|
260
|
-
)}
|
|
313
|
+
)}
|
|
261
314
|
</div>
|
|
262
315
|
);
|
|
263
316
|
}
|