@astryxdesign/core 0.4.0 → 0.4.1-canary.1ce0b4c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +1 -2
- package/dist/AspectRatio/index.d.ts.map +1 -1
- package/dist/AspectRatio/index.js +1 -2
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +1 -2
- package/dist/Badge/index.d.ts.map +1 -1
- package/dist/Badge/index.js +1 -2
- package/dist/Banner/Banner.d.ts +3 -0
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +17 -3
- package/dist/Blockquote/Blockquote.d.ts.map +1 -1
- package/dist/Blockquote/Blockquote.js +1 -2
- package/dist/Blockquote/index.d.ts.map +1 -1
- package/dist/Blockquote/index.js +1 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -9
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +1 -2
- package/dist/Card/index.d.ts.map +1 -1
- package/dist/Card/index.js +1 -2
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +1 -2
- package/dist/Center/index.d.ts.map +1 -1
- package/dist/Center/index.js +1 -2
- package/dist/Code/Code.d.ts.map +1 -1
- package/dist/Code/Code.js +1 -2
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +5 -0
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +7 -8
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +23 -10
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +44 -16
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +32 -13
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +10 -3
- package/dist/Field/PanelSearchInput.d.ts +81 -0
- package/dist/Field/PanelSearchInput.d.ts.map +1 -0
- package/dist/Field/PanelSearchInput.js +151 -0
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +21 -10
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +1 -2
- package/dist/Grid/index.d.ts.map +1 -1
- package/dist/Grid/index.js +1 -2
- package/dist/HoverCard/HoverCard.d.ts +1 -1
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +5 -13
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +6 -5
- package/dist/Icon/globalIconRegistry.d.ts +5 -4
- package/dist/Icon/globalIconRegistry.d.ts.map +1 -1
- package/dist/Icon/globalIconRegistry.js +5 -4
- package/dist/Indicator/index.d.ts +1 -1
- package/dist/Indicator/index.d.ts.map +1 -1
- package/dist/Indicator/types.d.ts +10 -0
- package/dist/Indicator/types.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +7 -2
- package/dist/Layer/layerHost.d.ts +24 -0
- package/dist/Layer/layerHost.d.ts.map +1 -0
- package/dist/Layer/layerHost.js +79 -0
- package/dist/Layer/useLayer.d.ts +14 -6
- package/dist/Layer/useLayer.d.ts.map +1 -1
- package/dist/Layer/useLayer.js +155 -29
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +24 -50
- package/dist/MultiSelector/MultiSelector.d.ts +8 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +134 -102
- package/dist/NavItem/navItemStyles.stylex.d.ts +17 -5
- package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
- package/dist/NavItem/navItemStyles.stylex.js +11 -5
- package/dist/Popover/usePopover.d.ts +15 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +14 -2
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +2 -3
- package/dist/Section/Section.d.ts.map +1 -1
- package/dist/Section/Section.js +1 -2
- package/dist/Section/index.d.ts.map +1 -1
- package/dist/Section/index.js +1 -2
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +135 -95
- package/dist/SideNav/SideNav.d.ts +7 -9
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +32 -5
- package/dist/SideNav/SideNavCollapseButton.d.ts +25 -9
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.js +39 -15
- package/dist/SideNav/SideNavCollapseContext.d.ts +21 -0
- package/dist/SideNav/SideNavCollapseContext.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseContext.js +16 -2
- package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
- package/dist/SideNav/SideNavHeading.js +70 -24
- package/dist/SideNav/SideNavItem.d.ts +7 -2
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +114 -75
- package/dist/SideNav/SideNavSection.d.ts.map +1 -1
- package/dist/SideNav/SideNavSection.js +7 -16
- package/dist/SideNav/index.d.ts +1 -1
- package/dist/SideNav/index.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +1 -2
- package/dist/Skeleton/index.d.ts.map +1 -1
- package/dist/Skeleton/index.js +1 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +2 -6
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +3 -3
- package/dist/Table/TableRow.d.ts +1 -1
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +4 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +7 -2
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +14 -3
- package/dist/Timestamp/formatInstant.d.ts +11 -1
- package/dist/Timestamp/formatInstant.d.ts.map +1 -1
- package/dist/Timestamp/formatInstant.js +3 -1
- package/dist/TopNav/TopNavMegaMenu.js +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +2 -2
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -1
- package/dist/VisuallyHidden/VisuallyHidden.js +1 -2
- package/dist/VisuallyHidden/index.d.ts.map +1 -1
- package/dist/VisuallyHidden/index.js +1 -2
- package/dist/astryx.css +23 -24
- package/dist/astryx.umd.js +48 -48
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +16 -2
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
- package/dist/hooks/useInputStatusIcon.js +17 -4
- package/dist/theme/defineTheme.d.ts +2 -2
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +2 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/tokens.stylex.d.ts +12 -0
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +23 -0
- package/dist/utils/focusOutline.stylex.d.ts +34 -19
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.js +46 -33
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -1
- package/dist/utils/interactionModality.d.ts +34 -0
- package/dist/utils/interactionModality.d.ts.map +1 -0
- package/dist/utils/interactionModality.js +70 -0
- package/locales/en.json +44 -0
- package/locales/pseudo.json +33 -0
- package/package.json +3 -3
- package/src/AppShell/AppShell.doc.mjs +3 -0
- package/src/AspectRatio/AspectRatio.tsx +0 -2
- package/src/AspectRatio/index.ts +0 -2
- package/src/Badge/Badge.tsx +0 -2
- package/src/Badge/index.ts +0 -2
- package/src/Banner/Banner.test.tsx +58 -1
- package/src/Banner/Banner.tsx +24 -5
- package/src/Blockquote/Blockquote.tsx +0 -2
- package/src/Blockquote/index.ts +0 -2
- package/src/Button/Button.doc.mjs +2 -2
- package/src/Button/Button.tsx +10 -7
- package/src/ButtonGroup/ButtonGroup.test.tsx +7 -4
- package/src/Card/Card.tsx +0 -2
- package/src/Card/index.ts +0 -2
- package/src/Center/Center.tsx +0 -2
- package/src/Center/index.ts +0 -2
- package/src/CheckboxInput/CheckboxInput.test.tsx +8 -3
- package/src/Code/Code.tsx +0 -2
- package/src/CommandPalette/CommandPalette.test.tsx +54 -0
- package/src/CommandPalette/CommandPalette.tsx +5 -0
- package/src/ComplexSelector/ComplexSelector.test.tsx +15 -6
- package/src/ComplexSelector/ComplexSelector.tsx +6 -10
- package/src/DateInput/DateInput.test.tsx +19 -0
- package/src/DateInput/DateInput.tsx +3 -6
- package/src/DateRangeInput/DateRangeInput.tsx +3 -9
- package/src/DateTimeInput/DateTimeInput.test.tsx +79 -1
- package/src/DateTimeInput/DateTimeInput.tsx +23 -9
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +2 -1
- package/src/Field/PanelSearchInput.tsx +271 -0
- package/src/FileInput/FileInput.tsx +21 -8
- package/src/Grid/Grid.tsx +0 -2
- package/src/Grid/index.ts +0 -2
- package/src/HoverCard/HoverCard.doc.mjs +3 -3
- package/src/HoverCard/HoverCard.test.tsx +283 -55
- package/src/HoverCard/HoverCard.tsx +5 -13
- package/src/HoverCard/useHoverCard.tsx +8 -11
- package/src/Icon/globalIconRegistry.tsx +5 -4
- package/src/Indicator/Indicator.doc.mjs +1 -1
- package/src/Indicator/index.ts +1 -0
- package/src/Indicator/types.ts +11 -0
- package/src/InputGroup/groupStyles.ts +7 -8
- package/src/Layer/layerHost.test.ts +99 -0
- package/src/Layer/layerHost.ts +141 -0
- package/src/Layer/useLayer.doc.mjs +14 -4
- package/src/Layer/useLayer.test.tsx +318 -7
- package/src/Layer/useLayer.tsx +229 -35
- package/src/Lightbox/Lightbox.tsx +2 -11
- package/src/MultiSelector/MultiSelector.doc.mjs +9 -0
- package/src/MultiSelector/MultiSelector.test.tsx +135 -5
- package/src/MultiSelector/MultiSelector.tsx +170 -127
- package/src/NavItem/navItemStyles.stylex.ts +31 -5
- package/src/Popover/Popover.doc.mjs +2 -0
- package/src/Popover/usePopover.tsx +37 -4
- package/src/ProgressBar/ProgressBar.tsx +2 -8
- package/src/Section/Section.tsx +0 -2
- package/src/Section/index.ts +0 -2
- package/src/Selector/Selector.doc.mjs +10 -0
- package/src/Selector/Selector.test.tsx +386 -2
- package/src/Selector/Selector.tsx +182 -122
- package/src/SideNav/SideNav.doc.mjs +13 -4
- package/src/SideNav/SideNav.test.tsx +771 -2
- package/src/SideNav/SideNav.tsx +37 -16
- package/src/SideNav/SideNavCollapseButton.doc.mjs +28 -6
- package/src/SideNav/SideNavCollapseButton.tsx +67 -15
- package/src/SideNav/SideNavCollapseContext.ts +29 -9
- package/src/SideNav/SideNavHeading.tsx +28 -10
- package/src/SideNav/SideNavItem.doc.mjs +13 -0
- package/src/SideNav/SideNavItem.tsx +92 -84
- package/src/SideNav/SideNavSection.tsx +6 -20
- package/src/SideNav/index.ts +2 -0
- package/src/Skeleton/Skeleton.tsx +0 -2
- package/src/Skeleton/index.ts +0 -2
- package/src/Slider/Slider.tsx +2 -11
- package/src/Switch/Switch.tsx +10 -2
- package/src/Table/Table.test.tsx +31 -0
- package/src/Table/TableRow.tsx +6 -0
- package/src/Text/Text.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.test.tsx +54 -0
- package/src/TimeInput/TimeInput.tsx +17 -2
- package/src/Timestamp/Timestamp.test.tsx +125 -30
- package/src/Timestamp/Timestamp.tsx +14 -2
- package/src/Timestamp/formatInstant.ts +16 -1
- package/src/Toolbar/Toolbar.test.tsx +14 -14
- package/src/TopNav/TopNavMegaMenu.doc.mjs +5 -0
- package/src/TopNav/TopNavMegaMenu.test.tsx +43 -0
- package/src/TopNav/TopNavMegaMenu.tsx +4 -1
- package/src/TopNav/TopNavMegaMenuItem.tsx +1 -1
- package/src/TopNav/TopNavMenu.test.tsx +41 -0
- package/src/TopNav/TopNavMenu.tsx +8 -2
- package/src/TreeList/TreeList.test.tsx +3 -2
- package/src/VisuallyHidden/VisuallyHidden.tsx +0 -2
- package/src/VisuallyHidden/index.ts +0 -2
- package/src/hooks/index.ts +5 -1
- package/src/hooks/useFocusTrap.test.tsx +16 -0
- package/src/hooks/useFocusTrap.ts +20 -2
- package/src/hooks/useInputStatusIcon.tsx +8 -8
- package/src/serverSafeComponents.test.ts +665 -0
- package/src/theme/defineTheme.ts +3 -0
- package/src/theme/generateThemeRules.test.ts +17 -0
- package/src/theme/index.ts +2 -0
- package/src/theme/themingTargets.test.ts +152 -26
- package/src/theme/tokens.stylex.ts +18 -0
- package/src/theme/tokens.test.ts +37 -0
- package/src/utils/focusOutline.stylex.ts +37 -23
- package/src/utils/index.ts +4 -0
- package/src/utils/interactionModality.ts +74 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file interactionModality.ts
|
|
5
|
+
* @input Global pointerdown/keydown events
|
|
6
|
+
* @output `getInteractionModality()` — how the user last interacted
|
|
7
|
+
* @position Internal utility; used where `:focus-visible` alone is too broad
|
|
8
|
+
*
|
|
9
|
+
* `:focus-visible` is the right selector for a focus ring and should stay the
|
|
10
|
+
* condition that draws one. It is not, on its own, "focused by keyboard":
|
|
11
|
+
* per CSS Selectors 4 a pointer-focused element that supports text entry
|
|
12
|
+
* matches it too, deliberately, so that a clicked text field still shows where
|
|
13
|
+
* typing will go. Measured in Chromium — a bare `<input>` clicked with a mouse
|
|
14
|
+
* matches `:focus-visible`.
|
|
15
|
+
*
|
|
16
|
+
* A component that wants a ring for keyboard focus ONLY therefore needs one bit
|
|
17
|
+
* `:focus-visible` cannot give it: which device moved focus. This records that,
|
|
18
|
+
* to be used as a gate ALONGSIDE `:focus-visible`, never as a replacement for
|
|
19
|
+
* it — the browser's heuristic still decides everything else, including
|
|
20
|
+
* `focus({focusVisible: true})`.
|
|
21
|
+
*
|
|
22
|
+
* Listeners are registered once for the document, in the capture phase so they
|
|
23
|
+
* run before the focus they explain, and are never removed: two passive
|
|
24
|
+
* listeners for the whole app, versus a pair per mounted input.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
// Default to keyboard: with no interaction yet, focus arrived programmatically
|
|
28
|
+
// or from the browser restoring it, and showing a ring is the safe error.
|
|
29
|
+
let modality = 'keyboard';
|
|
30
|
+
let isListening = false;
|
|
31
|
+
function onPointerDown() {
|
|
32
|
+
modality = 'pointer';
|
|
33
|
+
}
|
|
34
|
+
function onKeyDown(event) {
|
|
35
|
+
// Modifier-only presses are not navigation — holding Shift before a click
|
|
36
|
+
// must not turn that click into "keyboard".
|
|
37
|
+
if (event.metaKey || event.altKey || event.ctrlKey) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
modality = 'keyboard';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Start tracking, once per document. Safe to call repeatedly and on the server
|
|
45
|
+
* (where it does nothing).
|
|
46
|
+
*/
|
|
47
|
+
export function trackInteractionModality() {
|
|
48
|
+
if (isListening || typeof document === 'undefined') {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
isListening = true;
|
|
52
|
+
document.addEventListener('pointerdown', onPointerDown, {
|
|
53
|
+
capture: true,
|
|
54
|
+
passive: true
|
|
55
|
+
});
|
|
56
|
+
document.addEventListener('keydown', onKeyDown, {
|
|
57
|
+
capture: true,
|
|
58
|
+
passive: true
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** How the user last interacted with the page. */
|
|
63
|
+
export function getInteractionModality() {
|
|
64
|
+
return modality;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Test-only: restore the initial state between cases. */
|
|
68
|
+
export function __resetInteractionModalityForTest() {
|
|
69
|
+
modality = 'keyboard';
|
|
70
|
+
}
|
package/locales/en.json
CHANGED
|
@@ -639,6 +639,10 @@
|
|
|
639
639
|
"defaultMessage": "Clear {label}",
|
|
640
640
|
"description": "Aria label for the clear-value button in DateInput / DateRangeInput / DateTimeInput. `{label}` is the field's own label so screen readers announce e.g. 'Clear Start date'."
|
|
641
641
|
},
|
|
642
|
+
"@astryx.dateInput.invalidDate": {
|
|
643
|
+
"defaultMessage": "Invalid date",
|
|
644
|
+
"description": "Screen-reader-only live-region alert in DateInput / DateTimeInput when the typed date cannot be parsed. The field silently reverts on blur, so this is the only rejection feedback a screen-reader user gets."
|
|
645
|
+
},
|
|
642
646
|
"@astryx.dateTimeInput.timeSuffix": {
|
|
643
647
|
"defaultMessage": "{label} time",
|
|
644
648
|
"description": "Screen-reader-only accessible name for the time-of-day slot in a DateTimeInput. Example: `Start date time`; reorder freely (e.g. \"time of {label}\")."
|
|
@@ -711,6 +715,10 @@
|
|
|
711
715
|
"defaultMessage": "Select a time",
|
|
712
716
|
"description": "\"Time\" = clock time (HH:MM), not duration or era. Grey placeholder inside an empty TimeInput field; imperative verb."
|
|
713
717
|
},
|
|
718
|
+
"@astryx.timeInput.invalidTime": {
|
|
719
|
+
"defaultMessage": "Invalid time",
|
|
720
|
+
"description": "Screen-reader-only live-region alert in TimeInput / DateTimeInput when the typed time cannot be parsed. \"Time\" = clock time (HH:MM). The field silently reverts on blur, so this is the only rejection feedback a screen-reader user gets."
|
|
721
|
+
},
|
|
714
722
|
"@astryx.topNav.heading.dialogLabel": {
|
|
715
723
|
"defaultMessage": "Navigation menu",
|
|
716
724
|
"description": "Screen-reader-only accessible name for the dropdown dialog opened from a TopNavHeading's overflow menu. Kept separate from the SideNav sibling."
|
|
@@ -807,6 +815,38 @@
|
|
|
807
815
|
"defaultMessage": "Clear {label}",
|
|
808
816
|
"description": "Screen-reader-only label on the X that removes the selected file from a FileInput. Example: `Clear Attachment`, `Clear Résumé`."
|
|
809
817
|
},
|
|
818
|
+
"@astryx.fileInput.dropHint": {
|
|
819
|
+
"defaultMessage": "Drop files here",
|
|
820
|
+
"description": "Text shown in the dropzone area when a user is dragging files over the FileInput."
|
|
821
|
+
},
|
|
822
|
+
"@astryx.fileInput.errorInvalidType": {
|
|
823
|
+
"defaultMessage": "\"{fileName}\" is not an accepted file type",
|
|
824
|
+
"description": "Validation error shown when a selected file does not match the accepted types."
|
|
825
|
+
},
|
|
826
|
+
"@astryx.fileInput.errorMaxFiles": {
|
|
827
|
+
"defaultMessage": "Maximum {maxFiles} files allowed",
|
|
828
|
+
"description": "Validation error shown when the number of selected files exceeds the maximum."
|
|
829
|
+
},
|
|
830
|
+
"@astryx.fileInput.errorMaxSize": {
|
|
831
|
+
"defaultMessage": "\"{fileName}\" exceeds {maxSize} limit",
|
|
832
|
+
"description": "Validation error shown when a selected file exceeds the maximum size. {maxSize} is a formatted size string like \"2.0 MB\"."
|
|
833
|
+
},
|
|
834
|
+
"@astryx.fileInput.fileSelected": {
|
|
835
|
+
"defaultMessage": "1 file selected: {fileName}",
|
|
836
|
+
"description": "Live-region announcement when a single file is successfully selected."
|
|
837
|
+
},
|
|
838
|
+
"@astryx.fileInput.filesSelected": {
|
|
839
|
+
"defaultMessage": "{count} files selected",
|
|
840
|
+
"description": "Live-region announcement when multiple files are successfully selected."
|
|
841
|
+
},
|
|
842
|
+
"@astryx.fileInput.placeholder": {
|
|
843
|
+
"defaultMessage": "Choose file",
|
|
844
|
+
"description": "Default placeholder text shown when no file is selected in a single-file FileInput."
|
|
845
|
+
},
|
|
846
|
+
"@astryx.fileInput.placeholderMultiple": {
|
|
847
|
+
"defaultMessage": "Choose files",
|
|
848
|
+
"description": "Default placeholder text shown when no files are selected in a multi-file FileInput."
|
|
849
|
+
},
|
|
810
850
|
"@astryx.fileInput.required": {
|
|
811
851
|
"defaultMessage": "Required",
|
|
812
852
|
"description": "Screen-reader-only description on the FileInput trigger indicating the field must be filled in. Mirrors the visible `Required` indicator next to the field label; conveyed via description because aria-required is not supported on role=\"button\"."
|
|
@@ -895,6 +935,10 @@
|
|
|
895
935
|
"defaultMessage": "Collapse {label}",
|
|
896
936
|
"description": "Screen-reader-only label on the same chevron when the group is expanded. Example: `Collapse Settings`. Pairs with `sideNavItem.expand`."
|
|
897
937
|
},
|
|
938
|
+
"@astryx.sideNavItem.submenuLabel": {
|
|
939
|
+
"defaultMessage": "{label} submenu",
|
|
940
|
+
"description": "Accessible name for the flyout dialog that opens from a collapsed (icon-only) SideNav item to show its sub-items. {label} is the parent item's label. Example: `Settings submenu`."
|
|
941
|
+
},
|
|
898
942
|
"@astryx.tableFiltering.filterByColumn": {
|
|
899
943
|
"defaultMessage": "Filter {header}",
|
|
900
944
|
"description": "Triple-use string on per-column filter controls: visible label, placeholder, AND aria label. `{header}` is the column header — example: `Filter Name`. Keep short."
|
package/locales/pseudo.json
CHANGED
|
@@ -479,6 +479,9 @@
|
|
|
479
479
|
"@astryx.dateInput.clear": {
|
|
480
480
|
"defaultMessage": "⟦Çłéàř {label}⟧"
|
|
481
481
|
},
|
|
482
|
+
"@astryx.dateInput.invalidDate": {
|
|
483
|
+
"defaultMessage": "⟦Íñṽàłíð ðàţé⟧"
|
|
484
|
+
},
|
|
482
485
|
"@astryx.dateTimeInput.timeSuffix": {
|
|
483
486
|
"defaultMessage": "⟦{label} ţíɱé⟧"
|
|
484
487
|
},
|
|
@@ -533,6 +536,9 @@
|
|
|
533
536
|
"@astryx.timeInput.placeholder": {
|
|
534
537
|
"defaultMessage": "⟦Šéłéçţ à ţíɱé⟧"
|
|
535
538
|
},
|
|
539
|
+
"@astryx.timeInput.invalidTime": {
|
|
540
|
+
"defaultMessage": "⟦Íñṽàłíð ţíɱé⟧"
|
|
541
|
+
},
|
|
536
542
|
"@astryx.topNav.heading.dialogLabel": {
|
|
537
543
|
"defaultMessage": "⟦Ñàṽíĝàţíóñ ɱéñú⟧"
|
|
538
544
|
},
|
|
@@ -605,6 +611,30 @@
|
|
|
605
611
|
"@astryx.fileInput.clearLabel": {
|
|
606
612
|
"defaultMessage": "⟦Çłéàř {label}⟧"
|
|
607
613
|
},
|
|
614
|
+
"@astryx.fileInput.dropHint": {
|
|
615
|
+
"defaultMessage": "⟦Ðřóþ ƒíłéš ĥéřé⟧"
|
|
616
|
+
},
|
|
617
|
+
"@astryx.fileInput.errorInvalidType": {
|
|
618
|
+
"defaultMessage": "⟦\"{fileName}\" íš ñóţ àñ àççéþţéð ƒíłé ţýþé⟧"
|
|
619
|
+
},
|
|
620
|
+
"@astryx.fileInput.errorMaxFiles": {
|
|
621
|
+
"defaultMessage": "⟦Ṁàẋíɱúɱ {maxFiles} ƒíłéš àłłóŵéð⟧"
|
|
622
|
+
},
|
|
623
|
+
"@astryx.fileInput.errorMaxSize": {
|
|
624
|
+
"defaultMessage": "⟦\"{fileName}\" éẋçééðš {maxSize} łíɱíţ⟧"
|
|
625
|
+
},
|
|
626
|
+
"@astryx.fileInput.fileSelected": {
|
|
627
|
+
"defaultMessage": "⟦1 ƒíłé šéłéçţéð: {fileName}⟧"
|
|
628
|
+
},
|
|
629
|
+
"@astryx.fileInput.filesSelected": {
|
|
630
|
+
"defaultMessage": "⟦{count} ƒíłéš šéłéçţéð⟧"
|
|
631
|
+
},
|
|
632
|
+
"@astryx.fileInput.placeholder": {
|
|
633
|
+
"defaultMessage": "⟦Çĥóóšé ƒíłé⟧"
|
|
634
|
+
},
|
|
635
|
+
"@astryx.fileInput.placeholderMultiple": {
|
|
636
|
+
"defaultMessage": "⟦Çĥóóšé ƒíłéš⟧"
|
|
637
|
+
},
|
|
608
638
|
"@astryx.fileInput.required": {
|
|
609
639
|
"defaultMessage": "⟦Řéɋúířéð⟧"
|
|
610
640
|
},
|
|
@@ -671,6 +701,9 @@
|
|
|
671
701
|
"@astryx.sideNavItem.collapse": {
|
|
672
702
|
"defaultMessage": "⟦Çółłàþšé {label}⟧"
|
|
673
703
|
},
|
|
704
|
+
"@astryx.sideNavItem.submenuLabel": {
|
|
705
|
+
"defaultMessage": "⟦{label} šúƀɱéñú⟧"
|
|
706
|
+
},
|
|
674
707
|
"@astryx.tableFiltering.filterByColumn": {
|
|
675
708
|
"defaultMessage": "⟦Ƒíłţéř {header}⟧"
|
|
676
709
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1-canary.1ce0b4c",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -654,6 +654,7 @@
|
|
|
654
654
|
"react-dom": ">=19.0.0"
|
|
655
655
|
},
|
|
656
656
|
"devDependencies": {
|
|
657
|
+
"@astryxdesign/cli": "0.4.1-canary.1ce0b4c",
|
|
657
658
|
"@babel/cli": "^8.0.4",
|
|
658
659
|
"@babel/core": "^7.29.7",
|
|
659
660
|
"@babel/preset-react": "^8.0.1",
|
|
@@ -662,8 +663,7 @@
|
|
|
662
663
|
"@testing-library/dom": "^10.0.0",
|
|
663
664
|
"@testing-library/jest-dom": "^6.6.0",
|
|
664
665
|
"@testing-library/react": "^16.3.2",
|
|
665
|
-
"rimraf": "^6.0.1"
|
|
666
|
-
"@astryxdesign/cli": "0.4.0"
|
|
666
|
+
"rimraf": "^6.0.1"
|
|
667
667
|
},
|
|
668
668
|
"dependencies": {
|
|
669
669
|
"intl-messageformat": "^11.2.9"
|
|
@@ -203,8 +203,11 @@ export const docsDense = {
|
|
|
203
203
|
bestPractices: [
|
|
204
204
|
{guidance: true, description: 'Choose the right height: use "fill" for dashboards with internal scrolling and "auto" for pages that grow with content.'},
|
|
205
205
|
{guidance: true, description: 'Set `contentPadding` based on content type: 4 for forms and settings, 0 for tables and dashboards.'},
|
|
206
|
+
{guidance: true, description: 'Give every nav slot an accessible name: TopNav and SideNav are separate navigation landmarks, so pass `label` to each.'},
|
|
207
|
+
{guidance: true, description: 'Start the page heading inside `children`: AppShell owns the skip link, banner, and main landmarks but renders no heading, so the first content heading is the page h1.'},
|
|
206
208
|
{guidance: false, description: "Nest one AppShell inside another; it's the outermost layout frame."},
|
|
207
209
|
{guidance: false, description: 'Use for sub-page layouts; use Layout for content areas within AppShell.'},
|
|
210
|
+
{guidance: false, description: 'Add your own skip link or <main>; AppShell renders both, and a second main landmark makes the first ambiguous.'},
|
|
208
211
|
],
|
|
209
212
|
},
|
|
210
213
|
propDescriptions: {
|
package/src/AspectRatio/index.ts
CHANGED
package/src/Badge/Badge.tsx
CHANGED
package/src/Badge/index.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file Banner.test.tsx
|
|
5
5
|
* @input Uses vitest, @testing-library/react, Banner component
|
|
6
|
-
* @output Unit tests for Banner component behavior
|
|
6
|
+
* @output Unit tests for Banner component behavior, including the
|
|
7
|
+
* 'banner-icon' theme target riding on the status icon glyph (#4166)
|
|
7
8
|
* @position Testing; validates Banner.tsx implementation
|
|
8
9
|
*
|
|
9
10
|
* SYNC: When modified, update this header
|
|
@@ -337,6 +338,62 @@ describe('Banner', () => {
|
|
|
337
338
|
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
338
339
|
});
|
|
339
340
|
|
|
341
|
+
// =========================================================================
|
|
342
|
+
// Status icon color theming (#4166)
|
|
343
|
+
// =========================================================================
|
|
344
|
+
|
|
345
|
+
it("carries the 'banner-icon' theme target on the default status icon glyph", () => {
|
|
346
|
+
// Theme overrides for 'banner-icon' + 'status:X' compile to
|
|
347
|
+
// '.astryx-banner-icon.<status>' (parseStyleKey). The target must sit on
|
|
348
|
+
// the <Icon> span itself so those same-element rules in
|
|
349
|
+
// @layer astryx-theme beat the Icon's own color variant.
|
|
350
|
+
const statuses = ['info', 'warning', 'error', 'success'] as const;
|
|
351
|
+
for (const status of statuses) {
|
|
352
|
+
const {container, unmount} = render(
|
|
353
|
+
<Banner status={status} title={`${status} banner`} />,
|
|
354
|
+
);
|
|
355
|
+
const glyph = container.querySelector(
|
|
356
|
+
`.astryx-icon.astryx-banner-icon.${status}`,
|
|
357
|
+
);
|
|
358
|
+
expect(glyph).not.toBeNull();
|
|
359
|
+
expect(glyph).toHaveAttribute('data-status', status);
|
|
360
|
+
// Exactly one element carries the target — the layout wrapper no
|
|
361
|
+
// longer does.
|
|
362
|
+
expect(container.querySelectorAll('.astryx-banner-icon')).toHaveLength(1);
|
|
363
|
+
unmount();
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('keeps the color variant on the theme-target element (regression pin for #4166)', () => {
|
|
368
|
+
// Pre-fix, '.astryx-banner-icon.info' matched the layout wrapper while
|
|
369
|
+
// the color variant (data-color="accent") sat on an inner span that a
|
|
370
|
+
// theme override could never reach. Target and paint now share one
|
|
371
|
+
// element.
|
|
372
|
+
const {container} = render(<Banner status="info" title="Info" />);
|
|
373
|
+
const target = container.querySelector('.astryx-banner-icon.info');
|
|
374
|
+
expect(target).toHaveAttribute('data-color', 'accent');
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it("keeps the 'banner-icon' target on the wrapper for a custom icon node", () => {
|
|
378
|
+
// Core never injects props into consumer elements, so with a custom
|
|
379
|
+
// `icon` the target stays on the (layout-only) wrapper and overrides
|
|
380
|
+
// reach the node via inheritance. The node itself is untouched.
|
|
381
|
+
const {container} = render(
|
|
382
|
+
<Banner
|
|
383
|
+
status="info"
|
|
384
|
+
title="Custom icon"
|
|
385
|
+
icon={<span data-testid="custom-glyph">i</span>}
|
|
386
|
+
/>,
|
|
387
|
+
);
|
|
388
|
+
const targets = container.querySelectorAll('.astryx-banner-icon');
|
|
389
|
+
expect(targets).toHaveLength(1);
|
|
390
|
+
expect(targets[0]?.tagName).toBe('DIV');
|
|
391
|
+
expect(targets[0]).toHaveAttribute('aria-hidden', 'true');
|
|
392
|
+
const custom = container.querySelector('[data-testid="custom-glyph"]');
|
|
393
|
+
expect(custom).not.toBeNull();
|
|
394
|
+
expect(custom?.className).toBe('');
|
|
395
|
+
});
|
|
396
|
+
|
|
340
397
|
// =========================================================================
|
|
341
398
|
// Icon registry integration
|
|
342
399
|
// =========================================================================
|
package/src/Banner/Banner.tsx
CHANGED
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
* - Root container: layout-only wrapper (flex column), no visual styling, no theme target
|
|
13
13
|
* - Header area (themeProps 'banner'): colored status background with icon, title, description, actions, dismiss
|
|
14
14
|
* - Content area (themeProps 'banner-content'): collapsible card background for additional content (children)
|
|
15
|
+
* - Status icon (themeProps 'banner-icon'): the target rides on the default
|
|
16
|
+
* <Icon> itself — the element that paints — so 'status:X' overrides reach
|
|
17
|
+
* the glyph (#4166); for a custom `icon` node it stays on the layout wrapper
|
|
15
18
|
* - No left border accent — color is expressed through the full header background
|
|
16
19
|
* - Each visual area owns its own border-radius (no overflow:clip on the container)
|
|
17
20
|
* - When children are provided, a collapse/expand toggle button appears in the end area
|
|
@@ -445,16 +448,32 @@ export function Banner({
|
|
|
445
448
|
: styles.headerCardStandalone),
|
|
446
449
|
),
|
|
447
450
|
)}>
|
|
451
|
+
{/* The 'banner-icon' target rides on the element that paints: the
|
|
452
|
+
default <Icon> below, or this wrapper when a custom `icon` node
|
|
453
|
+
is passed (core never injects props into consumer elements, so
|
|
454
|
+
overrides reach it via inheritance). The wrapper itself stays
|
|
455
|
+
layout-only. */}
|
|
448
456
|
<div
|
|
449
|
-
{...
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
457
|
+
{...(icon != null
|
|
458
|
+
? mergeProps(
|
|
459
|
+
themeProps('banner-icon', {status}),
|
|
460
|
+
stylex.props(styles.iconWrapper),
|
|
461
|
+
)
|
|
462
|
+
: stylex.props(styles.iconWrapper))}
|
|
453
463
|
aria-hidden="true">
|
|
454
464
|
{icon != null ? (
|
|
455
465
|
icon
|
|
456
466
|
) : (
|
|
457
|
-
<Icon
|
|
467
|
+
// Applied to the status <Icon> itself rather than the wrapper, so
|
|
468
|
+
// the element that paints the glyph is the element a theme
|
|
469
|
+
// targets — a 'banner-icon' 'status:X' color override beats the
|
|
470
|
+
// Icon's own variant from @layer astryx-theme (#4166).
|
|
471
|
+
<Icon
|
|
472
|
+
icon={defaultIconName}
|
|
473
|
+
size="md"
|
|
474
|
+
color={iconColor}
|
|
475
|
+
{...themeProps('banner-icon', {status})}
|
|
476
|
+
/>
|
|
458
477
|
)}
|
|
459
478
|
</div>
|
|
460
479
|
<div {...stylex.props(styles.headerContent)}>
|
package/src/Blockquote/index.ts
CHANGED
|
@@ -163,7 +163,7 @@ export const docs = {
|
|
|
163
163
|
],
|
|
164
164
|
vars: [
|
|
165
165
|
{name: '--_button-radius', description: 'Border radius', default: 'var(--radius-element)', private: true},
|
|
166
|
-
{name: '--button-focus-offset', description: 'Focus ring outline offset', default: '
|
|
166
|
+
{name: '--button-focus-offset', description: 'Focus ring outline offset', default: 'var(--focus-outline-offset)'},
|
|
167
167
|
{name: '--button-icon-only-aspect', description: 'Aspect ratio for icon-only buttons', default: '1 / 1'},
|
|
168
168
|
],
|
|
169
169
|
derived: [
|
|
@@ -245,7 +245,7 @@ export const docsZh = {
|
|
|
245
245
|
],
|
|
246
246
|
vars: [
|
|
247
247
|
{name: '--_button-radius', description: '圆角半径', default: 'var(--radius-element)', private: true},
|
|
248
|
-
{name: '--button-focus-offset', description: '焦点环轮廓偏移', default: '
|
|
248
|
+
{name: '--button-focus-offset', description: '焦点环轮廓偏移', default: 'var(--focus-outline-offset)'},
|
|
249
249
|
{name: '--button-icon-only-aspect', description: '纯图标按钮的宽高比', default: '1 / 1'},
|
|
250
250
|
],
|
|
251
251
|
derived: [
|
package/src/Button/Button.tsx
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
fontWeightVars,
|
|
35
35
|
typeScaleVars,
|
|
36
36
|
shadowVars,
|
|
37
|
+
focusVars,
|
|
37
38
|
} from '../theme/tokens.stylex';
|
|
38
39
|
import {Spinner} from '../Spinner';
|
|
39
40
|
import {VisuallyHidden} from '../VisuallyHidden';
|
|
@@ -57,10 +58,10 @@ import type {ButtonVariantMap} from './index';
|
|
|
57
58
|
const styles = stylex.create({
|
|
58
59
|
base: {
|
|
59
60
|
// Kept as a public themeable var (documented in Button.doc.mjs) even though
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
'--button-focus-offset': '
|
|
61
|
+
// it now defaults to the shared token: removing it would break any theme
|
|
62
|
+
// setting it, for no gain. It overrides the shared offset, so a theme can
|
|
63
|
+
// still tune the ring distance on buttons specifically.
|
|
64
|
+
'--button-focus-offset': focusVars['--focus-outline-offset'],
|
|
64
65
|
outlineOffset: {
|
|
65
66
|
default: '0',
|
|
66
67
|
':focus-visible': 'var(--button-focus-offset)',
|
|
@@ -437,8 +438,10 @@ const loadingStyles = stylex.create({
|
|
|
437
438
|
*
|
|
438
439
|
* Layers always carry the native `popover` attribute (useLayer.tsx), and a
|
|
439
440
|
* popover is never an in-flow member — it is `display: none` until shown, then
|
|
440
|
-
* promoted to the top layer.
|
|
441
|
-
*
|
|
441
|
+
* promoted to the top layer. Context layers also retain an inert `<template>`
|
|
442
|
+
* marker so they can re-resolve their JSX position. Neither element is a group
|
|
443
|
+
* member, so "last member" is: no following element sibling besides those two
|
|
444
|
+
* pieces of layer infrastructure.
|
|
442
445
|
*
|
|
443
446
|
* Reading it the other way round — marking the *buttons* and testing for a
|
|
444
447
|
* marked sibling — is the trap: it silently reclassifies anything it doesn't
|
|
@@ -454,7 +457,7 @@ const loadingStyles = stylex.create({
|
|
|
454
457
|
* The leading edge still uses `:first-child` — a member's button always precedes
|
|
455
458
|
* its own layer, so the first button is genuinely `:first-child`.
|
|
456
459
|
*/
|
|
457
|
-
const IS_LAST_ITEM = ':not(:has(~ *:not([popover])))';
|
|
460
|
+
const IS_LAST_ITEM = ':not(:has(~ *:not([popover]):not(template)))';
|
|
458
461
|
|
|
459
462
|
const groupStyles = stylex.create({
|
|
460
463
|
horizontal: {
|
|
@@ -202,9 +202,9 @@ describe('ButtonGroup', () => {
|
|
|
202
202
|
// Trailing radius (issue #2508)
|
|
203
203
|
//
|
|
204
204
|
// The trailing end cap cannot be keyed off `:last-child`: members render an
|
|
205
|
-
// invisible layer AFTER their button (tooltip'd Button,
|
|
206
|
-
// useLayer renders
|
|
207
|
-
// slot. See IS_LAST_ITEM in Button.tsx.
|
|
205
|
+
// invisible layer infrastructure AFTER their button (tooltip'd Button,
|
|
206
|
+
// DropdownMenu), and useLayer renders a marker plus the layer inline when the
|
|
207
|
+
// host is safe, so they steal the slot. See IS_LAST_ITEM in Button.tsx.
|
|
208
208
|
//
|
|
209
209
|
// HOW THESE TESTS CATCH THE BUG
|
|
210
210
|
// jsdom applies no StyleX CSS, so a DOM-only test cannot prove which rule
|
|
@@ -219,7 +219,9 @@ describe('ButtonGroup', () => {
|
|
|
219
219
|
describe('trailing radius (#2508)', () => {
|
|
220
220
|
/** The group members, in DOM order (excludes invisible layer siblings). */
|
|
221
221
|
const items = (group: HTMLElement): Element[] =>
|
|
222
|
-
Array.from(
|
|
222
|
+
Array.from(
|
|
223
|
+
group.querySelectorAll(':scope > *:not([popover]):not(template)'),
|
|
224
|
+
);
|
|
223
225
|
|
|
224
226
|
// -- Compiled CSS, read from the source -----------------------------------
|
|
225
227
|
|
|
@@ -375,6 +377,7 @@ describe('ButtonGroup', () => {
|
|
|
375
377
|
// .stylex.ts file it compiles to a mangled selector like `[x13pbwiz]`
|
|
376
378
|
// that matches nothing in the DOM.
|
|
377
379
|
expect(selector).toContain('[popover]');
|
|
380
|
+
expect(selector).toContain('template');
|
|
378
381
|
}
|
|
379
382
|
},
|
|
380
383
|
);
|
package/src/Card/Card.tsx
CHANGED
package/src/Card/index.ts
CHANGED
package/src/Center/Center.tsx
CHANGED
package/src/Center/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {Theme} from '../theme/Theme';
|
|
|
17
17
|
import {defineTheme} from '../theme/defineTheme';
|
|
18
18
|
import {getForcedColorsRules} from '../__tests__/forcedColors';
|
|
19
19
|
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
20
|
+
import {FOCUS_OUTLINE_PARTS} from '../utils/focusOutline.stylex';
|
|
20
21
|
|
|
21
22
|
afterEach(() => {
|
|
22
23
|
__resetLiveRegionsForTest();
|
|
@@ -686,7 +687,9 @@ describe('focus ring ownership (WCAG 2.4.7)', () => {
|
|
|
686
687
|
<CheckboxInput label="Accept" value={false} onChange={() => {}} />,
|
|
687
688
|
);
|
|
688
689
|
focusInput(container);
|
|
689
|
-
expect(indicatorOf(container).style.outlineStyle).toBe(
|
|
690
|
+
expect(indicatorOf(container).style.outlineStyle).toBe(
|
|
691
|
+
FOCUS_OUTLINE_PARTS.outlineStyle,
|
|
692
|
+
);
|
|
690
693
|
});
|
|
691
694
|
|
|
692
695
|
it('paints it on a replacement that forwards nothing', () => {
|
|
@@ -701,7 +704,7 @@ describe('focus ring ownership (WCAG 2.4.7)', () => {
|
|
|
701
704
|
expect(replaced.className).toBe('');
|
|
702
705
|
// ...and it still gets a ring, because the owner drew it.
|
|
703
706
|
focusInput(container);
|
|
704
|
-
expect(replaced.style.outlineStyle).toBe(
|
|
707
|
+
expect(replaced.style.outlineStyle).toBe(FOCUS_OUTLINE_PARTS.outlineStyle);
|
|
705
708
|
});
|
|
706
709
|
|
|
707
710
|
it('clears the ring on blur', () => {
|
|
@@ -709,7 +712,9 @@ describe('focus ring ownership (WCAG 2.4.7)', () => {
|
|
|
709
712
|
<CheckboxInput label="Accept" value={false} onChange={() => {}} />,
|
|
710
713
|
);
|
|
711
714
|
const input = focusInput(container);
|
|
712
|
-
expect(indicatorOf(container).style.outlineStyle).toBe(
|
|
715
|
+
expect(indicatorOf(container).style.outlineStyle).toBe(
|
|
716
|
+
FOCUS_OUTLINE_PARTS.outlineStyle,
|
|
717
|
+
);
|
|
713
718
|
fireEvent.blur(input);
|
|
714
719
|
expect(indicatorOf(container).style.outlineStyle).toBe('');
|
|
715
720
|
});
|