Update BirthdayInputField

This commit is contained in:
Vesa Luusua 2017-07-06 12:44:33 +03:00
parent 248ba9a4c5
commit af76f42495
3 changed files with 151 additions and 54 deletions

View file

@ -1,20 +1,43 @@
@import '../../marketplace.css';
.fieldRoot {
}
.inputRoot {
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.dropdown {
.selectWrapper {
flex-basis: calc(33% - 12px);
}
.select {
display: inline-block;
flex: 1;
/* Border */
border: 1px solid #979797;
border-left-width: 0;
border-bottom-width: 2px;
border-bottom-style: solid;
border-bottom-color: var(--attentionColor);
&:first-child {
border-left-width: 1px;
&:hover,
&:focus {
border-bottom-color: var(--matterColorDark);
outline: none;
}
}
.notSet {
color: var(--matterColorAnti);
}
.selectSuccess {
color: var(--matterColor);
border-bottom-color: var(--successColor);
}
.selectError {
border-bottom-color: var(--failColor);
}

View file

@ -1,8 +1,9 @@
import React, { Component, PropTypes } from 'react';
import { Field } from 'redux-form';
import { injectIntl, intlShape } from 'react-intl';
import classNames from 'classnames';
import { range } from 'lodash';
import { Select, ValidationError } from '../../components';
import { ValidationError } from '../../components';
import css from './BirthdayInputField.css';
@ -70,7 +71,7 @@ const currentYear = new Date().getFullYear();
const yearsToShow = 80;
const years = range(currentYear, currentYear - yearsToShow, -1);
class BirthdayInput extends Component {
class BirthdayInputComponent extends Component {
constructor(props) {
super(props);
this.state = {
@ -123,74 +124,144 @@ class BirthdayInput extends Component {
});
}
render() {
const { id } = this.props;
const {
selectClassName,
dateId,
monthId,
yearId,
dateLabel,
monthLabel,
yearLabel,
intl,
} = this.props;
const selectedValue = n => {
return typeof n === 'number' ? n : '';
};
const datePlaceholder = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayDatePlaceholder' });
const monthPlaceholder = intl.formatMessage({
id: 'PayoutDetailsForm.birthdayMonthPlaceholder',
});
const yearPlaceholder = intl.formatMessage({ id: 'PayoutDetailsForm.birthdayYearPlaceholder' });
return (
<div className={css.inputRoot}>
<Select
id={id}
value={selectedValue(this.state.selected.day)}
className={css.dropdown}
onFocus={() => this.handleSelectFocus()}
onBlur={() => this.handleSelectBlur()}
onChange={e => this.handleSelectChange('day', e.target.value)}
>
<option />
{days.map(d => <option key={d} value={d}>{pad(d)}</option>)}
</Select>
<Select
value={selectedValue(this.state.selected.month)}
className={css.dropdown}
onFocus={() => this.handleSelectFocus()}
onBlur={() => this.handleSelectBlur()}
onChange={e => this.handleSelectChange('month', e.target.value)}
>
<option />
{months.map(m => <option key={m} value={m}>{pad(m)}</option>)}
</Select>
<Select
value={selectedValue(this.state.selected.year)}
className={css.dropdown}
onFocus={() => this.handleSelectFocus()}
onBlur={() => this.handleSelectBlur()}
onChange={e => this.handleSelectChange('year', e.target.value)}
>
<option />
{years.map(y => <option key={y} value={y}>{y}</option>)}
</Select>
<div className={css.selectWrapper}>
{dateLabel}
<select
id={dateId}
value={selectedValue(this.state.selected.day)}
className={classNames(css.select, selectClassName, {
[css.notSet]: !parseNum(this.state.selected.day),
})}
onFocus={() => this.handleSelectFocus()}
onBlur={() => this.handleSelectBlur()}
onChange={e => this.handleSelectChange('day', e.target.value)}
>
<option>{datePlaceholder}</option>
{days.map(d => <option key={d} value={d}>{pad(d)}</option>)}
</select>
</div>
<div className={css.selectWrapper}>
{monthLabel}
<select
id={monthId}
value={selectedValue(this.state.selected.month)}
className={classNames(css.select, selectClassName, {
[css.notSet]: !parseNum(this.state.selected.month),
})}
onFocus={() => this.handleSelectFocus()}
onBlur={() => this.handleSelectBlur()}
onChange={e => this.handleSelectChange('month', e.target.value)}
>
<option>{monthPlaceholder}</option>
{months.map(m => <option key={m} value={m}>{pad(m)}</option>)}
</select>
</div>
<div className={css.selectWrapper}>
{yearLabel}
<select
id={yearId}
value={selectedValue(this.state.selected.year)}
className={classNames(css.select, selectClassName, {
[css.notSet]: !parseNum(this.state.selected.year),
})}
onFocus={() => this.handleSelectFocus()}
onBlur={() => this.handleSelectBlur()}
onChange={e => this.handleSelectChange('year', e.target.value)}
>
<option>{yearPlaceholder}</option>
{years.map(y => <option key={y} value={y}>{y}</option>)}
</select>
</div>
</div>
);
}
}
BirthdayInput.defaultProps = { value: null };
BirthdayInputComponent.defaultProps = {
selectClassName: null,
dateLabel: null,
monthLabel: null,
yearLabel: null,
value: null,
};
const { string, instanceOf, func, object } = PropTypes;
const { func, instanceOf, object, node, string } = PropTypes;
BirthdayInput.propTypes = {
id: string.isRequired,
BirthdayInputComponent.propTypes = {
selectClassName: string,
dateId: string.isRequired,
monthId: string.isRequired,
yearId: string.isRequired,
dateLabel: node,
monthLabel: node,
yearLabel: node,
value: instanceOf(Date),
onChange: func.isRequired,
onFocus: func.isRequired,
onBlur: func.isRequired,
// from injectIntl
intl: intlShape.isRequired,
};
const BirthdayInput = injectIntl(BirthdayInputComponent);
const BirthdayInputFieldComponent = props => {
const { rootClassName, className, id, label, input, meta } = props;
const { rootClassName, className, id, label, labelForMonth, labelForYear, input, meta } = props;
const { valid, invalid, touched, error } = meta;
if (label && !id) {
throw new Error('id required when a label is given');
}
// Error message and input error styles are only shown if the
// field has been touched and the validation has failed.
const hasError = touched && invalid && error;
const dateId = id;
const monthId = `${id}-month`;
const yearId = `${id}-year`;
const dateLabel = label ? <label htmlFor={dateId}>{label}</label> : null;
const monthLabel = labelForMonth ? <label htmlFor={monthId}>{labelForMonth}</label> : null;
const yearLabel = labelForYear ? <label htmlFor={yearId}>{labelForYear}</label> : null;
const selectClassName = classNames({
[css.selectSuccess]: valid,
[css.selectError]: hasError,
});
const inputProps = {
selectClassName,
dateId,
monthId,
yearId,
dateLabel,
monthLabel,
yearLabel,
...input,
};
const classes = classNames(rootClassName || css.fieldRoot, className);
const inputProps = { id, ...input };
return (
<div className={classes}>
{label ? <label htmlFor={id}>{label}</label> : null}
<BirthdayInput {...inputProps} />
<ValidationError fieldMeta={meta} />
</div>
@ -200,15 +271,18 @@ const BirthdayInputFieldComponent = props => {
BirthdayInputFieldComponent.defaultProps = {
rootClassName: null,
className: null,
id: null,
label: null,
labelForMonth: null,
labelForYear: null,
};
BirthdayInputFieldComponent.propTypes = {
rootClassName: string,
className: string,
id: string,
id: string.isRequired,
label: string,
labelForMonth: string,
labelForYear: string,
input: object.isRequired,
meta: object.isRequired,
};

View file

@ -203,7 +203,7 @@
display: block;
width: 100%;
margin: 0;
padding: 5px 0 4px 0;
padding: 5px 0 5px 0;
/* Unset user agent styles */
appearance: none;
@ -216,7 +216,7 @@
border-radius: 0;
/* Background */
background-image: url('data:image/svg+xml;utf8,<svg width="16" height="10" viewBox="0 0 16 10" xmlns="http://www.w3.org/2000/svg"><path d="M15.027 2.5a.577.577 0 0 0 0-.813L13.545.214a.566.566 0 0 0-.804 0L8 4.955 3.259.215a.566.566 0 0 0-.804 0L.973 1.686a.577.577 0 0 0 0 .813l6.625 6.616c.223.223.58.223.804 0L15.027 2.5z" fill="#4A4A4A" fill-rule="evenodd"/></svg>');
background-image: url('data:image/svg+xml;utf8,<svg width="14" height="9" viewBox="0 0 14 9" xmlns="http://www.w3.org/2000/svg"><path d="M6.53 7.472c.26.26.68.26.94 0l5.335-5.333c.26-.263.26-.684 0-.944-.26-.26-.683-.26-.943 0L7 6.056l-4.862-4.86c-.26-.26-.683-.26-.943 0-.26.26-.26.68 0 .943L6.53 7.47z" stroke="#4A4A4A" fill="#4A4A4A" fill-rule="evenodd"/></svg>');
background-size: 16px 16px;
background-position: center right;