@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.
Files changed (226) hide show
  1. package/dist/Banner/Banner.d.ts.map +1 -1
  2. package/dist/Banner/Banner.js +1 -8
  3. package/dist/Calendar/Calendar.d.ts.map +1 -1
  4. package/dist/Calendar/Calendar.js +11 -31
  5. package/dist/Calendar/styles.d.ts +2 -2
  6. package/dist/Calendar/styles.d.ts.map +1 -1
  7. package/dist/Calendar/styles.js +2 -2
  8. package/dist/Carousel/Carousel.d.ts.map +1 -1
  9. package/dist/Carousel/Carousel.js +0 -1
  10. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  11. package/dist/Chat/ChatToolCalls.js +1 -8
  12. package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
  13. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  14. package/dist/CheckboxInput/CheckboxInput.js +1 -3
  15. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  16. package/dist/ClickableCard/ClickableCard.js +3 -26
  17. package/dist/CodeBlock/CodeBlock.d.ts +1 -10
  18. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  19. package/dist/CodeBlock/CodeBlock.js +5 -23
  20. package/dist/Collapsible/Collapsible.d.ts +3 -9
  21. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  22. package/dist/Collapsible/Collapsible.js +13 -101
  23. package/dist/Collapsible/CollapsibleGroup.d.ts +7 -39
  24. package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
  25. package/dist/Collapsible/CollapsibleGroup.js +9 -70
  26. package/dist/Collapsible/CollapsibleGroupContext.d.ts +0 -31
  27. package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
  28. package/dist/Collapsible/CollapsibleGroupContext.js +3 -33
  29. package/dist/Collapsible/index.d.ts +0 -1
  30. package/dist/Collapsible/index.d.ts.map +1 -1
  31. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  32. package/dist/ContextMenu/ContextMenu.js +0 -2
  33. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  34. package/dist/DateTimeInput/DateTimeInput.js +0 -2
  35. package/dist/Grid/Grid.d.ts +3 -4
  36. package/dist/Grid/Grid.d.ts.map +1 -1
  37. package/dist/Grid/Grid.js +9 -26
  38. package/dist/Icon/Icon.d.ts.map +1 -1
  39. package/dist/Icon/Icon.js +1 -5
  40. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  41. package/dist/Lightbox/Lightbox.js +2 -26
  42. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  43. package/dist/NumberInput/NumberInput.js +16 -32
  44. package/dist/Pagination/Pagination.d.ts.map +1 -1
  45. package/dist/Pagination/Pagination.js +36 -87
  46. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  47. package/dist/PowerSearch/PowerSearch.js +21 -39
  48. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  49. package/dist/ProgressBar/ProgressBar.js +1 -2
  50. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  51. package/dist/Resizable/ResizeHandle.js +2 -10
  52. package/dist/SideNav/SideNav.d.ts.map +1 -1
  53. package/dist/SideNav/SideNav.js +2 -6
  54. package/dist/TabList/TabMenu.d.ts.map +1 -1
  55. package/dist/TabList/TabMenu.js +7 -34
  56. package/dist/Table/BaseTable.d.ts.map +1 -1
  57. package/dist/Table/BaseTable.js +2 -12
  58. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  59. package/dist/Table/plugins/columnResize/useTableColumnResize.js +2 -5
  60. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  61. package/dist/Table/plugins/pagination/paginateData.js +1 -5
  62. package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
  63. package/dist/Table/plugins/selection/useTableSelectionState.js +1 -11
  64. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  65. package/dist/Table/plugins/sortable/useTableSortableState.js +4 -8
  66. package/dist/Thumbnail/Thumbnail.d.ts +2 -3
  67. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  68. package/dist/Thumbnail/Thumbnail.js +0 -1
  69. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  70. package/dist/TimeInput/TimeInput.js +1 -6
  71. package/dist/Toast/Toast.d.ts.map +1 -1
  72. package/dist/Toast/Toast.js +3 -11
  73. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  74. package/dist/Toast/ToastViewport.js +0 -12
  75. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  76. package/dist/Typeahead/BaseTypeahead.js +2 -5
  77. package/dist/astryx.css +0 -7
  78. package/dist/astryx.umd.js +48 -48
  79. package/dist/astryx.umd.js.map +4 -4
  80. package/dist/hooks/focusableSelector.d.ts +1 -1
  81. package/dist/hooks/focusableSelector.d.ts.map +1 -1
  82. package/dist/hooks/focusableSelector.js +1 -1
  83. package/dist/hooks/useFocusTrap.d.ts +0 -3
  84. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  85. package/dist/hooks/useFocusTrap.js +1 -38
  86. package/dist/theme/defineTheme.d.ts +1 -1
  87. package/dist/theme/defineTheme.d.ts.map +1 -1
  88. package/dist/theme/expandColorScale.d.ts.map +1 -1
  89. package/dist/theme/expandColorScale.js +3 -10
  90. package/dist/theme/hct.d.ts +15 -0
  91. package/dist/theme/hct.d.ts.map +1 -1
  92. package/dist/theme/hct.js +9 -9
  93. package/dist/theme/tokens.d.ts +2 -10
  94. package/dist/theme/tokens.d.ts.map +1 -1
  95. package/dist/theme/tokens.js +4 -220
  96. package/dist/utils/index.d.ts +0 -2
  97. package/dist/utils/index.d.ts.map +1 -1
  98. package/dist/utils/index.js +1 -2
  99. package/dist/utils/timeParser.d.ts.map +1 -1
  100. package/dist/utils/timeParser.js +5 -8
  101. package/package.json +1 -1
  102. package/src/Avatar/Avatar.doc.mjs +0 -11
  103. package/src/Banner/Banner.doc.mjs +0 -8
  104. package/src/Banner/Banner.test.tsx +0 -38
  105. package/src/Banner/Banner.tsx +1 -8
  106. package/src/Calendar/Calendar.doc.mjs +1 -1
  107. package/src/Calendar/Calendar.test.tsx +4 -112
  108. package/src/Calendar/Calendar.tsx +5 -26
  109. package/src/Calendar/styles.ts +2 -12
  110. package/src/Carousel/Carousel.doc.mjs +12 -12
  111. package/src/Carousel/Carousel.test.tsx +0 -13
  112. package/src/Carousel/Carousel.tsx +0 -1
  113. package/src/Chat/Chat.doc.mjs +1 -1
  114. package/src/Chat/ChatToolCalls.test.tsx +0 -55
  115. package/src/Chat/ChatToolCalls.tsx +12 -10
  116. package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -3
  117. package/src/CheckboxInput/CheckboxInput.test.tsx +4 -71
  118. package/src/CheckboxInput/CheckboxInput.tsx +0 -2
  119. package/src/Citation/Citation.doc.mjs +0 -5
  120. package/src/ClickableCard/ClickableCard.tsx +2 -45
  121. package/src/CodeBlock/CodeBlock.doc.mjs +2 -7
  122. package/src/CodeBlock/CodeBlock.test.tsx +3 -87
  123. package/src/CodeBlock/CodeBlock.tsx +3 -43
  124. package/src/Collapsible/Collapsible.doc.mjs +4 -8
  125. package/src/Collapsible/Collapsible.tsx +10 -86
  126. package/src/Collapsible/CollapsibleGroup.doc.mjs +3 -27
  127. package/src/Collapsible/CollapsibleGroup.test.tsx +0 -288
  128. package/src/Collapsible/CollapsibleGroup.tsx +9 -107
  129. package/src/Collapsible/CollapsibleGroupContext.tsx +2 -42
  130. package/src/Collapsible/index.ts +0 -4
  131. package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +0 -5
  132. package/src/CommandPalette/CommandPaletteGroup.doc.mjs +0 -10
  133. package/src/CommandPalette/CommandPaletteInput.doc.mjs +0 -6
  134. package/src/CommandPalette/CommandPaletteItem.doc.mjs +0 -6
  135. package/src/CommandPalette/CommandPaletteList.doc.mjs +0 -9
  136. package/src/ContextMenu/ContextMenu.doc.mjs +0 -15
  137. package/src/ContextMenu/ContextMenu.test.tsx +0 -15
  138. package/src/ContextMenu/ContextMenu.tsx +0 -2
  139. package/src/ContextMenu/ContextMenuItem.doc.mjs +0 -6
  140. package/src/DateTimeInput/DateTimeInput.test.tsx +0 -70
  141. package/src/DateTimeInput/DateTimeInput.tsx +0 -2
  142. package/src/Dialog/DialogHeader.doc.mjs +0 -45
  143. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
  144. package/src/Field/FieldLabel.test.tsx +2 -2
  145. package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +3 -0
  146. package/src/Grid/Grid.test.tsx +18 -43
  147. package/src/Grid/Grid.tsx +9 -26
  148. package/src/Icon/Icon.test.tsx +29 -41
  149. package/src/Icon/Icon.tsx +2 -6
  150. package/src/Layout/LayoutContent.doc.mjs +0 -12
  151. package/src/Layout/LayoutPanel.doc.mjs +0 -40
  152. package/src/Lightbox/Lightbox.doc.mjs +0 -5
  153. package/src/Lightbox/Lightbox.test.tsx +2 -123
  154. package/src/Lightbox/Lightbox.tsx +1 -29
  155. package/src/NavMenu/NavMenu.doc.mjs +0 -15
  156. package/src/NumberInput/NumberInput.test.tsx +3 -48
  157. package/src/NumberInput/NumberInput.tsx +15 -32
  158. package/src/Overlay/Overlay.doc.mjs +0 -6
  159. package/src/Pagination/Pagination.test.tsx +0 -143
  160. package/src/Pagination/Pagination.tsx +23 -75
  161. package/src/Popover/Popover.test.tsx +0 -65
  162. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  163. package/src/PowerSearch/PowerSearch.test.tsx +1 -109
  164. package/src/PowerSearch/PowerSearch.tsx +16 -37
  165. package/src/ProgressBar/ProgressBar.test.tsx +0 -10
  166. package/src/ProgressBar/ProgressBar.tsx +1 -2
  167. package/src/Resizable/ResizeHandle.tsx +2 -10
  168. package/src/SegmentedControl/SegmentedControl.doc.mjs +1 -1
  169. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  170. package/src/SideNav/SideNav.doc.mjs +1 -7
  171. package/src/SideNav/SideNav.test.tsx +0 -20
  172. package/src/SideNav/SideNav.tsx +2 -6
  173. package/src/TabList/TabList.test.tsx +1 -185
  174. package/src/TabList/TabMenu.tsx +9 -45
  175. package/src/Table/BaseTable.tsx +1 -11
  176. package/src/Table/Table.test.tsx +0 -51
  177. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +0 -13
  178. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +1 -3
  179. package/src/Table/plugins/pagination/paginateData.ts +1 -5
  180. package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -13
  181. package/src/Table/plugins/selection/useTableSelectionState.test.tsx +0 -23
  182. package/src/Table/plugins/selection/useTableSelectionState.tsx +6 -11
  183. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +1 -35
  184. package/src/Table/plugins/sortable/useTableSortableState.tsx +4 -8
  185. package/src/TextArea/TextArea.test.tsx +3 -3
  186. package/src/TextInput/TextInput.test.tsx +3 -3
  187. package/src/Thumbnail/Thumbnail.test.tsx +2 -36
  188. package/src/Thumbnail/Thumbnail.tsx +2 -4
  189. package/src/TimeInput/TimeInput.test.tsx +0 -37
  190. package/src/TimeInput/TimeInput.tsx +1 -11
  191. package/src/Timestamp/Timestamp.doc.mjs +1 -1
  192. package/src/Toast/Toast.tsx +3 -11
  193. package/src/Toast/ToastViewport.test.tsx +0 -64
  194. package/src/Toast/ToastViewport.tsx +0 -12
  195. package/src/ToggleButton/ToggleButton.doc.mjs +0 -1
  196. package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
  197. package/src/TopNav/TopNav.doc.mjs +1 -1
  198. package/src/TreeList/TreeList.doc.mjs +2 -2
  199. package/src/Typeahead/BaseTypeahead.doc.mjs +0 -19
  200. package/src/Typeahead/BaseTypeahead.tsx +2 -5
  201. package/src/Typeahead/Typeahead.doc.mjs +1 -1
  202. package/src/Typeahead/Typeahead.test.tsx +1 -45
  203. package/src/hooks/focusableSelector.ts +1 -1
  204. package/src/hooks/useFocusTrap.doc.mjs +2 -4
  205. package/src/hooks/useFocusTrap.test.tsx +1 -149
  206. package/src/hooks/useFocusTrap.ts +1 -50
  207. package/src/theme/SyntaxTheme.doc.mjs +4 -4
  208. package/src/theme/defineTheme.ts +13 -4
  209. package/src/theme/derivedVarRegistry.test.ts +2 -2
  210. package/src/theme/expandColorScale.test.ts +0 -29
  211. package/src/theme/expandColorScale.ts +6 -11
  212. package/src/theme/hct.ts +17 -9
  213. package/src/theme/tokens.test.ts +9 -182
  214. package/src/theme/tokens.ts +4 -239
  215. package/src/theme/useTheme.test.tsx +0 -20
  216. package/src/utils/index.ts +0 -3
  217. package/src/utils/timeParser.test.ts +0 -13
  218. package/src/utils/timeParser.ts +5 -8
  219. package/dist/utils/color.d.ts +0 -42
  220. package/dist/utils/color.d.ts.map +0 -1
  221. package/dist/utils/color.js +0 -150
  222. package/src/Resizable/ResizeHandle.test.tsx +0 -180
  223. package/src/__tests__/TestIcon.tsx +0 -26
  224. package/src/docPropReferences.test.ts +0 -178
  225. package/src/utils/color.test.ts +0 -133
  226. package/src/utils/color.ts +0 -148
@@ -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 (count capped, tracks still fill)', () => {
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
- // Cap lives on the track MIN (min(100%, max(minWidth, perColumn))); the
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(min(100%, max(250px, calc((100% - 2 * var(--spacing-4)) / 3))), 1fr))',
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 in the perColumn floor calculation
92
+ // columnGap takes precedence for track-max calculation
117
93
  expect(templateColumns(grid)).toBe(
118
- 'repeat(auto-fill, minmax(min(100%, max(200px, calc((100% - 3 * var(--spacing-6)) / 4))), 1fr))',
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 with a plain 1fr track when no max specified', () => {
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 the count-cap floor when both columnGap and gap are set', () => {
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 the perColumn floor
234
+ // columnGap takes precedence over gap in track-max
259
235
  expect(templateColumns(grid)).toBe(
260
- 'repeat(auto-fill, minmax(min(100%, max(200px, calc((100% - 2 * var(--spacing-6)) / 3))), 1fr))',
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 the count-cap floor when columnGap is not set', () => {
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(min(100%, max(150px, calc((100% - 1 * var(--spacing-3)) / 2))), 1fr))',
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 the count-cap floor when no gap is set', () => {
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(min(100%, max(100px, calc(100% / 3))), 1fr))',
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 the count while filling', () => {
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
- // Count is capped via the track MIN; track MAX stays 1fr so present
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(min(100%, max(280px, calc((100% - 2 * var(--spacing-4)) / 3))), 1fr))',
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 + count cap', () => {
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(min(100%, max(280px, calc((100% - 2 * var(--spacing-4)) / 3))), 1fr))',
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. The grid stretches to 100%
42
- * of its parent and the columns that are present always fill the row, so a
43
- * layout collapsing to a single column on mobile stretches to full width
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 the column *count* at `max`
326
- * while still letting the columns that are actually present stretch to fill
327
- * the row.
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
- // perColumn = (100% - (maxCols - 1) * gap) / maxCols — the width a track
355
- // would have if exactly `maxCols` columns were present. Used as a floor so
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
- // Track min caps the count; track max stays 1fr so present columns fill.
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
  /**
@@ -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 {TestIcon} from '../__tests__/TestIcon';
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={TestIcon} data-testid="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={TestIcon} data-testid="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={TestIcon} data-testid="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={TestIcon} color="primary" data-testid="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={TestIcon} color="secondary" data-testid="icon" />);
40
+ rerender(<Icon icon={HomeIcon} color="secondary" data-testid="icon" />);
41
41
  expect(screen.getByTestId('icon')).toBeInTheDocument();
42
42
 
43
- rerender(<Icon icon={TestIcon} color="accent" data-testid="icon" />);
43
+ rerender(<Icon icon={HomeIcon} color="accent" data-testid="icon" />);
44
44
  expect(screen.getByTestId('icon')).toBeInTheDocument();
45
45
 
46
- rerender(<Icon icon={TestIcon} color="success" data-testid="icon" />);
46
+ rerender(<Icon icon={HomeIcon} color="success" data-testid="icon" />);
47
47
  expect(screen.getByTestId('icon')).toBeInTheDocument();
48
48
 
49
- rerender(<Icon icon={TestIcon} color="error" data-testid="icon" />);
49
+ rerender(<Icon icon={HomeIcon} color="error" data-testid="icon" />);
50
50
  expect(screen.getByTestId('icon')).toBeInTheDocument();
51
51
 
52
- rerender(<Icon icon={TestIcon} color="warning" data-testid="icon" />);
52
+ rerender(<Icon icon={HomeIcon} color="warning" data-testid="icon" />);
53
53
  expect(screen.getByTestId('icon')).toBeInTheDocument();
54
54
 
55
- rerender(<Icon icon={TestIcon} color="inherit" data-testid="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 icon={TestIcon} color={nonSemanticColors[0]} data-testid="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={TestIcon} color={c} data-testid="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={TestIcon} size="xsm" data-testid="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={TestIcon} size="sm" data-testid="icon" />);
93
+ rerender(<Icon icon={HomeIcon} size="sm" data-testid="icon" />);
90
94
  expect(screen.getByTestId('icon')).toBeInTheDocument();
91
95
 
92
- rerender(<Icon icon={TestIcon} size="md" data-testid="icon" />);
96
+ rerender(<Icon icon={HomeIcon} size="md" data-testid="icon" />);
93
97
  expect(screen.getByTestId('icon')).toBeInTheDocument();
94
98
 
95
- rerender(<Icon icon={TestIcon} size="lg" data-testid="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={TestIcon} ref={ref} />);
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 icon={TestIcon} data-testid="icon" role="img" aria-label="Home" />,
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={TestIcon} data-testid="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, afterEach} from 'vitest';
4
- import {render, screen, fireEvent, waitFor} from '@testing-library/react';
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(