@astryxdesign/core 0.7.1 → 0.7.2-canary.020e1fe
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/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/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +5 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +38 -14
- 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/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/Drawer/Drawer.d.ts +191 -0
- package/dist/Drawer/Drawer.d.ts.map +1 -0
- package/dist/Drawer/Drawer.js +382 -0
- package/dist/Drawer/DrawerHeader.d.ts +85 -0
- package/dist/Drawer/DrawerHeader.d.ts.map +1 -0
- package/dist/Drawer/DrawerHeader.js +134 -0
- package/dist/Drawer/index.d.ts +11 -0
- package/dist/Drawer/index.d.ts.map +1 -0
- package/dist/Drawer/index.js +11 -0
- package/dist/Drawer/useDrawerDialogPresence.d.ts +42 -0
- package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -0
- package/dist/Drawer/useDrawerDialogPresence.js +236 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +83 -11
- 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/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +19 -8
- 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/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/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/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +10 -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/Layer/layerViewportInset.stylex.d.ts +6 -2
- package/dist/Layer/layerViewportInset.stylex.d.ts.map +1 -1
- package/dist/Layer/layerViewportInset.stylex.js +6 -2
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +13 -7
- package/dist/Layer/useLayerDismissal.d.ts +1 -1
- package/dist/Layer/useLayerDismissal.js +1 -1
- package/dist/Layout/container.stylex.d.ts +5 -5
- package/dist/Layout/container.stylex.js +4 -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/plugins/frontmatter.d.ts.map +1 -1
- package/dist/Markdown/plugins/frontmatter.js +7 -3
- 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/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +22 -8
- package/dist/MultiSelector/OptionActions.a11y.chromium.spec.js +100 -2
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +3 -1
- package/dist/Pagination/Pagination.d.ts +8 -5
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +21 -8
- package/dist/Popover/Popover.js +2 -2
- package/dist/PowerSearch/PowerSearch.js +1 -1
- 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/Selector/hooks.d.ts.map +1 -1
- package/dist/Selector/hooks.js +6 -2
- package/dist/SideNav/SideNavItem.d.ts +8 -1
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +71 -9
- package/dist/Switch/Switch.d.ts +1 -1
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +13 -1
- package/dist/TabList/TabMenu.d.ts +2 -2
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +6 -3
- package/dist/Text/Text.d.ts.map +1 -1
- package/dist/Text/Text.js +4 -2
- package/dist/Text/useTruncation.d.ts +7 -5
- package/dist/Text/useTruncation.d.ts.map +1 -1
- package/dist/Text/useTruncation.js +16 -5
- 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/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +11 -0
- package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +3 -3
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +3 -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/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +26 -0
- package/dist/astryx.css +47 -13
- package/dist/hooks/useEntryAnimation.d.ts +2 -1
- package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
- package/dist/hooks/useEntryAnimation.js +14 -12
- package/dist/hooks/useIsHydrating.d.ts +6 -0
- package/dist/hooks/useIsHydrating.d.ts.map +1 -0
- package/dist/hooks/useIsHydrating.js +30 -0
- package/dist/hooks/useMenuHover.d.ts.map +1 -1
- package/dist/hooks/useMenuHover.js +18 -13
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +112 -1
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +112 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +0 -3
- package/dist/utils/focusOutline.stylex.d.ts +4 -0
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.js +4 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +15 -9
- package/dist/utils/sharedMutationObserver.d.ts +21 -0
- package/dist/utils/sharedMutationObserver.d.ts.map +1 -0
- package/dist/utils/sharedMutationObserver.js +69 -0
- package/locales/en.json +444 -0
- package/locales/pseudo.json +333 -0
- package/package.json +9 -4
- package/scripts/agent-doc-state.mjs +38 -0
- package/scripts/postinstall.mjs +1 -1
- 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/AvatarGroup/AvatarGroup.doc.mjs +3 -2
- package/src/Breadcrumbs/BreadcrumbItem.tsx +4 -1
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +30 -1
- package/src/Button/Button.doc.mjs +2 -2
- package/src/Button/Button.test.tsx +155 -9
- package/src/Button/Button.tsx +39 -16
- package/src/Button/__tests__/Button.a11y.known-failures.ts +1 -62
- package/src/Button/__tests__/Button.a11y.states.ts +0 -10
- package/src/Card/Card.doc.mjs +1 -1
- 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.test.tsx +22 -0
- 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.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 +473 -0
- package/src/Drawer/Drawer.spec.md +357 -0
- package/src/Drawer/Drawer.test.tsx +1300 -0
- package/src/Drawer/Drawer.tsx +613 -0
- package/src/Drawer/DrawerDirection.test.tsx +127 -0
- package/src/Drawer/DrawerHeader.doc.mjs +293 -0
- package/src/Drawer/DrawerHeader.test.tsx +102 -0
- package/src/Drawer/DrawerHeader.tsx +235 -0
- package/src/Drawer/__tests__/DrawerAudit.a11y.chromium.spec.ts +401 -0
- package/src/Drawer/__tests__/DrawerHeaderAudit.a11y.chromium.spec.ts +297 -0
- package/src/Drawer/index.ts +13 -0
- package/src/Drawer/useDrawerDialogPresence.ts +284 -0
- package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +57 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +79 -18
- package/src/DropdownMenu/DropdownMenu.tsx +83 -12
- 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/DropdownMenuSubMenu.test.tsx +185 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +19 -5
- 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__/Menu.a11y.chromium.spec.ts +145 -0
- package/src/DropdownMenu/__tests__/Menu.a11y.known-failures.ts +26 -0
- package/src/DropdownMenu/__tests__/Menu.a11y.renders.tsx +65 -0
- package/src/DropdownMenu/__tests__/Menu.a11y.states.ts +138 -0
- package/src/DropdownMenu/__tests__/Menu.a11y.test.tsx +136 -0
- package/src/DropdownMenu/__tests__/warmMenuBottomSheet.tsx +43 -0
- package/src/DropdownMenu/lazyMenuBottomSheet.ts +71 -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/Grid/GridSpan.tsx +3 -0
- package/src/Heading/Heading.test.tsx +57 -0
- package/src/Heading/Heading.tsx +6 -2
- package/src/HoverCard/HoverCard.test.tsx +58 -0
- package/src/HoverCard/useHoverCard.tsx +10 -2
- package/src/Icon/Icon.doc.mjs +36 -0
- package/src/IconButton/IconButton.doc.mjs +2 -2
- package/src/IconButton/IconButton.test.tsx +3 -1
- package/src/Item/Item.doc.mjs +12 -8
- package/src/Item/Item.spec.md +226 -0
- package/src/Item/Item.test.tsx +61 -0
- package/src/Item/Item.tsx +40 -4
- package/src/Layer/layerViewportInset.source-build.test.mjs +90 -0
- package/src/Layer/layerViewportInset.stylex.ts +6 -2
- package/src/Layer/layerViewportInset.test.ts +1 -0
- package/src/Layer/useLayer.tsx +24 -13
- package/src/Layer/useLayerDismissal.ts +1 -1
- package/src/Layout/LayoutHeader.doc.mjs +5 -0
- package/src/Layout/container.stylex.ts +6 -6
- 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 +24 -5
- package/src/List/ListItem.tsx +56 -27
- package/src/List/index.ts +1 -1
- package/src/Markdown/Markdown.tsx +14 -1
- package/src/Markdown/plugins/frontmatter.test.tsx +25 -0
- package/src/Markdown/plugins/frontmatter.ts +7 -3
- 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/MultiSelector/MultiSelector.test.tsx +159 -0
- package/src/MultiSelector/MultiSelector.tsx +40 -7
- package/src/MultiSelector/OptionActions.a11y.chromium.spec.ts +112 -2
- package/src/MultiSelector/hooks.ts +3 -1
- package/src/MultiSelector/useMultiCombobox.doc.mjs +267 -0
- package/src/Pagination/Pagination.doc.mjs +7 -7
- package/src/Pagination/Pagination.spec.md +9 -9
- package/src/Pagination/Pagination.test.tsx +242 -0
- package/src/Pagination/Pagination.tsx +27 -11
- package/src/Popover/Popover.test.tsx +2 -2
- package/src/Popover/Popover.tsx +4 -4
- package/src/PowerSearch/PowerSearch.test.tsx +85 -0
- package/src/PowerSearch/PowerSearch.tsx +2 -2
- 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/Selector/hooks.ts +6 -2
- package/src/Selector/useCombobox.doc.mjs +237 -0
- package/src/SideNav/SideNav.spec.md +227 -0
- package/src/SideNav/SideNav.test.tsx +179 -0
- package/src/SideNav/SideNavItem.doc.mjs +2 -2
- package/src/SideNav/SideNavItem.tsx +96 -17
- package/src/Slider/Slider.spec.md +39 -18
- package/src/Spinner/Spinner.doc.mjs +1 -1
- package/src/Switch/Switch.test.tsx +49 -0
- package/src/Switch/Switch.tsx +11 -0
- package/src/TabList/TabList.spec.md +57 -44
- package/src/TabList/TabList.test.tsx +67 -0
- package/src/TabList/TabMenu.doc.mjs +4 -3
- package/src/TabList/TabMenu.tsx +7 -4
- package/src/Text/Text.test.tsx +57 -0
- package/src/Text/Text.tsx +6 -2
- package/src/Text/useTruncation.test.tsx +69 -0
- package/src/Text/useTruncation.ts +16 -5
- 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/Tokenizer/Tokenizer.test.tsx +54 -1
- 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/Tooltip/Tooltip.test.tsx +31 -0
- package/src/Tooltip/useTooltip.test.tsx +13 -0
- package/src/Tooltip/useTooltip.tsx +11 -0
- package/src/TopNav/TopNavMegaMenu.test.tsx +22 -0
- package/src/TopNav/TopNavMegaMenu.tsx +7 -3
- package/src/TopNav/TopNavMenu.test.tsx +19 -0
- package/src/TopNav/TopNavMenu.tsx +11 -3
- package/src/TreeList/TreeList.doc.mjs +1 -0
- package/src/TreeList/TreeList.spec.md +29 -17
- 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/Typeahead/BaseTypeahead.tsx +26 -0
- package/src/Typeahead/Typeahead.test.tsx +260 -2
- package/src/docPropLiterals.test.ts +36 -10
- package/src/hooks/useEntryAnimation.doc.mjs +4 -4
- package/src/hooks/useEntryAnimation.ssr.test.tsx +143 -0
- package/src/hooks/useEntryAnimation.ts +15 -16
- package/src/hooks/useIsHydrating.test.tsx +70 -0
- package/src/hooks/useIsHydrating.ts +31 -0
- package/src/hooks/useMenuHover.ts +18 -13
- package/src/i18n/InternationalizationProvider.doc.mjs +8 -0
- package/src/i18n/InternationalizationProvider.spec.md +252 -0
- package/src/i18n/__tests__/InternationalizationProvider.test.tsx +128 -0
- package/src/i18n/generated-locales/en.generated.ts +121 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +121 -0
- package/src/i18n/useTranslator.test.tsx +37 -0
- package/src/index.ts +1 -0
- package/src/postinstall.test.mjs +46 -0
- package/src/reset.css +0 -29
- package/src/theme/derivedVarRegistry.test.ts +7 -3
- package/src/theme/derivedVarRegistry.ts +0 -3
- package/src/utils/focusOutline.stylex.ts +4 -0
- package/src/utils/plainDate.test.ts +37 -1
- package/src/utils/plainDate.ts +15 -5
- package/src/utils/sharedMutationObserver.test.ts +53 -0
- package/src/utils/sharedMutationObserver.ts +78 -0
|
@@ -497,6 +497,78 @@ describe('ToggleButton', () => {
|
|
|
497
497
|
expect(button).not.toHaveAttribute('aria-busy', 'true');
|
|
498
498
|
await act(async () => {});
|
|
499
499
|
});
|
|
500
|
+
|
|
501
|
+
// family:buttons FR5: a caller onClick on a standalone ToggleButton runs
|
|
502
|
+
// before onPressedChange and pressedChangeAction, never replaces the toggle
|
|
503
|
+
// request, and cancels it only by calling preventDefault() itself.
|
|
504
|
+
describe('with a caller onClick', () => {
|
|
505
|
+
it('runs the caller onClick first, then requests the next state and runs the Action', async () => {
|
|
506
|
+
const calls: string[] = [];
|
|
507
|
+
render(
|
|
508
|
+
<ToggleButton
|
|
509
|
+
label="Compact"
|
|
510
|
+
isPressed={false}
|
|
511
|
+
onClick={() => calls.push('onClick')}
|
|
512
|
+
onPressedChange={next => calls.push(`onPressedChange:${next}`)}
|
|
513
|
+
pressedChangeAction={next => {
|
|
514
|
+
calls.push(`pressedChangeAction:${next}`);
|
|
515
|
+
}}
|
|
516
|
+
/>,
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
fireEvent.click(screen.getByRole('button', {name: 'Compact'}));
|
|
520
|
+
await act(async () => {});
|
|
521
|
+
|
|
522
|
+
expect(calls).toEqual([
|
|
523
|
+
'onClick',
|
|
524
|
+
'onPressedChange:true',
|
|
525
|
+
'pressedChangeAction:true',
|
|
526
|
+
]);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it('requests no next state when the caller onClick calls preventDefault', async () => {
|
|
530
|
+
const onPressedChange = vi.fn();
|
|
531
|
+
const pressedChangeAction = vi.fn();
|
|
532
|
+
render(
|
|
533
|
+
<ToggleButton
|
|
534
|
+
label="Compact"
|
|
535
|
+
isPressed={false}
|
|
536
|
+
onClick={event => event.preventDefault()}
|
|
537
|
+
onPressedChange={onPressedChange}
|
|
538
|
+
pressedChangeAction={pressedChangeAction}
|
|
539
|
+
/>,
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
const button = screen.getByRole('button', {name: 'Compact'});
|
|
543
|
+
fireEvent.click(button);
|
|
544
|
+
await act(async () => {});
|
|
545
|
+
|
|
546
|
+
expect(onPressedChange).not.toHaveBeenCalled();
|
|
547
|
+
expect(pressedChangeAction).not.toHaveBeenCalled();
|
|
548
|
+
expect(button).toHaveAttribute('aria-pressed', 'false');
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('still requests the next state when the click was already prevented before the caller onClick ran', () => {
|
|
552
|
+
const onPressedChange = vi.fn();
|
|
553
|
+
render(
|
|
554
|
+
<div onClickCapture={event => event.preventDefault()}>
|
|
555
|
+
<ToggleButton
|
|
556
|
+
label="Compact"
|
|
557
|
+
isPressed={false}
|
|
558
|
+
onClick={() => {}}
|
|
559
|
+
onPressedChange={onPressedChange}
|
|
560
|
+
/>
|
|
561
|
+
</div>,
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
fireEvent.click(screen.getByRole('button', {name: 'Compact'}));
|
|
565
|
+
|
|
566
|
+
expect(onPressedChange).toHaveBeenCalledExactlyOnceWith(
|
|
567
|
+
true,
|
|
568
|
+
expect.anything(),
|
|
569
|
+
);
|
|
570
|
+
});
|
|
571
|
+
});
|
|
500
572
|
});
|
|
501
573
|
|
|
502
574
|
// =============================================================================
|
|
@@ -721,6 +793,29 @@ describe.each(['single', 'multiple'] as const)(
|
|
|
721
793
|
expect(onPressedChange).not.toHaveBeenCalled();
|
|
722
794
|
expect(pressedChangeAction).not.toHaveBeenCalled();
|
|
723
795
|
});
|
|
796
|
+
|
|
797
|
+
// family:buttons FR11: a member's selection request comes from the group,
|
|
798
|
+
// and a caller onClick on the member must not replace it.
|
|
799
|
+
it('still asks the group to toggle a member that receives an onClick', async () => {
|
|
800
|
+
const onChange = vi.fn();
|
|
801
|
+
const onClick = vi.fn();
|
|
802
|
+
const groupProps =
|
|
803
|
+
type === 'single'
|
|
804
|
+
? {type, value: null, onChange}
|
|
805
|
+
: {type, value: [], onChange};
|
|
806
|
+
render(
|
|
807
|
+
<ToggleButtonGroup {...groupProps} label="View">
|
|
808
|
+
<ToggleButton value="grid" label="Grid" onClick={onClick} />
|
|
809
|
+
</ToggleButtonGroup>,
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
await userEvent.setup().click(screen.getByRole('button', {name: 'Grid'}));
|
|
813
|
+
|
|
814
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
815
|
+
expect(onChange).toHaveBeenCalledExactlyOnceWith(
|
|
816
|
+
type === 'single' ? 'grid' : ['grid'],
|
|
817
|
+
);
|
|
818
|
+
});
|
|
724
819
|
},
|
|
725
820
|
);
|
|
726
821
|
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* @position Thin wrapper over Button; adds controlled toggle pattern
|
|
10
10
|
*
|
|
11
11
|
* ToggleButton wraps Button with `isPressed` and adds:
|
|
12
|
-
* - `onPressedChange` from internal `onClick
|
|
12
|
+
* - `onPressedChange` from internal `onClick`, after any caller `onClick`;
|
|
13
|
+
* `pressedChangeAction` via Button
|
|
13
14
|
* - `pressedIcon` for outline-to-filled icon swap
|
|
14
15
|
* - Font weight shift on press with width reservation to prevent layout shift
|
|
15
16
|
* - Group integration via ToggleButtonGroupContext
|
|
@@ -258,6 +259,7 @@ export function ToggleButton({
|
|
|
258
259
|
xstyle,
|
|
259
260
|
className,
|
|
260
261
|
style,
|
|
262
|
+
onClick: onClickProp,
|
|
261
263
|
...props
|
|
262
264
|
}: ToggleButtonProps): ReactNode {
|
|
263
265
|
const group = useToggleButtonGroup();
|
|
@@ -287,11 +289,20 @@ export function ToggleButton({
|
|
|
287
289
|
if (isDisabled) {
|
|
288
290
|
return;
|
|
289
291
|
}
|
|
292
|
+
// A caller onClick (a trigger wrapper often supplies one) runs first and
|
|
293
|
+
// never replaces the toggle's own request (family:buttons FR5, FR11).
|
|
294
|
+
const wasPrevented = event.defaultPrevented;
|
|
295
|
+
onClickProp?.(event);
|
|
290
296
|
if (group && value != null) {
|
|
291
297
|
group.toggle(value);
|
|
292
298
|
event.preventDefault();
|
|
293
299
|
return;
|
|
294
300
|
}
|
|
301
|
+
// A standalone toggle is cancelled only by the caller's own
|
|
302
|
+
// preventDefault(); Button then skips pressedChangeAction too.
|
|
303
|
+
if (!wasPrevented && event.defaultPrevented) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
295
306
|
|
|
296
307
|
onPressedChangeProp?.(nextPressed, event);
|
|
297
308
|
};
|
|
@@ -6,6 +6,10 @@ export const docs = {
|
|
|
6
6
|
name: 'ToggleButtonGroup',
|
|
7
7
|
subComponentOf: 'ToggleButton',
|
|
8
8
|
displayName: 'Toggle Button Group',
|
|
9
|
+
keywords: [
|
|
10
|
+
'togglegroup',
|
|
11
|
+
'buttontogglegroup',
|
|
12
|
+
],
|
|
9
13
|
description: 'Groups toggle buttons for exclusive (single) or multi-select behavior. Uses discriminated union on type for type-safe value/onChange.',
|
|
10
14
|
props: [
|
|
11
15
|
{
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
afterEach,
|
|
21
21
|
} from 'vitest';
|
|
22
22
|
import {render, screen, fireEvent, act, waitFor} from '@testing-library/react';
|
|
23
|
-
import {createRef, Profiler} from 'react';
|
|
23
|
+
import {createRef, Profiler, useState} from 'react';
|
|
24
24
|
import userEvent from '@testing-library/user-event';
|
|
25
25
|
import {Tokenizer} from './Tokenizer';
|
|
26
26
|
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
@@ -1790,6 +1790,59 @@ describe('Tokenizer end-lane reserve', () => {
|
|
|
1790
1790
|
expect(renderedTokens).toEqual([]);
|
|
1791
1791
|
});
|
|
1792
1792
|
|
|
1793
|
+
it('clears the busy spinner when a token is added while a bootstrap load is in flight', async () => {
|
|
1794
|
+
let settle: (items: SearchableItem[]) => void = () => {};
|
|
1795
|
+
const bootstrap = vi.fn(
|
|
1796
|
+
async () =>
|
|
1797
|
+
new Promise<SearchableItem[]>(resolve => {
|
|
1798
|
+
settle = resolve;
|
|
1799
|
+
}),
|
|
1800
|
+
);
|
|
1801
|
+
const source: SearchSource = {search: () => [], bootstrap};
|
|
1802
|
+
|
|
1803
|
+
function Harness() {
|
|
1804
|
+
const [value, setValue] = useState<SearchableItem[]>([]);
|
|
1805
|
+
return (
|
|
1806
|
+
<>
|
|
1807
|
+
<Tokenizer
|
|
1808
|
+
label="Items"
|
|
1809
|
+
searchSource={source}
|
|
1810
|
+
value={value}
|
|
1811
|
+
onChange={setValue}
|
|
1812
|
+
hasEntriesOnFocus
|
|
1813
|
+
/>
|
|
1814
|
+
<button type="button" onClick={() => setValue([users[0]])}>
|
|
1815
|
+
Add Token
|
|
1816
|
+
</button>
|
|
1817
|
+
</>
|
|
1818
|
+
);
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
render(<Harness />);
|
|
1822
|
+
const input = screen.getByRole('combobox');
|
|
1823
|
+
|
|
1824
|
+
fireEvent.focus(input);
|
|
1825
|
+
await waitFor(() => {
|
|
1826
|
+
expect(screen.getByRole('status', {name: 'Loading'})).toBeInTheDocument();
|
|
1827
|
+
});
|
|
1828
|
+
|
|
1829
|
+
fireEvent.click(screen.getByRole('button', {name: 'Add Token'}));
|
|
1830
|
+
|
|
1831
|
+
await waitFor(() => {
|
|
1832
|
+
expect(
|
|
1833
|
+
screen.queryByRole('status', {name: 'Loading'}),
|
|
1834
|
+
).not.toBeInTheDocument();
|
|
1835
|
+
});
|
|
1836
|
+
|
|
1837
|
+
await act(async () => {
|
|
1838
|
+
settle([]);
|
|
1839
|
+
await Promise.resolve();
|
|
1840
|
+
});
|
|
1841
|
+
expect(
|
|
1842
|
+
screen.queryByRole('status', {name: 'Loading'}),
|
|
1843
|
+
).not.toBeInTheDocument();
|
|
1844
|
+
});
|
|
1845
|
+
|
|
1793
1846
|
it('shares one observer across every field on the page', () => {
|
|
1794
1847
|
// Browsers batch per observer instance, so one observer per lane meant N
|
|
1795
1848
|
// callback dispatches a frame for N fields.
|
|
@@ -140,14 +140,6 @@ describe('Toolbar', () => {
|
|
|
140
140
|
);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
it('sets aria-orientation to vertical', () => {
|
|
144
|
-
render(<Toolbar label="Actions" orientation="vertical" />);
|
|
145
|
-
expect(screen.getByRole('toolbar')).toHaveAttribute(
|
|
146
|
-
'aria-orientation',
|
|
147
|
-
'vertical',
|
|
148
|
-
);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
143
|
it('reflects the size', () => {
|
|
152
144
|
render(<Toolbar label="Actions" size="sm" />);
|
|
153
145
|
const toolbar = screen.getByRole('toolbar');
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Toolbar.a11y.chromium.spec.ts
|
|
5
|
+
* @input Built toolbar stories and TOOLBAR_PATTERN in real Chromium
|
|
6
|
+
* @output Browser evidence for every toolbar expectation across Toolbar's representative states, and a paint check that each state's controls sit on the axis and in the direction it declares
|
|
7
|
+
* @position Chromium lane of the Toolbar binding
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
expect,
|
|
12
|
+
test,
|
|
13
|
+
type CDPSession,
|
|
14
|
+
type Locator,
|
|
15
|
+
type Page,
|
|
16
|
+
} from '@playwright/test';
|
|
17
|
+
import {
|
|
18
|
+
TOOLBAR_PATTERN,
|
|
19
|
+
blockingResults,
|
|
20
|
+
checkAccessibilitySpec,
|
|
21
|
+
formatFailures,
|
|
22
|
+
neverExercised,
|
|
23
|
+
summarize,
|
|
24
|
+
type BindingResult,
|
|
25
|
+
} from '@astryxdesign/a11y-spec';
|
|
26
|
+
import {
|
|
27
|
+
createChromiumHarness,
|
|
28
|
+
holdMotionStill,
|
|
29
|
+
} from '@astryxdesign/a11y-spec/chromium';
|
|
30
|
+
import {
|
|
31
|
+
DEFAULT_STORYBOOK_DIR,
|
|
32
|
+
serveStorybook,
|
|
33
|
+
type StaticServer,
|
|
34
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
35
|
+
import {
|
|
36
|
+
TOOLBAR_BINDING_STATES,
|
|
37
|
+
type ToolbarBindingState,
|
|
38
|
+
} from './Toolbar.a11y.states';
|
|
39
|
+
|
|
40
|
+
let storybook: StaticServer;
|
|
41
|
+
|
|
42
|
+
test.beforeAll(async () => {
|
|
43
|
+
storybook = await serveStorybook(
|
|
44
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test.afterAll(async () => {
|
|
49
|
+
await storybook?.close();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function storyUrl(storyId: string): string {
|
|
53
|
+
return `${storybook.origin}/iframe.html?id=${storyId}&viewMode=story`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function mountState(
|
|
57
|
+
page: Page,
|
|
58
|
+
state: ToolbarBindingState,
|
|
59
|
+
): Promise<{subject: Locator; related: Record<string, Locator>}> {
|
|
60
|
+
await page.goto(storyUrl(state.storyId), {waitUntil: 'load'});
|
|
61
|
+
await holdMotionStill(page);
|
|
62
|
+
const root = page.locator('#storybook-root');
|
|
63
|
+
const subject = root.getByRole('toolbar', {name: state.label, exact: true});
|
|
64
|
+
await expect(subject).toBeVisible();
|
|
65
|
+
const related: Record<string, Locator> = {
|
|
66
|
+
before: root.getByRole('button', {
|
|
67
|
+
name: 'Before the toolbar',
|
|
68
|
+
exact: true,
|
|
69
|
+
}),
|
|
70
|
+
after: root.getByRole('button', {name: 'After the toolbar', exact: true}),
|
|
71
|
+
};
|
|
72
|
+
for (const [relation, name] of Object.entries(state.controls)) {
|
|
73
|
+
related[relation] = subject.getByRole('button', {name, exact: true});
|
|
74
|
+
}
|
|
75
|
+
return {subject, related};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function runState(
|
|
79
|
+
page: Page,
|
|
80
|
+
cdp: CDPSession,
|
|
81
|
+
state: ToolbarBindingState,
|
|
82
|
+
): Promise<BindingResult> {
|
|
83
|
+
return checkAccessibilitySpec({
|
|
84
|
+
spec: TOOLBAR_PATTERN,
|
|
85
|
+
binding: 'Toolbar',
|
|
86
|
+
state: state.id,
|
|
87
|
+
facts: state.facts,
|
|
88
|
+
mount: async () => {
|
|
89
|
+
const {subject, related} = await mountState(page, state);
|
|
90
|
+
return createChromiumHarness({page, cdp, subject, related});
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
test('every toolbar expectation is exercised by a Toolbar state', async ({
|
|
96
|
+
page,
|
|
97
|
+
}) => {
|
|
98
|
+
const cdp = await page.context().newCDPSession(page);
|
|
99
|
+
const results: BindingResult[] = [];
|
|
100
|
+
for (const state of TOOLBAR_BINDING_STATES) {
|
|
101
|
+
results.push(await runState(page, cdp, state));
|
|
102
|
+
}
|
|
103
|
+
expect(neverExercised(results)).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The contract reads `orientation` and `direction` as facts the binding
|
|
108
|
+
* declares. This checks the painted page agrees: the second control sits
|
|
109
|
+
* below the first in a vertical state, and after it in reading order — to
|
|
110
|
+
* its right, or its left under right-to-left — in a horizontal one.
|
|
111
|
+
*/
|
|
112
|
+
for (const state of TOOLBAR_BINDING_STATES) {
|
|
113
|
+
test(`Toolbar [${state.id}] paints its controls on the declared axis and direction`, async ({
|
|
114
|
+
page,
|
|
115
|
+
}) => {
|
|
116
|
+
const {related} = await mountState(page, state);
|
|
117
|
+
const first = await related['first-control'].boundingBox();
|
|
118
|
+
const second = await related['second-control'].boundingBox();
|
|
119
|
+
if (first == null || second == null) {
|
|
120
|
+
throw new Error(`state "${state.id}" has an unpainted control`);
|
|
121
|
+
}
|
|
122
|
+
const firstMiddle = {
|
|
123
|
+
x: first.x + first.width / 2,
|
|
124
|
+
y: first.y + first.height / 2,
|
|
125
|
+
};
|
|
126
|
+
const secondMiddle = {
|
|
127
|
+
x: second.x + second.width / 2,
|
|
128
|
+
y: second.y + second.height / 2,
|
|
129
|
+
};
|
|
130
|
+
if (state.facts.orientation === 'vertical') {
|
|
131
|
+
expect(second.y, 'second control below the first').toBeGreaterThanOrEqual(
|
|
132
|
+
first.y + first.height,
|
|
133
|
+
);
|
|
134
|
+
} else {
|
|
135
|
+
expect(
|
|
136
|
+
Math.abs(secondMiddle.y - firstMiddle.y),
|
|
137
|
+
'controls share a row',
|
|
138
|
+
).toBeLessThan(first.height / 2);
|
|
139
|
+
if (state.facts.direction === 'rtl') {
|
|
140
|
+
expect(secondMiddle.x, 'second control left of the first').toBeLessThan(
|
|
141
|
+
firstMiddle.x,
|
|
142
|
+
);
|
|
143
|
+
} else {
|
|
144
|
+
expect(
|
|
145
|
+
secondMiddle.x,
|
|
146
|
+
'second control right of the first',
|
|
147
|
+
).toBeGreaterThan(firstMiddle.x);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for (const state of TOOLBAR_BINDING_STATES) {
|
|
154
|
+
test(`Toolbar [${state.id}] — ${state.summary}`, async ({page}) => {
|
|
155
|
+
const cdp = await page.context().newCDPSession(page);
|
|
156
|
+
const result = await runState(page, cdp, state);
|
|
157
|
+
const report = summarize(TOOLBAR_PATTERN, [result]);
|
|
158
|
+
// Required rows block (AST-020 FR9).
|
|
159
|
+
expect(formatFailures(blockingResults([result]))).toBe('');
|
|
160
|
+
expect(report.unrunLayers).toEqual([]);
|
|
161
|
+
expect(report.counts.unexpectedPass).toBe(0);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file Toolbar.a11y.renders.tsx
|
|
7
|
+
* @input One toolbar binding state from Toolbar.a11y.states.ts
|
|
8
|
+
* @output The React tree every lane renders for that state (the jsdom test directly, the story through Storybook): a page button before the Toolbar, the Toolbar, and a page button after it
|
|
9
|
+
* @position Shared fixture for the Toolbar binding
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {ReactElement} from 'react';
|
|
13
|
+
import {Button} from '../../Button/Button';
|
|
14
|
+
import {VStack} from '../../VStack/VStack';
|
|
15
|
+
import {Toolbar} from '../Toolbar';
|
|
16
|
+
import type {ToolbarBindingState, ToolbarControl} from './Toolbar.a11y.states';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One slot's controls. A slot lays its children out in a row, so a vertical
|
|
20
|
+
* toolbar's caller stacks them, as a product toolbar would, to arrange the
|
|
21
|
+
* controls on the axis the state declares.
|
|
22
|
+
*/
|
|
23
|
+
function controls(
|
|
24
|
+
list: ReadonlyArray<ToolbarControl>,
|
|
25
|
+
orientation: ToolbarBindingState['facts']['orientation'],
|
|
26
|
+
): ReactElement | undefined {
|
|
27
|
+
if (list.length === 0) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const buttons = list.map(control => (
|
|
31
|
+
<Button
|
|
32
|
+
key={control.label}
|
|
33
|
+
label={control.label}
|
|
34
|
+
variant="ghost"
|
|
35
|
+
isDisabled={control.isDisabled}
|
|
36
|
+
/>
|
|
37
|
+
));
|
|
38
|
+
return orientation === 'vertical' ? (
|
|
39
|
+
<VStack gap={1}>{buttons}</VStack>
|
|
40
|
+
) : (
|
|
41
|
+
<>{buttons}</>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function renderToolbarState(
|
|
46
|
+
state: Pick<
|
|
47
|
+
ToolbarBindingState,
|
|
48
|
+
'label' | 'startControls' | 'endControls' | 'facts'
|
|
49
|
+
>,
|
|
50
|
+
): ReactElement {
|
|
51
|
+
return (
|
|
52
|
+
<div dir={state.facts.direction} style={{padding: 24, maxWidth: 640}}>
|
|
53
|
+
<button type="button">Before the toolbar</button>
|
|
54
|
+
<Toolbar
|
|
55
|
+
label={state.label}
|
|
56
|
+
orientation={state.facts.orientation}
|
|
57
|
+
startContent={controls(state.startControls, state.facts.orientation)}
|
|
58
|
+
endContent={controls(state.endControls, state.facts.orientation)}
|
|
59
|
+
/>
|
|
60
|
+
<button type="button">After the toolbar</button>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Toolbar.a11y.states.ts
|
|
5
|
+
* @input Toolbar's representative states and the facts TOOLBAR_PATTERN reads
|
|
6
|
+
* @output TOOLBAR_BINDING_STATES and the explicit ownership exclusions for this binding
|
|
7
|
+
* @position Binding inventory shared by the jsdom test and the Chromium spec (Toolbar under spec:AST-020 FR15)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {ToolbarStateFacts} from '@astryxdesign/a11y-spec';
|
|
11
|
+
|
|
12
|
+
export interface ToolbarControl {
|
|
13
|
+
readonly label: string;
|
|
14
|
+
readonly isDisabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ToolbarBindingState {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly summary: string;
|
|
20
|
+
readonly storyId: string;
|
|
21
|
+
/** The Toolbar `label`, which names the toolbar. */
|
|
22
|
+
readonly label: string;
|
|
23
|
+
/** Controls in the start slot, in order. */
|
|
24
|
+
readonly startControls: ReadonlyArray<ToolbarControl>;
|
|
25
|
+
/** Controls in the end slot, in order. */
|
|
26
|
+
readonly endControls: ReadonlyArray<ToolbarControl>;
|
|
27
|
+
/** Relation name → accessible name of the control it designates. */
|
|
28
|
+
readonly controls: Readonly<
|
|
29
|
+
Record<'first-control' | 'second-control' | 'last-control', string>
|
|
30
|
+
>;
|
|
31
|
+
readonly facts: ToolbarStateFacts;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const story = (id: string) => `a11y-toolbar--${id}`;
|
|
35
|
+
|
|
36
|
+
const FORMATTING = [
|
|
37
|
+
{label: 'Bold'},
|
|
38
|
+
{label: 'Italic'},
|
|
39
|
+
{label: 'Underline'},
|
|
40
|
+
] as const;
|
|
41
|
+
|
|
42
|
+
const FORMATTING_CONTROLS = {
|
|
43
|
+
'first-control': 'Bold',
|
|
44
|
+
'second-control': 'Italic',
|
|
45
|
+
'last-control': 'Settings',
|
|
46
|
+
} as const;
|
|
47
|
+
|
|
48
|
+
const HORIZONTAL_LTR: ToolbarStateFacts = {
|
|
49
|
+
orientation: 'horizontal',
|
|
50
|
+
direction: 'ltr',
|
|
51
|
+
wraps: true,
|
|
52
|
+
homeEnd: true,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const TOOLBAR_BINDING_STATES = [
|
|
56
|
+
{
|
|
57
|
+
id: 'horizontal',
|
|
58
|
+
summary:
|
|
59
|
+
'a named horizontal Toolbar with three formatting buttons in the start slot and Settings in the end slot',
|
|
60
|
+
storyId: story('horizontal'),
|
|
61
|
+
label: 'Text formatting',
|
|
62
|
+
startControls: FORMATTING,
|
|
63
|
+
endControls: [{label: 'Settings'}],
|
|
64
|
+
controls: FORMATTING_CONTROLS,
|
|
65
|
+
facts: HORIZONTAL_LTR,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'horizontal-rtl',
|
|
69
|
+
summary:
|
|
70
|
+
'the same Toolbar inside a right-to-left container, where ArrowLeft moves to the next control',
|
|
71
|
+
storyId: story('horizontal-rtl'),
|
|
72
|
+
label: 'Text formatting',
|
|
73
|
+
startControls: FORMATTING,
|
|
74
|
+
endControls: [{label: 'Settings'}],
|
|
75
|
+
controls: FORMATTING_CONTROLS,
|
|
76
|
+
facts: {...HORIZONTAL_LTR, direction: 'rtl'},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'vertical',
|
|
80
|
+
summary:
|
|
81
|
+
'a vertical Toolbar of drawing tools, moved through with ArrowDown and ArrowUp',
|
|
82
|
+
storyId: story('vertical'),
|
|
83
|
+
label: 'Drawing tools',
|
|
84
|
+
startControls: [{label: 'Select'}, {label: 'Pen'}, {label: 'Eraser'}],
|
|
85
|
+
endControls: [],
|
|
86
|
+
controls: {
|
|
87
|
+
'first-control': 'Select',
|
|
88
|
+
'second-control': 'Pen',
|
|
89
|
+
'last-control': 'Eraser',
|
|
90
|
+
},
|
|
91
|
+
facts: {...HORIZONTAL_LTR, orientation: 'vertical'},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'first-control-disabled',
|
|
95
|
+
summary:
|
|
96
|
+
'a Toolbar whose first button is disabled, so Tab enters on the first enabled button and the arrows skip the disabled one',
|
|
97
|
+
storyId: story('first-control-disabled'),
|
|
98
|
+
label: 'Edit actions',
|
|
99
|
+
startControls: [
|
|
100
|
+
{label: 'Undo', isDisabled: true},
|
|
101
|
+
{label: 'Cut'},
|
|
102
|
+
{label: 'Copy'},
|
|
103
|
+
],
|
|
104
|
+
endControls: [{label: 'Paste'}],
|
|
105
|
+
controls: {
|
|
106
|
+
'first-control': 'Cut',
|
|
107
|
+
'second-control': 'Copy',
|
|
108
|
+
'last-control': 'Paste',
|
|
109
|
+
},
|
|
110
|
+
facts: HORIZONTAL_LTR,
|
|
111
|
+
},
|
|
112
|
+
] as const satisfies ReadonlyArray<ToolbarBindingState>;
|
|
113
|
+
|
|
114
|
+
export const TOOLBAR_EXCLUSIONS = [
|
|
115
|
+
{
|
|
116
|
+
id: 'control-semantics',
|
|
117
|
+
owner:
|
|
118
|
+
'the button, toggle-button and menu-button patterns and their bindings',
|
|
119
|
+
reason:
|
|
120
|
+
'Each control placed in a Toolbar exposes its own role, name, state and activation through its own pattern; this binding renders plain Buttons only so the toolbar container and the movement between controls are what is checked.',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'caret-guard-and-nested-popovers',
|
|
124
|
+
owner:
|
|
125
|
+
'Toolbar.test.tsx (caret-guard, nested popover and onKeyDown composition suites)',
|
|
126
|
+
reason:
|
|
127
|
+
'Leaving caret keys to a text field mid-line, leaving keys inside an open MoreMenu to that menu, and composing a caller onKeyDown are binding behaviour the APG leaves to the author, so no state here places a text field or popover in the toolbar.',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 'three-lane-layout',
|
|
131
|
+
owner:
|
|
132
|
+
'Toolbar.test.tsx (centerContent and three-slot layout tests) and the repository visual gate',
|
|
133
|
+
reason:
|
|
134
|
+
'Center content switches the row to a three-column grid, but the controls stay in DOM order inside the same toolbar element and the same roving tab stop walks them, so the role, name, Tab stop and arrow order this contract reads do not change; the grid itself is layout paint.',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'outer-chrome',
|
|
138
|
+
owner: 'component:Section and the repository visual gate',
|
|
139
|
+
reason:
|
|
140
|
+
'Variant, dividers, block padding and edge compensation are Section and layout paint around the toolbar row; they change no role, name, Tab stop or arrow movement, so every state uses the default transparent chrome.',
|
|
141
|
+
},
|
|
142
|
+
] as const;
|