diff --git a/packages/@react-aria/test-utils/src/index.ts b/packages/@react-aria/test-utils/src/index.ts index b5b7da34492..7ad297d0245 100644 --- a/packages/@react-aria/test-utils/src/index.ts +++ b/packages/@react-aria/test-utils/src/index.ts @@ -14,5 +14,16 @@ export {triggerLongPress} from './events'; export {installMouseEvent, installPointerEvent} from './testSetup'; export {pointerMap} from './userEventMaps'; export {User} from './user'; +export type {CheckboxGroupTester} from './checkboxgroup'; +export type {ComboBoxTester} from './combobox'; +export type {DialogTester} from './dialog'; +export type {GridListTester} from './gridlist'; +export type {ListBoxTester} from './listbox'; +export type {MenuTester} from './menu'; +export type {RadioGroupTester} from './radiogroup'; +export type {SelectTester} from './select'; +export type {TableTester} from './table'; +export type {TabsTester} from './tabs'; +export type {TreeTester} from './tree'; export type {UserOpts} from './types'; diff --git a/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx b/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx index 67dd8eba6b7..91e27b8197e 100644 --- a/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx +++ b/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx @@ -11,8 +11,9 @@ import {Layout} from '@react-spectrum/docs'; export default Layout; import docs from 'docs:@react-spectrum/checkbox'; +import checkboxgroupUtil from 'docs:@react-aria/test-utils/src/checkboxgroup.ts'; import packageData from '@react-spectrum/checkbox/package.json'; -import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs'; +import {HeaderInfo, PropTable, PageDescription, VersionBadge, ClassAPI} from '@react-spectrum/docs'; ```jsx import import {Checkbox, CheckboxGroup} from '@react-spectrum/checkbox'; @@ -332,3 +333,43 @@ See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/ Basketball ``` + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the checkbox group tester and a sample of how you could use it in your test suite. + +```ts +// CheckboxGroup.test.ts +import {render} from '@testing-library/react'; +import {theme} from '@react-spectrum/theme-default'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + + ... + + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + + diff --git a/packages/@react-spectrum/combobox/docs/ComboBox.mdx b/packages/@react-spectrum/combobox/docs/ComboBox.mdx index 81ccd82d6a4..ff582b53d73 100644 --- a/packages/@react-spectrum/combobox/docs/ComboBox.mdx +++ b/packages/@react-spectrum/combobox/docs/ComboBox.mdx @@ -996,7 +996,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the combobox tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/dialog/docs/Dialog.mdx b/packages/@react-spectrum/dialog/docs/Dialog.mdx index 6bfd237b82e..3353600dec6 100644 --- a/packages/@react-spectrum/dialog/docs/Dialog.mdx +++ b/packages/@react-spectrum/dialog/docs/Dialog.mdx @@ -12,7 +12,8 @@ export default Layout; import DialogAnatomy from './images/DialogAnatomy.svg'; import docs from 'docs:@react-spectrum/dialog'; -import {Image, HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs'; +import dialogUtil from 'docs:@react-aria/test-utils/src/dialog.ts'; +import {Image, HeaderInfo, PropTable, PageDescription, VersionBadge, ClassAPI} from '@react-spectrum/docs'; import packageData from '@react-spectrum/dialog/package.json'; import styles from '@react-spectrum/docs/src/docs.css'; @@ -398,3 +399,43 @@ respectively for container sizing considerations. Modal sizes on mobile devices )} ``` + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common dialog interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the dialog tester and a sample of how you could use it in your test suite. + +```ts +// Dialog.test.ts +import {render} from '@testing-library/react'; +import {theme} from '@react-spectrum/theme-default'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Dialog can be opened and closed', async function () { + // Render your test component/app and initialize the dialog tester + let {getByTestId, getByRole} = render( + + + Trigger + + ... + + + + ); + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toBeVisible(); + await dialogTester.close(); + expect(dialog).not.toBeInTheDocument(); +}); +``` + + diff --git a/packages/@react-spectrum/list/docs/ListView.mdx b/packages/@react-spectrum/list/docs/ListView.mdx index ae791b43f22..2c64058f70e 100644 --- a/packages/@react-spectrum/list/docs/ListView.mdx +++ b/packages/@react-spectrum/list/docs/ListView.mdx @@ -1195,7 +1195,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common gridlist interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common gridlist interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the gridlist tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/listbox/docs/ListBox.mdx b/packages/@react-spectrum/listbox/docs/ListBox.mdx index 336386f4572..2ef9cf0a17b 100644 --- a/packages/@react-spectrum/listbox/docs/ListBox.mdx +++ b/packages/@react-spectrum/listbox/docs/ListBox.mdx @@ -413,7 +413,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common listbox interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common listbox interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the listbox tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/menu/docs/MenuTrigger.mdx b/packages/@react-spectrum/menu/docs/MenuTrigger.mdx index 251bddaec2f..b3b72eb44c8 100644 --- a/packages/@react-spectrum/menu/docs/MenuTrigger.mdx +++ b/packages/@react-spectrum/menu/docs/MenuTrigger.mdx @@ -260,7 +260,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the menu tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/picker/docs/Picker.mdx b/packages/@react-spectrum/picker/docs/Picker.mdx index 6c327e3166b..d1eb7f56fc5 100644 --- a/packages/@react-spectrum/picker/docs/Picker.mdx +++ b/packages/@react-spectrum/picker/docs/Picker.mdx @@ -592,7 +592,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common select interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common select interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the select tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/radio/docs/RadioGroup.mdx b/packages/@react-spectrum/radio/docs/RadioGroup.mdx index 050672f1517..e82b7c5d428 100644 --- a/packages/@react-spectrum/radio/docs/RadioGroup.mdx +++ b/packages/@react-spectrum/radio/docs/RadioGroup.mdx @@ -11,8 +11,9 @@ import {Layout} from '@react-spectrum/docs'; export default Layout; import docs from 'docs:@react-spectrum/radio'; +import radiogroupUtil from 'docs:@react-aria/test-utils/src/radiogroup.ts'; import packageData from '@react-spectrum/radio/package.json'; -import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs'; +import {HeaderInfo, PropTable, PageDescription, VersionBadge, ClassAPI} from '@react-spectrum/docs'; ```jsx import import {Radio, RadioGroup} from '@react-spectrum/radio'; @@ -306,3 +307,43 @@ See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/ Dragon ``` + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the radio group tester and a sample of how you could use it in your test suite. + +```ts +// RadioGroup.test.ts +import {render} from '@testing-library/react'; +import {theme} from '@react-spectrum/theme-default'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + + ... + + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + + diff --git a/packages/@react-spectrum/s2/style/index.ts b/packages/@react-spectrum/s2/style/index.ts index 33c4aa925b3..7ca5b24f0b4 100644 --- a/packages/@react-spectrum/s2/style/index.ts +++ b/packages/@react-spectrum/s2/style/index.ts @@ -77,7 +77,7 @@ const iconSizes = { export function iconStyle(this: MacroContext | void, options: IconStyle): StyleString> { let {size = 'M', color, ...styles} = options; - + if (color) { styles['--iconPrimary'] = { type: 'fill', diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index ae4e9909584..1740998ce5e 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -1964,7 +1964,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common table interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common table interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the table tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/tabs/docs/Tabs.mdx b/packages/@react-spectrum/tabs/docs/Tabs.mdx index 3c81dd2e12e..74eb54adbf2 100644 --- a/packages/@react-spectrum/tabs/docs/Tabs.mdx +++ b/packages/@react-spectrum/tabs/docs/Tabs.mdx @@ -639,7 +639,7 @@ Tabs features automatic tab collapse behavior and may need specific mocks to tes [React Spectrum's test suite](https://github.com/adobe/react-spectrum/blob/326f48154e301edab425c8198c5c3af72422462b/packages/%40react-spectrum/tabs/test/Tabs.test.js#L58-L62) if you run into any issues with your tests. -`@react-spectrum/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the tabs tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/tree/docs/TreeView.mdx b/packages/@react-spectrum/tree/docs/TreeView.mdx index 4dc29a886d7..7d2915fe3ea 100644 --- a/packages/@react-spectrum/tree/docs/TreeView.mdx +++ b/packages/@react-spectrum/tree/docs/TreeView.mdx @@ -533,7 +533,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the tree tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/dev/docs/pages/react-aria/testing.mdx b/packages/dev/docs/pages/react-aria/testing.mdx index aab35a898ec..c649d45ea4d 100644 --- a/packages/dev/docs/pages/react-aria/testing.mdx +++ b/packages/dev/docs/pages/react-aria/testing.mdx @@ -201,14 +201,20 @@ See below for the full definition of the `User` object. Below is a list of the ARIA patterns testers currently supported by `createTester`. See the accompanying component testing docs pages for a sample of how to use the testers in your test suite. +- [CheckboxGroup](CheckboxGroup.html#test-utils) + - [ComboBox](ComboBox.html#test-utils) +- [Dialog](Dialog.html#test-utils) + - [GridList](GridList.html#test-utils) - [ListBox](ListBox.html#test-utils) - [Menu](Menu.html#test-utils) +- [RadioGroup](RadioGroup.html#test-utils) + - [Select](Select.html#test-utils) - [Table](Table.html#test-utils) diff --git a/packages/dev/docs/pages/react-spectrum/testing.mdx b/packages/dev/docs/pages/react-spectrum/testing.mdx index 99c179e3d61..90dd1e8050d 100644 --- a/packages/dev/docs/pages/react-spectrum/testing.mdx +++ b/packages/dev/docs/pages/react-spectrum/testing.mdx @@ -395,14 +395,20 @@ See below for the full definition of the `User` object. Below is a list of the ARIA patterns testers currently supported by `createTester`. See the accompanying component testing docs pages for a sample of how to use the testers in your test suite. +- [CheckboxGroup](CheckboxGroup.html#test-utils) + - [ComboBox](ComboBox.html#test-utils) +- [Dialog](Dialog.html#test-utils) + - [ListView](ListView.html#test-utils) - [ListBox](ListBox.html#test-utils) - [MenuTrigger](MenuTrigger.html#test-utils) +- [RadioGroup](RadioGroup.html#test-utils) + - [Picker](Picker.html#test-utils) - [TableView](TableView.html#test-utils) diff --git a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx index 95c6e3260b5..2c6d39d70c0 100644 --- a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx @@ -5,8 +5,12 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/CheckboxGroup'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/checkbox/docs/checkboxgroup-anatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['input']; +export const relatedPages = [ + {title: 'Testing', url: './CheckboxGroup/testing.html'} +]; # CheckboxGroup diff --git a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx new file mode 100644 index 00000000000..2dc6269b891 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'checkboxgroup', 'test-utils']; +export const description = 'Testing CheckboxGroup with React Aria test utils'; + +# Testing CheckboxGroup + +## Test utils + +`@react-aria/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `CheckboxGroup` tester in your test cases. This gives you access to `CheckboxGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `CheckboxGroup` tester, use it to simulate checkbox selection, and verify the checkbox group's state after each interaction. + +```ts +// CheckboxGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + +See below for the full definition of the `User` and the `CheckboxGroup` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx b/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx index 0efd57fda82..6fabb1fa95f 100644 --- a/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx @@ -6,8 +6,12 @@ import {ComboBox as VanillaComboBox, ComboBoxItem} from 'vanilla-starter/ComboBo import vanillaDocs from 'docs:vanilla-starter/ComboBox'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/ComboBoxAnatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['autocomplete', 'search', 'typeahead', 'input']; +export const relatedPages = [ + {title: 'Testing', url: './ComboBox/testing.html'} +]; # ComboBox diff --git a/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx b/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx new file mode 100644 index 00000000000..bd41496956f --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx @@ -0,0 +1,65 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'combobox', 'test-utils']; +export const description = 'Testing ComboBox with React Aria test utils'; + +# Testing ComboBox + +## Test utils + +`@react-aria/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ComboBox` tester in your test cases. This gives you access to `ComboBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ComboBox` tester, use it to simulate option selection, and verify the combobox's state after each interaction. + +```ts +// Combobox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ComboBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the combobox tester + let {getByTestId} = render( + + ... + + ); + let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); + + await comboboxTester.open(); + expect(comboboxTester.listbox).toBeInTheDocument(); + + let options = comboboxTester.options(); + await comboboxTester.selectOption({option: options[0]}); + expect(comboboxTester.combobox.value).toBe('One'); + expect(comboboxTester.listbox).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `ComboBox` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/GridList.mdx b/packages/dev/s2-docs/pages/react-aria/GridList.mdx index 5f86d9bf1dd..46b04a7bb9a 100644 --- a/packages/dev/s2-docs/pages/react-aria/GridList.mdx +++ b/packages/dev/s2-docs/pages/react-aria/GridList.mdx @@ -6,8 +6,12 @@ import {GridList as VanillaGridList, GridListItem} from 'vanilla-starter/GridLis import vanillaDocs from 'docs:vanilla-starter/GridList'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/GridListAnatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['list view']; +export const relatedPages = [ + {title: 'Testing', url: './GridList/testing.html'} +]; # GridList diff --git a/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx b/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx new file mode 100644 index 00000000000..f90f7f4a8cb --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx @@ -0,0 +1,79 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'gridlist', 'test-utils']; +export const description = 'Testing GridList with React Aria test utils'; + +# Testing GridList + +## General setup + +GridList features long press interactions on its items depending on the item actions provided and if the user is interacting with the gridlist on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common gridlist interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `GridList` tester in your test cases. This gives you access to `GridList` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `GridList` tester, use it to simulate item selection, and verify the gridlist's state after each interaction. + +```ts +// GridList.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('GridList can select a row via keyboard', async function () { + // Render your test component/app and initialize the gridlist tester + let {getByTestId} = render( + + ... + + ); + let gridListTester = testUtilUser.createTester('GridList', {root: getByTestId('test-gridlist'), interactionType: 'keyboard'}); + + let row = gridListTester.rows[0]; + expect(within(row).getByRole('checkbox')).not.toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(0); + + await gridListTester.toggleRowSelection({row: 0}); + expect(within(row).getByRole('checkbox')).toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(1); + + await gridListTester.toggleRowSelection({row: 0}); + expect(within(row).getByRole('checkbox')).not.toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `GridList` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/ListBox.mdx b/packages/dev/s2-docs/pages/react-aria/ListBox.mdx index 93fbcd78ccf..5c8cc4e3109 100644 --- a/packages/dev/s2-docs/pages/react-aria/ListBox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ListBox.mdx @@ -9,6 +9,9 @@ import Anatomy from 'react-aria-components/docs/ListBoxAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['options']; +export const relatedPages = [ + {title: 'Testing', url: './ListBox/testing.html'} +]; # ListBox diff --git a/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx b/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx new file mode 100644 index 00000000000..93e0f3c4cbe --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx @@ -0,0 +1,70 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'listbox', 'test-utils']; +export const description = 'Testing ListBox with React Aria test utils'; + +# Testing ListBox + +## General setup + +ListBox features long press interactions on its options depending on the option actions provided and if the user is interacting with the listbox on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common listbox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ListBox` tester in your test cases. This gives you access to `ListBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ListBox` tester, use it to simulate option selection, and verify the listbox's state after each interaction. + +```ts +// ListBox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ListBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let listboxTester = testUtilUser.createTester('ListBox', {root: getByTestId('test-listbox'), interactionType: 'keyboard'}); + + await listboxTester.toggleOptionSelection({option: 4}); + expect(listboxTester.options()[4]).toHaveAttribute('aria-selected', 'true'); +}); +``` + +See below for the full definition of the `User` and the `ListBox` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Menu.mdx b/packages/dev/s2-docs/pages/react-aria/Menu.mdx index d20963fb7a2..8489a45d7d3 100644 --- a/packages/dev/s2-docs/pages/react-aria/Menu.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Menu.mdx @@ -7,6 +7,9 @@ import Anatomy from 'react-aria-components/docs/MenuAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['dropdown']; +export const relatedPages = [ + {title: 'Testing', url: './Menu/testing.html'} +]; # Menu diff --git a/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx new file mode 100644 index 00000000000..b1ac772f170 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx @@ -0,0 +1,80 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'menu', 'test-utils']; +export const description = 'Testing Menu with React Aria test utils'; + +# Testing Menu + +## General setup + +Menu features long press interactions depending on the actions provided and if the user is interacting with the menu on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Menu` tester in your test cases. This gives you access to `Menu` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Menu` tester, use it to find and open a submenu, and verify the menu's state after each interaction. + +```ts +// Menu.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Menu can open its submenu via keyboard', async function () { + // Render your test component/app and initialize the menu tester + let {getByTestId} = render( + + + ... + + ); + let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); + + await menuTester.open(); + expect(menuTester.menu).toBeInTheDocument(); + let submenuTriggers = menuTester.submenuTriggers; + expect(submenuTriggers).toHaveLength(1); + + let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); + expect(submenuTester.menu).toBeInTheDocument(); + + await submenuTester.selectOption({option: submenuTester.options()[0]}); + expect(submenuTester.menu).not.toBeInTheDocument(); + expect(menuTester.menu).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Menu` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx b/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx index c412c6dd29a..2750f2cebe3 100644 --- a/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx @@ -5,8 +5,12 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/RadioGroup'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/radio/docs/anatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['input']; +export const relatedPages = [ + {title: 'Testing', url: './RadioGroup/testing.html'} +]; # RadioGroup diff --git a/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx b/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx new file mode 100644 index 00000000000..ea61d6933a4 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'radiogroup', 'test-utils']; +export const description = 'Testing RadioGroup with React Aria test utils'; + +# Testing RadioGroup + +## Test utils + +`@react-aria/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `RadioGroup` tester in your test cases. This gives you access to `RadioGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `RadioGroup` tester, use it to change radio selection, and verify the radio group's state after each interaction. + +```ts +// RadioGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + +See below for the full definition of the `User` and the `RadioGroup` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Select.mdx b/packages/dev/s2-docs/pages/react-aria/Select.mdx index 1f60d9f4d2f..d784307b475 100644 --- a/packages/dev/s2-docs/pages/react-aria/Select.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Select.mdx @@ -5,8 +5,12 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/Select'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/SelectAnatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['picker', 'dropdown', 'menu', 'input']; +export const relatedPages = [ + {title: 'Testing', url: './Select/testing.html'} +]; # Select @@ -242,7 +246,7 @@ function SelectWithTagGroup() { {/*- end highlight -*/} - + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Select` tester in your test cases. This gives you access to `Select` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Select` tester, use it to simulate option selection, and verify the select's state after each interaction. + +```ts +// Select.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Select can select an option via keyboard', async function () { + // Render your test component/app and initialize the select tester + let {getByTestId} = render( + + ); + let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); + let trigger = selectTester.trigger; + expect(trigger).toHaveTextContent('Select an item'); + + await selectTester.selectOption({option: 'Cat'}); + expect(trigger).toHaveTextContent('Cat'); +}); +``` + +See below for the full definition of the `User` and the `Select` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Table.mdx b/packages/dev/s2-docs/pages/react-aria/Table.mdx index c60bf737500..bffe5a8593a 100644 --- a/packages/dev/s2-docs/pages/react-aria/Table.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Table.mdx @@ -1,3 +1,4 @@ +import {InstallCommand} from '../../src/InstallCommand'; import {Layout} from '../../src/Layout'; export default Layout; @@ -5,9 +6,12 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/Table'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/TableAnatomy.svg'; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['data', 'grid']; +export const relatedPages = [ + {title: 'Testing', url: './Table/testing.html'} +]; # Table @@ -89,7 +93,7 @@ export const tags = ['data', 'grid']; ## Content -`Table` follows the [Collection Components API](collections.html?component=Table), accepting both static and dynamic collections. +`Table` follows the [Collection Components API](collections.html?component=Table), accepting both static and dynamic collections. In this example, both the columns and the rows are provided to the table via a render function, enabling the user to hide and show columns and add additional rows. ```tsx render diff --git a/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx new file mode 100644 index 00000000000..f3eb7623b9b --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx @@ -0,0 +1,83 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'table', 'test-utils']; +export const description = 'Testing Table with React Aria test utils'; + +# Testing Table + +## General setup + +Table features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common table interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Table` tester in your test cases. This gives you access to `Table` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Table` tester, use it to simulate row selection, and verify the table's state after each interaction. + +```ts +// Table.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Table can toggle row selection', async function () { + // Render your test component/app and initialize the table tester + let {getByTestId} = render( + + ... +
+ ); + let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); + expect(tableTester.selectedRows).toHaveLength(0); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + + await tableTester.toggleRowSelection({row: 2}); + expect(tableTester.selectedRows).toHaveLength(9); + let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + expect(checkbox).toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `Table` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx index 6ca184cd397..1e7bd64c08e 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx @@ -6,8 +6,12 @@ import {Tabs as VanillaTabs, TabsItem} from 'vanilla-starter/Tabs'; import vanillaDocs from 'docs:vanilla-starter/Tabs'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/tabs/docs/anatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['navigation']; +export const relatedPages = [ + {title: 'Testing', url: './Tabs/testing.html'} +]; # Tabs @@ -143,7 +147,7 @@ function Example() { return (
- + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tabs` tester in your test cases. This gives you access to `Tabs` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tabs` tester, use it to change tab selection, and verify the tabs' state after each interaction. + +```ts +// Tabs.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tabs can change selection via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); + + let tabs = tabsTester.tabs; + expect(tabsTester.selectedTab).toBe(tabs[0]); + + await tabsTester.triggerTab({tab: 1}); + expect(tabsTester.selectedTab).toBe(tabs[1]); +}); +``` + +See below for the full definition of the `User` and the `Tabs` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Tree.mdx b/packages/dev/s2-docs/pages/react-aria/Tree.mdx index dca4d8bb346..8e23f026743 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tree.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tree.mdx @@ -6,8 +6,12 @@ import {Tree as VanillaTree, TreeItem} from 'vanilla-starter/Tree'; import vanillaDocs from 'docs:vanilla-starter/Tree'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/TreeAnatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['data', 'tree', 'nested', 'hierarchy']; +export const relatedPages = [ + {title: 'Testing', url: './Tree/testing.html'} +]; # Tree diff --git a/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx new file mode 100644 index 00000000000..0bbd11cea7b --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx @@ -0,0 +1,82 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'tree', 'test-utils']; +export const description = 'Testing Tree with React Aria test utils'; + +# Testing Tree + +## General setup + +Tree features long press interactions on its rows depending on the row actions provided and if the user is interacting with the tree on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tree` tester in your test cases. This gives you access to `Tree` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tree` tester, use it to simulate row expansion and selection, and verify the tree's state after each interaction. + +```ts +// Tree.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tree can select and expand a item via keyboard', async function () { + // Render your test component/app and initialize the Tree tester + let {getByTestId} = render( + + ... + + ); + let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 1}); + expect(treeTester.selectedRows).toHaveLength(2); + expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); + + await treeTester.toggleRowExpansion({index: 0}); + expect(treeTester.rows[0]).toHaveAttribute('aria-expanded', 'true'); +}); +``` + +See below for the full definition of the `User` and the `Tree` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/styling.mdx b/packages/dev/s2-docs/pages/react-aria/styling.mdx index f880c30ca71..c804858de12 100644 --- a/packages/dev/s2-docs/pages/react-aria/styling.mdx +++ b/packages/dev/s2-docs/pages/react-aria/styling.mdx @@ -242,9 +242,28 @@ With this configured, all states for React Aria Components can be accessed with ``` +## Style macro + +If you want to build custom components that follow Spectrum design tokens and styling, you can use the [style macro](../s2/styling.html) from React Spectrum. The `style` macro is a build-time CSS generator that provides type safe access to Spectrum 2 design tokens including colors, spacing, sizing, and typography. + +```tsx +import {Checkbox} from 'react-aria-components'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + + +``` + ## Animation -React Aria Components supports both [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transitions/Using_CSS_transitions) and [keyframe animations](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes), and works with JavaScript animation libraries like [Framer Motion](https://www.framer.com/motion/). +React Aria Components supports both [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transitions/Using_CSS_transitions) and [keyframe animations](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes), and works with JavaScript animation libraries like [Motion](https://motion.dev/). ### CSS transitions diff --git a/packages/dev/s2-docs/pages/react-aria/testing.mdx b/packages/dev/s2-docs/pages/react-aria/testing.mdx new file mode 100644 index 00000000000..e128d757363 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/testing.mdx @@ -0,0 +1,244 @@ +import {VersionBadge} from '../../src/VersionBadge'; +import {InstallCommand} from '../../src/InstallCommand'; +import {Layout} from '../../src/Layout'; +export default Layout; + +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' + +export const section = 'Guides'; +export const description = 'Writing tests for apps built with React Aria'; + +# Testing + +This page describes how to test an application built with React Aria. It documents the available testing utilities available for each aria pattern and how they can be used to simulate common user interactions. + +## Testing semantics + +The recommended way to query for React Aria Components and their internals is by semantics. React Aria +Components implement [ARIA patterns](https://www.w3.org/TR/wai-aria-practices-1.2/). ARIA is a W3C standard +that specifies the semantics for many UI components. Unlike the DOM structure of the component, these semantics are much less likely to change over time, +making them ideal to query for. + +The main attribute to look for when querying is the [role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques). +This attribute represents the type of element a DOM node represents, e.g. a button, list option, or tab. + +### React Testing Library + +[React Testing Library](https://testing-library.com/docs/react-testing-library/intro) is useful because it +enforces that you write tests using semantics instead of implementation details. We use React Testing Library +to test React Aria itself, and it's quite easy to [query](https://testing-library.com/docs/dom-testing-library/api-queries) +elements by role, text, label, etc. + +```tsx +import {render} from '@testing-library/react'; + +let tree = render(); +let option = tree.getByRole('button'); +``` + +## Test ids + +Querying by semantics covers many scenarios, but what if you have many buttons on a page or its text changes due to translations based on locale? +In these cases, you may need a way to identify specific elements in tests, and that's where test ids come in. + +React Aria Components pass all [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) +through to their underlying DOM nodes, which allows you to use an attribute like `data-testid` to identify +a particular instance of a component. + +```tsx +import {render} from '@testing-library/react'; +import {Input, Label, TextField} from 'react-aria-components'; + +function LoginForm() { + return ( + <> + + + + + + + + + + ); +} + +let tree = render(); +let username = tree.getByTestId('username'); +let password = tree.getByTestId('password'); +``` + +## Triggering events + +React Aria Components rely on many different browser events to support different devices and platforms, so it's important to simulate +these correctly in your tests. For example, a click is really a `mousemove` and `mouseover` the target, followed +by `mousedown`, `focus`, and `mouseup` events, and finally a `click` event. + +The best way to handle this is with the [user-event](https://github.com/testing-library/user-event) library. +This lets you trigger high level interactions like a user would, and the library handles firing all of the individual +events that make up that interaction. + +```tsx +import {render} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +let tree = render(); + +// Click on the username field to focus it, and enter the value. +userEvent.click(tree.getByLabelText('Username')); +userEvent.type(document.activeElement, 'devon'); + +// Tab to the password field, and enter the value. +userEvent.tab(); +userEvent.type(document.activeElement, 'Pas$w0rd'); + +// Tab to the submit button and click it. +userEvent.tab(); +userEvent.click(document.activeElement); +``` + +## Test setup and common gotchas + +### Timers + +If you are using fake timers in your test suite, be aware that you may need to advance your timers after various interactions. We have `requestAnimationFrame` calls in various underlying hooks that you will need to also handle by advancing your timers in the tests. +This happens most prominently in our collection components after selection. In Jest, this can be handled by calling `act(() => jest.runAllTimers());` but you may require more precise control +depending on the other time-sensitive behavior you are testing. Please see [Jest's timer docs](https://jestjs.io/docs/timer-mocks) or the equivalent docs of your test frameworks for more information on how to do so. +It is also a good idea to run all timers to completion after each test case to avoid any left over transitions or timeouts that a component may have setup during its lifecycle. + +```tsx +afterEach(() => { + act(() => jest.runAllTimers()); +}); +``` + +Consider adding a `act(() => jest.runAllTimers());` after your simulated user interaction if you run into a test failure that looks like the following: + +``` +TestingLibraryElementError: Unable to find an accessible element with the role "listbox" +``` + +If you are using real timers instead, you can await a particular state of your app to be reached. If you are using React Testing Library, you can perform a `waitFor` query +to wait for a dialog to appear: + +```tsx +await waitFor(() => { + expect(getByRole('dialog')).toBeInTheDocument(); +}); +``` + +### Simulating user long press + +Some components like Menu support long press operations. Unfortunately, the approach of using the userEvent library to simulate a press event and running timers to hit the +long press internal timer threshold isn't sufficient due to `useLongPress`'s usage of `PointerEvent` and our own detection of `virtual` vs `mouse`/`touch` pointer types. Mock [PointerEvent](https://github.com/adobe/react-spectrum/blob/16ff0efac57eebeb1cd601ab376ce7c58a4e4efd/packages/dev/test-utils/src/events.ts#L70-L103) +globally and use `fireEvent` from `@testing-library/react` to properly simulate these long press events in your tests. +If you are using Jest, you can call our utility to automatically set up and tear down this mock in your test. +Additionally, if you are using fake timers and don't need to control the specific timings around the long press interaction, feel free to use our utility as shown below. + +```tsx +import {fireEvent} from '@testing-library/react'; +import {installPointerEvent, triggerLongPress} from '@react-aria/test-utils'; +installPointerEvent(); + +// In test case +let button = getByRole('button'); + +// With fireEvent and specific timing control +fireEvent.pointerDown(el, {pointerType: 'touch'}); +act(() => jest.advanceTimersByTime(800)); +fireEvent.up(el, {pointerType: 'touch'}); + +// With triggerLongPress +triggerLongPress(button); +``` + +### Simulating move event + +Components like ColorArea, ColorSlider, ColorWheel, and Slider each feature a draggable handle that a user can interact with to change the component's value. Similar to long press, the interactions offered by userEvent library aren't sufficient to trigger +the underlying event handlers governing these drag/move operations. [Mock MouseEvent globally](https://github.com/adobe/react-spectrum/blob/f40b575e38837e1aa7cabf0431406e81275d118a/packages/%40react-aria/test-utils/src/testSetup.ts#L16-L36) and `fireEvent` from `@testing-library/react` to simulate these drag/move events in your tests. +If you are using Jest, you can call our utility to automatically set up and tear down this mock in your test. Additionally, the track dimensions +for the draggable handle should be mocked so that the move operation calculations can be properly computed. + +```tsx +import {fireEvent} from '@testing-library/react'; +import {installMouseEvent} from '@react-aria/test-utils'; +installMouseEvent(); + +beforeAll(() => { + jest.spyOn(window.HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({top: 0, left: 0, width: 100, height: 10})); +}) + +// In test case +let sliderThumb = getByRole('slider').parentElement; + +// With fireEvent, move thumb from 0 to 50 +fireEvent.mouseDown(thumb, {clientX: 0, pageX: 0}); +fireEvent.mouseMove(thumb, {pageX: 50}); +fireEvent.mouseUp(thumb, {pageX: 50}); +``` + +## React Aria test utils + + +[@react-aria/test-utils](https://www.npmjs.com/package/@react-aria/test-utils) is a set of testing utilities that aims to make writing unit tests easier for consumers of React Aria +or for users who have built their own components following the respective ARIA pattern specification. + +### Installation + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + + +### Setup + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +specific ARIA pattern tester in your test cases. This gives you access to that pattern's specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +See [below](#patterns) for what patterns are currently supported. + +```ts +// YourTest.test.ts +import {screen} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +// Provide whatever method of advancing timers you use in your test, this example assumes Jest with fake timers. +// 'interactionType' specifies what mode of interaction should be simulated by the tester +// 'advanceTimer' is used by the tester to advance the timers in the tests for specific interactions (e.g. long press) +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('my test case', async function () { + // Render your test component/app + render(); + // Initialize the table tester via providing the 'Table' pattern name and the root element of said table + let table = testUtilUser.createTester('Table', {root: screen.getByTestId('test_table')}); + + // ... +}); +``` + +See below for the full definition of the `User` object. + + + + +### Patterns + +Below is a list of the ARIA patterns testers currently supported by `createTester`. See the accompanying component testing docs pages for a sample of how to use +the testers in your test suite. + +- [CheckboxGroup](./CheckboxGroup/testing.html) +- [ComboBox](./ComboBox/testing.html) +- [GridList](./GridList/testing.html) +- [ListBox](./ListBox/testing.html) +- [Menu](./Menu/testing.html) +- [RadioGroup](./RadioGroup/testing.html) +- [Select](./Select/testing.html) +- [Table](./Table/testing.html) +- [Tabs](./Tabs/testing.html) +- [Tree](./Tree/testing.html) diff --git a/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx b/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx index 7ecc1ad5288..a680f8ce2ab 100644 --- a/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx +++ b/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx @@ -1,10 +1,13 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {CheckboxGroup} from '@react-spectrum/s2'; +import {CheckboxGroup, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; export const tags = []; +export const relatedPages = [ + {title: 'Testing', url: './CheckboxGroup/testing.html'} +]; # CheckboxGroup diff --git a/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx b/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx new file mode 100644 index 00000000000..58d51ec077a --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'checkboxgroup', 'test-utils']; +export const description = 'Testing CheckboxGroup with React Spectrum test utils'; + +# Testing CheckboxGroup + +## Test utils + +`@react-spectrum/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `CheckboxGroup` tester in your test cases. This gives you access to `CheckboxGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `CheckboxGroup` tester, use it to simulate checkbox selection, and verify the checkbox group's state after each interaction. + +```ts +// CheckboxGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + +See below for the full definition of the `User` and the `CheckboxGroup` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/ComboBox.mdx b/packages/dev/s2-docs/pages/s2/ComboBox.mdx index 45a7218308a..54a9e2e47e4 100644 --- a/packages/dev/s2-docs/pages/s2/ComboBox.mdx +++ b/packages/dev/s2-docs/pages/s2/ComboBox.mdx @@ -1,10 +1,13 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; export const tags = ['autocomplete', 'search', 'typeahead']; +export const relatedPages = [ + {title: 'Testing', url: './ComboBox/testing.html'} +]; # ComboBox diff --git a/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx b/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx new file mode 100644 index 00000000000..ce899b38df9 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx @@ -0,0 +1,65 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'combobox', 'test-utils']; +export const description = 'Testing ComboBox with React Spectrum test utils'; + +# Testing ComboBox + +## Test utils + +`@react-spectrum/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ComboBox` tester in your test cases. This gives you access to `ComboBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ComboBox` tester, use it to simulate option selection, and verify the combobox's state after each interaction. + +```ts +// Combobox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ComboBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the combobox tester + let {getByTestId} = render( + + ... + + ); + let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); + + await comboboxTester.open(); + expect(comboboxTester.listbox).toBeInTheDocument(); + + let options = comboboxTester.options(); + await comboboxTester.selectOption({option: options[0]}); + expect(comboboxTester.combobox.value).toBe('One'); + expect(comboboxTester.listbox).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `ComboBox` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Dialog.mdx b/packages/dev/s2-docs/pages/s2/Dialog.mdx index dfc3e176a13..6a3f3af9e92 100644 --- a/packages/dev/s2-docs/pages/s2/Dialog.mdx +++ b/packages/dev/s2-docs/pages/s2/Dialog.mdx @@ -1,10 +1,13 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {Dialog, FullscreenDialog, CustomDialog, DialogTrigger, DialogContainer, Button, ButtonGroup, Heading, Header, Content, Footer, Image, TextField, Checkbox, CloseButton, ActionButton, IllustratedMessage} from '@react-spectrum/s2'; +import {Dialog, FullscreenDialog, CustomDialog, DialogTrigger, DialogContainer, Button, ButtonGroup, Heading, Header, Content, Footer, Image, TextField, Checkbox, CloseButton, ActionButton, IllustratedMessage, InlineAlert} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; export const tags = ['modal', 'popup', 'overlay']; +export const relatedPages = [ + {title: 'Testing', url: './Dialog/testing.html'} +]; # Dialog @@ -200,7 +203,7 @@ function DialogContainerExample() { Delete Item Share Item - + {/*- begin highlight -*/} setDialogType(null)}> {/*- end highlight -*/} diff --git a/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx b/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx new file mode 100644 index 00000000000..ea5e8e36e71 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'dialog', 'test-utils']; +export const description = 'Testing Dialog with React Spectrum test utils'; + +# Testing Dialog + +## Test utils + +`@react-spectrum/test-utils` offers common dialog interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Dialog` tester in your test cases. This gives you access to `Dialog` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Dialog` tester, use it to open and close the dialog, and verify the dialog's state after each interaction. + +```ts +// Dialog.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Dialog can be opened and closed', async function () { + // Render your test component/app and initialize the dialog tester + let {getByTestId, getByRole} = render( + + Trigger + + ... + + + ); + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toBeVisible(); + await dialogTester.close(); + expect(dialog).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Dialog` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Menu.mdx b/packages/dev/s2-docs/pages/s2/Menu.mdx index 248d3471389..b2164057f53 100644 --- a/packages/dev/s2-docs/pages/s2/Menu.mdx +++ b/packages/dev/s2-docs/pages/s2/Menu.mdx @@ -5,6 +5,9 @@ import {InlineAlert, Heading, Content} from '@react-spectrum/s2' import docs from 'docs:@react-spectrum/s2'; export const tags = ['dropdown']; +export const relatedPages = [ + {title: 'Testing', url: './Menu/testing.html'} +]; # Menu diff --git a/packages/dev/s2-docs/pages/s2/Menu/testing.mdx b/packages/dev/s2-docs/pages/s2/Menu/testing.mdx new file mode 100644 index 00000000000..83f962a3694 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/Menu/testing.mdx @@ -0,0 +1,80 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'menu', 'test-utils']; +export const description = 'Testing Menu with React Spectrum test utils'; + +# Testing Menu + +## General setup + +Menu features long press interactions depending on the actions provided and if the user is interacting with the menu on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-spectrum/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Menu` tester in your test cases. This gives you access to `Menu` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Menu` tester, use it to find and open a submenu, and verify the menu's state after each interaction. + +```ts +// Menu.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Menu can open its submenu via keyboard', async function () { + // Render your test component/app and initialize the menu tester + let {getByTestId} = render( + + + ... + + ); + let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); + + await menuTester.open(); + expect(menuTester.menu).toBeInTheDocument(); + let submenuTriggers = menuTester.submenuTriggers; + expect(submenuTriggers).toHaveLength(1); + + let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); + expect(submenuTester.menu).toBeInTheDocument(); + + await submenuTester.selectOption({option: submenuTester.options()[0]}); + expect(submenuTester.menu).not.toBeInTheDocument(); + expect(menuTester.menu).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Menu` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Picker.mdx b/packages/dev/s2-docs/pages/s2/Picker.mdx index 88c956a12fb..4c695a9ce22 100644 --- a/packages/dev/s2-docs/pages/s2/Picker.mdx +++ b/packages/dev/s2-docs/pages/s2/Picker.mdx @@ -1,10 +1,13 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; export const tags = ['select', 'dropdown']; +export const relatedPages = [ + {title: 'Testing', url: './Picker/testing.html'} +]; # Picker diff --git a/packages/dev/s2-docs/pages/s2/Picker/testing.mdx b/packages/dev/s2-docs/pages/s2/Picker/testing.mdx new file mode 100644 index 00000000000..92c4036e5a5 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/Picker/testing.mdx @@ -0,0 +1,62 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'picker', 'test-utils']; +export const description = 'Testing Picker with React Spectrum test utils'; + +# Testing Picker + +## Test utils + +`@react-spectrum/test-utils` offers common picker interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Picker` tester in your test cases. This gives you access to `Picker` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Picker` tester, use it to simulate option selection, and verify the picker's state after each interaction. + +```ts +// Picker.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Picker can select an option via keyboard', async function () { + // Render your test component/app and initialize the select tester + let {getByTestId} = render( + + ... + + ); + let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); + let trigger = selectTester.trigger; + expect(trigger).toHaveTextContent('Select an item'); + + await selectTester.selectOption({option: 'Cat'}); + expect(trigger).toHaveTextContent('Cat'); +}); +``` + +See below for the full definition of the `User` and the `Picker` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/RadioGroup.mdx b/packages/dev/s2-docs/pages/s2/RadioGroup.mdx index 30ae82b98eb..dc86d41a0bc 100644 --- a/packages/dev/s2-docs/pages/s2/RadioGroup.mdx +++ b/packages/dev/s2-docs/pages/s2/RadioGroup.mdx @@ -1,10 +1,13 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {RadioGroup, Radio} from '@react-spectrum/s2'; +import {RadioGroup, Radio, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; export const tags = ['input']; +export const relatedPages = [ + {title: 'Testing', url: './RadioGroup/testing.html'} +]; # RadioGroup diff --git a/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx b/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx new file mode 100644 index 00000000000..c07838d9230 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'radiogroup', 'test-utils']; +export const description = 'Testing RadioGroup with React Spectrum test utils'; + +# Testing RadioGroup + +## Test utils + +`@react-spectrum/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `RadioGroup` tester in your test cases. This gives you access to `RadioGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `RadioGroup` tester, use it to changing radio selection, and verify the radio group's state after each interaction. + +```ts +// RadioGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + +See below for the full definition of the `User` and the `RadioGroup` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/TableView.mdx b/packages/dev/s2-docs/pages/s2/TableView.mdx index 1d884e3c76f..2a67bba4e89 100644 --- a/packages/dev/s2-docs/pages/s2/TableView.mdx +++ b/packages/dev/s2-docs/pages/s2/TableView.mdx @@ -2,9 +2,12 @@ import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:@react-spectrum/s2'; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['table', 'data', 'grid']; +export const relatedPages = [ + {title: 'Testing', url: './TableView/testing.html'} +]; # TableView @@ -895,4 +898,4 @@ export default function EditableTable(props) { ### EditableCell - \ No newline at end of file + diff --git a/packages/dev/s2-docs/pages/s2/TableView/testing.mdx b/packages/dev/s2-docs/pages/s2/TableView/testing.mdx new file mode 100644 index 00000000000..1dd9ebdd349 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/TableView/testing.mdx @@ -0,0 +1,83 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'tableview', 'test-utils']; +export const description = 'Testing TableView with React Spectrum test utils'; + +# Testing TableView + +## General setup + +TableView features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-spectrum/test-utils` offers common table interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `TableView` tester in your test cases. This gives you access to `TableView` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `TableView` tester, use it to simulate row selection, and verify the table's state after each interaction. + +```ts +// Table.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('TableView can toggle row selection', async function () { + // Render your test component/app and initialize the table tester + let {getByTestId} = render( + + ... + + ); + let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); + expect(tableTester.selectedRows).toHaveLength(0); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + + await tableTester.toggleRowSelection({row: 2}); + expect(tableTester.selectedRows).toHaveLength(9); + let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + expect(checkbox).toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `TableView` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Tabs.mdx b/packages/dev/s2-docs/pages/s2/Tabs.mdx index 3bd7a1013e6..04bcd1031b3 100644 --- a/packages/dev/s2-docs/pages/s2/Tabs.mdx +++ b/packages/dev/s2-docs/pages/s2/Tabs.mdx @@ -1,8 +1,12 @@ import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:@react-spectrum/s2'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['navigation']; +export const relatedPages = [ + {title: 'Testing', url: './Tabs/testing.html'} +]; # Tabs @@ -161,7 +165,7 @@ import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; width: 320, padding: 16, borderWidth: 1, - borderStyle: 'solid', + borderStyle: 'solid', borderColor: 'gray-300', borderRadius: 'default', overflow: 'hidden', @@ -275,4 +279,4 @@ function Example() { ### TabPanel - \ No newline at end of file + diff --git a/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx b/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx new file mode 100644 index 00000000000..ce630d611a5 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx @@ -0,0 +1,68 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'tabs', 'test-utils']; +export const description = 'Testing Tabs with React Spectrum test utils'; + +# Testing Tabs + +## General setup + +Tabs features automatic tab collapse behavior and may need specific mocks to test said behavior. +TODO: update this with what mocks are required + +## Test utils + +`@react-spectrum/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tabs` tester in your test cases. This gives you access to `Tabs` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tabs` tester, use it to change tab selection, and verify the tabs' state after each interaction. + +```ts +// Tabs.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tabs can change selection via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); + + let tabs = tabsTester.tabs; + expect(tabsTester.selectedTab).toBe(tabs[0]); + + await tabsTester.triggerTab({tab: 1}); + expect(tabsTester.selectedTab).toBe(tabs[1]); +}); +``` + +See below for the full definition of the `User` and the `Tabs` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/TreeView.mdx b/packages/dev/s2-docs/pages/s2/TreeView.mdx index e754da1a821..4d1e72006cd 100644 --- a/packages/dev/s2-docs/pages/s2/TreeView.mdx +++ b/packages/dev/s2-docs/pages/s2/TreeView.mdx @@ -1,10 +1,13 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {TreeView, TreeViewItem, TreeViewItemContent, Collection, Text, ActionMenu, MenuItem} from '@react-spectrum/s2'; +import {TreeView, TreeViewItem, TreeViewItemContent, Collection, Text, ActionMenu, MenuItem, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; export const tags = ['hierarchy', 'data', 'nested']; +export const relatedPages = [ + {title: 'Testing', url: './TreeView/testing.html'} +]; # TreeView diff --git a/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx b/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx new file mode 100644 index 00000000000..f1e713d1ded --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx @@ -0,0 +1,82 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const isSubpage = true; +export const tags = ['testing', 'treeview', 'test-utils']; +export const description = 'Testing TreeView with React Spectrum test utils'; + +# Testing TreeView + +## General setup + +TreeView features long press interactions on its rows depending on the row actions provided and if the user is interacting with the tree on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-spectrum/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `TreeView` tester in your test cases. This gives you access to `TreeView` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `TreeView` tester, use it to simulate row expansion and selection, and verify the tree's state after each interaction. + +```ts +// Tree.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('TreeView can select a item via keyboard', async function () { + // Render your test component/app and initialize the Tree tester + let {getByTestId} = render( + + ... + + ); + let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 1}); + expect(treeTester.selectedRows).toHaveLength(2); + expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); + + await treeTester.toggleRowExpansion({index: 0}); + expect(treeTester.rows[0]).toHaveAttribute('aria-expanded', 'true'); +}); +``` + +See below for the full definition of the `User` and the `TreeView` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Icons.mdx b/packages/dev/s2-docs/pages/s2/icons.mdx similarity index 100% rename from packages/dev/s2-docs/pages/s2/Icons.mdx rename to packages/dev/s2-docs/pages/s2/icons.mdx diff --git a/packages/dev/s2-docs/pages/s2/index.mdx b/packages/dev/s2-docs/pages/s2/index.mdx index 5856d0107d9..c6774e6e4ed 100644 --- a/packages/dev/s2-docs/pages/s2/index.mdx +++ b/packages/dev/s2-docs/pages/s2/index.mdx @@ -49,7 +49,7 @@ export const title = 'Home'; {id: 'Divider', name: 'Divider', href: 'Divider.html'}, {id: 'DropZone', name: 'DropZone', href: 'DropZone.html'}, {id: 'Form', name: 'Form', href: 'Form.html'}, - {id: 'Icons', name: 'Icons', href: 'Icons.html'}, + {id: 'Icons', name: 'Icons', href: 'icons.html'}, {id: 'IllustratedMessage', name: 'IllustratedMessage', href: 'IllustratedMessage.html'}, {id: 'Illustrations', name: 'Illustrations', href: 'Illustrations.html'}, {id: 'Image', name: 'Image', href: 'Image.html'}, diff --git a/packages/dev/s2-docs/pages/s2/reference.mdx b/packages/dev/s2-docs/pages/s2/reference.mdx new file mode 100644 index 00000000000..4d312d9a2ca --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/reference.mdx @@ -0,0 +1,82 @@ +import {Layout} from '../../src/Layout'; +import {InlineAlert, Heading, Content, Link} from '@react-spectrum/s2'; +import {S2Colors} from '../../src/S2Colors'; +import {S2Typography} from '../../src/S2Typography'; +import {StyleMacroProperties} from '../../src/types'; +import {getPropertyDefinitions, getShorthandDefinitions} from '../../src/styleProperties'; +export default Layout; + +export const section = 'Components'; +export const tags = ['style', 'macro', 'spectrum', 'custom', 'values', 'reference']; +export const description = 'Reference table for the style macro'; + +# Style Macro + +The `style` macro supports a constrained set of values per property that conform to Spectrum 2. + +## Colors + +All Spectrum 2 color tokens are available across color properties (e.g., `backgroundColor`, `color`, `borderColor`). +`baseColors` consists of the semantic and global colors listed below. + + + + + +## Dimensions + +Spacing props like `margin` and `padding` accept values on a **4px grid**. These are specified in `px` and get converted to `rem`. In addition to numbers, these named options are available: + +- `edge-to-text` – default spacing between the edge of a control and its text. Relative to control height. +- `pill` – default spacing between the edge of a pill-shaped control and its text. Relative to control height. +- `text-to-control` – default spacing between text and a control (e.g., label and input). Scales with font size. +- `text-to-visual` – default spacing between text and a visual element (e.g., icon). Scales with font size. + +Size props like `width` and `height` accept arbitrary pixel values. Values are converted to `rem` and multiplied by 1.25x on touch devices to increase hit targets. + + + +## Text + +Spectrum 2 typography can be applied via the `font` [shorthand](#shorthands), which sets `fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, and `color`. You can override any of these individually. +Note that `font` should be applied on a per element basis rather than globally so as to properly conform with Spectrum designs. + +```tsx +
+

Heading

+

Body

+
    +
  • List item
  • +
+
+``` + +Type scales include: UI, Body, Heading, Title, Detail, and Code. Each scale has a default and additional t-shirt sizes (e.g., `ui-sm`, `heading-2xl`, `code-xl`). + + + + + + + +## Effects + + + +## Layout + + + +## Misc + + + +## Shorthands + +Shorthands apply their provided value to commonly grouped properties. + + + +## Conditions + + diff --git a/packages/dev/s2-docs/pages/s2/styling.mdx b/packages/dev/s2-docs/pages/s2/styling.mdx index 3735835c868..1f244ade31e 100644 --- a/packages/dev/s2-docs/pages/s2/styling.mdx +++ b/packages/dev/s2-docs/pages/s2/styling.mdx @@ -1,8 +1,7 @@ import {Layout} from '../../src/Layout'; import {InlineAlert, Heading, Content, Link} from '@react-spectrum/s2'; -import {S2Colors} from '../../src/S2Colors'; -import {S2Typography} from '../../src/S2Typography'; import {S2StyleProperties} from '../../src/S2StyleProperties'; +import {S2FAQ} from '../../src/S2FAQ'; export default Layout; export const section = 'Guides'; @@ -11,11 +10,13 @@ export const description = 'Styling in React Spectrum'; # Styling -React Spectrum includes a build-time style macro that generates atomic CSS and lets you apply Spectrum tokens directly in your components with type-safe autocompletion. +React Spectrum includes a build-time `style` macro that generates atomic CSS and lets you apply Spectrum tokens directly in your components with type-safe autocompletion. ## Style macro -The `style` macro runs at build time and returns a class name for applying Spectrum 2 design tokens (colors, spacing, sizing, typography, etc.). +The `style` macro runs at build time and returns a class name for applying Spectrum 2 design tokens (colors, spacing, sizing, typography, etc.). As can been seen below, +the keys of the object passed to the `style` macro correspond to a CSS property, each paired with the property's desired value. See [here](./reference.html) for a full list +of supported values. ```tsx import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; @@ -37,6 +38,14 @@ Colocating styles with your component code means: - Develop more efficiently – no switching files or writing selectors. - Refactor with confidence – changes are isolated; deleting a component removes its styles. + + Important Note + + Due to the atomic nature of the generated CSS rules, it is strongly recommended that you follow the CSS optimization guide listed [below](#css-optimization). + Failure to do so can result in large number of duplicate rules and obtuse styling bugs. + + + ## Spectrum components The `styles` prop accepts a limited set of CSS properties, including layout, spacing, sizing, and positioning. Other styles such as colors and internal padding cannot be customized within Spectrum components. @@ -86,77 +95,6 @@ import {Button} from '@react-spectrum/s2'; 'visibility' ]} /> -### UNSAFE Style Overrides - -We highly discourage overriding the styles of React Spectrum components because it may break at any time when we change our implementation, making it difficult for you to update in the future. Consider using [React Aria Components](https://react-spectrum.adobe.com/react-aria/) with our style macro to build a custom component with Spectrum styles instead. - -With that being said, the `UNSAFE_className` and `UNSAFE_style` props are supported on Spectrum 2 components as last-resort escape hatches. - -```tsx -/* YourComponent.tsx */ -import {Button} from '@react-spectrum/s2'; -import './YourComponent.css'; - -function YourComponent() { - return ; -} -``` - -```css -/* YourComponent.css */ -.your-unsafe-class { - background: red; -} -``` - -## Values - -The `style` macro supports a constrained set of values per property that conform to Spectrum 2. This improves consistency and maintainability. - -### Colors - -All Spectrum 2 color tokens are available across color properties (e.g., `backgroundColor`, `color`, `borderColor`). - - - -### Spacing - -Spacing props like `margin` and `padding` accept values on a **4px grid**. These are specified in `px` and get converted to `rem`. In addition to numbers, these named options are available: - -- `edge-to-text` – default spacing between the edge of a control and its text. Relative to control height. -- `pill` – default spacing between the edge of a pill-shaped control and its text. Relative to control height. -- `text-to-control` – default spacing between text and a control (e.g., label and input). Scales with font size. -- `text-to-visual` – default spacing between text and a visual element (e.g., icon). Scales with font size. - -### Sizing - -Size props like `width` and `height` accept arbitrary pixel values. Values are converted to `rem` and multiplied by 1.25x on touch devices to increase hit targets. - -### Typography - -Spectrum 2 typography is applied via the `font` shorthand, which sets `fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, and `color`. You can override any of these individually. - -```tsx -
-

Heading

-

Body

-
    -
  • List item
  • -
-
-``` - -Type scales include: UI, Body, Heading, Title, Detail, and Code. Each scale has a default and additional t-shirt sizes (e.g., `ui-sm`, `heading-2xl`, `code-xl`). - - - - - Important Note - - Only use `` and `` inside Spectrum components with predefined styles (e.g., ``, ``). They are unstyled by default and should not be used standalone. Use HTML elements with the style macro instead. - - - ## Conditional styles Define conditional values as objects to handle media queries, UI states (hover/press), and variants. This keeps all values for a property together. @@ -166,12 +104,19 @@ Define conditional values as objects to handle media queries, UI states (hover/p className={style({ padding: { default: 8, - lg: 32 + lg: 32, + '@media (min-width: 2560px)': 64 } })} /> ``` +In the example above, the keys of the nested object now map out the "conditions" that govern the padding of the `div`. This translates to the following: + +- If the viewport is larger than `2560px`, as specified by a user defined [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries), the padding of the `div` is set to `64px`. +- If the viewport matches the `style` macro's predefined `lg` [breakpoint](./reference.html#conditions) (i.e. the viewport is larger than `1024px`), but does not exceed previous condition, the padding of the `div` is set to `32px` +- Otherwise, default to a padding of `8px`. + Conditions are mutually exclusive and ordered. The macro uses CSS cascade layers so the last matching condition wins without specificity issues. ### Runtime conditions @@ -195,13 +140,14 @@ function MyComponent({variant}: {variant: 'primary' | 'secondary'}) { } ``` -Boolean conditions starting with `is` can be used directly without nesting: +Boolean conditions starting with `is` or `allows` can be used directly without nesting: ```tsx const styles = style({ backgroundColor: { default: 'gray-100', - isSelected: 'gray-900' + isSelected: 'gray-900', + allowsRemoving: 'gray-400' } }); @@ -222,7 +168,7 @@ import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; isSelected: 'gray-900' } })} -/> +/> ``` ### Nesting conditions @@ -253,6 +199,11 @@ const styles = style({ Extract common styles into constants and spread them into `style` calls. These must be in the same file or imported from another file as a macro. ```tsx +// style-utils.ts +export const bannerBackground = () => 'blue-1000' as const; + +// component.tsx +import {bannerBackground} from './style-utils' with {type: 'macro'}; const horizontalStack = { display: 'flex', alignItems: 'center', @@ -261,6 +212,7 @@ const horizontalStack = { const styles = style({ ...horizontalStack, + backgroundColor: bannerBackground(), columnGap: 4 }); ``` @@ -308,9 +260,28 @@ const buttonStyle = style({ ``` +## Setting CSS variables + +CSS variables can be directly defined in a `style` macro, allowing child elements to then access them in their own styles. +A `type` should be provided to specify the CSS property type the `value` represents. + +```tsx +const parentStyle = style({ + '--rowBackgroundColor': { + type: 'backgroundColor', + value: 'gray-400' + } +}); + +const childStyle = style({ + backgroundColor: '--rowBackgroundColor' +}); +``` + ## CSS optimization -The style macro relies on CSS bundling and minification for optimal output. Follow these best practices: +The `style` macro relies on CSS bundling and minification for optimal output. Failure to perform this optimization will result in a suboptimal developer experience and obtuse styling bugs. +Follow these best practices: - Ensure styles are extracted into a CSS bundle; do not inject at runtime with `