Add prefix support to Fields

This allows Fields to have an optional prefix component which is placed inside
the border of the Field and to the left of the input. Since this label animation
would be complex to get right for this case, it is instead fixed to the top left
if there is a prefix component.

This canonical example of this today would be a phone number field which
includes a country dropdown.
This commit is contained in:
J. Ryan Stinnett 2019-03-05 15:13:12 +00:00
parent ae5c32d28b
commit 26b2aa174b
2 changed files with 27 additions and 3 deletions

View file

@ -17,6 +17,7 @@ limitations under the License.
/* TODO: Consider unifying with general input styles in _light.scss */ /* TODO: Consider unifying with general input styles in _light.scss */
.mx_Field { .mx_Field {
display: flex;
position: relative; position: relative;
margin: 1em 0; margin: 1em 0;
border-radius: 4px; border-radius: 4px;
@ -24,6 +25,10 @@ limitations under the License.
border: 1px solid $input-border-color; border: 1px solid $input-border-color;
} }
.mx_Field_prefix {
border-right: 1px solid $input-border-color;
}
.mx_Field input, .mx_Field input,
.mx_Field select, .mx_Field select,
.mx_Field textarea { .mx_Field textarea {
@ -106,7 +111,8 @@ limitations under the License.
.mx_Field input:not(:placeholder-shown) + label, .mx_Field input:not(:placeholder-shown) + label,
.mx_Field textarea:focus + label, .mx_Field textarea:focus + label,
.mx_Field textarea:not(:placeholder-shown) + label, .mx_Field textarea:not(:placeholder-shown) + label,
.mx_Field select + label /* Always show a select's label on top to not collide with the value */ { .mx_Field select + label /* Always show a select's label on top to not collide with the value */,
.mx_Field_labelAlwaysTopLeft label {
transition: transition:
font-size 0.25s ease-out 0s, font-size 0.25s ease-out 0s,
color 0.25s ease-out 0s, color 0.25s ease-out 0s,

View file

@ -16,6 +16,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames';
export default class Field extends React.PureComponent { export default class Field extends React.PureComponent {
static propTypes = { static propTypes = {
@ -30,6 +31,8 @@ export default class Field extends React.PureComponent {
label: PropTypes.string, label: PropTypes.string,
// The field's placeholder string. Defaults to the label. // The field's placeholder string. Defaults to the label.
placeholder: PropTypes.string, placeholder: PropTypes.string,
// Optional component to include inside the field before the input.
prefix: PropTypes.node,
// All other props pass through to the <input>. // All other props pass through to the <input>.
}; };
@ -46,7 +49,7 @@ export default class Field extends React.PureComponent {
} }
render() { render() {
const { element, children, ...inputProps } = this.props; const { element, prefix, children, ...inputProps } = this.props;
const inputElement = element || "input"; const inputElement = element || "input";
@ -57,7 +60,22 @@ export default class Field extends React.PureComponent {
const fieldInput = React.createElement(inputElement, inputProps, children); const fieldInput = React.createElement(inputElement, inputProps, children);
return <div className={`mx_Field mx_Field_${inputElement}`}> let prefixContainer = null;
if (prefix) {
prefixContainer = <span className="mx_Field_prefix">
{prefix}
</span>;
}
const classes = classNames("mx_Field", `mx_Field_${inputElement}`, {
// If we have a prefix element, leave the label always at the top left and
// don't animate it, as it looks a bit clunky and would add complexity to do
// properly.
mx_Field_labelAlwaysTopLeft: prefix,
});
return <div className={classes}>
{prefixContainer}
{fieldInput} {fieldInput}
<label htmlFor={this.props.id}>{this.props.label}</label> <label htmlFor={this.props.id}>{this.props.label}</label>
</div>; </div>;