@astryxdesign/core 0.6.0 → 0.6.1
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 +41 -3
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts +12 -1
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +44 -15
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +3 -2
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +3 -7
- package/dist/Center/Center.d.ts +23 -16
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +7 -5
- package/dist/CodeBlock/CodeBlock.js +2 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +12 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +12 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +1 -0
- package/dist/Field/InputClearButton.d.ts +2 -2
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +5 -1
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +16 -4
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +12 -1
- package/dist/HoverCard/useHoverCard.js +2 -2
- package/dist/Indicator/CheckboxIndicator.js +2 -2
- package/dist/Indicator/RadioIndicator.js +2 -2
- package/dist/Layer/layerStack.d.ts +10 -0
- package/dist/Layer/layerStack.d.ts.map +1 -1
- package/dist/Layer/layerStack.js +21 -9
- package/dist/Layer/useLayerDismissal.d.ts +2 -3
- package/dist/Layer/useLayerDismissal.d.ts.map +1 -1
- package/dist/Layer/useLayerDismissal.js +2 -3
- package/dist/NavIcon/NavIcon.js +2 -2
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +12 -2
- package/dist/Popover/usePopover.d.ts +3 -2
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +4 -2
- package/dist/ProgressBar/ProgressBar.js +2 -2
- package/dist/ScrollableArea/ScrollableArea.d.ts +79 -0
- package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -0
- package/dist/ScrollableArea/ScrollableArea.js +144 -0
- package/dist/ScrollableArea/index.d.ts +11 -0
- package/dist/ScrollableArea/index.d.ts.map +1 -0
- package/dist/ScrollableArea/index.js +11 -0
- package/dist/StatusDot/StatusDot.js +2 -2
- package/dist/TextArea/TextArea.js +2 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +19 -4
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +12 -2
- package/dist/Typeahead/BaseTypeahead.d.ts +21 -14
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +56 -20
- package/dist/astryx.css +15 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/scrollGeometry.d.ts +24 -0
- package/dist/hooks/scrollGeometry.d.ts.map +1 -0
- package/dist/hooks/scrollGeometry.js +86 -0
- package/dist/hooks/scrollOwnerRegistry.d.ts +15 -0
- package/dist/hooks/scrollOwnerRegistry.d.ts.map +1 -0
- package/dist/hooks/scrollOwnerRegistry.js +24 -0
- package/dist/hooks/useFocusTrap.d.ts +8 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +22 -11
- package/dist/hooks/useScrollableArea.d.ts +51 -0
- package/dist/hooks/useScrollableArea.d.ts.map +1 -0
- package/dist/hooks/useScrollableArea.js +287 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/defineTheme.d.ts +2 -6
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +1 -1
- package/dist/theme/derivedVarRegistry.js +1 -1
- package/dist/theme/localTokens.d.ts +8 -11
- package/dist/theme/localTokens.d.ts.map +1 -1
- package/dist/theme/localTokens.js +17 -71
- package/dist/theme/themeAdaptations.d.ts.map +1 -1
- package/dist/theme/themeAdaptations.js +4 -4
- package/dist/utils/themeProps.d.ts +10 -10
- package/dist/utils/themeProps.d.ts.map +1 -1
- package/dist/utils/themeProps.js +27 -10
- package/locales/en.json +16 -0
- package/locales/pseudo.json +12 -0
- package/package.json +7 -2
- package/src/AppShell/AppShell.test.tsx +36 -0
- package/src/AppShell/AppShell.tsx +4 -1
- package/src/AspectRatio/AspectRatio.doc.mjs +3 -3
- package/src/Banner/Banner.test.tsx +3 -1
- package/src/BottomSheet/BottomSheetSwitcher.doc.mjs +56 -1
- package/src/BottomSheet/BottomSheetSwitcher.spec.md +211 -0
- package/src/BottomSheet/BottomSheetSwitcher.test.tsx +134 -2
- package/src/BottomSheet/BottomSheetSwitcher.tsx +43 -20
- package/src/Breadcrumbs/BreadcrumbItem.doc.mjs +10 -5
- package/src/Breadcrumbs/BreadcrumbItem.spec.md +225 -0
- package/src/Breadcrumbs/BreadcrumbItem.tsx +8 -13
- package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -2
- package/src/Breadcrumbs/Breadcrumbs.test.tsx +49 -2
- package/src/Center/Center.doc.mjs +32 -28
- package/src/Center/Center.spec.md +225 -0
- package/src/Center/Center.test.tsx +42 -4
- package/src/Center/Center.tsx +24 -17
- package/src/Chat/ChatSystemMessage.test.tsx +2 -9
- package/src/CodeBlock/CodeBlock.doc.mjs +2 -2
- package/src/CodeBlock/CodeBlock.tsx +2 -2
- package/src/DateInput/DateInput.test.tsx +4 -4
- package/src/DateInput/DateInput.tsx +15 -4
- package/src/DateRangeInput/DateRangeInput.test.tsx +2 -2
- package/src/DateTimeInput/DateTimeInput.test.tsx +6 -4
- package/src/DateTimeInput/DateTimeInput.tsx +18 -7
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +4 -77
- package/src/Field/Field.test.tsx +42 -0
- package/src/Field/Field.tsx +6 -0
- package/src/Field/InputClearButton.test.tsx +35 -1
- package/src/Field/InputClearButton.tsx +7 -3
- package/src/Field/PanelSearchInput.tsx +21 -8
- package/src/FieldStatus/FieldStatus.spec.md +27 -17
- package/src/FieldStatus/FieldStatus.test.tsx +7 -5
- package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +198 -0
- package/src/FieldStatus/__tests__/StatusMessage.a11y.known-failures.ts +13 -0
- package/src/FieldStatus/__tests__/StatusMessage.a11y.renders.tsx +305 -0
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +317 -0
- package/src/FieldStatus/__tests__/StatusMessage.a11y.test.tsx +155 -0
- package/src/FileInput/FileInput.tsx +10 -1
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +3 -3
- package/src/HoverCard/HoverCard.doc.mjs +4 -4
- package/src/HoverCard/useHoverCard.tsx +2 -2
- package/src/Indicator/CheckboxIndicator.tsx +2 -2
- package/src/Indicator/Indicator.doc.mjs +2 -2
- package/src/Indicator/Indicator.test.tsx +1 -1
- package/src/Indicator/RadioIndicator.tsx +2 -2
- package/src/Layer/layerStack.ts +20 -9
- package/src/Layer/useLayerDismissal.ts +2 -3
- package/src/MultiSelector/MultiSelector.test.tsx +4 -4
- package/src/NavIcon/NavIcon.doc.mjs +4 -4
- package/src/NavIcon/NavIcon.tsx +2 -2
- package/src/NumberInput/NumberInput.tsx +18 -7
- package/src/Popover/Popover.doc.mjs +10 -10
- package/src/Popover/Popover.spec.md +55 -65
- package/src/Popover/Popover.test.tsx +29 -0
- package/src/Popover/usePopover.doc.mjs +4 -4
- package/src/Popover/usePopover.tsx +7 -4
- package/src/ProgressBar/ProgressBar.doc.mjs +4 -4
- package/src/ProgressBar/ProgressBar.test.tsx +1 -31
- package/src/ProgressBar/ProgressBar.tsx +2 -2
- package/src/RadioList/RadioList.test.tsx +5 -144
- package/src/RadioList/__tests__/RadioGroup.a11y.chromium.spec.ts +255 -0
- package/src/RadioList/__tests__/RadioGroup.a11y.known-failures.ts +12 -0
- package/src/RadioList/__tests__/RadioGroup.a11y.renders.tsx +232 -0
- package/src/RadioList/__tests__/RadioGroup.a11y.states.ts +503 -0
- package/src/RadioList/__tests__/RadioGroup.a11y.test.tsx +217 -0
- package/src/ScrollableArea/ScrollableArea.doc.mjs +100 -0
- package/src/ScrollableArea/ScrollableArea.spec.md +189 -0
- package/src/ScrollableArea/ScrollableArea.test.tsx +299 -0
- package/src/ScrollableArea/ScrollableArea.tsx +259 -0
- package/src/ScrollableArea/index.ts +26 -0
- package/src/ScrollableArea/modules/useScrollableArea.spec.md +121 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +5 -172
- package/src/Selector/Selector.test.tsx +4 -4
- package/src/Spinner/Spinner.test.tsx +0 -18
- package/src/StatusDot/StatusDot.doc.mjs +4 -4
- package/src/StatusDot/StatusDot.tsx +2 -2
- package/src/TabList/TabList.test.tsx +5 -9
- package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +191 -0
- package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +45 -0
- package/src/TabList/__tests__/Tabs.a11y.renders.tsx +92 -0
- package/src/TabList/__tests__/Tabs.a11y.states.ts +247 -0
- package/src/TabList/__tests__/Tabs.a11y.test.tsx +153 -0
- package/src/Table/Table.doc.mjs +2 -2
- package/src/TextArea/TextArea.doc.mjs +4 -4
- package/src/TextArea/TextArea.tsx +2 -2
- package/src/TextInput/TextInput.doc.mjs +2 -1
- package/src/TextInput/TextInput.test.tsx +94 -0
- package/src/TextInput/TextInput.tsx +22 -6
- package/src/TimeInput/TimeInput.tsx +18 -7
- package/src/Toast/ToastViewport.test.tsx +1 -39
- package/src/Typeahead/BaseTypeahead.doc.mjs +229 -33
- package/src/Typeahead/BaseTypeahead.spec.md +269 -0
- package/src/Typeahead/BaseTypeahead.test.tsx +200 -0
- package/src/Typeahead/BaseTypeahead.tsx +99 -30
- package/src/hooks/index.ts +13 -0
- package/src/hooks/scrollGeometry.ts +155 -0
- package/src/hooks/scrollOwnerRegistry.ts +47 -0
- package/src/hooks/useFocusTrap.ts +22 -11
- package/src/hooks/useFocusTrapEscapeShim.test.tsx +4 -3
- package/src/hooks/useScrollableArea.doc.mjs +108 -0
- package/src/hooks/useScrollableArea.test.tsx +437 -0
- package/src/hooks/useScrollableArea.ts +469 -0
- package/src/index.ts +1 -0
- package/src/theme/defineTheme.test.ts +65 -105
- package/src/theme/defineTheme.ts +3 -9
- package/src/theme/derivedVarRegistry.ts +1 -1
- package/src/theme/localTokens.ts +25 -96
- package/src/theme/publicThemeHelperContract.test.ts +2 -2
- package/src/theme/themeAdaptations.test.ts +16 -42
- package/src/theme/themeAdaptations.ts +6 -9
- package/src/utils/themeProps.test.ts +29 -10
- package/src/utils/themeProps.ts +36 -17
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file DropdownMenuSelectable.test.tsx
|
|
5
5
|
* @input vitest, @testing-library/react, DropdownMenu + selectable items
|
|
6
|
-
* @output
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* @output Component-local callback, composition, and marker styling tests for
|
|
7
|
+
* DropdownMenuCheckboxItem / RadioGroup / RadioItem (#3829)
|
|
8
|
+
* @position Shared checkbox and radio-group role, name, state, and interaction
|
|
9
|
+
* outcomes live in their reusable contracts; Menu retains navigation.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
12
13
|
import {render, screen} from '@testing-library/react';
|
|
13
|
-
import {useState} from 'react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {DropdownMenu} from './DropdownMenu';
|
|
16
16
|
import {DropdownMenuCheckboxItem} from './DropdownMenuCheckboxItem';
|
|
@@ -120,61 +120,6 @@ describe('DropdownMenuCheckboxItem', () => {
|
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
describe('DropdownMenuRadioGroup / RadioItem', () => {
|
|
123
|
-
it('keeps option-2 unchecked until it is activated', async () => {
|
|
124
|
-
const user = userEvent.setup();
|
|
125
|
-
|
|
126
|
-
function RadioPreview() {
|
|
127
|
-
const [value, setValue] = useState('option-1');
|
|
128
|
-
return (
|
|
129
|
-
<DropdownMenu button={{label: 'Sort'}}>
|
|
130
|
-
<DropdownMenuRadioGroup
|
|
131
|
-
value={value}
|
|
132
|
-
onChange={setValue}
|
|
133
|
-
label="Radio group">
|
|
134
|
-
<DropdownMenuRadioItem value="option-2" label="Option 2" />
|
|
135
|
-
</DropdownMenuRadioGroup>
|
|
136
|
-
</DropdownMenu>
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
render(<RadioPreview />);
|
|
141
|
-
await user.click(screen.getByRole('button', {name: /Sort/}));
|
|
142
|
-
|
|
143
|
-
const option = screen.getByRole('menuitemradio', {
|
|
144
|
-
name: 'Option 2',
|
|
145
|
-
hidden: true,
|
|
146
|
-
});
|
|
147
|
-
expect(option).toHaveAttribute('aria-checked', 'false');
|
|
148
|
-
|
|
149
|
-
await user.click(option);
|
|
150
|
-
expect(option).toHaveAttribute('aria-checked', 'true');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('renders a named group with radios reflecting the selected value', async () => {
|
|
154
|
-
const user = userEvent.setup();
|
|
155
|
-
render(
|
|
156
|
-
<DropdownMenu button={{label: 'Sort'}}>
|
|
157
|
-
<DropdownMenuRadioGroup
|
|
158
|
-
value="newest"
|
|
159
|
-
onChange={() => {}}
|
|
160
|
-
label="Sort by">
|
|
161
|
-
<DropdownMenuRadioItem value="newest" label="Newest" />
|
|
162
|
-
<DropdownMenuRadioItem value="oldest" label="Oldest" />
|
|
163
|
-
</DropdownMenuRadioGroup>
|
|
164
|
-
</DropdownMenu>,
|
|
165
|
-
);
|
|
166
|
-
await user.click(screen.getByRole('button', {name: /Sort/}));
|
|
167
|
-
expect(
|
|
168
|
-
screen.getByRole('menuitemradio', {name: 'Newest', hidden: true}),
|
|
169
|
-
).toHaveAttribute('aria-checked', 'true');
|
|
170
|
-
expect(
|
|
171
|
-
screen.getByRole('menuitemradio', {name: 'Oldest', hidden: true}),
|
|
172
|
-
).toHaveAttribute('aria-checked', 'false');
|
|
173
|
-
expect(
|
|
174
|
-
screen.getByRole('group', {name: 'Sort by', hidden: true}),
|
|
175
|
-
).toBeInTheDocument();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
123
|
it('renders the shared radio indicator in the menu marker', async () => {
|
|
179
124
|
const user = userEvent.setup();
|
|
180
125
|
render(
|
|
@@ -236,24 +181,6 @@ describe('DropdownMenuRadioGroup / RadioItem', () => {
|
|
|
236
181
|
expect(onChange).toHaveBeenCalledWith('oldest');
|
|
237
182
|
});
|
|
238
183
|
|
|
239
|
-
it('names the group from the required label prop', async () => {
|
|
240
|
-
const user = userEvent.setup();
|
|
241
|
-
render(
|
|
242
|
-
<DropdownMenu button={{label: 'Sort'}}>
|
|
243
|
-
<DropdownMenuRadioGroup
|
|
244
|
-
value="newest"
|
|
245
|
-
onChange={() => {}}
|
|
246
|
-
label="Sort by">
|
|
247
|
-
<DropdownMenuRadioItem value="newest" label="Newest" />
|
|
248
|
-
</DropdownMenuRadioGroup>
|
|
249
|
-
</DropdownMenu>,
|
|
250
|
-
);
|
|
251
|
-
await user.click(screen.getByRole('button', {name: /Sort/}));
|
|
252
|
-
expect(
|
|
253
|
-
screen.getByRole('group', {name: 'Sort by', hidden: true}),
|
|
254
|
-
).toBeInTheDocument();
|
|
255
|
-
});
|
|
256
|
-
|
|
257
184
|
it('throws when a radio item is used outside a group', () => {
|
|
258
185
|
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
259
186
|
expect(() =>
|
package/src/Field/Field.test.tsx
CHANGED
|
@@ -538,4 +538,46 @@ describe('Field', () => {
|
|
|
538
538
|
expect(labelWrapper.querySelector('label')).not.toBeNull();
|
|
539
539
|
});
|
|
540
540
|
});
|
|
541
|
+
|
|
542
|
+
describe('local stacking ownership', () => {
|
|
543
|
+
// Input wrappers paint their surface above the attached status message
|
|
544
|
+
// box through a local z-index, so that layer must be contained by a
|
|
545
|
+
// Field-owned isolation boundary. Otherwise a detached or tooltip field
|
|
546
|
+
// — whose input wrapper renders outside the attached-status wrapper —
|
|
547
|
+
// competes with page-level stacking (AppShell sticky header, later
|
|
548
|
+
// siblings) and can paint over unrelated chrome (#5689).
|
|
549
|
+
it.each(['attached', 'detached', 'tooltip'] as const)(
|
|
550
|
+
'isolates the field surface for the %s status variant',
|
|
551
|
+
variant => {
|
|
552
|
+
render(
|
|
553
|
+
<Field
|
|
554
|
+
label="Name"
|
|
555
|
+
inputID="name-input"
|
|
556
|
+
status={{type: 'error', message: 'Required'}}
|
|
557
|
+
statusVariant={variant}
|
|
558
|
+
data-testid="field">
|
|
559
|
+
<input id="name-input" data-testid="control" />
|
|
560
|
+
</Field>,
|
|
561
|
+
);
|
|
562
|
+
const field = screen.getByTestId('field');
|
|
563
|
+
// The Field root is the isolation owner: it stays at its normal
|
|
564
|
+
// parent paint level (it carries no positioning or z-index of its
|
|
565
|
+
// own) while containing every local layer — the input wrapper's 1
|
|
566
|
+
// and the attached status -1.
|
|
567
|
+
expect(getComputedStyle(field).isolation).toBe('isolate');
|
|
568
|
+
// Unpositioned and unranked — the owner never competes itself.
|
|
569
|
+
expect(getComputedStyle(field).zIndex).toBe('');
|
|
570
|
+
},
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
it('keeps a custom control without status isolated too', () => {
|
|
574
|
+
render(
|
|
575
|
+
<Field label="Name" inputID="name-input" data-testid="field">
|
|
576
|
+
<input id="name-input" data-testid="control" />
|
|
577
|
+
</Field>,
|
|
578
|
+
);
|
|
579
|
+
const field = screen.getByTestId('field');
|
|
580
|
+
expect(getComputedStyle(field).isolation).toBe('isolate');
|
|
581
|
+
});
|
|
582
|
+
});
|
|
541
583
|
});
|
package/src/Field/Field.tsx
CHANGED
|
@@ -35,6 +35,12 @@ const styles = stylex.create({
|
|
|
35
35
|
container: {
|
|
36
36
|
display: 'flex',
|
|
37
37
|
flexDirection: 'column',
|
|
38
|
+
// The Field root owns the local stacking boundary (AST-027): the input
|
|
39
|
+
// wrapper's z-index (1, above the attached status box) and the attached
|
|
40
|
+
// status layer (-1) order parts inside this surface only. Without this,
|
|
41
|
+
// detached/tooltip fields — whose input wrapper renders outside the
|
|
42
|
+
// attached-status wrapper — compete with page-level stacking (#5689).
|
|
43
|
+
isolation: 'isolate',
|
|
38
44
|
},
|
|
39
45
|
containerGap: {
|
|
40
46
|
gap: spacingVars['--spacing-1'],
|
|
@@ -19,7 +19,11 @@ import path from 'node:path';
|
|
|
19
19
|
import {describe, it, expect, vi, afterEach} from 'vitest';
|
|
20
20
|
import {render, screen, fireEvent} from '@testing-library/react';
|
|
21
21
|
import * as stylex from '@stylexjs/stylex';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
InputClearButton,
|
|
24
|
+
InternalInputClearButton,
|
|
25
|
+
type InputClearButtonProps,
|
|
26
|
+
} from './InputClearButton';
|
|
23
27
|
import {Icon, registerIcons, resetIcons} from '../Icon';
|
|
24
28
|
import {defineTheme} from '../theme/defineTheme';
|
|
25
29
|
import {generateThemeCSS} from '../theme/generateThemeRules';
|
|
@@ -323,3 +327,33 @@ describe('InputClearButton glyph source', () => {
|
|
|
323
327
|
expect(getGlyph()).toContainElement(screen.getByTestId('custom-close'));
|
|
324
328
|
});
|
|
325
329
|
});
|
|
330
|
+
|
|
331
|
+
describe('InputClearButton pointer and focus interactions', () => {
|
|
332
|
+
it('prevents default on pointerdown and mousedown to keep focus on input', () => {
|
|
333
|
+
render(<InputClearButton label="Clear" onClick={() => {}} />);
|
|
334
|
+
const button = screen.getByRole('button', {name: 'Clear'});
|
|
335
|
+
|
|
336
|
+
const pointerDownEvent = fireEvent.pointerDown(button);
|
|
337
|
+
expect(pointerDownEvent).toBe(false);
|
|
338
|
+
|
|
339
|
+
const mouseDownEvent = fireEvent.mouseDown(button);
|
|
340
|
+
expect(mouseDownEvent).toBe(false);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('composes custom onPointerDown while preventing default in InternalInputClearButton', () => {
|
|
344
|
+
const handlePointerDown = vi.fn();
|
|
345
|
+
render(
|
|
346
|
+
<InternalInputClearButton
|
|
347
|
+
label="Clear"
|
|
348
|
+
onClick={() => {}}
|
|
349
|
+
onPointerDown={handlePointerDown}
|
|
350
|
+
onClickCapture={() => {}}
|
|
351
|
+
/>,
|
|
352
|
+
);
|
|
353
|
+
const button = screen.getByRole('button', {name: 'Clear'});
|
|
354
|
+
const pointerDownEvent = fireEvent.pointerDown(button);
|
|
355
|
+
|
|
356
|
+
expect(handlePointerDown).toHaveBeenCalledTimes(1);
|
|
357
|
+
expect(pointerDownEvent).toBe(false);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
@@ -73,8 +73,8 @@ export interface InputClearButtonProps {
|
|
|
73
73
|
* Extra class(es) for the clear glyph itself, merged onto the shared
|
|
74
74
|
* `astryx-input-clear-icon` target. Used by inputs that shipped a
|
|
75
75
|
* component-specific clear-icon target before the family converged here
|
|
76
|
-
* (e.g. `astryx-date-input-clear-icon`) to keep emitting it for
|
|
77
|
-
*
|
|
76
|
+
* (e.g. `astryx-date-input-clear-icon`) to keep emitting it for backwards
|
|
77
|
+
* compatibility; new callers don't need it.
|
|
78
78
|
*/
|
|
79
79
|
iconClassName?: string;
|
|
80
80
|
}
|
|
@@ -117,8 +117,12 @@ function renderInputClearButton({
|
|
|
117
117
|
}
|
|
118
118
|
/>
|
|
119
119
|
}
|
|
120
|
+
onPointerDown={e => {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
onPointerDown?.(e);
|
|
123
|
+
}}
|
|
124
|
+
onMouseDown={e => e.preventDefault()}
|
|
120
125
|
onClick={onClick}
|
|
121
|
-
onPointerDown={onPointerDown}
|
|
122
126
|
onClickCapture={onClickCapture}
|
|
123
127
|
isIconOnly
|
|
124
128
|
xstyle={[styles.button, xstyle]}
|
|
@@ -225,14 +225,27 @@ export function PanelSearchInput({
|
|
|
225
225
|
[onBlur],
|
|
226
226
|
);
|
|
227
227
|
|
|
228
|
-
const handleClear = useCallback(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
228
|
+
const handleClear = useCallback(
|
|
229
|
+
(e?: React.MouseEvent<HTMLButtonElement>) => {
|
|
230
|
+
onValueChange('');
|
|
231
|
+
// Clearing puts the caret back where the user was typing, matching
|
|
232
|
+
// TextInput's built-in clear. Defer focus restoration past the button's
|
|
233
|
+
// unmount task so touch browsers don't jump page scroll on tap, while
|
|
234
|
+
// preserving synchronous focus for keyboard users.
|
|
235
|
+
if (!e || e.detail === 0) {
|
|
236
|
+
if (typeof ref === 'object' && ref?.current) {
|
|
237
|
+
ref.current.focus();
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
requestAnimationFrame(() => {
|
|
241
|
+
if (typeof ref === 'object' && ref?.current) {
|
|
242
|
+
ref.current.focus({preventScroll: true});
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
[onValueChange, ref],
|
|
248
|
+
);
|
|
236
249
|
|
|
237
250
|
return (
|
|
238
251
|
<div
|
|
@@ -13,6 +13,8 @@ review_triggers: [public-api, behavior, theming, accessibility]
|
|
|
13
13
|
verified_by:
|
|
14
14
|
[
|
|
15
15
|
packages/core/src/FieldStatus/FieldStatus.test.tsx,
|
|
16
|
+
packages/core/src/FieldStatus/__tests__/StatusMessage.a11y.test.tsx,
|
|
17
|
+
packages/core/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts,
|
|
16
18
|
scripts/check-knowledge.mjs,
|
|
17
19
|
]
|
|
18
20
|
modules: []
|
|
@@ -20,7 +22,7 @@ families: []
|
|
|
20
22
|
design_specs: []
|
|
21
23
|
architecture: [architecture:component-theming-surface]
|
|
22
24
|
contributing: []
|
|
23
|
-
system_specs: []
|
|
25
|
+
system_specs: [spec:AST-009, spec:AST-020, spec:AST-021]
|
|
24
26
|
---
|
|
25
27
|
|
|
26
28
|
# FieldStatus component contract
|
|
@@ -116,23 +118,28 @@ intentional decision.
|
|
|
116
118
|
|
|
117
119
|
### Performance and resources
|
|
118
120
|
|
|
119
|
-
- **PR1 —
|
|
120
|
-
live-region
|
|
121
|
-
|
|
121
|
+
- **PR1 — Channel-update work.** A message change schedules only the persistent
|
|
122
|
+
live-region update; FieldStatus owns no global event listeners, observers, or
|
|
123
|
+
measurements.
|
|
122
124
|
|
|
123
125
|
## Accessibility contract
|
|
124
126
|
|
|
125
|
-
- **AR1 —
|
|
126
|
-
assertive region; warnings and successes MUST
|
|
127
|
-
|
|
127
|
+
- **AR1 — Programmatic announcement channel.** Errors MUST update the persistent
|
|
128
|
+
assertive region; warnings and successes MUST update the persistent polite
|
|
129
|
+
region, including on first mount and when message or type changes, without
|
|
130
|
+
moving focus.
|
|
131
|
+
- **AR5 — Assistive-technology output.** Claims about whether, when, or how the
|
|
132
|
+
message is spoken or presented in braille MUST be verified through the real-AT
|
|
133
|
+
evidence process in `spec:AST-009`; DOM and accessibility-tree checks prove
|
|
134
|
+
only programmatic exposure and routing.
|
|
128
135
|
- **AR2 — Visible description.** The rendered message box MUST remain available
|
|
129
136
|
to assistive technology for `aria-describedby` association and MUST NOT become
|
|
130
137
|
its own newly mounted live region.
|
|
131
138
|
- **AR3 — Non-color cue.** Detached status MUST include a leading status glyph so
|
|
132
139
|
status is not conveyed only by color or position.
|
|
133
140
|
- **AR4 — Redundant glyph.** The detached glyph MUST remain hidden from assistive
|
|
134
|
-
technology because the visible message already conveys
|
|
135
|
-
|
|
141
|
+
technology because the visible message already conveys the status in text;
|
|
142
|
+
announcement output remains owned by AR5.
|
|
136
143
|
|
|
137
144
|
## Design relationships
|
|
138
145
|
|
|
@@ -140,7 +147,7 @@ intentional decision.
|
|
|
140
147
|
| ---------------- | ---------------------------------------------------------- | ----------------------------------------------- | -------------- | ------------------ |
|
|
141
148
|
| Message box | Carries the visible validation treatment. | Current behavior and approved component intent. | Supporting | FR1, FR3 |
|
|
142
149
|
| Detached icon | Adds a non-color status cue only in detached presentation. | Current behavior and approved component intent. | Supporting | FR2, FR4, AR3 |
|
|
143
|
-
| Message text | Communicates the validation result in words. | Current behavior and approved component intent. | Prominent | FR1, AR1, AR2
|
|
150
|
+
| Message text | Communicates the validation result in words. | Current behavior and approved component intent. | Prominent | FR1, AR1, AR2, AR5 |
|
|
144
151
|
|
|
145
152
|
Current behavior renders the detached icon before message text. The intended
|
|
146
153
|
public theming ownership is the exact map below; it does not add a target or
|
|
@@ -167,13 +174,16 @@ change either presentation.
|
|
|
167
174
|
|
|
168
175
|
## Verification map
|
|
169
176
|
|
|
170
|
-
| Contract | Verification
|
|
171
|
-
| ------------------- |
|
|
172
|
-
| FR1, FR2, AR3, AR4 | `FieldStatus.test.tsx` rendering and detached-icon suites
|
|
173
|
-
| FR3, FR4 | `FieldStatus.test.tsx` theme target suites
|
|
174
|
-
| FR5, FR6 | `FieldStatus.test.tsx` forwarding and update suites
|
|
175
|
-
| AR1
|
|
176
|
-
|
|
|
177
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
178
|
+
| ------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
|
|
179
|
+
| FR1, FR2, AR3, AR4 | `FieldStatus.test.tsx` rendering and detached-icon suites | Attached and detached; all status types | Removing or reordering presentation parts fails DOM and accessibility assertions. | `audit:FieldStatus/anatomy` |
|
|
180
|
+
| FR3, FR4 | `FieldStatus.test.tsx` theme target suites | Attached/detached; error/warning/success | Removing a target or status reflection fails class/data-attribute assertions. | `audit:FieldStatus/theming` |
|
|
181
|
+
| FR5, FR6 | `FieldStatus.test.tsx` forwarding and update suites | Initial render and rerender | Breaking the released root or update behavior fails compatibility assertions. | `audit:FieldStatus/behavior` |
|
|
182
|
+
| AR1 | `StatusMessage.a11y.test.tsx` and `StatusMessage.a11y.chromium.spec.ts` | Error, warning, and success; post-mount message replacement | Removing the persistent channel, changing its role/urgency, or dropping a post-mount update fails the shared status-message contract. | `audit:FieldStatus/accessibility` |
|
|
183
|
+
| AR1 | `FieldStatus.test.tsx` channel-routing suite | First non-empty mount; same-instance message and type changes | Breaking FieldStatus's first-use hook call or severity rerouting fails component-specific assertions. | `audit:FieldStatus/accessibility` |
|
|
184
|
+
| AR5 | Pending/unverified real-AT evidence governed by `spec:AST-009` | First mount; message replacement; type/urgency change | Spoken and braille output remain unverified until a durable named AT/browser receipt exists. | `audit:FieldStatus/accessibility` |
|
|
185
|
+
| AR2 | `FieldStatus.test.tsx` | Attached/detached visible message and empty message | The visible description becomes a second live region or is hidden from assistive technology. | `audit:FieldStatus/accessibility` |
|
|
186
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Consumer anatomy and current targets | Missing, extra, duplicated, prefixed, or stale mappings fail repository validation. | `audit:FieldStatus/theming` |
|
|
177
187
|
|
|
178
188
|
## Decision log
|
|
179
189
|
|
|
@@ -53,7 +53,8 @@ describe('FieldStatus', () => {
|
|
|
53
53
|
expect(el).not.toHaveAttribute('aria-live');
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
//
|
|
56
|
+
// These remain local because they exercise FieldStatus's first-use hook
|
|
57
|
+
// routing. The shared binding starts from an already established channel.
|
|
57
58
|
it('announces error messages assertively, including on first mount', async () => {
|
|
58
59
|
render(<FieldStatus type="error" message="This field is required" />);
|
|
59
60
|
await waitFor(() => {
|
|
@@ -62,7 +63,7 @@ describe('FieldStatus', () => {
|
|
|
62
63
|
expect(politeRegion()).toHaveTextContent('');
|
|
63
64
|
});
|
|
64
65
|
|
|
65
|
-
it('announces warning messages politely', async () => {
|
|
66
|
+
it('announces warning messages politely on first mount', async () => {
|
|
66
67
|
render(<FieldStatus type="warning" message="Check this value" />);
|
|
67
68
|
await waitFor(() => {
|
|
68
69
|
expect(politeRegion()).toHaveTextContent('Check this value');
|
|
@@ -70,14 +71,14 @@ describe('FieldStatus', () => {
|
|
|
70
71
|
expect(assertiveRegion()).toHaveTextContent('');
|
|
71
72
|
});
|
|
72
73
|
|
|
73
|
-
it('announces success messages politely', async () => {
|
|
74
|
+
it('announces success messages politely on first mount', async () => {
|
|
74
75
|
render(<FieldStatus type="success" message="Looks good" />);
|
|
75
76
|
await waitFor(() => {
|
|
76
77
|
expect(politeRegion()).toHaveTextContent('Looks good');
|
|
77
78
|
});
|
|
78
79
|
});
|
|
79
80
|
|
|
80
|
-
it('announces message changes', async () => {
|
|
81
|
+
it('announces message changes through the component hook', async () => {
|
|
81
82
|
const {rerender} = render(<FieldStatus type="error" message="First" />);
|
|
82
83
|
await waitFor(() => {
|
|
83
84
|
expect(assertiveRegion()).toHaveTextContent('First');
|
|
@@ -88,7 +89,8 @@ describe('FieldStatus', () => {
|
|
|
88
89
|
});
|
|
89
90
|
});
|
|
90
91
|
|
|
91
|
-
//
|
|
92
|
+
// The generic contract checks each fixed urgency. This local test protects
|
|
93
|
+
// the component-specific same-instance type reroute.
|
|
92
94
|
it('re-routes to the polite channel when type changes from error', async () => {
|
|
93
95
|
const {rerender} = render(<FieldStatus type="error" message="msg" />);
|
|
94
96
|
await waitFor(() => {
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file StatusMessage.a11y.chromium.spec.ts
|
|
5
|
+
* @input Uses the shared status-message contract, checked-in stories, Core state inventory, and exact known failures
|
|
6
|
+
* @output Real-browser and accessibility-tree evidence for every current Core status-message binding
|
|
7
|
+
* @position Browser migration lane; it makes no claim about assistive-technology speech or timing
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
expect,
|
|
12
|
+
test,
|
|
13
|
+
type CDPSession,
|
|
14
|
+
type Locator,
|
|
15
|
+
type Page,
|
|
16
|
+
} from '@playwright/test';
|
|
17
|
+
import {
|
|
18
|
+
STATUS_MESSAGE_PATTERN,
|
|
19
|
+
blockingResults,
|
|
20
|
+
checkAccessibilitySpec,
|
|
21
|
+
formatFailures,
|
|
22
|
+
formatReport,
|
|
23
|
+
neverExercised,
|
|
24
|
+
summarize,
|
|
25
|
+
unmatchedKnownFailures,
|
|
26
|
+
type BindingResult,
|
|
27
|
+
} from '@astryxdesign/a11y-spec';
|
|
28
|
+
import {
|
|
29
|
+
createChromiumHarness,
|
|
30
|
+
holdMotionStill,
|
|
31
|
+
} from '@astryxdesign/a11y-spec/chromium';
|
|
32
|
+
import {
|
|
33
|
+
DEFAULT_STORYBOOK_DIR,
|
|
34
|
+
serveStorybook,
|
|
35
|
+
type StaticServer,
|
|
36
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
37
|
+
import {CORE_STATUS_MESSAGE_KNOWN_FAILURES} from './StatusMessage.a11y.known-failures';
|
|
38
|
+
import {
|
|
39
|
+
CORE_STATUS_MESSAGE_BINDING_STATES,
|
|
40
|
+
type CoreStatusMessageBindingState,
|
|
41
|
+
} from './StatusMessage.a11y.states';
|
|
42
|
+
|
|
43
|
+
function transitionTestId(name: string): string {
|
|
44
|
+
return `status-transition-${name}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let storybook: StaticServer;
|
|
48
|
+
|
|
49
|
+
test.beforeAll(async () => {
|
|
50
|
+
storybook = await serveStorybook(
|
|
51
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test.afterAll(async () => {
|
|
56
|
+
await storybook?.close();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
function storyUrl(storyId: string): string {
|
|
60
|
+
return `${storybook.origin}/iframe.html?id=${storyId}&viewMode=story`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const BOUND_SUBJECT_ATTRIBUTE = 'data-a11y-binding-subject';
|
|
64
|
+
|
|
65
|
+
function subjectFor(page: Page): Locator {
|
|
66
|
+
return page.locator(`[${BOUND_SUBJECT_ATTRIBUTE}]`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function bindSubject(
|
|
70
|
+
page: Page,
|
|
71
|
+
state: CoreStatusMessageBindingState,
|
|
72
|
+
): Promise<void> {
|
|
73
|
+
const facts = state.facts;
|
|
74
|
+
const role = facts.kind === 'progressbar' ? 'progressbar' : facts.role;
|
|
75
|
+
if (role == null) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`${state.id}: this component binding declares no public role`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const candidates = page.getByRole(role, {includeHidden: true});
|
|
81
|
+
let target: Locator;
|
|
82
|
+
if (facts.kind === 'progressbar') {
|
|
83
|
+
target = page.getByRole(role, {includeHidden: true, name: facts.name});
|
|
84
|
+
} else if (facts.messageSource === 'accessible-name') {
|
|
85
|
+
target = page.getByRole(role, {
|
|
86
|
+
includeHidden: true,
|
|
87
|
+
name: facts.initialMessage,
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
const texts = await candidates.evaluateAll(elements =>
|
|
91
|
+
elements.map(element =>
|
|
92
|
+
(element.textContent ?? '').replace(/\s+/g, ' ').trim(),
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
const indexes = texts.flatMap((text, index) =>
|
|
96
|
+
text === facts.initialMessage ? [index] : [],
|
|
97
|
+
);
|
|
98
|
+
if (indexes.length !== 1) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`${state.id}: expected one ${role} subject in initial state, found ${indexes.length}`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
target = candidates.nth(indexes[0]);
|
|
104
|
+
}
|
|
105
|
+
await expect(target).toHaveCount(1);
|
|
106
|
+
await target.evaluate((element, attribute) => {
|
|
107
|
+
element.setAttribute(attribute, '');
|
|
108
|
+
}, BOUND_SUBJECT_ATTRIBUTE);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function mountState(
|
|
112
|
+
page: Page,
|
|
113
|
+
state: CoreStatusMessageBindingState,
|
|
114
|
+
): Promise<void> {
|
|
115
|
+
await page.goto(storyUrl(state.storyId), {waitUntil: 'load'});
|
|
116
|
+
await holdMotionStill(page);
|
|
117
|
+
await page.locator('[data-a11y-ready="true"]').waitFor({state: 'attached'});
|
|
118
|
+
await bindSubject(page, state);
|
|
119
|
+
await subjectFor(page).waitFor({state: 'attached'});
|
|
120
|
+
await expect(subjectFor(page)).toHaveCount(1);
|
|
121
|
+
if ('focusSelector' in state) {
|
|
122
|
+
await expect(page.locator(state.focusSelector)).toHaveAttribute(
|
|
123
|
+
'aria-describedby',
|
|
124
|
+
/\S+/,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function transition(page: Page, name: string): Promise<void> {
|
|
130
|
+
await page
|
|
131
|
+
.getByTestId(transitionTestId(name))
|
|
132
|
+
.evaluate(element => (element as HTMLElement).click());
|
|
133
|
+
await page.evaluate(
|
|
134
|
+
async () =>
|
|
135
|
+
new Promise<void>(resolve =>
|
|
136
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve())),
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function runState(
|
|
142
|
+
page: Page,
|
|
143
|
+
cdp: CDPSession,
|
|
144
|
+
state: CoreStatusMessageBindingState,
|
|
145
|
+
): Promise<BindingResult> {
|
|
146
|
+
return checkAccessibilitySpec({
|
|
147
|
+
spec: STATUS_MESSAGE_PATTERN,
|
|
148
|
+
binding: state.binding,
|
|
149
|
+
state: state.id,
|
|
150
|
+
facts: state.facts,
|
|
151
|
+
knownFailures: CORE_STATUS_MESSAGE_KNOWN_FAILURES,
|
|
152
|
+
mount: async () => {
|
|
153
|
+
await mountState(page, state);
|
|
154
|
+
return createChromiumHarness({
|
|
155
|
+
page,
|
|
156
|
+
cdp,
|
|
157
|
+
subject: subjectFor(page),
|
|
158
|
+
related: {
|
|
159
|
+
'focus-anchor': page.locator(
|
|
160
|
+
'focusSelector' in state
|
|
161
|
+
? state.focusSelector
|
|
162
|
+
: '[data-a11y-relation="focus-anchor"]',
|
|
163
|
+
),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
transition: async name => transition(page, name),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
test('every expectation is exercised by at least one Core binding state', async ({
|
|
172
|
+
page,
|
|
173
|
+
}) => {
|
|
174
|
+
test.setTimeout(4 * 60 * 1000);
|
|
175
|
+
const cdp = await page.context().newCDPSession(page);
|
|
176
|
+
const results: BindingResult[] = [];
|
|
177
|
+
for (const state of CORE_STATUS_MESSAGE_BINDING_STATES) {
|
|
178
|
+
results.push(await runState(page, cdp, state));
|
|
179
|
+
}
|
|
180
|
+
expect(neverExercised(results)).toEqual([]);
|
|
181
|
+
expect(
|
|
182
|
+
unmatchedKnownFailures(CORE_STATUS_MESSAGE_KNOWN_FAILURES, results),
|
|
183
|
+
).toEqual([]);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
for (const state of CORE_STATUS_MESSAGE_BINDING_STATES) {
|
|
187
|
+
test(`${state.binding} [${state.id}] — ${state.summary}`, async ({page}) => {
|
|
188
|
+
test.setTimeout(2 * 60 * 1000);
|
|
189
|
+
const cdp = await page.context().newCDPSession(page);
|
|
190
|
+
const result = await runState(page, cdp, state);
|
|
191
|
+
const report = summarize(STATUS_MESSAGE_PATTERN, [result]);
|
|
192
|
+
// eslint-disable-next-line no-console -- the report is this run's artifact
|
|
193
|
+
console.log(formatReport(report));
|
|
194
|
+
expect(formatFailures(blockingResults([result]))).toBe('');
|
|
195
|
+
expect(report.unrunLayers).toEqual([]);
|
|
196
|
+
expect(report.counts.unexpectedPass).toBe(0);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file StatusMessage.a11y.known-failures.ts
|
|
5
|
+
* @input Uses the exact known-failure vocabulary from the shared runner
|
|
6
|
+
* @output Public-safe Core status-message debt records (currently none)
|
|
7
|
+
* @position Migration debt only; operational ownership is maintained outside this public repository
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {KnownFailure} from '@astryxdesign/a11y-spec';
|
|
11
|
+
|
|
12
|
+
export const CORE_STATUS_MESSAGE_KNOWN_FAILURES: ReadonlyArray<KnownFailure> =
|
|
13
|
+
[];
|