Create BookingDateRangeFilter component

This commit is contained in:
Jenni Nurmi 2018-11-12 14:23:08 +02:00 committed by Vesa Luusua
parent 0870f01b13
commit 69631bf5fc
8 changed files with 335 additions and 0 deletions

View file

@ -0,0 +1,5 @@
@import '../../marketplace.css';
.popupSize {
padding: 0 10px 7px 10px;
}

View file

@ -0,0 +1,73 @@
import React from 'react';
import { withRouter } from 'react-router-dom';
import { stringify, parse } from '../../util/urlHelpers';
import { parseDateFromISO8601, stringifyDateToISO8601 } from '../../util/dates';
import BookingDateRangeFilter from './BookingDateRangeFilter';
const URL_PARAM = 'dates';
const handleSubmit = (urlParam, values, history) => {
const hasDates = values && values.dates;
const { startDate, endDate } = hasDates ? values.dates : {};
const start = startDate ? stringifyDateToISO8601(startDate) : null;
const end = endDate ? stringifyDateToISO8601(endDate) : null;
const queryParams =
start != null && end != null ? `?${stringify({ [urlParam]: [start, end].join(',') })}` : '';
history.push(`${window.location.pathname}${queryParams}`);
};
const BookingDateRangeFilterWrapper = withRouter(props => {
const { history, location } = props;
const params = parse(location.search);
const dates = params[URL_PARAM];
const rawValuesFromParams = !!dates ? dates.split(',') : [];
const valuesFromParams = rawValuesFromParams.map(v => parseDateFromISO8601(v));
const initialValues =
!!dates && valuesFromParams.length === 2
? {
dates: { startDate: valuesFromParams[0], endDate: valuesFromParams[1] },
}
: { dates: null };
return (
<BookingDateRangeFilter
{...props}
initialValues={initialValues}
onSubmit={(urlParam, values) => {
console.log('Submit BookingDateRangeFilter with (unformatted) values:', values);
handleSubmit(urlParam, values, history);
}}
/>
);
});
export const BookingDateRangeFilterPopup = {
component: BookingDateRangeFilterWrapper,
props: {
id: 'BookingDateRangeFilterPopupExample',
urlParam: URL_PARAM,
liveEdit: false,
showAsPopup: true,
contentPlacementOffset: -14,
// initialValues: handled inside wrapper
// onSubmit: handled inside wrapper
},
group: 'misc',
};
export const BookingDateRangeFilterPlain = {
component: BookingDateRangeFilterWrapper,
props: {
id: 'BookingDateRangeFilterPlainExample',
urlParam: URL_PARAM,
liveEdit: true,
showAsPopup: false,
contentPlacementOffset: -14,
// initialValues: handled inside wrapper
// onSubmit: handled inside wrapper
},
group: 'misc',
};

View file

@ -0,0 +1,153 @@
import React, { Component } from 'react';
import { bool, func, number, object, string } from 'prop-types';
import { injectIntl, intlShape } from 'react-intl';
import { FieldDateRangeController, FilterPopup, FilterPlain } from '../../components';
import css from './BookingDateRangeFilter.css';
export class BookingDateRangeFilterComponent extends Component {
constructor(props) {
super(props);
this.popupControllerRef = null;
this.plainControllerRef = null;
}
render() {
const {
className,
rootClassName,
showAsPopup,
initialValues: initialValuesRaw,
id,
contentPlacementOffset,
onSubmit,
urlParam,
intl,
...rest
} = this.props;
const isSelected = !!initialValuesRaw && !!initialValuesRaw.dates;
const initialValues = isSelected ? initialValuesRaw : { dates: null };
const startDate = isSelected ? initialValues.dates.startDate : null;
const endDate = isSelected ? initialValues.dates.endDate : null;
const format = {
month: 'short',
day: 'numeric',
};
const formattedStartDate = isSelected ? intl.formatDate(startDate, format) : null;
const formattedEndDate = isSelected ? intl.formatDate(endDate, format) : null;
const labelForPlain = isSelected
? intl.formatMessage(
{ id: 'BookingDateRangeFilter.labelSelectedPlain' },
{
dates: `${formattedStartDate} - ${formattedEndDate}`,
}
)
: intl.formatMessage({ id: 'BookingDateRangeFilter.labelPlain' });
const labelForPopup = isSelected
? intl.formatMessage(
{ id: 'BookingDateRangeFilter.labelSelectedPopup' },
{
dates: `${formattedStartDate} - ${formattedEndDate}`,
}
)
: intl.formatMessage({ id: 'BookingDateRangeFilter.labelPopup' });
const onClearPopupMaybe =
this.popupControllerRef && this.popupControllerRef.onReset
? { onClear: () => this.popupControllerRef.onReset(null, null) }
: {};
const onCancelPopupMaybe =
this.popupControllerRef && this.popupControllerRef.onReset
? { onCancel: () => this.popupControllerRef.onReset(startDate, endDate) }
: {};
const onClearPlainMaybe =
this.plainControllerRef && this.plainControllerRef.onReset
? { onClear: () => this.plainControllerRef.onReset(null, null) }
: {};
return showAsPopup ? (
<FilterPopup
className={className}
rootClassName={rootClassName}
popupClassName={css.popupSize}
label={labelForPopup}
isSelected={isSelected}
id={`${id}.popup`}
showAsPopup
contentPlacementOffset={contentPlacementOffset}
onSubmit={onSubmit}
{...onClearPopupMaybe}
{...onCancelPopupMaybe}
initialValues={initialValues}
urlParam={urlParam}
{...rest}
>
<FieldDateRangeController
name="dates"
controllerRef={node => {
this.popupControllerRef = node;
}}
/>
</FilterPopup>
) : (
<FilterPlain
className={className}
rootClassName={rootClassName}
label={labelForPlain}
isSelected={isSelected}
id={`${id}.plain`}
liveEdit
contentPlacementOffset={contentPlacementOffset}
onSubmit={onSubmit}
{...onClearPlainMaybe}
initialValues={initialValues}
urlParam={urlParam}
{...rest}
>
<FieldDateRangeController
name="dates"
controllerRef={node => {
this.plainControllerRef = node;
}}
/>
</FilterPlain>
);
}
}
BookingDateRangeFilterComponent.defaultProps = {
rootClassName: null,
className: null,
showAsPopup: true,
liveEdit: false,
initialValues: null,
contentPlacementOffset: 0,
};
BookingDateRangeFilterComponent.propTypes = {
rootClassName: string,
className: string,
id: string.isRequired,
showAsPopup: bool,
liveEdit: bool,
urlParam: string.isRequired,
onSubmit: func.isRequired,
initialValues: object,
contentPlacementOffset: number,
// form injectIntl
intl: intlShape.isRequired,
};
const BookingDateRangeFilter = injectIntl(BookingDateRangeFilterComponent);
export default BookingDateRangeFilter;

View file

@ -0,0 +1,43 @@
import React from 'react';
// react-dates needs to be initialized before using any react-dates component
// Since this is currently only component using react-dates we can do it here
// https://github.com/airbnb/react-dates#initialize
import 'react-dates/initialize';
import { renderShallow } from '../../util/test-helpers';
import { fakeIntl } from '../../util/test-data';
import { BookingDateRangeFilterComponent } from './BookingDateRangeFilter';
describe('BookingDateRangeFilter', () => {
it('matches popup snapshot', () => {
const tree = renderShallow(
<BookingDateRangeFilterComponent
id="BookingDateRangeFilter"
urlParam="dates"
liveEdit={false}
showAsPopup={true}
contentPlacementOffset={-14}
initialValues={{}}
onSubmit={() => null}
intl={fakeIntl}
/>
);
expect(tree).toMatchSnapshot();
});
it('matches plain snapshot', () => {
const tree = renderShallow(
<BookingDateRangeFilterComponent
id="BookingDateRangeFilter"
urlParam="dates"
liveEdit={true}
showAsPopup={false}
contentPlacementOffset={-14}
initialValues={{}}
onSubmit={() => null}
intl={fakeIntl}
/>
);
expect(tree).toMatchSnapshot();
});
});

View file

@ -0,0 +1,54 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BookingDateRangeFilter matches plain snapshot 1`] = `
<InjectIntl(FilterPlainComponent)
className={null}
contentPlacementOffset={-14}
id="BookingDateRangeFilter.plain"
initialValues={
Object {
"dates": null,
}
}
isSelected={false}
label="BookingDateRangeFilter.labelPlain"
liveEdit={true}
onSubmit={[Function]}
rootClassName={null}
urlParam="dates"
>
<FieldDateRangeController
className={null}
controllerRef={[Function]}
name="dates"
rootClassName={null}
/>
</InjectIntl(FilterPlainComponent)>
`;
exports[`BookingDateRangeFilter matches popup snapshot 1`] = `
<InjectIntl(FilterPopup)
className={null}
contentPlacementOffset={-14}
id="BookingDateRangeFilter.popup"
initialValues={
Object {
"dates": null,
}
}
isSelected={false}
label="BookingDateRangeFilter.labelPopup"
liveEdit={false}
onSubmit={[Function]}
rootClassName={null}
showAsPopup={true}
urlParam="dates"
>
<FieldDateRangeController
className={null}
controllerRef={[Function]}
name="dates"
rootClassName={null}
/>
</InjectIntl(FilterPopup)>
`;

View file

@ -2,6 +2,7 @@ export { default as ActivityFeed } from './ActivityFeed/ActivityFeed';
export { default as AddImages } from './AddImages/AddImages';
export { default as Avatar, AvatarMedium, AvatarLarge } from './Avatar/Avatar';
export { default as BookingBreakdown } from './BookingBreakdown/BookingBreakdown';
export { default as BookingDateRangeFilter } from './BookingDateRangeFilter/BookingDateRangeFilter';
export { default as Button, PrimaryButton, SecondaryButton, InlineTextButton } from './Button/Button';
export { default as BookingPanel } from './BookingPanel/BookingPanel';
export { default as CookieConsent } from './CookieConsent/CookieConsent';

View file

@ -4,6 +4,7 @@ import * as AddImages from './components/AddImages/AddImages.example';
import * as Avatar from './components/Avatar/Avatar.example';
import * as BookingBreakdown from './components/BookingBreakdown/BookingBreakdown.example';
import * as BookingPanel from './components/BookingPanel/BookingPanel.example';
import * as BookingDateRangeFilter from './components/BookingDateRangeFilter/BookingDateRangeFilter.example';
import * as Button from './components/Button/Button.example';
import * as ExpandingTextarea from './components/ExpandingTextarea/ExpandingTextarea.example';
import * as FieldBirthdayInput from './components/FieldBirthdayInput/FieldBirthdayInput.example';
@ -94,6 +95,7 @@ export {
AddImages,
Avatar,
BookingBreakdown,
BookingDateRangeFilter,
BookingDatesForm,
BookingPanel,
Button,

View file

@ -67,6 +67,10 @@
"BookingDatesForm.requiredDate": "Oops, make sure your date is correct!",
"BookingDatesForm.timeSlotsError": "Loading listing availability failed. Please refresh the page.",
"BookingDatesForm.youWontBeChargedInfo": "You won't be charged yet",
"BookingDateRangeFilter.labelPlain": "Dates",
"BookingDateRangeFilter.labelPopup": "Dates",
"BookingDateRangeFilter.labelSelectedPlain": "{dates}",
"BookingDateRangeFilter.labelSelectedPopup": "{dates}",
"BookingPanel.closedListingButtonText": "Sorry, this listing has been closed.",
"BookingPanel.ctaButtonMessage": "Request to book",
"BookingPanel.hostedBy": "Hosted by {name}",