@astryxdesign/core 0.1.4-canary.37c19d3 → 0.1.4-canary.39ffd21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +1 -8
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +11 -31
- package/dist/Calendar/styles.d.ts +2 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +2 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +0 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +1 -8
- package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +1 -3
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -26
- package/dist/CodeBlock/CodeBlock.d.ts +1 -10
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +5 -23
- package/dist/Collapsible/Collapsible.d.ts +3 -9
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +13 -101
- package/dist/Collapsible/CollapsibleGroup.d.ts +7 -39
- package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroup.js +9 -70
- package/dist/Collapsible/CollapsibleGroupContext.d.ts +0 -31
- package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroupContext.js +3 -33
- package/dist/Collapsible/index.d.ts +0 -1
- package/dist/Collapsible/index.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +0 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +0 -2
- package/dist/Grid/Grid.d.ts +3 -4
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +9 -26
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +1 -5
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +2 -26
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +16 -32
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +36 -87
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +21 -39
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +1 -2
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +2 -10
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +2 -6
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +7 -34
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +2 -12
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +2 -5
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +1 -5
- package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelectionState.js +1 -11
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.js +4 -8
- package/dist/Thumbnail/Thumbnail.d.ts +2 -3
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +0 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +1 -6
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +3 -11
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +0 -12
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +2 -5
- package/dist/astryx.css +0 -7
- package/dist/astryx.umd.js +48 -48
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/focusableSelector.d.ts +1 -1
- package/dist/hooks/focusableSelector.d.ts.map +1 -1
- package/dist/hooks/focusableSelector.js +1 -1
- package/dist/hooks/useFocusTrap.d.ts +0 -3
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +1 -38
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +3 -10
- package/dist/theme/hct.d.ts +15 -0
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +9 -9
- package/dist/theme/tokens.d.ts +2 -10
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +4 -220
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -2
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +5 -8
- package/package.json +1 -1
- package/src/Avatar/Avatar.doc.mjs +0 -11
- package/src/Banner/Banner.doc.mjs +0 -8
- package/src/Banner/Banner.test.tsx +0 -38
- package/src/Banner/Banner.tsx +1 -8
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Calendar/Calendar.test.tsx +4 -112
- package/src/Calendar/Calendar.tsx +5 -26
- package/src/Calendar/styles.ts +2 -12
- package/src/Carousel/Carousel.doc.mjs +12 -12
- package/src/Carousel/Carousel.test.tsx +0 -13
- package/src/Carousel/Carousel.tsx +0 -1
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatToolCalls.test.tsx +0 -55
- package/src/Chat/ChatToolCalls.tsx +12 -10
- package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -3
- package/src/CheckboxInput/CheckboxInput.test.tsx +4 -71
- package/src/CheckboxInput/CheckboxInput.tsx +0 -2
- package/src/Citation/Citation.doc.mjs +0 -5
- package/src/ClickableCard/ClickableCard.tsx +2 -45
- package/src/CodeBlock/CodeBlock.doc.mjs +2 -7
- package/src/CodeBlock/CodeBlock.test.tsx +3 -87
- package/src/CodeBlock/CodeBlock.tsx +3 -43
- package/src/Collapsible/Collapsible.doc.mjs +4 -8
- package/src/Collapsible/Collapsible.tsx +10 -86
- package/src/Collapsible/CollapsibleGroup.doc.mjs +3 -27
- package/src/Collapsible/CollapsibleGroup.test.tsx +0 -288
- package/src/Collapsible/CollapsibleGroup.tsx +9 -107
- package/src/Collapsible/CollapsibleGroupContext.tsx +2 -42
- package/src/Collapsible/index.ts +0 -4
- package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +0 -5
- package/src/CommandPalette/CommandPaletteGroup.doc.mjs +0 -10
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +0 -6
- package/src/CommandPalette/CommandPaletteItem.doc.mjs +0 -6
- package/src/CommandPalette/CommandPaletteList.doc.mjs +0 -9
- package/src/ContextMenu/ContextMenu.doc.mjs +0 -15
- package/src/ContextMenu/ContextMenu.test.tsx +0 -15
- package/src/ContextMenu/ContextMenu.tsx +0 -2
- package/src/ContextMenu/ContextMenuItem.doc.mjs +0 -6
- package/src/DateTimeInput/DateTimeInput.test.tsx +0 -70
- package/src/DateTimeInput/DateTimeInput.tsx +0 -2
- package/src/Dialog/DialogHeader.doc.mjs +0 -45
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
- package/src/Field/FieldLabel.test.tsx +2 -2
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +3 -0
- package/src/Grid/Grid.test.tsx +18 -43
- package/src/Grid/Grid.tsx +9 -26
- package/src/Icon/Icon.test.tsx +29 -41
- package/src/Icon/Icon.tsx +2 -6
- package/src/Layout/LayoutContent.doc.mjs +0 -12
- package/src/Layout/LayoutPanel.doc.mjs +0 -40
- package/src/Lightbox/Lightbox.doc.mjs +0 -5
- package/src/Lightbox/Lightbox.test.tsx +2 -123
- package/src/Lightbox/Lightbox.tsx +1 -29
- package/src/NavMenu/NavMenu.doc.mjs +0 -15
- package/src/NumberInput/NumberInput.test.tsx +3 -48
- package/src/NumberInput/NumberInput.tsx +15 -32
- package/src/Overlay/Overlay.doc.mjs +0 -6
- package/src/Pagination/Pagination.test.tsx +0 -143
- package/src/Pagination/Pagination.tsx +23 -75
- package/src/Popover/Popover.test.tsx +0 -65
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearch.test.tsx +1 -109
- package/src/PowerSearch/PowerSearch.tsx +16 -37
- package/src/ProgressBar/ProgressBar.test.tsx +0 -10
- package/src/ProgressBar/ProgressBar.tsx +1 -2
- package/src/Resizable/ResizeHandle.tsx +2 -10
- package/src/SegmentedControl/SegmentedControl.doc.mjs +1 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/SideNav/SideNav.doc.mjs +1 -7
- package/src/SideNav/SideNav.test.tsx +0 -20
- package/src/SideNav/SideNav.tsx +2 -6
- package/src/TabList/TabList.test.tsx +1 -185
- package/src/TabList/TabMenu.tsx +9 -45
- package/src/Table/BaseTable.tsx +1 -11
- package/src/Table/Table.test.tsx +0 -51
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +0 -13
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +1 -3
- package/src/Table/plugins/pagination/paginateData.ts +1 -5
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -13
- package/src/Table/plugins/selection/useTableSelectionState.test.tsx +0 -23
- package/src/Table/plugins/selection/useTableSelectionState.tsx +6 -11
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +1 -35
- package/src/Table/plugins/sortable/useTableSortableState.tsx +4 -8
- package/src/TextArea/TextArea.test.tsx +3 -3
- package/src/TextInput/TextInput.test.tsx +3 -3
- package/src/Thumbnail/Thumbnail.test.tsx +2 -36
- package/src/Thumbnail/Thumbnail.tsx +2 -4
- package/src/TimeInput/TimeInput.test.tsx +0 -37
- package/src/TimeInput/TimeInput.tsx +1 -11
- package/src/Timestamp/Timestamp.doc.mjs +1 -1
- package/src/Toast/Toast.tsx +3 -11
- package/src/Toast/ToastViewport.test.tsx +0 -64
- package/src/Toast/ToastViewport.tsx +0 -12
- package/src/ToggleButton/ToggleButton.doc.mjs +0 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
- package/src/TopNav/TopNav.doc.mjs +1 -1
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/Typeahead/BaseTypeahead.doc.mjs +0 -19
- package/src/Typeahead/BaseTypeahead.tsx +2 -5
- package/src/Typeahead/Typeahead.doc.mjs +1 -1
- package/src/Typeahead/Typeahead.test.tsx +1 -45
- package/src/hooks/focusableSelector.ts +1 -1
- package/src/hooks/useFocusTrap.doc.mjs +2 -4
- package/src/hooks/useFocusTrap.test.tsx +1 -149
- package/src/hooks/useFocusTrap.ts +1 -50
- package/src/theme/SyntaxTheme.doc.mjs +4 -4
- package/src/theme/defineTheme.ts +13 -4
- package/src/theme/derivedVarRegistry.test.ts +2 -2
- package/src/theme/expandColorScale.test.ts +0 -29
- package/src/theme/expandColorScale.ts +6 -11
- package/src/theme/hct.ts +17 -9
- package/src/theme/tokens.test.ts +9 -182
- package/src/theme/tokens.ts +4 -239
- package/src/theme/useTheme.test.tsx +0 -20
- package/src/utils/index.ts +0 -3
- package/src/utils/timeParser.test.ts +0 -13
- package/src/utils/timeParser.ts +5 -8
- package/dist/utils/color.d.ts +0 -42
- package/dist/utils/color.d.ts.map +0 -1
- package/dist/utils/color.js +0 -150
- package/src/Resizable/ResizeHandle.test.tsx +0 -180
- package/src/__tests__/TestIcon.tsx +0 -26
- package/src/docPropReferences.test.ts +0 -178
- package/src/utils/color.test.ts +0 -133
- package/src/utils/color.ts +0 -148
package/src/Grid/Grid.test.tsx
CHANGED
|
@@ -65,7 +65,7 @@ describe('Grid', () => {
|
|
|
65
65
|
expect(templateColumns(grid)).toBe('repeat(auto-fill, minmax(250px, 1fr))');
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
it('renders with columns object max (
|
|
68
|
+
it('renders with columns object max (capped via track-max)', () => {
|
|
69
69
|
render(
|
|
70
70
|
<Grid columns={{minWidth: 250, max: 3}} gap={4} data-testid="grid">
|
|
71
71
|
<div>Item 1</div>
|
|
@@ -74,37 +74,13 @@ describe('Grid', () => {
|
|
|
74
74
|
</Grid>,
|
|
75
75
|
);
|
|
76
76
|
const grid = screen.getByTestId('grid');
|
|
77
|
-
//
|
|
78
|
-
// track MAX stays 1fr so present columns fill the row (a lone column on
|
|
79
|
-
// mobile stretches to 100% instead of leaving dead space).
|
|
77
|
+
// Track max caps at (100% - 2 * gap) / 3 — no maxWidth on container
|
|
80
78
|
expect(templateColumns(grid)).toBe(
|
|
81
|
-
'repeat(auto-fill, minmax(
|
|
79
|
+
'repeat(auto-fill, minmax(250px, calc((100% - 2 * var(--spacing-4)) / 3)))',
|
|
82
80
|
);
|
|
83
81
|
expect(grid.style.maxWidth).toBe('');
|
|
84
82
|
});
|
|
85
83
|
|
|
86
|
-
it('keeps track max at 1fr with a max cap so a lone column can fill (#3391)', () => {
|
|
87
|
-
// Regression: previously the cap was applied to the track MAX
|
|
88
|
-
// (minmax(minWidth, 100%/max)), so when fewer than `max` columns fit —
|
|
89
|
-
// e.g. a single column on mobile — the lone column was pinned to ~100%/max
|
|
90
|
-
// and left dead space on the right. The cap now lives on the track MIN and
|
|
91
|
-
// the track MAX stays 1fr, so present columns always stretch to fill.
|
|
92
|
-
render(
|
|
93
|
-
<Grid columns={{minWidth: 360, max: 2}} gap={4} data-testid="grid">
|
|
94
|
-
<div>Left</div>
|
|
95
|
-
<div>Right</div>
|
|
96
|
-
</Grid>,
|
|
97
|
-
);
|
|
98
|
-
const grid = screen.getByTestId('grid');
|
|
99
|
-
const template = templateColumns(grid);
|
|
100
|
-
// Track max must be 1fr (fills), not a fraction-of-container cap.
|
|
101
|
-
expect(template).toMatch(/, 1fr\)\)$/);
|
|
102
|
-
expect(template).not.toMatch(/, calc\([^)]*\/ 2\)\)\)$/);
|
|
103
|
-
expect(template).toBe(
|
|
104
|
-
'repeat(auto-fill, minmax(min(100%, max(360px, calc((100% - 1 * var(--spacing-4)) / 2))), 1fr))',
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
84
|
it('renders with columns object max using columnGap', () => {
|
|
109
85
|
render(
|
|
110
86
|
<Grid columns={{minWidth: 200, max: 4}} columnGap={6} data-testid="grid">
|
|
@@ -113,9 +89,9 @@ describe('Grid', () => {
|
|
|
113
89
|
</Grid>,
|
|
114
90
|
);
|
|
115
91
|
const grid = screen.getByTestId('grid');
|
|
116
|
-
// columnGap takes precedence
|
|
92
|
+
// columnGap takes precedence for track-max calculation
|
|
117
93
|
expect(templateColumns(grid)).toBe(
|
|
118
|
-
'repeat(auto-fill, minmax(
|
|
94
|
+
'repeat(auto-fill, minmax(200px, calc((100% - 3 * var(--spacing-6)) / 4)))',
|
|
119
95
|
);
|
|
120
96
|
expect(grid.style.maxWidth).toBe('');
|
|
121
97
|
});
|
|
@@ -189,7 +165,7 @@ describe('Grid', () => {
|
|
|
189
165
|
expect(templateColumns(grid)).toBe('1fr');
|
|
190
166
|
});
|
|
191
167
|
|
|
192
|
-
it('uses auto-fill
|
|
168
|
+
it('uses auto-fill without track-max cap when no max specified', () => {
|
|
193
169
|
render(
|
|
194
170
|
<Grid columns={{minWidth: 200}} data-testid="grid">
|
|
195
171
|
<div>Item</div>
|
|
@@ -244,7 +220,7 @@ describe('Grid', () => {
|
|
|
244
220
|
|
|
245
221
|
// --- P2: columns object + columnGap interaction (hardening #719) ---
|
|
246
222
|
|
|
247
|
-
it('uses columnGap var in
|
|
223
|
+
it('uses columnGap var in track-max when both columnGap and gap are set', () => {
|
|
248
224
|
render(
|
|
249
225
|
<Grid
|
|
250
226
|
columns={{minWidth: 200, max: 3}}
|
|
@@ -255,14 +231,14 @@ describe('Grid', () => {
|
|
|
255
231
|
</Grid>,
|
|
256
232
|
);
|
|
257
233
|
const grid = screen.getByTestId('grid');
|
|
258
|
-
// columnGap takes precedence over gap in
|
|
234
|
+
// columnGap takes precedence over gap in track-max
|
|
259
235
|
expect(templateColumns(grid)).toBe(
|
|
260
|
-
'repeat(auto-fill, minmax(
|
|
236
|
+
'repeat(auto-fill, minmax(200px, calc((100% - 2 * var(--spacing-6)) / 3)))',
|
|
261
237
|
);
|
|
262
238
|
expect(grid.style.maxWidth).toBe('');
|
|
263
239
|
});
|
|
264
240
|
|
|
265
|
-
it('uses gap var in
|
|
241
|
+
it('uses gap var in track-max when columnGap is not set', () => {
|
|
266
242
|
render(
|
|
267
243
|
<Grid columns={{minWidth: 150, max: 2}} gap={3} data-testid="grid">
|
|
268
244
|
<div>Item</div>
|
|
@@ -270,12 +246,12 @@ describe('Grid', () => {
|
|
|
270
246
|
);
|
|
271
247
|
const grid = screen.getByTestId('grid');
|
|
272
248
|
expect(templateColumns(grid)).toBe(
|
|
273
|
-
'repeat(auto-fill, minmax(
|
|
249
|
+
'repeat(auto-fill, minmax(150px, calc((100% - 1 * var(--spacing-3)) / 2)))',
|
|
274
250
|
);
|
|
275
251
|
expect(grid.style.maxWidth).toBe('');
|
|
276
252
|
});
|
|
277
253
|
|
|
278
|
-
it('uses simple fraction in
|
|
254
|
+
it('uses simple fraction in track-max when no gap is set', () => {
|
|
279
255
|
render(
|
|
280
256
|
<Grid columns={{minWidth: 100, max: 3}} data-testid="grid">
|
|
281
257
|
<div>Item</div>
|
|
@@ -283,7 +259,7 @@ describe('Grid', () => {
|
|
|
283
259
|
);
|
|
284
260
|
const grid = screen.getByTestId('grid');
|
|
285
261
|
expect(templateColumns(grid)).toBe(
|
|
286
|
-
'repeat(auto-fill, minmax(
|
|
262
|
+
'repeat(auto-fill, minmax(100px, calc(100% / 3)))',
|
|
287
263
|
);
|
|
288
264
|
expect(grid.style.maxWidth).toBe('');
|
|
289
265
|
});
|
|
@@ -356,7 +332,7 @@ describe('Grid', () => {
|
|
|
356
332
|
expect(templateColumns(grid)).toBe('repeat(auto-fill, minmax(280px, 1fr))');
|
|
357
333
|
});
|
|
358
334
|
|
|
359
|
-
it('renders with columns={{minWidth, max}} capping
|
|
335
|
+
it('renders with columns={{minWidth, max}} capping via track-max', () => {
|
|
360
336
|
render(
|
|
361
337
|
<Grid columns={{minWidth: 280, max: 3}} gap={4} data-testid="grid">
|
|
362
338
|
<div>Item 1</div>
|
|
@@ -364,15 +340,14 @@ describe('Grid', () => {
|
|
|
364
340
|
</Grid>,
|
|
365
341
|
);
|
|
366
342
|
const grid = screen.getByTestId('grid');
|
|
367
|
-
//
|
|
368
|
-
// columns fill the row (grid stays full width).
|
|
343
|
+
// Track-max limits columns — grid stays full width
|
|
369
344
|
expect(templateColumns(grid)).toBe(
|
|
370
|
-
'repeat(auto-fill, minmax(
|
|
345
|
+
'repeat(auto-fill, minmax(280px, calc((100% - 2 * var(--spacing-4)) / 3)))',
|
|
371
346
|
);
|
|
372
347
|
expect(grid.style.maxWidth).toBe('');
|
|
373
348
|
});
|
|
374
349
|
|
|
375
|
-
it('renders with columns={{minWidth, max, repeat: "fit"}} using auto-fit +
|
|
350
|
+
it('renders with columns={{minWidth, max, repeat: "fit"}} using auto-fit + track-max', () => {
|
|
376
351
|
render(
|
|
377
352
|
<Grid
|
|
378
353
|
columns={{minWidth: 280, max: 3, repeat: 'fit'}}
|
|
@@ -384,7 +359,7 @@ describe('Grid', () => {
|
|
|
384
359
|
);
|
|
385
360
|
const grid = screen.getByTestId('grid');
|
|
386
361
|
expect(templateColumns(grid)).toBe(
|
|
387
|
-
'repeat(auto-fit, minmax(
|
|
362
|
+
'repeat(auto-fit, minmax(280px, calc((100% - 2 * var(--spacing-4)) / 3)))',
|
|
388
363
|
);
|
|
389
364
|
expect(grid.style.maxWidth).toBe('');
|
|
390
365
|
});
|
package/src/Grid/Grid.tsx
CHANGED
|
@@ -38,10 +38,9 @@ export type GridAlignment = 'start' | 'center' | 'end' | 'stretch';
|
|
|
38
38
|
* - `minWidth` — minimum width (px) for each column track
|
|
39
39
|
* - `repeat` — `'fill'` (default) preserves empty tracks for consistent widths;
|
|
40
40
|
* `'fit'` collapses empty tracks so items stretch to fill
|
|
41
|
-
* - `max` — caps the maximum number of columns
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* (no dead space on the right).
|
|
41
|
+
* - `max` — caps the maximum number of columns by limiting track max size.
|
|
42
|
+
* The grid stretches to 100% of its parent; `max` only limits how many
|
|
43
|
+
* columns appear.
|
|
45
44
|
*/
|
|
46
45
|
export type GridColumns =
|
|
47
46
|
| number
|
|
@@ -322,20 +321,9 @@ const spacingVarNames: Record<SpacingStep, string> = {
|
|
|
322
321
|
};
|
|
323
322
|
|
|
324
323
|
/**
|
|
325
|
-
* Build a grid-template-columns value that caps
|
|
326
|
-
*
|
|
327
|
-
* the
|
|
328
|
-
*
|
|
329
|
-
* The cap lives on the track's **min** size, not its max: each track is at
|
|
330
|
-
* least `perColumn = (100% - (max-1) * gap) / max`, so more than `max` columns
|
|
331
|
-
* can never fit. The track **max** stays `1fr`, so whenever fewer than `max`
|
|
332
|
-
* columns fit (most importantly a lone column on mobile) they still grow to
|
|
333
|
-
* fill the whole row — no dead space on the right.
|
|
334
|
-
*
|
|
335
|
-
* The track min is `max(minWidth, perColumn)` so an explicit `minWidth` is
|
|
336
|
-
* still honored, wrapped in `min(100%, …)` so a single column on a viewport
|
|
337
|
-
* narrower than `minWidth`/`perColumn` shrinks to the container instead of
|
|
338
|
-
* overflowing it.
|
|
324
|
+
* Build a grid-template-columns value that caps columns at `max` by
|
|
325
|
+
* limiting each track's max size to `(100% - (max-1) * gap) / max`.
|
|
326
|
+
* The grid stays 100% width; the track-max prevents more than `max` columns.
|
|
339
327
|
*/
|
|
340
328
|
function buildCappedTemplate(
|
|
341
329
|
minWidth: number,
|
|
@@ -351,17 +339,12 @@ function buildCappedTemplate(
|
|
|
351
339
|
? spacingVarNames[gap]
|
|
352
340
|
: null;
|
|
353
341
|
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
// the grid never fits more than `maxCols` columns.
|
|
357
|
-
const perColumn = gapVar
|
|
342
|
+
// trackMax = (100% - (maxCols - 1) * gap) / maxCols
|
|
343
|
+
const trackMax = gapVar
|
|
358
344
|
? `calc((100% - ${maxCols - 1} * var(${gapVar})) / ${maxCols})`
|
|
359
345
|
: `calc(100% / ${maxCols})`;
|
|
360
346
|
|
|
361
|
-
|
|
362
|
-
const trackMin = `min(100%, max(${minWidth}px, ${perColumn}))`;
|
|
363
|
-
|
|
364
|
-
return `repeat(${repeatMode}, minmax(${trackMin}, 1fr))`;
|
|
347
|
+
return `repeat(${repeatMode}, minmax(${minWidth}px, ${trackMax}))`;
|
|
365
348
|
}
|
|
366
349
|
|
|
367
350
|
/**
|
package/src/Icon/Icon.test.tsx
CHANGED
|
@@ -11,48 +11,48 @@
|
|
|
11
11
|
|
|
12
12
|
import {describe, it, expect, vi} from 'vitest';
|
|
13
13
|
import {render, screen} from '@testing-library/react';
|
|
14
|
-
import {
|
|
14
|
+
import {HomeIcon} from '@heroicons/react/24/outline';
|
|
15
15
|
import {Icon} from './Icon';
|
|
16
16
|
|
|
17
17
|
describe('Icon', () => {
|
|
18
18
|
it('renders the icon component', () => {
|
|
19
|
-
render(<Icon icon={
|
|
19
|
+
render(<Icon icon={HomeIcon} data-testid="icon" />);
|
|
20
20
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
it('renders as an SVG element', () => {
|
|
24
|
-
render(<Icon icon={
|
|
24
|
+
render(<Icon icon={HomeIcon} data-testid="icon" />);
|
|
25
25
|
const icon = screen.getByTestId('icon');
|
|
26
26
|
expect(icon.tagName.toLowerCase()).toBe('svg');
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
it('applies aria-hidden by default', () => {
|
|
30
|
-
render(<Icon icon={
|
|
30
|
+
render(<Icon icon={HomeIcon} data-testid="icon" />);
|
|
31
31
|
expect(screen.getByTestId('icon')).toHaveAttribute('aria-hidden', 'true');
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
it('renders with different color variants', () => {
|
|
35
35
|
const {rerender} = render(
|
|
36
|
-
<Icon icon={
|
|
36
|
+
<Icon icon={HomeIcon} color="primary" data-testid="icon" />,
|
|
37
37
|
);
|
|
38
38
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
39
39
|
|
|
40
|
-
rerender(<Icon icon={
|
|
40
|
+
rerender(<Icon icon={HomeIcon} color="secondary" data-testid="icon" />);
|
|
41
41
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
42
42
|
|
|
43
|
-
rerender(<Icon icon={
|
|
43
|
+
rerender(<Icon icon={HomeIcon} color="accent" data-testid="icon" />);
|
|
44
44
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
45
45
|
|
|
46
|
-
rerender(<Icon icon={
|
|
46
|
+
rerender(<Icon icon={HomeIcon} color="success" data-testid="icon" />);
|
|
47
47
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
48
48
|
|
|
49
|
-
rerender(<Icon icon={
|
|
49
|
+
rerender(<Icon icon={HomeIcon} color="error" data-testid="icon" />);
|
|
50
50
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
51
51
|
|
|
52
|
-
rerender(<Icon icon={
|
|
52
|
+
rerender(<Icon icon={HomeIcon} color="warning" data-testid="icon" />);
|
|
53
53
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
54
54
|
|
|
55
|
-
rerender(<Icon icon={
|
|
55
|
+
rerender(<Icon icon={HomeIcon} color="inherit" data-testid="icon" />);
|
|
56
56
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
57
57
|
});
|
|
58
58
|
|
|
@@ -70,41 +70,50 @@ describe('Icon', () => {
|
|
|
70
70
|
'purple',
|
|
71
71
|
] as const;
|
|
72
72
|
const {rerender} = render(
|
|
73
|
-
<Icon
|
|
73
|
+
<Icon
|
|
74
|
+
icon={HomeIcon}
|
|
75
|
+
color={nonSemanticColors[0]}
|
|
76
|
+
data-testid="icon"
|
|
77
|
+
/>,
|
|
74
78
|
);
|
|
75
79
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
76
80
|
|
|
77
81
|
for (const c of nonSemanticColors.slice(1)) {
|
|
78
|
-
rerender(<Icon icon={
|
|
82
|
+
rerender(<Icon icon={HomeIcon} color={c} data-testid="icon" />);
|
|
79
83
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
80
84
|
}
|
|
81
85
|
});
|
|
82
86
|
|
|
83
87
|
it('renders with different size variants', () => {
|
|
84
88
|
const {rerender} = render(
|
|
85
|
-
<Icon icon={
|
|
89
|
+
<Icon icon={HomeIcon} size="xsm" data-testid="icon" />,
|
|
86
90
|
);
|
|
87
91
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
88
92
|
|
|
89
|
-
rerender(<Icon icon={
|
|
93
|
+
rerender(<Icon icon={HomeIcon} size="sm" data-testid="icon" />);
|
|
90
94
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
91
95
|
|
|
92
|
-
rerender(<Icon icon={
|
|
96
|
+
rerender(<Icon icon={HomeIcon} size="md" data-testid="icon" />);
|
|
93
97
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
94
98
|
|
|
95
|
-
rerender(<Icon icon={
|
|
99
|
+
rerender(<Icon icon={HomeIcon} size="lg" data-testid="icon" />);
|
|
96
100
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
97
101
|
});
|
|
98
102
|
|
|
99
103
|
it('forwards ref correctly', () => {
|
|
100
104
|
const ref = vi.fn();
|
|
101
|
-
render(<Icon icon={
|
|
105
|
+
render(<Icon icon={HomeIcon} ref={ref} />);
|
|
102
106
|
expect(ref).toHaveBeenCalledWith(expect.any(SVGSVGElement));
|
|
103
107
|
});
|
|
104
108
|
|
|
105
109
|
it('passes additional SVG props', () => {
|
|
106
110
|
render(
|
|
107
|
-
<Icon
|
|
111
|
+
<Icon
|
|
112
|
+
icon={HomeIcon}
|
|
113
|
+
data-testid="icon"
|
|
114
|
+
role="img"
|
|
115
|
+
aria-label="Home"
|
|
116
|
+
/>,
|
|
108
117
|
);
|
|
109
118
|
const icon = screen.getByTestId('icon');
|
|
110
119
|
expect(icon).toHaveAttribute('role', 'img');
|
|
@@ -112,29 +121,8 @@ describe('Icon', () => {
|
|
|
112
121
|
});
|
|
113
122
|
|
|
114
123
|
it('uses default color and size when not specified', () => {
|
|
115
|
-
render(<Icon icon={
|
|
124
|
+
render(<Icon icon={HomeIcon} data-testid="icon" />);
|
|
116
125
|
// The component should render without errors with defaults
|
|
117
126
|
expect(screen.getByTestId('icon')).toBeInTheDocument();
|
|
118
127
|
});
|
|
119
|
-
|
|
120
|
-
it('applies aria-hidden by default in string (registry) mode', () => {
|
|
121
|
-
render(<Icon icon="check" data-testid="icon" />);
|
|
122
|
-
expect(screen.getByTestId('icon')).toHaveAttribute('aria-hidden', 'true');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('lets a string-mode icon be made meaningful by overriding aria-hidden', () => {
|
|
126
|
-
render(
|
|
127
|
-
<Icon
|
|
128
|
-
icon="check"
|
|
129
|
-
data-testid="icon"
|
|
130
|
-
role="img"
|
|
131
|
-
aria-label="Done"
|
|
132
|
-
aria-hidden={false}
|
|
133
|
-
/>,
|
|
134
|
-
);
|
|
135
|
-
const icon = screen.getByTestId('icon');
|
|
136
|
-
expect(icon).toHaveAttribute('role', 'img');
|
|
137
|
-
expect(icon).toHaveAttribute('aria-label', 'Done');
|
|
138
|
-
expect(icon).toHaveAttribute('aria-hidden', 'false');
|
|
139
|
-
});
|
|
140
128
|
});
|
package/src/Icon/Icon.tsx
CHANGED
|
@@ -281,16 +281,12 @@ function IconFromRegistry({
|
|
|
281
281
|
|
|
282
282
|
return (
|
|
283
283
|
<span
|
|
284
|
-
// Decorative by default, but placed BEFORE the prop spread so consumers
|
|
285
|
-
// can override it (e.g. `aria-hidden={false}` + `role="img"` +
|
|
286
|
-
// `aria-label`) to expose a meaningful standalone icon. This matches
|
|
287
|
-
// component-mode Icon, which already sets aria-hidden before its spread.
|
|
288
|
-
aria-hidden="true"
|
|
289
284
|
{...(spanProps as React.HTMLAttributes<HTMLSpanElement>)}
|
|
290
285
|
{...mergeProps(
|
|
291
286
|
themeProps('icon', {size, color}),
|
|
292
287
|
stylex.props(styles.span, colorStyles[color], spanSizeStyles[size]),
|
|
293
|
-
)}
|
|
288
|
+
)}
|
|
289
|
+
aria-hidden="true">
|
|
294
290
|
{resolvedIcon}
|
|
295
291
|
</span>
|
|
296
292
|
);
|
|
@@ -14,12 +14,6 @@ export const docs = {
|
|
|
14
14
|
type: 'ReactNode',
|
|
15
15
|
description: 'Content.',
|
|
16
16
|
},
|
|
17
|
-
{
|
|
18
|
-
name: 'padding',
|
|
19
|
-
type: '0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10',
|
|
20
|
-
description:
|
|
21
|
-
'Internal padding using the spacing scale. Overrides the default padding from the layout container.',
|
|
22
|
-
},
|
|
23
17
|
{
|
|
24
18
|
name: 'isScrollable',
|
|
25
19
|
type: 'boolean',
|
|
@@ -50,11 +44,6 @@ export const docsZh = {
|
|
|
50
44
|
type: 'ReactNode',
|
|
51
45
|
description: '内容。',
|
|
52
46
|
},
|
|
53
|
-
{
|
|
54
|
-
name: 'padding',
|
|
55
|
-
type: '0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10',
|
|
56
|
-
description: '使用间距比例的内边距。覆盖布局容器的默认内边距。',
|
|
57
|
-
},
|
|
58
47
|
{
|
|
59
48
|
name: 'isScrollable',
|
|
60
49
|
type: 'boolean',
|
|
@@ -81,7 +70,6 @@ export const docsDense = {
|
|
|
81
70
|
description: 'Scrollable main content area.',
|
|
82
71
|
propDescriptions: {
|
|
83
72
|
children: 'Content.',
|
|
84
|
-
padding: 'Internal padding on spacing scale. Overrides layout container default.',
|
|
85
73
|
isScrollable: 'Enable scrollable overflow.',
|
|
86
74
|
label: 'Accessible label for landmark element.',
|
|
87
75
|
role: 'ARIA landmark role.',
|
|
@@ -20,12 +20,6 @@ export const docs = {
|
|
|
20
20
|
description: 'Border on the appropriate edge.',
|
|
21
21
|
default: 'false',
|
|
22
22
|
},
|
|
23
|
-
{
|
|
24
|
-
name: 'padding',
|
|
25
|
-
type: '0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10',
|
|
26
|
-
description:
|
|
27
|
-
'Internal padding using the spacing scale. Overrides the default padding from the layout container.',
|
|
28
|
-
},
|
|
29
23
|
{
|
|
30
24
|
name: 'isScrollable',
|
|
31
25
|
type: 'boolean',
|
|
@@ -42,18 +36,6 @@ export const docs = {
|
|
|
42
36
|
type: 'AriaRole',
|
|
43
37
|
description: 'ARIA landmark role.',
|
|
44
38
|
},
|
|
45
|
-
{
|
|
46
|
-
name: 'width',
|
|
47
|
-
type: 'number | string',
|
|
48
|
-
description:
|
|
49
|
-
'Width of the panel. Numbers are treated as pixels, strings are used as-is. Ignored when resizable is provided; the hook controls width.',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'resizable',
|
|
53
|
-
type: 'ResizableProps',
|
|
54
|
-
description:
|
|
55
|
-
'Resize props from useResizable(). When provided, the hook drives the panel width and a ResizeHandle should be placed adjacent to the panel.',
|
|
56
|
-
},
|
|
57
39
|
],
|
|
58
40
|
};
|
|
59
41
|
|
|
@@ -74,11 +56,6 @@ export const docsZh = {
|
|
|
74
56
|
description: '相应边缘的边框。',
|
|
75
57
|
default: 'false',
|
|
76
58
|
},
|
|
77
|
-
{
|
|
78
|
-
name: 'padding',
|
|
79
|
-
type: '0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10',
|
|
80
|
-
description: '使用间距比例的内边距。覆盖布局容器的默认内边距。',
|
|
81
|
-
},
|
|
82
59
|
{
|
|
83
60
|
name: 'isScrollable',
|
|
84
61
|
type: 'boolean',
|
|
@@ -95,18 +72,6 @@ export const docsZh = {
|
|
|
95
72
|
type: 'AriaRole',
|
|
96
73
|
description: 'ARIA 地标角色。',
|
|
97
74
|
},
|
|
98
|
-
{
|
|
99
|
-
name: 'width',
|
|
100
|
-
type: 'number | string',
|
|
101
|
-
description:
|
|
102
|
-
'面板宽度。数字按像素处理,字符串按原样使用。提供 resizable 时忽略,宽度由 hook 控制。',
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
name: 'resizable',
|
|
106
|
-
type: 'ResizableProps',
|
|
107
|
-
description:
|
|
108
|
-
'来自 useResizable() 的调整大小属性。提供时面板宽度由 hook 驱动,应在面板旁放置 ResizeHandle。',
|
|
109
|
-
},
|
|
110
75
|
],
|
|
111
76
|
};
|
|
112
77
|
|
|
@@ -118,13 +83,8 @@ export const docsDense = {
|
|
|
118
83
|
propDescriptions: {
|
|
119
84
|
children: 'Panel content.',
|
|
120
85
|
hasDivider: 'Border on appropriate edge.',
|
|
121
|
-
padding: 'Internal padding on spacing scale. Overrides layout container default.',
|
|
122
86
|
isScrollable: 'Enable scrollable overflow.',
|
|
123
87
|
label: 'Accessible label for landmark element.',
|
|
124
88
|
role: 'ARIA landmark role.',
|
|
125
|
-
width:
|
|
126
|
-
'Panel width. Numbers = pixels, strings as-is. Ignored when resizable provided; hook controls width.',
|
|
127
|
-
resizable:
|
|
128
|
-
'Resize props from useResizable(). Hook drives panel width; place ResizeHandle adjacent.',
|
|
129
89
|
},
|
|
130
90
|
};
|
|
@@ -64,13 +64,8 @@ export const docs = {
|
|
|
64
64
|
{ guidance: false, description: 'Nest interactive content inside captions; keep them plain text.' },
|
|
65
65
|
],
|
|
66
66
|
},
|
|
67
|
-
// The lightbox opens via showModal() and renders nothing while closed —
|
|
68
|
-
// overlay mode gives the Properties preview an open trigger instead of an
|
|
69
|
-
// empty stage, mirroring MobileNav (#3616).
|
|
70
67
|
playground: {
|
|
71
|
-
overlay: true,
|
|
72
68
|
defaults: {
|
|
73
|
-
isOpen: false,
|
|
74
69
|
media: {
|
|
75
70
|
src: 'https://lookaside.facebook.com/assets/astryx/light-scene-horizontal-1.png',
|
|
76
71
|
alt: 'Coastal shoreline with ocean waves',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
-
import {describe, it, expect, vi, beforeEach
|
|
4
|
-
import {render, screen, fireEvent
|
|
3
|
+
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
4
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
5
5
|
import {Lightbox} from './Lightbox';
|
|
6
|
-
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
7
6
|
|
|
8
7
|
// Mock showModal/close for jsdom
|
|
9
8
|
beforeEach(() => {
|
|
@@ -17,16 +16,6 @@ beforeEach(() => {
|
|
|
17
16
|
});
|
|
18
17
|
});
|
|
19
18
|
|
|
20
|
-
// useAnnounce mounts singleton live regions on <body>; reset between tests so
|
|
21
|
-
// stale announcements from one test don't leak into the next.
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
__resetLiveRegionsForTest();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
function politeRegion(): HTMLElement | null {
|
|
27
|
-
return document.querySelector('[data-astryx-live-region="polite"]');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
19
|
describe('Lightbox', () => {
|
|
31
20
|
it('renders as a dialog element', () => {
|
|
32
21
|
render(
|
|
@@ -311,116 +300,6 @@ describe('Lightbox', () => {
|
|
|
311
300
|
});
|
|
312
301
|
});
|
|
313
302
|
|
|
314
|
-
describe('screen-reader announcements', () => {
|
|
315
|
-
const media = [
|
|
316
|
-
{src: '/a.jpg', alt: 'Image A', caption: 'First'},
|
|
317
|
-
{src: '/b.jpg', alt: 'Image B', caption: 'Second'},
|
|
318
|
-
{src: '/c.jpg', alt: 'Image C'},
|
|
319
|
-
];
|
|
320
|
-
|
|
321
|
-
it('announces the new image and position when navigating next via button', async () => {
|
|
322
|
-
render(
|
|
323
|
-
<Lightbox
|
|
324
|
-
isOpen={true}
|
|
325
|
-
onOpenChange={() => {}}
|
|
326
|
-
media={media}
|
|
327
|
-
defaultIndex={0}
|
|
328
|
-
/>,
|
|
329
|
-
);
|
|
330
|
-
fireEvent.click(screen.getByLabelText('Next'));
|
|
331
|
-
await waitFor(() => {
|
|
332
|
-
expect(politeRegion()).toHaveTextContent('Image B, 2 of 3');
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it('announces the new image and position when navigating via arrow keys', async () => {
|
|
337
|
-
render(
|
|
338
|
-
<Lightbox
|
|
339
|
-
isOpen={true}
|
|
340
|
-
onOpenChange={() => {}}
|
|
341
|
-
media={media}
|
|
342
|
-
defaultIndex={1}
|
|
343
|
-
/>,
|
|
344
|
-
);
|
|
345
|
-
const dialog = document.querySelector('dialog')!;
|
|
346
|
-
fireEvent.keyDown(dialog, {key: 'ArrowRight'});
|
|
347
|
-
await waitFor(() => {
|
|
348
|
-
expect(politeRegion()).toHaveTextContent('Image C, 3 of 3');
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it('announces the new image and position when navigating prev', async () => {
|
|
353
|
-
render(
|
|
354
|
-
<Lightbox
|
|
355
|
-
isOpen={true}
|
|
356
|
-
onOpenChange={() => {}}
|
|
357
|
-
media={media}
|
|
358
|
-
defaultIndex={2}
|
|
359
|
-
/>,
|
|
360
|
-
);
|
|
361
|
-
fireEvent.click(screen.getByLabelText('Previous'));
|
|
362
|
-
await waitFor(() => {
|
|
363
|
-
expect(politeRegion()).toHaveTextContent('Image B, 2 of 3');
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it('falls back to a positional label when the image has no alt', async () => {
|
|
368
|
-
const unlabeled = [
|
|
369
|
-
{src: '/a.jpg', alt: 'Image A'},
|
|
370
|
-
{src: '/b.jpg', alt: ''},
|
|
371
|
-
];
|
|
372
|
-
render(
|
|
373
|
-
<Lightbox
|
|
374
|
-
isOpen={true}
|
|
375
|
-
onOpenChange={() => {}}
|
|
376
|
-
media={unlabeled}
|
|
377
|
-
defaultIndex={0}
|
|
378
|
-
/>,
|
|
379
|
-
);
|
|
380
|
-
fireEvent.click(screen.getByLabelText('Next'));
|
|
381
|
-
await waitFor(() => {
|
|
382
|
-
expect(politeRegion()).toHaveTextContent('Image 2 of 2');
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
it('does not announce on initial open', async () => {
|
|
387
|
-
render(
|
|
388
|
-
<Lightbox
|
|
389
|
-
isOpen={true}
|
|
390
|
-
onOpenChange={() => {}}
|
|
391
|
-
media={media}
|
|
392
|
-
defaultIndex={1}
|
|
393
|
-
/>,
|
|
394
|
-
);
|
|
395
|
-
// Allow any scheduled rAF to flush; nothing should have been announced.
|
|
396
|
-
await new Promise(resolve => requestAnimationFrame(() => resolve(null)));
|
|
397
|
-
// The dialog's aria-label already names the current image on open, so no
|
|
398
|
-
// live region is created (announce is never called).
|
|
399
|
-
expect(politeRegion()).toBeNull();
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
it('does not announce when the lightbox opens at a new index', async () => {
|
|
403
|
-
const {rerender} = render(
|
|
404
|
-
<Lightbox
|
|
405
|
-
isOpen={false}
|
|
406
|
-
onOpenChange={() => {}}
|
|
407
|
-
media={media}
|
|
408
|
-
index={0}
|
|
409
|
-
/>,
|
|
410
|
-
);
|
|
411
|
-
rerender(
|
|
412
|
-
<Lightbox
|
|
413
|
-
isOpen={true}
|
|
414
|
-
onOpenChange={() => {}}
|
|
415
|
-
media={media}
|
|
416
|
-
index={2}
|
|
417
|
-
/>,
|
|
418
|
-
);
|
|
419
|
-
await new Promise(resolve => requestAnimationFrame(() => resolve(null)));
|
|
420
|
-
expect(politeRegion()).toBeNull();
|
|
421
|
-
});
|
|
422
|
-
});
|
|
423
|
-
|
|
424
303
|
describe('video support', () => {
|
|
425
304
|
it('renders a video element when type is video', () => {
|
|
426
305
|
const {container} = render(
|