@astryxdesign/core 0.4.7-canary.d895769 → 0.4.7-canary.de6d448

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.
@@ -88,6 +88,9 @@ export interface UseTablePaginationConfig {
88
88
  align?: 'start' | 'center' | 'end';
89
89
  /**
90
90
  * Accessible label for the pagination nav landmark.
91
+ * When `position` is 'both', the two navs get distinct names by suffixing
92
+ * this label — "{label} (top)" above and "{label} (bottom)" below — so
93
+ * same-type landmarks stay distinguishable to assistive technology.
91
94
  * @default 'Table pagination'
92
95
  */
93
96
  label?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"useTablePagination.d.ts","sourceRoot":"","sources":["../../../../src/Table/plugins/pagination/useTablePagination.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAgC7C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,wBAAwB;IAGvC,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IAEb,sEAAsE;IACtE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAIrC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAIlB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9C;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAI3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAE1D;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAEnB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAE/C;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IAInC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,MAAM,EAAE,wBAAwB,GAC/B,WAAW,CAAC,CAAC,CAAC,CAmGhB"}
1
+ {"version":3,"file":"useTablePagination.d.ts","sourceRoot":"","sources":["../../../../src/Table/plugins/pagination/useTablePagination.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAgC7C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,wBAAwB;IAGvC,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IAEb,sEAAsE;IACtE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAIrC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAIlB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9C;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAI3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAE1D;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAEnB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAE/C;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IAInC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,MAAM,EAAE,wBAAwB,GAC/B,WAAW,CAAC,CAAC,CAAC,CAgIhB"}
@@ -116,6 +116,14 @@ export function useTablePagination(config) {
116
116
  label: labelFromProps
117
117
  } = config;
118
118
  const label = labelFromProps ?? t('@astryx.table.pagination.label');
119
+ // position='both' renders two nav landmarks; suffix the base label so each
120
+ // gets a unique accessible name (axe landmark-unique).
121
+ const labelAbove = t('@astryx.table.pagination.labelAbove', {
122
+ label
123
+ });
124
+ const labelBelow = t('@astryx.table.pagination.labelBelow', {
125
+ label
126
+ });
119
127
 
120
128
  // Same guard as Pagination itself: 0/NaN/negative pageSize would produce an
121
129
  // Infinity/NaN totalPages here, which bypasses Pagination's own coercion
@@ -143,12 +151,16 @@ export function useTablePagination(config) {
143
151
  const configRef = useRef({
144
152
  paginationProps,
145
153
  position,
146
- align
154
+ align,
155
+ labelAbove,
156
+ labelBelow
147
157
  });
148
158
  configRef.current = {
149
159
  paginationProps,
150
160
  position,
151
- align
161
+ align,
162
+ labelAbove,
163
+ labelBelow
152
164
  };
153
165
 
154
166
  // Stable plugin object \u2014 created once, reads config via ref.
@@ -157,7 +169,9 @@ export function useTablePagination(config) {
157
169
  const {
158
170
  position: pos,
159
171
  paginationProps: props,
160
- align: a
172
+ align: a,
173
+ labelAbove: above,
174
+ labelBelow: below
161
175
  } = configRef.current;
162
176
  if (pos === 'none') {
163
177
  return children;
@@ -172,7 +186,10 @@ export function useTablePagination(config) {
172
186
  const makeWrapper = side => /*#__PURE__*/_jsx("div", {
173
187
  ...stylex.props(styles.wrapper, side === 'below' && styles.marginTop, side === 'above' && styles.marginBottom, a === 'center' && styles.alignCenter, a === 'end' && styles.alignEnd, a === 'start' && styles.alignStart),
174
188
  children: /*#__PURE__*/_jsx(Pagination, {
175
- ...props
189
+ ...props,
190
+ // Two navs with the same name are indistinguishable landmarks;
191
+ // give each side a unique name when both render.
192
+ label: pos === 'both' ? side === 'above' ? above : below : props.label
176
193
  })
177
194
  });
178
195
  return /*#__PURE__*/_jsxs(_Fragment, {
package/locales/en.json CHANGED
@@ -813,7 +813,15 @@
813
813
  },
814
814
  "@astryx.table.pagination.label": {
815
815
  "defaultMessage": "Table pagination",
816
- "description": "Screen-reader-only accessible name for the pagination `<nav>` appended below a data table. Kept separate from `@astryx.pagination.label` so translations may diverge."
816
+ "description": "Screen-reader-only accessible name for a table's pagination `<nav>` (rendered below and/or above the table per the plugin's position config). Kept separate from `@astryx.pagination.label` so translations may diverge. When position='both', this string is interpolated as the {label} value into `@astryx.table.pagination.labelAbove`/`labelBelow` — keep it compatible with those suffix templates."
817
+ },
818
+ "@astryx.table.pagination.labelAbove": {
819
+ "defaultMessage": "{label} (top)",
820
+ "description": "Accessible name for the pagination `<nav>` rendered above the table when position='both' renders two navs. `label` is the resolved base label (default \"Table pagination\" or consumer-supplied); the suffix keeps the two same-type landmarks distinguishable."
821
+ },
822
+ "@astryx.table.pagination.labelBelow": {
823
+ "defaultMessage": "{label} (bottom)",
824
+ "description": "Accessible name for the pagination `<nav>` rendered below the table when position='both' renders two navs. `label` is the resolved base label (default \"Table pagination\" or consumer-supplied); the suffix keeps the two same-type landmarks distinguishable."
817
825
  },
818
826
  "@astryx.timeInput.placeholder": {
819
827
  "defaultMessage": "Select a time",
@@ -611,6 +611,12 @@
611
611
  "@astryx.table.pagination.label": {
612
612
  "defaultMessage": "⟦Ţàƀłé þàĝíñàţíóñ⟧"
613
613
  },
614
+ "@astryx.table.pagination.labelAbove": {
615
+ "defaultMessage": "⟦{label} (ţóþ)⟧"
616
+ },
617
+ "@astryx.table.pagination.labelBelow": {
618
+ "defaultMessage": "⟦{label} (ƀóţţóɱ)⟧"
619
+ },
614
620
  "@astryx.timeInput.placeholder": {
615
621
  "defaultMessage": "⟦Šéłéçţ à ţíɱé⟧"
616
622
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.4.7-canary.d895769",
3
+ "version": "0.4.7-canary.de6d448",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -661,7 +661,7 @@
661
661
  "react-dom": ">=19.0.0"
662
662
  },
663
663
  "devDependencies": {
664
- "@astryxdesign/cli": "0.4.7-canary.d895769",
664
+ "@astryxdesign/cli": "0.4.7-canary.de6d448",
665
665
  "@babel/cli": "^8.0.4",
666
666
  "@babel/core": "^7.29.7",
667
667
  "@babel/preset-react": "^8.0.1",
@@ -229,7 +229,7 @@ describe('useTablePagination', () => {
229
229
  ).toBeTruthy();
230
230
  });
231
231
 
232
- it('transformTableContext renders Pagination above and below', () => {
232
+ it('transformTableContext renders distinctly named Pagination above and below', () => {
233
233
  render(
234
234
  <PaginatedTable
235
235
  data={generateItems(30)}
@@ -237,10 +237,40 @@ describe('useTablePagination', () => {
237
237
  position="both"
238
238
  />,
239
239
  );
240
- const navs = screen.getAllByRole('navigation', {
241
- name: 'Table pagination',
240
+ const table = screen.getByRole('table');
241
+ // Each nav landmark gets a unique accessible name (axe landmark-unique)
242
+ const topNav = screen.getByRole('navigation', {
243
+ name: 'Table pagination (top)',
244
+ });
245
+ const bottomNav = screen.getByRole('navigation', {
246
+ name: 'Table pagination (bottom)',
242
247
  });
243
- expect(navs).toHaveLength(2);
248
+ // Top nav comes before the table, bottom nav after it in DOM order
249
+ expect(
250
+ table.compareDocumentPosition(topNav) &
251
+ Node.DOCUMENT_POSITION_PRECEDING,
252
+ ).toBeTruthy();
253
+ expect(
254
+ table.compareDocumentPosition(bottomNav) &
255
+ Node.DOCUMENT_POSITION_FOLLOWING,
256
+ ).toBeTruthy();
257
+ });
258
+
259
+ it('position="both" interpolates a consumer label into distinct nav names', () => {
260
+ render(
261
+ <PaginatedTable
262
+ data={generateItems(30)}
263
+ pageSize={10}
264
+ position="both"
265
+ label="Users table"
266
+ />,
267
+ );
268
+ expect(
269
+ screen.getByRole('navigation', {name: 'Users table (top)'}),
270
+ ).toBeInTheDocument();
271
+ expect(
272
+ screen.getByRole('navigation', {name: 'Users table (bottom)'}),
273
+ ).toBeInTheDocument();
244
274
  });
245
275
 
246
276
  it('transformTableContext does not render Pagination when position is none', () => {
@@ -164,6 +164,9 @@ export interface UseTablePaginationConfig {
164
164
 
165
165
  /**
166
166
  * Accessible label for the pagination nav landmark.
167
+ * When `position` is 'both', the two navs get distinct names by suffixing
168
+ * this label — "{label} (top)" above and "{label} (bottom)" below — so
169
+ * same-type landmarks stay distinguishable to assistive technology.
167
170
  * @default 'Table pagination'
168
171
  */
169
172
  label?: string;
@@ -211,6 +214,10 @@ export function useTablePagination<T extends Record<string, unknown>>(
211
214
  label: labelFromProps,
212
215
  } = config;
213
216
  const label = labelFromProps ?? t('@astryx.table.pagination.label');
217
+ // position='both' renders two nav landmarks; suffix the base label so each
218
+ // gets a unique accessible name (axe landmark-unique).
219
+ const labelAbove = t('@astryx.table.pagination.labelAbove', {label});
220
+ const labelBelow = t('@astryx.table.pagination.labelBelow', {label});
214
221
 
215
222
  // Same guard as Pagination itself: 0/NaN/negative pageSize would produce an
216
223
  // Infinity/NaN totalPages here, which bypasses Pagination's own coercion
@@ -240,8 +247,20 @@ export function useTablePagination<T extends Record<string, unknown>>(
240
247
 
241
248
  // Keep current config in a ref so the plugin can read the latest values
242
249
  // without needing to recreate the plugin object on every change.
243
- const configRef = useRef({paginationProps, position, align});
244
- configRef.current = {paginationProps, position, align};
250
+ const configRef = useRef({
251
+ paginationProps,
252
+ position,
253
+ align,
254
+ labelAbove,
255
+ labelBelow,
256
+ });
257
+ configRef.current = {
258
+ paginationProps,
259
+ position,
260
+ align,
261
+ labelAbove,
262
+ labelBelow,
263
+ };
245
264
 
246
265
  // Stable plugin object \u2014 created once, reads config via ref.
247
266
  return useMemo(
@@ -251,6 +270,8 @@ export function useTablePagination<T extends Record<string, unknown>>(
251
270
  position: pos,
252
271
  paginationProps: props,
253
272
  align: a,
273
+ labelAbove: above,
274
+ labelBelow: below,
254
275
  } = configRef.current;
255
276
  if (pos === 'none') {
256
277
  return children;
@@ -277,7 +298,18 @@ export function useTablePagination<T extends Record<string, unknown>>(
277
298
  a === 'end' && styles.alignEnd,
278
299
  a === 'start' && styles.alignStart,
279
300
  )}>
280
- <Pagination {...props} />
301
+ <Pagination
302
+ {...props}
303
+ // Two navs with the same name are indistinguishable landmarks;
304
+ // give each side a unique name when both render.
305
+ label={
306
+ pos === 'both'
307
+ ? side === 'above'
308
+ ? above
309
+ : below
310
+ : props.label
311
+ }
312
+ />
281
313
  </div>
282
314
  );
283
315
 
@@ -72,6 +72,12 @@ export const docs = {
72
72
  description: 'Horizontal alignment of the pagination controls.',
73
73
  default: "'center'",
74
74
  },
75
+ {
76
+ name: 'label',
77
+ type: 'string',
78
+ description: "Accessible name for the pagination nav landmark. With position='both' the two navs get distinct names — the label suffixed as '(top)' and '(bottom)' — so same-type landmarks stay unique (axe landmark-unique).",
79
+ default: "'Table pagination'",
80
+ },
75
81
  ],
76
82
  };
77
83
 
@@ -129,5 +135,6 @@ export const docsDense = {
129
135
  pageSize: 'Items per page. Default 10.',
130
136
  variant: "Pagination variant: 'pages'|'count'|'compact'|'dots'|'none'.",
131
137
  position: "Render position: 'below'|'above'|'both'|'none'.",
138
+ label: "Accessible name for the nav landmark; position='both' emits '<label> (top)' / '<label> (bottom)'.",
132
139
  },
133
140
  };
@@ -1,80 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- import {describe, it, expect} from 'vitest';
4
-
5
- import {docs as ButtonDocs} from '../Button/Button.doc.mjs';
6
- import {docs as ContextMenuDocs} from '../ContextMenu/ContextMenu.doc.mjs';
7
- import {docs as DialogDocs} from '../Dialog/Dialog.doc.mjs';
8
- import {docs as LinkDocs} from '../Link/Link.doc.mjs';
9
- import {docs as MoreMenuDocs} from '../MoreMenu/MoreMenu.doc.mjs';
10
- import {docs as NumberInputDocs} from '../NumberInput/NumberInput.doc.mjs';
11
- import {docs as PowerSearchDocs} from '../PowerSearch/PowerSearch.doc.mjs';
12
- import {docs as SelectorDocs} from '../Selector/Selector.doc.mjs';
13
- import {docs as TextInputDocs} from '../TextInput/TextInput.doc.mjs';
14
- import {docs as ToastDocs} from '../Toast/Toast.doc.mjs';
15
- import {docs as TokenizerDocs} from '../Tokenizer/Tokenizer.doc.mjs';
16
-
17
- // `docs` is the ComponentDoc union (single vs multi component); narrow it
18
- // for structural access to either the top-level props or the first
19
- // sub-component's props.
20
- function getProps(docs: unknown): {name: string}[] {
21
- const doc = docs as {
22
- props?: {name: string}[];
23
- components?: {props?: {name: string}[]}[];
24
- };
25
- return doc.props || doc.components?.[0]?.props || [];
26
- }
27
-
28
- describe('API Contract Drift Audit (#4163)', () => {
29
- it('documents Button link mode props', () => {
30
- const props = getProps(ButtonDocs).map(p => p.name);
31
- expect(props).toContain('href');
32
- expect(props).toContain('as');
33
- expect(props).toContain('target');
34
- expect(props).toContain('rel');
35
- });
36
-
37
- it('documents Toast onDismiss callback', () => {
38
- const props = getProps(ToastDocs).map(p => p.name);
39
- expect(props).toContain('onDismiss');
40
- });
41
-
42
- it('documents ContextMenu and MoreMenu onOpenChange callback', () => {
43
- const contextProps = getProps(ContextMenuDocs).map(p => p.name);
44
- const moreProps = getProps(MoreMenuDocs).map(p => p.name);
45
- expect(contextProps).toContain('onOpenChange');
46
- expect(moreProps).toContain('onOpenChange');
47
- });
48
-
49
- it('documents Selector startIcon and isLoading props', () => {
50
- const props = getProps(SelectorDocs).map(p => p.name);
51
- expect(props).toContain('startIcon');
52
- expect(props).toContain('isLoading');
53
- });
54
-
55
- it('documents TextInput and NumberInput onEnter / onKeyDown callbacks', () => {
56
- const textProps = getProps(TextInputDocs).map(p => p.name);
57
- const numProps = getProps(NumberInputDocs).map(p => p.name);
58
- expect(textProps).toContain('onEnter');
59
- expect(textProps).toContain('onKeyDown');
60
- expect(numProps).toContain('onEnter');
61
- expect(numProps).toContain('onKeyDown');
62
- });
63
-
64
- it('documents PowerSearch and Tokenizer onFocus / onBlur callbacks', () => {
65
- const powerProps = getProps(PowerSearchDocs).map(p => p.name);
66
- const tokProps = getProps(TokenizerDocs).map(p => p.name);
67
- expect(powerProps).toContain('onFocus');
68
- expect(powerProps).toContain('onBlur');
69
- expect(tokProps).toContain('onFocus');
70
- expect(tokProps).toContain('onBlur');
71
- });
72
-
73
- it('documents Link download / referrerPolicy and Dialog padding props', () => {
74
- const linkProps = getProps(LinkDocs).map(p => p.name);
75
- const dialogProps = getProps(DialogDocs).map(p => p.name);
76
- expect(linkProps).toContain('download');
77
- expect(linkProps).toContain('referrerPolicy');
78
- expect(dialogProps).toContain('padding');
79
- });
80
- });
@@ -1,24 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- import {describe, it, expect} from 'vitest';
4
-
5
- import {docs as FieldDocs} from '../Field/Field.doc.mjs';
6
-
7
- // `docs` is the ComponentDoc union (single vs multi component); narrow it
8
- // for structural access to either the top-level props or the first
9
- // sub-component's props.
10
- function getProps(docs: unknown): {name: string}[] {
11
- const doc = docs as {
12
- props?: {name: string}[];
13
- components?: {props?: {name: string}[]}[];
14
- };
15
- return doc.props || doc.components?.[0]?.props || [];
16
- }
17
-
18
- describe('Field Component API Contract Drift (#4163)', () => {
19
- it('documents Field labelID and isGroupLabel props', () => {
20
- const props = getProps(FieldDocs).map(p => p.name);
21
- expect(props).toContain('labelID');
22
- expect(props).toContain('isGroupLabel');
23
- });
24
- });
@@ -1,69 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- import {describe, it, expect} from 'vitest';
4
-
5
- import {docs as CheckboxInputDocs} from '../CheckboxInput/CheckboxInput.doc.mjs';
6
- import {docs as CheckboxListDocs} from '../CheckboxList/CheckboxList.doc.mjs';
7
- import {docs as DateInputDocs} from '../DateInput/DateInput.doc.mjs';
8
- import {docs as DateRangeInputDocs} from '../DateRangeInput/DateRangeInput.doc.mjs';
9
- import {docs as DateTimeInputDocs} from '../DateTimeInput/DateTimeInput.doc.mjs';
10
- import {docs as FileInputDocs} from '../FileInput/FileInput.doc.mjs';
11
- import {docs as MultiSelectorDocs} from '../MultiSelector/MultiSelector.doc.mjs';
12
- import {docs as NumberInputDocs} from '../NumberInput/NumberInput.doc.mjs';
13
- import {docs as RadioListDocs} from '../RadioList/RadioList.doc.mjs';
14
- import {docs as SelectorDocs} from '../Selector/Selector.doc.mjs';
15
- import {docs as SliderDocs} from '../Slider/Slider.doc.mjs';
16
- import {docs as SwitchDocs} from '../Switch/Switch.doc.mjs';
17
- import {docs as TextAreaDocs} from '../TextArea/TextArea.doc.mjs';
18
- import {docs as TextInputDocs} from '../TextInput/TextInput.doc.mjs';
19
- import {docs as TimeInputDocs} from '../TimeInput/TimeInput.doc.mjs';
20
- import {docs as TokenizerDocs} from '../Tokenizer/Tokenizer.doc.mjs';
21
- import {docs as TypeaheadDocs} from '../Typeahead/Typeahead.doc.mjs';
22
-
23
- const inputDocsSuite = [
24
- {name: 'CheckboxInput', docs: CheckboxInputDocs},
25
- {name: 'CheckboxList', docs: CheckboxListDocs},
26
- {name: 'DateInput', docs: DateInputDocs},
27
- {name: 'DateRangeInput', docs: DateRangeInputDocs},
28
- {name: 'DateTimeInput', docs: DateTimeInputDocs},
29
- {name: 'FileInput', docs: FileInputDocs},
30
- {name: 'MultiSelector', docs: MultiSelectorDocs},
31
- {name: 'NumberInput', docs: NumberInputDocs},
32
- {name: 'RadioList', docs: RadioListDocs},
33
- {name: 'Selector', docs: SelectorDocs},
34
- {name: 'Slider', docs: SliderDocs},
35
- {name: 'Switch', docs: SwitchDocs},
36
- {name: 'TextArea', docs: TextAreaDocs},
37
- {name: 'TextInput', docs: TextInputDocs},
38
- {name: 'TimeInput', docs: TimeInputDocs},
39
- {name: 'Tokenizer', docs: TokenizerDocs},
40
- {name: 'Typeahead', docs: TypeaheadDocs},
41
- ];
42
-
43
- describe('Input Family width Prop Contract (#4163)', () => {
44
- inputDocsSuite.forEach(({name, docs}) => {
45
- it(`documents the width prop for ${name}`, () => {
46
- // `docs` is the ComponentDoc union (single vs multi component); narrow it
47
- // for structural access to either the top-level props or the first
48
- // sub-component's props.
49
- const doc = docs as {
50
- props?: {name: string; type: string}[];
51
- components?: {props?: {name: string; type: string}[]}[];
52
- };
53
- const propsList = (doc.props || doc.components?.[0]?.props) as {
54
- name: string;
55
- type: string;
56
- }[];
57
- expect(
58
- propsList,
59
- `${name}.doc.mjs must expose a props array`,
60
- ).toBeDefined();
61
- const widthProp = propsList.find(p => p.name === 'width');
62
- expect(
63
- widthProp,
64
- `${name}.doc.mjs must document the width prop`,
65
- ).toBeDefined();
66
- expect(widthProp?.type).toBe('SizeValue');
67
- });
68
- });
69
- });
@@ -1,39 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- import {describe, it, expect} from 'vitest';
4
-
5
- import {docs as CodeBlockDocs} from '../CodeBlock/CodeBlock.doc.mjs';
6
- import {docs as StackDocs} from '../Stack/Stack.doc.mjs';
7
- import {docs as ToolbarDocs} from '../Toolbar/Toolbar.doc.mjs';
8
-
9
- // `docs` is the ComponentDoc union (single vs multi component); narrow it
10
- // for structural access to either the top-level props or the first
11
- // sub-component's props.
12
- function getProps(docs: unknown): {name: string}[] {
13
- const doc = docs as {
14
- props?: {name: string}[];
15
- components?: {props?: {name: string}[]}[];
16
- };
17
- return doc.props || doc.components?.[0]?.props || [];
18
- }
19
-
20
- describe('Structural Component API Contract Drift (#4163)', () => {
21
- it('documents CodeBlock highlightMode prop', () => {
22
- const props = getProps(CodeBlockDocs).map(p => p.name);
23
- expect(props).toContain('highlightMode');
24
- });
25
-
26
- it('documents Toolbar dividers prop', () => {
27
- const props = getProps(ToolbarDocs).map(p => p.name);
28
- expect(props).toContain('dividers');
29
- });
30
-
31
- it('documents Stack direction and base props', () => {
32
- const props = getProps(StackDocs).map(p => p.name);
33
- expect(props).toContain('direction');
34
- expect(props).toContain('gap');
35
- expect(props).toContain('padding');
36
- expect(props).toContain('width');
37
- expect(props).toContain('height');
38
- });
39
- });