@astryxdesign/core 0.7.1 → 0.7.2-canary.01eab6a
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/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.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +47 -4
- 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/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +19 -8
- 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/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/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/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/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 +472 -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.test.tsx +62 -13
- package/src/DropdownMenu/DropdownMenu.tsx +47 -4
- 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/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__/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/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/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/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/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 +2 -2
- package/src/MetadataList/MetadataList.test.tsx +38 -0
- package/src/MetadataList/MetadataList.tsx +8 -1
- package/src/MetadataList/MetadataListContext.tsx +1 -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/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/Selector/hooks.ts +6 -2
- package/src/Selector/useCombobox.doc.mjs +237 -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/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/ToggleButton/ToggleButton.test.tsx +95 -0
- package/src/ToggleButton/ToggleButton.tsx +12 -1
- package/src/Tokenizer/Tokenizer.test.tsx +54 -1
- 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/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,235 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file DrawerHeader.tsx
|
|
7
|
+
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, i18n, BaseProps, isRenderable, mergeProps, themeProps
|
|
8
|
+
* @output Exports DrawerHeader component and DrawerHeaderProps
|
|
9
|
+
* @position Drawer header; composed in a Layout header slot inside Drawer, tested by DrawerHeader.test.tsx
|
|
10
|
+
*
|
|
11
|
+
* Mirrors DialogHeader's API: a title, an optional subtitle, start/end content
|
|
12
|
+
* slots, and a close button that renders only when `onOpenChange` is passed.
|
|
13
|
+
* Two differences follow from Drawer's contract:
|
|
14
|
+
* - It does not move focus on mount. Drawer owns focus entry through
|
|
15
|
+
* `data-autofocus` once its native host is presented.
|
|
16
|
+
* - It does not name the drawer. Drawer's required `label` does.
|
|
17
|
+
*
|
|
18
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
19
|
+
* - /packages/core/src/Drawer/DrawerHeader.doc.mjs (props, anatomy, theming)
|
|
20
|
+
* - /packages/core/src/Drawer/DrawerHeader.test.tsx (tests for new/changed behavior)
|
|
21
|
+
* - /packages/core/src/Drawer/index.ts (exports if types change)
|
|
22
|
+
* - /packages/cli/assets/templates/blocks/components/Drawer/ (showcase blocks)
|
|
23
|
+
* - /packages/cli/assets/templates/blocks/components/DrawerHeader/ (showcase blocks)
|
|
24
|
+
* - /apps/storybook/stories/Drawer.stories.tsx (examples and visual coverage)
|
|
25
|
+
* - /apps/storybook/stories/DrawerHeader.stories.tsx (component audit fixture)
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type {ReactNode} from 'react';
|
|
29
|
+
import * as stylex from '@stylexjs/stylex';
|
|
30
|
+
import type {BaseProps} from '../BaseProps';
|
|
31
|
+
import {sizeVars, spacingVars, typeScaleVars} from '../theme/tokens.stylex';
|
|
32
|
+
import {Button} from '../Button';
|
|
33
|
+
import {Heading} from '../Heading';
|
|
34
|
+
import {Icon} from '../Icon';
|
|
35
|
+
import {LayoutHeader} from '../Layout';
|
|
36
|
+
import {Text} from '../Text';
|
|
37
|
+
import {useTranslator} from '../i18n';
|
|
38
|
+
import {isRenderable, mergeProps, themeProps} from '../utils';
|
|
39
|
+
|
|
40
|
+
const styles = stylex.create({
|
|
41
|
+
container: {
|
|
42
|
+
display: 'flex',
|
|
43
|
+
alignItems: 'flex-start',
|
|
44
|
+
justifyContent: 'space-between',
|
|
45
|
+
gap: spacingVars['--spacing-3'],
|
|
46
|
+
},
|
|
47
|
+
// Compensate for the medium icon button's visual padding on the end slot.
|
|
48
|
+
endBlockEdgeCompensation: {
|
|
49
|
+
marginBlock: `calc(-1 * ${spacingVars['--spacing-2']})`,
|
|
50
|
+
},
|
|
51
|
+
endInlineEdgeCompensation: {
|
|
52
|
+
marginInlineEnd: `calc(-1 * ${spacingVars['--spacing-2']})`,
|
|
53
|
+
},
|
|
54
|
+
titleWrapper: {
|
|
55
|
+
display: 'flex',
|
|
56
|
+
flexDirection: 'column',
|
|
57
|
+
gap: spacingVars['--spacing-0'],
|
|
58
|
+
flex: 1,
|
|
59
|
+
minWidth: 0,
|
|
60
|
+
// Align the title's center with the close button's center, as
|
|
61
|
+
// DialogHeader does.
|
|
62
|
+
marginBlock: `calc(${sizeVars['--size-element-md']} / 2 - ${spacingVars['--spacing-2']} - ${typeScaleVars['--text-heading-2-size']} * ${typeScaleVars['--text-heading-2-leading']} / 2)`,
|
|
63
|
+
},
|
|
64
|
+
actions: {
|
|
65
|
+
display: 'flex',
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
gap: spacingVars['--spacing-2'],
|
|
68
|
+
flexShrink: 0,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export interface DrawerHeaderProps extends BaseProps<HTMLDivElement> {
|
|
73
|
+
/** Ref forwarded to the root element */
|
|
74
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The title of the drawer, rendered as an h2. Keep it inline and
|
|
78
|
+
* non-interactive. It does not name the drawer; Drawer's `label` does.
|
|
79
|
+
*/
|
|
80
|
+
title: ReactNode;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Optional subtitle displayed below the title in smaller, secondary text.
|
|
84
|
+
* Accepts inline content such as a Link; it renders inside a span, so
|
|
85
|
+
* avoid block elements. Nothing renders for `null`, `undefined`, booleans,
|
|
86
|
+
* or an empty string.
|
|
87
|
+
*/
|
|
88
|
+
subtitle?: ReactNode;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Called with `false` when the close button is clicked. Pass the Drawer's
|
|
92
|
+
* own `onOpenChange`. If not provided, no close button is rendered.
|
|
93
|
+
*/
|
|
94
|
+
onOpenChange?: (isOpen: boolean) => unknown;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Content to render before the title (e.g., a back button).
|
|
98
|
+
*/
|
|
99
|
+
startContent?: ReactNode;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Content to render after the title, before the close button (e.g., action
|
|
103
|
+
* buttons).
|
|
104
|
+
*/
|
|
105
|
+
endContent?: ReactNode;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Overrides automatic end-slot compensation. When omitted, rendering the
|
|
109
|
+
* close action applies block and logical inline-end compensation. Use
|
|
110
|
+
* `inline`, `block`, or `all` to select the axes explicitly.
|
|
111
|
+
*/
|
|
112
|
+
endContentEdgeCompensation?: 'inline' | 'block' | 'all';
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Adds a themed border at the bottom edge.
|
|
116
|
+
* When false, spacing collapse is applied automatically for seamless visual flow.
|
|
117
|
+
* Defaults to the parent Layout's `defaultHasDividers` context value.
|
|
118
|
+
*/
|
|
119
|
+
hasDivider?: boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Header for a Drawer: a title, optional subtitle and start/end content, and a
|
|
124
|
+
* close button when `onOpenChange` is passed. Same API as DialogHeader.
|
|
125
|
+
*
|
|
126
|
+
* Uses LayoutHeader internally for consistent styling with other layout headers.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```
|
|
130
|
+
* <Drawer isOpen={isOpen} onOpenChange={setIsOpen} label="Details">
|
|
131
|
+
* <DrawerHeader title="Details" onOpenChange={setIsOpen} />
|
|
132
|
+
* <Section padding={4}>Content</Section>
|
|
133
|
+
* </Drawer>
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export function DrawerHeader({
|
|
137
|
+
title,
|
|
138
|
+
subtitle,
|
|
139
|
+
onOpenChange,
|
|
140
|
+
startContent,
|
|
141
|
+
endContent,
|
|
142
|
+
endContentEdgeCompensation,
|
|
143
|
+
hasDivider,
|
|
144
|
+
xstyle,
|
|
145
|
+
className,
|
|
146
|
+
style,
|
|
147
|
+
ref,
|
|
148
|
+
...rest
|
|
149
|
+
}: DrawerHeaderProps) {
|
|
150
|
+
const t = useTranslator();
|
|
151
|
+
const hasSubtitle = isRenderable(subtitle);
|
|
152
|
+
const hasStartContent = isRenderable(startContent);
|
|
153
|
+
const hasEndContent = isRenderable(endContent);
|
|
154
|
+
const shouldCompensateEndBlock =
|
|
155
|
+
endContentEdgeCompensation == null
|
|
156
|
+
? onOpenChange != null
|
|
157
|
+
: endContentEdgeCompensation === 'block' ||
|
|
158
|
+
endContentEdgeCompensation === 'all';
|
|
159
|
+
const shouldCompensateEndInline =
|
|
160
|
+
endContentEdgeCompensation == null
|
|
161
|
+
? onOpenChange != null
|
|
162
|
+
: endContentEdgeCompensation === 'inline' ||
|
|
163
|
+
endContentEdgeCompensation === 'all';
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<LayoutHeader
|
|
167
|
+
ref={ref}
|
|
168
|
+
hasDivider={hasDivider}
|
|
169
|
+
xstyle={xstyle}
|
|
170
|
+
className={className}
|
|
171
|
+
style={style}
|
|
172
|
+
{...rest}>
|
|
173
|
+
<div
|
|
174
|
+
{...mergeProps(
|
|
175
|
+
themeProps('drawer-header'),
|
|
176
|
+
stylex.props(styles.container),
|
|
177
|
+
)}>
|
|
178
|
+
{hasStartContent && (
|
|
179
|
+
<div
|
|
180
|
+
{...mergeProps(
|
|
181
|
+
themeProps('drawer-header-start-content'),
|
|
182
|
+
stylex.props(styles.actions),
|
|
183
|
+
)}>
|
|
184
|
+
{startContent}
|
|
185
|
+
</div>
|
|
186
|
+
)}
|
|
187
|
+
<div
|
|
188
|
+
{...mergeProps(
|
|
189
|
+
themeProps('drawer-header-title-block'),
|
|
190
|
+
stylex.props(styles.titleWrapper),
|
|
191
|
+
)}>
|
|
192
|
+
<Heading level={2}>{title}</Heading>
|
|
193
|
+
{hasSubtitle && (
|
|
194
|
+
<Text type="body" size="sm" color="secondary">
|
|
195
|
+
{subtitle}
|
|
196
|
+
</Text>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
{(hasEndContent || onOpenChange != null) && (
|
|
200
|
+
<div
|
|
201
|
+
{...mergeProps(
|
|
202
|
+
themeProps('drawer-header-end-content'),
|
|
203
|
+
stylex.props(
|
|
204
|
+
styles.actions,
|
|
205
|
+
shouldCompensateEndBlock && styles.endBlockEdgeCompensation,
|
|
206
|
+
shouldCompensateEndInline && styles.endInlineEdgeCompensation,
|
|
207
|
+
),
|
|
208
|
+
)}>
|
|
209
|
+
{endContent}
|
|
210
|
+
{onOpenChange && (
|
|
211
|
+
<Button
|
|
212
|
+
variant="ghost"
|
|
213
|
+
label={t('@astryx.dialog.close')}
|
|
214
|
+
tooltip={t('@astryx.dialog.close')}
|
|
215
|
+
icon={
|
|
216
|
+
<Icon
|
|
217
|
+
icon="close"
|
|
218
|
+
color="inherit"
|
|
219
|
+
{...themeProps('drawer-header-close-icon')}
|
|
220
|
+
/>
|
|
221
|
+
}
|
|
222
|
+
onClick={() => {
|
|
223
|
+
onOpenChange(false);
|
|
224
|
+
}}
|
|
225
|
+
isIconOnly
|
|
226
|
+
/>
|
|
227
|
+
)}
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
</LayoutHeader>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
DrawerHeader.displayName = 'DrawerHeader';
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file DrawerAudit.a11y.chromium.spec.ts
|
|
5
|
+
* @input Exact-head Core/Drawer Storybook fixtures in light/dark, neutral/custom themes, LTR/RTL, wide/narrow, modal/non-modal, purpose, padding, and stacked states
|
|
6
|
+
* @output Bounded screenshots and fail-closed sensor receipts for the Drawer whole-component audit
|
|
7
|
+
* @position Component-owned Night Watch browser evidence; CI runs it only for a Drawer component-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('test-results/drawer-audit-evidence');
|
|
23
|
+
const WIDE = {width: 1024, height: 768};
|
|
24
|
+
const NARROW = {width: 320, height: 720};
|
|
25
|
+
|
|
26
|
+
type Mode = 'light' | 'dark';
|
|
27
|
+
type Direction = 'ltr' | 'rtl';
|
|
28
|
+
type Theme = 'neutral' | 'butter';
|
|
29
|
+
|
|
30
|
+
type Case = {
|
|
31
|
+
key: string;
|
|
32
|
+
story: string;
|
|
33
|
+
buttons: string[];
|
|
34
|
+
mode: Mode;
|
|
35
|
+
direction: Direction;
|
|
36
|
+
theme: Theme;
|
|
37
|
+
viewport: {width: number; height: number};
|
|
38
|
+
expected: {
|
|
39
|
+
count?: number;
|
|
40
|
+
side?: 'start' | 'end';
|
|
41
|
+
modal?: boolean;
|
|
42
|
+
role?: 'dialog' | 'alertdialog';
|
|
43
|
+
narrowCoverage?: 'reveal' | 'full';
|
|
44
|
+
motionDirection?: Direction;
|
|
45
|
+
paddingPx?: number;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const cases: Case[] = [];
|
|
50
|
+
function addPair(
|
|
51
|
+
key: string,
|
|
52
|
+
story: string,
|
|
53
|
+
buttons: string[],
|
|
54
|
+
expected: Case['expected'],
|
|
55
|
+
options: Partial<Pick<Case, 'direction' | 'theme' | 'viewport'>> = {},
|
|
56
|
+
) {
|
|
57
|
+
for (const mode of ['light', 'dark'] as const) {
|
|
58
|
+
cases.push({
|
|
59
|
+
key: `${key}-${mode}`,
|
|
60
|
+
story,
|
|
61
|
+
buttons,
|
|
62
|
+
mode,
|
|
63
|
+
direction: options.direction ?? 'ltr',
|
|
64
|
+
theme: options.theme ?? 'neutral',
|
|
65
|
+
viewport: options.viewport ?? WIDE,
|
|
66
|
+
expected,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
addPair('modal-end', 'showcase', ['Open inspector'], {
|
|
72
|
+
side: 'end',
|
|
73
|
+
modal: true,
|
|
74
|
+
role: 'dialog',
|
|
75
|
+
});
|
|
76
|
+
addPair(
|
|
77
|
+
'modal-end-custom-theme',
|
|
78
|
+
'showcase',
|
|
79
|
+
['Open inspector'],
|
|
80
|
+
{side: 'end', modal: true, role: 'dialog'},
|
|
81
|
+
{theme: 'butter'},
|
|
82
|
+
);
|
|
83
|
+
addPair('nonmodal-end', 'row-inspector', ['Open drawer'], {
|
|
84
|
+
side: 'end',
|
|
85
|
+
modal: false,
|
|
86
|
+
role: 'dialog',
|
|
87
|
+
});
|
|
88
|
+
addPair('start-ltr', 'sides', ['Open from start'], {
|
|
89
|
+
side: 'start',
|
|
90
|
+
modal: true,
|
|
91
|
+
role: 'dialog',
|
|
92
|
+
});
|
|
93
|
+
addPair(
|
|
94
|
+
'end-ambient-rtl',
|
|
95
|
+
'sides',
|
|
96
|
+
['Open from end'],
|
|
97
|
+
{
|
|
98
|
+
side: 'end',
|
|
99
|
+
modal: true,
|
|
100
|
+
role: 'dialog',
|
|
101
|
+
motionDirection: 'rtl',
|
|
102
|
+
},
|
|
103
|
+
{direction: 'rtl'},
|
|
104
|
+
);
|
|
105
|
+
addPair(
|
|
106
|
+
'start-self-rtl',
|
|
107
|
+
'self-directed-rtl-start',
|
|
108
|
+
['Open self-directed RTL start drawer'],
|
|
109
|
+
{side: 'start', modal: true, role: 'dialog', motionDirection: 'rtl'},
|
|
110
|
+
);
|
|
111
|
+
addPair(
|
|
112
|
+
'end-self-rtl',
|
|
113
|
+
'self-directed-rtl-end',
|
|
114
|
+
['Open self-directed RTL end drawer'],
|
|
115
|
+
{side: 'end', modal: true, role: 'dialog', motionDirection: 'rtl'},
|
|
116
|
+
);
|
|
117
|
+
addPair(
|
|
118
|
+
'nested-ltr-override',
|
|
119
|
+
'nested-ltr-override',
|
|
120
|
+
['Open nested LTR drawer'],
|
|
121
|
+
{side: 'end', modal: true, role: 'dialog', motionDirection: 'ltr'},
|
|
122
|
+
{direction: 'rtl'},
|
|
123
|
+
);
|
|
124
|
+
addPair(
|
|
125
|
+
'mobile-reveal',
|
|
126
|
+
'mobile-width',
|
|
127
|
+
['56px reveal on mobile'],
|
|
128
|
+
{
|
|
129
|
+
side: 'end',
|
|
130
|
+
modal: true,
|
|
131
|
+
role: 'dialog',
|
|
132
|
+
narrowCoverage: 'reveal',
|
|
133
|
+
},
|
|
134
|
+
{viewport: NARROW},
|
|
135
|
+
);
|
|
136
|
+
addPair(
|
|
137
|
+
'mobile-full',
|
|
138
|
+
'mobile-width',
|
|
139
|
+
['Full width on mobile'],
|
|
140
|
+
{side: 'end', modal: true, role: 'dialog', narrowCoverage: 'full'},
|
|
141
|
+
{viewport: NARROW},
|
|
142
|
+
);
|
|
143
|
+
addPair('purpose-form', 'purpose', ['form'], {
|
|
144
|
+
side: 'end',
|
|
145
|
+
modal: true,
|
|
146
|
+
role: 'dialog',
|
|
147
|
+
});
|
|
148
|
+
addPair('purpose-required', 'purpose', ['required'], {
|
|
149
|
+
side: 'end',
|
|
150
|
+
modal: true,
|
|
151
|
+
role: 'alertdialog',
|
|
152
|
+
});
|
|
153
|
+
addPair('themed-padding', 'themed-padding', ['Open themed drawer'], {
|
|
154
|
+
side: 'end',
|
|
155
|
+
modal: true,
|
|
156
|
+
role: 'dialog',
|
|
157
|
+
paddingPx: 24,
|
|
158
|
+
});
|
|
159
|
+
addPair('stacked', 'stacked-drawers', ['Open order', 'Open line item'], {
|
|
160
|
+
count: 2,
|
|
161
|
+
modal: false,
|
|
162
|
+
role: 'dialog',
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test.describe.configure({mode: 'serial', retries: 0});
|
|
166
|
+
let server: StaticServer;
|
|
167
|
+
let checkoutSha: string;
|
|
168
|
+
let storybookSha: string;
|
|
169
|
+
|
|
170
|
+
function storyUrl(item: Case) {
|
|
171
|
+
return `${server.origin}/iframe.html?id=core-drawer--${item.story}&viewMode=story&globals=colorMode:${item.mode};astryxTheme:${item.theme};direction:${item.direction}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function settle(page: Page) {
|
|
175
|
+
await holdMotionStill(page);
|
|
176
|
+
await page.evaluate(async () => document.fonts.ready);
|
|
177
|
+
await page.waitForTimeout(40);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function readClosedTranslateX(page: Page): Promise<number | null> {
|
|
181
|
+
return page.locator('dialog.astryx-drawer').evaluate(dialog => {
|
|
182
|
+
const element = dialog as HTMLElement;
|
|
183
|
+
const previous = element.style.display;
|
|
184
|
+
element.style.display = 'flex';
|
|
185
|
+
const transform = getComputedStyle(element).transform;
|
|
186
|
+
element.style.display = previous;
|
|
187
|
+
if (transform === 'none') {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
return new DOMMatrixReadOnly(transform).m41;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
test.beforeAll(async () => {
|
|
195
|
+
fs.rmSync(OUTPUT, {recursive: true, force: true});
|
|
196
|
+
fs.mkdirSync(OUTPUT, {recursive: true});
|
|
197
|
+
checkoutSha = execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
198
|
+
encoding: 'utf8',
|
|
199
|
+
}).trim();
|
|
200
|
+
const expected = process.env.ASTRYX_HEAD_SHA;
|
|
201
|
+
if (expected && expected !== checkoutSha) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`checkout ${checkoutSha} does not match expected head ${expected}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
server = await serveStorybook(
|
|
207
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
208
|
+
);
|
|
209
|
+
const response = await fetch(`${server.origin}/astryx-build-sha.txt`);
|
|
210
|
+
if (!response.ok) {
|
|
211
|
+
throw new Error(`Storybook build has no SHA stamp (${response.status})`);
|
|
212
|
+
}
|
|
213
|
+
storybookSha = (await response.text()).trim();
|
|
214
|
+
if (storybookSha !== checkoutSha) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Storybook ${storybookSha} does not match checkout ${checkoutSha}`,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test.afterAll(async () => {
|
|
222
|
+
await server?.close();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
for (const item of cases) {
|
|
226
|
+
test(item.key, async ({browser}) => {
|
|
227
|
+
const context = await browser.newContext({
|
|
228
|
+
viewport: item.viewport,
|
|
229
|
+
colorScheme: item.mode,
|
|
230
|
+
reducedMotion: 'reduce',
|
|
231
|
+
});
|
|
232
|
+
const page = await context.newPage();
|
|
233
|
+
const pageErrors: string[] = [];
|
|
234
|
+
page.on('pageerror', error => pageErrors.push(String(error)));
|
|
235
|
+
await page.goto(storyUrl(item), {waitUntil: 'load'});
|
|
236
|
+
await page.locator('#storybook-root').waitFor();
|
|
237
|
+
await settle(page);
|
|
238
|
+
|
|
239
|
+
const closedTranslateX = item.expected.motionDirection
|
|
240
|
+
? await readClosedTranslateX(page)
|
|
241
|
+
: null;
|
|
242
|
+
if (item.expected.motionDirection) {
|
|
243
|
+
const ltrSign = item.expected.side === 'start' ? -1 : 1;
|
|
244
|
+
const directionSign = item.expected.motionDirection === 'rtl' ? -1 : 1;
|
|
245
|
+
expect(
|
|
246
|
+
(closedTranslateX ?? 0) * ltrSign * directionSign,
|
|
247
|
+
'closed drawer moves toward its resolved logical edge',
|
|
248
|
+
).toBeGreaterThan(0);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const label of item.buttons) {
|
|
252
|
+
await page.getByRole('button', {name: label, exact: true}).click();
|
|
253
|
+
await settle(page);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const drawers = page.locator('dialog.astryx-drawer:visible');
|
|
257
|
+
const expectedCount = item.expected.count ?? 1;
|
|
258
|
+
await expect(drawers).toHaveCount(expectedCount);
|
|
259
|
+
const drawer = drawers.last();
|
|
260
|
+
const box = await drawer.boundingBox();
|
|
261
|
+
if (box == null) {
|
|
262
|
+
throw new Error(`${item.key}: visible drawer has no geometry`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const observed = await drawer.evaluate(element => {
|
|
266
|
+
const style = getComputedStyle(element);
|
|
267
|
+
const backdrop = getComputedStyle(element, '::backdrop');
|
|
268
|
+
const content = element.firstElementChild;
|
|
269
|
+
const contentStyle = content ? getComputedStyle(content) : null;
|
|
270
|
+
return {
|
|
271
|
+
role: element.getAttribute('role') ?? 'dialog',
|
|
272
|
+
name: element.getAttribute('aria-label'),
|
|
273
|
+
modal: element.getAttribute('aria-modal') === 'true',
|
|
274
|
+
side: element.getAttribute('data-side'),
|
|
275
|
+
direction: style.direction,
|
|
276
|
+
backgroundColor: style.backgroundColor,
|
|
277
|
+
color: style.color,
|
|
278
|
+
transform: style.transform,
|
|
279
|
+
backdropColor: backdrop.backgroundColor,
|
|
280
|
+
contentPaddingInlineStart: contentStyle?.paddingInlineStart ?? null,
|
|
281
|
+
contentPaddingInlineEnd: contentStyle?.paddingInlineEnd ?? null,
|
|
282
|
+
contentOverflowY: contentStyle?.overflowY ?? null,
|
|
283
|
+
};
|
|
284
|
+
});
|
|
285
|
+
const environment = await page.evaluate(() => ({
|
|
286
|
+
theme: document
|
|
287
|
+
.querySelector('[data-astryx-theme]')
|
|
288
|
+
?.getAttribute('data-astryx-theme'),
|
|
289
|
+
mode: document.documentElement.getAttribute('data-theme'),
|
|
290
|
+
viewport: {width: innerWidth, height: innerHeight},
|
|
291
|
+
reducedMotion: matchMedia('(prefers-reduced-motion: reduce)').matches,
|
|
292
|
+
horizontalOverflow: document.documentElement.scrollWidth > innerWidth + 1,
|
|
293
|
+
fonts: document.fonts.status,
|
|
294
|
+
}));
|
|
295
|
+
|
|
296
|
+
expect(pageErrors).toEqual([]);
|
|
297
|
+
expect(environment.theme).toBe(item.theme);
|
|
298
|
+
expect(environment.mode).toBe(item.mode);
|
|
299
|
+
expect(environment.viewport).toEqual(item.viewport);
|
|
300
|
+
expect(environment.reducedMotion).toBe(true);
|
|
301
|
+
expect(environment.horizontalOverflow).toBe(false);
|
|
302
|
+
expect(environment.fonts).toBe('loaded');
|
|
303
|
+
expect(observed.side).toBe(item.expected.side ?? observed.side);
|
|
304
|
+
expect(observed.modal).toBe(item.expected.modal);
|
|
305
|
+
expect(observed.role).toBe(item.expected.role);
|
|
306
|
+
expect(observed.transform).toBe('matrix(1, 0, 0, 1, 0, 0)');
|
|
307
|
+
expect(observed.contentOverflowY).toBe('auto');
|
|
308
|
+
|
|
309
|
+
const expectedDirection = item.expected.motionDirection ?? item.direction;
|
|
310
|
+
expect(observed.direction).toBe(expectedDirection);
|
|
311
|
+
if (item.expected.side === 'start' && observed.direction === 'ltr') {
|
|
312
|
+
expect(box.x).toBeLessThanOrEqual(0.5);
|
|
313
|
+
}
|
|
314
|
+
if (item.expected.side === 'start' && observed.direction === 'rtl') {
|
|
315
|
+
expect(box.x + box.width).toBeGreaterThanOrEqual(
|
|
316
|
+
item.viewport.width - 0.5,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
if (item.expected.side === 'end' && observed.direction === 'ltr') {
|
|
320
|
+
expect(box.x + box.width).toBeGreaterThanOrEqual(
|
|
321
|
+
item.viewport.width - 0.5,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if (item.expected.side === 'end' && observed.direction === 'rtl') {
|
|
325
|
+
expect(box.x).toBeLessThanOrEqual(0.5);
|
|
326
|
+
}
|
|
327
|
+
if (item.expected.narrowCoverage === 'reveal') {
|
|
328
|
+
expect(box.width).toBeCloseTo(item.viewport.width - 56, 0);
|
|
329
|
+
}
|
|
330
|
+
if (item.expected.narrowCoverage === 'full') {
|
|
331
|
+
expect(box.width).toBeCloseTo(item.viewport.width, 0);
|
|
332
|
+
}
|
|
333
|
+
if (item.expected.paddingPx != null) {
|
|
334
|
+
expect(Number.parseFloat(observed.contentPaddingInlineStart ?? '')).toBe(
|
|
335
|
+
item.expected.paddingPx,
|
|
336
|
+
);
|
|
337
|
+
expect(Number.parseFloat(observed.contentPaddingInlineEnd ?? '')).toBe(
|
|
338
|
+
item.expected.paddingPx,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const png = await page.screenshot({animations: 'disabled'});
|
|
343
|
+
expect(png.length).toBeGreaterThan(100);
|
|
344
|
+
const image = `${item.key}.png`;
|
|
345
|
+
const receipt = {
|
|
346
|
+
schemaVersion: 1,
|
|
347
|
+
expected: {
|
|
348
|
+
build: checkoutSha,
|
|
349
|
+
storyId: `core-drawer--${item.story}`,
|
|
350
|
+
mode: item.mode,
|
|
351
|
+
direction: item.direction,
|
|
352
|
+
theme: item.theme,
|
|
353
|
+
viewport: item.viewport,
|
|
354
|
+
...item.expected,
|
|
355
|
+
},
|
|
356
|
+
observed: {
|
|
357
|
+
build: storybookSha,
|
|
358
|
+
drawerCount: expectedCount,
|
|
359
|
+
closedTranslateX,
|
|
360
|
+
geometry: box,
|
|
361
|
+
...observed,
|
|
362
|
+
...environment,
|
|
363
|
+
pageErrors,
|
|
364
|
+
},
|
|
365
|
+
image: {
|
|
366
|
+
file: image,
|
|
367
|
+
sha256: createHash('sha256').update(png).digest('hex'),
|
|
368
|
+
width: png.readUInt32BE(16),
|
|
369
|
+
height: png.readUInt32BE(20),
|
|
370
|
+
},
|
|
371
|
+
passed: true,
|
|
372
|
+
};
|
|
373
|
+
fs.writeFileSync(path.join(OUTPUT, image), png);
|
|
374
|
+
fs.writeFileSync(
|
|
375
|
+
path.join(OUTPUT, `${item.key}.sensors.json`),
|
|
376
|
+
`${JSON.stringify(receipt, null, 2)}\n`,
|
|
377
|
+
);
|
|
378
|
+
await context.close();
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
test('manifest is complete', async () => {
|
|
383
|
+
const receipts = fs
|
|
384
|
+
.readdirSync(OUTPUT)
|
|
385
|
+
.filter(file => file.endsWith('.sensors.json'));
|
|
386
|
+
expect(receipts).toHaveLength(cases.length);
|
|
387
|
+
const manifest = {
|
|
388
|
+
schemaVersion: 1,
|
|
389
|
+
component: 'core/Drawer',
|
|
390
|
+
build: checkoutSha,
|
|
391
|
+
frames: cases.map(item => ({
|
|
392
|
+
key: item.key,
|
|
393
|
+
image: `${item.key}.png`,
|
|
394
|
+
receipt: `${item.key}.sensors.json`,
|
|
395
|
+
})),
|
|
396
|
+
};
|
|
397
|
+
fs.writeFileSync(
|
|
398
|
+
path.join(OUTPUT, 'manifest.json'),
|
|
399
|
+
`${JSON.stringify(manifest, null, 2)}\n`,
|
|
400
|
+
);
|
|
401
|
+
});
|