Merge pull request woocommerce/woocommerce-admin#1081 from woocommerce/add/currency-input-component
Add `TextControlWithAffixes` component
This commit is contained in:
commit
7cf157077a
|
@ -23,5 +23,6 @@
|
|||
{ "component": "Summary", "render": "MySummaryList" },
|
||||
{ "component": "Table" },
|
||||
{ "component": "Tag" },
|
||||
{ "component": "TextControlWithAffixes" },
|
||||
{ "component": "ViewMoreList" }
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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>
|
||||
) );
|
||||
```
|
|
@ -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 );
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue