Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CustomSelectControlV2: Rename for consistency #60178

Merged
merged 6 commits into from Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/components/CHANGELOG.md
Expand Up @@ -6,6 +6,10 @@

- `InputControl`: Ignore IME events when `isPressEnterToChange` is enabled ([#60090](https://github.com/WordPress/gutenberg/pull/60090)).

### Experimental

- `CustomSelectControlV2`: Rename for consistency ([#60178](https://github.com/WordPress/gutenberg/pull/60178)).

### Internal

- `Popover`, `ColorPicker`: Obviate pointer event trap #59449 ([#59449](https://github.com/WordPress/gutenberg/pull/59449)).
Expand Down
54 changes: 27 additions & 27 deletions packages/components/src/custom-select-control-v2/README.md
@@ -1,4 +1,4 @@
# CustomSelect
# CustomSelectControlV2

<div class="callout callout-alert">
This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
Expand All @@ -12,31 +12,31 @@ Used to render a customizable select control component.

#### Uncontrolled Mode

CustomSelect can be used in an uncontrolled mode, where the component manages its own state. In this mode, the `defaultValue` prop can be used to set the initial selected value. If this prop is not set, the first value from the children will be selected by default.
`CustomSelectControlV2` can be used in an uncontrolled mode, where the component manages its own state. In this mode, the `defaultValue` prop can be used to set the initial selected value. If this prop is not set, the first value from the children will be selected by default.

```jsx
const UncontrolledCustomSelect = () => (
<CustomSelect label="Colors">
<CustomSelectItem value="Blue">
const UncontrolledCustomSelectControlV2 = () => (
<CustomSelectControlV2 label="Colors">
<CustomSelectControlV2.Item value="Blue">
{ /* The `defaultValue` since it wasn't defined */ }
<span style={ { color: 'blue' } }>Blue</span>
</CustomSelectItem>
<CustomSelectItem value="Purple">
</CustomSelectControlV2.Item>
<CustomSelectControlV2.Item value="Purple">
<span style={ { color: 'purple' } }>Purple</span>
</CustomSelectItem>
<CustomSelectItem value="Pink">
</CustomSelectControlV2.Item>
<CustomSelectControlV2.Item value="Pink">
<span style={ { color: 'deeppink' } }>Pink</span>
</CustomSelectItem>
</CustomSelect>
</CustomSelectControlV2.Item>
</CustomSelectControlV2>
);
```

#### Controlled Mode

CustomSelect can also be used in a controlled mode, where the parent component specifies the `value` and the `onChange` props to control selection.
`CustomSelectControlV2` can also be used in a controlled mode, where the parent component specifies the `value` and the `onChange` props to control selection.

```jsx
const ControlledCustomSelect = () => {
const ControlledCustomSelectControlV2 = () => {
const [ value, setValue ] = useState< string | string[] >();

const renderControlledValue = ( renderValue: string | string[] ) => (
Expand All @@ -46,7 +46,7 @@ const ControlledCustomSelect = () => {
);

return (
<CustomSelect
<CustomSelectControlV2
{ ...props }
onChange={ ( nextValue ) => {
setValue( nextValue );
Expand All @@ -55,11 +55,11 @@ const ControlledCustomSelect = () => {
value={ value }
>
{ [ 'blue', 'purple', 'pink' ].map( ( option ) => (
<CustomSelectItem key={ option } value={ option }>
<CustomSelectControlV2.Item key={ option } value={ option }>
{ renderControlledValue( option ) }
</CustomSelectItem>
</CustomSelectControlV2.Item>
) ) }
</CustomSelect>
</CustomSelectControlV2>
);
};
```
Expand All @@ -70,31 +70,31 @@ Multiple selection can be enabled by using an array for the `value` and
`defaultValue` props. The argument of the `onChange` function will also change accordingly.

```jsx
const MultiSelectCustomSelect = () => (
<CustomSelect defaultValue={ [ 'blue', 'pink' ] } label="Colors">
const MultiSelectCustomSelectControlV2 = () => (
<CustomSelectControlV2 defaultValue={ [ 'blue', 'pink' ] } label="Colors">
{ [ 'blue', 'purple', 'pink' ].map( ( item ) => (
<CustomSelectItem key={ item } value={ item }>
<CustomSelectControlV2.Item key={ item } value={ item }>
{ item }
</CustomSelectItem>
</CustomSelectControlV2.Item>
) ) }
</CustomSelect>
</CustomSelectControlV2>
);
```

### Components and Sub-components

CustomSelect is comprised of two individual components:
`CustomSelectControlV2` is comprised of two individual components:

- `CustomSelect`: a wrapper component and context provider. It is responsible for managing the state of the `CustomSelectItem` children.
- `CustomSelectItem`: renders a single select item. The first `CustomSelectItem` child will be used as the `defaultValue` when `defaultValue` is undefined.
- `CustomSelectControlV2`: a wrapper component and context provider. It is responsible for managing the state of the `CustomSelectControlV2.Item` children.
- `CustomSelectControlV2.Item`: renders a single select item. The first `CustomSelectControlV2.Item` child will be used as the `defaultValue` when `defaultValue` is undefined.

#### Props

The component accepts the following props:

##### `children`: `React.ReactNode`

The child elements. This should be composed of CustomSelect.Item components.
The child elements. This should be composed of `CustomSelectControlV2.Item` components.

- Required: yes

Expand Down Expand Up @@ -142,7 +142,7 @@ Can be used to externally control the value of the control.

- Required: no

### `CustomSelectItem`
### `CustomSelectControlV2.Item`

Used to render a select item.

Expand Down
Expand Up @@ -9,8 +9,9 @@ import * as Ariakit from '@ariakit/react';
import _CustomSelect from '../custom-select';
import type { CustomSelectProps } from '../types';
import type { WordPressComponentProps } from '../../context';
import Item from '../item';

function CustomSelect(
function CustomSelectControlV2(
props: WordPressComponentProps< CustomSelectProps, 'button', false >
) {
const { defaultValue, onChange, value, ...restProps } = props;
Expand All @@ -24,4 +25,6 @@ function CustomSelect(
return <_CustomSelect { ...restProps } store={ store } />;
}

export default CustomSelect;
CustomSelectControlV2.Item = Item;

export default CustomSelectControlV2;
@@ -1,5 +1,4 @@
/**
* Internal dependencies
*/
export { default as CustomSelect } from './default-component';
export { default as CustomSelectItem } from './custom-select-item';
export { default } from './default-component';
Expand Up @@ -26,4 +26,6 @@ export function CustomSelectItem( {
);
}

CustomSelectItem.displayName = 'CustomSelectControlV2.Item';
Copy link
Member Author

Choose a reason for hiding this comment

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

This was necessary to ensure that React devtools and the Storybook code snippet generator displays the intended name.

Copy link
Member

Choose a reason for hiding this comment

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

Perhaps this shouldn't be necessary if we exported them like this (untested):

const CustomSelectControlV2 = CustomSelect;
CustomSelectControlV2.Item = Item;
export default CustomSelectControlV2;

which would be something similar to what we're doing with Tabs right now.

Copy link
Member Author

Choose a reason for hiding this comment

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

That doesn't seem to work, it looks like it just uses the name of the initial function no matter what.

And your comment made me realize that the Tabs subcomponents aren't showing the correct display names either 😅 I'll fix that.


export default CustomSelectItem;
Expand Up @@ -11,12 +11,12 @@ import { useMemo } from '@wordpress/element';
* Internal dependencies
*/
import _CustomSelect from '../custom-select';
import CustomSelectItem from '../item';
import type { LegacyCustomSelectProps } from '../types';
import { CustomSelectItem } from '..';
import * as Styled from '../styles';
import { ContextSystemProvider } from '../../context';

function CustomSelect( props: LegacyCustomSelectProps ) {
function CustomSelectControl( props: LegacyCustomSelectProps ) {
const {
__experimentalShowSelectedHint,
__next40pxDefaultSize = false,
Expand Down Expand Up @@ -128,4 +128,4 @@ function CustomSelect( props: LegacyCustomSelectProps ) {
);
}

export default CustomSelect;
export default CustomSelectControl;
Expand Up @@ -12,7 +12,7 @@ import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import UncontrolledCustomSelect from '..';
import UncontrolledCustomSelectControl from '..';

const customClass = 'amber-skies';

Expand Down Expand Up @@ -48,14 +48,14 @@ const legacyProps = {
],
};

const LegacyControlledCustomSelect = ( {
const ControlledCustomSelectControl = ( {
options,
onChange,
...restProps
}: React.ComponentProps< typeof UncontrolledCustomSelect > ) => {
}: React.ComponentProps< typeof UncontrolledCustomSelectControl > ) => {
const [ value, setValue ] = useState( options[ 0 ] );
return (
<UncontrolledCustomSelect
<UncontrolledCustomSelectControl
{ ...restProps }
options={ options }
onChange={ ( args: any ) => {
Expand All @@ -70,8 +70,8 @@ const LegacyControlledCustomSelect = ( {
};

describe.each( [
[ 'Uncontrolled', UncontrolledCustomSelect ],
[ 'Controlled', LegacyControlledCustomSelect ],
[ 'Uncontrolled', UncontrolledCustomSelectControl ],
[ 'Controlled', ControlledCustomSelectControl ],
] )( 'CustomSelectControl (%s)', ( ...modeAndComponent ) => {
const [ , Component ] = modeAndComponent;

Expand Down
Expand Up @@ -11,15 +11,14 @@ import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import CustomSelect from '../default-component';
import { CustomSelectItem } from '..';
import CustomSelectControlV2 from '..';

const meta: Meta< typeof CustomSelect > = {
const meta: Meta< typeof CustomSelectControlV2 > = {
title: 'Components (Experimental)/CustomSelectControl v2/Default',
component: CustomSelect,
component: CustomSelectControlV2,
subcomponents: {
// @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
CustomSelectItem,
'CustomSelectControlV2.Item': CustomSelectControlV2.Item,
},
argTypes: {
children: { control: { type: null } },
Expand Down Expand Up @@ -48,10 +47,10 @@ const meta: Meta< typeof CustomSelect > = {
};
export default meta;

const Template: StoryFn< typeof CustomSelect > = ( props ) => {
const Template: StoryFn< typeof CustomSelectControlV2 > = ( props ) => {
const [ value, setValue ] = useState< string | string[] >();
return (
<CustomSelect
<CustomSelectControlV2
{ ...props }
onChange={ ( nextValue: string | string[] ) => {
setValue( nextValue );
Expand All @@ -68,15 +67,15 @@ Default.args = {
defaultValue: 'Select a color...',
children: (
<>
<CustomSelectItem value="Blue">
<CustomSelectControlV2.Item value="Blue">
<span style={ { color: 'blue' } }>Blue</span>
</CustomSelectItem>
<CustomSelectItem value="Purple">
</CustomSelectControlV2.Item>
<CustomSelectControlV2.Item value="Purple">
<span style={ { color: 'purple' } }>Purple</span>
</CustomSelectItem>
<CustomSelectItem value="Pink">
</CustomSelectControlV2.Item>
<CustomSelectControlV2.Item value="Pink">
<span style={ { color: 'deeppink' } }>Pink</span>
</CustomSelectItem>
</CustomSelectControlV2.Item>
</>
),
};
Expand All @@ -100,9 +99,9 @@ MultipleSelection.args = {
'maroon',
'tangerine',
].map( ( item ) => (
<CustomSelectItem key={ item } value={ item }>
<CustomSelectControlV2.Item key={ item } value={ item }>
{ item }
</CustomSelectItem>
</CustomSelectControlV2.Item>
) ) }
</>
),
Expand Down Expand Up @@ -134,9 +133,9 @@ CustomSelectedValue.args = {
<>
{ [ 'mystery-person', 'identicon', 'wavatar', 'retro' ].map(
( option ) => (
<CustomSelectItem key={ option } value={ option }>
<CustomSelectControlV2.Item key={ option } value={ option }>
{ renderItem( option ) }
</CustomSelectItem>
</CustomSelectControlV2.Item>
)
) }
</>
Expand Down
Expand Up @@ -11,12 +11,12 @@ import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import CustomSelect from '../legacy-component';
import CustomSelectControl from '../legacy-component';
import * as V1Story from '../../custom-select-control/stories/index.story';

const meta: Meta< typeof CustomSelect > = {
const meta: Meta< typeof CustomSelectControl > = {
title: 'Components (Experimental)/CustomSelectControl v2/Legacy',
component: CustomSelect,
component: CustomSelectControl,
argTypes: {
onChange: { control: { type: null } },
value: { control: { type: null } },
Expand All @@ -43,17 +43,23 @@ const meta: Meta< typeof CustomSelect > = {
};
export default meta;

const Template: StoryFn< typeof CustomSelect > = ( props ) => {
const Template: StoryFn< typeof CustomSelectControl > = ( props ) => {
const [ value, setValue ] = useState( props.options[ 0 ] );

const onChange: React.ComponentProps<
typeof CustomSelect
typeof CustomSelectControl
>[ 'onChange' ] = ( changeObject ) => {
setValue( changeObject.selectedItem );
props.onChange?.( changeObject );
};

return <CustomSelect { ...props } onChange={ onChange } value={ value } />;
return (
<CustomSelectControl
{ ...props }
onChange={ onChange }
value={ value }
/>
);
};

export const Default = Template.bind( {} );
Expand Down