@astryxdesign/core 0.1.2-canary.eeff948 → 0.1.2-canary.f03e2cc
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/Field/FieldLabel.d.ts.map +1 -1
- package/dist/Field/FieldLabel.js +5 -5
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +3 -3
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +2 -1
- package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
- package/dist/NavMenu/NavHeadingMenu.js +2 -18
- package/dist/Pagination/Pagination.d.ts +1 -1
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +1 -7
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +2 -1
- package/dist/Table/plugins/pagination/paginateData.d.ts +1 -2
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +3 -8
- package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/useTablePagination.js +1 -6
- package/dist/astryx.css +0 -3
- package/dist/astryx.umd.js +34 -34
- package/dist/astryx.umd.js.map +4 -4
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +0 -1
- package/package.json +5 -6
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Field/FieldLabel.tsx +0 -2
- package/src/Layer/useLayer.doc.mjs +0 -5
- package/src/Link/Link.tsx +3 -18
- package/src/MetadataList/MetadataListItem.doc.mjs +0 -4
- package/src/MultiSelector/MultiSelector.test.tsx +0 -60
- package/src/MultiSelector/MultiSelector.tsx +2 -1
- package/src/NavMenu/NavHeadingMenu.test.tsx +0 -45
- package/src/NavMenu/NavHeadingMenu.tsx +2 -21
- package/src/Pagination/Pagination.doc.mjs +3 -3
- package/src/Pagination/Pagination.test.tsx +0 -59
- package/src/Pagination/Pagination.tsx +1 -9
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/Selector/Selector.test.tsx +0 -56
- package/src/Selector/Selector.tsx +2 -1
- package/src/Table/plugins/pagination/paginateData.ts +3 -10
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -16
- package/src/Table/plugins/pagination/useTablePagination.tsx +1 -8
- package/src/utils/index.ts +0 -7
package/dist/utils/index.d.ts
CHANGED
|
@@ -14,8 +14,6 @@ export { parseStyleKey } from './parseStyleKey';
|
|
|
14
14
|
export { getKey, type Key, type KeyFallback } from './getKey';
|
|
15
15
|
export { mergeProps } from './mergeProps';
|
|
16
16
|
export { mergeRefs } from './mergeRefs';
|
|
17
|
-
export { themeProps, themeDataAttributes } from './themeProps';
|
|
18
|
-
export type { ClassValue, ClassProps, ThemeDataAttributes, ThemeProps, } from './themeProps';
|
|
19
17
|
export { groupItems, getItemGroup } from './groupItems';
|
|
20
18
|
export type { ItemGroup } from './groupItems';
|
|
21
19
|
export { observeResize, unobserveResize } from './sharedResizeObserver';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,YAAY,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAEvC,YAAY,EAAC,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAEhF,OAAO,EACL,cAAc,EACd,SAAS,EACT,QAAQ,EACR,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,8BAA8B,EAC9B,sBAAsB,EACtB,eAAe,EACf,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,2BAA2B,GAC5B,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,aAAa,EACb,SAAS,EACT,UAAU,EACV,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAC,aAAa,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,MAAM,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAC,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,YAAY,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAEvC,YAAY,EAAC,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAEhF,OAAO,EACL,cAAc,EACd,SAAS,EACT,QAAQ,EACR,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,8BAA8B,EAC9B,sBAAsB,EACtB,eAAe,EACf,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,2BAA2B,GAC5B,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,aAAa,EACb,SAAS,EACT,UAAU,EACV,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAC,aAAa,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,MAAM,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAC,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AACtD,YAAY,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,aAAa,EAAE,eAAe,EAAC,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -14,7 +14,6 @@ export { parseStyleKey } from "./parseStyleKey.js";
|
|
|
14
14
|
export { getKey } from "./getKey.js";
|
|
15
15
|
export { mergeProps } from "./mergeProps.js";
|
|
16
16
|
export { mergeRefs } from "./mergeRefs.js";
|
|
17
|
-
export { themeProps, themeDataAttributes } from "./themeProps.js";
|
|
18
17
|
export { groupItems, getItemGroup } from "./groupItems.js";
|
|
19
18
|
export { observeResize, unobserveResize } from "./sharedResizeObserver.js";
|
|
20
19
|
export { isRenderable } from "./isRenderable.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.1.2-canary.
|
|
3
|
+
"version": "0.1.2-canary.f03e2cc",
|
|
4
4
|
"displayName": "XDS 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",
|
|
@@ -635,15 +635,14 @@
|
|
|
635
635
|
"@stylexjs/babel-plugin": "^0.19.0",
|
|
636
636
|
"@testing-library/dom": "^10.0.0",
|
|
637
637
|
"@testing-library/jest-dom": "^6.6.0",
|
|
638
|
-
"@testing-library/react": "^16.3.2"
|
|
639
|
-
"rimraf": "^6.0.1"
|
|
638
|
+
"@testing-library/react": "^16.3.2"
|
|
640
639
|
},
|
|
641
640
|
"scripts": {
|
|
642
|
-
"build": "
|
|
643
|
-
"build:esm": "babel src --out-dir dist --extensions .ts,.tsx --out-file-extension .js --ignore
|
|
641
|
+
"build": "rm -rf dist && pnpm build:esm && tsc --project tsconfig.build.json && pnpm build:css && pnpm build:umd",
|
|
642
|
+
"build:esm": "babel src --out-dir dist --extensions '.ts,.tsx' --out-file-extension '.js' --ignore '**/*.test.ts,**/*.test.tsx,**/__tests__/**,**/*.perf.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css,**/*.css.d.ts' --config-file ./babel.config.json",
|
|
644
643
|
"build:css": "node ../../scripts/build-css.mjs",
|
|
645
644
|
"build:umd": "node ../../scripts/build-umd.mjs",
|
|
646
|
-
"dev": "babel src --watch --out-dir dist --extensions .ts,.tsx --out-file-extension .js --ignore
|
|
645
|
+
"dev": "babel src --watch --out-dir dist --extensions '.ts,.tsx' --out-file-extension '.js' --ignore '**/*.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css*' --config-file ./babel.config.json",
|
|
647
646
|
"test": "vitest run --root ../..",
|
|
648
647
|
"test:watch": "vitest --root ../..",
|
|
649
648
|
"lint": "eslint src",
|
|
@@ -147,7 +147,7 @@ export const docsZh = {
|
|
|
147
147
|
{name: 'hasOutsideDays', type: 'boolean', description: '显示相邻月份的日期。', default: 'true'},
|
|
148
148
|
{name: 'hasWeekNumbers', type: 'boolean', description: '显示 ISO 周数。', default: 'false'},
|
|
149
149
|
{name: 'hasVariableRowCount', type: 'boolean', description: '可变行数与固定 6 行网格。', default: 'false'},
|
|
150
|
-
{name: 'weekStartsOn', type: "0 | 1 | 2 | 3 | 4 | 5 | 6 | 'sun'
|
|
150
|
+
{name: 'weekStartsOn', type: "0 | 1 | 2 | 3 | 4 | 5 | 6 | 'sun'…'sat'", description: '每周起始日。可为数字(0=周日)或三字母星期缩写(如 "mon")。', default: '0'},
|
|
151
151
|
],
|
|
152
152
|
theming: {
|
|
153
153
|
targets: [
|
package/src/Field/FieldLabel.tsx
CHANGED
|
@@ -35,7 +35,6 @@ const styles = stylex.create({
|
|
|
35
35
|
gap: spacingVars['--spacing-1'],
|
|
36
36
|
fontFamily: typographyVars['--font-family-body'],
|
|
37
37
|
fontSize: typeScaleVars['--text-label-size'],
|
|
38
|
-
lineHeight: typeScaleVars['--text-label-leading'],
|
|
39
38
|
fontWeight: fontWeightVars['--font-weight-medium'],
|
|
40
39
|
color: colorVars['--color-text-secondary'],
|
|
41
40
|
cursor: 'pointer',
|
|
@@ -62,7 +61,6 @@ const styles = stylex.create({
|
|
|
62
61
|
optionalRequired: {
|
|
63
62
|
fontWeight: fontWeightVars['--font-weight-normal'],
|
|
64
63
|
fontSize: typeScaleVars['--text-supporting-size'],
|
|
65
|
-
lineHeight: typeScaleVars['--text-supporting-leading'],
|
|
66
64
|
color: colorVars['--color-text-secondary'],
|
|
67
65
|
},
|
|
68
66
|
description: {
|
|
@@ -144,11 +144,6 @@ export const docsDense = {
|
|
|
144
144
|
guidance: true,
|
|
145
145
|
description: 'Build on Popover/HoverCard/Tooltip for common overlay patterns.',
|
|
146
146
|
},
|
|
147
|
-
{
|
|
148
|
-
guidance: true,
|
|
149
|
-
description:
|
|
150
|
-
'Rely on the Popover API top layer to escape clipping/stacking: render inline (no portal) to inherit the trigger theme cascade and natural focus order. Use `as: "span"` when the layer must be valid in inline contexts like a paragraph.',
|
|
151
|
-
},
|
|
152
147
|
{
|
|
153
148
|
guidance: false,
|
|
154
149
|
description:
|
package/src/Link/Link.tsx
CHANGED
|
@@ -106,20 +106,10 @@ const styles = stylex.create({
|
|
|
106
106
|
*/
|
|
107
107
|
const linkColorStyles = stylex.create({
|
|
108
108
|
primary: {
|
|
109
|
-
color:
|
|
110
|
-
default: colorVars['--color-text-primary'],
|
|
111
|
-
':hover': {
|
|
112
|
-
'@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-text-primary']}, ${colorVars['--color-tint-hover']} 15%)`,
|
|
113
|
-
},
|
|
114
|
-
},
|
|
109
|
+
color: colorVars['--color-text-primary'],
|
|
115
110
|
},
|
|
116
111
|
secondary: {
|
|
117
|
-
color:
|
|
118
|
-
default: colorVars['--color-text-secondary'],
|
|
119
|
-
':hover': {
|
|
120
|
-
'@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-text-secondary']}, ${colorVars['--color-tint-hover']} 15%)`,
|
|
121
|
-
},
|
|
122
|
-
},
|
|
112
|
+
color: colorVars['--color-text-secondary'],
|
|
123
113
|
},
|
|
124
114
|
disabled: {
|
|
125
115
|
color: colorVars['--color-text-disabled'],
|
|
@@ -128,12 +118,7 @@ const linkColorStyles = stylex.create({
|
|
|
128
118
|
color: colorVars['--color-text-secondary'],
|
|
129
119
|
},
|
|
130
120
|
accent: {
|
|
131
|
-
color:
|
|
132
|
-
default: colorVars['--color-text-accent'],
|
|
133
|
-
':hover': {
|
|
134
|
-
'@media (hover: hover)': `color-mix(in srgb, ${colorVars['--color-text-accent']}, ${colorVars['--color-tint-hover']} 15%)`,
|
|
135
|
-
},
|
|
136
|
-
},
|
|
121
|
+
color: colorVars['--color-text-accent'],
|
|
137
122
|
},
|
|
138
123
|
inherit: {
|
|
139
124
|
color: 'inherit',
|
|
@@ -8,10 +8,6 @@ export const docs = {
|
|
|
8
8
|
displayName: 'Metadata List Item',
|
|
9
9
|
isHiddenFromOverview: true,
|
|
10
10
|
description: 'A single labeled metadata value within an MetadataList.',
|
|
11
|
-
playground: {
|
|
12
|
-
defaults: {label: 'Status', children: 'Active'},
|
|
13
|
-
wrapper: {component: 'MetadataList'},
|
|
14
|
-
},
|
|
15
11
|
props: [
|
|
16
12
|
{
|
|
17
13
|
name: 'children',
|
|
@@ -675,64 +675,4 @@ describe('MultiSelector', () => {
|
|
|
675
675
|
it('has displayName', () => {
|
|
676
676
|
expect(MultiSelector.displayName).toBe('MultiSelector');
|
|
677
677
|
});
|
|
678
|
-
|
|
679
|
-
describe('keyboard accessibility', () => {
|
|
680
|
-
it('trigger is focusable via Tab when enabled', async () => {
|
|
681
|
-
const user = userEvent.setup();
|
|
682
|
-
render(
|
|
683
|
-
<MultiSelector
|
|
684
|
-
label="Fruit"
|
|
685
|
-
options={defaultOptions}
|
|
686
|
-
value={[]}
|
|
687
|
-
onChange={() => {}}
|
|
688
|
-
/>,
|
|
689
|
-
);
|
|
690
|
-
await user.tab();
|
|
691
|
-
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
it('trigger is not focusable when disabled', () => {
|
|
695
|
-
render(
|
|
696
|
-
<MultiSelector
|
|
697
|
-
label="Fruit"
|
|
698
|
-
options={defaultOptions}
|
|
699
|
-
value={[]}
|
|
700
|
-
onChange={() => {}}
|
|
701
|
-
isDisabled
|
|
702
|
-
/>,
|
|
703
|
-
);
|
|
704
|
-
expect(screen.getByRole('combobox')).toHaveAttribute('tabIndex', '-1');
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
it('opens the listbox with ArrowDown from a focused trigger', async () => {
|
|
708
|
-
const user = userEvent.setup();
|
|
709
|
-
render(
|
|
710
|
-
<MultiSelector
|
|
711
|
-
label="Fruit"
|
|
712
|
-
options={defaultOptions}
|
|
713
|
-
value={[]}
|
|
714
|
-
onChange={() => {}}
|
|
715
|
-
/>,
|
|
716
|
-
);
|
|
717
|
-
const trigger = screen.getByRole('combobox');
|
|
718
|
-
await user.tab();
|
|
719
|
-
expect(trigger).toHaveFocus();
|
|
720
|
-
await user.keyboard('{ArrowDown}');
|
|
721
|
-
expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
it('clear button is reachable by keyboard', () => {
|
|
725
|
-
render(
|
|
726
|
-
<MultiSelector
|
|
727
|
-
label="Fruit"
|
|
728
|
-
options={defaultOptions}
|
|
729
|
-
value={['Apple', 'Banana']}
|
|
730
|
-
onChange={() => {}}
|
|
731
|
-
hasClear
|
|
732
|
-
/>,
|
|
733
|
-
);
|
|
734
|
-
const clear = screen.getByRole('button', {name: 'Clear all Fruit'});
|
|
735
|
-
expect(clear).not.toHaveAttribute('tabIndex', '-1');
|
|
736
|
-
});
|
|
737
|
-
});
|
|
738
678
|
});
|
|
@@ -1159,7 +1159,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
1159
1159
|
aria-busy={isBusy || undefined}
|
|
1160
1160
|
disabled={isDisabled}
|
|
1161
1161
|
onKeyDown={onKeyDown}
|
|
1162
|
-
tabIndex={
|
|
1162
|
+
tabIndex={-1}
|
|
1163
1163
|
{...stylex.props(styles.trigger)}>
|
|
1164
1164
|
<span {...stylex.props(styles.triggerContent)}>
|
|
1165
1165
|
{renderTriggerContent()}
|
|
@@ -1169,6 +1169,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
|
|
|
1169
1169
|
{hasClear && value.length > 0 && !isDisabled && (
|
|
1170
1170
|
<button
|
|
1171
1171
|
type="button"
|
|
1172
|
+
tabIndex={-1}
|
|
1172
1173
|
onClick={handleClear}
|
|
1173
1174
|
aria-label={`Clear all ${label}`}
|
|
1174
1175
|
{...stylex.props(styles.clearButton)}>
|
|
@@ -204,51 +204,6 @@ describe('keyboard navigation', () => {
|
|
|
204
204
|
await user.keyboard('{End}');
|
|
205
205
|
expect(document.activeElement).toBe(items[2]);
|
|
206
206
|
});
|
|
207
|
-
|
|
208
|
-
it('activates a focused onClick-only item with Enter', async () => {
|
|
209
|
-
const user = userEvent.setup();
|
|
210
|
-
const onClick = vi.fn();
|
|
211
|
-
render(
|
|
212
|
-
<NavHeadingMenu>
|
|
213
|
-
<NavHeadingMenuItem label="Action" onClick={onClick} />
|
|
214
|
-
</NavHeadingMenu>,
|
|
215
|
-
);
|
|
216
|
-
const item = screen.getByRole('menuitem');
|
|
217
|
-
item.focus();
|
|
218
|
-
|
|
219
|
-
await user.keyboard('{Enter}');
|
|
220
|
-
expect(onClick).toHaveBeenCalledOnce();
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('activates a focused onClick-only item with Space', async () => {
|
|
224
|
-
const user = userEvent.setup();
|
|
225
|
-
const onClick = vi.fn();
|
|
226
|
-
render(
|
|
227
|
-
<NavHeadingMenu>
|
|
228
|
-
<NavHeadingMenuItem label="Action" onClick={onClick} />
|
|
229
|
-
</NavHeadingMenu>,
|
|
230
|
-
);
|
|
231
|
-
const item = screen.getByRole('menuitem');
|
|
232
|
-
item.focus();
|
|
233
|
-
|
|
234
|
-
await user.keyboard('{ }');
|
|
235
|
-
expect(onClick).toHaveBeenCalledOnce();
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it('does not activate a disabled item with Enter', async () => {
|
|
239
|
-
const user = userEvent.setup();
|
|
240
|
-
const onClick = vi.fn();
|
|
241
|
-
render(
|
|
242
|
-
<NavHeadingMenu>
|
|
243
|
-
<NavHeadingMenuItem label="Action" onClick={onClick} isDisabled />
|
|
244
|
-
</NavHeadingMenu>,
|
|
245
|
-
);
|
|
246
|
-
const item = screen.getByRole('menuitem');
|
|
247
|
-
item.focus();
|
|
248
|
-
|
|
249
|
-
await user.keyboard('{Enter}');
|
|
250
|
-
expect(onClick).not.toHaveBeenCalled();
|
|
251
|
-
});
|
|
252
207
|
});
|
|
253
208
|
|
|
254
209
|
describe('context forwarding', () => {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - /packages/core/src/NavMenu/index.ts
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import React, {
|
|
15
|
+
import React, {useMemo, type ReactNode} from 'react';
|
|
16
16
|
import * as stylex from '@stylexjs/stylex';
|
|
17
17
|
import {spacingVars} from '../theme/tokens.stylex';
|
|
18
18
|
import {mergeProps, mergeRefs} from '../utils';
|
|
@@ -102,25 +102,6 @@ export function NavHeadingMenu({
|
|
|
102
102
|
onEscape: closeMenu,
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
// Extend useListFocus with Enter/Space activation. Items rendered without an
|
|
106
|
-
// `href` are `<div role="menuitem">` elements, which have no native keyboard
|
|
107
|
-
// activation — without this, Enter/Space on a focused onClick-only item does
|
|
108
|
-
// nothing. Anchor items (with `href`) already activate on Enter natively.
|
|
109
|
-
const listKeyDown = useCallback(
|
|
110
|
-
(e: React.KeyboardEvent) => {
|
|
111
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
112
|
-
const focused = document.activeElement as HTMLElement | null;
|
|
113
|
-
if (focused?.getAttribute('role') === 'menuitem') {
|
|
114
|
-
e.preventDefault();
|
|
115
|
-
focused.click();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
handleKeyDown(e);
|
|
120
|
-
},
|
|
121
|
-
[handleKeyDown],
|
|
122
|
-
);
|
|
123
|
-
|
|
124
105
|
const ctx = useMemo(
|
|
125
106
|
() => ({
|
|
126
107
|
closeMenu: closeMenu ?? (() => {}),
|
|
@@ -136,7 +117,7 @@ export function NavHeadingMenu({
|
|
|
136
117
|
<div
|
|
137
118
|
ref={mergeRefs(ref, listRef)}
|
|
138
119
|
role="menu"
|
|
139
|
-
onKeyDown={
|
|
120
|
+
onKeyDown={handleKeyDown}
|
|
140
121
|
data-testid={testId}
|
|
141
122
|
{...mergeProps(
|
|
142
123
|
themeProps('nav-heading-menu', {size}),
|
|
@@ -47,7 +47,7 @@ export const docs = {
|
|
|
47
47
|
{
|
|
48
48
|
name: 'pageSize',
|
|
49
49
|
type: 'number',
|
|
50
|
-
description: 'Number of items per page.
|
|
50
|
+
description: 'Number of items per page.',
|
|
51
51
|
default: '10',
|
|
52
52
|
},
|
|
53
53
|
{
|
|
@@ -175,7 +175,7 @@ export const docsZh = {
|
|
|
175
175
|
{
|
|
176
176
|
name: 'pageSize',
|
|
177
177
|
type: 'number',
|
|
178
|
-
description: '
|
|
178
|
+
description: '每页项目数。',
|
|
179
179
|
default: '10',
|
|
180
180
|
},
|
|
181
181
|
{
|
|
@@ -277,7 +277,7 @@ export const docsDense = {
|
|
|
277
277
|
totalItems: 'Total items. Calculates page count. Precedence over totalPages.',
|
|
278
278
|
totalPages: 'Total pages. Use when page count known but not item count.',
|
|
279
279
|
hasMore: 'More pages exist after current. For cursor-based pagination.',
|
|
280
|
-
pageSize: 'Items per page
|
|
280
|
+
pageSize: 'Items per page.',
|
|
281
281
|
pageSizeOptions: 'Page size options. Shows selector dropdown when provided.',
|
|
282
282
|
onPageSizeChange: 'Called on page size change. Auto resets to page 1.',
|
|
283
283
|
variant: 'Display between prev/next buttons.',
|
|
@@ -239,65 +239,6 @@ describe('Pagination', () => {
|
|
|
239
239
|
});
|
|
240
240
|
});
|
|
241
241
|
|
|
242
|
-
// ---------------------------------------------------------------------------
|
|
243
|
-
// pageSize guarding
|
|
244
|
-
// ---------------------------------------------------------------------------
|
|
245
|
-
|
|
246
|
-
describe('pageSize guarding', () => {
|
|
247
|
-
it('does not crash the dots variant when pageSize is 0', () => {
|
|
248
|
-
render(
|
|
249
|
-
<Pagination
|
|
250
|
-
page={1}
|
|
251
|
-
onChange={() => {}}
|
|
252
|
-
totalItems={5}
|
|
253
|
-
pageSize={0}
|
|
254
|
-
variant="dots"
|
|
255
|
-
/>,
|
|
256
|
-
);
|
|
257
|
-
const group = screen.getByRole('group', {name: 'Page indicators'});
|
|
258
|
-
expect(within(group).getAllByRole('button')).toHaveLength(5);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it('treats NaN pageSize as the default', () => {
|
|
262
|
-
render(
|
|
263
|
-
<Pagination
|
|
264
|
-
page={1}
|
|
265
|
-
onChange={() => {}}
|
|
266
|
-
totalItems={50}
|
|
267
|
-
pageSize={NaN}
|
|
268
|
-
variant="compact"
|
|
269
|
-
/>,
|
|
270
|
-
);
|
|
271
|
-
expect(screen.getByText('Page 1 of 5')).toBeInTheDocument();
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it('clamps negative pageSize to 1', () => {
|
|
275
|
-
render(
|
|
276
|
-
<Pagination
|
|
277
|
-
page={1}
|
|
278
|
-
onChange={() => {}}
|
|
279
|
-
totalItems={5}
|
|
280
|
-
pageSize={-10}
|
|
281
|
-
variant="compact"
|
|
282
|
-
/>,
|
|
283
|
-
);
|
|
284
|
-
expect(screen.getByText('Page 1 of 5')).toBeInTheDocument();
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('floors fractional pageSize', () => {
|
|
288
|
-
render(
|
|
289
|
-
<Pagination
|
|
290
|
-
page={1}
|
|
291
|
-
onChange={() => {}}
|
|
292
|
-
totalItems={50}
|
|
293
|
-
pageSize={2.5}
|
|
294
|
-
variant="compact"
|
|
295
|
-
/>,
|
|
296
|
-
);
|
|
297
|
-
expect(screen.getByText('Page 1 of 25')).toBeInTheDocument();
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
|
-
|
|
301
242
|
// ---------------------------------------------------------------------------
|
|
302
243
|
// Variant: dots
|
|
303
244
|
// ---------------------------------------------------------------------------
|
|
@@ -335,7 +335,7 @@ export function Pagination({
|
|
|
335
335
|
totalItems,
|
|
336
336
|
totalPages: totalPagesProp,
|
|
337
337
|
hasMore,
|
|
338
|
-
pageSize
|
|
338
|
+
pageSize = 10,
|
|
339
339
|
pageSizeOptions,
|
|
340
340
|
onPageSizeChange,
|
|
341
341
|
variant = 'pages',
|
|
@@ -351,14 +351,6 @@ export function Pagination({
|
|
|
351
351
|
}: PaginationProps) {
|
|
352
352
|
const [, startTransition] = useTransition();
|
|
353
353
|
|
|
354
|
-
// pageSize is typed as number, so 0, NaN, and negatives are valid at the
|
|
355
|
-
// type level but yield Infinity/NaN page counts, and
|
|
356
|
-
// Array.from({length: Infinity}) crashes the dots variant. Coerce to a
|
|
357
|
-
// positive integer; non-finite values fall back to the default.
|
|
358
|
-
const pageSize = Number.isFinite(pageSizeProp)
|
|
359
|
-
? Math.max(1, Math.floor(pageSizeProp))
|
|
360
|
-
: 10;
|
|
361
|
-
|
|
362
354
|
// Track the page optimistically so rapid prev/next clicks advance from the
|
|
363
355
|
// in-flight target instead of stalling on the last committed page.
|
|
364
356
|
const [optimisticPage, setOptimisticPage] = useOptimistic(page);
|
|
@@ -46,7 +46,7 @@ export const docs = {
|
|
|
46
46
|
props: {gap: 1},
|
|
47
47
|
children: [
|
|
48
48
|
{__element: 'XDSHeading', props: {level: 3}, children: 'Pro plan'},
|
|
49
|
-
{__element: 'XDSText', props: {type: 'body'}, children: '$29/month
|
|
49
|
+
{__element: 'XDSText', props: {type: 'body'}, children: '$29/month — unlimited projects and priority support.'},
|
|
50
50
|
],
|
|
51
51
|
},
|
|
52
52
|
},
|
|
@@ -467,60 +467,4 @@ describe('Selector', () => {
|
|
|
467
467
|
).toBeInTheDocument();
|
|
468
468
|
});
|
|
469
469
|
});
|
|
470
|
-
|
|
471
|
-
describe('keyboard accessibility', () => {
|
|
472
|
-
it('trigger is focusable via Tab when enabled', async () => {
|
|
473
|
-
const user = userEvent.setup();
|
|
474
|
-
render(<Selector label="Fruit" options={OPTIONS} />);
|
|
475
|
-
|
|
476
|
-
await user.tab();
|
|
477
|
-
expect(screen.getByRole('combobox')).toHaveFocus();
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
it('trigger is not focusable when disabled', () => {
|
|
481
|
-
render(<Selector label="Fruit" options={OPTIONS} isDisabled />);
|
|
482
|
-
expect(screen.getByRole('combobox')).toHaveAttribute('tabIndex', '-1');
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
it('opens the listbox with ArrowDown from a focused trigger', async () => {
|
|
486
|
-
const user = userEvent.setup();
|
|
487
|
-
render(<Selector label="Fruit" options={OPTIONS} />);
|
|
488
|
-
|
|
489
|
-
const trigger = screen.getByRole('combobox');
|
|
490
|
-
await user.tab();
|
|
491
|
-
expect(trigger).toHaveFocus();
|
|
492
|
-
|
|
493
|
-
await user.keyboard('{ArrowDown}');
|
|
494
|
-
expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
it('opens and selects an option with Enter (no mouse)', async () => {
|
|
498
|
-
const user = userEvent.setup();
|
|
499
|
-
const onChange = vi.fn();
|
|
500
|
-
render(
|
|
501
|
-
<Selector label="Fruit" options={OPTIONS} onChange={onChange} />,
|
|
502
|
-
);
|
|
503
|
-
|
|
504
|
-
await user.tab();
|
|
505
|
-
await user.keyboard('{Enter}'); // open
|
|
506
|
-
await user.keyboard('{ArrowDown}'); // move highlight
|
|
507
|
-
await user.keyboard('{Enter}'); // select
|
|
508
|
-
|
|
509
|
-
expect(onChange).toHaveBeenCalled();
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
it('clear button is reachable by keyboard', () => {
|
|
513
|
-
render(
|
|
514
|
-
<Selector
|
|
515
|
-
label="Fruit"
|
|
516
|
-
options={OPTIONS}
|
|
517
|
-
value="Apple"
|
|
518
|
-
onChange={() => {}}
|
|
519
|
-
hasClear
|
|
520
|
-
/>,
|
|
521
|
-
);
|
|
522
|
-
const clear = screen.getByRole('button', {name: 'Clear Fruit'});
|
|
523
|
-
expect(clear).not.toHaveAttribute('tabIndex', '-1');
|
|
524
|
-
});
|
|
525
|
-
});
|
|
526
470
|
});
|
|
@@ -908,7 +908,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
908
908
|
aria-busy={isBusy || undefined}
|
|
909
909
|
disabled={isDisabled}
|
|
910
910
|
onKeyDown={onKeyDown}
|
|
911
|
-
tabIndex={
|
|
911
|
+
tabIndex={-1}
|
|
912
912
|
{...stylex.props(styles.trigger)}>
|
|
913
913
|
<span {...stylex.props(styles.triggerLabel)}>
|
|
914
914
|
{selectedItem?.label ?? placeholder}
|
|
@@ -918,6 +918,7 @@ export function Selector<T extends SelectorOptionType>(
|
|
|
918
918
|
{hasClear && value != null && !isDisabled && (
|
|
919
919
|
<button
|
|
920
920
|
type="button"
|
|
921
|
+
tabIndex={-1}
|
|
921
922
|
onClick={handleClear}
|
|
922
923
|
aria-label={`Clear ${label}`}
|
|
923
924
|
{...stylex.props(styles.clearButton)}>
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* @param data - Full data array
|
|
17
17
|
* @param page - Current page number (1-based)
|
|
18
|
-
* @param pageSize - Number of items per page
|
|
19
|
-
* non-finite values fall back to 10
|
|
18
|
+
* @param pageSize - Number of items per page
|
|
20
19
|
* @returns Slice of data for the current page
|
|
21
20
|
*
|
|
22
21
|
* @example
|
|
@@ -32,12 +31,6 @@ export function paginateData<T>(
|
|
|
32
31
|
page: number,
|
|
33
32
|
pageSize: number,
|
|
34
33
|
): T[] {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// renders page buttons.
|
|
38
|
-
const size = Number.isFinite(pageSize)
|
|
39
|
-
? Math.max(1, Math.floor(pageSize))
|
|
40
|
-
: 10;
|
|
41
|
-
const start = (page - 1) * size;
|
|
42
|
-
return data.slice(start, start + size);
|
|
34
|
+
const start = (page - 1) * pageSize;
|
|
35
|
+
return data.slice(start, start + pageSize);
|
|
43
36
|
}
|
|
@@ -184,22 +184,6 @@ describe('useTablePagination', () => {
|
|
|
184
184
|
).toBeTruthy();
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
-
it('guards pageSize 0 against Infinity page counts', () => {
|
|
188
|
-
render(<PaginatedTable data={generateItems(5)} pageSize={0} />);
|
|
189
|
-
expect(
|
|
190
|
-
screen.getByRole('navigation', {name: 'Table pagination'}),
|
|
191
|
-
).toBeInTheDocument();
|
|
192
|
-
// pageSize is coerced to 1, so 5 items produce 5 pages, not Infinity,
|
|
193
|
-
// and page 1 shows the first item instead of an empty slice
|
|
194
|
-
expect(
|
|
195
|
-
screen.queryByRole('button', {name: 'Go to page Infinity'}),
|
|
196
|
-
).toBeNull();
|
|
197
|
-
expect(
|
|
198
|
-
screen.getByRole('button', {name: 'Go to page 5'}),
|
|
199
|
-
).toBeInTheDocument();
|
|
200
|
-
expect(screen.getByText('Item 1')).toBeInTheDocument();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
187
|
it('transformTableContext renders Pagination above table', () => {
|
|
204
188
|
render(
|
|
205
189
|
<PaginatedTable
|
|
@@ -199,7 +199,7 @@ export function useTablePagination<T extends Record<string, unknown>>(
|
|
|
199
199
|
totalItems,
|
|
200
200
|
totalPages: totalPagesProp,
|
|
201
201
|
hasMore,
|
|
202
|
-
pageSize
|
|
202
|
+
pageSize = 10,
|
|
203
203
|
onPageSizeChange,
|
|
204
204
|
pageSizeOptions,
|
|
205
205
|
variant = 'pages',
|
|
@@ -209,13 +209,6 @@ export function useTablePagination<T extends Record<string, unknown>>(
|
|
|
209
209
|
label = 'Table pagination',
|
|
210
210
|
} = config;
|
|
211
211
|
|
|
212
|
-
// Same guard as Pagination itself: 0/NaN/negative pageSize would produce an
|
|
213
|
-
// Infinity/NaN totalPages here, which bypasses Pagination's own coercion
|
|
214
|
-
// because it is passed down as the explicit totalPages prop.
|
|
215
|
-
const pageSize = Number.isFinite(pageSizeConfig)
|
|
216
|
-
? Math.max(1, Math.floor(pageSizeConfig))
|
|
217
|
-
: 10;
|
|
218
|
-
|
|
219
212
|
const computedTotalPages =
|
|
220
213
|
totalPagesProp ??
|
|
221
214
|
(totalItems != null ? Math.ceil(totalItems / pageSize) : undefined);
|
package/src/utils/index.ts
CHANGED
|
@@ -68,13 +68,6 @@ export {getKey, type Key, type KeyFallback} from './getKey';
|
|
|
68
68
|
|
|
69
69
|
export {mergeProps} from './mergeProps';
|
|
70
70
|
export {mergeRefs} from './mergeRefs';
|
|
71
|
-
export {themeProps, themeDataAttributes} from './themeProps';
|
|
72
|
-
export type {
|
|
73
|
-
ClassValue,
|
|
74
|
-
ClassProps,
|
|
75
|
-
ThemeDataAttributes,
|
|
76
|
-
ThemeProps,
|
|
77
|
-
} from './themeProps';
|
|
78
71
|
export {groupItems, getItemGroup} from './groupItems';
|
|
79
72
|
export type {ItemGroup} from './groupItems';
|
|
80
73
|
export {observeResize, unobserveResize} from './sharedResizeObserver';
|