@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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* SYNC: When Dialog.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {useState} from 'react';
|
|
12
|
+
import {useState, type SyntheticEvent} from 'react';
|
|
13
13
|
import {readFileSync} from 'node:fs';
|
|
14
14
|
import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
|
|
15
15
|
import type {MockInstance} from 'vitest';
|
|
@@ -1030,6 +1030,32 @@ describe('Dialog', () => {
|
|
|
1030
1030
|
expect(screen.getByTestId('custom-dialog')).toBeInTheDocument();
|
|
1031
1031
|
});
|
|
1032
1032
|
|
|
1033
|
+
it('runs backdrop dismissal before a consumer onClick', () => {
|
|
1034
|
+
const callOrder: string[] = [];
|
|
1035
|
+
const onClick = vi.fn((event: SyntheticEvent) => {
|
|
1036
|
+
event.preventDefault();
|
|
1037
|
+
callOrder.push('consumer');
|
|
1038
|
+
});
|
|
1039
|
+
const onOpenChange = vi.fn(() => {
|
|
1040
|
+
callOrder.push('dialog');
|
|
1041
|
+
});
|
|
1042
|
+
render(
|
|
1043
|
+
<Dialog
|
|
1044
|
+
isOpen={true}
|
|
1045
|
+
onOpenChange={onOpenChange}
|
|
1046
|
+
onClick={onClick}
|
|
1047
|
+
data-testid="click-dialog">
|
|
1048
|
+
Content
|
|
1049
|
+
</Dialog>,
|
|
1050
|
+
);
|
|
1051
|
+
|
|
1052
|
+
fireEvent.click(screen.getByTestId('click-dialog'));
|
|
1053
|
+
|
|
1054
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
1055
|
+
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
1056
|
+
expect(callOrder).toEqual(['dialog', 'consumer']);
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1033
1059
|
it('does not forward native open prop to dialog element', () => {
|
|
1034
1060
|
render(
|
|
1035
1061
|
<Dialog
|
package/src/Dialog/Dialog.tsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Dialog.tsx
|
|
7
7
|
* @input Uses React, DialogHTMLAttributes, ReactNode, container (Layout), DialogContext, layerTextReset,
|
|
8
|
-
* layerStructureReset, modalOutlet, interactionModality
|
|
8
|
+
* layerStructureReset, modalOutlet, interactionModality, chainEventHandlers
|
|
9
9
|
* @output Exports Dialog component, DialogProps, DialogVariant, DialogPurpose types
|
|
10
10
|
* @position Core implementation; consumed by index.ts, tested by Dialog.test.tsx
|
|
11
11
|
*
|
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
} from '../Layout/padding.stylex';
|
|
66
66
|
import type {SpacingStep} from '../utils/types';
|
|
67
67
|
import {mergeProps} from '../utils';
|
|
68
|
+
import {chainEventHandlers} from '../utils/chainEventHandlers';
|
|
68
69
|
import {devWarn} from '../utils/devWarning';
|
|
69
70
|
import {DialogContext} from './DialogContext';
|
|
70
71
|
import {themeProps} from '../utils/themeProps';
|
|
@@ -679,9 +680,19 @@ export function Dialog({
|
|
|
679
680
|
</div>
|
|
680
681
|
);
|
|
681
682
|
|
|
682
|
-
// Filter out native open to prevent InvalidStateError when accidentally passed
|
|
683
|
+
// Filter out native open to prevent InvalidStateError when accidentally passed.
|
|
684
|
+
// Keep the consumer click handler separate so the modal path can compose it
|
|
685
|
+
// with Dialog-owned behavior without changing the inline preview path.
|
|
683
686
|
const hasPosition = position != null && !isFullscreen;
|
|
684
|
-
const {
|
|
687
|
+
const {onClick: onClickProp, ...propsWithoutClick} = props;
|
|
688
|
+
const {open: _open, ...safeProps} = propsWithoutClick as Record<
|
|
689
|
+
string,
|
|
690
|
+
unknown
|
|
691
|
+
>;
|
|
692
|
+
const inlineSafeProps: Record<string, unknown> = {
|
|
693
|
+
...safeProps,
|
|
694
|
+
onClick: onClickProp,
|
|
695
|
+
};
|
|
685
696
|
|
|
686
697
|
// --- Inline rendering path (for documentation previews) ---
|
|
687
698
|
if (isInline) {
|
|
@@ -691,7 +702,7 @@ export function Dialog({
|
|
|
691
702
|
|
|
692
703
|
return (
|
|
693
704
|
<div
|
|
694
|
-
{...
|
|
705
|
+
{...inlineSafeProps}
|
|
695
706
|
{...mergeProps(
|
|
696
707
|
themeProps('dialog', {variant}),
|
|
697
708
|
stylex.props(
|
|
@@ -760,7 +771,7 @@ export function Dialog({
|
|
|
760
771
|
className,
|
|
761
772
|
style,
|
|
762
773
|
)}
|
|
763
|
-
onClick={handleClick}
|
|
774
|
+
onClick={chainEventHandlers(handleClick, onClickProp)}
|
|
764
775
|
onCancel={handleCancel}
|
|
765
776
|
aria-modal="true"
|
|
766
777
|
// The default aria-labelledby (from a DialogHeader title) is set
|
|
@@ -260,6 +260,19 @@ describe('DialogHeader', () => {
|
|
|
260
260
|
expect(closeIcon).toHaveClass('astryx-icon');
|
|
261
261
|
});
|
|
262
262
|
|
|
263
|
+
it('renders numeric zero in both content slots', () => {
|
|
264
|
+
const {container} = render(
|
|
265
|
+
<DialogHeader title="Title" startContent={0} endContent={0} />,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
expect(
|
|
269
|
+
container.querySelector('.astryx-dialog-header-start-content'),
|
|
270
|
+
).toHaveTextContent(/^0$/);
|
|
271
|
+
expect(
|
|
272
|
+
container.querySelector('.astryx-dialog-header-end-content'),
|
|
273
|
+
).toHaveTextContent(/^0$/);
|
|
274
|
+
});
|
|
275
|
+
|
|
263
276
|
it('exposes the start-content target when startContent renders', () => {
|
|
264
277
|
const {container} = render(
|
|
265
278
|
<DialogHeader
|
|
@@ -292,8 +305,20 @@ describe('DialogHeader', () => {
|
|
|
292
305
|
).toHaveLength(1);
|
|
293
306
|
});
|
|
294
307
|
|
|
295
|
-
it(
|
|
296
|
-
|
|
308
|
+
it.each([
|
|
309
|
+
['empty string', ''],
|
|
310
|
+
['false', false],
|
|
311
|
+
['true', true],
|
|
312
|
+
['null', null],
|
|
313
|
+
['undefined', undefined],
|
|
314
|
+
])('omits both content-slot targets for %s', (_, content) => {
|
|
315
|
+
const {container} = render(
|
|
316
|
+
<DialogHeader
|
|
317
|
+
title="Title"
|
|
318
|
+
startContent={content}
|
|
319
|
+
endContent={content}
|
|
320
|
+
/>,
|
|
321
|
+
);
|
|
297
322
|
|
|
298
323
|
expect(
|
|
299
324
|
container.querySelector('.astryx-dialog-header-start-content'),
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file DialogHeader.tsx
|
|
7
|
-
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, DialogContext, mergeProps, themeProps
|
|
7
|
+
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, DialogContext, isRenderable, mergeProps, themeProps
|
|
8
8
|
* @output Exports DialogHeader component and DialogHeaderProps
|
|
9
9
|
* @position Dialog header component; used with Dialog and Layout
|
|
10
10
|
*
|
|
@@ -25,7 +25,7 @@ import {Icon} from '../Icon';
|
|
|
25
25
|
import {Heading} from '../Heading/Heading';
|
|
26
26
|
import {Text} from '../Text/Text';
|
|
27
27
|
import type {BaseProps} from '../BaseProps';
|
|
28
|
-
import {mergeProps} from '../utils';
|
|
28
|
+
import {isRenderable, mergeProps} from '../utils';
|
|
29
29
|
import {themeProps} from '../utils/themeProps';
|
|
30
30
|
import {useDialogContext} from './DialogContext';
|
|
31
31
|
import {useTranslator} from '../i18n';
|
|
@@ -164,10 +164,9 @@ export function DialogHeader({
|
|
|
164
164
|
const dialogContext = useDialogContext();
|
|
165
165
|
const shouldAutoFocus = dialogContext?.isInline !== true;
|
|
166
166
|
const titleId = dialogContext?.titleId;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
subtitle != null && typeof subtitle !== 'boolean' && subtitle !== '';
|
|
167
|
+
const hasSubtitle = isRenderable(subtitle);
|
|
168
|
+
const hasStartContent = isRenderable(startContent);
|
|
169
|
+
const hasEndContent = isRenderable(endContent);
|
|
171
170
|
const shouldCompensateEndBlock =
|
|
172
171
|
endContentEdgeCompensation == null
|
|
173
172
|
? onOpenChange != null
|
|
@@ -203,7 +202,7 @@ export function DialogHeader({
|
|
|
203
202
|
themeProps('dialog-header'),
|
|
204
203
|
stylex.props(styles.container),
|
|
205
204
|
)}>
|
|
206
|
-
{
|
|
205
|
+
{hasStartContent && (
|
|
207
206
|
<div
|
|
208
207
|
{...mergeProps(
|
|
209
208
|
themeProps('dialog-header-start-content'),
|
|
@@ -231,7 +230,7 @@ export function DialogHeader({
|
|
|
231
230
|
</Text>
|
|
232
231
|
)}
|
|
233
232
|
</div>
|
|
234
|
-
{(
|
|
233
|
+
{(hasEndContent || onOpenChange != null) && (
|
|
235
234
|
<div
|
|
236
235
|
{...mergeProps(
|
|
237
236
|
themeProps('dialog-header-end-content'),
|
|
@@ -180,25 +180,17 @@ async function runState(
|
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const results: BindingResult[] = [];
|
|
189
|
-
for (const state of DIALOG_MODAL_BINDING_STATES) {
|
|
190
|
-
results.push(await runState(page, cdp, state));
|
|
191
|
-
}
|
|
192
|
-
expect(neverExercised(results)).toEqual([]);
|
|
193
|
-
expect(unmatchedKnownFailures(DIALOG_MODAL_KNOWN_FAILURES, results)).toEqual(
|
|
194
|
-
[],
|
|
195
|
-
);
|
|
196
|
-
});
|
|
183
|
+
// Each state's result, recorded by its own test below so the whole-suite check
|
|
184
|
+
// at the end reads it instead of measuring every state a second time. A state
|
|
185
|
+
// whose test did not run in this worker (a filtered run, or a worker restarted
|
|
186
|
+
// after a failure) is measured by the whole-suite check itself.
|
|
187
|
+
const recorded = new Map<string, BindingResult>();
|
|
197
188
|
|
|
198
189
|
for (const state of DIALOG_MODAL_BINDING_STATES) {
|
|
199
190
|
test(`Dialog [${state.id}] — ${state.summary}`, async ({page}) => {
|
|
200
191
|
const cdp = await page.context().newCDPSession(page);
|
|
201
192
|
const result = await runState(page, cdp, state);
|
|
193
|
+
recorded.set(state.id, result);
|
|
202
194
|
const report = summarize(MODAL_DIALOG_PATTERN, [result]);
|
|
203
195
|
// eslint-disable-next-line no-console -- the report is this run's artifact
|
|
204
196
|
console.log(formatReport(report));
|
|
@@ -208,3 +200,18 @@ for (const state of DIALOG_MODAL_BINDING_STATES) {
|
|
|
208
200
|
expect(report.counts.unexpectedPass).toBe(0);
|
|
209
201
|
});
|
|
210
202
|
}
|
|
203
|
+
|
|
204
|
+
test('every expectation is exercised and every known failure matches once', async ({
|
|
205
|
+
page,
|
|
206
|
+
}) => {
|
|
207
|
+
test.setTimeout(5 * 60 * 1000);
|
|
208
|
+
const cdp = await page.context().newCDPSession(page);
|
|
209
|
+
const results: BindingResult[] = [];
|
|
210
|
+
for (const state of DIALOG_MODAL_BINDING_STATES) {
|
|
211
|
+
results.push(recorded.get(state.id) ?? (await runState(page, cdp, state)));
|
|
212
|
+
}
|
|
213
|
+
expect(neverExercised(results)).toEqual([]);
|
|
214
|
+
expect(unmatchedKnownFailures(DIALOG_MODAL_KNOWN_FAILURES, results)).toEqual(
|
|
215
|
+
[],
|
|
216
|
+
);
|
|
217
|
+
});
|
|
@@ -40,7 +40,8 @@ export const docs = {
|
|
|
40
40
|
{
|
|
41
41
|
name: 'label',
|
|
42
42
|
type: 'ReactNode',
|
|
43
|
-
description:
|
|
43
|
+
description:
|
|
44
|
+
'Optional label centered on the divider. Numeric zero is preserved; null, booleans, and empty strings render no label.',
|
|
44
45
|
},
|
|
45
46
|
{
|
|
46
47
|
name: 'variant',
|
|
@@ -92,7 +93,8 @@ export const docsZh = {
|
|
|
92
93
|
{
|
|
93
94
|
name: 'label',
|
|
94
95
|
type: 'ReactNode',
|
|
95
|
-
description:
|
|
96
|
+
description:
|
|
97
|
+
'居中显示在分隔线上的可选标签。数字 0 会保留;null、布尔值和空字符串不渲染标签。',
|
|
96
98
|
},
|
|
97
99
|
{
|
|
98
100
|
name: 'variant',
|
|
@@ -143,7 +145,7 @@ export const docsDense = {
|
|
|
143
145
|
},
|
|
144
146
|
propDescriptions: {
|
|
145
147
|
orientation: 'divider orientation',
|
|
146
|
-
label: 'optional centered label
|
|
148
|
+
label: 'optional centered label; preserves 0; null/boolean/"" omit it',
|
|
147
149
|
variant: 'visual weight of divider line',
|
|
148
150
|
isFullBleed: 'extend to container edges w/ negative margins',
|
|
149
151
|
xstyle: 'StyleX styles for layout; must be stylex.create() value',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
schema_version: 3
|
|
3
|
-
template_version:
|
|
3
|
+
template_version: 7
|
|
4
4
|
kind: component
|
|
5
5
|
id: component:Divider
|
|
6
6
|
authority: draft
|
|
@@ -9,33 +9,61 @@ superseded_by: null
|
|
|
9
9
|
approved_by: null
|
|
10
10
|
approved_at: null
|
|
11
11
|
owners: [cixzhang]
|
|
12
|
-
review_triggers: [theming]
|
|
12
|
+
review_triggers: [public-api, behavior, layout, theming, accessibility, testing]
|
|
13
13
|
verified_by:
|
|
14
|
-
[
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/Divider/Divider.test.tsx,
|
|
16
|
+
packages/core/src/theme/themingTargets.test.ts,
|
|
17
|
+
apps/storybook/stories/Divider.stories.tsx,
|
|
18
|
+
]
|
|
15
19
|
modules: []
|
|
16
20
|
families: []
|
|
17
21
|
design_specs: []
|
|
18
22
|
architecture:
|
|
19
|
-
[
|
|
23
|
+
[
|
|
24
|
+
architecture:public-component-api,
|
|
25
|
+
architecture:component-theming-surface,
|
|
26
|
+
architecture:container-padding,
|
|
27
|
+
architecture:component-test-sufficiency,
|
|
28
|
+
]
|
|
20
29
|
contributing: []
|
|
21
|
-
system_specs: []
|
|
30
|
+
system_specs: [spec:AST-020, spec:AST-029]
|
|
22
31
|
---
|
|
23
32
|
|
|
24
33
|
# Divider component contract
|
|
25
34
|
|
|
35
|
+
## Contract at a glance
|
|
36
|
+
|
|
37
|
+
| Area | Contract |
|
|
38
|
+
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
+
| Public contract | `orientation`, `label`, theme-extensible `variant`, `isFullBleed`, `BaseProps<HTMLDivElement>`, and a root `ref`. |
|
|
40
|
+
| Behavior | The separator renders one rule without a label and two rules around a renderable label; full bleed consumes inherited logical container inset. The theme-extensible variant axis has no settled no-match line-paint fallback. |
|
|
41
|
+
| End-user impact | People can perceive a named separator across horizontal, vertical, labelled, full-bleed, narrow, and RTL compositions. A custom theme variant can still lose its line paint; OQ2 records that retained gap. |
|
|
42
|
+
| Builder impact | No new caller choice. Numeric zero remains a valid label; booleans and empty strings leave one continuous unnamed rule. |
|
|
43
|
+
| Compatibility/readiness | Patch-compatible observational contract. Public props, defaults, target names, and built-in appearances remain unchanged; authority stays `draft` until exact-head owner approval. |
|
|
44
|
+
| Review checks | Reject missing numeric labels, physical-edge bleed, dropped root inputs, or a target/axis that disagrees with the rendered separator. Keep the custom-variant paint gap unresolved until OQ2 is settled. |
|
|
45
|
+
| Governing rules | `architecture:public-component-api/INV1,INV5–INV9`; `architecture:component-theming-surface/INV3–INV7,INV14`; `architecture:container-padding/INV1,INV3,INV7–INV10`; WCAG 2.2 SC 1.3.1 and 4.1.2 through `spec:AST-020/FR1`. |
|
|
46
|
+
|
|
47
|
+
This table is a review projection; the body below is authoritative.
|
|
48
|
+
|
|
26
49
|
## Intent
|
|
27
50
|
|
|
28
|
-
Divider
|
|
29
|
-
|
|
30
|
-
|
|
51
|
+
Divider provides a programmatic and visual separator between caller-owned content
|
|
52
|
+
regions. It owns orientation, line weight, optional label placement and naming,
|
|
53
|
+
logical full-bleed geometry, and the aggregate `divider` theme target. The caller
|
|
54
|
+
owns the meaning of the surrounding regions.
|
|
55
|
+
|
|
56
|
+
Consumer syntax and examples belong in `Divider.doc.mjs`.
|
|
31
57
|
|
|
32
58
|
## Compatibility and migration
|
|
33
59
|
|
|
34
|
-
- Released default preserved:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
60
|
+
- Released default preserved: yes — horizontal, subtle, unlabelled, and not full
|
|
61
|
+
bleed.
|
|
62
|
+
- Compatibility class: patch fix. ReactNode values that React renders, including
|
|
63
|
+
numeric zero, stay inside the label wrapper; skipped scalar labels leave one
|
|
64
|
+
continuous unnamed rule.
|
|
65
|
+
- Controlled/uncontrolled behavior: not applicable.
|
|
66
|
+
- Migration decision: none.
|
|
39
67
|
|
|
40
68
|
Consumer migration instructions belong in consumer docs and release notes.
|
|
41
69
|
|
|
@@ -43,61 +71,104 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
43
71
|
|
|
44
72
|
**Owns**
|
|
45
73
|
|
|
46
|
-
-
|
|
47
|
-
and
|
|
74
|
+
- One semantic separator root and its public ref, DOM, data, ARIA, class, style,
|
|
75
|
+
event, and `xstyle` composition.
|
|
76
|
+
- Horizontal and vertical rule geometry.
|
|
77
|
+
- Optional label placement and the separator naming relationship.
|
|
78
|
+
- Built-in subtle and strong line weights.
|
|
79
|
+
- The current reflected custom-variant state, including the missing deterministic
|
|
80
|
+
line-paint fallback recorded by OQ2.
|
|
81
|
+
- Logical full-bleed consumption of inherited container inset.
|
|
82
|
+
- The aggregate `divider` target with reflected orientation and variant axes.
|
|
48
83
|
|
|
49
84
|
**Does not own / non-goals**
|
|
50
85
|
|
|
51
|
-
- The meaning of
|
|
52
|
-
|
|
53
|
-
- Container inset publication
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
|
|
86
|
+
- The meaning or heading structure of surrounding content regions — owned by the
|
|
87
|
+
product callsite.
|
|
88
|
+
- Container inset publication — owned by `architecture:container-padding`.
|
|
89
|
+
- A separate public target for Rule or Label — unresolved under OQ1.
|
|
90
|
+
- Theme-authored custom variant paint and the public part that should own it —
|
|
91
|
+
unresolved under OQ2.
|
|
57
92
|
|
|
58
93
|
## Public concepts
|
|
59
94
|
|
|
60
|
-
|
|
61
|
-
|
|
95
|
+
| Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
|
|
96
|
+
| ------------ | --------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------- | ------------ | ---------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
97
|
+
| Orientation | `horizontal`; `vertical` | Selects the separator axis, rule geometry, and `aria-orientation`. | Every divider | `horizontal` | component | stable | Closed union; rejected by types |
|
|
98
|
+
| Label | renderable ReactNode; omitted/skipped scalar | Inserts one label between two rules and names the separator unless explicit ARIA naming wins. | Every orientation and variant | omitted | caller/component | stable | `null`, booleans, and empty strings render no label; numeric zero remains visible |
|
|
99
|
+
| Variant | `subtle`; `strong`; theme-added visual values | Selects line paint and reflects the requested value on the target. | Every orientation and label state | `subtle` | component/theme | stable built-ins; theme-extensible axis | An unmatched custom value has no deterministic line-paint baseline; see OQ2 |
|
|
100
|
+
| Full bleed | false; true | Consumes inherited logical container inset on the divider axis. | Every orientation | false | component/container protocol | stable | Missing variables resolve to zero |
|
|
101
|
+
| Root surface | ref; DOM/data/ARIA; class/style/xstyle/events | Extends the semantic separator root. | Every divider | omitted | public component API | stable | Accepted inputs compose; component-owned role and orientation remain authoritative |
|
|
62
102
|
|
|
63
103
|
## Behavioral and layout contract
|
|
64
104
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
|
105
|
+
Draft requirements identify their basis so observed code is not mistaken for an
|
|
106
|
+
intentional decision.
|
|
107
|
+
|
|
108
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
109
|
+
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------- |
|
|
110
|
+
| FR1 | The divider MUST render one rule without a renderable label and two rules with the label between them. Numeric zero MUST remain a renderable label. | Released ReactNode API; current source/docs; `architecture:public-component-api/INV9` | verify |
|
|
111
|
+
| FR2 | The root MUST expose `role="separator"` and the selected `aria-orientation`. A renderable label MUST name it through `aria-labelledby` unless an explicit caller `aria-label` or `aria-labelledby` wins. | WCAG 2.2 SC 1.3.1 and 4.1.2; current source/tests | verify |
|
|
112
|
+
| FR3 | Built-in variants MUST paint subtle or strong lines. The exported map accepts theme-added values, but an unmatched value currently leaves the inner rule without line paint; the correct fallback/target ownership requires OQ2. | `architecture:component-theming-surface/INV4,INV6,INV14`; exported `DividerVariantMap` | human decision; open BLOCK |
|
|
113
|
+
| FR4 | Full bleed MUST consume inherited logical inline inset for horizontal dividers and logical block inset for vertical dividers; absent inset MUST resolve to zero. | `architecture:container-padding/INV1,INV3,INV7–INV10` | verify |
|
|
114
|
+
| FR5 | The root MUST carry the `divider` target and reflect orientation and requested variant. Rule and Label currently carry no public target. | `architecture:component-theming-surface/INV3–INV6`; current metadata | verify; part exposure unsettled |
|
|
115
|
+
| FR6 | Accepted ref, DOM/data/ARIA, class, style, events, and `xstyle` MUST reach or compose on the separator root without replacing component-owned role or orientation. | `architecture:public-component-api/INV5–INV8` | verify |
|
|
69
116
|
|
|
70
117
|
### Allowed variation
|
|
71
118
|
|
|
72
|
-
-
|
|
73
|
-
|
|
119
|
+
- **AV1 — Label content.** Any caller-owned ReactNode that React renders may fill
|
|
120
|
+
the label wrapper.
|
|
121
|
+
- **AV2 — Theme paint.** An active theme may restyle the aggregate target and its
|
|
122
|
+
variant axis. OQ2 owns the unresolved inner-rule paint and fallback boundary.
|
|
123
|
+
- **AV3 — Container geometry.** Full bleed follows the inherited per-edge inset,
|
|
124
|
+
including asymmetric and zero values.
|
|
74
125
|
|
|
75
126
|
### Representative states
|
|
76
127
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
128
|
+
| State | Required invariant | Allowed variation |
|
|
129
|
+
| ----------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------ |
|
|
130
|
+
| Unlabelled horizontal | One subtle or strong inline rule; horizontal separator semantics | Container width and theme paint |
|
|
131
|
+
| Labelled horizontal | Two rules around one visible, naming label | Any renderable label content |
|
|
132
|
+
| Numeric zero label | One visible `0` inside the label wrapper; separator name `0` | Any orientation or variant |
|
|
133
|
+
| Vertical labelled/unlabelled | Block-axis rules and vertical separator semantics | Container height and label content |
|
|
134
|
+
| Theme-added variant without a match | Requested variant reflected; inner rule has no component-owned paint (open BLOCK) | Custom variant name |
|
|
135
|
+
| Full bleed | Logical-axis inset consumed without physical-direction assumptions | Uniform, asymmetric, zero, or absent inset |
|
|
136
|
+
| RTL | Horizontal geometry remains logical; full bleed consumes inline start/end independently | Surrounding content and label language |
|
|
80
137
|
|
|
81
138
|
### Transformation and precedence order
|
|
82
139
|
|
|
83
|
-
-
|
|
140
|
+
- **ORD1 — Label presence.** Apply the shared renderable-value predicate; when a
|
|
141
|
+
label remains, render and reference one wrapper between two rules.
|
|
142
|
+
- **ORD2 — Accessible name.** Explicit `aria-labelledby` wins, then explicit
|
|
143
|
+
`aria-label`, then the rendered label wrapper; an unlabelled separator stays
|
|
144
|
+
unnamed.
|
|
145
|
+
- **ORD3 — Variant paint.** Reflect the requested value on the root target. Built-in
|
|
146
|
+
values select subtle or strong inner-rule paint; unmatched custom values select no
|
|
147
|
+
inner-rule paint pending OQ2.
|
|
84
148
|
|
|
85
149
|
### Performance and resources
|
|
86
150
|
|
|
87
|
-
- No
|
|
151
|
+
- **PR1 — No external lifecycle.** Divider installs no listener, observer, timer,
|
|
152
|
+
request, state machine, or Effect.
|
|
88
153
|
|
|
89
154
|
## Accessibility contract
|
|
90
155
|
|
|
91
|
-
|
|
92
|
-
orientation
|
|
156
|
+
- **AR1 — Programmatic separator.** The root exposes separator role and the exact
|
|
157
|
+
visual orientation.
|
|
158
|
+
- **AR2 — Label relationship.** A rendered label names the separator unless the
|
|
159
|
+
caller supplies an explicit accessible name relationship.
|
|
160
|
+
- **AR3 — Decorative rule segments.** Internal rule elements add no separate role,
|
|
161
|
+
name, focus target, or interaction.
|
|
93
162
|
|
|
94
163
|
## Design relationships
|
|
95
164
|
|
|
96
|
-
| Anatomy or state | Design requirement
|
|
97
|
-
| ---------------- |
|
|
98
|
-
| Divider group |
|
|
99
|
-
| Rule |
|
|
100
|
-
| Label |
|
|
165
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
166
|
+
| ---------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------- | ------------------ |
|
|
167
|
+
| Divider group | Arrange rule segments and the optional label on one logical axis. | observed; container geometry settled by current architecture | supporting | FR1, FR4 |
|
|
168
|
+
| Rule | Paint a visible subtle or strong separation line; theme-added values lack a settled fallback. | built-ins observed; custom fallback unsettled | supporting | FR3 |
|
|
169
|
+
| Label | Present optional content between two rules using supporting text tokens. | observed; separate target exposure unsettled | prominent | FR1–FR2 |
|
|
170
|
+
|
|
171
|
+
No component-local visual decision is settled by this draft.
|
|
101
172
|
|
|
102
173
|
### Theming anatomy
|
|
103
174
|
|
|
@@ -125,20 +196,27 @@ public targets.
|
|
|
125
196
|
|
|
126
197
|
## Family and system relationships
|
|
127
198
|
|
|
128
|
-
`
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
199
|
+
- `architecture:container-padding` owns the inherited internal inset protocol;
|
|
200
|
+
Divider consumes it only through `isFullBleed`.
|
|
201
|
+
- `architecture:component-theming-surface` owns the aggregate target, reflected
|
|
202
|
+
axes, anatomy qualification, and the requirement that an open visual axis have
|
|
203
|
+
a deterministic fallback. Divider's current custom-value path does not satisfy
|
|
204
|
+
that requirement; OQ2 keeps remediation out of this objective audit patch.
|
|
205
|
+
- `architecture:public-component-api` owns root composition, ref behavior, and
|
|
206
|
+
compatibility of the released ReactNode label surface.
|
|
207
|
+
- There is no current Divider family or design record.
|
|
208
|
+
- `spec:AST-029` keeps this observational draft separate from the audit receipt.
|
|
134
209
|
|
|
135
210
|
## Verification map
|
|
136
211
|
|
|
137
|
-
| Contract
|
|
138
|
-
|
|
|
139
|
-
| FR1
|
|
140
|
-
|
|
|
141
|
-
|
|
|
212
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
213
|
+
| ---------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------- |
|
|
214
|
+
| FR1–FR2, AR1–AR3 | `Divider.test.tsx`; component-owned Storybook states; scoped axe | labelled/unlabelled, numeric zero, explicit names, both orientations | Truthiness renders zero outside wrappers or leaves the separator unnamed; role/orientation/name drift fails | `audit:Divider/§1,§4` |
|
|
215
|
+
| FR3 | Source review; global extensible-axis structural guard | subtle, strong, unmatched theme-added value | The current unmatched path leaves the rule unpainted; no Divider-specific browser fallback proof exists | `audit:Divider/§2` |
|
|
216
|
+
| FR4 | Source review; horizontal and vertical full-bleed stories | inline/block, LTR/RTL logical properties, absent inset | A physical property or wrong axis consumes unrelated container geometry | `audit:Divider/§5,§9,§10` |
|
|
217
|
+
| FR5 | `themingTargets.test.ts`; component metadata; spec anatomy map | both orientations and variants | Target/axis/anatomy metadata disagrees with runtime reflection | `audit:Divider/§2` |
|
|
218
|
+
| FR6 | `Divider.test.tsx`; lint, typecheck, and source review | ref, DOM/data/ARIA, class/style/xstyle/events | An accepted input is dropped or component-owned semantics are overwritten | `audit:Divider/§3` |
|
|
219
|
+
| PR1 | Source review | every state | A lifecycle mechanism appears without a resource contract | `audit:Divider/§7` |
|
|
142
220
|
|
|
143
221
|
## Decision log
|
|
144
222
|
|
|
@@ -150,8 +228,16 @@ or theming decision.
|
|
|
150
228
|
- **OQ1 — Should Rule or Label gain stable public theming targets?** (`human-api`)
|
|
151
229
|
Their target exposure is unsettled; the current lack of reachability does not
|
|
152
230
|
decide the answer.
|
|
231
|
+
- **OQ2 — Should the open `variant` axis close, or which painting part owns its
|
|
232
|
+
deterministic custom-value fallback?** (`human-api`) The root target can carry
|
|
233
|
+
theme paint while the inner rule carries built-in paint. Applying a subtle inner
|
|
234
|
+
fallback can cover an existing root background; keeping the current path can make
|
|
235
|
+
the rule disappear. Current authority requires a safe fallback but does not choose
|
|
236
|
+
between closing the axis, moving target ownership, or routing paint through a
|
|
237
|
+
reviewed public property.
|
|
153
238
|
|
|
154
239
|
## Content boundary
|
|
155
240
|
|
|
156
|
-
This file does not duplicate consumer prop tables/examples, current audit
|
|
157
|
-
|
|
241
|
+
This file does not duplicate consumer prop tables/examples, current audit scores,
|
|
242
|
+
screenshot matrices, implementation steps, or system rules. The audit pull request
|
|
243
|
+
owns the closed run evidence required by `spec:AST-029/FR5`.
|
|
@@ -128,6 +128,21 @@ describe('Divider', () => {
|
|
|
128
128
|
expect(divider).not.toHaveAttribute('aria-labelledby');
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
+
it.each([false, true, ''])('omits a label wrapper for %j', label => {
|
|
132
|
+
render(<Divider label={label} data-testid="divider" />);
|
|
133
|
+
const divider = screen.getByTestId('divider');
|
|
134
|
+
expect(divider).not.toHaveAttribute('aria-labelledby');
|
|
135
|
+
expect(divider.children).toHaveLength(1);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('preserves a numeric zero label and uses it as the accessible name', () => {
|
|
139
|
+
render(<Divider label={0} data-testid="divider" />);
|
|
140
|
+
const divider = screen.getByTestId('divider');
|
|
141
|
+
expect(screen.getByText('0')).toBeInTheDocument();
|
|
142
|
+
expect(divider).toHaveAccessibleName('0');
|
|
143
|
+
expect(divider.children).toHaveLength(3);
|
|
144
|
+
});
|
|
145
|
+
|
|
131
146
|
it('renders astryx-* class names for theme targeting', () => {
|
|
132
147
|
render(
|
|
133
148
|
<Divider variant="strong" orientation="vertical" data-testid="divider" />,
|
package/src/Divider/Divider.tsx
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
typeScaleVars,
|
|
26
26
|
borderVars,
|
|
27
27
|
} from '../theme/tokens.stylex';
|
|
28
|
-
import {mergeProps} from '../utils';
|
|
28
|
+
import {isRenderable, mergeProps} from '../utils';
|
|
29
29
|
import {themeProps} from '../utils/themeProps';
|
|
30
30
|
import type {DividerVariantMap} from './index';
|
|
31
31
|
|
|
@@ -45,6 +45,7 @@ export interface DividerProps extends BaseProps<HTMLDivElement> {
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Optional label to display centered on the divider.
|
|
48
|
+
* Numeric zero is preserved; null, booleans, and empty strings render no label.
|
|
48
49
|
* Rendered with small, secondary text styling.
|
|
49
50
|
*/
|
|
50
51
|
label?: ReactNode;
|
|
@@ -153,6 +154,7 @@ export function Divider({
|
|
|
153
154
|
...props
|
|
154
155
|
}: DividerProps) {
|
|
155
156
|
const isHorizontal = orientation === 'horizontal';
|
|
157
|
+
const hasLabel = isRenderable(label);
|
|
156
158
|
const labelId = useId();
|
|
157
159
|
|
|
158
160
|
// A separator does not derive its accessible name from its content, so the
|
|
@@ -160,7 +162,7 @@ export function Divider({
|
|
|
160
162
|
// exposed as the separator's name (WCAG 1.3.1). An explicit aria-label or
|
|
161
163
|
// aria-labelledby from the consumer takes precedence.
|
|
162
164
|
const resolvedLabelledBy =
|
|
163
|
-
ariaLabelledBy ?? (
|
|
165
|
+
ariaLabelledBy ?? (hasLabel && ariaLabel == null ? labelId : undefined);
|
|
164
166
|
|
|
165
167
|
return (
|
|
166
168
|
<div
|
|
@@ -189,7 +191,7 @@ export function Divider({
|
|
|
189
191
|
lineStyles[variant],
|
|
190
192
|
)}
|
|
191
193
|
/>
|
|
192
|
-
{
|
|
194
|
+
{hasLabel && (
|
|
193
195
|
<div
|
|
194
196
|
id={labelId}
|
|
195
197
|
{...stylex.props(
|
|
@@ -199,7 +201,7 @@ export function Divider({
|
|
|
199
201
|
{label}
|
|
200
202
|
</div>
|
|
201
203
|
)}
|
|
202
|
-
{
|
|
204
|
+
{hasLabel && (
|
|
203
205
|
<div
|
|
204
206
|
{...stylex.props(
|
|
205
207
|
isHorizontal ? lineStyles.horizontalLine : lineStyles.verticalLine,
|