@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.
Files changed (44) hide show
  1. package/dist/Field/FieldLabel.d.ts.map +1 -1
  2. package/dist/Field/FieldLabel.js +5 -5
  3. package/dist/Link/Link.d.ts.map +1 -1
  4. package/dist/Link/Link.js +3 -3
  5. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  6. package/dist/MultiSelector/MultiSelector.js +2 -1
  7. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  8. package/dist/NavMenu/NavHeadingMenu.js +2 -18
  9. package/dist/Pagination/Pagination.d.ts +1 -1
  10. package/dist/Pagination/Pagination.d.ts.map +1 -1
  11. package/dist/Pagination/Pagination.js +1 -7
  12. package/dist/Selector/Selector.d.ts.map +1 -1
  13. package/dist/Selector/Selector.js +2 -1
  14. package/dist/Table/plugins/pagination/paginateData.d.ts +1 -2
  15. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  16. package/dist/Table/plugins/pagination/paginateData.js +3 -8
  17. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  18. package/dist/Table/plugins/pagination/useTablePagination.js +1 -6
  19. package/dist/astryx.css +0 -3
  20. package/dist/astryx.umd.js +34 -34
  21. package/dist/astryx.umd.js.map +4 -4
  22. package/dist/utils/index.d.ts +0 -2
  23. package/dist/utils/index.d.ts.map +1 -1
  24. package/dist/utils/index.js +0 -1
  25. package/package.json +5 -6
  26. package/src/Calendar/Calendar.doc.mjs +1 -1
  27. package/src/Field/FieldLabel.tsx +0 -2
  28. package/src/Layer/useLayer.doc.mjs +0 -5
  29. package/src/Link/Link.tsx +3 -18
  30. package/src/MetadataList/MetadataListItem.doc.mjs +0 -4
  31. package/src/MultiSelector/MultiSelector.test.tsx +0 -60
  32. package/src/MultiSelector/MultiSelector.tsx +2 -1
  33. package/src/NavMenu/NavHeadingMenu.test.tsx +0 -45
  34. package/src/NavMenu/NavHeadingMenu.tsx +2 -21
  35. package/src/Pagination/Pagination.doc.mjs +3 -3
  36. package/src/Pagination/Pagination.test.tsx +0 -59
  37. package/src/Pagination/Pagination.tsx +1 -9
  38. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  39. package/src/Selector/Selector.test.tsx +0 -56
  40. package/src/Selector/Selector.tsx +2 -1
  41. package/src/Table/plugins/pagination/paginateData.ts +3 -10
  42. package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -16
  43. package/src/Table/plugins/pagination/useTablePagination.tsx +1 -8
  44. package/src/utils/index.ts +0 -7
@@ -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,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAC7D,YAAY,EACV,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,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"}
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"}
@@ -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.eeff948",
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": "rimraf dist && pnpm build:esm && tsc --project tsconfig.build.json && pnpm build:css && pnpm build:umd",
643
- "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",
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 \"**/*.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css*\" --config-file ./babel.config.json",
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' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'", description: '每周起始日。可为数字(0=周日)或三字母星期缩写(如 "mon")。', default: '0'},
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: [
@@ -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={isDisabled ? -1 : 0}
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, {useCallback, useMemo, type ReactNode} from '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={listKeyDown}
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. Coerced to a positive integer; non-finite values fall back to the default.',
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; coerced to positive integer, non-finite falls back to default',
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: pageSizeProp = 10,
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, unlimited projects and priority support.'},
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={isDisabled ? -1 : 0}
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; coerced to a positive integer,
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
- // Same coercion as Pagination and useTablePagination: a 0/NaN/negative
36
- // pageSize would slice every page empty while the pagination chrome still
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: pageSizeConfig = 10,
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);
@@ -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';