mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-29 05:00:43 +10:00
Refactor BookingDatesForm: use Form connector from Final Form
This commit is contained in:
parent
ac003123ef
commit
fdeba7369b
2 changed files with 180 additions and 174 deletions
|
|
@ -1,12 +1,11 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { string, bool } from 'prop-types';
|
||||
import { compose } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { reduxForm, formValueSelector, propTypes as formPropTypes } from 'redux-form';
|
||||
import { Form as FinalForm } from 'react-final-form';
|
||||
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import moment from 'moment';
|
||||
import { required, bookingDatesRequired } from '../../util/validators';
|
||||
import { required, bookingDatesRequired, composeValidators } from '../../util/validators';
|
||||
import { START_DATE, END_DATE } from '../../util/dates';
|
||||
import { propTypes } from '../../util/types';
|
||||
import config from '../../config';
|
||||
|
|
@ -34,7 +33,7 @@ export class BookingDatesFormComponent extends Component {
|
|||
// focus on that input, otherwise continue with the
|
||||
// default handleSubmit function.
|
||||
handleFormSubmit(e) {
|
||||
const { startDate, endDate } = this.props.bookingDates;
|
||||
const { startDate, endDate } = e.bookingDates || {};
|
||||
if (!startDate) {
|
||||
e.preventDefault();
|
||||
this.setState({ focusedInput: START_DATE });
|
||||
|
|
@ -42,28 +41,13 @@ export class BookingDatesFormComponent extends Component {
|
|||
e.preventDefault();
|
||||
this.setState({ focusedInput: END_DATE });
|
||||
} else {
|
||||
this.props.handleSubmit(e);
|
||||
this.props.onSubmit(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
rootClassName,
|
||||
className,
|
||||
submitButtonWrapperClassName,
|
||||
unitType,
|
||||
bookingDates,
|
||||
form,
|
||||
price: unitPrice,
|
||||
intl,
|
||||
startDatePlaceholder,
|
||||
endDatePlaceholder,
|
||||
isOwnListing,
|
||||
} = this.props;
|
||||
|
||||
const { startDate, endDate } = bookingDates;
|
||||
const { rootClassName, className, price: unitPrice, ...rest } = this.props;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
const submitButtonClasses = classNames(css.submitButtonWrapper, submitButtonWrapperClassName);
|
||||
|
||||
if (!unitPrice) {
|
||||
return (
|
||||
|
|
@ -84,91 +68,124 @@ export class BookingDatesFormComponent extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
const bookingStartLabel = intl.formatMessage({ id: 'BookingDatesForm.bookingStartTitle' });
|
||||
const bookingEndLabel = intl.formatMessage({ id: 'BookingDatesForm.bookingEndTitle' });
|
||||
const requiredMessage = intl.formatMessage({ id: 'BookingDatesForm.requiredDate' });
|
||||
const startDateErrorMessage = intl.formatMessage({
|
||||
id: 'FieldDateRangeInput.invalidStartDate',
|
||||
});
|
||||
const endDateErrorMessage = intl.formatMessage({ id: 'FieldDateRangeInput.invalidEndDate' });
|
||||
|
||||
// This is the place to collect breakdown estimation data. See the
|
||||
// EstimatedBreakdownMaybe component to change the calculations
|
||||
// for customised payment processes.
|
||||
const bookingData =
|
||||
startDate && endDate
|
||||
? {
|
||||
unitType,
|
||||
unitPrice,
|
||||
startDate,
|
||||
endDate,
|
||||
|
||||
// NOTE: If unitType is `line-item/units`, a new picker
|
||||
// for the quantity should be added to the form.
|
||||
quantity: 1,
|
||||
}
|
||||
: null;
|
||||
const bookingInfo = bookingData ? (
|
||||
<div className={css.priceBreakdownContainer}>
|
||||
<h3 className={css.priceBreakdownTitle}>
|
||||
<FormattedMessage id="BookingDatesForm.priceBreakdownTitle" />
|
||||
</h3>
|
||||
<EstimatedBreakdownMaybe bookingData={bookingData} />
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const dateFormatOptions = {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
};
|
||||
|
||||
const now = moment();
|
||||
const today = now.startOf('day').toDate();
|
||||
const tomorrow = now
|
||||
.startOf('day')
|
||||
.add(1, 'days')
|
||||
.toDate();
|
||||
const startDatePlaceholderText =
|
||||
startDatePlaceholder || intl.formatDate(today, dateFormatOptions);
|
||||
const endDatePlaceholderText =
|
||||
endDatePlaceholder || intl.formatDate(tomorrow, dateFormatOptions);
|
||||
|
||||
return (
|
||||
<Form className={className} onSubmit={this.handleFormSubmit}>
|
||||
<FieldDateRangeInput
|
||||
className={css.bookingDates}
|
||||
name="bookingDates"
|
||||
unitType={unitType}
|
||||
startDateId={`${form}.bookingStartDate`}
|
||||
startDateLabel={bookingStartLabel}
|
||||
startDatePlaceholderText={startDatePlaceholderText}
|
||||
endDateId={`${form}.bookingEndDate`}
|
||||
endDateLabel={bookingEndLabel}
|
||||
endDatePlaceholderText={endDatePlaceholderText}
|
||||
focusedInput={this.state.focusedInput}
|
||||
onFocusedInputChange={this.onFocusedInputChange}
|
||||
format={null}
|
||||
useMobileMargins
|
||||
validate={[
|
||||
required(requiredMessage),
|
||||
bookingDatesRequired(startDateErrorMessage, endDateErrorMessage),
|
||||
]}
|
||||
/>
|
||||
{bookingInfo}
|
||||
<p className={css.smallPrint}>
|
||||
<FormattedMessage
|
||||
id={
|
||||
isOwnListing ? 'BookingDatesForm.ownListing' : 'BookingDatesForm.youWontBeChargedInfo'
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<div className={submitButtonClasses}>
|
||||
<PrimaryButton type="submit">
|
||||
<FormattedMessage id="BookingDatesForm.requestToBook" />
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</Form>
|
||||
<FinalForm
|
||||
{...rest}
|
||||
unitPrice={unitPrice}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
render={fieldRenderProps => {
|
||||
const {
|
||||
endDatePlaceholder,
|
||||
startDatePlaceholder,
|
||||
form,
|
||||
handleSubmit,
|
||||
intl,
|
||||
isOwnListing,
|
||||
submitButtonWrapperClassName,
|
||||
unitPrice,
|
||||
unitType,
|
||||
values,
|
||||
} = fieldRenderProps;
|
||||
const { startDate, endDate } = values && values.bookingDates ? values.bookingDates : {};
|
||||
|
||||
const bookingStartLabel = intl.formatMessage({
|
||||
id: 'BookingDatesForm.bookingStartTitle',
|
||||
});
|
||||
const bookingEndLabel = intl.formatMessage({ id: 'BookingDatesForm.bookingEndTitle' });
|
||||
const requiredMessage = intl.formatMessage({ id: 'BookingDatesForm.requiredDate' });
|
||||
const startDateErrorMessage = intl.formatMessage({
|
||||
id: 'FieldDateRangeInput.invalidStartDate',
|
||||
});
|
||||
const endDateErrorMessage = intl.formatMessage({
|
||||
id: 'FieldDateRangeInput.invalidEndDate',
|
||||
});
|
||||
|
||||
// This is the place to collect breakdown estimation data. See the
|
||||
// EstimatedBreakdownMaybe component to change the calculations
|
||||
// for customised payment processes.
|
||||
const bookingData =
|
||||
startDate && endDate
|
||||
? {
|
||||
unitType,
|
||||
unitPrice,
|
||||
startDate,
|
||||
endDate,
|
||||
|
||||
// NOTE: If unitType is `line-item/units`, a new picker
|
||||
// for the quantity should be added to the form.
|
||||
quantity: 1,
|
||||
}
|
||||
: null;
|
||||
const bookingInfo = bookingData ? (
|
||||
<div className={css.priceBreakdownContainer}>
|
||||
<h3 className={css.priceBreakdownTitle}>
|
||||
<FormattedMessage id="BookingDatesForm.priceBreakdownTitle" />
|
||||
</h3>
|
||||
<EstimatedBreakdownMaybe bookingData={bookingData} />
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const dateFormatOptions = {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
};
|
||||
|
||||
const now = moment();
|
||||
const today = now.startOf('day').toDate();
|
||||
const tomorrow = now
|
||||
.startOf('day')
|
||||
.add(1, 'days')
|
||||
.toDate();
|
||||
const startDatePlaceholderText =
|
||||
startDatePlaceholder || intl.formatDate(today, dateFormatOptions);
|
||||
const endDatePlaceholderText =
|
||||
endDatePlaceholder || intl.formatDate(tomorrow, dateFormatOptions);
|
||||
const submitButtonClasses = classNames(
|
||||
css.submitButtonWrapper,
|
||||
submitButtonWrapperClassName
|
||||
);
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit} className={classes}>
|
||||
<FieldDateRangeInput
|
||||
className={css.bookingDates}
|
||||
name="bookingDates"
|
||||
unitType={unitType}
|
||||
startDateId={`${form}.bookingStartDate`}
|
||||
startDateLabel={bookingStartLabel}
|
||||
startDatePlaceholderText={startDatePlaceholderText}
|
||||
endDateId={`${form}.bookingEndDate`}
|
||||
endDateLabel={bookingEndLabel}
|
||||
endDatePlaceholderText={endDatePlaceholderText}
|
||||
focusedInput={this.state.focusedInput}
|
||||
onFocusedInputChange={this.onFocusedInputChange}
|
||||
format={null}
|
||||
useMobileMargins
|
||||
validate={composeValidators(
|
||||
required(requiredMessage),
|
||||
bookingDatesRequired(startDateErrorMessage, endDateErrorMessage)
|
||||
)}
|
||||
/>
|
||||
{bookingInfo}
|
||||
<p className={css.smallPrint}>
|
||||
<FormattedMessage
|
||||
id={
|
||||
isOwnListing
|
||||
? 'BookingDatesForm.ownListing'
|
||||
: 'BookingDatesForm.youWontBeChargedInfo'
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<div className={submitButtonClasses}>
|
||||
<PrimaryButton type="submit">
|
||||
<FormattedMessage id="BookingDatesForm.requestToBook" />
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -183,11 +200,7 @@ BookingDatesFormComponent.defaultProps = {
|
|||
endDatePlaceholder: null,
|
||||
};
|
||||
|
||||
const { instanceOf, shape, string, bool } = PropTypes;
|
||||
|
||||
BookingDatesFormComponent.propTypes = {
|
||||
...formPropTypes,
|
||||
|
||||
rootClassName: string,
|
||||
className: string,
|
||||
submitButtonWrapperClassName: string,
|
||||
|
|
@ -196,12 +209,6 @@ BookingDatesFormComponent.propTypes = {
|
|||
price: propTypes.money,
|
||||
isOwnListing: bool,
|
||||
|
||||
// from formValueSelector
|
||||
bookingDates: shape({
|
||||
startDate: instanceOf(Date),
|
||||
endDate: instanceOf(Date),
|
||||
}).isRequired,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
|
||||
|
|
@ -210,21 +217,7 @@ BookingDatesFormComponent.propTypes = {
|
|||
endDatePlaceholder: string,
|
||||
};
|
||||
|
||||
const formName = 'BookingDates';
|
||||
|
||||
// When a field depends on the value of another field, we must connect
|
||||
// to the store and select the required values to inject to the
|
||||
// component.
|
||||
//
|
||||
// See: http://redux-form.com/6.6.1/examples/selectingFormValues/
|
||||
const selector = formValueSelector(formName);
|
||||
const mapStateToProps = state => ({ bookingDates: selector(state, 'bookingDates') || {} });
|
||||
|
||||
const BookingDatesForm = compose(
|
||||
connect(mapStateToProps),
|
||||
reduxForm({ form: formName }),
|
||||
injectIntl
|
||||
)(BookingDatesFormComponent);
|
||||
const BookingDatesForm = compose(injectIntl)(BookingDatesFormComponent);
|
||||
BookingDatesForm.displayName = 'BookingDatesForm';
|
||||
|
||||
export default BookingDatesForm;
|
||||
|
|
|
|||
|
|
@ -1,48 +1,61 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`BookingDatesForm matches snapshot without selected dates 1`] = `
|
||||
<Form
|
||||
className={null}
|
||||
contentRef={null}
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<FieldDateRangeInput
|
||||
endDateId="fakeTestForm.bookingEndDate"
|
||||
endDateLabel="BookingDatesForm.bookingEndTitle"
|
||||
endDatePlaceholderText="tomorrow"
|
||||
focusedInput={null}
|
||||
format={null}
|
||||
name="bookingDates"
|
||||
onFocusedInputChange={[Function]}
|
||||
startDateId="fakeTestForm.bookingStartDate"
|
||||
startDateLabel="BookingDatesForm.bookingStartTitle"
|
||||
startDatePlaceholderText="today"
|
||||
unitType="line-item/night"
|
||||
useMobileMargins={true}
|
||||
validate={
|
||||
Array [
|
||||
[Function],
|
||||
[Function],
|
||||
]
|
||||
<ReactFinalForm(4.3.1)(3.1.5)
|
||||
anyTouched={false}
|
||||
asyncValidate={[Function]}
|
||||
asyncValidating={false}
|
||||
autofill={[Function]}
|
||||
blur={[Function]}
|
||||
bookingDates={Object {}}
|
||||
change={[Function]}
|
||||
clearAsyncError={[Function]}
|
||||
clearFields={[Function]}
|
||||
clearSubmit={[Function]}
|
||||
clearSubmitErrors={[Function]}
|
||||
destroy={[Function]}
|
||||
dirty={false}
|
||||
dispatch={[Function]}
|
||||
endDatePlaceholder="tomorrow"
|
||||
form="fakeTestForm"
|
||||
handleSubmit={[Function]}
|
||||
initialize={[Function]}
|
||||
initialized={true}
|
||||
intl={
|
||||
Object {
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"now": [Function],
|
||||
}
|
||||
/>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="BookingDatesForm.youWontBeChargedInfo"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
<PrimaryButton
|
||||
type="submit"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="BookingDatesForm.requestToBook"
|
||||
values={Object {}}
|
||||
/>
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</Form>
|
||||
}
|
||||
invalid={false}
|
||||
isOwnListing={false}
|
||||
onSubmit={[Function]}
|
||||
pristine={true}
|
||||
pure={true}
|
||||
render={[Function]}
|
||||
reset={[Function]}
|
||||
resetSection={[Function]}
|
||||
startDatePlaceholder="today"
|
||||
submit={[Function]}
|
||||
submitButtonWrapperClassName={null}
|
||||
submitFailed={false}
|
||||
submitSucceeded={false}
|
||||
submitting={false}
|
||||
touch={[Function]}
|
||||
unitPrice={
|
||||
Money {
|
||||
"amount": 1099,
|
||||
"currency": "USD",
|
||||
}
|
||||
}
|
||||
unitType="line-item/night"
|
||||
untouch={[Function]}
|
||||
valid={true}
|
||||
/>
|
||||
`;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue