@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
|
@@ -262,6 +262,51 @@ describe('ComplexSelector', () => {
|
|
|
262
262
|
).toHaveLength(2);
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
+
it('applies distinct trigger chrome to the secondary variant', () => {
|
|
266
|
+
// data-testid reaches the native control; the variant is on the trigger root.
|
|
267
|
+
const triggerRoot = () =>
|
|
268
|
+
screen
|
|
269
|
+
.getByTestId('view-options')
|
|
270
|
+
.closest('.astryx-complex-selector') as HTMLElement;
|
|
271
|
+
const {rerender} = render(
|
|
272
|
+
<ComplexSelector
|
|
273
|
+
label="View options"
|
|
274
|
+
value={['name']}
|
|
275
|
+
variant="input"
|
|
276
|
+
data-testid="view-options">
|
|
277
|
+
{() => <div>Columns</div>}
|
|
278
|
+
</ComplexSelector>,
|
|
279
|
+
);
|
|
280
|
+
const inputClasses = triggerRoot().className;
|
|
281
|
+
|
|
282
|
+
rerender(
|
|
283
|
+
<ComplexSelector
|
|
284
|
+
label="View options"
|
|
285
|
+
value={['name']}
|
|
286
|
+
variant="secondary"
|
|
287
|
+
data-testid="view-options">
|
|
288
|
+
{() => <div>Columns</div>}
|
|
289
|
+
</ComplexSelector>,
|
|
290
|
+
);
|
|
291
|
+
const secondary = triggerRoot();
|
|
292
|
+
const secondaryClasses = secondary.className;
|
|
293
|
+
expect(secondary).toHaveAttribute('data-variant', 'secondary');
|
|
294
|
+
|
|
295
|
+
rerender(
|
|
296
|
+
<ComplexSelector
|
|
297
|
+
label="View options"
|
|
298
|
+
value={['name']}
|
|
299
|
+
variant="ghost"
|
|
300
|
+
data-testid="view-options">
|
|
301
|
+
{() => <div>Columns</div>}
|
|
302
|
+
</ComplexSelector>,
|
|
303
|
+
);
|
|
304
|
+
const ghostClasses = triggerRoot().className;
|
|
305
|
+
|
|
306
|
+
expect(secondaryClasses).not.toBe(inputClasses);
|
|
307
|
+
expect(secondaryClasses).not.toBe(ghostClasses);
|
|
308
|
+
});
|
|
309
|
+
|
|
265
310
|
it('supports end-aligned popup positioning', () => {
|
|
266
311
|
render(
|
|
267
312
|
<ComplexSelector label="View options" value={[]} alignment="end">
|
|
@@ -721,22 +766,25 @@ describe('ComplexSelector onOpenChange', () => {
|
|
|
721
766
|
);
|
|
722
767
|
});
|
|
723
768
|
|
|
724
|
-
it('
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
'
|
|
738
|
-
|
|
739
|
-
|
|
769
|
+
it.each(['secondary', 'ghost'] as const)(
|
|
770
|
+
'forces detached when variant="%s" even if statusVariant="attached"',
|
|
771
|
+
variant => {
|
|
772
|
+
const {container} = render(
|
|
773
|
+
<ComplexSelector
|
|
774
|
+
label="Filter"
|
|
775
|
+
value={[]}
|
|
776
|
+
variant={variant}
|
|
777
|
+
status={{type: 'error', message: 'Required'}}
|
|
778
|
+
statusVariant="attached">
|
|
779
|
+
{() => <button type="button">Apply</button>}
|
|
780
|
+
</ComplexSelector>,
|
|
781
|
+
);
|
|
782
|
+
expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
|
|
783
|
+
'data-variant',
|
|
784
|
+
'detached',
|
|
785
|
+
);
|
|
786
|
+
},
|
|
787
|
+
);
|
|
740
788
|
|
|
741
789
|
it('renders no message box for statusVariant="tooltip"', () => {
|
|
742
790
|
const {container} = render(
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file ComplexSelector.tsx
|
|
7
7
|
* @input Uses React, StyleX, Field, Icon slots, Layer positioning, and usePopover
|
|
8
|
-
* @output Exports a rich-selector shell with exact token-sized input and ghost triggers, plus an imperative open/close handle
|
|
8
|
+
* @output Exports a rich-selector shell with exact token-sized input, secondary, and ghost triggers, plus an imperative open/close handle
|
|
9
9
|
* @position Core implementation; consumed by index.ts
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update:
|
|
@@ -151,6 +151,31 @@ const styles = stylex.create({
|
|
|
151
151
|
'[data-astryx-press="on"]': 'scale(0.98)',
|
|
152
152
|
},
|
|
153
153
|
},
|
|
154
|
+
// Keep the input-family width contract while reusing ghost's borderless paint
|
|
155
|
+
// and the neutral resting fill used by secondary buttons.
|
|
156
|
+
triggerSecondary: {
|
|
157
|
+
borderWidth: 0,
|
|
158
|
+
backgroundColor: colorVars['--color-neutral'],
|
|
159
|
+
boxShadow: {
|
|
160
|
+
default: 'none',
|
|
161
|
+
':hover:not(:focus-within):where(:not(:disabled,[aria-disabled="true"]))':
|
|
162
|
+
{
|
|
163
|
+
'@media (hover: hover)': 'none',
|
|
164
|
+
},
|
|
165
|
+
':focus-within': 'none',
|
|
166
|
+
},
|
|
167
|
+
fontWeight: fontWeightVars['--font-weight-medium'],
|
|
168
|
+
transitionProperty:
|
|
169
|
+
'background-image, background-color, color, opacity, transform',
|
|
170
|
+
transform: {
|
|
171
|
+
default: 'scale(1)',
|
|
172
|
+
':active': {
|
|
173
|
+
default: 'scale(0.98)',
|
|
174
|
+
'@media (pointer: coarse)': 'scale(1)',
|
|
175
|
+
},
|
|
176
|
+
'[data-astryx-press="on"]': 'scale(0.98)',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
154
179
|
triggerGhostDisabled: {
|
|
155
180
|
backgroundImage: 'none',
|
|
156
181
|
transform: {
|
|
@@ -202,7 +227,7 @@ const styles = stylex.create({
|
|
|
202
227
|
},
|
|
203
228
|
});
|
|
204
229
|
|
|
205
|
-
export type ComplexSelectorVariant = 'input' | 'ghost';
|
|
230
|
+
export type ComplexSelectorVariant = 'input' | 'secondary' | 'ghost';
|
|
206
231
|
|
|
207
232
|
export type ComplexSelectorSize = 'sm' | 'md' | 'lg';
|
|
208
233
|
|
|
@@ -338,17 +363,20 @@ export interface ComplexSelectorProps<Value> extends Omit<
|
|
|
338
363
|
status?: ComplexSelectorStatus;
|
|
339
364
|
/**
|
|
340
365
|
* How the status message is placed relative to the input.
|
|
341
|
-
* - 'attached': message overlaps directly below the input (bordered treatment; detached when variant is ghost)
|
|
366
|
+
* - 'attached': message overlaps directly below the input (bordered treatment; detached when variant is secondary or ghost)
|
|
342
367
|
* - 'detached': message floats below as a separate element with spacing
|
|
343
368
|
* - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap
|
|
344
|
-
* @default 'attached'
|
|
369
|
+
* @default 'attached' for input selectors; 'detached' for secondary and ghost selectors
|
|
345
370
|
*/
|
|
346
371
|
statusVariant?: FieldStatusVariant;
|
|
347
372
|
/** Tooltip text displayed next to the label. */
|
|
348
373
|
labelTooltip?: string;
|
|
349
374
|
/** Trigger and field size. */
|
|
350
375
|
size?: ComplexSelectorSize;
|
|
351
|
-
/**
|
|
376
|
+
/**
|
|
377
|
+
* Visual trigger style. Secondary matches secondary buttons; ghost matches
|
|
378
|
+
* ghost buttons.
|
|
379
|
+
*/
|
|
352
380
|
variant?: ComplexSelectorVariant;
|
|
353
381
|
/** Icon displayed at the start of the trigger. */
|
|
354
382
|
startIcon?: ReactNode | IconType;
|
|
@@ -494,7 +522,7 @@ export function ComplexSelector<Value>({
|
|
|
494
522
|
const isEffectivelyRequired = useResolvedRequired({isRequired, isOptional});
|
|
495
523
|
const placeholder = placeholderFromProps ?? t('@astryx.selector.placeholder');
|
|
496
524
|
const effectiveStatusVariant =
|
|
497
|
-
variant
|
|
525
|
+
variant !== 'input' && statusVariant === 'attached'
|
|
498
526
|
? 'detached'
|
|
499
527
|
: statusVariant;
|
|
500
528
|
|
|
@@ -709,10 +737,11 @@ export function ComplexSelector<Value>({
|
|
|
709
737
|
// matched a mouse click on the trigger and drew the outline for
|
|
710
738
|
// pointer users too. `focusWithin` here is `:has(:focus-visible)`.
|
|
711
739
|
focusOutlineStyles.focusWithin,
|
|
740
|
+
variant === 'secondary' && styles.triggerSecondary,
|
|
712
741
|
variant === 'ghost' && styles.triggerGhost,
|
|
713
|
-
variant
|
|
742
|
+
variant !== 'input' && interactionOverlayStyles.backgroundImage,
|
|
714
743
|
isDisabled && inputWrapperStyles.disabled,
|
|
715
|
-
variant
|
|
744
|
+
variant !== 'input' && isDisabled && styles.triggerGhostDisabled,
|
|
716
745
|
isDisabled && styles.disabled,
|
|
717
746
|
triggerLabel == null && styles.placeholder,
|
|
718
747
|
controlXstyle,
|
|
@@ -143,6 +143,12 @@ export const docs = {
|
|
|
143
143
|
'When true, right-click shows the native browser context menu instead.',
|
|
144
144
|
default: 'false',
|
|
145
145
|
},
|
|
146
|
+
{
|
|
147
|
+
name: 'triggerXstyle',
|
|
148
|
+
type: 'StyleXStyles | StyleXStyles[]',
|
|
149
|
+
description:
|
|
150
|
+
'Styles applied to the trigger wrapper. Accepts `stylex.create()` values, not inline style objects.',
|
|
151
|
+
},
|
|
146
152
|
{
|
|
147
153
|
name: 'triggerAs',
|
|
148
154
|
type: "'div' | 'span'",
|
|
@@ -26,6 +26,7 @@ architecture:
|
|
|
26
26
|
architecture:component-theming-surface,
|
|
27
27
|
architecture:interaction-modality,
|
|
28
28
|
architecture:layer-runtime,
|
|
29
|
+
architecture:public-component-api,
|
|
29
30
|
]
|
|
30
31
|
contributing: []
|
|
31
32
|
system_specs: []
|
|
@@ -36,17 +37,16 @@ system_specs: []
|
|
|
36
37
|
## Intent
|
|
37
38
|
|
|
38
39
|
ContextMenu presents actions for a caller-provided region in either a
|
|
39
|
-
cursor-positioned pointer menu or a BottomSheet-hosted touch action list.
|
|
40
|
-
|
|
41
|
-
theming reachability
|
|
42
|
-
public API.
|
|
40
|
+
cursor-positioned pointer menu or a BottomSheet-hosted touch action list. The
|
|
41
|
+
record describes current consumer anatomy, presentation-specific ownership, and
|
|
42
|
+
theming reachability.
|
|
43
43
|
|
|
44
44
|
## Compatibility and migration
|
|
45
45
|
|
|
46
|
-
- Released default
|
|
47
|
-
- Compatibility class:
|
|
48
|
-
|
|
49
|
-
- Controlled/uncontrolled behavior:
|
|
46
|
+
- Released default: `popover`
|
|
47
|
+
- Compatibility class: compatible behavior correction; public API, defaults,
|
|
48
|
+
DOM shape, styling, and targets are preserved
|
|
49
|
+
- Controlled/uncontrolled behavior: internally managed open state
|
|
50
50
|
- Migration decision: none
|
|
51
51
|
|
|
52
52
|
Consumer migration instructions belong in consumer docs and release notes.
|
|
@@ -81,16 +81,17 @@ Consumer props, content modes, and presentation policy remain documented in
|
|
|
81
81
|
|
|
82
82
|
## Behavioral and layout contract
|
|
83
83
|
|
|
84
|
-
| ID | Candidate invariant
|
|
85
|
-
| --- |
|
|
86
|
-
| FR1 | Every render contains a caller-provided Trigger area. Pointer presentation opens a Pointer menu surface at the current local cursor anchor; touch presentation opens a Touch sheet frame containing a Touch menu surface and, for data mode, a Touch action list and Touch action rows.
|
|
87
|
-
| FR2 | The current `context-menu` target remains on the painted Pointer menu surface and alternative Touch menu surface, not on the Trigger area or cursor anchor.
|
|
88
|
-
| FR3 | Pointer action rows retain DropdownMenu ownership. The touch frame retains BottomSheet ownership, while data-driven touch lists and rows retain List ownership.
|
|
89
|
-
| FR4 | Compound `menuContent` remains a caller-supplied pointer-menu interior and may also render inside the touch frame; the data-driven touch path instead converts the same item data to List and ListItem presentation.
|
|
90
|
-
| FR5 | The Pointer menu surface carries `data-astryx-menu-press` and follows `module:DropdownMenu/useMenuPress` FR1–FR7: the row under the release acts, the highlight follows a held pointer, a mouse released outside closes and a finger leaves the menu open, the stray click never acts, and `touch-action` follows overflow. Right-click, Shift+F10, long-press invocation and keyboard navigation
|
|
91
|
-
| FR6 | With `triggerAs="span"` the Trigger area MUST be an inline element that owns a box, so the cursor anchor measures from the trigger's own rect on every invocation path.
|
|
92
|
-
| FR7 | Sub-menu rows follow `component:DropdownMenu` FR12 inside the pointer menu: ContextMenu mounts the same drill-in view stack, named after its `label`, so a sub-menu drills in on a phone here too.
|
|
93
|
-
| FR8 | In the pointer menu ArrowDown on the last enabled row wraps to the first and ArrowUp on the first to the last; PageDown and PageUp page as in DropdownMenu FR6; a held key's auto-repeat does not activate; typeahead matches the row's label element alone.
|
|
84
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
85
|
+
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
86
|
+
| FR1 | Every render contains a caller-provided Trigger area. Pointer presentation opens a Pointer menu surface at the current local cursor anchor; touch presentation opens a Touch sheet frame containing a Touch menu surface and, for data mode, a Touch action list and Touch action rows. | Current source, docs, and tests | Current behavior; verified |
|
|
87
|
+
| FR2 | The current `context-menu` target remains on the painted Pointer menu surface and alternative Touch menu surface, not on the Trigger area or cursor anchor. | Current source, docs, and tests | Current inventory; verified |
|
|
88
|
+
| FR3 | Pointer action rows retain DropdownMenu ownership. The touch frame retains BottomSheet ownership, while data-driven touch lists and rows retain List ownership. | Current source and owner docs | Current delegation; verified |
|
|
89
|
+
| FR4 | Compound `menuContent` remains a caller-supplied pointer-menu interior and may also render inside the touch frame; the data-driven touch path instead converts the same item data to List and ListItem presentation. | Current source and tests | Current branches; verified |
|
|
90
|
+
| FR5 | The Pointer menu surface carries `data-astryx-menu-press` and follows `module:DropdownMenu/useMenuPress` FR1–FR7: the row under the release acts, the highlight follows a held pointer, a mouse released outside closes and a finger leaves the menu open, the stray click never acts, and `touch-action` follows overflow. Right-click, Shift+F10, long-press invocation and keyboard navigation keep their existing behavior. | `module:DropdownMenu/useMenuPress`; `ContextMenu.test.tsx` press model suite | Current module projection; verified in jsdom |
|
|
91
|
+
| FR6 | With `triggerAs="span"` the Trigger area MUST be an inline element that owns a box, so the cursor anchor measures from the trigger's own rect on every invocation path. Right-click, Shift+F10, long-press, and `context-menu` target placement all use that box. | `component:ContextMenu/DEC-1` | Accepted; verified in jsdom |
|
|
92
|
+
| FR7 | Sub-menu rows follow `component:DropdownMenu` FR12 inside the pointer menu: ContextMenu mounts the same drill-in view stack, named after its `label`, so a sub-menu drills in on a phone here too. | `component:DropdownMenu/DEC-2` | Proposed; verified in jsdom; owner decision pending |
|
|
93
|
+
| FR8 | In the pointer menu ArrowDown on the last enabled row wraps to the first and ArrowUp on the first to the last; PageDown and PageUp page as in DropdownMenu FR6; a held key's auto-repeat does not activate; typeahead matches the row's label element alone. | Current source and tests; shared `useListFocus` and `useTypeahead` suites | Proposed; verified in jsdom; owner decision pending |
|
|
94
|
+
| FR9 | While the Pointer menu surface is open, it participates in `family:overlay-dismissal` through the shared stack and provides descendant depth. One Escape press affects only the top-most present nested surface; ContextMenu closes only when it is that surface. | `family:overlay-dismissal` FR1–FR6; `ContextMenu.test.tsx` nested-layer case | Current family projection; verified in jsdom |
|
|
94
95
|
|
|
95
96
|
### Allowed variation
|
|
96
97
|
|
|
@@ -115,19 +116,21 @@ Consumer props, content modes, and presentation policy remain documented in
|
|
|
115
116
|
|
|
116
117
|
### Transformation and precedence order
|
|
117
118
|
|
|
118
|
-
-
|
|
119
|
-
|
|
119
|
+
- Presentation resolution, cursor positioning, long-press handling, item
|
|
120
|
+
rendering, and styling keep their current precedence.
|
|
120
121
|
|
|
121
122
|
### Performance and resources
|
|
122
123
|
|
|
123
|
-
-
|
|
124
|
+
- The component introduces no separate loading, listener, measurement, or render
|
|
125
|
+
constraint beyond the owners named above.
|
|
124
126
|
|
|
125
127
|
## Accessibility contract
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
the highlight is DOM focus per
|
|
129
|
+
ContextMenu keeps its menu and dialog naming, keyboard invocation, long-press
|
|
130
|
+
path, keyboard navigation, item semantics, focus return, and presentation-specific
|
|
131
|
+
dismissal behavior. Pointer-menu Escape requests participate in the shared layer
|
|
132
|
+
stack; while a pointer is held in the pointer menu, the highlight is DOM focus per
|
|
133
|
+
`module:DropdownMenu/useMenuPress` AR1.
|
|
131
134
|
|
|
132
135
|
- **AR1 — An inline trigger keeps its semantics.** `triggerAs` changes the
|
|
133
136
|
Trigger area's element and box only; it carries no role or `aria-haspopup`
|
|
@@ -185,27 +188,28 @@ anchor is positioning infrastructure rather than consumer anatomy.
|
|
|
185
188
|
- `architecture:component-theming-surface` owns anatomy qualification, local
|
|
186
189
|
target mapping, presentation-specific rows, and delegation rules.
|
|
187
190
|
- `architecture:interaction-modality` owns the shared right-click, keyboard,
|
|
188
|
-
long-press, pointer, touch, and adaptive-presentation boundaries
|
|
189
|
-
changes none of them.
|
|
191
|
+
long-press, pointer, touch, and adaptive-presentation boundaries.
|
|
190
192
|
- `architecture:layer-runtime` owns the current context-mode `useLayer` host and
|
|
191
|
-
cursor-anchor positioning. ContextMenu
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
cursor-anchor positioning. ContextMenu retains local outside-click handling and
|
|
194
|
+
joins shared Escape ordering through `useLayerDismissal`; touch hosting delegates
|
|
195
|
+
to BottomSheet.
|
|
196
|
+
- `family:overlay-dismissal` owns shared Escape and platform-close ordering.
|
|
197
|
+
ContextMenu's pointer presentation is a shared-stack adopter; the touch
|
|
198
|
+
presentation delegates dismissal to BottomSheet, which is also a shared-stack
|
|
199
|
+
owner.
|
|
196
200
|
|
|
197
201
|
## Verification map
|
|
198
202
|
|
|
199
|
-
| Contract | Verification | Representative states
|
|
200
|
-
| ------------------- | ------------------------------------------------------------------------------------------------- |
|
|
201
|
-
| FR1, FR4 | `ContextMenu.test.tsx` pointer, long-press, bottom-sheet, adaptive, compound, and drill-in suites | Pointer data/compound and touch data/compound
|
|
202
|
-
| FR2 | ContextMenu target tests, source inspection, and theming target inventories | Pointer and touch menu surfaces
|
|
203
|
-
| FR3 | ContextMenu, DropdownMenu, BottomSheet, and List owner tests | Pointer rows, touch frame, touch list/rows
|
|
204
|
-
| Layer relationships | `ContextMenu.test.tsx` and current layer/dismissal architecture records
|
|
205
|
-
| FR5 | `ContextMenu.test.tsx` press model case; `module:DropdownMenu/useMenuPress` map | Finger slide across rows, stray click
|
|
206
|
-
| FR6 | `ContextMenu.test.tsx` keyboard case; shared `useListFocus` and `useTypeahead` suites | wrap at both ends
|
|
207
|
-
| Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current target
|
|
208
|
-
| FR6, AR1 | `ContextMenu.test.tsx` "inline trigger (triggerAs)" suite | `span` in a paragraph, default `div`
|
|
203
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
204
|
+
| ------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------- |
|
|
205
|
+
| FR1, FR4 | `ContextMenu.test.tsx` pointer, long-press, bottom-sheet, adaptive, compound, and drill-in suites | Pointer data/compound and touch data/compound | Collapsing presentation-specific parts or content owners fails structure, role, or behavior tests. | `audit:ContextMenu/anatomy` |
|
|
206
|
+
| FR2 | ContextMenu target tests, source inspection, and theming target inventories | Pointer and touch menu surfaces | Moving `context-menu` to the trigger or removing it from a painted branch fails evidence. | `audit:ContextMenu/theming` |
|
|
207
|
+
| FR3 | ContextMenu, DropdownMenu, BottomSheet, and List owner tests | Pointer rows, touch frame, touch list/rows | A composed part loses its owner target or is documented as a new ContextMenu target. | `audit:ContextMenu/theming` |
|
|
208
|
+
| Layer relationships | `ContextMenu.test.tsx` nested-layer case and current layer/dismissal architecture records | Outside click, Escape, nested context layers, sheet | A Pointer menu bypassing the shared stack or dismissing with its nested child fails. | `audit:ContextMenu/behavior` |
|
|
209
|
+
| FR5 | `ContextMenu.test.tsx` press model case; `module:DropdownMenu/useMenuPress` map | Finger slide across rows, stray click | A row acting on the press row or the stray click fails. | `audit:ContextMenu/behavior` |
|
|
210
|
+
| FR6 | `ContextMenu.test.tsx` keyboard case; shared `useListFocus` and `useTypeahead` suites | wrap at both ends | Clamping arrows fail. | `audit:ContextMenu/behavior` |
|
|
211
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current target | Missing, extra, prefixed, stale, or unclassified mappings fail repository validation. | `audit:ContextMenu/theming` |
|
|
212
|
+
| FR6, AR1 | `ContextMenu.test.tsx` "inline trigger (triggerAs)" suite | `span` in a paragraph, default `div` | A block trigger inside prose, or a lost invocation path, fails. | `audit:ContextMenu/behavior` |
|
|
209
213
|
|
|
210
214
|
## Decision log
|
|
211
215
|
|
|
@@ -222,17 +226,11 @@ doors of a context menu without becoming a block that breaks the paragraph.
|
|
|
222
226
|
`triggerAs` renders the Trigger area as a `span`. The default `div` and its
|
|
223
227
|
`position: relative` are unchanged.
|
|
224
228
|
|
|
225
|
-
Rejected: a `
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
Rejected:
|
|
229
|
-
|
|
230
|
-
falls back to the nearest positioned ancestor — which in running prose is
|
|
231
|
-
usually the page. A mouse user gets the menu at the pointer and a keyboard
|
|
232
|
-
user pressing Shift+F10 gets it at the page corner, and the keyboard path is
|
|
233
|
-
the one with no way around it. A `span` is inline, owns a box, measures
|
|
234
|
-
correctly, and is invisible to layout in every way the prose case cares
|
|
235
|
-
about.
|
|
229
|
+
Rejected: styling a block `div` as inline because it remains invalid inside a
|
|
230
|
+
`<p>`.
|
|
231
|
+
|
|
232
|
+
Rejected: `display: contents` because a box-less wrapper has no rect for pointer
|
|
233
|
+
or keyboard anchoring.
|
|
236
234
|
|
|
237
235
|
## Open questions
|
|
238
236
|
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* SYNC: When ContextMenu.tsx changes, update tests to match new behavior
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import {useRef, useState} from 'react';
|
|
12
|
+
import {createPortal} from 'react-dom';
|
|
11
13
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
12
14
|
import {render, screen, fireEvent, act, waitFor} from '@testing-library/react';
|
|
13
15
|
import userEvent from '@testing-library/user-event';
|
|
@@ -23,6 +25,32 @@ import {
|
|
|
23
25
|
} from './index';
|
|
24
26
|
import {DropdownMenuItem} from '../DropdownMenu/DropdownMenuItem';
|
|
25
27
|
import {Divider} from '../Divider';
|
|
28
|
+
import {useLayerDismissal} from '../Layer/useLayerDismissal';
|
|
29
|
+
|
|
30
|
+
function NestedDismissibleLayer({onDismiss}: {onDismiss: () => void}) {
|
|
31
|
+
const [isOpen, setIsOpen] = useState(true);
|
|
32
|
+
const containerRef = useRef<HTMLButtonElement>(null);
|
|
33
|
+
|
|
34
|
+
useLayerDismissal({
|
|
35
|
+
isActive: isOpen,
|
|
36
|
+
onDismiss: () => {
|
|
37
|
+
setIsOpen(false);
|
|
38
|
+
onDismiss();
|
|
39
|
+
},
|
|
40
|
+
getContainer: () => containerRef.current,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Portaled outside the menu's DOM, so only the layer depth ContextMenu
|
|
44
|
+
// provides can rank it above the menu.
|
|
45
|
+
return isOpen
|
|
46
|
+
? createPortal(
|
|
47
|
+
<button ref={containerRef} type="button">
|
|
48
|
+
Nested layer
|
|
49
|
+
</button>,
|
|
50
|
+
document.body,
|
|
51
|
+
)
|
|
52
|
+
: null;
|
|
53
|
+
}
|
|
26
54
|
|
|
27
55
|
beforeEach(() => {
|
|
28
56
|
HTMLDialogElement.prototype.showModal = vi.fn(function (
|
|
@@ -149,6 +177,56 @@ describe('ContextMenu', () => {
|
|
|
149
177
|
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
150
178
|
});
|
|
151
179
|
|
|
180
|
+
it('composes the consumer context-menu handler with menu opening', () => {
|
|
181
|
+
const onContextMenu = vi.fn();
|
|
182
|
+
render(
|
|
183
|
+
<ContextMenu items={[{label: 'Item 1'}]} onContextMenu={onContextMenu}>
|
|
184
|
+
<div>Right-click me</div>
|
|
185
|
+
</ContextMenu>,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
fireEvent.contextMenu(screen.getByText('Right-click me'));
|
|
189
|
+
expect(onContextMenu).toHaveBeenCalledOnce();
|
|
190
|
+
expect(HTMLElement.prototype.showPopover).toHaveBeenCalledOnce();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('keeps component-owned opening when a consumer prevents default', () => {
|
|
194
|
+
render(
|
|
195
|
+
<ContextMenu
|
|
196
|
+
items={[{label: 'Item 1'}]}
|
|
197
|
+
onContextMenu={event => event.preventDefault()}>
|
|
198
|
+
<div>Right-click me</div>
|
|
199
|
+
</ContextMenu>,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
fireEvent.contextMenu(screen.getByText('Right-click me'));
|
|
203
|
+
expect(HTMLElement.prototype.showPopover).toHaveBeenCalledOnce();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('lets a nested layer consume Escape before the context menu', () => {
|
|
207
|
+
const onNestedDismiss = vi.fn();
|
|
208
|
+
render(
|
|
209
|
+
<ContextMenu
|
|
210
|
+
menuContent={<NestedDismissibleLayer onDismiss={onNestedDismiss} />}>
|
|
211
|
+
<div>Right-click me</div>
|
|
212
|
+
</ContextMenu>,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
fireEvent.contextMenu(screen.getByText('Right-click me'));
|
|
216
|
+
const nestedLayer = screen.getByRole('button', {
|
|
217
|
+
name: 'Nested layer',
|
|
218
|
+
hidden: true,
|
|
219
|
+
});
|
|
220
|
+
nestedLayer.focus();
|
|
221
|
+
fireEvent.keyDown(nestedLayer, {key: 'Escape'});
|
|
222
|
+
|
|
223
|
+
expect(onNestedDismiss).toHaveBeenCalledOnce();
|
|
224
|
+
expect(HTMLElement.prototype.hidePopover).not.toHaveBeenCalled();
|
|
225
|
+
|
|
226
|
+
fireEvent.keyDown(document, {key: 'Escape'});
|
|
227
|
+
expect(HTMLElement.prototype.hidePopover).toHaveBeenCalledOnce();
|
|
228
|
+
});
|
|
229
|
+
|
|
152
230
|
it('opens the real menu content in a BottomSheet when requested', async () => {
|
|
153
231
|
render(
|
|
154
232
|
<ContextMenu
|
|
@@ -393,6 +471,60 @@ describe('ContextMenu', () => {
|
|
|
393
471
|
}
|
|
394
472
|
});
|
|
395
473
|
|
|
474
|
+
it('composes consumer touch handlers with long-press detection', () => {
|
|
475
|
+
vi.useFakeTimers();
|
|
476
|
+
try {
|
|
477
|
+
const onTouchStart = vi.fn();
|
|
478
|
+
render(
|
|
479
|
+
<ContextMenu
|
|
480
|
+
items={[{label: 'Item 1'}]}
|
|
481
|
+
data-testid="ctx"
|
|
482
|
+
onTouchStart={onTouchStart}>
|
|
483
|
+
<div>Long-press me</div>
|
|
484
|
+
</ContextMenu>,
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
fireEvent.touchStart(screen.getByTestId('ctx'), {
|
|
488
|
+
touches: [{clientX: 20, clientY: 20}],
|
|
489
|
+
});
|
|
490
|
+
expect(onTouchStart).toHaveBeenCalledOnce();
|
|
491
|
+
|
|
492
|
+
act(() => {
|
|
493
|
+
vi.advanceTimersByTime(500);
|
|
494
|
+
});
|
|
495
|
+
expect(HTMLElement.prototype.showPopover).toHaveBeenCalledOnce();
|
|
496
|
+
} finally {
|
|
497
|
+
vi.useRealTimers();
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('clears a pending long-press when a consumer prevents default on touch end', () => {
|
|
502
|
+
vi.useFakeTimers();
|
|
503
|
+
try {
|
|
504
|
+
render(
|
|
505
|
+
<ContextMenu
|
|
506
|
+
items={[{label: 'Item 1'}]}
|
|
507
|
+
data-testid="ctx"
|
|
508
|
+
onTouchEnd={event => event.preventDefault()}>
|
|
509
|
+
<div>Long-press me</div>
|
|
510
|
+
</ContextMenu>,
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
const trigger = screen.getByTestId('ctx');
|
|
514
|
+
fireEvent.touchStart(trigger, {
|
|
515
|
+
touches: [{clientX: 20, clientY: 20}],
|
|
516
|
+
});
|
|
517
|
+
fireEvent.touchEnd(trigger);
|
|
518
|
+
|
|
519
|
+
act(() => {
|
|
520
|
+
vi.advanceTimersByTime(500);
|
|
521
|
+
});
|
|
522
|
+
expect(HTMLElement.prototype.showPopover).not.toHaveBeenCalled();
|
|
523
|
+
} finally {
|
|
524
|
+
vi.useRealTimers();
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
|
|
396
528
|
it('suppresses native text selection on the long-press trigger', () => {
|
|
397
529
|
const source = readFileSync(
|
|
398
530
|
'packages/core/src/ContextMenu/ContextMenu.tsx',
|
|
@@ -1103,7 +1235,6 @@ describe('ContextMenuGroup', () => {
|
|
|
1103
1235
|
// Three public surfaces alias this component at once. An alias that
|
|
1104
1236
|
// re-exports without the semantics would pass a render test and fail a
|
|
1105
1237
|
// screen reader.
|
|
1106
|
-
const user = userEvent.setup();
|
|
1107
1238
|
render(
|
|
1108
1239
|
<ContextMenu
|
|
1109
1240
|
menuContent={
|