Split SelectSingleCustomAttribute into it's own file

This commit is contained in:
Hannu Lyytikainen 2018-01-03 15:45:36 +02:00
parent 4d751af165
commit a0ff57705d
6 changed files with 212 additions and 192 deletions

View file

@ -6,116 +6,6 @@
height: 40px;
}
.menu {
display: inline-block;
padding-right: 8px;
&:last-of-type {
padding-right: 0;
}
}
.menuLabel {
@apply --marketplaceButtonStylesSecondary;
@apply --marketplaceSearchFilterLabelFontStyles;
padding: 0 16px 2px 16px;
width: auto;
min-height: 0;
border-radius: 4px;
&:focus {
outline: none;
background-color: var(--matterColorLight);
border-color: transparent;
text-decoration: none;
box-shadow: var(--boxShadowFilterButton);
}
}
.menuLabelSelected {
@apply --marketplaceButtonStyles;
@apply --marketplaceSearchFilterLabelFontStyles;
font-weight: var(--fontWeightSemiBold);
padding: 0 16px 2px 16px;
width: auto;
min-height: 0;
border-radius: 4px;
}
.menuContent {
margin-top: 8px;
padding-top: 15px;
min-width: 300px;
border-radius: 4px;
}
/* left animated "border" like hover element */
.menuItemBorder {
position: absolute;
top: 2px;
left: 0px;
height: calc(100% - 4px);
width: 0;
background-color: var(--marketplaceColor);
transition: width var(--transitionStyleButton);
}
/* left static border for selected element */
.menuItemBorderSelected {
position: absolute;
top: 2px;
left: 0px;
height: calc(100% - 4px);
width: 6px;
background-color: var(--matterColorDark);
}
.menuItem {
@apply --marketplaceSearchFilterOptionFontStyles;
/* Layout */
position: relative;
display: block;
/* Dimensions */
min-width: 300px;
margin: 0;
padding: 4px 30px;
color: var(--matterColor);
&:hover {
color: var(--matterColorDark);
text-decoration: none;
}
&:hover .menuItemBorder {
width: 6px;
}
}
.clearMenuItem {
@apply --marketplaceH5FontStyles;
font-weight: var(--fontWeightMedium);
color: var(--matterColorAnti);
/* Layout */
position: relative;
display: block;
/* Dimensions */
min-width: 300px;
margin: 0;
padding: 35px 30px 19px 30px;
&:hover {
color: var(--matterColor);
text-decoration: none;
}
}
.searchResultSummary {
@apply --marketplaceH4FontStyles;
line-height: 20px;

View file

@ -2,84 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import classNames from 'classnames';
import { omit } from 'lodash';
import config from '../../config';
import { stringify } from '../../util/urlHelpers';
import { Menu, MenuContent, MenuItem, MenuLabel, NamedLink } from '../../components';
import { SelectSingleCustomAttribute } from '../../components';
import css from './SearchFilters.css';
const { object, string, bool, number, func } = PropTypes;
const SelectSingleCustomAttribute = props => {
const { customAttribute, urlQueryParams, intl } = props;
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];
// query params where this custom attribute is cleared
const clearUrlQueryParams = omit(urlQueryParams, caParam);
// query string for clearing this custom attribute
const clearQueryString = stringify(clearUrlQueryParams);
// resolve menu label text and class
const menuLabel =
currentValue || intl.formatMessage({ id: `SearchFilters.${customAttribute}.label` });
const menuLabelClass = currentValue ? css.menuLabelSelected : css.menuLabel;
return (
<Menu className={css.menu} useArrow={false} contentPlacementOffset={-14}>
<MenuLabel className={menuLabelClass}>{menuLabel}</MenuLabel>
<MenuContent className={css.menuContent}>
{ca.values.map(v => {
// check if this option is selected
const selected = currentValue === v;
// search query string with this option added
const queryString = stringify({ ...urlQueryParams, [caParam]: v });
// menu item border class
const menuItemBorderClass = selected ? css.menuItemBorderSelected : css.menuItemBorder;
return (
<MenuItem key={v}>
<NamedLink className={css.menuItem} name="SearchPage" to={{ search: queryString }}>
<span className={menuItemBorderClass} />
{v}
</NamedLink>
</MenuItem>
);
})}
<MenuItem key={'clearLink'}>
<NamedLink
className={css.clearMenuItem}
name="SearchPage"
to={{ search: clearQueryString }}
>
<FormattedMessage id={'SearchFilters.clear'} />
</NamedLink>
</MenuItem>
</MenuContent>
</Menu>
);
};
SelectSingleCustomAttribute.defaultProps = {
rootClassName: null,
className: null,
};
SelectSingleCustomAttribute.propTypes = {
rootClassName: string,
className: string,
customAttribute: string.isRequired,
urlQueryParams: object.isRequired,
// from injectIntl
intl: intlShape.isRequired,
};
const SearchFiltersComponent = props => {
const {
rootClassName,
@ -138,6 +64,8 @@ const SearchFiltersComponent = props => {
);
};
const { object, string, bool, number, func } = PropTypes;
SearchFiltersComponent.defaultProps = {
rootClassName: null,
className: null,

View file

@ -0,0 +1,111 @@
@import '../../marketplace.css';
.root {
display: inline-block;
padding-right: 8px;
&:last-of-type {
padding-right: 0;
}
}
.menuLabel {
@apply --marketplaceButtonStylesSecondary;
@apply --marketplaceSearchFilterLabelFontStyles;
padding: 0 16px 2px 16px;
width: auto;
min-height: 0;
border-radius: 4px;
&:focus {
outline: none;
background-color: var(--matterColorLight);
border-color: transparent;
text-decoration: none;
box-shadow: var(--boxShadowFilterButton);
}
}
.menuLabelSelected {
@apply --marketplaceButtonStyles;
@apply --marketplaceSearchFilterLabelFontStyles;
font-weight: var(--fontWeightSemiBold);
padding: 0 16px 2px 16px;
width: auto;
min-height: 0;
border-radius: 4px;
}
.menuContent {
margin-top: 8px;
padding-top: 15px;
min-width: 300px;
border-radius: 4px;
}
/* left animated "border" like hover element */
.menuItemBorder {
position: absolute;
top: 2px;
left: 0px;
height: calc(100% - 4px);
width: 0;
background-color: var(--marketplaceColor);
transition: width var(--transitionStyleButton);
}
/* left static border for selected element */
.menuItemBorderSelected {
position: absolute;
top: 2px;
left: 0px;
height: calc(100% - 4px);
width: 6px;
background-color: var(--matterColorDark);
}
.menuItem {
@apply --marketplaceSearchFilterOptionFontStyles;
/* Layout */
position: relative;
display: block;
/* Dimensions */
min-width: 300px;
margin: 0;
padding: 4px 30px;
color: var(--matterColor);
&:hover {
color: var(--matterColorDark);
text-decoration: none;
}
&:hover .menuItemBorder {
width: 6px;
}
}
.clearMenuItem {
@apply --marketplaceH5FontStyles;
font-weight: var(--fontWeightMedium);
color: var(--matterColorAnti);
/* Layout */
position: relative;
display: block;
/* Dimensions */
min-width: 300px;
margin: 0;
padding: 35px 30px 19px 30px;
&:hover {
color: var(--matterColor);
text-decoration: none;
}
}

View file

@ -0,0 +1,88 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import classNames from 'classnames';
import { omit } from 'lodash';
import config from '../../config';
import { stringify } from '../../util/urlHelpers';
import { Menu, MenuContent, MenuItem, MenuLabel, NamedLink } from '../../components';
import css from './SelectSingleCustomAttribute.css';
const SelectSingleCustomAttributeComponent = props => {
const { rootClassName, className, customAttribute, urlQueryParams, intl } = props;
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];
// query params where this custom attribute is cleared
const clearUrlQueryParams = omit(urlQueryParams, caParam);
// query string for clearing this custom attribute
const clearQueryString = stringify(clearUrlQueryParams);
// resolve menu label text and class
const menuLabel = currentValue
? intl.formatMessage({ id: `SelectSingleCustomAttribute.category.option.${currentValue}` })
: intl.formatMessage({ id: `SelectSingleCustomAttribute.${customAttribute}.label` });
const menuLabelClass = currentValue ? css.menuLabelSelected : css.menuLabel;
const classes = classNames(rootClassName || css.root, className);
return (
<Menu className={classes} useArrow={false} contentPlacementOffset={-14}>
<MenuLabel className={menuLabelClass}>{menuLabel}</MenuLabel>
<MenuContent className={css.menuContent}>
{ca.values.map(v => {
// check if this option is selected
const selected = currentValue === v;
// search query string with this option added
const queryString = stringify({ ...urlQueryParams, [caParam]: v });
// menu item border class
const menuItemBorderClass = selected ? css.menuItemBorderSelected : css.menuItemBorder;
return (
<MenuItem key={v}>
<NamedLink className={css.menuItem} name="SearchPage" to={{ search: queryString }}>
<span className={menuItemBorderClass} />
<FormattedMessage id={`SelectSingleCustomAttribute.category.option.${v}`} />
</NamedLink>
</MenuItem>
);
})}
<MenuItem key={'clearLink'}>
<NamedLink
className={css.clearMenuItem}
name="SearchPage"
to={{ search: clearQueryString }}
>
<FormattedMessage id={'SelectSingleCustomAttribute.clear'} />
</NamedLink>
</MenuItem>
</MenuContent>
</Menu>
);
};
const { object, string } = PropTypes;
SelectSingleCustomAttributeComponent.defaultProps = {
rootClassName: null,
className: null,
};
SelectSingleCustomAttributeComponent.propTypes = {
rootClassName: string,
className: string,
customAttribute: string.isRequired,
urlQueryParams: object.isRequired,
// from injectIntl
intl: intlShape.isRequired,
};
const SelectSingleCustomAttribute = injectIntl(SelectSingleCustomAttributeComponent);
export default SelectSingleCustomAttribute;

View file

@ -95,6 +95,9 @@ export { default as SectionHero } from './SectionHero/SectionHero';
export { default as SectionHowItWorks } from './SectionHowItWorks/SectionHowItWorks';
export { default as SectionLocations } from './SectionLocations/SectionLocations';
export { default as SelectField } from './SelectField/SelectField';
export {
default as SelectSingleCustomAttribute,
} from './SelectSingleCustomAttribute/SelectSingleCustomAttribute';
export {
default as StripeBankAccountTokenInputField,
} from './StripeBankAccountTokenInputField/StripeBankAccountTokenInputField';

View file

@ -455,17 +455,11 @@
"ReviewModal.description": "Reviews are an important part of the Saunatime community. Please share what went well and what could have been improved.",
"ReviewModal.later": "Later",
"ReviewModal.title": "Leave a review for {revieweeName}",
"SearchFilters.category.label": "Category",
"SearchFilters.category.option.mountain": "Mountain",
"SearchFilters.category.option.other": "Other",
"SearchFilters.category.option.road": "Road",
"SearchFilters.category.option.track": "Track",
"SearchFilters.clear": "Clear",
"SearchFilters.foundResults": "{count, number} {count, plural, one {result} other {results}}",
"SearchFilters.loadingResults": "Loading search results…",
"SearchFilters.noResults": "Could not find any listings.",
"SearchFilters.openMapView": "Map",
"SearchAttributes.searchError": "Search failed. Please try again.",
"SearchFilters.searchError": "Search failed. Please try again.",
"SearchMapInfoCard.noImage": "No image",
"SearchPage.schemaDescription": "Showing search results",
"SearchPage.schemaMapSearch": "map search",
@ -487,6 +481,12 @@
"SectionLocations.listingsInLocation": "Saunas in {location}",
"SectionLocations.subtitle": "We have more than 1000 saunas around Finland. Here are some of our most popular locations.",
"SectionLocations.title": "We have wooden saunas, electric saunas, and even tent saunas.",
"SelectSingleCustomAttribute.category.label": "Category",
"SelectSingleCustomAttribute.category.option.mountain": "Mountain",
"SelectSingleCustomAttribute.category.option.other": "Other",
"SelectSingleCustomAttribute.category.option.road": "Road",
"SelectSingleCustomAttribute.category.option.track": "Track",
"SelectSingleCustomAttribute.clear": "Clear",
"SendMessageForm.sendFailed": "Failed to send. Please try again.",
"SendMessageForm.sendMessage": "Send message",
"SignupForm.emailInvalid": "A valid email address is required",