@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,112 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Slider.a11y.chromium.spec.ts
|
|
5
|
+
* @input Built Slider stories and SLIDER_PATTERN in real Chromium
|
|
6
|
+
* @output Browser evidence for every slider expectation across the binding's states, reconciled against the exact known failures
|
|
7
|
+
* @position Chromium lane of the Slider binding (component:Slider AR1)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
expect,
|
|
12
|
+
test,
|
|
13
|
+
type CDPSession,
|
|
14
|
+
type Locator,
|
|
15
|
+
type Page,
|
|
16
|
+
} from '@playwright/test';
|
|
17
|
+
import {
|
|
18
|
+
SLIDER_PATTERN,
|
|
19
|
+
blockingResults,
|
|
20
|
+
checkAccessibilitySpec,
|
|
21
|
+
formatFailures,
|
|
22
|
+
neverExercised,
|
|
23
|
+
summarize,
|
|
24
|
+
unmatchedKnownFailures,
|
|
25
|
+
type BindingResult,
|
|
26
|
+
} from '@astryxdesign/a11y-spec';
|
|
27
|
+
import {
|
|
28
|
+
createChromiumHarness,
|
|
29
|
+
holdMotionStill,
|
|
30
|
+
} from '@astryxdesign/a11y-spec/chromium';
|
|
31
|
+
import {
|
|
32
|
+
DEFAULT_STORYBOOK_DIR,
|
|
33
|
+
serveStorybook,
|
|
34
|
+
type StaticServer,
|
|
35
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
36
|
+
import {SLIDER_KNOWN_FAILURES} from './Slider.a11y.known-failures';
|
|
37
|
+
import {
|
|
38
|
+
SLIDER_BINDING_STATES,
|
|
39
|
+
type SliderBindingState,
|
|
40
|
+
} from './Slider.a11y.states';
|
|
41
|
+
|
|
42
|
+
let storybook: StaticServer;
|
|
43
|
+
|
|
44
|
+
test.beforeAll(async () => {
|
|
45
|
+
storybook = await serveStorybook(
|
|
46
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test.afterAll(async () => {
|
|
51
|
+
await storybook?.close();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function storyUrl(storyId: string): string {
|
|
55
|
+
return `${storybook.origin}/iframe.html?id=${storyId}&viewMode=story`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function mountState(
|
|
59
|
+
page: Page,
|
|
60
|
+
state: SliderBindingState,
|
|
61
|
+
): Promise<{subject: Locator; related: Record<string, Locator>}> {
|
|
62
|
+
await page.goto(storyUrl(state.storyId), {waitUntil: 'load'});
|
|
63
|
+
await holdMotionStill(page);
|
|
64
|
+
const subject = page
|
|
65
|
+
.locator('#storybook-root')
|
|
66
|
+
.getByRole('slider', {name: state.label});
|
|
67
|
+
await subject.waitFor({state: 'attached'});
|
|
68
|
+
return {subject, related: {}};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function runState(
|
|
72
|
+
page: Page,
|
|
73
|
+
cdp: CDPSession,
|
|
74
|
+
state: SliderBindingState,
|
|
75
|
+
): Promise<BindingResult> {
|
|
76
|
+
return checkAccessibilitySpec({
|
|
77
|
+
spec: SLIDER_PATTERN,
|
|
78
|
+
binding: 'Slider',
|
|
79
|
+
state: state.id,
|
|
80
|
+
facts: state.facts,
|
|
81
|
+
knownFailures: SLIDER_KNOWN_FAILURES,
|
|
82
|
+
mount: async () => {
|
|
83
|
+
const {subject, related} = await mountState(page, state);
|
|
84
|
+
return createChromiumHarness({page, cdp, subject, related});
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
test('every slider expectation is exercised by a Slider state', async ({
|
|
90
|
+
page,
|
|
91
|
+
}) => {
|
|
92
|
+
const cdp = await page.context().newCDPSession(page);
|
|
93
|
+
const results: BindingResult[] = [];
|
|
94
|
+
for (const state of SLIDER_BINDING_STATES) {
|
|
95
|
+
results.push(await runState(page, cdp, state));
|
|
96
|
+
}
|
|
97
|
+
expect(neverExercised(results)).toEqual([]);
|
|
98
|
+
expect(unmatchedKnownFailures(SLIDER_KNOWN_FAILURES, results)).toEqual([]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
for (const state of SLIDER_BINDING_STATES) {
|
|
102
|
+
test(`Slider [${state.id}] — ${state.summary}`, async ({page}) => {
|
|
103
|
+
const cdp = await page.context().newCDPSession(page);
|
|
104
|
+
const result = await runState(page, cdp, state);
|
|
105
|
+
const report = summarize(SLIDER_PATTERN, [result]);
|
|
106
|
+
// Required rows block (AST-020 FR9); advisory rows and exact known
|
|
107
|
+
// failures report.
|
|
108
|
+
expect(formatFailures(blockingResults([result]))).toBe('');
|
|
109
|
+
expect(report.unrunLayers).toEqual([]);
|
|
110
|
+
expect(report.counts.unexpectedPass).toBe(0);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Slider.a11y.known-failures.ts
|
|
5
|
+
* @input The exact slider-contract failures the shipped Slider produces today
|
|
6
|
+
* @output SLIDER_KNOWN_FAILURES — records both lanes reconcile against, so a fix shows up as an unexpected pass and any other failure stays a failure
|
|
7
|
+
* @position Known-failure ledger for the Slider binding
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {KnownFailure} from '@astryxdesign/a11y-spec';
|
|
11
|
+
|
|
12
|
+
export const SLIDER_KNOWN_FAILURES: ReadonlyArray<KnownFailure> = [];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file Slider.a11y.renders.tsx
|
|
7
|
+
* @input One Slider binding state
|
|
8
|
+
* @output The React tree every lane renders for that state (the jsdom test directly, the story through Storybook)
|
|
9
|
+
* @position Shared fixture for the Slider binding
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {useState, type ReactElement} from 'react';
|
|
13
|
+
import {Slider} from '../Slider';
|
|
14
|
+
import type {SliderBindingState} from './Slider.a11y.states';
|
|
15
|
+
|
|
16
|
+
function BoundSlider({state}: {state: SliderBindingState}) {
|
|
17
|
+
const [value, setValue] = useState(state.facts.value);
|
|
18
|
+
return (
|
|
19
|
+
<Slider
|
|
20
|
+
label={state.label}
|
|
21
|
+
min={state.facts.min}
|
|
22
|
+
max={state.facts.max}
|
|
23
|
+
step={state.facts.step}
|
|
24
|
+
value={value}
|
|
25
|
+
onChange={setValue}
|
|
26
|
+
orientation={state.facts.orientation}
|
|
27
|
+
isDisabled={!state.facts.operable}
|
|
28
|
+
disabledMessage={state.disabledMessage}
|
|
29
|
+
formatValue={
|
|
30
|
+
state.formatValue ? current => `${current} percent` : undefined
|
|
31
|
+
}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function renderSliderState(state: SliderBindingState): ReactElement {
|
|
37
|
+
return (
|
|
38
|
+
<div dir={state.direction ?? 'ltr'} style={{padding: 24, width: 320}}>
|
|
39
|
+
<BoundSlider state={state} />
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Slider.a11y.states.ts
|
|
5
|
+
* @input Slider's representative thumb states and the facts SLIDER_PATTERN reads
|
|
6
|
+
* @output SLIDER_BINDING_STATES and the explicit ownership exclusions for this binding
|
|
7
|
+
* @position Binding inventory shared by the jsdom test, the Chromium spec, and the story (component:Slider AR1)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {SliderStateFacts} from '@astryxdesign/a11y-spec';
|
|
11
|
+
|
|
12
|
+
export interface SliderBindingState {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly summary: string;
|
|
15
|
+
readonly storyId: string;
|
|
16
|
+
/** The thumb's accessible name (the visible label). */
|
|
17
|
+
readonly label: string;
|
|
18
|
+
readonly facts: SliderStateFacts;
|
|
19
|
+
/** Props beyond label/min/max/step/value that shape the state. */
|
|
20
|
+
readonly disabledMessage?: string;
|
|
21
|
+
readonly formatValue?: boolean;
|
|
22
|
+
readonly direction?: 'ltr' | 'rtl';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function facts(overrides: Partial<SliderStateFacts> = {}): SliderStateFacts {
|
|
26
|
+
return {
|
|
27
|
+
min: 0,
|
|
28
|
+
max: 100,
|
|
29
|
+
step: 1,
|
|
30
|
+
value: 40,
|
|
31
|
+
orientation: 'horizontal',
|
|
32
|
+
rtl: false,
|
|
33
|
+
operable: true,
|
|
34
|
+
focusable: true,
|
|
35
|
+
valueText: null,
|
|
36
|
+
largeStep: 10,
|
|
37
|
+
...overrides,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const story = (id: string) => `a11y-slider--${id}`;
|
|
42
|
+
|
|
43
|
+
export const SLIDER_BINDING_STATES = [
|
|
44
|
+
{
|
|
45
|
+
id: 'horizontal',
|
|
46
|
+
summary: 'a horizontal single-thumb Slider at 40 of 0–100',
|
|
47
|
+
storyId: story('horizontal'),
|
|
48
|
+
label: 'Volume',
|
|
49
|
+
facts: facts(),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'vertical',
|
|
53
|
+
summary: 'a vertical single-thumb Slider',
|
|
54
|
+
storyId: story('vertical'),
|
|
55
|
+
label: 'Temperature',
|
|
56
|
+
facts: facts({orientation: 'vertical'}),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'horizontal-rtl',
|
|
60
|
+
summary:
|
|
61
|
+
'a horizontal Slider on a right-to-left page: ArrowLeft must raise the value',
|
|
62
|
+
storyId: story('horizontal-rtl'),
|
|
63
|
+
label: 'Volume',
|
|
64
|
+
facts: facts({rtl: true}),
|
|
65
|
+
direction: 'rtl',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'formatted',
|
|
69
|
+
summary: 'a Slider that speaks a formatted value',
|
|
70
|
+
storyId: story('formatted'),
|
|
71
|
+
label: 'Brightness',
|
|
72
|
+
facts: facts({valueText: '40 percent'}),
|
|
73
|
+
formatValue: true,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'disabled-with-message',
|
|
77
|
+
summary:
|
|
78
|
+
'a disabled Slider with a disabled message: the thumb keeps focus and its value',
|
|
79
|
+
storyId: story('disabled-with-message'),
|
|
80
|
+
label: 'Volume',
|
|
81
|
+
facts: facts({operable: false}),
|
|
82
|
+
disabledMessage: 'Volume is locked while sharing your screen',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'step-three',
|
|
86
|
+
summary: 'a Slider with step 3 on a 0–100 range: End must reach 100',
|
|
87
|
+
storyId: story('step-three'),
|
|
88
|
+
label: 'Zoom',
|
|
89
|
+
facts: facts({step: 3, value: 48, largeStep: 30}),
|
|
90
|
+
},
|
|
91
|
+
] as const satisfies ReadonlyArray<SliderBindingState>;
|
|
92
|
+
|
|
93
|
+
export const SLIDER_EXCLUSIONS = [
|
|
94
|
+
{
|
|
95
|
+
id: 'range-thumb-separation',
|
|
96
|
+
owner: 'component:Slider AR1 and Slider.test.tsx',
|
|
97
|
+
reason:
|
|
98
|
+
"A range Slider renders two thumbs whose aria-valuemin/aria-valuemax narrow to the other thumb minus the minimum separation; that two-thumb coupling and the group semantics around them are the binding's own tests. This binding proves the single-thumb pattern each thumb follows.",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'pointer-drag-model',
|
|
102
|
+
owner: 'Slider.test.tsx pointer suites and the Chromium pointer evidence',
|
|
103
|
+
reason:
|
|
104
|
+
"Dragging, track clicks (mirrored under RTL), and the value tooltip are pointer behavior outside the keyboard-and-semantics pattern; the component's own suites prove them.",
|
|
105
|
+
},
|
|
106
|
+
] as const;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
/** @vitest-environment jsdom */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file Slider.a11y.test.tsx
|
|
6
|
+
* @input SLIDER_PATTERN bound to Slider's representative states
|
|
7
|
+
* @output jsdom-lane proof of the DOM expectations and an honest "unrun" report for the browser layers
|
|
8
|
+
* @position Binding test for component:Slider AR1 (the Chromium spec covers the rest)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {cleanup, render, screen} from '@testing-library/react';
|
|
12
|
+
import {describe, expect, it} from 'vitest';
|
|
13
|
+
import {
|
|
14
|
+
SLIDER_PATTERN,
|
|
15
|
+
checkAccessibilitySpec,
|
|
16
|
+
createJsdomHarness,
|
|
17
|
+
expectAccessibilitySpec,
|
|
18
|
+
summarize,
|
|
19
|
+
type BindingResult,
|
|
20
|
+
} from '@astryxdesign/a11y-spec';
|
|
21
|
+
import {SLIDER_KNOWN_FAILURES} from './Slider.a11y.known-failures';
|
|
22
|
+
import {renderSliderState} from './Slider.a11y.renders';
|
|
23
|
+
import {
|
|
24
|
+
SLIDER_BINDING_STATES,
|
|
25
|
+
SLIDER_EXCLUSIONS,
|
|
26
|
+
type SliderBindingState,
|
|
27
|
+
} from './Slider.a11y.states';
|
|
28
|
+
|
|
29
|
+
function thumb(state: SliderBindingState): HTMLElement {
|
|
30
|
+
return screen.getByRole('slider', {name: state.label});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function checkState(state: SliderBindingState): Promise<BindingResult> {
|
|
34
|
+
return checkAccessibilitySpec({
|
|
35
|
+
spec: SLIDER_PATTERN,
|
|
36
|
+
binding: 'Slider',
|
|
37
|
+
state: state.id,
|
|
38
|
+
facts: state.facts,
|
|
39
|
+
knownFailures: SLIDER_KNOWN_FAILURES,
|
|
40
|
+
mount: async () => {
|
|
41
|
+
render(renderSliderState(state));
|
|
42
|
+
return createJsdomHarness({subject: thumb(state), related: {}});
|
|
43
|
+
},
|
|
44
|
+
unmount: cleanup,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe('Slider — slider pattern, jsdom lane', () => {
|
|
49
|
+
it.each(
|
|
50
|
+
SLIDER_BINDING_STATES.map(
|
|
51
|
+
state => [state.id, state.summary, state] as const,
|
|
52
|
+
),
|
|
53
|
+
)('%s — %s', async (_id, _summary, state) => {
|
|
54
|
+
await expectAccessibilitySpec({
|
|
55
|
+
spec: SLIDER_PATTERN,
|
|
56
|
+
binding: 'Slider',
|
|
57
|
+
state: state.id,
|
|
58
|
+
facts: state.facts,
|
|
59
|
+
knownFailures: SLIDER_KNOWN_FAILURES,
|
|
60
|
+
render: () => {
|
|
61
|
+
render(renderSliderState(state));
|
|
62
|
+
},
|
|
63
|
+
subject: () => thumb(state),
|
|
64
|
+
related: () => ({}),
|
|
65
|
+
cleanup,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('runs the DOM layer and reports the browser-owned outcomes as unrun', async () => {
|
|
70
|
+
const results: BindingResult[] = [];
|
|
71
|
+
for (const state of SLIDER_BINDING_STATES) {
|
|
72
|
+
results.push(await checkState(state));
|
|
73
|
+
}
|
|
74
|
+
const report = summarize(SLIDER_PATTERN, results);
|
|
75
|
+
expect(report.counts.pass).toBeGreaterThan(0);
|
|
76
|
+
expect(report.unrunLayers).toEqual(['accessibility-tree', 'real-browser']);
|
|
77
|
+
expect(report.counts.unexpectedPass).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('Slider binding inventory', () => {
|
|
82
|
+
it('gives every representative state a distinct checked-in story', () => {
|
|
83
|
+
const stories = SLIDER_BINDING_STATES.map(state => state.storyId);
|
|
84
|
+
expect(new Set(stories).size).toBe(stories.length);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('keeps range coupling and pointer ownership explicit', () => {
|
|
88
|
+
expect(SLIDER_EXCLUSIONS.map(exclusion => exclusion.id)).toEqual([
|
|
89
|
+
'range-thumb-separation',
|
|
90
|
+
'pointer-drag-model',
|
|
91
|
+
]);
|
|
92
|
+
for (const exclusion of SLIDER_EXCLUSIONS) {
|
|
93
|
+
expect(exclusion.owner.length, exclusion.id).toBeGreaterThan(20);
|
|
94
|
+
expect(exclusion.reason.length, exclusion.id).toBeGreaterThan(80);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('renders exactly one named thumb for every state', () => {
|
|
99
|
+
for (const state of SLIDER_BINDING_STATES) {
|
|
100
|
+
render(renderSliderState(state));
|
|
101
|
+
expect(thumb(state), state.id).toBeTruthy();
|
|
102
|
+
cleanup();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -166,29 +166,17 @@ test('the state inventory describes the labels the page actually renders', async
|
|
|
166
166
|
expect(wrong).toEqual([]);
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// test takes.
|
|
175
|
-
test.setTimeout(5 * 60 * 1000);
|
|
176
|
-
const cdp = await page.context().newCDPSession(page);
|
|
177
|
-
const results: BindingResult[] = [];
|
|
178
|
-
for (const state of SWITCH_BINDING_STATES) {
|
|
179
|
-
results.push(await runState(page, cdp, state));
|
|
180
|
-
}
|
|
181
|
-
// An expectation that applies to nothing is not coverage, however green it
|
|
182
|
-
// looks. This is the only place that can notice: the contract never sees the
|
|
183
|
-
// states, so whether a condition matches a real one is a binding-side fact.
|
|
184
|
-
expect(neverExercised(results)).toEqual([]);
|
|
185
|
-
expect(unmatchedKnownFailures(SWITCH_KNOWN_FAILURES, results)).toEqual([]);
|
|
186
|
-
});
|
|
169
|
+
// Each state's result, recorded by its own test below so the whole-suite check
|
|
170
|
+
// at the end reads it instead of measuring every state a second time. A state
|
|
171
|
+
// whose test did not run in this worker (a filtered run, or a worker restarted
|
|
172
|
+
// after a failure) is measured by the whole-suite check itself.
|
|
173
|
+
const recorded = new Map<string, BindingResult>();
|
|
187
174
|
|
|
188
175
|
for (const state of SWITCH_BINDING_STATES) {
|
|
189
176
|
test(`Switch [${state.id}] — ${state.summary}`, async ({page}) => {
|
|
190
177
|
const cdp = await page.context().newCDPSession(page);
|
|
191
178
|
const result = await runState(page, cdp, state);
|
|
179
|
+
recorded.set(state.id, result);
|
|
192
180
|
const report = summarize(SWITCH_PATTERN, [result]);
|
|
193
181
|
// eslint-disable-next-line no-console -- the report is this run's artifact
|
|
194
182
|
console.log(formatReport(report));
|
|
@@ -201,3 +189,22 @@ for (const state of SWITCH_BINDING_STATES) {
|
|
|
201
189
|
expect(report.counts.unexpectedPass).toBe(0);
|
|
202
190
|
});
|
|
203
191
|
}
|
|
192
|
+
|
|
193
|
+
test('every expectation is exercised by at least one bound state', async ({
|
|
194
|
+
page,
|
|
195
|
+
}) => {
|
|
196
|
+
// A whole-binding sweep: every state's result, read from its own test or,
|
|
197
|
+
// when that test did not run in this worker, measured here (minutes of real
|
|
198
|
+
// browser work in that case, so the budget stays).
|
|
199
|
+
test.setTimeout(5 * 60 * 1000);
|
|
200
|
+
const cdp = await page.context().newCDPSession(page);
|
|
201
|
+
const results: BindingResult[] = [];
|
|
202
|
+
for (const state of SWITCH_BINDING_STATES) {
|
|
203
|
+
results.push(recorded.get(state.id) ?? (await runState(page, cdp, state)));
|
|
204
|
+
}
|
|
205
|
+
// An expectation that applies to nothing is not coverage, however green it
|
|
206
|
+
// looks. This is the only place that can notice: the contract never sees the
|
|
207
|
+
// states, so whether a condition matches a real one is a binding-side fact.
|
|
208
|
+
expect(neverExercised(results)).toEqual([]);
|
|
209
|
+
expect(unmatchedKnownFailures(SWITCH_KNOWN_FAILURES, results)).toEqual([]);
|
|
210
|
+
});
|
|
@@ -163,24 +163,18 @@ test('every binding fact matches the rendered semantic state', async ({
|
|
|
163
163
|
expect(wrong).toEqual([]);
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const results: BindingResult[] = [];
|
|
172
|
-
for (const state of TABS_BINDING_STATES) {
|
|
173
|
-
results.push(await runState(page, cdp, state));
|
|
174
|
-
}
|
|
175
|
-
expect(neverExercised(results)).toEqual([]);
|
|
176
|
-
expect(unmatchedKnownFailures(TABS_KNOWN_FAILURES, results)).toEqual([]);
|
|
177
|
-
});
|
|
166
|
+
// Each state's result, recorded by its own test below so the whole-suite check
|
|
167
|
+
// at the end reads it instead of measuring every state a second time. A state
|
|
168
|
+
// whose test did not run in this worker (a filtered run, or a worker restarted
|
|
169
|
+
// after a failure) is measured by the whole-suite check itself.
|
|
170
|
+
const recorded = new Map<string, BindingResult>();
|
|
178
171
|
|
|
179
172
|
for (const state of TABS_BINDING_STATES) {
|
|
180
173
|
test(`${state.binding} [${state.id}] — ${state.summary}`, async ({page}) => {
|
|
181
174
|
test.setTimeout(3 * 60 * 1000);
|
|
182
175
|
const cdp = await page.context().newCDPSession(page);
|
|
183
176
|
const result = await runState(page, cdp, state);
|
|
177
|
+
recorded.set(state.id, result);
|
|
184
178
|
const report = summarize(TABS_PATTERN, [result]);
|
|
185
179
|
// eslint-disable-next-line no-console -- the report is this run's artifact
|
|
186
180
|
console.log(formatReport(report));
|
|
@@ -189,3 +183,16 @@ for (const state of TABS_BINDING_STATES) {
|
|
|
189
183
|
expect(report.counts.unexpectedPass).toBe(0);
|
|
190
184
|
});
|
|
191
185
|
}
|
|
186
|
+
|
|
187
|
+
test('every expectation is exercised by at least one bound state', async ({
|
|
188
|
+
page,
|
|
189
|
+
}) => {
|
|
190
|
+
test.setTimeout(5 * 60 * 1000);
|
|
191
|
+
const cdp = await page.context().newCDPSession(page);
|
|
192
|
+
const results: BindingResult[] = [];
|
|
193
|
+
for (const state of TABS_BINDING_STATES) {
|
|
194
|
+
results.push(recorded.get(state.id) ?? (await runState(page, cdp, state)));
|
|
195
|
+
}
|
|
196
|
+
expect(neverExercised(results)).toEqual([]);
|
|
197
|
+
expect(unmatchedKnownFailures(TABS_KNOWN_FAILURES, results)).toEqual([]);
|
|
198
|
+
});
|
|
@@ -176,26 +176,18 @@ test('every state declaration matches the browser accessibility node', async ({
|
|
|
176
176
|
expect(wrong).toEqual([]);
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const results: BindingResult[] = [];
|
|
185
|
-
for (const state of TEXT_INPUT_BINDING_STATES) {
|
|
186
|
-
results.push(await runState(page, cdp, state));
|
|
187
|
-
}
|
|
188
|
-
expect(neverExercised(results)).toEqual([]);
|
|
189
|
-
expect(unmatchedKnownFailures(TEXT_INPUT_KNOWN_FAILURES, results)).toEqual(
|
|
190
|
-
[],
|
|
191
|
-
);
|
|
192
|
-
});
|
|
179
|
+
// Each state's result, recorded by its own test below so the whole-suite check
|
|
180
|
+
// at the end reads it instead of measuring every state a second time. A state
|
|
181
|
+
// whose test did not run in this worker (a filtered run, or a worker restarted
|
|
182
|
+
// after a failure) is measured by the whole-suite check itself.
|
|
183
|
+
const recorded = new Map<string, BindingResult>();
|
|
193
184
|
|
|
194
185
|
for (const state of TEXT_INPUT_BINDING_STATES) {
|
|
195
186
|
test(`${state.binding} [${state.id}] — ${state.summary}`, async ({page}) => {
|
|
196
187
|
test.setTimeout(3 * 60 * 1000);
|
|
197
188
|
const cdp = await page.context().newCDPSession(page);
|
|
198
189
|
const result = await runState(page, cdp, state);
|
|
190
|
+
recorded.set(state.id, result);
|
|
199
191
|
const report = summarize(TEXT_INPUT_PATTERN, [result]);
|
|
200
192
|
// eslint-disable-next-line no-console -- the report is this run's artifact
|
|
201
193
|
console.log(formatReport(report));
|
|
@@ -204,3 +196,18 @@ for (const state of TEXT_INPUT_BINDING_STATES) {
|
|
|
204
196
|
expect(report.counts.unexpectedPass).toBe(0);
|
|
205
197
|
});
|
|
206
198
|
}
|
|
199
|
+
|
|
200
|
+
test('every expectation is exercised and every known failure matches once', async ({
|
|
201
|
+
page,
|
|
202
|
+
}) => {
|
|
203
|
+
test.setTimeout(8 * 60 * 1000);
|
|
204
|
+
const cdp = await page.context().newCDPSession(page);
|
|
205
|
+
const results: BindingResult[] = [];
|
|
206
|
+
for (const state of TEXT_INPUT_BINDING_STATES) {
|
|
207
|
+
results.push(recorded.get(state.id) ?? (await runState(page, cdp, state)));
|
|
208
|
+
}
|
|
209
|
+
expect(neverExercised(results)).toEqual([]);
|
|
210
|
+
expect(unmatchedKnownFailures(TEXT_INPUT_KNOWN_FAILURES, results)).toEqual(
|
|
211
|
+
[],
|
|
212
|
+
);
|
|
213
|
+
});
|
|
@@ -143,22 +143,17 @@ test('the state inventory matches rendered labels and applicability facts', asyn
|
|
|
143
143
|
expect(wrong).toEqual([]);
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const results: BindingResult[] = [];
|
|
152
|
-
for (const state of TOGGLE_BUTTON_BINDING_STATES) {
|
|
153
|
-
results.push(await runState(page, cdp, state));
|
|
154
|
-
}
|
|
155
|
-
expect(neverExercised(results)).toEqual([]);
|
|
156
|
-
});
|
|
146
|
+
// Each state's result, recorded by its own test below so the whole-suite check
|
|
147
|
+
// at the end reads it instead of measuring every state a second time. A state
|
|
148
|
+
// whose test did not run in this worker (a filtered run, or a worker restarted
|
|
149
|
+
// after a failure) is measured by the whole-suite check itself.
|
|
150
|
+
const recorded = new Map<string, BindingResult>();
|
|
157
151
|
|
|
158
152
|
for (const state of TOGGLE_BUTTON_BINDING_STATES) {
|
|
159
153
|
test(`ToggleButton [${state.id}] — ${state.summary}`, async ({page}) => {
|
|
160
154
|
const cdp = await page.context().newCDPSession(page);
|
|
161
155
|
const result = await runState(page, cdp, state);
|
|
156
|
+
recorded.set(state.id, result);
|
|
162
157
|
const report = summarize(TOGGLE_BUTTON_PATTERN, [result]);
|
|
163
158
|
// eslint-disable-next-line no-console -- the report is this run's evidence
|
|
164
159
|
console.log(formatReport(report));
|
|
@@ -167,3 +162,15 @@ for (const state of TOGGLE_BUTTON_BINDING_STATES) {
|
|
|
167
162
|
expect(report.counts.unexpectedPass).toBe(0);
|
|
168
163
|
});
|
|
169
164
|
}
|
|
165
|
+
|
|
166
|
+
test('every expectation is exercised by at least one ToggleButton state', async ({
|
|
167
|
+
page,
|
|
168
|
+
}) => {
|
|
169
|
+
test.setTimeout(5 * 60 * 1000);
|
|
170
|
+
const cdp = await page.context().newCDPSession(page);
|
|
171
|
+
const results: BindingResult[] = [];
|
|
172
|
+
for (const state of TOGGLE_BUTTON_BINDING_STATES) {
|
|
173
|
+
results.push(recorded.get(state.id) ?? (await runState(page, cdp, state)));
|
|
174
|
+
}
|
|
175
|
+
expect(neverExercised(results)).toEqual([]);
|
|
176
|
+
});
|
|
@@ -920,6 +920,93 @@ describe('Tokenizer', () => {
|
|
|
920
920
|
expect(screen.queryByText('Create "existing"')).not.toBeInTheDocument();
|
|
921
921
|
});
|
|
922
922
|
|
|
923
|
+
it('does not show Create option for an already-selected label whose id differs from it', async () => {
|
|
924
|
+
// Real selections usually carry an opaque id (`u1` under the label
|
|
925
|
+
// "Alice"). The duplicate check used to compare the typed text against
|
|
926
|
+
// selected ids only, and the search results could not catch the match
|
|
927
|
+
// because filteredSource had already removed the selected item — so
|
|
928
|
+
// typing the label of an existing token offered "Create", which added a
|
|
929
|
+
// duplicate token under a new id.
|
|
930
|
+
const source: SearchSource = {
|
|
931
|
+
search: (query: string) =>
|
|
932
|
+
[{id: 'u1', label: 'Alice'}].filter(u =>
|
|
933
|
+
u.label.toLowerCase().includes(query.toLowerCase()),
|
|
934
|
+
),
|
|
935
|
+
bootstrap: () => [],
|
|
936
|
+
};
|
|
937
|
+
const onChange = vi.fn();
|
|
938
|
+
render(
|
|
939
|
+
<Tokenizer
|
|
940
|
+
label="Tags"
|
|
941
|
+
searchSource={source}
|
|
942
|
+
value={[{id: 'u1', label: 'Alice'}]}
|
|
943
|
+
onChange={onChange}
|
|
944
|
+
hasCreate
|
|
945
|
+
debounceMs={0}
|
|
946
|
+
/>,
|
|
947
|
+
);
|
|
948
|
+
|
|
949
|
+
const input = screen.getByRole('combobox');
|
|
950
|
+
await act(async () => {
|
|
951
|
+
fireEvent.change(input, {target: {value: 'Alice'}});
|
|
952
|
+
});
|
|
953
|
+
await act(async () => {
|
|
954
|
+
await new Promise(r => setTimeout(r, 50));
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
expect(screen.queryByText('Create "Alice"')).not.toBeInTheDocument();
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
it('does not add a duplicate token when a stale Create entry is committed', async () => {
|
|
961
|
+
// The guard in handleAdd must apply the same label check: a Create
|
|
962
|
+
// entry rendered before the selection changed still commits through
|
|
963
|
+
// here, and an opaque id must not let a same-label token through.
|
|
964
|
+
const source: SearchSource = {
|
|
965
|
+
search: (query: string) =>
|
|
966
|
+
// A label that contains the query but is not exactly it, so the
|
|
967
|
+
// Create entry for the query itself is legitimately offered.
|
|
968
|
+
[{id: 'u2', label: 'Alice Smith'}].filter(u =>
|
|
969
|
+
u.label.toLowerCase().includes(query.toLowerCase()),
|
|
970
|
+
),
|
|
971
|
+
bootstrap: () => [],
|
|
972
|
+
};
|
|
973
|
+
const onChange = vi.fn();
|
|
974
|
+
const {rerender} = render(
|
|
975
|
+
<Tokenizer
|
|
976
|
+
label="Tags"
|
|
977
|
+
searchSource={source}
|
|
978
|
+
value={[]}
|
|
979
|
+
onChange={onChange}
|
|
980
|
+
hasCreate
|
|
981
|
+
debounceMs={0}
|
|
982
|
+
/>,
|
|
983
|
+
);
|
|
984
|
+
|
|
985
|
+
const input = screen.getByRole('combobox');
|
|
986
|
+
await act(async () => {
|
|
987
|
+
fireEvent.change(input, {target: {value: 'Alice'}});
|
|
988
|
+
});
|
|
989
|
+
await act(async () => {
|
|
990
|
+
await new Promise(r => setTimeout(r, 50));
|
|
991
|
+
});
|
|
992
|
+
// The Create entry exists while the selection is empty...
|
|
993
|
+
expect(screen.queryByText('Create "Alice"')).toBeInTheDocument();
|
|
994
|
+
// ...then the selection gains Alice (under an opaque id) before the
|
|
995
|
+
// entry is committed.
|
|
996
|
+
rerender(
|
|
997
|
+
<Tokenizer
|
|
998
|
+
label="Tags"
|
|
999
|
+
searchSource={source}
|
|
1000
|
+
value={[{id: 'u1', label: 'Alice'}]}
|
|
1001
|
+
onChange={onChange}
|
|
1002
|
+
hasCreate
|
|
1003
|
+
debounceMs={0}
|
|
1004
|
+
/>,
|
|
1005
|
+
);
|
|
1006
|
+
fireEvent.click(screen.getByText('Create "Alice"'));
|
|
1007
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
1008
|
+
});
|
|
1009
|
+
|
|
923
1010
|
it('does not show Create option when hasCreate is false', async () => {
|
|
924
1011
|
render(
|
|
925
1012
|
<Tokenizer
|