Skip to content

Conversation

LFDanLu
Copy link
Member

@LFDanLu LFDanLu commented Oct 13, 2025

Closes

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

🧢 Your Project:

@rspbot
Copy link

rspbot commented Oct 13, 2025

Comment on lines +17 to +26
// TODO: had to export these for the docs, but not sure why I didn't have to do
// so for the v3 docs?
export {ComboBoxTester} from './combobox';
export {GridListTester} from './gridlist';
export {ListBoxTester} from './listbox';
export {MenuTester} from './menu';
export {SelectTester} from './select';
export {TableTester} from './table';
export {TabsTester} from './tabs';
export {TreeTester} from './tree';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why but something like import comboboxUtils from 'docs:@react-aria/test-utils/src/combobox.ts'; works in the old RAC docs but I had to do the above to get access to the tester types in the new docs

Comment on lines +139 to +140
TODO can't place this next to the header here
<VersionBadge version="beta" />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to investigate why, but it seems to break here, probably since it is trying to create an anchor id on a header that has non string content

Comment on lines +185 to +187
### Patterns

<ExampleSwitcher type={null} examples={['ComboBox', 'GridList', 'ListBox', 'Menu', 'Select', 'Table', 'Tabs', 'Tree']}>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In an effort to cut down on bloating the component specific pages, I've tried including the sample code and associated class interface docs in an example switcher. Probably won't scale the best when we add more patterns but open to opinions here

@rspbot
Copy link

rspbot commented Oct 13, 2025

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pared down the content in the page a bit compared to whats on the old docs, feel free to comment on if it feels like too much or not. I kinda wanna add a FAQ/tips part in the test utils section to address some of the common questions that I've seen in the past (what to do if a certain interaction doesn't work, how to handle flows that aren't 100% covered, etc) but it might be too much...

I also remember we discussed in the past that we might like to have one page since these testing/test util practices aren't necessarily specific to a certain implementation. If that is the case, I can flesh this out with some of the RSP/S2 specific points and introduce the FAQ I mentioned above but for now I'll add the S2 equivalent page as well

@rspbot
Copy link

rspbot commented Oct 13, 2025

@rspbot
Copy link

rspbot commented Oct 13, 2025

## API Changes

@react-aria/test-utils

/@react-aria/test-utils:ComboBoxTester

+ComboBoxTester {
+  close: () => Promise<void>
+  combobox: HTMLElement
+  constructor: (ComboBoxTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  focusedOption: HTMLElement | null
+  listbox: HTMLElement | null
+  open: (ComboBoxOpenOpts) => Promise<void>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (ComboBoxSelectOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-aria/test-utils:GridListTester

+GridListTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  constructor: (GridListTesterOpts) => void
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  gridlist: HTMLElement
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleRowSelection: (GridListToggleRowOpts) => Promise<void>
+  triggerRowAction: (GridListRowActionOpts) => Promise<void>
+}

/@react-aria/test-utils:ListBoxTester

+ListBoxTester {
+  constructor: (ListBoxTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  listbox: HTMLElement
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectedOptions: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleOptionSelection: (ListBoxToggleOptionOpts) => Promise<void>
+  triggerOptionAction: (ListBoxOptionActionOpts) => Promise<void>
+}

/@react-aria/test-utils:MenuTester

+MenuTester {
+  close: () => Promise<void>
+  constructor: (MenuTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  menu: HTMLElement | null
+  open: (MenuOpenOpts) => Promise<void>
+  openSubmenu: (MenuOpenSubmenuOpts) => Promise<MenuTester | null>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (MenuSelectOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  submenuTriggers: Array<HTMLElement>
+  trigger: HTMLElement
+}

/@react-aria/test-utils:SelectTester

+SelectTester {
+  close: () => Promise<void>
+  constructor: (SelectTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  listbox: HTMLElement | null
+  open: (SelectOpenOpts) => Promise<void>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (SelectTriggerOptionOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-aria/test-utils:TableTester

+TableTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  columns: Array<HTMLElement>
+  constructor: (TableTesterOpts) => void
+  findCell: ({
+    text: string
+}) => HTMLElement
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  rowGroups: Array<HTMLElement>
+  rowHeaders: Array<HTMLElement>
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  table: HTMLElement
+  toggleRowSelection: (TableToggleRowOpts) => Promise<void>
+  toggleSelectAll: ({
+    interactionType?: UserOpts['interactionType']
+}) => Promise<void>
+  toggleSort: (TableToggleSortOpts) => Promise<void>
+  triggerColumnHeaderAction: (TableColumnHeaderActionOpts) => Promise<void>
+  triggerRowAction: (TableRowActionOpts) => Promise<void>
+}

/@react-aria/test-utils:TabsTester

+TabsTester {
+  activeTabpanel: HTMLElement | null
+  constructor: (TabsTesterOpts) => void
+  findTab: ({
+    tabIndexOrText: number | string
+}) => HTMLElement
+  selectedTab: HTMLElement | null
+  setInteractionType: (UserOpts['interactionType']) => void
+  tablist: HTMLElement
+  tabpanels: Array<HTMLElement>
+  tabs: Array<HTMLElement>
+  triggerTab: (TriggerTabOptions) => Promise<void>
+}

/@react-aria/test-utils:TreeTester

+TreeTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  constructor: (TreeTesterOpts) => void
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleRowExpansion: (TreeToggleExpansionOpts) => Promise<void>
+  toggleRowSelection: (TreeToggleRowOpts) => Promise<void>
+  tree: HTMLElement
+  triggerRowAction: (TreeRowActionOpts) => Promise<void>
+}

@react-spectrum/test-utils

/@react-spectrum/test-utils:ComboBoxTester

+ComboBoxTester {
+  close: () => Promise<void>
+  combobox: HTMLElement
+  constructor: (ComboBoxTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  focusedOption: HTMLElement | null
+  listbox: HTMLElement | null
+  open: (ComboBoxOpenOpts) => Promise<void>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (ComboBoxSelectOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-spectrum/test-utils:GridListTester

+GridListTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  constructor: (GridListTesterOpts) => void
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  gridlist: HTMLElement
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleRowSelection: (GridListToggleRowOpts) => Promise<void>
+  triggerRowAction: (GridListRowActionOpts) => Promise<void>
+}

/@react-spectrum/test-utils:ListBoxTester

+ListBoxTester {
+  constructor: (ListBoxTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  listbox: HTMLElement
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectedOptions: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleOptionSelection: (ListBoxToggleOptionOpts) => Promise<void>
+  triggerOptionAction: (ListBoxOptionActionOpts) => Promise<void>
+}

/@react-spectrum/test-utils:MenuTester

+MenuTester {
+  close: () => Promise<void>
+  constructor: (MenuTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  menu: HTMLElement | null
+  open: (MenuOpenOpts) => Promise<void>
+  openSubmenu: (MenuOpenSubmenuOpts) => Promise<MenuTester | null>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (MenuSelectOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  submenuTriggers: Array<HTMLElement>
+  trigger: HTMLElement
+}

/@react-spectrum/test-utils:SelectTester

+SelectTester {
+  close: () => Promise<void>
+  constructor: (SelectTesterOpts) => void
+  findOption: ({
+    optionIndexOrText: number | string
+}) => HTMLElement
+  listbox: HTMLElement | null
+  open: (SelectOpenOpts) => Promise<void>
+  options: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  sections: Array<HTMLElement>
+  selectOption: (SelectTriggerOptionOpts) => Promise<void>
+  setInteractionType: (UserOpts['interactionType']) => void
+  trigger: HTMLElement
+}

/@react-spectrum/test-utils:TableTester

+TableTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  columns: Array<HTMLElement>
+  constructor: (TableTesterOpts) => void
+  findCell: ({
+    text: string
+}) => HTMLElement
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  rowGroups: Array<HTMLElement>
+  rowHeaders: Array<HTMLElement>
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  table: HTMLElement
+  toggleRowSelection: (TableToggleRowOpts) => Promise<void>
+  toggleSelectAll: ({
+    interactionType?: UserOpts['interactionType']
+}) => Promise<void>
+  toggleSort: (TableToggleSortOpts) => Promise<void>
+  triggerColumnHeaderAction: (TableColumnHeaderActionOpts) => Promise<void>
+  triggerRowAction: (TableRowActionOpts) => Promise<void>
+}

/@react-spectrum/test-utils:TabsTester

+TabsTester {
+  activeTabpanel: HTMLElement | null
+  constructor: (TabsTesterOpts) => void
+  findTab: ({
+    tabIndexOrText: number | string
+}) => HTMLElement
+  selectedTab: HTMLElement | null
+  setInteractionType: (UserOpts['interactionType']) => void
+  tablist: HTMLElement
+  tabpanels: Array<HTMLElement>
+  tabs: Array<HTMLElement>
+  triggerTab: (TriggerTabOptions) => Promise<void>
+}

/@react-spectrum/test-utils:TreeTester

+TreeTester {
+  cells: ({
+    element?: HTMLElement
+}) => Array<HTMLElement>
+  constructor: (TreeTesterOpts) => void
+  findRow: ({
+    rowIndexOrText: number | string
+}) => HTMLElement
+  rows: Array<HTMLElement>
+  selectedRows: Array<HTMLElement>
+  setInteractionType: (UserOpts['interactionType']) => void
+  toggleRowExpansion: (TreeToggleExpansionOpts) => Promise<void>
+  toggleRowSelection: (TreeToggleRowOpts) => Promise<void>
+  tree: HTMLElement
+  triggerRowAction: (TreeRowActionOpts) => Promise<void>
+}

userEvent.click(document.activeElement);
```

## Test setup and common gotchas
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically this section is what differentiates this page from the aria one, as well as the lack of listbox/listview/etc examples since they don't have S2 variants yet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants