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

CheckboxControl: Convert component to TypeScript #40915

Merged
merged 9 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

- `DateTimePicker`: Convert to TypeScript ([#40775](https://github.com/WordPress/gutenberg/pull/40775)).
- `DateTimePicker`: Convert unit tests to TypeScript ([#40957](https://github.com/WordPress/gutenberg/pull/40957)).
- `CheckboxControl`: Convert to TypeScript ([#40915](https://github.com/WordPress/gutenberg/pull/40915)).

## 19.10.0 (2022-05-04)

Expand All @@ -28,15 +29,15 @@
- `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)).
- Improve `ToolsPanel` accessibility. ([#40716](https://github.com/WordPress/gutenberg/pull/40716))
- Improve `ToolsPanel` accessibility. ([#40716](https://github.com/WordPress/gutenberg/pull/40716))

### Bug Fix

- The `Button` component now displays the label as the tooltip for icon only buttons. ([#40716](https://github.com/WordPress/gutenberg/pull/40716))
- Use fake timers and fix usage of async methods from `@testing-library/user-event`. ([#40790](https://github.com/WordPress/gutenberg/pull/40790))
- UnitControl: avoid calling onChange callback twice when unit changes. ([#40796](https://github.com/WordPress/gutenberg/pull/40796))
- `UnitControl`: show unit label when units prop has only one unit. ([#40784](https://github.com/WordPress/gutenberg/pull/40784))
- `AnglePickerControl`: Fix closing of gradient popover when the angle control is clicked. ([#40735](https://github.com/WordPress/gutenberg/pull/40735))
- The `Button` component now displays the label as the tooltip for icon only buttons. ([#40716](https://github.com/WordPress/gutenberg/pull/40716))
- Use fake timers and fix usage of async methods from `@testing-library/user-event`. ([#40790](https://github.com/WordPress/gutenberg/pull/40790))
- UnitControl: avoid calling onChange callback twice when unit changes. ([#40796](https://github.com/WordPress/gutenberg/pull/40796))
- `UnitControl`: show unit label when units prop has only one unit. ([#40784](https://github.com/WordPress/gutenberg/pull/40784))
- `AnglePickerControl`: Fix closing of gradient popover when the angle control is clicked. ([#40735](https://github.com/WordPress/gutenberg/pull/40735))

### Internal

Expand Down
18 changes: 10 additions & 8 deletions packages/components/src/checkbox-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,37 +77,39 @@ const MyCheckboxControl = () => {
The set of props accepted by the component will be specified below.
Props not included in this set will be applied to the input element.

#### label
#### `label`: `string`

A label for the input field, that appears at the side of the checkbox.
The prop will be rendered as content a label element.
If no prop is passed an empty label is rendered.

- Type: `String`
- Required: No

#### help
#### `help`: `string|WPElement`

If this property is added, a help text will be generated using help property as the content.

- Type: `String|WPElement`
- Required: No

#### checked
#### `checked`: `boolean`

If checked is true the checkbox will be checked. If checked is false the checkbox will be unchecked.
If no value is passed the checkbox will be unchecked.

- Type: `Boolean`
- Required: No

#### onChange
#### `onChange`: `function`

A function that receives the checked state (boolean) as input.

- Type: `function`
- Required: Yes

#### `indeterminate`: `boolean`

If indeterminate is true the state of the checkbox will be indeterminate.

- Required: No

## Related components

- To select one option from a set, and you want to show all the available options at once, use the `RadioControl` component.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import classnames from 'classnames';
import type { ChangeEvent } from 'react';

/**
* WordPress dependencies
Expand All @@ -15,17 +16,43 @@ import { Icon, check, reset } from '@wordpress/icons';
* Internal dependencies
*/
import BaseControl from '../base-control';
import type { CheckboxControlProps } from './types';
import type { WordPressComponentProps } from '../ui/context';

/**
* Checkboxes allow the user to select one or more items from a set.
*
walbo marked this conversation as resolved.
Show resolved Hide resolved
* ```jsx
* import { CheckboxControl } from '@wordpress/components';
* import { useState } from '@wordpress/element';
*
* const MyCheckboxControl = () => {
* const [ isChecked, setChecked ] = useState( true );
* return (
* <CheckboxControl
* label="Is author"
* help="Is the user a author or not?"
* checked={ isChecked }
* onChange={ setChecked }
* />
* );
* };
* ```
*/
export function CheckboxControl(
props: WordPressComponentProps< CheckboxControlProps, 'input', false >
walbo marked this conversation as resolved.
Show resolved Hide resolved
) {
const {
label,
className,
heading,
checked,
indeterminate,
help,
onChange,
...additionalProps
} = props;

export default function CheckboxControl( {
label,
className,
heading,
checked,
indeterminate,
help,
onChange,
...props
} ) {
if ( heading ) {
deprecated( '`heading` prop in `CheckboxControl`', {
alternative: 'a separate element to implement a heading',
Expand All @@ -40,7 +67,7 @@ export default function CheckboxControl( {

// Run the following callback everytime the `ref` (and the additional
walbo marked this conversation as resolved.
Show resolved Hide resolved
// dependencies) change.
const ref = useRefEffect(
const ref = useRefEffect< HTMLInputElement >(
( node ) => {
if ( ! node ) {
return;
Expand All @@ -56,7 +83,8 @@ export default function CheckboxControl( {
);
const instanceId = useInstanceId( CheckboxControl );
const id = `inspector-checkbox-control-${ instanceId }`;
const onChangeValue = ( event ) => onChange( event.target.checked );
const onChangeValue = ( event: ChangeEvent< HTMLInputElement > ) =>
onChange( event.target.checked );

return (
<BaseControl
Expand All @@ -75,7 +103,7 @@ export default function CheckboxControl( {
onChange={ onChangeValue }
checked={ checked }
aria-describedby={ !! help ? id + '__help' : undefined }
{ ...props }
{ ...additionalProps }
/>
{ showIndeterminateIcon ? (
<Icon
Expand All @@ -101,3 +129,5 @@ export default function CheckboxControl( {
</BaseControl>
);
}

export default CheckboxControl;
91 changes: 0 additions & 91 deletions packages/components/src/checkbox-control/stories/index.js

This file was deleted.

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

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

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

const meta: ComponentMeta< typeof CheckboxControl > = {
component: CheckboxControl,
title: 'Components/CheckboxControl',
argTypes: {
onChange: {
action: 'onChange',
},
checked: {
control: { type: null },
},
help: { control: { type: 'text' } },
},
parameters: {
controls: {
expanded: true,
walbo marked this conversation as resolved.
Show resolved Hide resolved
},
docs: { source: { state: 'open' } },
},
};
export default meta;

const DefaultTemplate: ComponentStory< typeof CheckboxControl > = ( {
onChange,
...args
} ) => {
const [ isChecked, setChecked ] = useState( true );

return (
<CheckboxControl
{ ...args }
checked={ isChecked }
onChange={ ( v ) => {
setChecked( v );
onChange( v );
} }
/>
);
};

export const Default: ComponentStory<
typeof CheckboxControl
> = DefaultTemplate.bind( {} );
Default.args = {};

export const WithLabel: ComponentStory<
typeof CheckboxControl
> = DefaultTemplate.bind( {} );
WithLabel.args = {
...Default.args,
label: 'Is author',
};

export const WithLabelAndHelpText: ComponentStory<
typeof CheckboxControl
> = DefaultTemplate.bind( {} );
WithLabelAndHelpText.args = {
...Default.args,
label: 'Is author',
help: 'Is the user an author or not?',
};

export const Indeterminate: ComponentStory< typeof CheckboxControl > = ( {
onChange,
...args
} ) => {
const [ fruits, setFruits ] = useState( { apple: false, orange: false } );

const isAllChecked = Object.values( fruits ).every( Boolean );
const isIndeterminate =
Object.values( fruits ).some( Boolean ) && ! isAllChecked;

return (
<>
<CheckboxControl
{ ...args }
checked={ isAllChecked }
indeterminate={ isIndeterminate }
onChange={ ( v ) => {
setFruits( {
apple: v,
orange: v,
} );
onChange( v );
} }
/>
<CheckboxControl
label="Apple"
checked={ fruits.apple }
onChange={ ( apple ) =>
setFruits( ( prevState ) => ( {
...prevState,
apple,
} ) )
}
/>
<CheckboxControl
label="Orange"
checked={ fruits.orange }
onChange={ ( orange ) =>
setFruits( ( prevState ) => ( {
...prevState,
orange,
} ) )
}
/>
</>
);
};
Indeterminate.args = {
label: 'Select all',
};
Loading