@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.
- package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
- package/locales/en.json +9 -1
- package/locales/pseudo.json +6 -0
- package/package.json +2 -2
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
- package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
- package/src/Table/useTablePagination.doc.mjs +7 -0
- package/src/__tests__/apiContractDrift.test.tsx +0 -80
- package/src/__tests__/fieldContract.test.tsx +0 -24
- package/src/__tests__/inputWidthContract.test.tsx +0 -69
- package/src/__tests__/structuralComponentContract.test.tsx +0 -39
|
@@ -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
|
|
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
|
|
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",
|
package/locales/pseudo.json
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
|
241
|
-
|
|
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
|
-
|
|
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({
|
|
244
|
-
|
|
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
|
|
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
|
-
});
|