mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 20:53:24 +10:00
Split SelectSingleCustomAttribute into it's own file
This commit is contained in:
parent
4d751af165
commit
a0ff57705d
6 changed files with 212 additions and 192 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue