From ae2db171529965b5a0110f5a313ab034db6ffdc9 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Wed, 18 Apr 2018 11:38:46 +0300 Subject: [PATCH] Update filters configuration and validation Changes the way filters are defined and how the filter params are validated. --- src/components/SearchFilters/SearchFilters.js | 49 +++---- .../SearchFiltersMobile.js | 84 +++++------- .../SearchFiltersPanel/SearchFiltersPanel.js | 43 +++--- src/containers/SearchPage/MainPanel.js | 44 +++++-- .../SearchPage/SearchPage.helpers.js | 92 +++++++++---- .../SearchPage/SearchPage.helpers.test.js | 124 ++++++++++++++++++ src/containers/SearchPage/SearchPage.js | 43 +++--- .../__snapshots__/SearchPage.test.js.snap | 52 ++++---- 8 files changed, 355 insertions(+), 176 deletions(-) create mode 100644 src/containers/SearchPage/SearchPage.helpers.test.js diff --git a/src/components/SearchFilters/SearchFilters.js b/src/components/SearchFilters/SearchFilters.js index de2f8071..345794ee 100644 --- a/src/components/SearchFilters/SearchFilters.js +++ b/src/components/SearchFilters/SearchFilters.js @@ -1,6 +1,6 @@ import React from 'react'; import { compose } from 'redux'; -import { object, string, bool, number, func, shape, array } from 'prop-types'; +import { object, string, bool, number, func, shape } from 'prop-types'; import { injectIntl, intlShape, FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import { withRouter } from 'react-router-dom'; @@ -11,12 +11,19 @@ import routeConfiguration from '../../routeConfiguration'; import { createResourceLocatorString } from '../../util/routes'; import css from './SearchFilters.css'; -const CATEGORY_URL_PARAM = 'pub_category'; -const AMENITIES_URL_PARAM = 'pub_amenities'; - // Dropdown container can have a positional offset (in pixels) const FILTER_DROPDOWN_OFFSET = -14; +// resolve initial value for a single value filter +const initialSingleValue = (queryParams, paramName) => { + return queryParams[paramName]; +}; + +// resolve initial values for a multi value filter +const initialMultiValue = (queryParams, paramName) => { + return !!queryParams[paramName] ? queryParams[paramName].split(',') : []; +}; + const SearchFiltersComponent = props => { const { rootClassName, @@ -25,8 +32,8 @@ const SearchFiltersComponent = props => { listingsAreLoaded, resultsCount, searchInProgress, - categories, - amenities, + categoryFilter, + amenitiesFilter, isSearchFiltersPanelOpen, toggleSearchFiltersPanel, searchFiltersPanelSelectedCount, @@ -45,11 +52,9 @@ const SearchFiltersComponent = props => { id: 'SearchFilters.amenitiesLabel', }); - const initialAmenities = !!urlQueryParams[AMENITIES_URL_PARAM] - ? urlQueryParams[AMENITIES_URL_PARAM].split(',') - : []; + const initialAmenities = initialMultiValue(urlQueryParams, amenitiesFilter.paramName); - const initialCategory = urlQueryParams[CATEGORY_URL_PARAM]; + const initialCategory = initialSingleValue(urlQueryParams, categoryFilter.paramName); const handleSelectOptions = (urlParam, options) => { const queryParams = @@ -70,24 +75,24 @@ const SearchFiltersComponent = props => { history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, queryParams)); }; - const categoryFilter = categories ? ( + const categoryFilterElement = categoryFilter ? ( ) : null; - const amenitiesFilter = amenities ? ( + const amenitiesFilterElement = amenitiesFilter ? ( @@ -113,8 +118,8 @@ const SearchFiltersComponent = props => { return (
- {categoryFilter} - {amenitiesFilter} + {categoryFilterElement} + {amenitiesFilterElement} {toggleSearchFiltersPanelButton}
@@ -146,8 +151,8 @@ SearchFiltersComponent.defaultProps = { className: null, resultsCount: null, searchingInProgress: false, - categories: null, - amenities: null, + categoryFilter: null, + amenitiesFilter: null, isSearchFiltersPanelOpen: false, toggleSearchFiltersPanel: null, searchFiltersPanelSelectedCount: 0, @@ -161,8 +166,8 @@ SearchFiltersComponent.propTypes = { resultsCount: number, searchingInProgress: bool, onManageDisableScrolling: func.isRequired, - categories: array, - amenities: array, + categoriesFilter: object, + amenitiesFilter: object, isSearchFiltersPanelOpen: bool, toggleSearchFiltersPanel: func, searchFiltersPanelSelectedCount: number, diff --git a/src/components/SearchFiltersMobile/SearchFiltersMobile.js b/src/components/SearchFiltersMobile/SearchFiltersMobile.js index 0e60304c..b2f7baec 100644 --- a/src/components/SearchFiltersMobile/SearchFiltersMobile.js +++ b/src/components/SearchFiltersMobile/SearchFiltersMobile.js @@ -3,7 +3,7 @@ import { object, string, bool, number, func, shape, array } from 'prop-types'; import classNames from 'classnames'; import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; import { withRouter } from 'react-router-dom'; -import { omit, toPairs } from 'lodash'; +import { omit } from 'lodash'; import routeConfiguration from '../../routeConfiguration'; import { createResourceLocatorString } from '../../util/routes'; @@ -16,21 +16,6 @@ import { } from '../../components'; import css from './SearchFiltersMobile.css'; -const CATEGORY_URL_PARAM = 'pub_category'; -const AMENITIES_URL_PARAM = 'pub_amenities'; -const allowedParams = [CATEGORY_URL_PARAM, AMENITIES_URL_PARAM]; - -const validateParamValue = value => value !== null && value !== undefined && value.length > 0; -const validateParamKey = key => allowedParams.includes(key); - -// Check if a filter parameter is included query parameters -const hasFilterQueryParams = queryParams => { - const firstFilterParam = toPairs(queryParams).find(entry => { - return validateParamKey(entry[0]) && validateParamValue(entry[1]); - }); - return !!firstFilterParam; -}; - class SearchFiltersMobileComponent extends Component { constructor(props) { super(props); @@ -100,9 +85,9 @@ class SearchFiltersMobileComponent extends Component { // Reset all filter query parameters resetAll(e) { - const { urlQueryParams, history } = this.props; + const { urlQueryParams, history, filterParamNames } = this.props; - const queryParams = omit(urlQueryParams, [CATEGORY_URL_PARAM, AMENITIES_URL_PARAM]); + const queryParams = omit(urlQueryParams, filterParamNames); history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, queryParams)); // blur event target if event is passed @@ -113,30 +98,28 @@ class SearchFiltersMobileComponent extends Component { // resolve initial value for a single value filter initialSingleValue(paramName) { - return this.props.urlQueryParams[paramName] + return this.props.urlQueryParams[paramName]; } // resolve initial values for a multi value filter initialMultiValue(paramName) { const urlQueryParams = this.props.urlQueryParams; - return !!urlQueryParams[paramName] - ? urlQueryParams[paramName].split(',') - : []; + return !!urlQueryParams[paramName] ? urlQueryParams[paramName].split(',') : []; } render() { const { rootClassName, className, - urlQueryParams, listingsAreLoaded, resultsCount, searchInProgress, showAsModalMaxWidth, onMapIconClick, onManageDisableScrolling, - categories, - amenities, + selectedFiltersCount, + categoryFilter, + amenitiesFilter, intl, } = this.props; @@ -155,27 +138,28 @@ class SearchFiltersMobileComponent extends Component { { count: resultsCount } ); - const filtersButton = hasFilterQueryParams(urlQueryParams) ? ( - - ) : ( - - - - ); + const filtersButton = + selectedFiltersCount > 0 ? ( + + ) : ( + + + + ); const categoryLabel = intl.formatMessage({ id: 'SearchFiltersMobile.categoryLabel', }); - const initialCategory = this.initialSingleValue(CATEGORY_URL_PARAM); + const initialCategory = this.initialSingleValue(categoryFilter.paramName); - const categoryFilter = categories ? ( + const categoryFilterElement = categoryFilter ? ( @@ -183,15 +167,15 @@ class SearchFiltersMobileComponent extends Component { const amenitiesLabel = intl.formatMessage({ id: 'SearchFiltersMobile.amenitiesLabel' }); - const initialAmenities = this.initialMultiValue(AMENITIES_URL_PARAM); + const initialAmenities = this.initialMultiValue(amenitiesFilter.paramName); - const amenitiesFilter = amenities ? ( + const amenitiesFilterElement = amenitiesFilter ? ( ) : null; @@ -225,8 +209,8 @@ class SearchFiltersMobileComponent extends Component {
- {categoryFilter} - {amenitiesFilter} + {categoryFilterElement} + {amenitiesFilterElement}