Implement "Simple" Select Component (https://github.com/woocommerce/woocommerce-admin/pull/2598)
* Implement "Simple" Select Component * Add value prop and disabled option. * Expand click target of .woocommerce-simple-select-control__selector * Fix select state. * remove left position Co-Authored-By: Joshua T Flowers <joshuatf@gmail.com> * Update packages/components/src/simple-select-control/style.scss Co-Authored-By: Joshua T Flowers <joshuatf@gmail.com> * Update packages/components/src/simple-select-control/style.scss Co-Authored-By: Joshua T Flowers <joshuatf@gmail.com> * Update packages/components/src/simple-select-control/style.scss Co-Authored-By: Joshua T Flowers <joshuatf@gmail.com>
This commit is contained in:
parent
2a75bcefe6
commit
4e478d9fbb
|
@ -6,7 +6,7 @@ import { __, _x, sprintf } from '@wordpress/i18n';
|
|||
import { Component, Fragment } from '@wordpress/element';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { FormToggle } from '@wordpress/components';
|
||||
import { Button, SelectControl } from 'newspack-components';
|
||||
import { Button } from 'newspack-components';
|
||||
import { withDispatch } from '@wordpress/data';
|
||||
import { keys, pickBy } from 'lodash';
|
||||
|
||||
|
@ -18,7 +18,7 @@ import { numberFormat } from '@woocommerce/number';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { H, Card } from '@woocommerce/components';
|
||||
import { H, Card, SimpleSelectControl } from '@woocommerce/components';
|
||||
import withSelect from 'wc-api/with-select';
|
||||
import { recordEvent } from 'lib/tracks';
|
||||
|
||||
|
@ -294,7 +294,7 @@ class BusinessDetails extends Component {
|
|||
<p>{ __( 'Tell us about the business' ) }</p>
|
||||
|
||||
<Card>
|
||||
<SelectControl
|
||||
<SimpleSelectControl
|
||||
label={ __( 'How many products will you add?', 'woocommerce-admin' ) }
|
||||
onChange={ value => this.updateValue( 'product_count', value ) }
|
||||
onFocus={ this.setDefaultValue.bind( this, 'product_count', productCountOptions ) }
|
||||
|
@ -305,7 +305,7 @@ class BusinessDetails extends Component {
|
|||
required
|
||||
/>
|
||||
|
||||
<SelectControl
|
||||
<SimpleSelectControl
|
||||
label={ __( 'Currently selling elsewhere?', 'woocommerce-admin' ) }
|
||||
onChange={ value => this.updateValue( 'selling_venues', value ) }
|
||||
onFocus={ this.setDefaultValue.bind( this, 'selling_venues', sellingVenueOptions ) }
|
||||
|
@ -317,7 +317,7 @@ class BusinessDetails extends Component {
|
|||
/>
|
||||
|
||||
{ [ 'other', 'brick-mortar-other' ].includes( selling_venues ) && (
|
||||
<SelectControl
|
||||
<SimpleSelectControl
|
||||
label={ __( 'Which platform is the store using?', 'woocommerce-admin' ) }
|
||||
onChange={ value => this.updateValue( 'other_platform', value ) }
|
||||
onFocus={ this.setDefaultValue.bind( this, 'other_platform', otherPlatformOptions ) }
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
color: $muriel-gray-600;
|
||||
text-align: left;
|
||||
|
||||
button {
|
||||
.muriel-button {
|
||||
display: flex;
|
||||
margin: $gap-smaller auto 0;
|
||||
min-width: 310px;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
{ "component": "SearchListControl" },
|
||||
{ "component": "Section" },
|
||||
{ "component": "SegmentedSelection" },
|
||||
{ "component": "SimpleSelectControl" },
|
||||
{ "component": "Spinner" },
|
||||
{ "component": "SplitButton" },
|
||||
{ "component": "Stepper" },
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
* [SectionHeader](components/packages/section-header.md)
|
||||
* [Section](components/packages/section.md)
|
||||
* [SegmentedSelection](components/packages/segmented-selection.md)
|
||||
* [SimpleSelectControl](components/packages/simple-select-control.md)
|
||||
* [Spinner](components/packages/spinner.md)
|
||||
* [SplitButton](components/packages/split-button.md)
|
||||
* [Stepper](components/packages/stepper.md)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
`SimpleSelectControl` (component)
|
||||
=================================
|
||||
|
||||
A component for displaying a material styled 'simple' select control.
|
||||
|
||||
Props
|
||||
-----
|
||||
|
||||
### `className`
|
||||
|
||||
- Type: String
|
||||
- Default: null
|
||||
|
||||
Additional class name to style the component.
|
||||
|
||||
### `label`
|
||||
|
||||
- Type: String
|
||||
- Default: null
|
||||
|
||||
A label to use for the main select element.
|
||||
|
||||
### `options`
|
||||
|
||||
- Type: Array
|
||||
- value: String - Input value for this option.
|
||||
- label: String - Label for this option.
|
||||
- disabled: Boolean - Disable this option in the list.
|
||||
- Default: null
|
||||
|
||||
An array of options to use for the dropddown.
|
||||
|
||||
### `onChange`
|
||||
|
||||
- Type: Function
|
||||
- Default: null
|
||||
|
||||
A function that receives the value of the new option that is being selected as input.
|
||||
|
||||
### `value`
|
||||
|
||||
- Type: String
|
||||
- Default: null
|
||||
|
||||
The selected value for the control.
|
|
@ -4,6 +4,7 @@
|
|||
- TableCard component: add `onSearch` an `onSort` function props.
|
||||
- Add new component `<List />` for displaying interactive list items.
|
||||
- Fix z-index issue in `<Chart />` empty message.
|
||||
- Added a new `<SimpleSelectControl />` component.
|
||||
|
||||
# 3.1.0
|
||||
- Added support for a countLabel prop on SearchListItem to allow custom counts.
|
||||
|
|
|
@ -40,6 +40,7 @@ export { default as SearchListControl } from './search-list-control';
|
|||
export { default as SearchListItem } from './search-list-control/item';
|
||||
export { default as SectionHeader } from './section-header';
|
||||
export { default as SegmentedSelection } from './segmented-selection';
|
||||
export { default as SimpleSelectControl } from './simple-select-control';
|
||||
export { default as SplitButton } from './split-button';
|
||||
export { default as Spinner } from './spinner';
|
||||
export { default as Stepper } from './stepper';
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
```jsx
|
||||
import { SimpleSelectControl } from '@woocommerce/components';
|
||||
|
||||
class MySimpleSelectControl extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
pet: '',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { pet } = this.state;
|
||||
|
||||
const petOptions = [
|
||||
{
|
||||
value: 'cat',
|
||||
label: 'Cat',
|
||||
},
|
||||
{
|
||||
value: 'dog',
|
||||
label: 'Dog',
|
||||
},
|
||||
{
|
||||
value: 'bunny',
|
||||
label: 'Bunny',
|
||||
},
|
||||
{
|
||||
value: 'snake',
|
||||
label: 'Snake',
|
||||
},
|
||||
{
|
||||
value: 'chinchilla',
|
||||
label: 'Chinchilla',
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SimpleSelectControl
|
||||
label="What is your favorite pet?"
|
||||
onChange={ value => this.setState( { pet: value } ) }
|
||||
options={ petOptions }
|
||||
value={ pet }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,176 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Dropdown, Button, NavigableMenu, withFocusOutside } from '@wordpress/components';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { map, find } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* A component for displaying a material styled 'simple' select control.
|
||||
*/
|
||||
class SimpleSelectControl extends Component {
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
this.state = {
|
||||
currentValue: props.value,
|
||||
isFocused: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate( prevProps ) {
|
||||
if (
|
||||
this.props.value !== prevProps.value &&
|
||||
this.state.currentValue !== this.props.value
|
||||
) {
|
||||
/* eslint-disable react/no-did-update-set-state */
|
||||
this.setState( {
|
||||
currentValue: this.props.value,
|
||||
} );
|
||||
/* eslint-enable react/no-did-update-set-state */
|
||||
}
|
||||
}
|
||||
|
||||
handleFocusOutside() {
|
||||
this.setState( { isFocused: false } );
|
||||
}
|
||||
|
||||
handleOnClick( onToggle ) {
|
||||
this.setState( { isFocused: true } );
|
||||
if ( 'function' === typeof onToggle ) {
|
||||
onToggle();
|
||||
}
|
||||
const { onClick } = this.props;
|
||||
if ( 'function' === typeof onClick ) {
|
||||
onClick();
|
||||
}
|
||||
}
|
||||
|
||||
handleOnFocus() {
|
||||
this.setState( { isFocused: true } );
|
||||
const { onFocus } = this.props;
|
||||
if ( 'function' === typeof onFocus ) {
|
||||
onFocus();
|
||||
}
|
||||
}
|
||||
|
||||
onChange( value ) {
|
||||
this.props.onChange( value );
|
||||
this.setState( { currentValue: value } );
|
||||
}
|
||||
|
||||
render() {
|
||||
const { options, label, className } = this.props;
|
||||
const { currentValue, isFocused } = this.state;
|
||||
const onChange = ( value ) => {
|
||||
this.onChange( value );
|
||||
this.handleFocusOutside();
|
||||
};
|
||||
|
||||
const isEmpty = currentValue === '' || currentValue === null;
|
||||
const currentOption = find( options, ( { value } ) => value === currentValue );
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
className={ classNames(
|
||||
'woocommerce-simple-select-control__dropdown',
|
||||
'components-base-control',
|
||||
className,
|
||||
{
|
||||
'is-empty': isEmpty,
|
||||
'has-value': ! isEmpty,
|
||||
'is-active': isFocused,
|
||||
}
|
||||
) }
|
||||
contentClassName="woocommerce-simple-select-control__dropdown-content"
|
||||
position="center"
|
||||
renderToggle={ ( { isOpen, onToggle } ) => (
|
||||
<Button
|
||||
className="woocommerce-simple-select-control__selector"
|
||||
onClick={ () => this.handleOnClick( onToggle ) }
|
||||
onFocus={ () => this.handleOnFocus() }
|
||||
aria-expanded={ isOpen }
|
||||
aria-label={ ! isEmpty ? sprintf(
|
||||
/* translators: Label: Current Value for a Select Dropddown */
|
||||
__( '%s: %s' ), label, currentOption && currentOption.label
|
||||
) : label }
|
||||
>
|
||||
<span className="woocommerce-simple-select-control__label">{ label }</span>
|
||||
<span className="woocommerce-simple-select-control__value">{ currentOption && currentOption.label }</span>
|
||||
</Button>
|
||||
) }
|
||||
renderContent={ ( { onClose } ) => (
|
||||
<NavigableMenu>
|
||||
{ map( options, ( option ) => {
|
||||
const optionValue = option.value;
|
||||
const optionLabel = option.label;
|
||||
const optionDisabled = option.disabled || false;
|
||||
const isSelected = ( currentValue === optionValue );
|
||||
return (
|
||||
<Button
|
||||
key={ optionValue }
|
||||
onClick={ () => {
|
||||
onChange( optionValue );
|
||||
onClose();
|
||||
} }
|
||||
className={ classNames( {
|
||||
'is-selected': isSelected,
|
||||
} ) }
|
||||
disabled={ optionDisabled }
|
||||
role="menuitemradio"
|
||||
aria-checked={ isSelected }
|
||||
>
|
||||
<span>
|
||||
{ optionLabel }
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
} ) }
|
||||
</NavigableMenu>
|
||||
) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleSelectControl.propTypes = {
|
||||
/**
|
||||
* Additional class name to style the component.
|
||||
*/
|
||||
className: PropTypes.string,
|
||||
/**
|
||||
* A label to use for the main select element.
|
||||
*/
|
||||
label: PropTypes.string,
|
||||
/**
|
||||
* An array of options to use for the dropddown.
|
||||
*/
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
/**
|
||||
* Input value for this option.
|
||||
*/
|
||||
value: PropTypes.string,
|
||||
/**
|
||||
* Label for this option.
|
||||
*/
|
||||
label: PropTypes.string,
|
||||
/**
|
||||
* Disable the option.
|
||||
*/
|
||||
disabled: PropTypes.bool,
|
||||
} )
|
||||
),
|
||||
/**
|
||||
* A function that receives the value of the new option that is being selected as input.
|
||||
*/
|
||||
onChange: PropTypes.func,
|
||||
/**
|
||||
* The currently value of the select element.
|
||||
*/
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export default withFocusOutside( SimpleSelectControl );
|
|
@ -0,0 +1,125 @@
|
|||
.woocommerce-simple-select-control__dropdown {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
border: 1px solid #b0b5b8;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
font-size: 16px;
|
||||
line-height: 54px;
|
||||
font-weight: 400;
|
||||
margin-top: $gap;
|
||||
margin-bottom: $gap;
|
||||
|
||||
.woocommerce-simple-select-control__selector {
|
||||
&.components-button {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
justify-content: unset;
|
||||
margin-left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
padding-left: $gap;
|
||||
padding-right: $gap-largest;
|
||||
|
||||
&:focus:not(:disabled) {
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
outline-offset: initial;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
float: right;
|
||||
line-height: 56px;
|
||||
font-family: dashicons, sans-serif;
|
||||
font-size: 20px;
|
||||
content: '\f140';
|
||||
z-index: 101;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin-top: 0;
|
||||
top: 0;
|
||||
right: $gap;
|
||||
bottom: 16px;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-simple-select-control__dropdown-content {
|
||||
&.components-popover.is-bottom {
|
||||
margin-top: -$gap-largest * 2;
|
||||
z-index: 999;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.components-button {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 10px 20px 10px 40px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
&.is-selected {
|
||||
background: $muriel-gray-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-empty {
|
||||
.woocommerce-simple-select-control__selector {
|
||||
&.components-button {
|
||||
font-size: 16px;
|
||||
line-height: 54px;
|
||||
height: 54px;
|
||||
z-index: 100;
|
||||
color: #636d75;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
box-shadow: 0 0 0 2px #673d99;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
&.has-value {
|
||||
.woocommerce-simple-select-control__selector {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&.components-button {
|
||||
&::after {
|
||||
bottom: 23px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-simple-select-control__label {
|
||||
margin-top: -$gap-smallest;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
margin-top: 8px;
|
||||
color: #636d75;
|
||||
}
|
||||
|
||||
.woocommerce-simple-select-control__value {
|
||||
color: #2b2d2f;
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
```jsx
|
||||
import { MySpinner } from '@woocommerce/components';
|
||||
import { Spinner } from '@woocommerce/components';
|
||||
|
||||
const MySpinner = () => (
|
||||
<div>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
@import 'search-list-control/style.scss';
|
||||
@import 'section-header/style.scss';
|
||||
@import 'segmented-selection/style.scss';
|
||||
@import 'simple-select-control/style.scss';
|
||||
@import 'split-button/style.scss';
|
||||
@import 'stepper/style.scss';
|
||||
@import 'spinner/style.scss';
|
||||
|
|
Loading…
Reference in New Issue