Skip to content

Commit

Permalink
SelectControl: Finish typescript migration (#40737)
Browse files Browse the repository at this point in the history
* SelectControl: Move types

* Inherit types from underlying components

* Add named export

* Convert stories to TS and Controls

* Convert tests

* Add changelog

* Simplify size prop types

Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com>

* Use `text` control for prefix/suffix props

Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com>
  • Loading branch information
mirka and ciampo authored May 2, 2022
1 parent dd1bef2 commit ac5dd04
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 150 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- `InputControl`: Add `__next36pxDefaultSize` flag for larger default size ([#40622](https://github.com/WordPress/gutenberg/pull/40622)).
- `UnitControl`: Add `__next36pxDefaultSize` flag for larger default size ([#40627](https://github.com/WordPress/gutenberg/pull/40627)).
- `SelectControl`: Improved TypeScript support ([#40737](https://github.com/WordPress/gutenberg/pull/40737)).
- `ToggleControlGroup`: Switch to internal `Icon` component for dashicon support ([40717](https://github.com/WordPress/gutenberg/pull/40717)).

### Internal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DimensionControl rendering renders with custom sizes 1`] = `
<ForwardRef(SelectControl)
<ForwardRef(UnforwardedSelectControl)
className="block-editor-dimension-control"
hideLabelFromVision={false}
label={
Expand Down Expand Up @@ -34,7 +34,7 @@ exports[`DimensionControl rendering renders with custom sizes 1`] = `
`;

exports[`DimensionControl rendering renders with defaults 1`] = `
<ForwardRef(SelectControl)
<ForwardRef(UnforwardedSelectControl)
className="block-editor-dimension-control"
hideLabelFromVision={false}
label={
Expand Down Expand Up @@ -75,7 +75,7 @@ exports[`DimensionControl rendering renders with defaults 1`] = `
`;

exports[`DimensionControl rendering renders with icon and custom icon label 1`] = `
<ForwardRef(SelectControl)
<ForwardRef(UnforwardedSelectControl)
className="block-editor-dimension-control"
hideLabelFromVision={false}
label={
Expand Down Expand Up @@ -128,7 +128,7 @@ exports[`DimensionControl rendering renders with icon and custom icon label 1`]
`;

exports[`DimensionControl rendering renders with icon and default icon label 1`] = `
<ForwardRef(SelectControl)
<ForwardRef(UnforwardedSelectControl)
className="block-editor-dimension-control"
hideLabelFromVision={false}
label={
Expand Down
4 changes: 2 additions & 2 deletions packages/components/src/select-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Render a user interface to select multiple users from a list.
this.setState( { users } );
} }
options={ [
{ value: null, label: 'Select a User', disabled: true },
{ value: '', label: 'Select a User', disabled: true },
{ value: 'a', label: 'User A' },
{ value: 'b', label: 'User B' },
{ value: 'c', label: 'User c' },
Expand Down Expand Up @@ -194,7 +194,7 @@ If this property is added, multiple values can be selected. The value passed sho
An array of objects containing the following properties:

- `label`: (string) The label to be shown to the user.
- `value`: (Object) The internal value used to choose the selected value. This is also the value passed to onChange when the option is selected.
- `value`: (string) The internal value used to choose the selected value. This is also the value passed to onChange when the option is selected.
- `disabled`: (boolean) Whether or not the option should have the disabled attribute.
- Type: `Array`
- Required: No
Expand Down
59 changes: 30 additions & 29 deletions packages/components/src/select-control/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { isEmpty, noop } from 'lodash';
import classNames from 'classnames';
import type { ChangeEvent, FocusEvent, ReactNode, ForwardedRef } from 'react';
import type { ChangeEvent, FocusEvent, ForwardedRef } from 'react';

/**
* WordPress dependencies
Expand All @@ -17,10 +17,9 @@ import { Icon, chevronDown } from '@wordpress/icons';
*/
import BaseControl from '../base-control';
import InputBase from '../input-control/input-base';
import type { InputBaseProps, LabelPosition } from '../input-control/types';
import { Select, DownArrowWrapper } from './styles/select-control-styles';
import type { Size } from './types';
import type { WordPressComponentProps } from '../ui/context';
import type { SelectControlProps } from './types';

function useUniqueId( idProp?: string ) {
const instanceId = useInstanceId( SelectControl );
Expand All @@ -29,30 +28,7 @@ function useUniqueId( idProp?: string ) {
return idProp || id;
}

export interface SelectControlProps
extends Omit< InputBaseProps, 'children' | 'isFocused' > {
help?: string;
hideLabelFromVision?: boolean;
multiple?: boolean;
onBlur?: ( event: FocusEvent< HTMLSelectElement > ) => void;
onFocus?: ( event: FocusEvent< HTMLSelectElement > ) => void;
onChange?: (
value: string | string[],
extra?: { event?: ChangeEvent< HTMLSelectElement > }
) => void;
options?: {
label: string;
value: string;
id?: string;
disabled?: boolean;
}[];
size?: Size;
value?: string | string[];
labelPosition?: LabelPosition;
children?: ReactNode;
}

function SelectControl(
function UnforwardedSelectControl(
{
className,
disabled = false,
Expand Down Expand Up @@ -165,6 +141,31 @@ function SelectControl(
/* eslint-enable jsx-a11y/no-onchange */
}

const ForwardedComponent = forwardRef( SelectControl );
/**
* `SelectControl` allows users to select from a single or multiple option menu.
* It functions as a wrapper around the browser's native `<select>` element.
*
* @example
* import { SelectControl } from '@wordpress/components';
* import { useState } from '@wordpress/element';
*
* const MySelectControl = () => {
* const [ size, setSize ] = useState( '50%' );
*
* return (
* <SelectControl
* label="Size"
* value={ size }
* options={ [
* { label: 'Big', value: '100%' },
* { label: 'Medium', value: '50%' },
* { label: 'Small', value: '25%' },
* ] }
* onChange={ setSize }
* />
* );
* };
*/
export const SelectControl = forwardRef( UnforwardedSelectControl );

export default ForwardedComponent;
export default SelectControl;
104 changes: 0 additions & 104 deletions packages/components/src/select-control/stories/index.js

This file was deleted.

90 changes: 90 additions & 0 deletions packages/components/src/select-control/stories/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* External dependencies
*/
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import type { ComponentProps } from 'react';

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

/**
* Internal dependencies
*/
import SelectControl from '..';

const meta: ComponentMeta< typeof SelectControl > = {
title: 'Components/SelectControl',
component: SelectControl,
argTypes: {
help: { control: { type: 'text' } },
label: { control: { type: 'text' } },
prefix: { control: { type: 'text' } },
suffix: { control: { type: 'text' } },
value: { control: { type: null } },
},
parameters: {
controls: { expanded: true },
docs: { source: { state: 'open' } },
},
};
export default meta;

const SelectControlWithState: ComponentStory< typeof SelectControl > = (
args
) => {
const [ selection, setSelection ] = useState<
ComponentProps< typeof SelectControl >[ 'value' ]
>();

return (
<SelectControl
{ ...args }
value={ selection }
onChange={ setSelection }
/>
);
};

export const Default = SelectControlWithState.bind( {} );
Default.args = {
options: [
{ value: '', label: 'Select an Option', disabled: true },
{ value: 'a', label: 'Option A' },
{ value: 'b', label: 'Option B' },
{ value: 'c', label: 'Option C' },
],
};

export const WithLabelAndHelpText = SelectControlWithState.bind( {} );
WithLabelAndHelpText.args = {
...Default.args,
help: 'Help text to explain the select control.',
label: 'Value',
};

/**
* As an alternative to the `options` prop, `optgroup`s and `options` can be
* passed in as `children` for more customizability.
*/
export const WithCustomChildren: ComponentStory< typeof SelectControl > = (
args
) => {
return (
<SelectControlWithState { ...args }>
<option value="option-1">Option 1</option>
<option value="option-2" disabled>
Option 2 - Disabled
</option>
<optgroup label="Option Group 1">
<option value="option-group-1-option-1">
Option Group 1 - Option 1
</option>
<option value="option-group-1-option-2" disabled>
Option Group 1 - Option 2 - Disabled
</option>
</optgroup>
</SelectControlWithState>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import styled from '@emotion/styled';
* Internal dependencies
*/
import { COLORS, rtl } from '../../utils';
import type { Size } from '../types';
import type { SelectControlProps } from '../types';

interface SelectProps {
disabled?: boolean;
selectSize?: Size;
interface SelectProps extends Pick< SelectControlProps, 'disabled' > {
// Using `selectSize` instead of `size` to avoid a type conflict with the
// `size` HTML attribute of the `select` element.
selectSize?: SelectControlProps[ 'size' ];
}

const disabledStyles = ( { disabled }: SelectProps ) => {
Expand All @@ -23,14 +24,14 @@ const disabledStyles = ( { disabled }: SelectProps ) => {
} );
};

const fontSizeStyles = ( { selectSize }: SelectProps ) => {
const fontSizeStyles = ( { selectSize = 'default' }: SelectProps ) => {
const sizes = {
default: '13px',
small: '11px',
'__unstable-large': '13px',
};

const fontSize = sizes[ selectSize as Size ];
const fontSize = sizes[ selectSize ];
const fontSizeMobile = '16px';

if ( ! fontSize ) return '';
Expand All @@ -44,7 +45,7 @@ const fontSizeStyles = ( { selectSize }: SelectProps ) => {
`;
};

const sizeStyles = ( { selectSize }: SelectProps ) => {
const sizeStyles = ( { selectSize = 'default' }: SelectProps ) => {
const sizes = {
default: {
height: 30,
Expand All @@ -63,7 +64,7 @@ const sizeStyles = ( { selectSize }: SelectProps ) => {
},
};

const style = sizes[ selectSize as Size ] || sizes.default;
const style = sizes[ selectSize ];

return css( style );
};
Expand Down
Loading

0 comments on commit ac5dd04

Please sign in to comment.