Skip to content

Commit

Permalink
CustomSelect: add tests for new features (#58583)
Browse files Browse the repository at this point in the history
* CustomSelect: add tests for new features

* Update tests based on PR feedback

* Test against controlled vs uncontrolled and group multiselect tests

* Remove span and add additional assertion for option

* Update changelog
  • Loading branch information
brookewp authored Feb 6, 2024
1 parent 769b1e7 commit 519824a
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 1 deletion.
3 changes: 2 additions & 1 deletion packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
- `Tabs`: improve controlled mode focus handling and keyboard navigation ([#57696](https://github.com/WordPress/gutenberg/pull/57696)).
- `Tabs`: prevent internal focus from updating too early ([#58625](https://github.com/WordPress/gutenberg/pull/58625)).
- Expand theming support in the `COLORS` variable object ([#58097](https://github.com/WordPress/gutenberg/pull/58097)).
- `CustomSelect`: disable `virtualFocus` to fix issue for screenreaders ([#58585](https://github.com/WordPress/gutenberg/pull/58585))
- `CustomSelect`: disable `virtualFocus` to fix issue for screenreaders ([#58585](https://github.com/WordPress/gutenberg/pull/58585)).

### Enhancements

Expand All @@ -35,6 +35,7 @@

- `Composite`: Removing Reakit `Composite` implementation ([#58620](https://github.com/WordPress/gutenberg/pull/58620)).
- Removing Reakit as a dependency of the components package ([#58631](https://github.com/WordPress/gutenberg/pull/58631)).
- `CustomSelect`: add unit tests ([#58583](https://github.com/WordPress/gutenberg/pull/58583)).

## 25.16.0 (2024-01-24)

Expand Down
219 changes: 219 additions & 0 deletions packages/components/src/custom-select-control-v2/test/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/**
* External dependencies
*/
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import { CustomSelect, CustomSelectItem } from '..';
import type { CustomSelectProps } from '../types';

const ControlledCustomSelect = ( props: CustomSelectProps ) => {
const [ value, setValue ] = useState< string | string[] >();
return (
<CustomSelect
{ ...props }
onChange={ ( nextValue ) => {
setValue( nextValue );
props.onChange?.( nextValue );
} }
value={ value }
/>
);
};

describe.each( [
[ 'uncontrolled', CustomSelect ],
[ 'controlled', ControlledCustomSelect ],
] )( 'CustomSelect %s', ( ...modeAndComponent ) => {
const [ , Component ] = modeAndComponent;

describe( 'Multiple selection', () => {
it( 'Should be able to select multiple items when provided an array', async () => {
const user = userEvent.setup();
const onChangeMock = jest.fn();

// initial selection as defaultValue
const defaultValues = [
'incandescent glow',
'ultraviolet morning light',
];

render(
<Component
defaultValue={ defaultValues }
onChange={ onChangeMock }
label="Multi-select"
>
{ [
'aurora borealis green',
'flamingo pink sunrise',
'incandescent glow',
'rose blush',
'ultraviolet morning light',
].map( ( item ) => (
<CustomSelectItem key={ item } value={ item }>
{ item }
</CustomSelectItem>
) ) }
</Component>
);

const currentSelectedItem = screen.getByRole( 'combobox', {
expanded: false,
} );

// ensure more than one item is selected due to defaultValues
expect( currentSelectedItem ).toHaveTextContent(
`${ defaultValues.length } items selected`
);

await user.click( currentSelectedItem );

expect( screen.getByRole( 'listbox' ) ).toHaveAttribute(
'aria-multiselectable'
);

// ensure defaultValues are selected in list of items
defaultValues.forEach( ( value ) =>
expect(
screen.getByRole( 'option', {
name: value,
selected: true,
} )
).toBeVisible()
);

// name of next selection
const nextSelectionName = 'rose blush';

// element for next selection
const nextSelection = screen.getByRole( 'option', {
name: nextSelectionName,
} );

// click next selection to add another item to current selection
await user.click( nextSelection );

// updated array containing defaultValues + the item just selected
const updatedSelection = defaultValues.concat( nextSelectionName );

expect( onChangeMock ).toHaveBeenCalledWith( updatedSelection );

expect( nextSelection ).toHaveAttribute( 'aria-selected' );

// expect increased array length for current selection
expect( currentSelectedItem ).toHaveTextContent(
`${ updatedSelection.length } items selected`
);
} );

it( 'Should be able to deselect items when provided an array', async () => {
const user = userEvent.setup();

// initial selection as defaultValue
const defaultValues = [
'aurora borealis green',
'incandescent glow',
'key lime green',
'rose blush',
'ultraviolet morning light',
];

render(
<Component defaultValue={ defaultValues } label="Multi-select">
{ defaultValues.map( ( item ) => (
<CustomSelectItem key={ item } value={ item }>
{ item }
</CustomSelectItem>
) ) }
</Component>
);

const currentSelectedItem = screen.getByRole( 'combobox', {
expanded: false,
} );

await user.click( currentSelectedItem );

// Array containing items to deselect
const nextSelection = [
'aurora borealis green',
'rose blush',
'incandescent glow',
];

// Deselect some items by clicking them to ensure that changes
// are reflected correctly
await Promise.all(
nextSelection.map( async ( value ) => {
await user.click(
screen.getByRole( 'option', { name: value } )
);
expect(
screen.getByRole( 'option', {
name: value,
selected: false,
} )
).toBeVisible();
} )
);

// expect different array length from defaultValues due to deselecting items
expect( currentSelectedItem ).toHaveTextContent(
`${
defaultValues.length - nextSelection.length
} items selected`
);
} );
} );

it( 'Should allow rendering a custom value when using `renderSelectedValue`', async () => {
const user = userEvent.setup();

const renderValue = ( value: string | string[] ) => {
return <img src={ `${ value }.jpg` } alt={ value as string } />;
};

render(
<Component label="Rendered" renderSelectedValue={ renderValue }>
<CustomSelectItem value="april-29">
{ renderValue( 'april-29' ) }
</CustomSelectItem>
<CustomSelectItem value="july-9">
{ renderValue( 'july-9' ) }
</CustomSelectItem>
</Component>
);

const currentSelectedItem = screen.getByRole( 'combobox', {
expanded: false,
} );

expect( currentSelectedItem ).toBeVisible();

// expect that the initial selection renders an image
expect( currentSelectedItem ).toContainElement(
screen.getByRole( 'img', { name: 'april-29' } )
);

expect(
screen.queryByRole( 'img', { name: 'july-9' } )
).not.toBeInTheDocument();

await user.click( currentSelectedItem );

// expect that the other image is only visible after opening popover with options
expect( screen.getByRole( 'img', { name: 'july-9' } ) ).toBeVisible();
expect(
screen.getByRole( 'option', { name: 'july-9' } )
).toBeVisible();
} );
} );

0 comments on commit 519824a

Please sign in to comment.