@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
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file FieldLabelAudit.a11y.chromium.spec.ts
|
|
5
|
+
* @input Exact-head FieldLabel story across semantics, themes, direction, and viewport sizes
|
|
6
|
+
* @output Screenshots and fail-closed sensor receipts for the FieldLabel whole-component audit
|
|
7
|
+
* @position Component-owned Night Watch browser evidence; CI runs it only for a FieldLabel audit pull request
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {createHash} from 'node:crypto';
|
|
11
|
+
import {execFileSync} from 'node:child_process';
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import {expect, test, type Page} from '@playwright/test';
|
|
15
|
+
import {holdMotionStill} from '@astryxdesign/a11y-spec/chromium';
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_STORYBOOK_DIR,
|
|
18
|
+
serveStorybook,
|
|
19
|
+
type StaticServer,
|
|
20
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
21
|
+
|
|
22
|
+
const OUTPUT = path.resolve('field-label-audit-evidence');
|
|
23
|
+
const WIDE = {width: 1024, height: 900};
|
|
24
|
+
const NARROW = {width: 320, height: 900};
|
|
25
|
+
|
|
26
|
+
type Mode = 'light' | 'dark';
|
|
27
|
+
type Direction = 'ltr' | 'rtl';
|
|
28
|
+
type Theme = 'neutral' | 'butter';
|
|
29
|
+
|
|
30
|
+
type AuditCase = {
|
|
31
|
+
key: string;
|
|
32
|
+
mode: Mode;
|
|
33
|
+
direction: Direction;
|
|
34
|
+
theme: Theme;
|
|
35
|
+
viewport: {width: number; height: number};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type AuditReceipt = {
|
|
39
|
+
passed: boolean;
|
|
40
|
+
expected: Record<string, unknown>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const cases: AuditCase[] = [];
|
|
44
|
+
function addPair(
|
|
45
|
+
key: string,
|
|
46
|
+
options: Partial<Omit<AuditCase, 'key' | 'mode'>> = {},
|
|
47
|
+
) {
|
|
48
|
+
for (const mode of ['light', 'dark'] as const) {
|
|
49
|
+
cases.push({
|
|
50
|
+
key: `${key}-${mode}`,
|
|
51
|
+
mode,
|
|
52
|
+
direction: options.direction ?? 'ltr',
|
|
53
|
+
theme: options.theme ?? 'neutral',
|
|
54
|
+
viewport: options.viewport ?? WIDE,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
addPair('default');
|
|
60
|
+
addPair('custom-theme', {theme: 'butter'});
|
|
61
|
+
addPair('rtl', {direction: 'rtl'});
|
|
62
|
+
addPair('narrow', {viewport: NARROW});
|
|
63
|
+
|
|
64
|
+
test.describe.configure({mode: 'serial', retries: 0});
|
|
65
|
+
let server: StaticServer;
|
|
66
|
+
let checkoutSha: string;
|
|
67
|
+
let storybookSha: string;
|
|
68
|
+
|
|
69
|
+
function storyUrl(item: AuditCase) {
|
|
70
|
+
return `${server.origin}/iframe.html?id=core-fieldlabel--states&viewMode=story&globals=colorMode:${item.mode};astryxTheme:${item.theme};direction:${item.direction}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function settle(page: Page) {
|
|
74
|
+
await holdMotionStill(page);
|
|
75
|
+
await page.evaluate(async () => document.fonts.ready);
|
|
76
|
+
await page.waitForTimeout(40);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
test.beforeAll(async () => {
|
|
80
|
+
fs.rmSync(OUTPUT, {recursive: true, force: true});
|
|
81
|
+
fs.mkdirSync(OUTPUT, {recursive: true});
|
|
82
|
+
checkoutSha = execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
83
|
+
encoding: 'utf8',
|
|
84
|
+
}).trim();
|
|
85
|
+
const expected = process.env.ASTRYX_HEAD_SHA;
|
|
86
|
+
if (expected && expected !== checkoutSha) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`checkout ${checkoutSha} does not match expected head ${expected}`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
server = await serveStorybook(
|
|
92
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
93
|
+
);
|
|
94
|
+
const response = await fetch(`${server.origin}/astryx-build-sha.txt`);
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new Error(`Storybook build has no SHA stamp (${response.status})`);
|
|
97
|
+
}
|
|
98
|
+
storybookSha = (await response.text()).trim();
|
|
99
|
+
if (storybookSha !== checkoutSha) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Storybook ${storybookSha} does not match checkout ${checkoutSha}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test.afterAll(async () => {
|
|
107
|
+
await server?.close();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
for (const item of cases) {
|
|
111
|
+
test(item.key, async ({browser}) => {
|
|
112
|
+
const context = await browser.newContext({
|
|
113
|
+
viewport: item.viewport,
|
|
114
|
+
colorScheme: item.mode,
|
|
115
|
+
reducedMotion: 'reduce',
|
|
116
|
+
});
|
|
117
|
+
const page = await context.newPage();
|
|
118
|
+
const pageErrors: string[] = [];
|
|
119
|
+
page.on('pageerror', error => pageErrors.push(String(error)));
|
|
120
|
+
await page.goto(storyUrl(item), {waitUntil: 'load'});
|
|
121
|
+
await page.locator('#storybook-root').waitFor();
|
|
122
|
+
|
|
123
|
+
const targets = page.locator('.astryx-field-label');
|
|
124
|
+
await expect(targets).toHaveCount(7);
|
|
125
|
+
await expect(page.locator('label[for="field-label-default"]')).toHaveText(
|
|
126
|
+
'Email',
|
|
127
|
+
);
|
|
128
|
+
const description = page.locator('#field-label-description-help');
|
|
129
|
+
await expect(description).toHaveText(
|
|
130
|
+
'Shown in navigation and invitations.',
|
|
131
|
+
);
|
|
132
|
+
await expect(description.locator('xpath=ancestor::label')).toHaveCount(0);
|
|
133
|
+
await expect(page.locator('#field-label-group-label')).toHaveJSProperty(
|
|
134
|
+
'tagName',
|
|
135
|
+
'SPAN',
|
|
136
|
+
);
|
|
137
|
+
await expect(
|
|
138
|
+
page.getByRole('radiogroup', {name: 'Visibility'}),
|
|
139
|
+
).toBeVisible();
|
|
140
|
+
await expect(page.getByText(/Required/)).toBeVisible();
|
|
141
|
+
await expect(page.getByText(/Optional/)).toBeVisible();
|
|
142
|
+
await expect(page.getByLabel('Search')).toBeVisible();
|
|
143
|
+
const tooltipAnchor = page
|
|
144
|
+
.locator('label[for="field-label-optional"] [aria-describedby]')
|
|
145
|
+
.first();
|
|
146
|
+
await expect(tooltipAnchor).toHaveCount(1);
|
|
147
|
+
|
|
148
|
+
await settle(page);
|
|
149
|
+
const observed = await page.evaluate(() => {
|
|
150
|
+
const labels = Array.from(
|
|
151
|
+
document.querySelectorAll<HTMLElement>('.astryx-field-label'),
|
|
152
|
+
).map(label => {
|
|
153
|
+
const style = getComputedStyle(label);
|
|
154
|
+
const box = label.getBoundingClientRect();
|
|
155
|
+
return {
|
|
156
|
+
text: label.textContent?.replace(/\s+/g, ' ').trim(),
|
|
157
|
+
tagName: label.tagName,
|
|
158
|
+
htmlFor: label.getAttribute('for'),
|
|
159
|
+
color: style.color,
|
|
160
|
+
cursor: style.cursor,
|
|
161
|
+
geometry: {x: box.x, y: box.y, width: box.width, height: box.height},
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
const hidden = document.querySelector<HTMLElement>(
|
|
165
|
+
'label[for="field-label-hidden"]',
|
|
166
|
+
);
|
|
167
|
+
const hiddenBox = hidden?.getBoundingClientRect();
|
|
168
|
+
const firstLabel = document.querySelector<HTMLElement>(
|
|
169
|
+
'.astryx-field-label',
|
|
170
|
+
);
|
|
171
|
+
const currentTooltipAnchor = document.querySelector<HTMLElement>(
|
|
172
|
+
'label[for="field-label-optional"] [aria-describedby]',
|
|
173
|
+
);
|
|
174
|
+
return {
|
|
175
|
+
theme: document
|
|
176
|
+
.querySelector('[data-astryx-theme]')
|
|
177
|
+
?.getAttribute('data-astryx-theme'),
|
|
178
|
+
mode: document.documentElement.getAttribute('data-theme'),
|
|
179
|
+
direction: firstLabel ? getComputedStyle(firstLabel).direction : null,
|
|
180
|
+
viewport: {width: innerWidth, height: innerHeight},
|
|
181
|
+
reducedMotion: matchMedia('(prefers-reduced-motion: reduce)').matches,
|
|
182
|
+
horizontalOverflow:
|
|
183
|
+
document.documentElement.scrollWidth > innerWidth + 1,
|
|
184
|
+
fonts: document.fonts.status,
|
|
185
|
+
labels,
|
|
186
|
+
hiddenGeometry: hiddenBox
|
|
187
|
+
? {width: hiddenBox.width, height: hiddenBox.height}
|
|
188
|
+
: null,
|
|
189
|
+
tooltipAnchor: currentTooltipAnchor
|
|
190
|
+
? {
|
|
191
|
+
ariaHidden: currentTooltipAnchor.getAttribute('aria-hidden'),
|
|
192
|
+
ariaLabel: currentTooltipAnchor.getAttribute('aria-label'),
|
|
193
|
+
describedBy:
|
|
194
|
+
currentTooltipAnchor.getAttribute('aria-describedby'),
|
|
195
|
+
tabIndex: currentTooltipAnchor.getAttribute('tabindex'),
|
|
196
|
+
}
|
|
197
|
+
: null,
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const semanticsPass =
|
|
202
|
+
observed.labels.length === 7 &&
|
|
203
|
+
observed.labels.filter(label => label.tagName === 'SPAN').length === 1 &&
|
|
204
|
+
observed.labels.every(label => label.geometry.width > 0) &&
|
|
205
|
+
observed.hiddenGeometry?.width === 1 &&
|
|
206
|
+
observed.hiddenGeometry.height === 1;
|
|
207
|
+
const knownTooltipGapObserved =
|
|
208
|
+
observed.tooltipAnchor?.ariaHidden === 'true' &&
|
|
209
|
+
observed.tooltipAnchor.ariaLabel == null &&
|
|
210
|
+
observed.tooltipAnchor.tabIndex == null;
|
|
211
|
+
|
|
212
|
+
const png = await page.screenshot({animations: 'disabled', fullPage: true});
|
|
213
|
+
const image = `${item.key}.png`;
|
|
214
|
+
const receipt = {
|
|
215
|
+
schemaVersion: 1,
|
|
216
|
+
expected: {
|
|
217
|
+
build: checkoutSha,
|
|
218
|
+
storyId: 'core-fieldlabel--states',
|
|
219
|
+
mode: item.mode,
|
|
220
|
+
direction: item.direction,
|
|
221
|
+
theme: item.theme,
|
|
222
|
+
viewport: item.viewport,
|
|
223
|
+
targetCount: 7,
|
|
224
|
+
groupLabelTag: 'SPAN',
|
|
225
|
+
hiddenGeometry: {width: 1, height: 1},
|
|
226
|
+
knownGap:
|
|
227
|
+
'the tooltip anchor is anonymous and nonfocusable on this audited head',
|
|
228
|
+
},
|
|
229
|
+
observed: {
|
|
230
|
+
build: storybookSha,
|
|
231
|
+
...observed,
|
|
232
|
+
pageErrors,
|
|
233
|
+
},
|
|
234
|
+
image: {
|
|
235
|
+
file: image,
|
|
236
|
+
sha256: createHash('sha256').update(png).digest('hex'),
|
|
237
|
+
width: png.readUInt32BE(16),
|
|
238
|
+
height: png.readUInt32BE(20),
|
|
239
|
+
},
|
|
240
|
+
checks: {semanticsPass, knownTooltipGapObserved},
|
|
241
|
+
passed:
|
|
242
|
+
storybookSha === checkoutSha &&
|
|
243
|
+
pageErrors.length === 0 &&
|
|
244
|
+
observed.theme === item.theme &&
|
|
245
|
+
observed.mode === item.mode &&
|
|
246
|
+
observed.direction === item.direction &&
|
|
247
|
+
observed.viewport.width === item.viewport.width &&
|
|
248
|
+
observed.viewport.height === item.viewport.height &&
|
|
249
|
+
observed.reducedMotion &&
|
|
250
|
+
!observed.horizontalOverflow &&
|
|
251
|
+
observed.fonts === 'loaded' &&
|
|
252
|
+
semanticsPass,
|
|
253
|
+
};
|
|
254
|
+
fs.writeFileSync(path.join(OUTPUT, image), png);
|
|
255
|
+
fs.writeFileSync(
|
|
256
|
+
path.join(OUTPUT, `${item.key}.sensors.json`),
|
|
257
|
+
`${JSON.stringify(receipt, null, 2)}\n`,
|
|
258
|
+
);
|
|
259
|
+
await context.close();
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
test('manifest is complete', async () => {
|
|
264
|
+
const receiptFiles = fs
|
|
265
|
+
.readdirSync(OUTPUT)
|
|
266
|
+
.filter(file => file.endsWith('.sensors.json'));
|
|
267
|
+
const receipts = receiptFiles.map(
|
|
268
|
+
(file): AuditReceipt =>
|
|
269
|
+
JSON.parse(
|
|
270
|
+
fs.readFileSync(path.join(OUTPUT, file), 'utf8'),
|
|
271
|
+
) as AuditReceipt,
|
|
272
|
+
);
|
|
273
|
+
fs.writeFileSync(
|
|
274
|
+
path.join(OUTPUT, 'manifest.json'),
|
|
275
|
+
`${JSON.stringify(
|
|
276
|
+
{
|
|
277
|
+
schemaVersion: 1,
|
|
278
|
+
component: 'core/FieldLabel',
|
|
279
|
+
build: checkoutSha,
|
|
280
|
+
frames: cases.map(item => ({
|
|
281
|
+
key: item.key,
|
|
282
|
+
image: `${item.key}.png`,
|
|
283
|
+
receipt: `${item.key}.sensors.json`,
|
|
284
|
+
})),
|
|
285
|
+
},
|
|
286
|
+
null,
|
|
287
|
+
2,
|
|
288
|
+
)}\n`,
|
|
289
|
+
);
|
|
290
|
+
expect(receiptFiles).toHaveLength(cases.length);
|
|
291
|
+
expect(
|
|
292
|
+
receipts
|
|
293
|
+
.filter(receipt => !receipt.passed)
|
|
294
|
+
.map(receipt => receipt.expected),
|
|
295
|
+
).toEqual([]);
|
|
296
|
+
});
|
package/src/Grid/GridSpan.tsx
CHANGED
|
@@ -47,6 +47,9 @@ const baseStyles = stylex.create({
|
|
|
47
47
|
minWidth: 0, // Prevent overflow in grid
|
|
48
48
|
// Make span fill grid cell and stretch children
|
|
49
49
|
display: 'grid',
|
|
50
|
+
// The implicit `auto` column would grow to a child's min-content width;
|
|
51
|
+
// a zero minimum keeps it inside the span.
|
|
52
|
+
gridTemplateColumns: 'minmax(0, 1fr)',
|
|
50
53
|
height: '100%',
|
|
51
54
|
},
|
|
52
55
|
});
|
|
@@ -269,3 +269,60 @@ describe('Heading', () => {
|
|
|
269
269
|
expect(element).not.toHaveAttribute('data-type', 'editorial');
|
|
270
270
|
});
|
|
271
271
|
});
|
|
272
|
+
|
|
273
|
+
// With hasTruncateTooltip={false} nothing reads the measured overflow, so a
|
|
274
|
+
// long table of truncated cells must not pay a layout read per cell.
|
|
275
|
+
function countScrollWidthReads(run: () => void): number {
|
|
276
|
+
const proto = window.HTMLElement.prototype;
|
|
277
|
+
const original = Object.getOwnPropertyDescriptor(proto, 'scrollWidth');
|
|
278
|
+
let reads = 0;
|
|
279
|
+
Object.defineProperty(proto, 'scrollWidth', {
|
|
280
|
+
configurable: true,
|
|
281
|
+
get() {
|
|
282
|
+
reads += 1;
|
|
283
|
+
return 0;
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
try {
|
|
287
|
+
run();
|
|
288
|
+
} finally {
|
|
289
|
+
if (original) {
|
|
290
|
+
Object.defineProperty(proto, 'scrollWidth', original);
|
|
291
|
+
} else {
|
|
292
|
+
delete (proto as {scrollWidth?: number}).scrollWidth;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return reads;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
describe('Heading maxLines without a truncation tooltip', () => {
|
|
299
|
+
it('takes no truncation measurement when hasTruncateTooltip is false', () => {
|
|
300
|
+
const reads = countScrollWidthReads(() => {
|
|
301
|
+
render(
|
|
302
|
+
<>
|
|
303
|
+
{Array.from({length: 20}, (_, index) => (
|
|
304
|
+
<Heading
|
|
305
|
+
level={3}
|
|
306
|
+
maxLines={1}
|
|
307
|
+
hasTruncateTooltip={false}
|
|
308
|
+
key={index}>
|
|
309
|
+
Cell {index} with text far wider than its column
|
|
310
|
+
</Heading>
|
|
311
|
+
))}
|
|
312
|
+
</>,
|
|
313
|
+
);
|
|
314
|
+
});
|
|
315
|
+
expect(reads).toBe(0);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('still measures when the tooltip is enabled', () => {
|
|
319
|
+
const reads = countScrollWidthReads(() => {
|
|
320
|
+
render(
|
|
321
|
+
<Heading level={3} maxLines={1}>
|
|
322
|
+
Text far wider than its column
|
|
323
|
+
</Heading>,
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
expect(reads).toBeGreaterThan(0);
|
|
327
|
+
});
|
|
328
|
+
});
|
package/src/Heading/Heading.tsx
CHANGED
|
@@ -253,8 +253,12 @@ export function Heading({
|
|
|
253
253
|
// the closed built-in StyleX maps with an unknown name.
|
|
254
254
|
const builtinType = type && isBuiltinHeadingType(type) ? type : undefined;
|
|
255
255
|
|
|
256
|
-
// Truncation detection
|
|
257
|
-
|
|
256
|
+
// Truncation detection. Only the tooltip reads the measured overflow, so
|
|
257
|
+
// with the tooltip off the text is clipped by CSS alone and nothing is
|
|
258
|
+
// measured or observed.
|
|
259
|
+
const truncation = useTruncation({
|
|
260
|
+
maxLines: hasTruncateTooltip === false ? 0 : maxLines,
|
|
261
|
+
});
|
|
258
262
|
|
|
259
263
|
// Tooltip for truncated text
|
|
260
264
|
const tooltipPlacement: LayerPlacement =
|
|
@@ -109,6 +109,64 @@ describe('useHoverCard identity', () => {
|
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
+
// A card closed while the pointer rests on it unmounts without a mouseleave,
|
|
113
|
+
// so the hook must not keep believing the pointer is over the card: if it
|
|
114
|
+
// does, the next open of the same trigger never closes on leave.
|
|
115
|
+
describe('useHoverCard closing while the pointer is on the card', () => {
|
|
116
|
+
function Harness() {
|
|
117
|
+
const card = useHoverCard({delay: 0, hideDelay: 0});
|
|
118
|
+
return (
|
|
119
|
+
<div>
|
|
120
|
+
<span ref={card.ref}>Trigger</span>
|
|
121
|
+
<output data-testid="state">{card.isOpen ? 'open' : 'closed'}</output>
|
|
122
|
+
{card.renderHoverCard(
|
|
123
|
+
<button type="button" onClick={card.hide}>
|
|
124
|
+
Pick
|
|
125
|
+
</button>,
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function openThenRestOnCard() {
|
|
132
|
+
const state = screen.getByTestId('state');
|
|
133
|
+
fireEvent.mouseEnter(screen.getByText('Trigger'));
|
|
134
|
+
await waitFor(() => expect(state).toHaveTextContent('open'));
|
|
135
|
+
const pick = screen.getByRole('button', {name: 'Pick', hidden: true});
|
|
136
|
+
fireEvent.mouseLeave(screen.getByText('Trigger'));
|
|
137
|
+
fireEvent.mouseEnter(pick.parentElement!);
|
|
138
|
+
return {state, pick};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function expectNextOpenClosesOnLeave(state: HTMLElement) {
|
|
142
|
+
const trigger = screen.getByText('Trigger');
|
|
143
|
+
fireEvent.mouseEnter(trigger);
|
|
144
|
+
await waitFor(() => expect(state).toHaveTextContent('open'));
|
|
145
|
+
fireEvent.mouseLeave(trigger);
|
|
146
|
+
await waitFor(() => expect(state).toHaveTextContent('closed'));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
it('closes the next open on leave after hide() from inside the card', async () => {
|
|
150
|
+
render(<Harness />);
|
|
151
|
+
const {state, pick} = await openThenRestOnCard();
|
|
152
|
+
|
|
153
|
+
fireEvent.click(pick);
|
|
154
|
+
await waitFor(() => expect(state).toHaveTextContent('closed'));
|
|
155
|
+
|
|
156
|
+
await expectNextOpenClosesOnLeave(state);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('closes the next open on leave after Escape dismisses the card', async () => {
|
|
160
|
+
render(<Harness />);
|
|
161
|
+
const {state} = await openThenRestOnCard();
|
|
162
|
+
|
|
163
|
+
fireEvent.keyDown(document.body, {key: 'Escape'});
|
|
164
|
+
await waitFor(() => expect(state).toHaveTextContent('closed'));
|
|
165
|
+
|
|
166
|
+
await expectNextOpenClosesOnLeave(state);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
112
170
|
describe('HoverCard', () => {
|
|
113
171
|
it('renders trigger element', () => {
|
|
114
172
|
render(
|
|
@@ -345,6 +345,14 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
345
345
|
hideLayer();
|
|
346
346
|
}, [clearTimeouts, hideLayer]);
|
|
347
347
|
|
|
348
|
+
// The imperative hide. A card closed while the pointer rests on it unmounts
|
|
349
|
+
// without a mouseleave, so the content-hover latch is cleared here; left set,
|
|
350
|
+
// it would keep the trigger's next hover-open from closing on leave.
|
|
351
|
+
const hide = useCallback(() => {
|
|
352
|
+
isHoveringContentRef.current = false;
|
|
353
|
+
hideLayer();
|
|
354
|
+
}, [hideLayer]);
|
|
355
|
+
|
|
348
356
|
const {
|
|
349
357
|
isTouchPointerRef,
|
|
350
358
|
isTouchInteraction,
|
|
@@ -508,7 +516,7 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
508
516
|
? null
|
|
509
517
|
: document.getElementById(layerId);
|
|
510
518
|
const hadFocus = card?.contains(document.activeElement) ?? false;
|
|
511
|
-
|
|
519
|
+
hide();
|
|
512
520
|
if (hadFocus) {
|
|
513
521
|
isEscapeDismissingRef.current = true;
|
|
514
522
|
triggerRef.current?.focus();
|
|
@@ -720,6 +728,6 @@ export function useHoverCard(options: HoverCardOptions = {}): HoverCardReturn {
|
|
|
720
728
|
isOpen: isLayerOpen,
|
|
721
729
|
renderHoverCard,
|
|
722
730
|
show: showLayer,
|
|
723
|
-
hide
|
|
731
|
+
hide,
|
|
724
732
|
};
|
|
725
733
|
}
|
package/src/Icon/Icon.doc.mjs
CHANGED
|
@@ -23,6 +23,42 @@ export const docs = {
|
|
|
23
23
|
'pictogram',
|
|
24
24
|
'graphic',
|
|
25
25
|
'vector',
|
|
26
|
+
'arrow',
|
|
27
|
+
'chevron',
|
|
28
|
+
'caret',
|
|
29
|
+
'checkmark',
|
|
30
|
+
'cross',
|
|
31
|
+
'gear',
|
|
32
|
+
'cog',
|
|
33
|
+
'broom',
|
|
34
|
+
'sparkle',
|
|
35
|
+
'sparkles',
|
|
36
|
+
'star',
|
|
37
|
+
'heart',
|
|
38
|
+
'bell',
|
|
39
|
+
'trash',
|
|
40
|
+
'pencil',
|
|
41
|
+
'magnifyingglass',
|
|
42
|
+
'paperclip',
|
|
43
|
+
'house',
|
|
44
|
+
'flame',
|
|
45
|
+
'rocket',
|
|
46
|
+
'lightning',
|
|
47
|
+
'bolt',
|
|
48
|
+
'globe',
|
|
49
|
+
'sun',
|
|
50
|
+
'moon',
|
|
51
|
+
'bookmark',
|
|
52
|
+
'thumbsup',
|
|
53
|
+
'smiley',
|
|
54
|
+
'emoji',
|
|
55
|
+
'lock',
|
|
56
|
+
'eye',
|
|
57
|
+
'cloud',
|
|
58
|
+
'hourglass',
|
|
59
|
+
'trophy',
|
|
60
|
+
'gift',
|
|
61
|
+
'camera',
|
|
26
62
|
],
|
|
27
63
|
playground: {
|
|
28
64
|
// `icon` is required and its type can't be auto-generated, so the
|
|
@@ -46,7 +46,7 @@ export const docs = {
|
|
|
46
46
|
{
|
|
47
47
|
name: 'isLoading',
|
|
48
48
|
type: 'boolean',
|
|
49
|
-
description: 'Shows a loading spinner and
|
|
49
|
+
description: 'Shows a loading spinner and blocks re-activation while staying focusable (aria-busy + aria-disabled, not native disabled).',
|
|
50
50
|
default: 'false',
|
|
51
51
|
},
|
|
52
52
|
{
|
|
@@ -141,7 +141,7 @@ export const docsDense = {
|
|
|
141
141
|
variant: 'visual style variant',
|
|
142
142
|
size: 'size variant',
|
|
143
143
|
elevation: 'resting shadow depth: none|low|med|high; raise for a floating action button (FAB)',
|
|
144
|
-
isLoading: 'shows loading spinner
|
|
144
|
+
isLoading: 'shows loading spinner, blocks re-activation but stays focusable (aria-disabled, not native disabled)',
|
|
145
145
|
isDisabled: 'disables button',
|
|
146
146
|
tooltip: 'tooltip text shown on hover',
|
|
147
147
|
onClick: 'standard click handler',
|
|
@@ -124,7 +124,9 @@ describe('IconButton', () => {
|
|
|
124
124
|
it('shows loading state', () => {
|
|
125
125
|
render(<IconButton label="Save" icon={<span>💾</span>} isLoading />);
|
|
126
126
|
const button = screen.getByRole('button');
|
|
127
|
-
|
|
127
|
+
// Busy is aria-disabled (focusable), not native disabled — see #4871.
|
|
128
|
+
expect(button).not.toBeDisabled();
|
|
129
|
+
expect(button).toHaveAttribute('aria-disabled', 'true');
|
|
128
130
|
});
|
|
129
131
|
|
|
130
132
|
it('forwards ref correctly', () => {
|
package/src/Item/Item.doc.mjs
CHANGED
|
@@ -49,7 +49,7 @@ export const docs = {
|
|
|
49
49
|
{
|
|
50
50
|
name: '--_item-inset-inline',
|
|
51
51
|
description:
|
|
52
|
-
'Inline inset of the row. Item derives its paddingInline from this variable, and List reads it to
|
|
52
|
+
'Inline inset of the row. Item derives its paddingInline from this variable, and List reads it to place rows for its edge treatments (edge="flush", "outset", and "bleed"). Set paddingInline on `item` in a theme and both stay in sync.',
|
|
53
53
|
default: 'var(--spacing-2) (var(--spacing-3) for density="spacious")',
|
|
54
54
|
private: true,
|
|
55
55
|
},
|
|
@@ -118,8 +118,9 @@ export const docs = {
|
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
name: 'align',
|
|
121
|
-
type: "'center' | 'start'",
|
|
122
|
-
description:
|
|
121
|
+
type: "'center' | 'start' | 'first-line'",
|
|
122
|
+
description:
|
|
123
|
+
"Vertical alignment of start/end content slots. 'center' centers each slot on the whole text block; 'start' lines up slot tops with the text's top; 'first-line' centers each slot on the label's first line, so a small mark stays level with the first line of a wrapped label. A slot taller than that line moves the text down. The `item` theme target reflects the value as `align`.",
|
|
123
124
|
default: "'center'",
|
|
124
125
|
},
|
|
125
126
|
{
|
|
@@ -248,7 +249,7 @@ export const docs = {
|
|
|
248
249
|
{
|
|
249
250
|
guidance: true,
|
|
250
251
|
description:
|
|
251
|
-
'Use align="
|
|
252
|
+
'Use align="first-line" when a wrapping label sits beside a small mark (a status dot, a radio, a timestamp) that belongs to its first line. Use align="start" to line up the tops of a tall slot and the text.',
|
|
252
253
|
},
|
|
253
254
|
{
|
|
254
255
|
guidance: true,
|
|
@@ -321,7 +322,8 @@ export const docsZh = {
|
|
|
321
322
|
endContent:
|
|
322
323
|
'在标签/描述区域之后渲染的内容,例如徽章、元数据、时间戳或操作按钮。',
|
|
323
324
|
as: '根元素的 HTML 元素。',
|
|
324
|
-
align:
|
|
325
|
+
align:
|
|
326
|
+
"起始/结束内容插槽的垂直对齐方式:'center'(默认)、'start'(顶部对齐)或 'first-line'(以标签第一行为中心)。",
|
|
325
327
|
density:
|
|
326
328
|
'间距密度。"compact" 使用 4px 块内距,"balanced" 使用 8px,"spacious" 使用 12px 块内距和内联内距。',
|
|
327
329
|
labelLines: '标签截断前的最大行数。',
|
|
@@ -363,7 +365,8 @@ export const docsZh = {
|
|
|
363
365
|
},
|
|
364
366
|
{
|
|
365
367
|
guidance: true,
|
|
366
|
-
description:
|
|
368
|
+
description:
|
|
369
|
+
'当换行标签旁边是属于其第一行的小标记(状态点、单选按钮、时间戳)时,使用 align="first-line"。使用 align="start" 让较高插槽与文本顶部对齐。',
|
|
367
370
|
},
|
|
368
371
|
{
|
|
369
372
|
guidance: false,
|
|
@@ -426,7 +429,7 @@ export const docsDense = {
|
|
|
426
429
|
{
|
|
427
430
|
guidance: true,
|
|
428
431
|
description:
|
|
429
|
-
'align="
|
|
432
|
+
'align="first-line" for a small mark beside a wrapping label; align="start" to top-align a tall slot with the text.',
|
|
430
433
|
},
|
|
431
434
|
{
|
|
432
435
|
guidance: false,
|
|
@@ -458,7 +461,8 @@ export const docsDense = {
|
|
|
458
461
|
endContent:
|
|
459
462
|
'Content after label/description: badges, timestamps, actions.',
|
|
460
463
|
as: 'Root HTML element.',
|
|
461
|
-
align:
|
|
464
|
+
align:
|
|
465
|
+
"Start/end slot alignment: 'center' (default), 'start' (tops), or 'first-line' (centered on the label's first line).",
|
|
462
466
|
density:
|
|
463
467
|
'Spacing: "compact" (4px), "balanced" (8px), or "spacious" (12px).',
|
|
464
468
|
labelLines: 'Max label lines before truncation.',
|