@astryxdesign/core 0.7.2-canary.fe2c655 → 0.7.2
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 +156 -2
- package/README.md +4 -4
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +40 -1
- package/dist/AspectRatio/AspectRatio.d.ts +6 -8
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +44 -3
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +8 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +5 -2
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +1 -0
- package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +3 -1
- package/dist/CheckboxList/CheckboxList.d.ts +1 -1
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +6 -2
- package/dist/CheckboxList/CheckboxListItem.d.ts +9 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +4 -2
- package/dist/CommandPalette/CommandPaletteInput.d.ts +1 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +37 -5
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +5 -2
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +20 -8
- package/dist/DropdownMenu/DropdownMenu.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +84 -113
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -0
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.js +21 -0
- package/dist/DropdownMenu/DropdownMenuDivider.d.ts +5 -3
- package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuDivider.js +7 -3
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts +6 -4
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuGroup.js +4 -3
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +15 -4
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +24 -0
- package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -0
- package/dist/DropdownMenu/lazyMenuBottomSheet.js +46 -0
- package/dist/DropdownMenu/menuRegionStops.d.ts +38 -0
- package/dist/DropdownMenu/menuRegionStops.d.ts.map +1 -0
- package/dist/DropdownMenu/menuRegionStops.js +131 -0
- package/dist/EmptyState/EmptyState.d.ts.map +1 -1
- package/dist/EmptyState/EmptyState.js +4 -10
- package/dist/Field/Field.d.ts +1 -1
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +8 -4
- package/dist/FormLayout/FormLayout.d.ts +5 -4
- package/dist/FormLayout/FormLayout.d.ts.map +1 -1
- package/dist/FormLayout/FormLayout.js +1 -1
- package/dist/FormLayout/FormLayoutContext.d.ts +3 -3
- package/dist/FormLayout/FormLayoutContext.js +3 -3
- package/dist/Grid/Grid.d.ts +8 -7
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +5 -4
- package/dist/Grid/GridSpan.d.ts +1 -1
- package/dist/Grid/GridSpan.d.ts.map +1 -1
- package/dist/Grid/GridSpan.js +1 -0
- package/dist/Heading/Heading.d.ts.map +1 -1
- package/dist/Heading/Heading.js +4 -2
- package/dist/Item/Item.d.ts +6 -2
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +19 -4
- package/dist/List/List.d.ts +46 -19
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +28 -10
- package/dist/List/ListContext.d.ts +8 -3
- package/dist/List/ListContext.d.ts.map +1 -1
- package/dist/List/ListContext.js +7 -2
- package/dist/List/ListItem.d.ts +13 -5
- package/dist/List/ListItem.d.ts.map +1 -1
- package/dist/List/ListItem.js +21 -8
- package/dist/List/index.d.ts +1 -1
- package/dist/List/index.d.ts.map +1 -1
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +8 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +8 -2
- package/dist/Markdown/plugins/headingLinks.d.ts.map +1 -1
- package/dist/Markdown/plugins/headingLinks.js +9 -1
- package/dist/MetadataList/MetadataList.d.ts.map +1 -1
- package/dist/MetadataList/MetadataList.js +5 -1
- package/dist/MetadataList/MetadataListContext.d.ts +1 -1
- package/dist/MetadataList/MetadataListContext.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.d.ts +9 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +30 -3
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +5 -0
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +45 -11
- package/dist/RadioList/RadioList.d.ts +1 -1
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +6 -2
- package/dist/RadioList/RadioListItem.d.ts +9 -1
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +2 -0
- package/dist/SideNav/SideNavItem.d.ts +8 -1
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +76 -10
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +4 -2
- package/dist/ToggleButton/ToggleButton.d.ts +3 -2
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +12 -1
- package/dist/Token/Token.js +1 -1
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +5 -3
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +5 -3
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +1 -0
- package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
- package/dist/TreeList/TreeListBranches.js +2 -2
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +57 -9
- package/dist/astryx.css +27 -4
- package/dist/hooks/useMenuHover.d.ts.map +1 -1
- package/dist/hooks/useMenuHover.js +18 -13
- package/dist/reset.a11y.chromium.spec.js +50 -1
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +15 -9
- package/docs.mjs +4 -4
- package/package.json +4 -4
- package/scripts/agent-doc-state.mjs +66 -6
- package/scripts/postinstall.mjs +44 -3
- package/src/AppShell/AppShell.tsx +45 -1
- package/src/AspectRatio/AspectRatio.test.tsx +44 -43
- package/src/AspectRatio/AspectRatio.tsx +38 -18
- package/src/AspectRatio/__tests__/AspectRatioFitLayers.a11y.browser.spec.ts +157 -0
- package/src/Avatar/Avatar.doc.mjs +5 -3
- package/src/Avatar/Avatar.tsx +11 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +3 -2
- package/src/AvatarGroup/AvatarGroupOverflow.test.tsx +24 -1
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -0
- package/src/Breadcrumbs/BreadcrumbItem.tsx +4 -1
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +30 -1
- package/src/Card/Card.doc.mjs +1 -1
- package/src/Chat/ChatLayoutScrollButton.test.tsx +13 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +4 -2
- package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +64 -19
- package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +3 -1
- package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
- package/src/CheckboxInput/CheckboxInput.tsx +3 -1
- package/src/CheckboxList/CheckboxList.fr10.test.tsx +29 -2
- package/src/CheckboxList/CheckboxList.test.tsx +22 -0
- package/src/CheckboxList/CheckboxList.tsx +4 -1
- package/src/CheckboxList/CheckboxListItem.doc.mjs +16 -0
- package/src/CheckboxList/CheckboxListItem.tsx +10 -0
- package/src/Code/Code.doc.mjs +1 -0
- package/src/CommandPalette/CommandPalette.doc.mjs +2 -0
- package/src/CommandPalette/CommandPaletteInput.test.tsx +53 -4
- package/src/CommandPalette/CommandPaletteInput.tsx +33 -3
- package/src/CommandPalette/__tests__/CommandPaletteInputAudit.a11y.chromium.spec.ts +335 -0
- package/src/CommandPalette/__tests__/CommandPaletteItemAudit.a11y.chromium.spec.ts +292 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +2 -3
- package/src/ContextMenu/ContextMenu.test.tsx +12 -2
- package/src/ContextMenu/ContextMenu.tsx +4 -1
- package/src/ContextMenu/__tests__/ContextMenuAudit.a11y.chromium.spec.ts +438 -0
- package/src/Dialog/Dialog.test.tsx +46 -0
- package/src/Dialog/Dialog.tsx +20 -7
- package/src/Dialog/__tests__/Dialog.a11y.known-failures.ts +0 -14
- package/src/Drawer/Drawer.doc.mjs +1 -0
- package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +57 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +79 -6
- package/src/DropdownMenu/DropdownMenu.tsx +88 -144
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +1 -1
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -1
- package/src/DropdownMenu/DropdownMenuContext.tsx +24 -0
- package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +2 -2
- package/src/DropdownMenu/DropdownMenuDivider.spec.md +192 -0
- package/src/DropdownMenu/DropdownMenuDivider.tsx +9 -5
- package/src/DropdownMenu/DropdownMenuGroup.doc.mjs +169 -10
- package/src/DropdownMenu/DropdownMenuGroup.tsx +6 -4
- package/src/DropdownMenu/DropdownMenuLazySheet.test.tsx +126 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +1 -1
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +1 -1
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +185 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +17 -3
- package/src/DropdownMenu/__tests__/DropdownMenuDividerAudit.a11y.chromium.spec.ts +280 -0
- package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +334 -0
- package/src/DropdownMenu/__tests__/warmMenuBottomSheet.tsx +43 -0
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +71 -0
- package/src/DropdownMenu/menuRegionStops.ts +167 -0
- package/src/EmptyState/EmptyState.doc.mjs +3 -2
- package/src/EmptyState/EmptyState.spec.md +189 -0
- package/src/EmptyState/EmptyState.test.tsx +74 -3
- package/src/EmptyState/EmptyState.tsx +4 -8
- package/src/Field/Field.doc.mjs +14 -8
- package/src/Field/Field.spec.md +217 -0
- package/src/Field/Field.test.tsx +60 -2
- package/src/Field/Field.tsx +17 -8
- package/src/Field/FieldLabel.doc.mjs +133 -26
- package/src/Field/FieldLabel.spec.md +172 -0
- package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +296 -0
- package/src/FormLayout/FormLayout.doc.mjs +6 -3
- package/src/FormLayout/FormLayout.spec.md +192 -0
- package/src/FormLayout/FormLayout.test.tsx +15 -0
- package/src/FormLayout/FormLayout.tsx +7 -6
- package/src/FormLayout/FormLayoutContext.ts +3 -3
- package/src/Grid/Grid.doc.mjs +7 -1
- package/src/Grid/Grid.spec.md +93 -113
- package/src/Grid/Grid.test.tsx +22 -0
- package/src/Grid/Grid.tsx +10 -9
- package/src/Grid/GridSpan.doc.mjs +3 -3
- package/src/Grid/GridSpan.tsx +4 -1
- package/src/Heading/Heading.test.tsx +57 -0
- package/src/Heading/Heading.tsx +6 -2
- package/src/Icon/Icon.doc.mjs +36 -0
- package/src/Indicator/Indicator.doc.mjs +3 -3
- package/src/Item/Item.doc.mjs +12 -8
- package/src/Item/Item.test.tsx +61 -0
- package/src/Item/Item.tsx +40 -4
- package/src/Layout/LayoutHeader.doc.mjs +5 -0
- package/src/List/List.doc.mjs +14 -1
- package/src/List/List.spec.md +129 -77
- package/src/List/List.test.tsx +163 -94
- package/src/List/List.tsx +74 -18
- package/src/List/ListContext.tsx +9 -3
- package/src/List/ListItem.doc.mjs +16 -0
- package/src/List/ListItem.tsx +56 -27
- package/src/List/index.ts +1 -1
- package/src/Markdown/Markdown.tsx +14 -1
- package/src/Markdown/parser.ts +8 -2
- package/src/Markdown/plugins/headingLinks.ts +9 -1
- package/src/MetadataList/MetadataList.doc.mjs +3 -3
- package/src/MetadataList/MetadataList.test.tsx +38 -0
- package/src/MetadataList/MetadataList.tsx +8 -1
- package/src/MetadataList/MetadataListContext.tsx +1 -1
- package/src/MobileNav/MobileNav.tsx +30 -3
- package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +173 -15
- package/src/MoreMenu/MoreMenu.test.tsx +5 -1
- package/src/PowerSearch/PowerSearch.test.tsx +62 -0
- package/src/PowerSearch/PowerSearch.tsx +9 -0
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +71 -0
- package/src/PowerSearch/PowerSearchValueEditor.tsx +45 -0
- package/src/ProgressBar/ProgressBar.doc.mjs +1 -0
- package/src/RadioList/RadioList.fr10.test.tsx +45 -2
- package/src/RadioList/RadioList.test.tsx +22 -0
- package/src/RadioList/RadioList.tsx +4 -1
- package/src/RadioList/RadioListItem.doc.mjs +16 -0
- package/src/RadioList/RadioListItem.tsx +10 -1
- package/src/Resizable/useResizable.doc.mjs +2 -0
- package/src/ScrollableArea/ScrollableArea.doc.mjs +1 -1
- package/src/SideNav/SideNav.spec.md +227 -0
- package/src/SideNav/SideNav.test.tsx +220 -0
- package/src/SideNav/SideNavItem.doc.mjs +2 -2
- package/src/SideNav/SideNavItem.tsx +99 -17
- package/src/Slider/Slider.spec.md +39 -18
- package/src/Spinner/Spinner.doc.mjs +1 -1
- package/src/TabList/TabList.spec.md +57 -44
- package/src/Table/Table.spec.md +6 -1
- package/src/Table/plugins/activation/useTableActivation.spec.md +254 -0
- package/src/Text/Text.test.tsx +57 -0
- package/src/Text/Text.tsx +6 -2
- package/src/TimeInput/TimeInput.fr10.test.tsx +15 -0
- package/src/Timestamp/Timestamp.doc.mjs +1 -1
- package/src/ToggleButton/ToggleButton.test.tsx +95 -0
- package/src/ToggleButton/ToggleButton.tsx +12 -1
- package/src/ToggleButton/ToggleButtonGroup.doc.mjs +4 -0
- package/src/Token/Token.tsx +1 -1
- package/src/Token/__tests__/TokenRemoveTarget.a11y.chromium.spec.ts +86 -0
- package/src/Toolbar/Toolbar.test.tsx +0 -8
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +163 -0
- package/src/Toolbar/__tests__/Toolbar.a11y.renders.tsx +63 -0
- package/src/Toolbar/__tests__/Toolbar.a11y.states.ts +142 -0
- package/src/Toolbar/__tests__/Toolbar.a11y.test.tsx +126 -0
- package/src/TopNav/TopNavMegaMenu.test.tsx +47 -0
- package/src/TopNav/TopNavMegaMenu.tsx +9 -3
- package/src/TopNav/TopNavMenu.test.tsx +46 -1
- package/src/TopNav/TopNavMenu.tsx +13 -3
- package/src/TreeList/TreeList.doc.mjs +1 -0
- package/src/TreeList/TreeList.test.tsx +327 -6
- package/src/TreeList/TreeList.tsx +5 -1
- package/src/TreeList/TreeListBranches.tsx +6 -0
- package/src/TreeList/TreeListItem.tsx +88 -13
- package/src/docPropLiterals.test.ts +36 -10
- package/src/hooks/useMenuHover.ts +18 -13
- package/src/postinstall.test.mjs +79 -0
- package/src/reset.a11y.chromium.spec.ts +52 -1
- package/src/reset.css +9 -29
- package/src/reset.test.ts +11 -0
- package/src/theme/derivedVarRegistry.test.ts +7 -0
- package/src/unicodePropertyEscapes.source-build.test.mjs +69 -0
- package/src/utils/plainDate.test.ts +37 -1
- package/src/utils/plainDate.ts +15 -5
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 3
|
|
3
|
+
template_version: 7
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:DropdownMenuDivider
|
|
6
|
+
authority: draft
|
|
7
|
+
archive_reason: null
|
|
8
|
+
superseded_by: null
|
|
9
|
+
approved_by: null
|
|
10
|
+
approved_at: null
|
|
11
|
+
owners: [cixzhang]
|
|
12
|
+
review_triggers: [public-api, behavior, theming, accessibility, testing]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/DropdownMenu/DropdownMenu.test.tsx,
|
|
16
|
+
packages/core/src/ContextMenu/ContextMenu.test.tsx,
|
|
17
|
+
packages/core/src/Breadcrumbs/Breadcrumbs.test.tsx,
|
|
18
|
+
packages/core/src/theme/themingTargets.test.ts,
|
|
19
|
+
apps/storybook/stories/DropdownMenuDivider.stories.tsx,
|
|
20
|
+
scripts/check-knowledge.mjs,
|
|
21
|
+
]
|
|
22
|
+
modules: []
|
|
23
|
+
families: []
|
|
24
|
+
design_specs: []
|
|
25
|
+
architecture:
|
|
26
|
+
[
|
|
27
|
+
architecture:public-component-api,
|
|
28
|
+
architecture:component-theming-surface,
|
|
29
|
+
architecture:component-test-sufficiency,
|
|
30
|
+
]
|
|
31
|
+
contributing: []
|
|
32
|
+
system_specs: [spec:AST-020, spec:AST-029]
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# DropdownMenuDivider component contract
|
|
36
|
+
|
|
37
|
+
## Contract at a glance
|
|
38
|
+
|
|
39
|
+
| Area | Contract |
|
|
40
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| Public contract | `DropdownMenuDivider` / `DropdownMenuDividerProps`, plus the `ContextMenuDivider` and `BreadcrumbMenuDivider` aliases and their props; `BaseProps<HTMLDivElement>` minus caller-owned `role` and `aria-orientation`; a root `ref`. |
|
|
42
|
+
| Behavior | One unlabelled horizontal separator divides pointer-menu rows and stays out of arrow-key order; data and compound pointer modes render the same component. |
|
|
43
|
+
| End-user impact | Pointer-menu sections remain visually separated without adding a focus stop or changing row operation. |
|
|
44
|
+
| Builder impact | No new caller choice. Host attributes, handlers, and styling inputs continue to compose on the separator. |
|
|
45
|
+
| Compatibility/readiness | Patch-compatible observational contract. Runtime behavior, exports, defaults, and target names remain unchanged; authority stays `draft` until exact-head owner approval. |
|
|
46
|
+
| Review checks | Reject a focusable separator, changed role/orientation, pointer data/compound divergence, dropped root inputs, or a target moved off the visible separator. |
|
|
47
|
+
| Governing rules | `architecture:public-component-api/INV1,INV5–INV9`; `architecture:component-theming-surface/INV3–INV6`; WCAG 2.2 SC 1.3.1 and 4.1.2 through `spec:AST-020/FR1`. |
|
|
48
|
+
|
|
49
|
+
This table is a review projection; the body below is authoritative.
|
|
50
|
+
|
|
51
|
+
## Intent
|
|
52
|
+
|
|
53
|
+
DropdownMenuDivider provides the visual and programmatic boundary between groups
|
|
54
|
+
of rows in pointer-menu presentations. It ships directly under the DropdownMenu
|
|
55
|
+
name and through ContextMenu and Breadcrumbs aliases. It owns the separator
|
|
56
|
+
semantics, menu spacing, root input composition, and the
|
|
57
|
+
`dropdown-menu-divider` theme target. The surrounding DropdownMenu, ContextMenu,
|
|
58
|
+
or BreadcrumbItem owns menu lifecycle and row navigation; Divider supplies the
|
|
59
|
+
base rule geometry and paint.
|
|
60
|
+
|
|
61
|
+
Consumer syntax and examples belong in `DropdownMenuDivider.doc.mjs`.
|
|
62
|
+
|
|
63
|
+
## Compatibility and migration
|
|
64
|
+
|
|
65
|
+
- Released default preserved: yes — one horizontal, unlabelled separator.
|
|
66
|
+
- Compatibility class: observational patch; no behavior or public surface changes.
|
|
67
|
+
- Controlled/uncontrolled behavior: not applicable.
|
|
68
|
+
- Migration decision: none.
|
|
69
|
+
|
|
70
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
71
|
+
|
|
72
|
+
## Ownership boundary
|
|
73
|
+
|
|
74
|
+
**Owns**
|
|
75
|
+
|
|
76
|
+
- One horizontal separator root and its public ref, DOM/data/ARIA, class, style,
|
|
77
|
+
event, and `xstyle` composition.
|
|
78
|
+
- Menu-specific block-axis spacing.
|
|
79
|
+
- The `dropdown-menu-divider` target on the same visible root.
|
|
80
|
+
- Pointer-menu data/compound rendering parity for the divider row across
|
|
81
|
+
DropdownMenu, ContextMenu, and breadcrumb menus.
|
|
82
|
+
|
|
83
|
+
**Does not own / non-goals**
|
|
84
|
+
|
|
85
|
+
- Menu open state, focus entry, row activation, or arrow-key navigation — owned by
|
|
86
|
+
DropdownMenu, ContextMenu, BreadcrumbItem, and their shared hooks.
|
|
87
|
+
- Touch and bottom-sheet data dividers — those presentations render Divider
|
|
88
|
+
directly and are owned by their parent menu surface.
|
|
89
|
+
- Base separator rule geometry and token paint — delegated to Divider.
|
|
90
|
+
- Caller-authored grouping meaning — owned by the product callsite.
|
|
91
|
+
|
|
92
|
+
## Public concepts
|
|
93
|
+
|
|
94
|
+
| Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
|
|
95
|
+
| ------------- | --------------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- | ---------------------- | -------------------- | --------- | ------------------------------------------------------------------------------- |
|
|
96
|
+
| Separator row | one horizontal unlabelled separator | Divides adjacent pointer-menu row groups without becoming a row or focus stop. | Data and compound pointer-menu modes | present where authored | component | stable | Role and orientation are excluded from caller props and remain component-owned. |
|
|
97
|
+
| Root surface | ref; DOM/data/ARIA; class/style/xstyle/events | Extends the separator root while preserving component semantics. | Every separator row | omitted | public component API | stable | Accepted inputs compose; role and orientation remain authoritative. |
|
|
98
|
+
|
|
99
|
+
## Behavioral and layout contract
|
|
100
|
+
|
|
101
|
+
Draft requirements identify their basis so observed code is not mistaken for an
|
|
102
|
+
intentional decision.
|
|
103
|
+
|
|
104
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
105
|
+
| --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------------------ |
|
|
106
|
+
| FR1 | The component MUST render one unlabelled horizontal separator with pointer-menu-specific block spacing. | Released source/docs and focused tests | verify |
|
|
107
|
+
| FR2 | The root MUST expose `role="separator"` and `aria-orientation="horizontal"`, remain outside menu-item collection and arrow-key order, and never become focusable. | WCAG 2.2 SC 1.3.1 and 4.1.2; focused tests | verify |
|
|
108
|
+
| FR3 | In pointer-menu presentation, `{type: "divider"}` in the data API and the compound divider component or alias MUST render the same component DOM, spacing, semantics, and targets. | Released docs/source; data/compound parity test | verify |
|
|
109
|
+
| FR4 | The visible root MUST carry both the delegated `divider` target and the menu-specific `dropdown-menu-divider` target; menu spacing applies before caller `xstyle`. | `architecture:component-theming-surface/INV3–INV6`; source/tests | verify |
|
|
110
|
+
| FR5 | 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 |
|
|
111
|
+
|
|
112
|
+
### Allowed variation
|
|
113
|
+
|
|
114
|
+
- **AV1 — Theme paint.** The delegated Divider and menu-specific targets may restyle
|
|
115
|
+
their owned surface without changing separator semantics or focus behavior.
|
|
116
|
+
- **AV2 — Caller root inputs.** Supported host attributes, handlers, and styling
|
|
117
|
+
inputs may vary while the owned role and orientation remain fixed.
|
|
118
|
+
|
|
119
|
+
### Representative states
|
|
120
|
+
|
|
121
|
+
| State | Required invariant | Allowed variation |
|
|
122
|
+
| ------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
123
|
+
| Pointer data-mode divider | One horizontal separator between data-driven rows. | Adjacent row content and active theme may vary. |
|
|
124
|
+
| Pointer compound divider | DOM and behavior match the pointer data-mode divider. | Host attributes and supported styling inputs may be supplied. |
|
|
125
|
+
| Public aliases | ContextMenuDivider and BreadcrumbMenuDivider render this component. | The containing menu owns lifecycle and navigation. |
|
|
126
|
+
| Touch data divider | Outside this component; the parent surface renders Divider directly. | Touch-menu layout follows the parent contract. |
|
|
127
|
+
| RTL | The horizontal, full-width rule remains direction-neutral. | Surrounding text and menu alignment may mirror. |
|
|
128
|
+
| Narrow viewport | The rule remains inside the menu without horizontal overflow. | Menu width follows its owner. |
|
|
129
|
+
|
|
130
|
+
### Transformation and precedence order
|
|
131
|
+
|
|
132
|
+
- **ORD1 — Root composition.** Apply menu spacing, then caller `xstyle`; combine the
|
|
133
|
+
menu-specific target class with caller `className`; preserve caller `style` and
|
|
134
|
+
host props; keep role and orientation component-owned.
|
|
135
|
+
|
|
136
|
+
### Performance and resources
|
|
137
|
+
|
|
138
|
+
- **PR1 — No external lifecycle.** DropdownMenuDivider installs no listener,
|
|
139
|
+
observer, timer, request, state machine, or Effect.
|
|
140
|
+
|
|
141
|
+
## Accessibility contract
|
|
142
|
+
|
|
143
|
+
- **AR1 — Separator semantics.** The root exposes a horizontal separator and no
|
|
144
|
+
interactive role or tab stop.
|
|
145
|
+
- **AR2 — Navigation exclusion.** Menu navigation collects menu-item roles only, so
|
|
146
|
+
ArrowDown and ArrowUp pass across the separator without focusing it.
|
|
147
|
+
|
|
148
|
+
## Design relationships
|
|
149
|
+
|
|
150
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
151
|
+
| ---------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------- | ------------------ |
|
|
152
|
+
| Menu separator | Visually separates adjacent menu row groups without competing with row content. | Current source and delegated Divider tokens; no component-local visual decision added | supporting | FR1–FR4 |
|
|
153
|
+
|
|
154
|
+
No component-local visual decision is settled by this draft.
|
|
155
|
+
|
|
156
|
+
## Family and system relationships
|
|
157
|
+
|
|
158
|
+
- `architecture:public-component-api` owns exports and root input composition.
|
|
159
|
+
- `architecture:component-theming-surface` owns target placement and composed target
|
|
160
|
+
ownership.
|
|
161
|
+
- `architecture:component-test-sufficiency` owns proportionate, observable evidence.
|
|
162
|
+
- DropdownMenu exports the implementation directly. ContextMenu and Breadcrumbs
|
|
163
|
+
re-export it as `ContextMenuDivider` and `BreadcrumbMenuDivider`, with matching
|
|
164
|
+
props aliases; their pointer presentations retain the same component contract.
|
|
165
|
+
- Touch and bottom-sheet data dividers bypass this component and delegate directly
|
|
166
|
+
to Divider under the parent menu contract.
|
|
167
|
+
- Divider provides the base separator implementation; its draft record is
|
|
168
|
+
observational context, not authority for this contract.
|
|
169
|
+
- `spec:AST-029` keeps this observational draft separate from the audit receipt.
|
|
170
|
+
|
|
171
|
+
## Verification map
|
|
172
|
+
|
|
173
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
174
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
175
|
+
| FR1–FR3, AR1–AR2 | `DropdownMenu.test.tsx`; ContextMenu and Breadcrumbs alias tests; dedicated Storybook state; scoped axe | pointer data/compound, aliases, open menu, keyboard traversal | Pointer DOM modes drift, an alias disappears, the role/orientation changes, or the separator enters arrow-key order | `audit:DropdownMenuDivider/§1,§3,§4,§6` |
|
|
176
|
+
| FR4 | Focused root-composition test; exact-head Neutral/Butter browser frames | light/dark themes; menu and delegated targets | A target leaves the visible root or caller `xstyle` stops composing after menu spacing | `audit:DropdownMenuDivider/§2,§5` |
|
|
177
|
+
| FR5 | Focused root-composition test; lint and typecheck | ref, host attributes, class/style/xstyle/events | An accepted input is dropped or owned semantics are overwritten | `audit:DropdownMenuDivider/§3` |
|
|
178
|
+
| PR1 | Source review | every state | A lifecycle mechanism appears without a resource contract | `audit:DropdownMenuDivider/§7` |
|
|
179
|
+
|
|
180
|
+
## Decision log
|
|
181
|
+
|
|
182
|
+
None. This draft records current facts and introduces no product decision.
|
|
183
|
+
|
|
184
|
+
## Open questions
|
|
185
|
+
|
|
186
|
+
None.
|
|
187
|
+
|
|
188
|
+
## Content boundary
|
|
189
|
+
|
|
190
|
+
This file does not duplicate consumer prop tables/examples, current audit scores,
|
|
191
|
+
screenshot matrices, implementation steps, or system rules. The audit pull request
|
|
192
|
+
owns the closed run evidence required by `spec:AST-029/FR5`.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file DropdownMenuDivider.tsx
|
|
7
|
-
* @input Uses Divider, spacing tokens, themeProps
|
|
7
|
+
* @input Uses Divider, spacing tokens, themeProps; forwards host props to Divider
|
|
8
8
|
* @output Exports DropdownMenuDivider component and DropdownMenuDividerProps
|
|
9
9
|
* @position Sub-component; used inside DropdownMenu
|
|
10
10
|
*
|
|
@@ -37,9 +37,9 @@ const styles = stylex.create({
|
|
|
37
37
|
|
|
38
38
|
const THEME_CLASS_NAME = themeProps('dropdown-menu-divider').className;
|
|
39
39
|
|
|
40
|
-
export interface DropdownMenuDividerProps extends
|
|
41
|
-
BaseProps
|
|
42
|
-
'
|
|
40
|
+
export interface DropdownMenuDividerProps extends Omit<
|
|
41
|
+
BaseProps<HTMLDivElement>,
|
|
42
|
+
'role' | 'aria-orientation'
|
|
43
43
|
> {
|
|
44
44
|
/** Ref forwarded to the separator element. */
|
|
45
45
|
ref?: React.Ref<HTMLDivElement>;
|
|
@@ -49,7 +49,9 @@ export interface DropdownMenuDividerProps extends Pick<
|
|
|
49
49
|
* A horizontal rule separating groups of menu rows.
|
|
50
50
|
*
|
|
51
51
|
* Renders `role="separator"`, so it is never a stop in the menu's arrow-key
|
|
52
|
-
* order. Equivalent to `{type: 'divider'}` in the `items` data API.
|
|
52
|
+
* order. Equivalent to `{type: 'divider'}` in the `items` data API. Host
|
|
53
|
+
* attributes (`id`, `data-*`, `aria-*`) and DOM handlers reach the separator;
|
|
54
|
+
* its role and orientation stay component-owned.
|
|
53
55
|
*
|
|
54
56
|
* @example
|
|
55
57
|
* ```
|
|
@@ -65,9 +67,11 @@ export function DropdownMenuDivider({
|
|
|
65
67
|
className,
|
|
66
68
|
style,
|
|
67
69
|
ref,
|
|
70
|
+
...rest
|
|
68
71
|
}: DropdownMenuDividerProps) {
|
|
69
72
|
return (
|
|
70
73
|
<Divider
|
|
74
|
+
{...rest}
|
|
71
75
|
ref={ref}
|
|
72
76
|
xstyle={[styles.divider, xstyle]}
|
|
73
77
|
className={
|
|
@@ -13,25 +13,86 @@ export const docs = {
|
|
|
13
13
|
defaults: {title: 'Version history'},
|
|
14
14
|
},
|
|
15
15
|
description:
|
|
16
|
-
'A
|
|
16
|
+
'A group of rows in a compound menu. A visible title names role="group" through aria-labelledby; an omitted title leaves the group unnamed. The heading is not a menuitem, so arrow keys and typeahead skip it. Keep title content non-interactive and place controls in DropdownMenu header or footer. The data-driven equivalent is the `{type: "section", title, items}` entry in `items`; both forms share the heading treatment and theme target.',
|
|
17
|
+
usage: {
|
|
18
|
+
description:
|
|
19
|
+
'Use DropdownMenuGroup to give related compound-menu rows one visible and programmatic name. Use an untitled group only when a separate name would not help people understand the rows.',
|
|
20
|
+
bestPractices: [
|
|
21
|
+
{
|
|
22
|
+
guidance: true,
|
|
23
|
+
description:
|
|
24
|
+
'Use short, descriptive title content that explains what the grouped rows have in common.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
guidance: true,
|
|
28
|
+
description:
|
|
29
|
+
'Keep the title non-interactive so every available action remains in the menu arrow-key order.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
guidance: false,
|
|
33
|
+
description:
|
|
34
|
+
'Place buttons, links, fields, or other controls in title. Put them in DropdownMenu header or footer instead.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
guidance: false,
|
|
38
|
+
description:
|
|
39
|
+
'Wrap rows that do not form a meaningful set; a group adds structure only when its relationship helps users.',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
anatomy: [
|
|
43
|
+
{
|
|
44
|
+
name: 'Group',
|
|
45
|
+
required: true,
|
|
46
|
+
description:
|
|
47
|
+
'The role="group" wrapper containing the heading and menu rows.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'Heading',
|
|
51
|
+
required: false,
|
|
52
|
+
description:
|
|
53
|
+
'Visible non-interactive title content that names the group and carries the dropdown-menu-section-heading theme target.',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Rows',
|
|
57
|
+
required: false,
|
|
58
|
+
description:
|
|
59
|
+
'Menu items, selectable items, radio groups, or submenus owned by DropdownMenu.',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
17
63
|
props: [
|
|
18
64
|
{
|
|
19
65
|
name: 'title',
|
|
20
66
|
type: 'ReactNode',
|
|
21
67
|
description:
|
|
22
|
-
"
|
|
68
|
+
"Non-interactive heading content shown above the rows and used as the group's accessible name. Omit for an unnamed group; place controls in DropdownMenu header or footer.",
|
|
23
69
|
},
|
|
24
70
|
{
|
|
25
71
|
name: 'children',
|
|
26
72
|
type: 'ReactNode',
|
|
27
73
|
description:
|
|
28
|
-
'The rows
|
|
74
|
+
'The grouped rows: DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, or DropdownMenuSubMenu.',
|
|
29
75
|
},
|
|
30
76
|
{
|
|
31
77
|
name: 'xstyle',
|
|
32
78
|
type: 'StyleXStyles',
|
|
33
79
|
description:
|
|
34
|
-
'StyleX styles applied to the group wrapper. Must be a stylex.create() value
|
|
80
|
+
'StyleX styles applied to the role="group" wrapper. Must be a stylex.create() value, not an inline style object.',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'className',
|
|
84
|
+
type: 'string',
|
|
85
|
+
description: 'CSS class name applied to the role="group" wrapper.',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'style',
|
|
89
|
+
type: 'React.CSSProperties',
|
|
90
|
+
description: 'Inline styles applied to the role="group" wrapper.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'ref',
|
|
94
|
+
type: 'React.Ref<HTMLDivElement>',
|
|
95
|
+
description: 'Ref forwarded to the role="group" wrapper.',
|
|
35
96
|
},
|
|
36
97
|
],
|
|
37
98
|
theming: {
|
|
@@ -39,15 +100,113 @@ export const docs = {
|
|
|
39
100
|
},
|
|
40
101
|
};
|
|
41
102
|
|
|
42
|
-
|
|
103
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
104
|
+
export const docsZh = {
|
|
43
105
|
name: 'DropdownMenuGroup',
|
|
44
|
-
|
|
106
|
+
subComponentOf: 'DropdownMenu',
|
|
45
107
|
displayName: 'Dropdown Menu Group',
|
|
108
|
+
isHiddenFromOverview: true,
|
|
109
|
+
description:
|
|
110
|
+
'复合菜单中的一组菜单行。可见标题通过 aria-labelledby 为 role="group" 命名;省略标题时该组没有名称。标题不是 menuitem,因此方向键和预输入会跳过它。标题内容应保持非交互;控件应放在 DropdownMenu 的 header 或 footer 中。',
|
|
111
|
+
usage: {
|
|
112
|
+
description:
|
|
113
|
+
'使用 DropdownMenuGroup 为相关的复合菜单行提供同一个可见名称和无障碍名称。只有当名称能帮助理解这些菜单行时才使用分组。',
|
|
114
|
+
bestPractices: [
|
|
115
|
+
{guidance: true, description: '使用简短且能说明菜单行关系的标题。'},
|
|
116
|
+
{
|
|
117
|
+
guidance: true,
|
|
118
|
+
description: '保持标题非交互,让所有操作都位于菜单的方向键顺序中。',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
guidance: false,
|
|
122
|
+
description:
|
|
123
|
+
'在标题中放置按钮、链接或输入控件;应改用 DropdownMenu 的 header 或 footer。',
|
|
124
|
+
},
|
|
125
|
+
{guidance: false, description: '对没有共同含义的菜单行进行分组。'},
|
|
126
|
+
],
|
|
127
|
+
anatomy: [
|
|
128
|
+
{
|
|
129
|
+
name: '组',
|
|
130
|
+
required: true,
|
|
131
|
+
description: '包含标题和菜单行的 role="group" 包装元素。',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: '标题',
|
|
135
|
+
required: false,
|
|
136
|
+
description: '用于命名分组的可见非交互标题内容。',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: '菜单行',
|
|
140
|
+
required: false,
|
|
141
|
+
description: '由 DropdownMenu 持有的菜单项、可选项、单选组或子菜单。',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
props: [
|
|
146
|
+
{
|
|
147
|
+
name: 'title',
|
|
148
|
+
type: 'ReactNode',
|
|
149
|
+
description:
|
|
150
|
+
'显示在菜单行上方并作为分组无障碍名称的非交互标题内容。省略时分组无名称;控件应放在 DropdownMenu 的 header 或 footer 中。',
|
|
151
|
+
},
|
|
152
|
+
{name: 'children', type: 'ReactNode', description: '该分组中的菜单行。'},
|
|
153
|
+
{
|
|
154
|
+
name: 'xstyle',
|
|
155
|
+
type: 'StyleXStyles',
|
|
156
|
+
description: '应用于 role="group" 包装元素的 StyleX 样式。',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'className',
|
|
160
|
+
type: 'string',
|
|
161
|
+
description: '应用于 role="group" 包装元素的 CSS 类名。',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'style',
|
|
165
|
+
type: 'React.CSSProperties',
|
|
166
|
+
description: '应用于 role="group" 包装元素的内联样式。',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: 'ref',
|
|
170
|
+
type: 'React.Ref<HTMLDivElement>',
|
|
171
|
+
description: '转发到 role="group" 包装元素的 ref。',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
theming: {
|
|
175
|
+
targets: [{className: 'astryx-dropdown-menu-section-heading'}],
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentTranslationDoc} */
|
|
180
|
+
export const docsDense = {
|
|
46
181
|
description:
|
|
47
|
-
'
|
|
182
|
+
'named role="group" of compound-menu rows; compound peer of the data API\'s {type: "section"}',
|
|
183
|
+
usage: {
|
|
184
|
+
description:
|
|
185
|
+
'Use to give related compound-menu rows one visible and programmatic name.',
|
|
186
|
+
bestPractices: [
|
|
187
|
+
{
|
|
188
|
+
guidance: true,
|
|
189
|
+
description: "Short title that explains the rows' relationship.",
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
guidance: true,
|
|
193
|
+
description:
|
|
194
|
+
'Non-interactive title keeps every action in menu arrow navigation.',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
guidance: false,
|
|
198
|
+
description:
|
|
199
|
+
'Controls in title. Put them in DropdownMenu header or footer instead.',
|
|
200
|
+
},
|
|
201
|
+
{guidance: false, description: 'Group unrelated rows.'},
|
|
202
|
+
],
|
|
203
|
+
},
|
|
48
204
|
propDescriptions: {
|
|
49
|
-
title: 'heading above the rows; names the group',
|
|
50
|
-
children: 'the
|
|
51
|
-
xstyle: 'StyleX styles
|
|
205
|
+
title: 'non-interactive heading above the rows; names the group',
|
|
206
|
+
children: 'the grouped menu rows',
|
|
207
|
+
xstyle: 'StyleX styles on the role="group" wrapper',
|
|
208
|
+
className: 'CSS class on the role="group" wrapper',
|
|
209
|
+
style: 'inline styles on the role="group" wrapper',
|
|
210
|
+
ref: 'ref forwarded to the role="group" wrapper',
|
|
52
211
|
},
|
|
53
212
|
};
|
|
@@ -53,7 +53,8 @@ export interface DropdownMenuGroupProps extends Pick<
|
|
|
53
53
|
> {
|
|
54
54
|
/**
|
|
55
55
|
* Heading shown above the group's rows and used as the group's accessible
|
|
56
|
-
* name (`aria-labelledby`). Omit it for an unnamed group.
|
|
56
|
+
* name (`aria-labelledby`). Omit it for an unnamed group. Keep this content
|
|
57
|
+
* non-interactive; controls belong in DropdownMenu's `header` or `footer`.
|
|
57
58
|
*/
|
|
58
59
|
title?: ReactNode;
|
|
59
60
|
/** The rows of the group: menu items, selectable items, sub-menus. */
|
|
@@ -65,9 +66,10 @@ export interface DropdownMenuGroupProps extends Pick<
|
|
|
65
66
|
/**
|
|
66
67
|
* A titled group of menu rows.
|
|
67
68
|
*
|
|
68
|
-
* Renders `role="group"` named by its heading. The heading is
|
|
69
|
-
*
|
|
70
|
-
*
|
|
69
|
+
* Renders `role="group"` named by its heading. The heading is not a
|
|
70
|
+
* `menuitem`, so arrow-key navigation and typeahead skip it. Keep heading
|
|
71
|
+
* content non-interactive; place controls in DropdownMenu's `header` or
|
|
72
|
+
* `footer`. The compound peer of
|
|
71
73
|
* `{type: 'section', title, items}` in the `items` data API.
|
|
72
74
|
*
|
|
73
75
|
* @example
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file DropdownMenuLazySheet.test.tsx
|
|
5
|
+
* @input Uses DropdownMenu, Testing Library, and a held module load
|
|
6
|
+
* @output Tests DropdownMenu while its on-demand touch sheet is still loading
|
|
7
|
+
* @position Test file for DropdownMenu
|
|
8
|
+
*
|
|
9
|
+
* This file never warms the sheet, and it checks that a popover menu never
|
|
10
|
+
* requests it: each test holds the sheet modules until it
|
|
11
|
+
* releases them, so it exercises the path a real page takes while the sheet's
|
|
12
|
+
* code is still downloading.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {describe, it, expect, vi, beforeAll, beforeEach} from 'vitest';
|
|
16
|
+
import {render, screen, waitFor, act} from '@testing-library/react';
|
|
17
|
+
import userEvent from '@testing-library/user-event';
|
|
18
|
+
import type {DropdownMenu as DropdownMenuComponent} from './DropdownMenu';
|
|
19
|
+
|
|
20
|
+
const sheetLoad = vi.hoisted(() => {
|
|
21
|
+
let release: () => void = () => {};
|
|
22
|
+
const gate = new Promise<void>(resolve => {
|
|
23
|
+
release = resolve;
|
|
24
|
+
});
|
|
25
|
+
return {gate, release: () => release(), requested: [] as string[]};
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
vi.mock('./MenuBottomSheet', async importOriginal => {
|
|
29
|
+
sheetLoad.requested.push('MenuBottomSheet');
|
|
30
|
+
await sheetLoad.gate;
|
|
31
|
+
return importOriginal();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
vi.mock('./MenuBottomSheetActionList', async importOriginal => {
|
|
35
|
+
sheetLoad.requested.push('MenuBottomSheetActionList');
|
|
36
|
+
await sheetLoad.gate;
|
|
37
|
+
return importOriginal();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let DropdownMenu: typeof DropdownMenuComponent;
|
|
41
|
+
|
|
42
|
+
// If any module the dropdown loads imported the sheet eagerly, this import
|
|
43
|
+
// would wait on the held sheet and the hook would time out.
|
|
44
|
+
beforeAll(async () => {
|
|
45
|
+
({DropdownMenu} = await import('./DropdownMenu'));
|
|
46
|
+
}, 10000);
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
HTMLDialogElement.prototype.showModal = vi.fn(function (
|
|
50
|
+
this: HTMLDialogElement,
|
|
51
|
+
) {
|
|
52
|
+
this.setAttribute('open', '');
|
|
53
|
+
});
|
|
54
|
+
HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
|
|
55
|
+
this.removeAttribute('open');
|
|
56
|
+
});
|
|
57
|
+
vi.stubGlobal(
|
|
58
|
+
'matchMedia',
|
|
59
|
+
vi.fn().mockImplementation((query: string) => ({
|
|
60
|
+
matches: false,
|
|
61
|
+
media: query,
|
|
62
|
+
onchange: null,
|
|
63
|
+
addEventListener: vi.fn(),
|
|
64
|
+
removeEventListener: vi.fn(),
|
|
65
|
+
addListener: vi.fn(),
|
|
66
|
+
removeListener: vi.fn(),
|
|
67
|
+
dispatchEvent: vi.fn(),
|
|
68
|
+
})),
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('DropdownMenu touch sheet loaded on demand', () => {
|
|
73
|
+
// Runs first: nothing in this file has rendered a sheet yet.
|
|
74
|
+
it('never loads the sheet for a popover menu', async () => {
|
|
75
|
+
const user = userEvent.setup();
|
|
76
|
+
HTMLElement.prototype.showPopover = vi.fn();
|
|
77
|
+
HTMLElement.prototype.hidePopover = vi.fn();
|
|
78
|
+
render(
|
|
79
|
+
<DropdownMenu
|
|
80
|
+
button={{label: 'Actions'}}
|
|
81
|
+
items={[{label: 'Edit project'}, {label: 'Delete'}]}
|
|
82
|
+
/>,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
await user.click(screen.getByRole('button', {name: /Actions/}));
|
|
86
|
+
await act(async () => {
|
|
87
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Importing DropdownMenu and opening a popover menu requested neither
|
|
91
|
+
// sheet module, so no other module the dropdown loads imports it eagerly.
|
|
92
|
+
expect(sheetLoad.requested).toEqual([]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('focuses the first action when a keyboard open beats the sheet load', async () => {
|
|
96
|
+
const user = userEvent.setup();
|
|
97
|
+
render(
|
|
98
|
+
<DropdownMenu
|
|
99
|
+
button={{label: 'Actions'}}
|
|
100
|
+
presentation="bottom-sheet"
|
|
101
|
+
items={[{label: 'Edit project'}, {label: 'Delete'}]}
|
|
102
|
+
/>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const trigger = screen.getByRole('button', {name: /Actions/});
|
|
106
|
+
trigger.focus();
|
|
107
|
+
await user.keyboard('{Enter}');
|
|
108
|
+
// The open has committed, but the sheet's code has not arrived yet.
|
|
109
|
+
await act(async () => {
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
111
|
+
});
|
|
112
|
+
expect(screen.queryByRole('button', {name: 'Edit project'})).toBeNull();
|
|
113
|
+
|
|
114
|
+
await act(async () => {
|
|
115
|
+
sheetLoad.release();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await waitFor(
|
|
119
|
+
() =>
|
|
120
|
+
expect(
|
|
121
|
+
screen.getByRole('button', {name: 'Edit project'}),
|
|
122
|
+
).toHaveFocus(),
|
|
123
|
+
{timeout: 15000},
|
|
124
|
+
);
|
|
125
|
+
}, 20000);
|
|
126
|
+
});
|
|
@@ -43,7 +43,7 @@ export const docs = {
|
|
|
43
43
|
name: 'icon',
|
|
44
44
|
type: 'IconType',
|
|
45
45
|
description:
|
|
46
|
-
'Icon to display before the label. See `
|
|
46
|
+
'Icon to display before the label. See `astryx docs icons` for valid semantic names.',
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
name: 'isDisabled',
|
|
@@ -93,7 +93,7 @@ export interface DropdownMenuRadioItemProps extends Omit<
|
|
|
93
93
|
description?: ReactNode;
|
|
94
94
|
/**
|
|
95
95
|
* Icon to display before the label. Accepts a semantic icon name (see
|
|
96
|
-
* `
|
|
96
|
+
* `astryx docs icons`) or a rendered node.
|
|
97
97
|
*/
|
|
98
98
|
icon?: ReactNode | IconType;
|
|
99
99
|
/**
|