From 2fe85ad730d7f2a9873e832b1d88b8f21ef4ed8a Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Thu, 11 Jan 2018 12:47:52 +0200 Subject: [PATCH] Add category search filter --- .../SearchFiltersMobile.css | 64 +++++++++++++- .../SearchFiltersMobile.js | 86 ++++++++++++++++++- src/translations/en.json | 6 ++ 3 files changed, 154 insertions(+), 2 deletions(-) diff --git a/src/components/SearchFiltersMobile/SearchFiltersMobile.css b/src/components/SearchFiltersMobile/SearchFiltersMobile.css index 489d3152..25dcca21 100644 --- a/src/components/SearchFiltersMobile/SearchFiltersMobile.css +++ b/src/components/SearchFiltersMobile/SearchFiltersMobile.css @@ -66,12 +66,13 @@ .modalContainer { width: 100%; - padding: 94px 24px; + padding: 94px 0; } .modalHeadingWrapper { padding-bottom: 26px; border-bottom: 1px solid var(--matterColorNegative); + margin: 0 24px 30px 24px; } .modalHeading { @@ -99,3 +100,64 @@ color: var(--matterColor); } } + +.filterLabel { + @apply --marketplaceH3FontStyles; + color: var(--matterColorDark); + padding-left: 24px; +} + +.filterLabelSelected { + @apply --marketplaceH3FontStyles; + color: var(--marketplaceColor); + padding-left: 24px; +} + +/* left animated "border" like hover element */ +.optionBorder { + position: absolute; + top: 2px; + left: 0px; + height: calc(100% - 7px); + width: 0; + background-color: var(--matterColorDark); + transition: width var(--transitionStyleButton); +} + +/* left static border for selected element */ +.optionBorderSelected { + position: absolute; + top: 2px; + left: 0px; + height: calc(100% - 7px); + width: 12px; + background-color: var(--matterColorDark); + transition: width var(--transitionStyleButton); +} + +.option { + @apply --marketplaceH3FontStyles; + font-weight: var(--fontWeightMedium); + color: var(--matterColor); + + /* Layout */ + display: block; + position: relative; + margin: 0; + padding: 4px 24px 4px 44px; + + /* Override button styles */ + outline: none; + border: none; + + &:focus, + &:hover { + color: var(--matterColorDark); + } + + &:hover .menuItemBorder { + width: 6px; + } +} + + diff --git a/src/components/SearchFiltersMobile/SearchFiltersMobile.js b/src/components/SearchFiltersMobile/SearchFiltersMobile.js index 1a26512d..b46b5854 100644 --- a/src/components/SearchFiltersMobile/SearchFiltersMobile.js +++ b/src/components/SearchFiltersMobile/SearchFiltersMobile.js @@ -3,10 +3,63 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import { FormattedMessage, injectIntl, intlShape } from 'react-intl'; import { withRouter } from 'react-router-dom'; +import { omit } from 'lodash'; +import routeConfiguration from '../../routeConfiguration'; +import { createResourceLocatorString } from '../../util/routes'; import { SecondaryButton, ModalInMobile } from '../../components'; +import config from '../../config'; import css from './SearchFiltersMobile.css'; +class SelectSingleCustomAttributeMobile extends Component { + constructor(props) { + super(props); + this.selectOption = this.selectOption.bind(this); + } + + selectOption(option) { + const customAttribute = this.props.customAttribute; + console.log(`select option and ca: ${option} - ${customAttribute}`); + //this.setState({ isOpen: false }); + this.props.onSelect(customAttribute, option); + } + + render() { + const { customAttribute, urlQueryParams, intl } = this.props; + const filterLabel = intl.formatMessage({ + id: `SelectSingleCustomAttribute.${customAttribute}.label`, + }); + // custom attribute content + const ca = customAttribute && config.customAttributes[customAttribute]; + // name of the corresponding query parameter + const caParam = `ca_${customAttribute}`; + // current value of this custom attribute filter + const currentValue = urlQueryParams[caParam]; + + const labelClass = currentValue ? css.filterLabelSelected : css.filterLabel; + + return ( +
+
+ {filterLabel} +
+ {ca.values.map(v => { + // check if this option is selected + const selected = currentValue === v; + // menu item border class + const optionBorderClass = selected ? css.optionBorderSelected : css.optionBorder; + return ( + + ); + })} +
+ ); + } +} + class SearchFiltersMobileComponent extends Component { constructor(props) { super(props); @@ -24,6 +77,7 @@ class SearchFiltersMobileComponent extends Component { showAsModalMaxWidth, onMapIconClick, onManageDisableScrolling, + history, intl, } = this.props; @@ -44,6 +98,33 @@ class SearchFiltersMobileComponent extends Component { const classes = classNames(rootClassName || css.root, className); + const onSelectSingle = (customAttribute, option) => { + // Name of the corresponding query parameter. + // The custom attribute query parameters are named + // ca_ in the API. + const caParam = `ca_${customAttribute}`; + + // query parameters after selecting the option + // if no option is passed, clear the selection for the filter + const queryParams = option + ? { ...urlQueryParams, [caParam]: option } + : omit(urlQueryParams, caParam); + + history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, queryParams)); + }; + + const customAttribute = 'category'; + + const hasCategoryConfig = config.customAttributes && config.customAttributes.category; + const categoryFilter = hasCategoryConfig ? ( + + ) : null; + return (
@@ -73,7 +154,10 @@ class SearchFiltersMobileComponent extends Component {
-
+
{categoryFilter}
+
); diff --git a/src/translations/en.json b/src/translations/en.json index 4e8260d1..cbf47b13 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -502,6 +502,12 @@ "SelectSingleCustomAttribute.category.option.road": "Road", "SelectSingleCustomAttribute.category.option.track": "Track", "SelectSingleCustomAttribute.clear": "Clear", + "SelectSingleCustomAttributeMobile.category.label": "Category", + "SelectSingleCustomAttributeMobile.category.option.mountain": "Mountain", + "SelectSingleCustomAttributeMobile.category.option.other": "Other", + "SelectSingleCustomAttributeMobile.category.option.road": "Road", + "SelectSingleCustomAttributeMobile.category.option.track": "Track", + "SelectSingleCustomAttributeMobile.clear": "Clear", "SendMessageForm.sendFailed": "Failed to send. Please try again.", "SendMessageForm.sendMessage": "Send message", "SignupForm.emailInvalid": "A valid email address is required",