Skip to content

Commit

Permalink
SelectControl: Pass through options props (#64211)
Browse files Browse the repository at this point in the history
* SelectControl: Pass through `options` props

* Clean up types

* Add changelog

Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: tyxla <tyxla@git.wordpress.org>
  • Loading branch information
3 people authored Aug 5, 2024
1 parent f6c809f commit 650eb7f
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 40 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- `TimeInput`: Expose as subcomponent of `TimePicker` ([#63145](https://github.com/WordPress/gutenberg/pull/63145)).
- `RadioControl`: add support for option help text ([#63751](https://github.com/WordPress/gutenberg/pull/63751)).
- `Guide`: Add `__next40pxDefaultSize` to buttons ([#64181](https://github.com/WordPress/gutenberg/pull/64181)).
- `SelectControl`: Pass through `options` props ([#64211](https://github.com/WordPress/gutenberg/pull/64211)).

### Internal

Expand Down
9 changes: 8 additions & 1 deletion packages/components/src/select-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ In most cases, it is preferable to use the `FormTokenField` or `CheckboxControl`

#### options

An array of objects containing the following properties:
An array of objects containing the following properties, as well as any other `option` element attributes:

- `label`: (string) The label to be shown to the user.
- `value`: (string) The internal value used to choose the selected value. This is also the value passed to onChange when the option is selected.
Expand All @@ -214,6 +214,13 @@ If multiple is false the value received is a single value with the new selected
- Type: `function`
- Required: Yes

#### value

The value of the selected option. If `multiple` is true, the `value` should be an array with the values of the selected options.

- Type: `String|String[]`
- Required: No

#### variant

The style variant of the control.
Expand Down
34 changes: 17 additions & 17 deletions packages/components/src/select-control/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ function useUniqueId( idProp?: string ) {
return idProp || id;
}

function SelectOptions( {
options,
}: {
options: NonNullable< SelectControlProps[ 'options' ] >;
} ) {
return options.map( ( { id, label, value, ...optionProps }, index ) => {
const key = id || `${ label }-${ value }-${ index }`;

return (
<option key={ key } value={ value } { ...optionProps }>
{ label }
</option>
);
} );
}

function UnforwardedSelectControl(
props: WordPressComponentProps< SelectControlProps, 'select', false >,
ref: React.ForwardedRef< HTMLSelectElement >
Expand Down Expand Up @@ -115,23 +131,7 @@ function UnforwardedSelectControl(
value={ valueProp }
variant={ variant }
>
{ children ||
options.map( ( option, index ) => {
const key =
option.id ||
`${ option.label }-${ option.value }-${ index }`;

return (
<option
key={ key }
value={ option.value }
disabled={ option.disabled }
hidden={ option.hidden }
>
{ option.label }
</option>
);
} ) }
{ children || <SelectOptions options={ options } /> }
</Select>
</StyledInputBase>
</BaseControl>
Expand Down
27 changes: 23 additions & 4 deletions packages/components/src/select-control/test/select-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ describe( 'SelectControl', () => {
expect( screen.queryByRole( 'combobox' ) ).not.toBeInTheDocument();
} );

it( 'should not render its children', async () => {
const user = await userEvent.setup();
it( 'should render its children', async () => {
const user = userEvent.setup();
const handleChangeMock = jest.fn();

render(
Expand Down Expand Up @@ -46,8 +46,8 @@ describe( 'SelectControl', () => {
);
} );

it( 'should not render its options', async () => {
const user = await userEvent.setup();
it( 'should render its options', async () => {
const user = userEvent.setup();
const handleChangeMock = jest.fn();

render(
Expand Down Expand Up @@ -81,4 +81,23 @@ describe( 'SelectControl', () => {
expect.anything()
);
} );

it( 'should pass through options props', () => {
render(
<SelectControl
label="Select"
options={ [
{
value: 'value',
label: 'label',
'aria-label': 'Aria label',
},
] }
/>
);

expect(
screen.getByRole( 'option', { name: 'Aria label' } )
).toBeInTheDocument();
} );
} );
39 changes: 21 additions & 18 deletions packages/components/src/select-control/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import type { ChangeEvent, FocusEvent, ReactNode } from 'react';
import type { ChangeEvent, ReactNode } from 'react';

/**
* Internal dependencies
Expand All @@ -22,9 +22,12 @@ type SelectControlBaseProps = Pick<
| 'suffix'
> &
Pick< BaseControlProps, 'help' | '__nextHasNoMarginBottom' > & {
onBlur?: ( event: FocusEvent< HTMLSelectElement > ) => void;
onFocus?: ( event: FocusEvent< HTMLSelectElement > ) => void;
options?: {
/**
* An array of option property objects to be rendered,
* each with a `label` and `value` property, as well as any other
* `<option>` attributes.
*/
options?: ( {
/**
* The label to be shown to the user.
*/
Expand All @@ -34,20 +37,10 @@ type SelectControlBaseProps = Pick<
* This is also the value passed to `onChange` when the option is selected.
*/
value: string;
id?: string;
/**
* Whether or not the option should have the disabled attribute.
*
* @default false
*/
disabled?: boolean;
/**
* Whether or not the option should be hidden.
*
* @default false
*/
hidden?: boolean;
}[];
} & Omit<
React.OptionHTMLAttributes< HTMLOptionElement >,
'label' | 'value'
> )[];
/**
* As an alternative to the `options` prop, `optgroup`s and `options` can be
* passed in as `children` for more customizability.
Expand All @@ -70,6 +63,11 @@ export type SelectControlSingleSelectionProps = SelectControlBaseProps & {
* @default false
*/
multiple?: false;
/**
* The value of the selected option.
*
* If `multiple` is true, the `value` should be an array with the values of the selected options.
*/
value?: string;
/**
* A function that receives the value of the new option that is being selected as input.
Expand All @@ -92,6 +90,11 @@ export type SelectControlMultipleSelectionProps = SelectControlBaseProps & {
* @default false
*/
multiple: true;
/**
* The value of the selected option.
*
* If `multiple` is true, the `value` should be an array with the values of the selected options.
*/
value?: string[];
/**
* A function that receives the value of the new option that is being selected as input.
Expand Down

0 comments on commit 650eb7f

Please sign in to comment.