@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
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file CommandPalette.a11y.chromium.spec.ts
|
|
5
|
+
* @input Uses the component-owned Storybook fixture and exact-head build stamp
|
|
6
|
+
* @output Chromium screenshots and fail-closed semantic sensor receipts
|
|
7
|
+
* @position Browser evidence for the CommandPalette audit
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {createHash} from 'node:crypto';
|
|
11
|
+
import {execFileSync} from 'node:child_process';
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import {expect, test, type Page} from '@playwright/test';
|
|
15
|
+
import {holdMotionStill} from '@astryxdesign/a11y-spec/chromium';
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_STORYBOOK_DIR,
|
|
18
|
+
serveStorybook,
|
|
19
|
+
type StaticServer,
|
|
20
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
21
|
+
|
|
22
|
+
const OUTPUT = path.resolve('test-results/command-palette-audit-evidence');
|
|
23
|
+
const STORY_ID = 'core-commandpalette--audit-matrix';
|
|
24
|
+
const WIDE = {width: 1024, height: 900};
|
|
25
|
+
const NARROW = {width: 320, height: 900};
|
|
26
|
+
|
|
27
|
+
interface Case {
|
|
28
|
+
state: string;
|
|
29
|
+
mode: 'light' | 'dark';
|
|
30
|
+
direction: 'ltr' | 'rtl';
|
|
31
|
+
viewport: {width: number; height: number};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const CASES: Case[] = [
|
|
35
|
+
{state: 'wide-light-ltr', mode: 'light', direction: 'ltr', viewport: WIDE},
|
|
36
|
+
{state: 'wide-dark-ltr', mode: 'dark', direction: 'ltr', viewport: WIDE},
|
|
37
|
+
{state: 'wide-light-rtl', mode: 'light', direction: 'rtl', viewport: WIDE},
|
|
38
|
+
{state: 'wide-dark-rtl', mode: 'dark', direction: 'rtl', viewport: WIDE},
|
|
39
|
+
{
|
|
40
|
+
state: 'narrow-light',
|
|
41
|
+
mode: 'light',
|
|
42
|
+
direction: 'ltr',
|
|
43
|
+
viewport: NARROW,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
state: 'narrow-dark',
|
|
47
|
+
mode: 'dark',
|
|
48
|
+
direction: 'ltr',
|
|
49
|
+
viewport: NARROW,
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const frames: Record<string, unknown>[] = [];
|
|
54
|
+
const stateVisualMatrix: Record<string, unknown>[] = [];
|
|
55
|
+
const contrastPairMatrix: Record<string, unknown>[] = [];
|
|
56
|
+
let storybook: StaticServer;
|
|
57
|
+
let checkoutSha: string;
|
|
58
|
+
let storybookSha: string;
|
|
59
|
+
let browserVersion = 'unknown';
|
|
60
|
+
|
|
61
|
+
test.beforeAll(async () => {
|
|
62
|
+
checkoutSha = execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
63
|
+
encoding: 'utf8',
|
|
64
|
+
}).trim();
|
|
65
|
+
const expectedHead = process.env.ASTRYX_HEAD_SHA;
|
|
66
|
+
if (expectedHead && expectedHead !== checkoutSha) {
|
|
67
|
+
throw new Error('The audited checkout does not match the PR head');
|
|
68
|
+
}
|
|
69
|
+
storybook = await serveStorybook(
|
|
70
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
71
|
+
);
|
|
72
|
+
const stamp = await fetch(`${storybook.origin}/astryx-build-sha.txt`);
|
|
73
|
+
if (!stamp.ok) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`The Storybook build has no source stamp (${stamp.status})`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
storybookSha = (await stamp.text()).trim();
|
|
79
|
+
if (storybookSha !== checkoutSha) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
'The served Storybook build does not match the audited checkout',
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
fs.mkdirSync(OUTPUT, {recursive: true});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test.afterAll(async () => {
|
|
88
|
+
if (checkoutSha && storybookSha) {
|
|
89
|
+
fs.writeFileSync(
|
|
90
|
+
path.join(OUTPUT, 'manifest.json'),
|
|
91
|
+
`${JSON.stringify(
|
|
92
|
+
{
|
|
93
|
+
version: 1,
|
|
94
|
+
component: 'core/CommandPalette',
|
|
95
|
+
headSha: checkoutSha,
|
|
96
|
+
storybookSha,
|
|
97
|
+
browser: browserVersion,
|
|
98
|
+
frames,
|
|
99
|
+
stateVisualMatrix,
|
|
100
|
+
contrastPairMatrix,
|
|
101
|
+
},
|
|
102
|
+
null,
|
|
103
|
+
2,
|
|
104
|
+
)}\n`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
await storybook?.close();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
async function capture(page: Page, scenario: Case) {
|
|
111
|
+
const errors: string[] = [];
|
|
112
|
+
const onPageError = (error: Error) =>
|
|
113
|
+
errors.push(`pageerror: ${String(error)}`);
|
|
114
|
+
const onConsole = (message: {type(): string; text(): string}) => {
|
|
115
|
+
if (message.type() === 'error') {
|
|
116
|
+
errors.push(`console: ${message.text()}`);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
page.on('pageerror', onPageError);
|
|
120
|
+
page.on('console', onConsole);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await page.setViewportSize(scenario.viewport);
|
|
124
|
+
await page.emulateMedia({reducedMotion: 'reduce'});
|
|
125
|
+
browserVersion = page.context().browser()?.version() ?? 'unknown';
|
|
126
|
+
await page.goto(
|
|
127
|
+
`${storybook.origin}/iframe.html?id=${STORY_ID}&viewMode=story&globals=colorMode:${scenario.mode};astryxTheme:neutral;direction:${scenario.direction}`,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const canvas = page.locator('#storybook-root');
|
|
131
|
+
const subject = canvas.locator('[data-command-palette-audit-matrix]');
|
|
132
|
+
await subject.waitFor();
|
|
133
|
+
await expect(subject.locator('[role="region"]')).toHaveCount(3);
|
|
134
|
+
await expect(
|
|
135
|
+
subject
|
|
136
|
+
.getByRole('region', {name: 'Pending command palette'})
|
|
137
|
+
.locator('.astryx-spinner[role="status"]'),
|
|
138
|
+
).toHaveCount(1);
|
|
139
|
+
await page.waitForTimeout(200);
|
|
140
|
+
await holdMotionStill(page);
|
|
141
|
+
await page.evaluate(async () => document.fonts.ready);
|
|
142
|
+
await page.waitForFunction(
|
|
143
|
+
expected =>
|
|
144
|
+
document.documentElement.getAttribute('data-theme') === expected,
|
|
145
|
+
scenario.mode,
|
|
146
|
+
);
|
|
147
|
+
await page.waitForFunction(
|
|
148
|
+
expected =>
|
|
149
|
+
document.querySelector('#storybook-root [dir]')?.getAttribute('dir') ===
|
|
150
|
+
expected,
|
|
151
|
+
scenario.direction,
|
|
152
|
+
);
|
|
153
|
+
await subject.scrollIntoViewIfNeeded();
|
|
154
|
+
await page.evaluate(async () => {
|
|
155
|
+
await new Promise<void>(resolve =>
|
|
156
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve())),
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const initialBox = await subject.boundingBox();
|
|
161
|
+
if (initialBox == null) {
|
|
162
|
+
throw new Error(`${scenario.state}: the audit matrix has no layout box`);
|
|
163
|
+
}
|
|
164
|
+
await page.evaluate(async () => {
|
|
165
|
+
await new Promise<void>(resolve =>
|
|
166
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve())),
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
const settledBox = await subject.boundingBox();
|
|
170
|
+
if (settledBox == null) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`${scenario.state}: the audit matrix lost its layout box before capture`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const geometryDelta = Math.max(
|
|
176
|
+
Math.abs(settledBox.x - initialBox.x),
|
|
177
|
+
Math.abs(settledBox.y - initialBox.y),
|
|
178
|
+
Math.abs(settledBox.width - initialBox.width),
|
|
179
|
+
Math.abs(settledBox.height - initialBox.height),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const observed = await subject.evaluate((node, expectedDirection) => {
|
|
183
|
+
type Color = {r: number; g: number; b: number; a: number};
|
|
184
|
+
const elements = <T extends Element>(selector: string) => [
|
|
185
|
+
...node.querySelectorAll<T>(selector),
|
|
186
|
+
];
|
|
187
|
+
const geometry = (element: Element) => {
|
|
188
|
+
const rect = element.getBoundingClientRect();
|
|
189
|
+
return {
|
|
190
|
+
x: rect.x,
|
|
191
|
+
y: rect.y,
|
|
192
|
+
width: rect.width,
|
|
193
|
+
height: rect.height,
|
|
194
|
+
right: rect.right,
|
|
195
|
+
bottom: rect.bottom,
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
const parseColor = (value: string): Color => {
|
|
199
|
+
const channels = value.match(/[\d.]+/g)?.map(Number);
|
|
200
|
+
if (!channels || channels.length < 3) {
|
|
201
|
+
throw new Error(`Cannot parse computed color: ${value}`);
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
r: channels[0] ?? 0,
|
|
205
|
+
g: channels[1] ?? 0,
|
|
206
|
+
b: channels[2] ?? 0,
|
|
207
|
+
a: channels[3] ?? 1,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
const composite = (foreground: Color, background: Color): Color => {
|
|
211
|
+
const a = foreground.a + background.a * (1 - foreground.a);
|
|
212
|
+
if (a === 0) {
|
|
213
|
+
return {r: 0, g: 0, b: 0, a: 0};
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
r:
|
|
217
|
+
(foreground.r * foreground.a +
|
|
218
|
+
background.r * background.a * (1 - foreground.a)) /
|
|
219
|
+
a,
|
|
220
|
+
g:
|
|
221
|
+
(foreground.g * foreground.a +
|
|
222
|
+
background.g * background.a * (1 - foreground.a)) /
|
|
223
|
+
a,
|
|
224
|
+
b:
|
|
225
|
+
(foreground.b * foreground.a +
|
|
226
|
+
background.b * background.a * (1 - foreground.a)) /
|
|
227
|
+
a,
|
|
228
|
+
a,
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
const renderedBackground = (element: Element): Color => {
|
|
232
|
+
const layers: Color[] = [];
|
|
233
|
+
let current: Element | null = element;
|
|
234
|
+
while (current) {
|
|
235
|
+
const layer = parseColor(getComputedStyle(current).backgroundColor);
|
|
236
|
+
if (layer.a > 0) {
|
|
237
|
+
layers.push(layer);
|
|
238
|
+
}
|
|
239
|
+
if (layer.a >= 0.999) {
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
current = current.parentElement;
|
|
243
|
+
}
|
|
244
|
+
return layers
|
|
245
|
+
.reverse()
|
|
246
|
+
.reduce(
|
|
247
|
+
(background, foreground) => composite(foreground, background),
|
|
248
|
+
{r: 255, g: 255, b: 255, a: 1},
|
|
249
|
+
);
|
|
250
|
+
};
|
|
251
|
+
const formatColor = ({r, g, b, a}: Color) =>
|
|
252
|
+
`rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a.toFixed(3)})`;
|
|
253
|
+
const luminance = ({r, g, b}: Color) => {
|
|
254
|
+
const channel = (value: number) => {
|
|
255
|
+
const normalized = value / 255;
|
|
256
|
+
return normalized <= 0.04045
|
|
257
|
+
? normalized / 12.92
|
|
258
|
+
: ((normalized + 0.055) / 1.055) ** 2.4;
|
|
259
|
+
};
|
|
260
|
+
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
261
|
+
};
|
|
262
|
+
const contrast = (left: Color, right: Color) => {
|
|
263
|
+
const leftLuminance = luminance(left);
|
|
264
|
+
const rightLuminance = luminance(right);
|
|
265
|
+
return (
|
|
266
|
+
(Math.max(leftLuminance, rightLuminance) + 0.05) /
|
|
267
|
+
(Math.min(leftLuminance, rightLuminance) + 0.05)
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
const isVisible = (element: Element) => {
|
|
271
|
+
const rect = element.getBoundingClientRect();
|
|
272
|
+
const style = getComputedStyle(element);
|
|
273
|
+
return (
|
|
274
|
+
rect.width > 0 &&
|
|
275
|
+
rect.height > 0 &&
|
|
276
|
+
style.display !== 'none' &&
|
|
277
|
+
style.visibility !== 'hidden' &&
|
|
278
|
+
style.opacity !== '0'
|
|
279
|
+
);
|
|
280
|
+
};
|
|
281
|
+
const measure = (
|
|
282
|
+
element: Element,
|
|
283
|
+
part: string,
|
|
284
|
+
type: 'text' | 'non-text',
|
|
285
|
+
colorValue?: string,
|
|
286
|
+
styleOverride?: CSSStyleDeclaration,
|
|
287
|
+
) => {
|
|
288
|
+
const style = styleOverride ?? getComputedStyle(element);
|
|
289
|
+
const background = renderedBackground(element);
|
|
290
|
+
const foreground = parseColor(colorValue ?? style.color);
|
|
291
|
+
const renderedForeground = composite(foreground, background);
|
|
292
|
+
const ratio = contrast(renderedForeground, background);
|
|
293
|
+
const fontSize = Number.parseFloat(style.fontSize);
|
|
294
|
+
const fontWeight =
|
|
295
|
+
style.fontWeight === 'bold'
|
|
296
|
+
? 700
|
|
297
|
+
: Number.parseFloat(style.fontWeight) || 400;
|
|
298
|
+
const threshold =
|
|
299
|
+
type === 'non-text' ||
|
|
300
|
+
fontSize >= 24 ||
|
|
301
|
+
(fontSize >= 18.66 && fontWeight >= 700)
|
|
302
|
+
? 3
|
|
303
|
+
: 4.5;
|
|
304
|
+
return {
|
|
305
|
+
part,
|
|
306
|
+
type,
|
|
307
|
+
state: 'rest',
|
|
308
|
+
meaningful: true,
|
|
309
|
+
foreground: colorValue ?? style.color,
|
|
310
|
+
renderedForeground: formatColor(renderedForeground),
|
|
311
|
+
renderedBackdrop: formatColor(background),
|
|
312
|
+
fontSize,
|
|
313
|
+
fontWeight,
|
|
314
|
+
ratio: Number(ratio.toFixed(2)),
|
|
315
|
+
threshold,
|
|
316
|
+
exception: null,
|
|
317
|
+
passed: ratio + 1e-6 >= threshold,
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const regions = elements<HTMLElement>('[role="region"]');
|
|
322
|
+
const inputs = elements<HTMLInputElement>('[role="combobox"]');
|
|
323
|
+
const lists = elements<HTMLElement>('[role="listbox"]');
|
|
324
|
+
const options = elements<HTMLElement>('[role="option"]');
|
|
325
|
+
const groups = elements<HTMLElement>('[role="group"]');
|
|
326
|
+
const empties = elements<HTMLElement>('.astryx-command-palette-empty');
|
|
327
|
+
const footers = elements<HTMLElement>('.astryx-command-palette-footer');
|
|
328
|
+
const icons = elements<HTMLElement>(
|
|
329
|
+
'.astryx-command-palette-input .astryx-icon',
|
|
330
|
+
);
|
|
331
|
+
const spinners = elements<HTMLElement>(
|
|
332
|
+
'.astryx-command-palette-input .astryx-spinner',
|
|
333
|
+
);
|
|
334
|
+
const spinnerArcs = elements<SVGCircleElement>(
|
|
335
|
+
'.astryx-command-palette-input .astryx-spinner circle:last-of-type',
|
|
336
|
+
);
|
|
337
|
+
const shortcuts = elements<HTMLElement>(
|
|
338
|
+
'.astryx-command-palette-footer .astryx-kbd',
|
|
339
|
+
);
|
|
340
|
+
const headings = elements<HTMLElement>('h2');
|
|
341
|
+
const groupHeadings = elements<HTMLElement>(
|
|
342
|
+
'.astryx-command-palette-group-heading',
|
|
343
|
+
);
|
|
344
|
+
const targetSelectors = [
|
|
345
|
+
'.astryx-command-palette-input',
|
|
346
|
+
'.astryx-command-palette-list',
|
|
347
|
+
'.astryx-command-palette-item',
|
|
348
|
+
'.astryx-command-palette-group',
|
|
349
|
+
'.astryx-command-palette-group-heading',
|
|
350
|
+
'.astryx-command-palette-empty',
|
|
351
|
+
'.astryx-command-palette-footer',
|
|
352
|
+
];
|
|
353
|
+
const targets = targetSelectors.flatMap(selector => elements(selector));
|
|
354
|
+
const footerText = footers.flatMap(footer => [
|
|
355
|
+
...footer.querySelectorAll<HTMLElement>(':scope > span'),
|
|
356
|
+
]);
|
|
357
|
+
|
|
358
|
+
const contrastPairs = [
|
|
359
|
+
...headings
|
|
360
|
+
.filter(isVisible)
|
|
361
|
+
.map((element, index) =>
|
|
362
|
+
measure(element, `section-heading-${index + 1}`, 'text'),
|
|
363
|
+
),
|
|
364
|
+
...groupHeadings
|
|
365
|
+
.filter(isVisible)
|
|
366
|
+
.map((element, index) =>
|
|
367
|
+
measure(element, `group-heading-${index + 1}`, 'text'),
|
|
368
|
+
),
|
|
369
|
+
...inputs.filter(isVisible).map((element, index) => {
|
|
370
|
+
const placeholder = getComputedStyle(element, '::placeholder');
|
|
371
|
+
return measure(
|
|
372
|
+
element,
|
|
373
|
+
`input-placeholder-${index + 1}`,
|
|
374
|
+
'text',
|
|
375
|
+
placeholder.color,
|
|
376
|
+
placeholder,
|
|
377
|
+
);
|
|
378
|
+
}),
|
|
379
|
+
...options
|
|
380
|
+
.filter(isVisible)
|
|
381
|
+
.map((element, index) =>
|
|
382
|
+
measure(element, `option-${index + 1}`, 'text'),
|
|
383
|
+
),
|
|
384
|
+
...empties
|
|
385
|
+
.filter(isVisible)
|
|
386
|
+
.map((element, index) =>
|
|
387
|
+
measure(element, `empty-${index + 1}`, 'text'),
|
|
388
|
+
),
|
|
389
|
+
...footerText
|
|
390
|
+
.filter(isVisible)
|
|
391
|
+
.map((element, index) =>
|
|
392
|
+
measure(element, `footer-hint-${index + 1}`, 'text'),
|
|
393
|
+
),
|
|
394
|
+
...shortcuts.flatMap((shortcut, shortcutIndex) =>
|
|
395
|
+
[...shortcut.querySelectorAll<HTMLElement>('kbd')]
|
|
396
|
+
.filter(isVisible)
|
|
397
|
+
.map((element, keyIndex) =>
|
|
398
|
+
measure(
|
|
399
|
+
element,
|
|
400
|
+
`shortcut-${shortcutIndex + 1}-key-${keyIndex + 1}`,
|
|
401
|
+
'text',
|
|
402
|
+
),
|
|
403
|
+
),
|
|
404
|
+
),
|
|
405
|
+
...icons
|
|
406
|
+
.filter(isVisible)
|
|
407
|
+
.map((element, index) =>
|
|
408
|
+
measure(element, `search-icon-${index + 1}`, 'non-text'),
|
|
409
|
+
),
|
|
410
|
+
...spinnerArcs
|
|
411
|
+
.filter(isVisible)
|
|
412
|
+
.map((element, index) =>
|
|
413
|
+
measure(
|
|
414
|
+
element,
|
|
415
|
+
`loading-spinner-${index + 1}`,
|
|
416
|
+
'non-text',
|
|
417
|
+
getComputedStyle(element).stroke,
|
|
418
|
+
),
|
|
419
|
+
),
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
declaredDirection: expectedDirection,
|
|
424
|
+
computedDirections: regions.map(
|
|
425
|
+
region => getComputedStyle(region).direction,
|
|
426
|
+
),
|
|
427
|
+
text: (node as HTMLElement).innerText,
|
|
428
|
+
counts: {
|
|
429
|
+
regions: regions.length,
|
|
430
|
+
inputs: inputs.length,
|
|
431
|
+
lists: lists.length,
|
|
432
|
+
options: options.length,
|
|
433
|
+
groups: groups.length,
|
|
434
|
+
groupHeadings: groupHeadings.length,
|
|
435
|
+
empties: empties.length,
|
|
436
|
+
footers: footers.length,
|
|
437
|
+
icons: icons.length,
|
|
438
|
+
spinners: spinners.length,
|
|
439
|
+
shortcuts: shortcuts.length,
|
|
440
|
+
targets: targets.length,
|
|
441
|
+
},
|
|
442
|
+
inputLinks: inputs.map(input => ({
|
|
443
|
+
name: input.getAttribute('aria-label'),
|
|
444
|
+
controls: input.getAttribute('aria-controls'),
|
|
445
|
+
controlsExistingList:
|
|
446
|
+
input.getAttribute('aria-controls') != null &&
|
|
447
|
+
lists.some(list => list.id === input.getAttribute('aria-controls')),
|
|
448
|
+
expanded: input.getAttribute('aria-expanded'),
|
|
449
|
+
})),
|
|
450
|
+
selectedValues: options
|
|
451
|
+
.filter(option => option.getAttribute('aria-selected') === 'true')
|
|
452
|
+
.map(option => option.getAttribute('data-value')),
|
|
453
|
+
regions: regions.map(region => ({
|
|
454
|
+
name: region.getAttribute('aria-label'),
|
|
455
|
+
geometry: geometry(region),
|
|
456
|
+
})),
|
|
457
|
+
targetGeometry: targets.map(geometry),
|
|
458
|
+
noHorizontalOverflow:
|
|
459
|
+
document.documentElement.scrollWidth <= window.innerWidth + 1 &&
|
|
460
|
+
targets.every(element => {
|
|
461
|
+
const rect = element.getBoundingClientRect();
|
|
462
|
+
return rect.left >= -1 && rect.right <= window.innerWidth + 1;
|
|
463
|
+
}),
|
|
464
|
+
contrastPairs,
|
|
465
|
+
};
|
|
466
|
+
}, scenario.direction);
|
|
467
|
+
|
|
468
|
+
expect(observed.computedDirections).toEqual([
|
|
469
|
+
scenario.direction,
|
|
470
|
+
scenario.direction,
|
|
471
|
+
scenario.direction,
|
|
472
|
+
]);
|
|
473
|
+
expect(observed.counts).toEqual({
|
|
474
|
+
regions: 3,
|
|
475
|
+
inputs: 3,
|
|
476
|
+
lists: 1,
|
|
477
|
+
options: 4,
|
|
478
|
+
groups: 2,
|
|
479
|
+
groupHeadings: 2,
|
|
480
|
+
empties: 2,
|
|
481
|
+
footers: 3,
|
|
482
|
+
icons: 3,
|
|
483
|
+
spinners: 1,
|
|
484
|
+
shortcuts: 12,
|
|
485
|
+
targets: 19,
|
|
486
|
+
});
|
|
487
|
+
expect(observed.inputLinks).toEqual([
|
|
488
|
+
expect.objectContaining({
|
|
489
|
+
name: 'Search commands',
|
|
490
|
+
controlsExistingList: true,
|
|
491
|
+
expanded: 'true',
|
|
492
|
+
}),
|
|
493
|
+
expect.objectContaining({
|
|
494
|
+
name: 'Search commands',
|
|
495
|
+
controls: null,
|
|
496
|
+
controlsExistingList: false,
|
|
497
|
+
expanded: 'false',
|
|
498
|
+
}),
|
|
499
|
+
expect.objectContaining({
|
|
500
|
+
name: 'Search commands',
|
|
501
|
+
controls: null,
|
|
502
|
+
controlsExistingList: false,
|
|
503
|
+
expanded: 'false',
|
|
504
|
+
}),
|
|
505
|
+
]);
|
|
506
|
+
expect(observed.selectedValues).toEqual(['settings']);
|
|
507
|
+
expect(observed.text).toContain('Grouped results');
|
|
508
|
+
expect(observed.text).toContain('Empty bootstrap');
|
|
509
|
+
expect(observed.text).toContain('Pending bootstrap');
|
|
510
|
+
expect(observed.text).toContain('Type to search');
|
|
511
|
+
expect(observed.text).toContain('Create a command');
|
|
512
|
+
expect(observed.text).toContain('Navigate');
|
|
513
|
+
expect(observed.text).toContain('Select');
|
|
514
|
+
expect(observed.text).toContain('Close');
|
|
515
|
+
expect(observed.noHorizontalOverflow).toBe(true);
|
|
516
|
+
expect(geometryDelta).toBeLessThanOrEqual(0.5);
|
|
517
|
+
expect(observed.contrastPairs.length).toBeGreaterThanOrEqual(30);
|
|
518
|
+
expect(observed.contrastPairs.every(pair => pair.passed)).toBe(true);
|
|
519
|
+
expect(errors).toEqual([]);
|
|
520
|
+
|
|
521
|
+
const file = `CommandPalette__${scenario.state}.png`;
|
|
522
|
+
const png = await subject.screenshot({animations: 'disabled'});
|
|
523
|
+
const stateRows = [
|
|
524
|
+
{
|
|
525
|
+
scenario: scenario.state,
|
|
526
|
+
state: 'grouped-results-selected',
|
|
527
|
+
visible: true,
|
|
528
|
+
optionCount: 4,
|
|
529
|
+
groupCount: 2,
|
|
530
|
+
selectedValue: 'settings',
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
scenario: scenario.state,
|
|
534
|
+
state: 'empty-bootstrap',
|
|
535
|
+
visible: true,
|
|
536
|
+
emptyCount: 1,
|
|
537
|
+
listboxPresent: false,
|
|
538
|
+
comboboxExpanded: false,
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
scenario: scenario.state,
|
|
542
|
+
state: 'pending-bootstrap',
|
|
543
|
+
visible: true,
|
|
544
|
+
spinnerCount: 1,
|
|
545
|
+
emptyStateRemainsMounted: true,
|
|
546
|
+
listboxPresent: false,
|
|
547
|
+
comboboxExpanded: false,
|
|
548
|
+
},
|
|
549
|
+
];
|
|
550
|
+
const contrastRows = observed.contrastPairs.map(pair => ({
|
|
551
|
+
scenario: scenario.state,
|
|
552
|
+
...pair,
|
|
553
|
+
}));
|
|
554
|
+
const receipt = {
|
|
555
|
+
state: scenario.state,
|
|
556
|
+
mode: scenario.mode,
|
|
557
|
+
direction: scenario.direction,
|
|
558
|
+
viewport: scenario.viewport,
|
|
559
|
+
headSha: checkoutSha,
|
|
560
|
+
storybookSha,
|
|
561
|
+
image: file,
|
|
562
|
+
imageSha256: createHash('sha256').update(png).digest('hex'),
|
|
563
|
+
imageSize: {
|
|
564
|
+
width: png.readUInt32BE(16),
|
|
565
|
+
height: png.readUInt32BE(20),
|
|
566
|
+
},
|
|
567
|
+
geometryDelta,
|
|
568
|
+
observed,
|
|
569
|
+
stateVisualMatrix: stateRows,
|
|
570
|
+
contrastPairMatrix: contrastRows,
|
|
571
|
+
browser: browserVersion,
|
|
572
|
+
passed: true,
|
|
573
|
+
};
|
|
574
|
+
fs.writeFileSync(path.join(OUTPUT, file), png);
|
|
575
|
+
fs.writeFileSync(
|
|
576
|
+
path.join(OUTPUT, `${file}.sensors.json`),
|
|
577
|
+
`${JSON.stringify(receipt, null, 2)}\n`,
|
|
578
|
+
);
|
|
579
|
+
frames.push({
|
|
580
|
+
state: scenario.state,
|
|
581
|
+
mode: scenario.mode,
|
|
582
|
+
direction: scenario.direction,
|
|
583
|
+
viewport: scenario.viewport,
|
|
584
|
+
passed: true,
|
|
585
|
+
image: receipt.image,
|
|
586
|
+
});
|
|
587
|
+
stateVisualMatrix.push(...stateRows);
|
|
588
|
+
contrastPairMatrix.push(...contrastRows);
|
|
589
|
+
} finally {
|
|
590
|
+
page.off('pageerror', onPageError);
|
|
591
|
+
page.off('console', onConsole);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
for (const scenario of CASES) {
|
|
596
|
+
test(`captures ${scenario.state}`, async ({page}) => {
|
|
597
|
+
await capture(page, scenario);
|
|
598
|
+
});
|
|
599
|
+
}
|
|
@@ -146,8 +146,9 @@ export const docs = {
|
|
|
146
146
|
name: 'statusVariant',
|
|
147
147
|
type: "'attached' | 'detached' | 'tooltip'",
|
|
148
148
|
description:
|
|
149
|
-
'How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment; detached when variant is ghost); detached floats below as a separate element with spacing; tooltip reveals the message in an info-tip button.',
|
|
150
|
-
default:
|
|
149
|
+
'How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment; detached when variant is secondary or ghost); detached floats below as a separate element with spacing; tooltip reveals the message in an info-tip button.',
|
|
150
|
+
default:
|
|
151
|
+
"'attached' for input selectors; 'detached' for secondary and ghost selectors",
|
|
151
152
|
},
|
|
152
153
|
{
|
|
153
154
|
name: 'size',
|
|
@@ -157,9 +158,9 @@ export const docs = {
|
|
|
157
158
|
},
|
|
158
159
|
{
|
|
159
160
|
name: 'variant',
|
|
160
|
-
type: "'input' | 'ghost'",
|
|
161
|
+
type: "'input' | 'secondary' | 'ghost'",
|
|
161
162
|
description:
|
|
162
|
-
'Visual trigger style. Input is the bordered form treatment;
|
|
163
|
+
'Visual trigger style. Input is the bordered form treatment; secondary matches the secondary buttons of the active theme (borderless and neutral-filled by default); ghost is borderless and transparent to match ghost buttons.',
|
|
163
164
|
default: "'input'",
|
|
164
165
|
},
|
|
165
166
|
{
|
|
@@ -264,7 +265,7 @@ export const docs = {
|
|
|
264
265
|
{
|
|
265
266
|
guidance: true,
|
|
266
267
|
description:
|
|
267
|
-
'Use variant="ghost"
|
|
268
|
+
'Use variant="secondary" beside secondary buttons, or variant="ghost" beside ghost buttons; add a startIcon when it helps identify the trigger. Use alignment="end" when a wide surface should align its end edge to the trigger.',
|
|
268
269
|
},
|
|
269
270
|
{
|
|
270
271
|
guidance: true,
|
|
@@ -316,7 +317,7 @@ export const docsDense = {
|
|
|
316
317
|
group: 'Selector',
|
|
317
318
|
category: 'Form Controls',
|
|
318
319
|
description:
|
|
319
|
-
'Input/ghost trigger + dialog-popover shell for rich custom selectors. Content gets value/onChange/close/state; content owns semantics. Use focus hooks and evaluate custom content against WCAG 2.2.',
|
|
320
|
+
'Input/secondary/ghost trigger + dialog-popover shell for rich custom selectors. Content gets value/onChange/close/state; content owns semantics. Use focus hooks and evaluate custom content against WCAG 2.2.',
|
|
320
321
|
usage: {
|
|
321
322
|
anatomy,
|
|
322
323
|
description:
|
|
@@ -376,7 +377,8 @@ export const docsDense = {
|
|
|
376
377
|
changeAction: 'Async action after onChange; drives optimistic value/busy.',
|
|
377
378
|
children: 'Render custom dialog content from (value,onChange,close,state).',
|
|
378
379
|
triggerLabel: 'Closed trigger label/content.',
|
|
379
|
-
variant:
|
|
380
|
+
variant:
|
|
381
|
+
'input for forms; secondary beside secondary buttons; ghost beside ghost buttons.',
|
|
380
382
|
startIcon: 'Leading trigger icon.',
|
|
381
383
|
placement: 'Popup placement.',
|
|
382
384
|
alignment: 'Popup alignment.',
|
|
@@ -397,7 +399,7 @@ export const docsDense = {
|
|
|
397
399
|
controlClassName: 'Class name for the visual control box.',
|
|
398
400
|
controlStyle: 'Inline styles for the visual control box.',
|
|
399
401
|
statusVariant:
|
|
400
|
-
'How status message is placed: attached overlaps below input (detached for ghost); detached floats below w/ spacing; tooltip shows on status icon.',
|
|
402
|
+
'How status message is placed: attached overlaps below input (detached for secondary and ghost); detached floats below w/ spacing; tooltip shows on status icon.',
|
|
401
403
|
accessibility:
|
|
402
404
|
'Custom content must provide its own accessible structure. Use focus hooks and evaluate against WCAG 2.2.',
|
|
403
405
|
},
|
|
@@ -23,7 +23,7 @@ design_specs: []
|
|
|
23
23
|
architecture:
|
|
24
24
|
[architecture:component-theming-surface, architecture:layer-runtime]
|
|
25
25
|
contributing: []
|
|
26
|
-
system_specs: []
|
|
26
|
+
system_specs: [spec:AST-079/DEC-1]
|
|
27
27
|
---
|
|
28
28
|
|
|
29
29
|
# ComplexSelector component contract
|
|
@@ -98,7 +98,8 @@ remain documented in `ComplexSelector.doc.mjs`.
|
|
|
98
98
|
|
|
99
99
|
### Representative states
|
|
100
100
|
|
|
101
|
-
- Input and ghost variants use the same six-part anatomy and current
|
|
101
|
+
- Input, secondary, and ghost variants use the same six-part anatomy and current
|
|
102
|
+
targets.
|
|
102
103
|
- The start slot is absent when `startIcon` is omitted. When present, exactly
|
|
103
104
|
one of Icon-rendered start icon or Caller-rendered start content applies.
|
|
104
105
|
- The Popup remains mounted in both states: it is hidden while closed and shown
|
|
@@ -188,13 +189,17 @@ caller-owned ReactNode content.
|
|
|
188
189
|
|
|
189
190
|
## Decision log
|
|
190
191
|
|
|
191
|
-
- **DEC-1 — Anchored mode through `renderTrigger`.**
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
- **DEC-1 — Anchored mode through `renderTrigger`.**
|
|
193
|
+
|
|
194
|
+
**Decider:** pending owner review
|
|
195
|
+
|
|
196
|
+
A picker anchored in a row the caller owns cannot host the Field shell or a
|
|
197
|
+
nested button. The caller renders the control and spreads the given props; the
|
|
198
|
+
component keeps owning open state through the existing
|
|
199
|
+
`handleRef`/`onOpenChange`.
|
|
200
|
+
|
|
201
|
+
Rejected: an `anchorRef` plus a controlled `isOpen`, per the rule already
|
|
202
|
+
written on `ComplexSelectorHandle`.
|
|
198
203
|
|
|
199
204
|
## Open questions
|
|
200
205
|
|