Merge pull request woocommerce/woocommerce-admin#1081 from woocommerce/add/currency-input-component

Add `TextControlWithAffixes` component
This commit is contained in:
Jeff Stieler 2018-12-13 15:58:42 -07:00 committed by GitHub
commit 7cf157077a
9 changed files with 311 additions and 0 deletions

View File

@ -23,5 +23,6 @@
{ "component": "Summary", "render": "MySummaryList" },
{ "component": "Table" },
{ "component": "Tag" },
{ "component": "TextControlWithAffixes" },
{ "component": "ViewMoreList" }
]

View File

@ -27,4 +27,5 @@
* [Summary](components/summary.md)
* [Table](components/table.md)
* [Tag](components/tag.md)
* [TextControlWithAffixes](components/text-control-with-affixes.md)
* [ViewMoreList](components/view-more-list.md)

View File

@ -65,6 +65,13 @@ It will display `TablePlaceholder` component instead of `Table` if that's the ca
A function which returns a callback function to update the query string for a given `param`.
### `onColumnsChange`
- Type: Function
- Default: `noop`
A function which returns a callback function which is called upon the user changing the visiblity of columns.
### `downloadable`
- Type: Boolean

View File

@ -0,0 +1,69 @@
`TextControlWithAffixes` (component)
====================================
This component is essentially a wrapper (really a reimplementation) around the
TextControl component that adds support for affixes, i.e. the ability to display
a fixed part either at the beginning or at the end of the text input.
Props
-----
### `label`
- Type: String
- Default: null
If this property is added, a label will be generated using label property as the content.
### `help`
- Type: String
- Default: null
If this property is added, a help text will be generated using help property as the content.
### `type`
- Type: String
- Default: `'text'`
Type of the input element to render. Defaults to "text".
### `value`
- **Required**
- Type: String
- Default: null
The current value of the input.
### `className`
- Type: String
- Default: null
The class that will be added with "components-base-control" to the classes of the wrapper div.
If no className is passed only components-base-control is used.
### `onChange`
- **Required**
- Type: Function
- Default: null
A function that receives the value of the input.
### `prefix`
- Type: ReactNode
- Default: null
Markup to be inserted at the beginning of the input.
### `suffix`
- Type: ReactNode
- Default: null
Markup to be appended at the end of the input.

View File

@ -45,5 +45,6 @@ export { default as EmptyTable } from './table/empty';
export { default as TablePlaceholder } from './table/placeholder';
export { default as TableSummary } from './table/summary';
export { default as Tag } from './tag';
export { default as TextControlWithAffixes } from './text-control-with-affixes';
export { default as useFilters } from './higher-order/use-filters';
export { default as ViewMoreList } from './view-more-list';

View File

@ -28,4 +28,5 @@
@import 'summary/style.scss';
@import 'table/style.scss';
@import 'tag/style.scss';
@import 'text-control-with-affixes/style.scss';
@import 'view-more-list/style.scss';

View File

@ -0,0 +1,46 @@
```jsx
import { TextControlWithAffixes } from '@woocommerce/components';
const MyTextControlWithAffixes = withState( {
first: '',
second: '',
third: '',
fourth: '',
fifth: '',
} )( ( { first, second, third, fourth, fifth, setState } ) => (
<div>
<TextControlWithAffixes
label="Text field without affixes"
value={ first }
placeholder="Placeholder"
onChange={ value => setState( { first: value } ) }
/>
<TextControlWithAffixes
prefix="$"
label="Text field with a prefix"
value={ second }
onChange={ value => setState( { second: value } ) }
/>
<TextControlWithAffixes
prefix="Prefix"
suffix="Suffix"
label="Text field with both affixes"
value={ third }
onChange={ value => setState( { third: value } ) }
/>
<TextControlWithAffixes
suffix="%"
label="Text field with a suffix"
value={ fourth }
onChange={ value => setState( { fourth: value } ) }
/>
<TextControlWithAffixes
prefix="$"
label="Text field with prefix and help text"
value={ fifth }
onChange={ value => setState( { fifth: value } ) }
help="This is some help text."
/>
</div>
) );
```

View File

@ -0,0 +1,119 @@
/** @format */
/**
* External dependencies
*/
import { Component } from '@wordpress/element';
import PropTypes from 'prop-types';
import { BaseControl } from '@wordpress/components';
import { withInstanceId } from '@wordpress/compose';
/**
* This component is essentially a wrapper (really a reimplementation) around the
* TextControl component that adds support for affixes, i.e. the ability to display
* a fixed part either at the beginning or at the end of the text input.
*/
class TextControlWithAffixes extends Component {
render() {
const {
label,
value,
help,
className,
instanceId,
onChange,
prefix,
suffix,
type,
...props
} = this.props;
const id = `inspector-text-control-with-affixes-${ instanceId }`;
const onChangeValue = ( event ) => onChange( event.target.value );
const describedby = [];
if ( help ) {
describedby.push( `${ id }__help` );
}
if ( prefix ) {
describedby.push( `${ id }__prefix` );
}
if ( suffix ) {
describedby.push( `${ id }__suffix` );
}
return (
<BaseControl label={ label } id={ id } help={ help } className={ className }>
<div className="text-control-with-affixes">
{ prefix && (
<span
id={ `${ id }__prefix` }
className="text-control-with-affixes__prefix"
>
{ prefix }
</span>
) }
<input
className="components-text-control__input"
type={ type }
id={ id }
value={ value }
onChange={ onChangeValue }
aria-describedby={ describedby.join( ' ' ) }
{ ...props }
/>
{ suffix && (
<span
id={ `${ id }__suffix` }
className="text-control-with-affixes__suffix"
>
{ suffix }
</span>
) }
</div>
</BaseControl>
);
}
}
TextControlWithAffixes.defaultProps = {
type: 'text',
};
TextControlWithAffixes.propTypes = {
/**
* If this property is added, a label will be generated using label property as the content.
*/
label: PropTypes.string,
/**
* If this property is added, a help text will be generated using help property as the content.
*/
help: PropTypes.string,
/**
* Type of the input element to render. Defaults to "text".
*/
type: PropTypes.string,
/**
* The current value of the input.
*/
value: PropTypes.string.isRequired,
/**
* The class that will be added with "components-base-control" to the classes of the wrapper div.
* If no className is passed only components-base-control is used.
*/
className: PropTypes.string,
/**
* A function that receives the value of the input.
*/
onChange: PropTypes.func.isRequired,
/**
* Markup to be inserted at the beginning of the input.
*/
prefix: PropTypes.node,
/**
* Markup to be appended at the end of the input.
*/
suffix: PropTypes.node,
};
export default withInstanceId( TextControlWithAffixes );

View File

@ -0,0 +1,66 @@
.text-control-with-affixes {
display: inline-flex;
flex-direction: row;
width: 100%;
:nth-child(1) {
border-top-right-radius: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
:nth-last-child(1) {
border-bottom-left-radius: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
input[type='email'],
input[type='password'],
input[type='url'],
input[type='text'],
input[type='number'] {
flex-grow: 1;
margin: 0;
&:disabled {
border-right-width: 0;
& + .text-control-with-affixes__suffix {
border-left: 1px solid $core-grey-light-500;
}
}
}
}
.text-control-with-affixes__prefix,
.text-control-with-affixes__suffix {
position: relative;
background: $white;
border: 1px solid $core-grey-light-500;
color: $gray-text;
padding: 7px 14px;
white-space: nowrap;
flex: 1 0 auto;
font-size: 14px;
line-height: 1.5;
}
.text-control-with-affixes__prefix {
border-right: none;
& + input[type='email'],
& + input[type='password'],
& + input[type='url'],
& + input[type='text'],
& + input[type='number'] {
&:disabled {
border-left-color: $core-grey-light-500;
border-right-width: 1px;
}
}
}
.text-control-with-affixes__suffix {
border-left: none;
}