From 909845ea7627119615405162cd640b0d1363b943 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Fri, 6 Apr 2018 18:27:51 +0300 Subject: [PATCH 01/45] Fix: FieldArray non-unique name bug --- src/components/FieldGroupCheckbox/FieldGroupCheckbox.js | 9 ++++++--- .../SelectMultipleFilter/SelectMultipleFilterForm.js | 7 +------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js b/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js index 39f80445..f58fd806 100644 --- a/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js +++ b/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js @@ -29,8 +29,7 @@ class FieldCheckboxRenderer extends Component { } render() { - const { className, rootClassName, label, twoColumns, id, options, fields, meta } = this.props; - const name = fields.name; + const { className, rootClassName, label, twoColumns, id, options, meta, name } = this.props; const touched = this.state.touched; @@ -77,7 +76,11 @@ FieldCheckboxRenderer.propTypes = { twoColumns: bool, }; -const FieldGroupCheckbox = props => ; +// Redux Form: the name of FieldArray must be unique +// https://github.com/erikras/redux-form/issues/2740 +const FieldGroupCheckbox = props => ( + +); // Name and component are required fields for FieldArray. // Component-prop we define in this file, name needs to be passed in diff --git a/src/components/SelectMultipleFilter/SelectMultipleFilterForm.js b/src/components/SelectMultipleFilter/SelectMultipleFilterForm.js index 16575a31..96a9767d 100644 --- a/src/components/SelectMultipleFilter/SelectMultipleFilterForm.js +++ b/src/components/SelectMultipleFilter/SelectMultipleFilterForm.js @@ -49,12 +49,7 @@ const SelectMultipleFilterFormComponent = props => { contentRef={contentRef} style={style} > - +
- - ); -}; +const FormComponent = props => ( + { + const { + form, + handleSubmit, + onChange, + pristine, + submitting, + dateInputProps, + } = fieldRenderProps; + const submitDisabled = pristine || submitting; -FormComponent.propTypes = formPropTypes; + return ( +
{ + e.preventDefault(); + handleSubmit(e); + }} + > + + + + + ); + }} + /> +); -const defaultFormName = 'Styleguide.DateRangeInput.Form'; - -const Form = reduxForm({ - form: defaultFormName, -})(FormComponent); +const defaultFormName = 'FieldDateRangeInputExampleForm'; export const Empty = { - component: Form, + component: FormComponent, props: { dateInputProps: { name: 'bookingDates', @@ -43,19 +56,22 @@ export const Empty = { .add(1, 'days') .format('ddd, MMMM D'), format: null, - validate: [ + validate: composeValidators( required('Required'), - bookingDatesRequired('Start date is not valid', 'End date is not valid'), - ], + bookingDatesRequired('Start date is not valid', 'End date is not valid') + ), onBlur: () => console.log('onBlur called from DateRangeInput props.'), onFocus: () => console.log('onFocus called from DateRangeInput props.'), }, - onChange: values => { - const { startDate, endDate } = values; - console.log('Changed to', moment(startDate).format('L'), moment(endDate).format('L')); + onChange: formState => { + const { startDate, endDate } = formState.values; + if (startDate || endDate) { + console.log('Changed to', moment(startDate).format('L'), moment(endDate).format('L')); + } }, onSubmit: values => { console.log('Submitting a form with values:', values); + return false; }, }, group: 'custom inputs', diff --git a/src/components/FieldDateRangeInput/FieldDateRangeInput.js b/src/components/FieldDateRangeInput/FieldDateRangeInput.js index 087cebf4..17b0c07e 100644 --- a/src/components/FieldDateRangeInput/FieldDateRangeInput.js +++ b/src/components/FieldDateRangeInput/FieldDateRangeInput.js @@ -1,6 +1,13 @@ +/** + * Provides a date picker for Final Forms (using https://github.com/airbnb/react-dates) + * + * NOTE: If you are using this component inside BookingDatesForm, + * you should convert value.date to start date and end date before submitting it to API + */ + import React, { Component } from 'react'; import { bool, func, object, oneOf, string } from 'prop-types'; -import { Field } from 'redux-form'; +import { Field } from 'react-final-form'; import classNames from 'classnames'; import { START_DATE, END_DATE } from '../../util/dates'; import { propTypes } from '../../util/types'; @@ -117,6 +124,8 @@ class FieldDateRangeInputComponent extends Component { ...restOfInput, ...rest, focusedInput: this.state.focusedInput, + startDateId, + endDateId, }; const classes = classNames(rootClassName || css.fieldRoot, className); const errorClasses = classNames({ [css.mobileMargins]: useMobileMargins }); diff --git a/src/components/FieldDateRangeInput/FieldDateRangeInput.test.js b/src/components/FieldDateRangeInput/FieldDateRangeInput.test.js index c63903f4..bc2abaf1 100644 --- a/src/components/FieldDateRangeInput/FieldDateRangeInput.test.js +++ b/src/components/FieldDateRangeInput/FieldDateRangeInput.test.js @@ -18,8 +18,6 @@ describe('DateRangeInput', () => { onBlur: noop, onChange: noop, onFocus: noop, - onDragStart: noop, - onDrop: noop, startDateId: 'bookingStartDate', startDatePlaceholderText: 'today', endDateId: 'bookingEndDate', From fe7dedd43d3cbb594a6967dd7a494b3c96500664 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 11:54:40 +0300 Subject: [PATCH 07/45] Refactor FieldDateInput: use Field connector from Final Form --- src/components/FieldDateInput/DateInput.js | 9 +-- .../FieldDateInput/FieldDateInput.example.js | 70 ++++++++++++------- .../FieldDateInput/FieldDateInput.js | 5 +- .../FieldDateInput/FieldDateInput.test.js | 2 - src/util/validators.js | 3 + 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/components/FieldDateInput/DateInput.js b/src/components/FieldDateInput/DateInput.js index 6d124faa..5b51dc51 100644 --- a/src/components/FieldDateInput/DateInput.js +++ b/src/components/FieldDateInput/DateInput.js @@ -46,6 +46,8 @@ const defaultProps = { horizontalMargin: 0, withPortal: false, withFullScreenPortal: false, + appendToBody: false, + disableScroll: false, initialVisibleMonth: null, firstDayOfWeek: config.i18n.firstDayOfWeek, numberOfMonths: 1, @@ -137,12 +139,12 @@ class DateInputComponent extends Component { onBlur, onChange, onFocus, - onDragStart, - onDrop, phrases, screenReaderInputMessage, useMobileMargins, value, + children, + render, ...datePickerProps } = this.props; /* eslint-enable no-unused-vars */ @@ -194,6 +196,7 @@ DateInputComponent.defaultProps = { DateInputComponent.propTypes = { className: string, + id: string, focused: bool, initialDate: instanceOf(Date), intl: intlShape.isRequired, @@ -202,8 +205,6 @@ DateInputComponent.propTypes = { onChange: func.isRequired, onBlur: func.isRequired, onFocus: func.isRequired, - onDragStart: func.isRequired, - onDrop: func.isRequired, phrases: shape({ closeDatePicker: string, clearDate: string, diff --git a/src/components/FieldDateInput/FieldDateInput.example.js b/src/components/FieldDateInput/FieldDateInput.example.js index c0f57ef2..1d0249fb 100644 --- a/src/components/FieldDateInput/FieldDateInput.example.js +++ b/src/components/FieldDateInput/FieldDateInput.example.js @@ -1,34 +1,51 @@ /* eslint-disable no-console */ import React from 'react'; -import { reduxForm, propTypes as formPropTypes } from 'redux-form'; +import { Form as FinalForm, FormSpy } from 'react-final-form'; import moment from 'moment'; import { Button } from '../../components'; -import { required, bookingDateRequired } from '../../util/validators'; +import { required, bookingDateRequired, composeValidators } from '../../util/validators'; import FieldDateInput from './FieldDateInput'; -const FormComponent = props => { - const { form, handleSubmit, pristine, submitting, dateInputProps } = props; - const submitDisabled = pristine || submitting; - return ( -
- - - - ); -}; +const FormComponent = props => ( + { + const { + form, + handleSubmit, + onChange, + pristine, + submitting, + dateInputProps, + values, + } = fieldRenderProps; + const submitDisabled = pristine || submitting; + if (values && values.bookingDates) { + onChange(values.bookingDates); + } -FormComponent.propTypes = formPropTypes; + return ( +
{ + e.preventDefault(); + handleSubmit(e); + }} + > + + + + + ); + }} + /> +); -const defaultFormName = 'Styleguide.DateInput.Form'; - -const Form = reduxForm({ - form: defaultFormName, -})(FormComponent); +const defaultFormName = 'FieldDateInputExampleForm'; export const Empty = { - component: Form, + component: FormComponent, props: { dateInputProps: { name: 'bookingDate', @@ -37,16 +54,19 @@ export const Empty = { label: 'Date', placeholderText: moment().format('ddd, MMMM D'), format: null, - validate: [required('Required'), bookingDateRequired('Date is not valid')], + validate: composeValidators(required('Required'), bookingDateRequired('Date is not valid')), onBlur: () => console.log('onBlur called from DateInput props.'), onFocus: () => console.log('onFocus called from DateInput props.'), }, - onChange: values => { - const { date } = values; - console.log('Changed to', moment(date).format('L')); + onChange: formState => { + const { date } = formState.values; + if (date) { + console.log('Changed to', moment(date).format('L')); + } }, onSubmit: values => { console.log('Submitting a form with values:', values); + return false; }, }, group: 'custom inputs', diff --git a/src/components/FieldDateInput/FieldDateInput.js b/src/components/FieldDateInput/FieldDateInput.js index 6be23eb1..a1b3f9e5 100644 --- a/src/components/FieldDateInput/FieldDateInput.js +++ b/src/components/FieldDateInput/FieldDateInput.js @@ -1,12 +1,12 @@ /** - * Provides a date picker for Redux Forms (using https://github.com/airbnb/react-dates) + * Provides a date picker for Final Forms (using https://github.com/airbnb/react-dates) * * NOTE: If you are using this component inside BookingDatesForm, * you should convert value.date to start date and end date before submitting it to API */ import React, { Component } from 'react'; import { bool, object, string } from 'prop-types'; -import { Field } from 'redux-form'; +import { Field } from 'react-final-form'; import classNames from 'classnames'; import { ValidationError } from '../../components'; @@ -49,6 +49,7 @@ class FieldDateInputComponent extends Component { onBlur: input.onBlur, onFocus: input.onFocus, useMobileMargins, + id, ...restOfInput, ...rest, }; diff --git a/src/components/FieldDateInput/FieldDateInput.test.js b/src/components/FieldDateInput/FieldDateInput.test.js index cb4ee8a0..cd18f409 100644 --- a/src/components/FieldDateInput/FieldDateInput.test.js +++ b/src/components/FieldDateInput/FieldDateInput.test.js @@ -16,8 +16,6 @@ describe('DateInput', () => { onBlur: noop, onChange: noop, onFocus: noop, - onDragStart: noop, - onDrop: noop, id: 'bookingDate', placeholderText: 'today', }; diff --git a/src/util/validators.js b/src/util/validators.js index 74aba37d..dd0c81e0 100644 --- a/src/util/validators.js +++ b/src/util/validators.js @@ -119,3 +119,6 @@ export const ageAtLeast = (message, minYears) => value => { const ageInYears = now.diff(moment(value), 'years', true); return value && value instanceof Date && ageInYears >= minYears ? VALID : message; }; + +export const composeValidators = (...validators) => value => + validators.reduce((error, validator) => error || validator(value), VALID); From 63bd9aed65398b5add8fb02a56fc20e0a47ab383 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 11:58:30 +0300 Subject: [PATCH 08/45] Create FieldTextInput: Final Form version of TextInputField --- .../FieldTextInput/FieldTextInput.css | 19 +++ .../FieldTextInput/FieldTextInput.example.css | 7 ++ .../FieldTextInput/FieldTextInput.example.js | 90 ++++++++++++++ .../FieldTextInput/FieldTextInput.js | 117 ++++++++++++++++++ src/components/index.js | 1 + src/examples.js | 2 + 6 files changed, 236 insertions(+) create mode 100644 src/components/FieldTextInput/FieldTextInput.css create mode 100644 src/components/FieldTextInput/FieldTextInput.example.css create mode 100644 src/components/FieldTextInput/FieldTextInput.example.js create mode 100644 src/components/FieldTextInput/FieldTextInput.js diff --git a/src/components/FieldTextInput/FieldTextInput.css b/src/components/FieldTextInput/FieldTextInput.css new file mode 100644 index 00000000..2294a88d --- /dev/null +++ b/src/components/FieldTextInput/FieldTextInput.css @@ -0,0 +1,19 @@ +@import '../../marketplace.css'; + +.root { +} + +.input { + border-bottom-color: var(--attentionColor); +} + +.inputSuccess { + border-bottom-color: var(--successColor); +} + +.inputError { + border-bottom-color: var(--failColor); +} + +.textarea { +} diff --git a/src/components/FieldTextInput/FieldTextInput.example.css b/src/components/FieldTextInput/FieldTextInput.example.css new file mode 100644 index 00000000..9fe2d13d --- /dev/null +++ b/src/components/FieldTextInput/FieldTextInput.example.css @@ -0,0 +1,7 @@ +.field { + margin-top: 24px; +} + +.submit { + margin-top: 24px; +} diff --git a/src/components/FieldTextInput/FieldTextInput.example.js b/src/components/FieldTextInput/FieldTextInput.example.js new file mode 100644 index 00000000..68cd4e2e --- /dev/null +++ b/src/components/FieldTextInput/FieldTextInput.example.js @@ -0,0 +1,90 @@ +/* eslint-disable no-console */ +import React from 'react'; +import { Form as FinalForm, FormSpy } from 'react-final-form'; +import * as validators from '../../util/validators'; +import { Button } from '../../components'; +import FieldTextInput from './FieldTextInput'; + +import css from './FieldTextInput.example.css'; + +const FormComponent = props => ( + { + const { handleSubmit, onChange, invalid, pristine, submitting, formName } = fieldRenderProps; + const required = validators.required('This field is required'); + const submitDisabled = invalid || pristine || submitting; + return ( +
{ + e.preventDefault(); + handleSubmit(e); + }} + > + + + + + + + + + + ); + }} + /> +); + +export const Inputs = { + component: FormComponent, + props: { + formName: 'Inputs', + onChange: formState => { + if (Object.keys(formState.values).length > 0) { + console.log('form values changed to:', formState.values); + } + }, + onSubmit: values => { + console.log('submit values:', values); + }, + }, + group: 'inputs', +}; diff --git a/src/components/FieldTextInput/FieldTextInput.js b/src/components/FieldTextInput/FieldTextInput.js new file mode 100644 index 00000000..bef47687 --- /dev/null +++ b/src/components/FieldTextInput/FieldTextInput.js @@ -0,0 +1,117 @@ +/** + * NOTE this component is part of react-final-form instead of Redux Form. + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Field } from 'react-final-form'; +import classNames from 'classnames'; +import { ValidationError, ExpandingTextarea } from '../../components'; + +import css from './FieldTextInput.css'; + +const CONTENT_MAX_LENGTH = 5000; + +class FieldTextInputComponent extends Component { + componentWillUnmount() { + if (this.props.clearOnUnmount) { + this.props.input.onChange(''); + } + } + render() { + /* eslint-disable no-unused-vars */ + const { + rootClassName, + className, + inputRootClass, + clearOnUnmount, + customErrorText, + id, + label, + type, + input, + meta, + ...rest + } = this.props; + /* eslint-enable no-unused-vars */ + + if (label && !id) { + throw new Error('id required when a label is given'); + } + + const { valid, invalid, touched, error } = meta; + const isTextarea = type === 'textarea'; + + const errorText = customErrorText || error; + + // Error message and input error styles are only shown if the + // field has been touched and the validation has failed. + const hasError = touched && invalid && errorText; + + const fieldMeta = { touched, error: errorText }; + + const inputClasses = + inputRootClass || + classNames(css.input, { + [css.inputSuccess]: valid, + [css.inputError]: hasError, + [css.textarea]: isTextarea, + }); + const inputProps = isTextarea + ? { className: inputClasses, id, rows: 1, maxLength: CONTENT_MAX_LENGTH, ...input, ...rest } + : { className: inputClasses, id, type, ...input, ...rest }; + + const classes = classNames(rootClassName || css.root, className); + return ( +
+ {label ? : null} + {isTextarea ? : } + +
+ ); + } +} + +FieldTextInputComponent.defaultProps = { + rootClassName: null, + className: null, + inputRootClass: null, + clearOnUnmount: false, + customErrorText: null, + id: null, + label: null, +}; + +const { string, bool, shape, func, object } = PropTypes; + +FieldTextInputComponent.propTypes = { + rootClassName: string, + className: string, + inputRootClass: string, + + clearOnUnmount: bool, + + // Error message that can be manually passed to input field, + // overrides default validation message + customErrorText: string, + + // Label is optional, but if it is given, an id is also required so + // the label can reference the input in the `for` attribute + id: string, + label: string, + + // Either 'textarea' or something that is passed to the input element + type: string.isRequired, + + // Generated by redux-form's Field component + input: shape({ + onChange: func.isRequired, + }).isRequired, + meta: object.isRequired, +}; + +const FieldTextInput = props => { + return ; +}; + +export default FieldTextInput; diff --git a/src/components/index.js b/src/components/index.js index aa2b2ad4..ab485876 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -38,6 +38,7 @@ export { default as FieldDateRangeInput } from './FieldDateRangeInput/FieldDateR export { default as FieldGroupCheckbox } from './FieldGroupCheckbox/FieldGroupCheckbox'; export { default as FieldPhoneNumberInput } from './FieldPhoneNumberInput/FieldPhoneNumberInput'; export { default as FieldReviewRating } from './FieldReviewRating/FieldReviewRating'; +export { default as FieldTextInput } from './FieldTextInput/FieldTextInput'; export { default as Footer } from './Footer/Footer'; export { default as Form } from './Form/Form'; export { default as IconBannedUser } from './IconBannedUser/IconBannedUser'; diff --git a/src/examples.js b/src/examples.js index 6b057aa6..776a2f20 100644 --- a/src/examples.js +++ b/src/examples.js @@ -14,6 +14,7 @@ import * as FieldDateRangeInput from './components/FieldDateRangeInput/FieldDate import * as FieldGroupCheckbox from './components/FieldGroupCheckbox/FieldGroupCheckbox.example'; import * as FieldPhoneNumberInput from './components/FieldPhoneNumberInput/FieldPhoneNumberInput.example'; import * as FieldReviewRating from './components/FieldReviewRating/FieldReviewRating.example'; +import * as FieldTextInput from './components/FieldTextInput/FieldTextInput.example'; import * as Footer from './components/Footer/Footer.example'; import * as IconBannedUser from './components/IconBannedUser/IconBannedUser.example'; import * as IconCheckmark from './components/IconCheckmark/IconCheckmark.example'; @@ -104,6 +105,7 @@ export { FieldGroupCheckbox, FieldPhoneNumberInput, FieldReviewRating, + FieldTextInput, Footer, IconBannedUser, IconCheckmark, From b6782d8aae38a620338b362662dccb3c2fdf00f8 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 11:59:36 +0300 Subject: [PATCH 09/45] Create FieldSelect: Final Form version of SelectField --- src/components/FieldSelect/FieldSelect.css | 18 +++++ .../FieldSelect/FieldSelect.example.js | 51 +++++++++++++ src/components/FieldSelect/FieldSelect.js | 72 +++++++++++++++++++ src/components/index.js | 1 + src/examples.js | 2 + 5 files changed, 144 insertions(+) create mode 100644 src/components/FieldSelect/FieldSelect.css create mode 100644 src/components/FieldSelect/FieldSelect.example.js create mode 100644 src/components/FieldSelect/FieldSelect.js diff --git a/src/components/FieldSelect/FieldSelect.css b/src/components/FieldSelect/FieldSelect.css new file mode 100644 index 00000000..0aa58c74 --- /dev/null +++ b/src/components/FieldSelect/FieldSelect.css @@ -0,0 +1,18 @@ +@import '../../marketplace.css'; + +.root { +} + +.select { + color: var(--matterColorAnti); + border-bottom-color: var(--attentionColor); +} + +.selectSuccess { + color: var(--matterColor); + border-bottom-color: var(--successColor); +} + +.selectError { + border-bottom-color: var(--failColor); +} diff --git a/src/components/FieldSelect/FieldSelect.example.js b/src/components/FieldSelect/FieldSelect.example.js new file mode 100644 index 00000000..de6fae6a --- /dev/null +++ b/src/components/FieldSelect/FieldSelect.example.js @@ -0,0 +1,51 @@ +/* eslint-disable no-console */ +import React from 'react'; +import { Form as FinalForm, FormSpy } from 'react-final-form'; +import * as validators from '../../util/validators'; +import { Button } from '../../components'; +import FieldSelect from './FieldSelect'; + +const FormComponent = props => ( + { + const { form, handleSubmit, onChange, invalid, pristine, submitting } = fieldRenderProps; + const required = validators.required('This field is required'); + const submitDisabled = invalid || pristine || submitting; + return ( +
{ + e.preventDefault(); + handleSubmit(e); + }} + > + + + + + + + + + ); + }} + /> +); + +export const Select = { + component: FormComponent, + props: { + onChange: formState => { + if (formState.values.select1) { + console.log('form values changed to:', formState.values); + } + }, + onSubmit: values => { + console.log('submit values:', values); + return false; + }, + }, + group: 'inputs', +}; diff --git a/src/components/FieldSelect/FieldSelect.js b/src/components/FieldSelect/FieldSelect.js new file mode 100644 index 00000000..dd13589d --- /dev/null +++ b/src/components/FieldSelect/FieldSelect.js @@ -0,0 +1,72 @@ +/** + * NOTE this component is part of react-final-form instead of Redux Form. + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { Field } from 'react-final-form'; +import classNames from 'classnames'; +import { ValidationError } from '../../components'; + +import css from './FieldSelect.css'; + +const FieldSelectComponent = props => { + const { rootClassName, className, id, label, input, meta, children, ...rest } = props; + + if (label && !id) { + throw new Error('id required when a label is given'); + } + + const { valid, invalid, touched, error } = meta; + + // 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 selectClasses = classNames(css.select, { + [css.selectSuccess]: valid, + [css.selectError]: hasError, + }); + const selectProps = { className: selectClasses, id, ...input, ...rest }; + + const classes = classNames(rootClassName || css.root, className); + return ( +
+ {label ? : null} + + +
+ ); +}; + +FieldSelectComponent.defaultProps = { + rootClassName: null, + className: null, + id: null, + label: null, + children: null, +}; + +const { string, object, node } = PropTypes; + +FieldSelectComponent.propTypes = { + rootClassName: string, + className: string, + + // Label is optional, but if it is given, an id is also required so + // the label can reference the input in the `for` attribute + id: string, + label: string, + + // Generated by final-form's Field component + input: object.isRequired, + meta: object.isRequired, + + children: node, +}; + +const FieldSelect = props => { + return ; +}; + +export default FieldSelect; diff --git a/src/components/index.js b/src/components/index.js index ab485876..9be9fd6e 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -38,6 +38,7 @@ export { default as FieldDateRangeInput } from './FieldDateRangeInput/FieldDateR export { default as FieldGroupCheckbox } from './FieldGroupCheckbox/FieldGroupCheckbox'; export { default as FieldPhoneNumberInput } from './FieldPhoneNumberInput/FieldPhoneNumberInput'; export { default as FieldReviewRating } from './FieldReviewRating/FieldReviewRating'; +export { default as FieldSelect } from './FieldSelect/FieldSelect'; export { default as FieldTextInput } from './FieldTextInput/FieldTextInput'; export { default as Footer } from './Footer/Footer'; export { default as Form } from './Form/Form'; diff --git a/src/examples.js b/src/examples.js index 776a2f20..eac38cad 100644 --- a/src/examples.js +++ b/src/examples.js @@ -14,6 +14,7 @@ import * as FieldDateRangeInput from './components/FieldDateRangeInput/FieldDate import * as FieldGroupCheckbox from './components/FieldGroupCheckbox/FieldGroupCheckbox.example'; import * as FieldPhoneNumberInput from './components/FieldPhoneNumberInput/FieldPhoneNumberInput.example'; import * as FieldReviewRating from './components/FieldReviewRating/FieldReviewRating.example'; +import * as FieldSelect from './components/FieldSelect/FieldSelect.example'; import * as FieldTextInput from './components/FieldTextInput/FieldTextInput.example'; import * as Footer from './components/Footer/Footer.example'; import * as IconBannedUser from './components/IconBannedUser/IconBannedUser.example'; @@ -105,6 +106,7 @@ export { FieldGroupCheckbox, FieldPhoneNumberInput, FieldReviewRating, + FieldSelect, FieldTextInput, Footer, IconBannedUser, From 03641f61b89a5cd9a651600c3966b1939dbe31e4 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 12:02:39 +0300 Subject: [PATCH 10/45] Move FieldCheckbox (Redux Form stuff) inside FieldGroupCheckbox --- .../FieldGroupCheckbox/FieldCheckbox.css | 68 ++++++++++++++ .../FieldGroupCheckbox/FieldCheckbox.js | 91 +++++++++++++++++++ .../FieldGroupCheckbox/FieldGroupCheckbox.js | 3 +- 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 src/components/FieldGroupCheckbox/FieldCheckbox.css create mode 100644 src/components/FieldGroupCheckbox/FieldCheckbox.js diff --git a/src/components/FieldGroupCheckbox/FieldCheckbox.css b/src/components/FieldGroupCheckbox/FieldCheckbox.css new file mode 100644 index 00000000..a295dbf0 --- /dev/null +++ b/src/components/FieldGroupCheckbox/FieldCheckbox.css @@ -0,0 +1,68 @@ +@import '../../marketplace.css'; + +.root { + position: relative; +} + +.input { + position: absolute; + opacity: 0; + height: 0; + width: 0; + + /* Highlight the borders if the checkbox is hovered, focused or checked */ + &:hover + label .box, + &:focus + label .box, + &:checked + label .box { + stroke: var(--marketplaceColor); + } + + /* Display the "check" when checked */ + &:checked + label .checked { + display: inline; + stroke: var(--marketplaceColor); + stroke-width: 1px; + } + + /* Hightlight the text on checked, hover and focus */ + &:focus + label .text, + &:hover + label .text, + &:checked + label .text { + color: var(--matterColorDark); + } +} + +.label { + display: flex; + align-items: center; + padding: 0; +} + +.checkboxWrapper { + /* This should follow line-height */ + height: 32px; + margin-top: -1px; + margin-right: 12px; + align-self: baseline; + + display: inline-flex; + align-items: center; + cursor: pointer; +} + +.checked { + display: none; + fill: var(--marketplaceColor); +} + +.box { + stroke: var(--matterColorAnti); +} + +.text { + @apply --marketplaceListingAttributeFontStyles; + color: var(--matterColor); + margin-top: -1px; + margin-bottom: 1px; + cursor: pointer; +} diff --git a/src/components/FieldGroupCheckbox/FieldCheckbox.js b/src/components/FieldGroupCheckbox/FieldCheckbox.js new file mode 100644 index 00000000..13a94036 --- /dev/null +++ b/src/components/FieldGroupCheckbox/FieldCheckbox.js @@ -0,0 +1,91 @@ +import React from 'react'; +import { any, node, string, object, shape } from 'prop-types'; +import classNames from 'classnames'; +import { Field } from 'redux-form'; +import { ValidationError } from '../../components'; + +import css from './FieldCheckbox.css'; + +const IconCheckbox = props => { + return ( + + + + + + + + + + ); +}; + +IconCheckbox.defaultProps = { className: null }; + +IconCheckbox.propTypes = { className: string }; + +const FieldCheckboxComponent = props => { + const { rootClassName, className, svgClassName, id, label, input, meta, ...rest } = props; + + const classes = classNames(rootClassName || css.root, className); + + const { value, ...inputProps } = input; + const checked = value === true; + + const checkboxProps = { + id, + className: css.input, + type: 'checkbox', + checked, + ...inputProps, + ...rest, + }; + + return ( + + + + + + ); +}; + +FieldCheckboxComponent.defaultProps = { + className: null, + rootClassName: null, + svgClassName: null, + label: null, +}; + +FieldCheckboxComponent.propTypes = { + className: string, + rootClassName: string, + svgClassName: string, + id: string.isRequired, + label: node, + + // redux-form Field params + input: shape({ value: any }).isRequired, + meta: object.isRequired, +}; + +const FieldCheckbox = props => { + return ; +}; + +export default FieldCheckbox; diff --git a/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js b/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js index f58fd806..14876e96 100644 --- a/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js +++ b/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js @@ -11,8 +11,9 @@ import React, { Component } from 'react'; import { arrayOf, bool, node, shape, string } from 'prop-types'; import classNames from 'classnames'; import { FieldArray } from 'redux-form'; -import { FieldCheckbox, ValidationError } from '../../components'; +import { ValidationError } from '../../components'; +import FieldCheckbox from './FieldCheckbox'; import css from './FieldGroupCheckbox.css'; class FieldCheckboxRenderer extends Component { From 0e86cd532242db9e15534217f39918caed80ff82 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 12:03:55 +0300 Subject: [PATCH 11/45] Refactor FieldCheckbox: use Field connector from Final Form --- .../FieldCheckbox/FieldCheckbox.example.js | 60 ++++++++++--------- src/components/FieldCheckbox/FieldCheckbox.js | 37 ++++++------ 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/src/components/FieldCheckbox/FieldCheckbox.example.js b/src/components/FieldCheckbox/FieldCheckbox.example.js index 5c1f88a8..e5009cdf 100644 --- a/src/components/FieldCheckbox/FieldCheckbox.example.js +++ b/src/components/FieldCheckbox/FieldCheckbox.example.js @@ -1,43 +1,47 @@ import React from 'react'; -import { reduxForm, propTypes as formPropTypes } from 'redux-form'; +import { Form as FinalForm, FormSpy } from 'react-final-form'; import { Button } from '../../components'; import FieldCheckbox from './FieldCheckbox'; -import { required } from '../../util/validators'; const formName = 'Styleguide.FieldCheckbox.Form'; -const FormComponent = props => { - const { form, handleSubmit, invalid, pristine, submitting } = props; +const FormComponent = props => ( + { + const { form, handleSubmit, onChange, invalid, pristine, submitting } = fieldRenderProps; - const submitDisabled = invalid || pristine || submitting; + const submitDisabled = invalid || pristine || submitting; - return ( -
- + return ( + { + e.preventDefault(); + handleSubmit(e); + }} + > + + + - - - - - ); -}; - -FormComponent.propTypes = formPropTypes; - -const Form = reduxForm({ - form: formName, -})(FormComponent); + + + ); + }} + /> +); export const Checkbox = { - component: Form, + component: FormComponent, props: { + onChange: formState => { + if (Object.keys(formState.values).length > 0) { + console.log('form values changed to:', formState.values); + } + }, onSubmit: values => { console.log('Submit values of FieldCheckbox: ', values); }, diff --git a/src/components/FieldCheckbox/FieldCheckbox.js b/src/components/FieldCheckbox/FieldCheckbox.js index 13a94036..2084bbde 100644 --- a/src/components/FieldCheckbox/FieldCheckbox.js +++ b/src/components/FieldCheckbox/FieldCheckbox.js @@ -1,8 +1,11 @@ +/** + * NOTE this component is part of react-final-form instead of Redux Form. + */ + import React from 'react'; -import { any, node, string, object, shape } from 'prop-types'; +import { node, string } from 'prop-types'; import classNames from 'classnames'; -import { Field } from 'redux-form'; -import { ValidationError } from '../../components'; +import { Field } from 'react-final-form'; import css from './FieldCheckbox.css'; @@ -35,32 +38,26 @@ IconCheckbox.defaultProps = { className: null }; IconCheckbox.propTypes = { className: string }; const FieldCheckboxComponent = props => { - const { rootClassName, className, svgClassName, id, label, input, meta, ...rest } = props; + const { rootClassName, className, svgClassName, id, label, ...rest } = props; const classes = classNames(rootClassName || css.root, className); - - const { value, ...inputProps } = input; - const checked = value === true; - const checkboxProps = { id, className: css.input, + component: 'input', type: 'checkbox', - checked, - ...inputProps, ...rest, }; return ( - + - ); }; @@ -76,16 +73,16 @@ FieldCheckboxComponent.propTypes = { className: string, rootClassName: string, svgClassName: string, + + // Id is needed to connect the label with input. id: string.isRequired, label: node, - // redux-form Field params - input: shape({ value: any }).isRequired, - meta: object.isRequired, + // Name groups several checkboxes to an array of selected values + name: string.isRequired, + + // Checkbox needs a value that is passed forward when user checks the checkbox + value: string.isRequired, }; -const FieldCheckbox = props => { - return ; -}; - -export default FieldCheckbox; +export default FieldCheckboxComponent; From 7a81956dd1e45fa793c441b449538bd4baf335c7 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 12:06:39 +0300 Subject: [PATCH 12/45] Create FieldCheckboxGroup: using FieldGroupCheckbox as starting point --- .../FieldCheckboxGroup/FieldCheckboxGroup.css | 28 ++++ .../FieldCheckboxGroup.example.js | 151 ++++++++++++++++++ .../FieldCheckboxGroup/FieldCheckboxGroup.js | 96 +++++++++++ src/components/index.js | 1 + src/examples.js | 2 + 5 files changed, 278 insertions(+) create mode 100644 src/components/FieldCheckboxGroup/FieldCheckboxGroup.css create mode 100644 src/components/FieldCheckboxGroup/FieldCheckboxGroup.example.js create mode 100644 src/components/FieldCheckboxGroup/FieldCheckboxGroup.js diff --git a/src/components/FieldCheckboxGroup/FieldCheckboxGroup.css b/src/components/FieldCheckboxGroup/FieldCheckboxGroup.css new file mode 100644 index 00000000..0550ae37 --- /dev/null +++ b/src/components/FieldCheckboxGroup/FieldCheckboxGroup.css @@ -0,0 +1,28 @@ +@import '../../marketplace.css'; + +.root { + margin: 0; + padding: 0; + border: none; +} + +.list { + margin: 0; +} + +.twoColumns { + @media (--viewportMedium) { + columns: 2; + } +} + +.item { + padding: 2px 0; + + /* Fix broken multi-column layout in Chrome */ + page-break-inside: avoid; + + @media (--viewportMedium) { + padding: 4px 0; + } +} diff --git a/src/components/FieldCheckboxGroup/FieldCheckboxGroup.example.js b/src/components/FieldCheckboxGroup/FieldCheckboxGroup.example.js new file mode 100644 index 00000000..f7ce91f2 --- /dev/null +++ b/src/components/FieldCheckboxGroup/FieldCheckboxGroup.example.js @@ -0,0 +1,151 @@ +import React from 'react'; +import { Form as FinalForm, FormSpy } from 'react-final-form'; +import arrayMutators from 'final-form-arrays'; +import { Button } from '../../components'; +import FieldCheckboxGroup from './FieldCheckboxGroup'; +import { requiredFieldArrayCheckbox } from '../../util/validators'; + +const formName = 'Styleguide.FieldCheckboxGroup'; +const formNameRequired = 'Styleguide.FieldCheckboxGroupRequired'; + +const label =

Amenities

; + +const commonProps = { + label: label, + options: [ + { + key: 'towels', + label: 'Towels', + }, + { + key: 'bathroom', + label: 'Bathroom', + }, + { + key: 'swimming_pool', + label: 'Swimming pool', + }, + { + key: 'own_drinks', + label: 'Own drinks allowed', + }, + { + key: 'jacuzzi', + label: 'Jacuzzi', + }, + { + key: 'audiovisual_entertainment', + label: 'Audiovisual entertainment', + }, + { + key: 'barbeque', + label: 'Barbeque', + }, + { + key: 'own_food_allowed', + label: 'Own food allowed', + }, + ], + twoColumns: true, +}; + +const optionalProps = { + name: 'amenities-optional', + id: 'amenities-optional', + ...commonProps, +}; + +const requiredProps = { + name: 'amenities-required', + id: `${formNameRequired}.amenities-required`, + ...commonProps, + validate: requiredFieldArrayCheckbox('this is required'), +}; +const tosProps = { + name: 'terms-of-service', + id: `${formNameRequired}.tos-accepted`, + options: [ + { + key: 'tos', + label: 'Terms of Service', + }, + ], + validate: requiredFieldArrayCheckbox('You need to accept Terms of Service'), +}; + +const formComponent = country => props => ( + { + const { handleSubmit, invalid, submitting, componentProps, onChange } = fieldRenderProps; + + const submitDisabled = invalid || submitting; + + return ( +
{ + e.preventDefault(); + handleSubmit(e); + }} + > + + + + + + ); + }} + /> +); + +export const Optional = { + component: formComponent(formName), + props: { + onChange: formState => { + if (formState.dirty) { + console.log('form values changed to:', formState.values); + } + }, + onSubmit: values => { + console.log('Submit values: ', values); + }, + initialValues: { [optionalProps.name]: ['jacuzzi', 'towels'] }, + componentProps: optionalProps, + }, + group: 'inputs', +}; + +export const Required = { + component: formComponent(formNameRequired), + props: { + onChange: formState => { + if (formState.dirty) { + console.log('form values changed to:', formState.values); + } + }, + onSubmit: values => { + console.log('Submit values: ', values); + }, + componentProps: requiredProps, + }, + group: 'inputs', +}; + +export const ToSAccepted = { + component: formComponent(formNameRequired), + props: { + onChange: formState => { + if (formState.dirty) { + console.log('form values changed to:', formState.values); + } + }, + onSubmit: values => { + console.log('Submit values: ', values); + }, + componentProps: tosProps, + }, + group: 'inputs', +}; diff --git a/src/components/FieldCheckboxGroup/FieldCheckboxGroup.js b/src/components/FieldCheckboxGroup/FieldCheckboxGroup.js new file mode 100644 index 00000000..bd70f769 --- /dev/null +++ b/src/components/FieldCheckboxGroup/FieldCheckboxGroup.js @@ -0,0 +1,96 @@ +/** + * NOTE this component is part of react-final-form instead of Redux Form. + */ + +/* + * Renders a group of checkboxes that can be used to select + * multiple values from a set of options. + * + * The corresponding component when rendering the selected + * values is PropertyGroup. + * + */ + +import React, { Component } from 'react'; +import { arrayOf, bool, node, shape, string } from 'prop-types'; +import classNames from 'classnames'; +import { FieldArray } from 'react-final-form-arrays'; +import { FieldCheckbox, ValidationError } from '../../components'; + +import css from './FieldCheckboxGroup.css'; + +class FieldCheckboxRenderer extends Component { + constructor(props) { + super(props); + this.state = { touched: false }; + } + + componentWillReceiveProps(nextProps) { + // FieldArray doesn't have touched prop, so we'll keep track of it + if (!this.state.touched) { + this.setState({ touched: nextProps.meta.dirty }); + } + } + + render() { + const { className, rootClassName, label, twoColumns, id, fields, options, meta } = this.props; + + const touched = this.state.touched; + + const classes = classNames(rootClassName || css.root, className); + const listClasses = twoColumns ? classNames(css.list, css.twoColumns) : css.list; + + return ( +
+ {label ? {label} : null} +
    + {options.map((option, index) => { + const fieldId = `${id}.${option.key}`; + return ( +
  • + +
  • + ); + })} +
+ +
+ ); + } +} + +FieldCheckboxRenderer.defaultProps = { + rootClassName: null, + className: null, + label: null, + twoColumns: false, +}; + +FieldCheckboxRenderer.propTypes = { + rootClassName: string, + className: string, + id: string.isRequired, + label: node, + options: arrayOf( + shape({ + key: string.isRequired, + label: node.isRequired, + }) + ).isRequired, + twoColumns: bool, +}; + +const FieldCheckboxGroup = props => ; + +// Name and component are required fields for FieldArray. +// Component-prop we define in this file, name needs to be passed in +FieldCheckboxGroup.propTypes = { + name: string.isRequired, +}; + +export default FieldCheckboxGroup; diff --git a/src/components/index.js b/src/components/index.js index 9be9fd6e..cc104f6a 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -32,6 +32,7 @@ export { default as ExternalLink } from './ExternalLink/ExternalLink'; export { default as FieldBirthdayInput } from './FieldBirthdayInput/FieldBirthdayInput'; export { default as FieldBoolean } from './FieldBoolean/FieldBoolean'; export { default as FieldCheckbox } from './FieldCheckbox/FieldCheckbox'; +export { default as FieldCheckboxGroup } from './FieldCheckboxGroup/FieldCheckboxGroup'; export { default as FieldCurrencyInput } from './FieldCurrencyInput/FieldCurrencyInput'; export { default as FieldDateInput } from './FieldDateInput/FieldDateInput'; export { default as FieldDateRangeInput } from './FieldDateRangeInput/FieldDateRangeInput'; diff --git a/src/examples.js b/src/examples.js index eac38cad..b7043bcc 100644 --- a/src/examples.js +++ b/src/examples.js @@ -8,6 +8,7 @@ import * as ExpandingTextarea from './components/ExpandingTextarea/ExpandingText import * as FieldBirthdayInput from './components/FieldBirthdayInput/FieldBirthdayInput.example'; import * as FieldBoolean from './components/FieldBoolean/FieldBoolean.example'; import * as FieldCheckbox from './components/FieldCheckbox/FieldCheckbox.example'; +import * as FieldCheckboxGroup from './components/FieldCheckboxGroup/FieldCheckboxGroup.example'; import * as FieldCurrencyInput from './components/FieldCurrencyInput/FieldCurrencyInput.example'; import * as FieldDateInput from './components/FieldDateInput/FieldDateInput.example'; import * as FieldDateRangeInput from './components/FieldDateRangeInput/FieldDateRangeInput.example'; @@ -100,6 +101,7 @@ export { FieldBirthdayInput, FieldBoolean, FieldCheckbox, + FieldCheckboxGroup, FieldCurrencyInput, FieldDateInput, FieldDateRangeInput, From 89a1fe170161d53b2ec06cd857c4926a89de038d Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 12:08:00 +0300 Subject: [PATCH 13/45] Refactor FieldCurrencyInput: use Field connector from Final Form --- .../FieldCurrencyInput.example.js | 64 +++++++++++-------- .../FieldCurrencyInput/FieldCurrencyInput.js | 8 ++- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/components/FieldCurrencyInput/FieldCurrencyInput.example.js b/src/components/FieldCurrencyInput/FieldCurrencyInput.example.js index 78a18649..9f528f1d 100644 --- a/src/components/FieldCurrencyInput/FieldCurrencyInput.example.js +++ b/src/components/FieldCurrencyInput/FieldCurrencyInput.example.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { IntlProvider, addLocaleData } from 'react-intl'; -import { reduxForm, propTypes as formPropTypes } from 'redux-form'; +import { Form as FinalForm, FormSpy } from 'react-final-form'; import en from 'react-intl/locale-data/en'; import fi from 'react-intl/locale-data/fi'; import { currencyConfig } from '../../util/test-data'; @@ -81,36 +81,46 @@ export const defaultValueWithFiEUR = { group: 'custom inputs', }; -const formName = 'Styleguide.FieldCurrencyInput.Form'; +const FormComponent = props => ( + { + const { handleSubmit, onChange } = fieldRenderProps; + const required = validators.required('This field is required'); -const FormComponent = props => { - const { form } = props; - const required = validators.required('This field is required'); - return ( -
- - - ); -}; - -FormComponent.propTypes = formPropTypes; - -const Form = reduxForm({ - form: formName, -})(FormComponent); + return ( +
{ + e.preventDefault(); + handleSubmit(e); + }} + > + + + + ); + }} + /> +); export const FieldInForm = { - component: Form, + component: FormComponent, props: { - onChange: values => { - console.log('form values changed to:', values); + onChange: formState => { + if (formState.values && formState.values.price) { + console.log('form values changed to:', formState.values); + } + }, + onSubmit: values => { + console.log('FieldInForm submitted values:', values); + return false; }, }, group: 'custom inputs', diff --git a/src/components/FieldCurrencyInput/FieldCurrencyInput.js b/src/components/FieldCurrencyInput/FieldCurrencyInput.js index 2f3614d0..4893253a 100644 --- a/src/components/FieldCurrencyInput/FieldCurrencyInput.js +++ b/src/components/FieldCurrencyInput/FieldCurrencyInput.js @@ -1,3 +1,7 @@ +/** + * NOTE this component is part of react-final-form instead of Redux Form. + */ + /** * CurrencyInput renders an input field that format it's value according to currency formatting rules * onFocus: renders given value in unformatted manner: "9999,99" @@ -6,7 +10,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { intlShape, injectIntl } from 'react-intl'; -import { Field } from 'redux-form'; +import { Field } from 'react-final-form'; import classNames from 'classnames'; import Decimal from 'decimal.js'; import { ValidationError } from '../../components'; @@ -279,7 +283,7 @@ FieldCurrencyInputComponent.propTypes = { id: string, label: string, - // Generated by redux-form's Field component + // Generated by final-form's Field component input: object.isRequired, meta: object.isRequired, }; From d8525461807fe55830619695ae93655018546831 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 12:08:34 +0300 Subject: [PATCH 14/45] Refactor FieldBirthdayInput: use Field connector from Final Form --- .../FieldBirthdayInput.example.js | 64 +++++++++++-------- .../FieldBirthdayInput/FieldBirthdayInput.js | 18 ++++-- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/src/components/FieldBirthdayInput/FieldBirthdayInput.example.js b/src/components/FieldBirthdayInput/FieldBirthdayInput.example.js index 1fdc217f..f413fc8e 100644 --- a/src/components/FieldBirthdayInput/FieldBirthdayInput.example.js +++ b/src/components/FieldBirthdayInput/FieldBirthdayInput.example.js @@ -1,37 +1,51 @@ /* eslint-disable no-console */ import React from 'react'; -import { reduxForm } from 'redux-form'; +import { Form as FinalForm, FormSpy } from 'react-final-form'; import * as validators from '../../util/validators'; import FieldBirthdayInput from './FieldBirthdayInput'; -const formName = 'Styleguide.BirthdayInput.Form'; +const FormComponent = props => ( + { + const { handleSubmit, onChange, values } = fieldRenderProps; + const required = validators.required('A valid date is required'); + const minAge = 18; + const minAgeRequired = validators.ageAtLeast(`Age should be at least ${minAge}`, minAge); -const FormComponent = () => { - const required = validators.required('A valid date is required'); - const minAge = 18; - const minAgeRequired = validators.ageAtLeast(`Age should be at least ${minAge}`, minAge); - return ( -
- - - ); -}; - -const Form = reduxForm({ - form: formName, -})(FormComponent); + return ( +
{ + e.preventDefault(); + handleSubmit(e); + }} + > + + + + ); + }} + /> +); export const Empty = { - component: Form, + component: FormComponent, props: { - onChange: ({ birthday }) => { - console.log('birthday changed to:', birthday ? birthday.toUTCString() : birthday); + onChange: formState => { + const birthday = formState.values.birthday; + if (birthday) { + console.log('birthday changed to:', birthday.toUTCString()); + } + }, + onSubmit: values => { + console.log('BirthdayInput.Form submitted values:', values); }, }, group: 'custom inputs', diff --git a/src/components/FieldBirthdayInput/FieldBirthdayInput.js b/src/components/FieldBirthdayInput/FieldBirthdayInput.js index 39e8519a..af978655 100644 --- a/src/components/FieldBirthdayInput/FieldBirthdayInput.js +++ b/src/components/FieldBirthdayInput/FieldBirthdayInput.js @@ -1,6 +1,10 @@ +/** + * NOTE this component is part of react-final-form instead of Redux Form. + */ + import React, { Component } from 'react'; import { func, instanceOf, object, node, string, bool } from 'prop-types'; -import { Field } from 'redux-form'; +import { Field } from 'react-final-form'; import { injectIntl, intlShape } from 'react-intl'; import classNames from 'classnames'; import { range } from 'lodash'; @@ -8,7 +12,7 @@ import { ValidationError } from '../../components'; import css from './FieldBirthdayInput.css'; -// Since redux-form tracks the onBlur event for marking the field as +// Since final-form tracks the onBlur event for marking the field as // touched (which triggers possible error validation rendering), only // trigger the event asynchronously when no other input within this // component has received focus. @@ -88,14 +92,14 @@ class BirthdayInputComponent extends Component { this.handleSelectChange = this.handleSelectChange.bind(this); } componentWillMount() { - const value = this.props.value; + const value = this.props.valueFromForm; if (value instanceof Date) { this.setState({ selected: selectedFromDate(value) }); } } componentWillReceiveProps(newProps) { - const oldValue = this.props.value; - const newValue = newProps.value; + const oldValue = this.props.valueFromForm; + const newValue = newProps.valueFromForm; const valueChanged = oldValue !== newValue; if (valueChanged && newValue instanceof Date) { this.setState({ selected: selectedFromDate(newValue) }); @@ -219,7 +223,7 @@ BirthdayInputComponent.defaultProps = { dateLabel: null, monthLabel: null, yearLabel: null, - value: null, + valueFromForm: null, disabled: false, }; @@ -231,7 +235,7 @@ BirthdayInputComponent.propTypes = { dateLabel: node, monthLabel: node, yearLabel: node, - value: instanceOf(Date), + valueFromForm: instanceOf(Date), onChange: func.isRequired, onFocus: func.isRequired, onBlur: func.isRequired, From 62604faa69e62942d366e4019eb88583f3d502cb Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 16 Apr 2018 12:09:50 +0300 Subject: [PATCH 15/45] Refactor LocationAutocompleteInputField: use Field connector from Final Form --- .../LocationAutocompleteInput.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/LocationAutocompleteInput/LocationAutocompleteInput.js b/src/components/LocationAutocompleteInput/LocationAutocompleteInput.js index 8bca821b..d27e58cb 100644 --- a/src/components/LocationAutocompleteInput/LocationAutocompleteInput.js +++ b/src/components/LocationAutocompleteInput/LocationAutocompleteInput.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { Field } from 'redux-form'; +import { Field } from 'react-final-form'; import { debounce } from 'lodash'; import classNames from 'classnames'; import { propTypes } from '../../util/types'; @@ -503,9 +503,11 @@ class LocationAutocompleteInputFieldComponent extends Component { render() { /* eslint-disable no-unused-vars */ const { rootClassName, labelClassName, clearOnUnmount, ...restProps } = this.props; - const { input, label, meta, ...otherProps } = restProps; + const { input, label, meta, valueFromForm, ...otherProps } = restProps; /* eslint-enable no-unused-vars */ + const value = typeof valueFromForm !== 'undefined' ? valueFromForm : input.value; + const locationAutocompleteProps = { label, meta, ...otherProps, input: { ...input, value } }; const labelInfo = label ? (