@astryxdesign/core 0.4.0 → 0.4.1-canary.f4868b2
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 +2 -2
- 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/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/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 +20 -17
- package/dist/astryx.umd.js +48 -48
- package/dist/astryx.umd.js.map +4 -4
- 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 +5 -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/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/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/TimeInput/TimeInput.test.tsx +54 -0
- package/src/TimeInput/TimeInput.tsx +17 -2
- package/src/Timestamp/Timestamp.test.tsx +91 -14
- package/src/Timestamp/Timestamp.tsx +14 -2
- package/src/Timestamp/formatInstant.ts +16 -1
- 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/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/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
|
@@ -4,12 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file SideNavItem.tsx
|
|
7
|
-
* @input Uses React, ReactNode, StyleX, Icon, IconType
|
|
7
|
+
* @input Uses React, ReactNode, StyleX, Icon, IconType, useMenuHover
|
|
8
8
|
* @output Exports SideNavItem component and SideNavItemProps
|
|
9
9
|
* @position Core implementation; used inside SideNav children
|
|
10
10
|
*
|
|
11
11
|
* Navigation item with icon, selected state, and nesting.
|
|
12
12
|
*
|
|
13
|
+
* Collapsed items with children open their submenu flyout through
|
|
14
|
+
* `useMenuHover`, the shared hover-intent hook (same one `SideNavHeading` and
|
|
15
|
+
* `TopNavMenu` use). Hover is a progressive enhancement over the popover's
|
|
16
|
+
* click behavior and is inert on coarse pointers.
|
|
17
|
+
*
|
|
13
18
|
* SYNC: When modified, update these files to stay in sync:
|
|
14
19
|
* - /packages/core/src/SideNav/SideNav.doc.mjs
|
|
15
20
|
* - /packages/core/src/SideNav/SideNav.test.tsx
|
|
@@ -35,17 +40,18 @@ import {
|
|
|
35
40
|
typeScaleVars,
|
|
36
41
|
durationVars,
|
|
37
42
|
easeVars,
|
|
38
|
-
borderVars,
|
|
39
43
|
radiusVars,
|
|
40
44
|
} from '../theme/tokens.stylex';
|
|
41
45
|
import {Icon, renderIconSlot, type IconType} from '../Icon';
|
|
42
46
|
import {useLinkComponent} from '../Link/useLinkComponent';
|
|
43
47
|
import type {LinkComponentType} from '../Link/types';
|
|
44
48
|
import {usePopover} from '../Popover/usePopover';
|
|
49
|
+
import {useMenuHover} from '../hooks/useMenuHover';
|
|
45
50
|
import {mergeProps, mergeRefs} from '../utils';
|
|
46
51
|
import type {BaseProps} from '../BaseProps';
|
|
47
52
|
import {Tooltip} from '../Tooltip';
|
|
48
53
|
import {navItemStyles, type NavItemSize} from '../NavItem/navItemStyles.stylex';
|
|
54
|
+
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
49
55
|
import {
|
|
50
56
|
useSideNavCollapse,
|
|
51
57
|
SideNavCollapseContext,
|
|
@@ -91,7 +97,10 @@ const styles = stylex.create({
|
|
|
91
97
|
display: 'grid',
|
|
92
98
|
gridTemplateRows: '1fr',
|
|
93
99
|
transitionProperty: 'grid-template-rows',
|
|
94
|
-
transitionDuration:
|
|
100
|
+
transitionDuration: {
|
|
101
|
+
default: durationVars['--duration-medium'],
|
|
102
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
103
|
+
},
|
|
95
104
|
transitionTimingFunction: easeVars['--ease-standard'],
|
|
96
105
|
},
|
|
97
106
|
childrenCollapsed: {
|
|
@@ -114,7 +123,10 @@ const styles = stylex.create({
|
|
|
114
123
|
// target, not the glyph size, so keep the glyph on the inherited size.
|
|
115
124
|
fontSize: 'inherit',
|
|
116
125
|
transitionProperty: 'transform',
|
|
117
|
-
transitionDuration:
|
|
126
|
+
transitionDuration: {
|
|
127
|
+
default: durationVars['--duration-fast'],
|
|
128
|
+
'@media (prefers-reduced-motion: reduce)': '0s',
|
|
129
|
+
},
|
|
118
130
|
transitionTimingFunction: easeVars['--ease-standard'],
|
|
119
131
|
flexShrink: 0,
|
|
120
132
|
},
|
|
@@ -146,10 +158,14 @@ const styles = stylex.create({
|
|
|
146
158
|
},
|
|
147
159
|
// Primary action element inside the split-action row (link or button).
|
|
148
160
|
// Flex:1 so it fills remaining space, giving a wide click target.
|
|
161
|
+
// `alignSelf: stretch` makes it fill the row's height too — without it the
|
|
162
|
+
// element collapses to its line box (20px), under the 24px WCAG 2.5.8
|
|
163
|
+
// target-size floor even though the row it sits in is 32px tall.
|
|
149
164
|
// Resets both link and button appearance so it blends into the row.
|
|
150
165
|
splitAction: {
|
|
151
166
|
display: 'flex',
|
|
152
167
|
alignItems: 'center',
|
|
168
|
+
alignSelf: 'stretch',
|
|
153
169
|
gap: spacingVars['--spacing-2'],
|
|
154
170
|
flex: 1,
|
|
155
171
|
minWidth: 0,
|
|
@@ -167,16 +183,23 @@ const styles = stylex.create({
|
|
|
167
183
|
textAlign: 'start',
|
|
168
184
|
cursor: 'pointer',
|
|
169
185
|
},
|
|
170
|
-
//
|
|
186
|
+
// No border and no background: `usePopover` paints the panel this renders
|
|
187
|
+
// into. Drawing a second surface here put square corners inside its rounded
|
|
188
|
+
// ones. The radius matches so a theme retargeting `--radius-container`
|
|
189
|
+
// keeps the two in step.
|
|
171
190
|
popoverSurface: {
|
|
172
|
-
|
|
173
|
-
borderStyle: 'solid',
|
|
174
|
-
borderColor: colorVars['--color-border'],
|
|
191
|
+
borderRadius: radiusVars['--radius-container'],
|
|
175
192
|
paddingBlock: spacingVars['--spacing-1'],
|
|
176
193
|
paddingInline: spacingVars['--spacing-1'],
|
|
177
|
-
marginInlineStart: spacingVars['--spacing-1'],
|
|
178
194
|
minWidth: 180,
|
|
179
195
|
},
|
|
196
|
+
// The gap from the rail belongs on the positioned layer, where
|
|
197
|
+
// `DropdownMenu` keeps it. On the content div it insets the content instead,
|
|
198
|
+
// leaving the panel flush against the rail.
|
|
199
|
+
popoverGap: {
|
|
200
|
+
marginInlineStart: spacingVars['--spacing-1'],
|
|
201
|
+
marginInlineEnd: spacingVars['--spacing-1'],
|
|
202
|
+
},
|
|
180
203
|
popoverHeader: {
|
|
181
204
|
paddingInline: spacingVars['--spacing-2'],
|
|
182
205
|
paddingBlock: spacingVars['--spacing-1'],
|
|
@@ -361,6 +384,7 @@ export function SideNavItem({
|
|
|
361
384
|
'data-testid': testId,
|
|
362
385
|
ref,
|
|
363
386
|
xstyle,
|
|
387
|
+
...rest
|
|
364
388
|
}: SideNavItemProps) {
|
|
365
389
|
const t = useTranslator();
|
|
366
390
|
const {isCollapsed} = useSideNavCollapse();
|
|
@@ -376,7 +400,7 @@ export function SideNavItem({
|
|
|
376
400
|
hasLightDismiss: true,
|
|
377
401
|
hasAutoFocus: true,
|
|
378
402
|
hasCloseButton: false,
|
|
379
|
-
dialogLabel:
|
|
403
|
+
dialogLabel: t('@astryx.sideNavItem.submenuLabel', {label}),
|
|
380
404
|
});
|
|
381
405
|
|
|
382
406
|
// Collapse state for items with children
|
|
@@ -434,42 +458,17 @@ export function SideNavItem({
|
|
|
434
458
|
toggleItemCollapse();
|
|
435
459
|
};
|
|
436
460
|
|
|
437
|
-
//
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
hideTimeoutRef.current = null;
|
|
449
|
-
}
|
|
450
|
-
}, []);
|
|
451
|
-
|
|
452
|
-
const schedulePopoverShow = useCallback(() => {
|
|
453
|
-
clearPopoverTimeouts();
|
|
454
|
-
showTimeoutRef.current = setTimeout(() => {
|
|
455
|
-
popover.show({skipAutoFocus: true});
|
|
456
|
-
}, 150);
|
|
457
|
-
}, [clearPopoverTimeouts, popover]);
|
|
458
|
-
|
|
459
|
-
const schedulePopoverHide = useCallback(() => {
|
|
460
|
-
clearPopoverTimeouts();
|
|
461
|
-
hideTimeoutRef.current = setTimeout(() => {
|
|
462
|
-
popover.hide();
|
|
463
|
-
}, 200);
|
|
464
|
-
}, [clearPopoverTimeouts, popover]);
|
|
465
|
-
|
|
466
|
-
const handlePopoverMouseEnter = useCallback(() => {
|
|
467
|
-
schedulePopoverShow();
|
|
468
|
-
}, [schedulePopoverShow]);
|
|
469
|
-
|
|
470
|
-
const handlePopoverMouseLeave = useCallback(() => {
|
|
471
|
-
schedulePopoverHide();
|
|
472
|
-
}, [schedulePopoverHide]);
|
|
461
|
+
// Pointer half only. The hook's `onKeyDown`/`menuRef` drive a `useListFocus`
|
|
462
|
+
// over `[role="menuitem"]`, and this flyout is a focus-trapped dialog of
|
|
463
|
+
// links — wiring them would swallow arrow keys rather than navigate with
|
|
464
|
+
// them. Keyboard stays with `usePopover`'s trap, as in DropdownMenuSubMenu.
|
|
465
|
+
const {triggerProps: hoverTriggerProps, contentProps: hoverContentProps} =
|
|
466
|
+
useMenuHover({
|
|
467
|
+
show: popover.show,
|
|
468
|
+
hide: popover.hide,
|
|
469
|
+
isOpen: popover.isOpen,
|
|
470
|
+
isEnabled: isCollapsed && hasChildren,
|
|
471
|
+
});
|
|
473
472
|
|
|
474
473
|
// In collapsed mode: hide items without icons
|
|
475
474
|
if (isCollapsed && !icon) {
|
|
@@ -484,16 +483,21 @@ export function SideNavItem({
|
|
|
484
483
|
displayIcon &&
|
|
485
484
|
renderIconSlot(displayIcon, {
|
|
486
485
|
size: 'sm',
|
|
487
|
-
|
|
486
|
+
// `inherit` so a selected row's icon follows the row to HighlightText
|
|
487
|
+
// under forced colors. Identical to `primary` otherwise: both token
|
|
488
|
+
// families are emitted from one expression.
|
|
489
|
+
color: isSelected ? 'inherit' : isDisabled ? 'disabled' : 'secondary',
|
|
488
490
|
});
|
|
489
491
|
|
|
490
|
-
// Shared collapsed item styles — used by trigger, link, and button
|
|
492
|
+
// Shared collapsed item styles — used by trigger, link, and button.
|
|
493
|
+
// All three render a focusable element, so each draws the shared ring.
|
|
491
494
|
const collapsedItemStyles = mergeProps(
|
|
492
495
|
themeProps('side-nav-item', {
|
|
493
496
|
size,
|
|
494
497
|
selected: isSelected ? 'selected' : null,
|
|
498
|
+
disabled: isDisabled ? 'disabled' : null,
|
|
495
499
|
}),
|
|
496
|
-
|
|
500
|
+
focusOutlineProps.focusVisible(
|
|
497
501
|
navItemStyles.item,
|
|
498
502
|
navItemStyles[size],
|
|
499
503
|
styles.itemCollapsed,
|
|
@@ -511,9 +515,8 @@ export function SideNavItem({
|
|
|
511
515
|
<button
|
|
512
516
|
ref={mergeRefs(ref, popover.triggerRef)}
|
|
513
517
|
type="button"
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
onMouseLeave={handlePopoverMouseLeave}
|
|
518
|
+
{...rest}
|
|
519
|
+
{...hoverTriggerProps}
|
|
517
520
|
aria-label={label}
|
|
518
521
|
data-testid={testId}
|
|
519
522
|
{...popover.triggerProps}
|
|
@@ -523,15 +526,15 @@ export function SideNavItem({
|
|
|
523
526
|
{popover.render(
|
|
524
527
|
<div
|
|
525
528
|
{...stylex.props(styles.popoverSurface)}
|
|
526
|
-
onMouseEnter={
|
|
527
|
-
onMouseLeave={
|
|
529
|
+
onMouseEnter={hoverContentProps.onMouseEnter}
|
|
530
|
+
onMouseLeave={hoverContentProps.onMouseLeave}
|
|
528
531
|
onClick={() => popover.hide()}>
|
|
529
532
|
<div {...stylex.props(styles.popoverHeader)}>{label}</div>
|
|
530
533
|
<SideNavCollapseContext value={EXPANDED_COLLAPSE_STATE}>
|
|
531
534
|
{children}
|
|
532
535
|
</SideNavCollapseContext>
|
|
533
536
|
</div>,
|
|
534
|
-
{placement: 'end', alignment: 'start'},
|
|
537
|
+
{placement: 'end', alignment: 'start', xstyle: styles.popoverGap},
|
|
535
538
|
)}
|
|
536
539
|
</div>
|
|
537
540
|
);
|
|
@@ -552,6 +555,7 @@ export function SideNavItem({
|
|
|
552
555
|
as={as}
|
|
553
556
|
isDisabled={isDisabled}
|
|
554
557
|
onClick={handleClick}
|
|
558
|
+
{...rest}
|
|
555
559
|
{...collapsedAriaProps}
|
|
556
560
|
{...collapsedItemStyles}>
|
|
557
561
|
{collapsedIcon}
|
|
@@ -571,7 +575,8 @@ export function SideNavItem({
|
|
|
571
575
|
{displayIcon &&
|
|
572
576
|
renderIconSlot(displayIcon, {
|
|
573
577
|
size: 'sm',
|
|
574
|
-
|
|
578
|
+
// `inherit` — see the collapsed path above.
|
|
579
|
+
color: isSelected ? 'inherit' : isDisabled ? 'disabled' : 'secondary',
|
|
575
580
|
})}
|
|
576
581
|
{!isCollapsed && <span {...stylex.props(styles.label)}>{label}</span>}
|
|
577
582
|
{!isCollapsed && endContent && (
|
|
@@ -591,45 +596,47 @@ export function SideNavItem({
|
|
|
591
596
|
</>
|
|
592
597
|
);
|
|
593
598
|
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
stylex.props(
|
|
600
|
-
navItemStyles.item,
|
|
601
|
-
navItemStyles[size],
|
|
602
|
-
isSelected && navItemStyles.selected,
|
|
603
|
-
isDisabled && navItemStyles.disabled,
|
|
604
|
-
),
|
|
605
|
-
);
|
|
606
|
-
|
|
607
|
-
// ── Split-action path: <div> row with <a> + <button> as siblings ──
|
|
608
|
-
//
|
|
609
|
-
// When both collapsible and href are set, we can't nest a <button>
|
|
610
|
-
// inside an <a>. Instead we use a <div> as the flex row container,
|
|
611
|
-
// an <a display:contents> for the link (with ::before covering the row),
|
|
612
|
-
// and a <button> sibling lifted above the overlay via z-index.
|
|
599
|
+
const itemThemeProps = themeProps('side-nav-item', {
|
|
600
|
+
size,
|
|
601
|
+
selected: isSelected ? 'selected' : null,
|
|
602
|
+
disabled: isDisabled ? 'disabled' : null,
|
|
603
|
+
});
|
|
613
604
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
605
|
+
const itemStyleArgs = [
|
|
606
|
+
navItemStyles.item,
|
|
607
|
+
navItemStyles[size],
|
|
608
|
+
isSelected && navItemStyles.selected,
|
|
609
|
+
isDisabled && navItemStyles.disabled,
|
|
610
|
+
] as const;
|
|
611
|
+
|
|
612
|
+
// Two shapes of the same row appearance:
|
|
613
|
+
// - `rowProps` for the split-action path, where the row is a plain <div>
|
|
614
|
+
// container and its children take focus (so the ring belongs on them);
|
|
615
|
+
// - `focusableRowProps` for every other path, where the row element is
|
|
616
|
+
// itself the focusable control.
|
|
617
|
+
const rowProps = mergeProps(itemThemeProps, stylex.props(...itemStyleArgs));
|
|
618
|
+
const focusableRowProps = mergeProps(
|
|
619
|
+
itemThemeProps,
|
|
620
|
+
focusOutlineProps.focusVisible(...itemStyleArgs),
|
|
621
|
+
);
|
|
619
622
|
|
|
623
|
+
// Split-action path: collapsible plus a primary action renders the primary
|
|
624
|
+
// element and the chevron toggle as siblings in a <div> row, since a
|
|
625
|
+
// <button> cannot nest inside an <a>.
|
|
620
626
|
let itemElement;
|
|
621
627
|
|
|
622
628
|
if (hasIndependentToggle) {
|
|
623
629
|
itemElement = (
|
|
624
|
-
<div data-testid={testId} {...
|
|
630
|
+
<div data-testid={testId} {...rowProps}>
|
|
625
631
|
<NavItemElement
|
|
626
632
|
ref={ref}
|
|
627
633
|
href={href}
|
|
628
634
|
as={as}
|
|
629
635
|
isDisabled={isDisabled}
|
|
630
636
|
onClick={handleClick}
|
|
637
|
+
{...rest}
|
|
631
638
|
aria-current={isSelected ? ('page' as const) : undefined}
|
|
632
|
-
{...
|
|
639
|
+
{...focusOutlineProps.focusVisible(styles.splitAction)}>
|
|
633
640
|
{itemContent}
|
|
634
641
|
</NavItemElement>
|
|
635
642
|
<button
|
|
@@ -642,7 +649,7 @@ export function SideNavItem({
|
|
|
642
649
|
}
|
|
643
650
|
aria-expanded={!isItemCollapsed}
|
|
644
651
|
aria-controls={`${id}-children`}
|
|
645
|
-
{...
|
|
652
|
+
{...focusOutlineProps.focusVisible(styles.expandToggle)}>
|
|
646
653
|
<Icon
|
|
647
654
|
icon="chevronDown"
|
|
648
655
|
size="lg"
|
|
@@ -671,8 +678,9 @@ export function SideNavItem({
|
|
|
671
678
|
as={as}
|
|
672
679
|
isDisabled={isDisabled}
|
|
673
680
|
onClick={handleClick}
|
|
681
|
+
{...rest}
|
|
674
682
|
{...ariaProps}
|
|
675
|
-
{...
|
|
683
|
+
{...focusableRowProps}>
|
|
676
684
|
{itemContent}
|
|
677
685
|
</NavItemElement>
|
|
678
686
|
);
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import {mergeProps} from '../utils';
|
|
30
30
|
import type {BaseProps} from '../BaseProps';
|
|
31
31
|
import {useSideNavCollapse} from './SideNavCollapseContext';
|
|
32
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
32
33
|
import {themeProps} from '../utils/themeProps';
|
|
33
34
|
// =============================================================================
|
|
34
35
|
// Styles
|
|
@@ -167,20 +168,6 @@ export function SideNavSection({
|
|
|
167
168
|
|
|
168
169
|
const shouldHideHeader = isHeaderHidden || isCollapsed;
|
|
169
170
|
|
|
170
|
-
const visuallyHiddenStyle: React.CSSProperties = shouldHideHeader
|
|
171
|
-
? {
|
|
172
|
-
position: 'absolute',
|
|
173
|
-
width: 1,
|
|
174
|
-
height: 1,
|
|
175
|
-
padding: 0,
|
|
176
|
-
margin: -1,
|
|
177
|
-
overflow: 'hidden',
|
|
178
|
-
clip: 'rect(0, 0, 0, 0)',
|
|
179
|
-
whiteSpace: 'nowrap',
|
|
180
|
-
borderWidth: 0,
|
|
181
|
-
}
|
|
182
|
-
: {};
|
|
183
|
-
|
|
184
171
|
return (
|
|
185
172
|
<div
|
|
186
173
|
ref={ref}
|
|
@@ -194,12 +181,11 @@ export function SideNavSection({
|
|
|
194
181
|
role="group"
|
|
195
182
|
aria-labelledby={titleId}
|
|
196
183
|
data-testid={testId}>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
</div>
|
|
184
|
+
{shouldHideHeader ? (
|
|
185
|
+
<VisuallyHidden as="div">{headerContent}</VisuallyHidden>
|
|
186
|
+
) : (
|
|
187
|
+
<div {...stylex.props(styles.header)}>{headerContent}</div>
|
|
188
|
+
)}
|
|
203
189
|
<div {...stylex.props(styles.items)}>{children}</div>
|
|
204
190
|
</div>
|
|
205
191
|
);
|
package/src/SideNav/index.ts
CHANGED
|
@@ -29,6 +29,8 @@ export type {SideNavCollapseButtonProps} from './SideNavCollapseButton';
|
|
|
29
29
|
export {useSideNavCollapse} from './SideNavCollapseContext';
|
|
30
30
|
export type {
|
|
31
31
|
SideNavCollapseState,
|
|
32
|
+
SideNavCollapsibleConfig,
|
|
33
|
+
SideNavControlledCollapsible,
|
|
32
34
|
SideNavImperativeCollapseHandle,
|
|
33
35
|
} from './SideNavCollapseContext';
|
|
34
36
|
|
package/src/Skeleton/index.ts
CHANGED
package/src/Slider/Slider.tsx
CHANGED
|
@@ -41,6 +41,7 @@ import {useTooltip} from '../Tooltip';
|
|
|
41
41
|
import {VisuallyHidden} from '../VisuallyHidden';
|
|
42
42
|
import type {InputStatus} from '../Field/types';
|
|
43
43
|
import {mergeProps, mergeRefs, rtlStyles} from '../utils';
|
|
44
|
+
import {focusOutlineStyles} from '../utils/focusOutline.stylex';
|
|
44
45
|
import {isRtlElement} from '../hooks/isRtlElement';
|
|
45
46
|
import type {BaseProps} from '../BaseProps';
|
|
46
47
|
import type {SizeValue} from '../utils/types';
|
|
@@ -253,16 +254,6 @@ const styles = stylex.create({
|
|
|
253
254
|
},
|
|
254
255
|
},
|
|
255
256
|
},
|
|
256
|
-
thumbFocusVisible: {
|
|
257
|
-
outline: {
|
|
258
|
-
default: 'none',
|
|
259
|
-
':focus-visible': `2px solid ${colorVars['--color-accent']}`,
|
|
260
|
-
},
|
|
261
|
-
outlineOffset: {
|
|
262
|
-
default: '0',
|
|
263
|
-
':focus-visible': '2px',
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
257
|
thumbDisabled: {
|
|
267
258
|
backgroundColor: colorVars['--color-background-muted'],
|
|
268
259
|
cursor: 'not-allowed',
|
|
@@ -792,7 +783,7 @@ export function Slider({ref, ...props}: SliderProps) {
|
|
|
792
783
|
? styles.thumbHorizontal
|
|
793
784
|
: rtlStyles.centerInline('50%'),
|
|
794
785
|
!isDisabled && styles.thumbHover,
|
|
795
|
-
!isDisabled &&
|
|
786
|
+
!isDisabled && focusOutlineStyles.focusVisible,
|
|
796
787
|
isDisabled && styles.thumbDisabled,
|
|
797
788
|
),
|
|
798
789
|
undefined,
|
package/src/Switch/Switch.tsx
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
easeVars,
|
|
34
34
|
typographyVars,
|
|
35
35
|
typeScaleVars,
|
|
36
|
+
focusVars,
|
|
36
37
|
} from '../theme/tokens.stylex';
|
|
37
38
|
import {FieldLabel} from '../Field/FieldLabel';
|
|
38
39
|
import {FieldStatus} from '../FieldStatus/FieldStatus';
|
|
@@ -189,15 +190,22 @@ const styles = stylex.create({
|
|
|
189
190
|
'@media (forced-colors: active)': 'CanvasText',
|
|
190
191
|
},
|
|
191
192
|
},
|
|
193
|
+
// The one ring in the system not drawn by focusOutlineStyles. The focusable
|
|
194
|
+
// input is a sibling of the track, so the condition has to reach the shared
|
|
195
|
+
// scope marker — and a marker cannot be shared across components without
|
|
196
|
+
// leaking focus state from an outer one, so it cannot live in the utility.
|
|
197
|
+
// StyleX also cannot inline a constant imported from another module, so the
|
|
198
|
+
// values are read from the tokens the utility reads.
|
|
192
199
|
trackFocus: {
|
|
193
200
|
outline: {
|
|
194
201
|
default: 'none',
|
|
195
202
|
[stylex.when.ancestor(':has(:focus-visible)', switchScope)]:
|
|
196
|
-
|
|
203
|
+
`${focusVars['--focus-outline-width']} ${focusVars['--focus-outline-style']} ${focusVars['--focus-outline-color']}`,
|
|
197
204
|
},
|
|
198
205
|
outlineOffset: {
|
|
199
206
|
default: null,
|
|
200
|
-
[stylex.when.ancestor(':has(:focus-visible)', switchScope)]:
|
|
207
|
+
[stylex.when.ancestor(':has(:focus-visible)', switchScope)]:
|
|
208
|
+
focusVars['--focus-outline-offset'],
|
|
201
209
|
},
|
|
202
210
|
},
|
|
203
211
|
// State-dependent colors with ancestor hover behavior
|
package/src/Table/Table.test.tsx
CHANGED
|
@@ -496,6 +496,37 @@ describe('BaseTable', () => {
|
|
|
496
496
|
});
|
|
497
497
|
});
|
|
498
498
|
|
|
499
|
+
describe('TableRow styling props', () => {
|
|
500
|
+
it('applies className and style to the row inside a Table', () => {
|
|
501
|
+
render(
|
|
502
|
+
<Table>
|
|
503
|
+
<tbody>
|
|
504
|
+
<TableRow className="custom-row" style={{opacity: 0.9}}>
|
|
505
|
+
<TableCell>Cell</TableCell>
|
|
506
|
+
</TableRow>
|
|
507
|
+
</tbody>
|
|
508
|
+
</Table>,
|
|
509
|
+
);
|
|
510
|
+
const row = screen.getByRole('row');
|
|
511
|
+
expect(row.className).toContain('custom-row');
|
|
512
|
+
expect(row.className).toContain('astryx-table-row');
|
|
513
|
+
expect(row.style.opacity).toBe('0.9');
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('applies className to a standalone row (no table context)', () => {
|
|
517
|
+
render(
|
|
518
|
+
<table>
|
|
519
|
+
<tbody>
|
|
520
|
+
<TableRow className="custom-row">
|
|
521
|
+
<td>Cell</td>
|
|
522
|
+
</TableRow>
|
|
523
|
+
</tbody>
|
|
524
|
+
</table>,
|
|
525
|
+
);
|
|
526
|
+
expect(screen.getByRole('row').className).toContain('custom-row');
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
|
|
499
530
|
describe('plugin pipeline', () => {
|
|
500
531
|
it('applies transformTable plugin', () => {
|
|
501
532
|
const plugin: TablePlugin<User> = {
|
package/src/Table/TableRow.tsx
CHANGED
|
@@ -120,6 +120,8 @@ export function TableRow({
|
|
|
120
120
|
xstyle,
|
|
121
121
|
ref,
|
|
122
122
|
isHeaderRow = false,
|
|
123
|
+
className: incomingClassName,
|
|
124
|
+
style: incomingStyle,
|
|
123
125
|
...props
|
|
124
126
|
}: TableRowProps) {
|
|
125
127
|
const ctx = use(TableContext);
|
|
@@ -132,6 +134,8 @@ export function TableRow({
|
|
|
132
134
|
{...mergeProps(
|
|
133
135
|
themeProps('table-row'),
|
|
134
136
|
stylex.props(tableRowMarker, xstyle),
|
|
137
|
+
incomingClassName,
|
|
138
|
+
incomingStyle,
|
|
135
139
|
)}>
|
|
136
140
|
{children}
|
|
137
141
|
</tr>
|
|
@@ -169,6 +173,8 @@ export function TableRow({
|
|
|
169
173
|
{...mergeProps(
|
|
170
174
|
themeProps('table-row'),
|
|
171
175
|
stylex.props(tableRowMarker, ...rowStyles),
|
|
176
|
+
incomingClassName,
|
|
177
|
+
incomingStyle,
|
|
172
178
|
)}>
|
|
173
179
|
{children}
|
|
174
180
|
</tr>
|
|
@@ -14,6 +14,7 @@ import {render, screen, fireEvent, waitFor} from '@testing-library/react';
|
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {TimeInput} from './TimeInput';
|
|
16
16
|
import {InputGroup, InputGroupText} from '../InputGroup';
|
|
17
|
+
import {InternationalizationProvider} from '../i18n';
|
|
17
18
|
import type {ISOTimeString} from '../utils';
|
|
18
19
|
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
19
20
|
|
|
@@ -209,6 +210,59 @@ describe('TimeInput', () => {
|
|
|
209
210
|
expect(screen.queryByText('Invalid time')).not.toBeInTheDocument();
|
|
210
211
|
});
|
|
211
212
|
|
|
213
|
+
it('resolves the invalid-time announcement from the i18n catalog', () => {
|
|
214
|
+
render(
|
|
215
|
+
<InternationalizationProvider
|
|
216
|
+
locale="en"
|
|
217
|
+
overrides={{en: {'@astryx.timeInput.invalidTime': 'Ungültige Zeit'}}}>
|
|
218
|
+
<TimeInput label="Time" onChange={() => {}} />
|
|
219
|
+
</InternationalizationProvider>,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
fireEvent.change(screen.getByRole('textbox'), {target: {value: '25:99'}});
|
|
223
|
+
|
|
224
|
+
expect(screen.getByRole('alert')).toHaveTextContent('Ungültige Zeit');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Arrow-key stepping mutates a plain textbox programmatically, and screen
|
|
228
|
+
// readers do not announce programmatic textbox changes — the new value must
|
|
229
|
+
// be announced through the polite live region (WCAG 4.1.2).
|
|
230
|
+
it('politely announces the new time after ArrowUp stepping', async () => {
|
|
231
|
+
const onChange = vi.fn();
|
|
232
|
+
render(
|
|
233
|
+
<TimeInput
|
|
234
|
+
label="Time"
|
|
235
|
+
value={'14:30' as ISOTimeString}
|
|
236
|
+
onChange={onChange}
|
|
237
|
+
/>,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
fireEvent.keyDown(screen.getByRole('textbox'), {key: 'ArrowUp'});
|
|
241
|
+
|
|
242
|
+
expect(onChange).toHaveBeenCalledWith('14:31');
|
|
243
|
+
await waitFor(() => {
|
|
244
|
+
expect(politeRegion()).toHaveTextContent('2:31 PM');
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('politely announces the new time after ArrowDown stepping', async () => {
|
|
249
|
+
const onChange = vi.fn();
|
|
250
|
+
render(
|
|
251
|
+
<TimeInput
|
|
252
|
+
label="Time"
|
|
253
|
+
value={'14:30' as ISOTimeString}
|
|
254
|
+
onChange={onChange}
|
|
255
|
+
/>,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
fireEvent.keyDown(screen.getByRole('textbox'), {key: 'ArrowDown'});
|
|
259
|
+
|
|
260
|
+
expect(onChange).toHaveBeenCalledWith('14:29');
|
|
261
|
+
await waitFor(() => {
|
|
262
|
+
expect(politeRegion()).toHaveTextContent('2:29 PM');
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
212
266
|
it('calls onChange on blur when input is valid', async () => {
|
|
213
267
|
const user = userEvent.setup();
|
|
214
268
|
const onChange = vi.fn();
|
|
@@ -558,10 +558,25 @@ export function TimeInput({
|
|
|
558
558
|
// Check if within range
|
|
559
559
|
if (isTimeInRange(newTime, min, max)) {
|
|
560
560
|
fireChange(newTime);
|
|
561
|
+
// Stepping programmatically rewrites a plain textbox's value, and
|
|
562
|
+
// screen readers do not announce programmatic textbox changes — the
|
|
563
|
+
// new value must be spoken explicitly or stepping is silent
|
|
564
|
+
// (WCAG 4.1.2).
|
|
565
|
+
announce(formatDisplayTime(newTime, hasSeconds));
|
|
561
566
|
}
|
|
562
567
|
}
|
|
563
568
|
},
|
|
564
|
-
[
|
|
569
|
+
[
|
|
570
|
+
value,
|
|
571
|
+
hasSeconds,
|
|
572
|
+
increment,
|
|
573
|
+
min,
|
|
574
|
+
max,
|
|
575
|
+
fireChange,
|
|
576
|
+
isDisabled,
|
|
577
|
+
announce,
|
|
578
|
+
formatDisplayTime,
|
|
579
|
+
],
|
|
565
580
|
);
|
|
566
581
|
|
|
567
582
|
// Handle clear button click
|
|
@@ -659,7 +674,7 @@ export function TimeInput({
|
|
|
659
674
|
user would get no feedback that their entry was rejected (WCAG 3.3.1).
|
|
660
675
|
*/}
|
|
661
676
|
<VisuallyHidden as="div" role="alert" aria-live="assertive">
|
|
662
|
-
{!isInputValid ? '
|
|
677
|
+
{!isInputValid ? t('@astryx.timeInput.invalidTime') : ''}
|
|
663
678
|
</VisuallyHidden>
|
|
664
679
|
{isBusy && <Spinner size="sm" />}
|
|
665
680
|
{hasClear && value && !isDisabled && (
|