mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Create KeywordFilter
Add it to desktop and mobile layout
This commit is contained in:
parent
578a02039a
commit
2485b46b30
11 changed files with 447 additions and 2 deletions
65
src/components/KeywordFilter/KeywordFilter.css
Normal file
65
src/components/KeywordFilter/KeywordFilter.css
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.label {
|
||||
@apply --marketplaceButtonStylesSecondary;
|
||||
@apply --marketplaceSearchFilterLabelFontStyles;
|
||||
|
||||
padding: 9px 16px 10px 16px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
border-radius: 4px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background-color: var(--matterColorLight);
|
||||
border-color: transparent;
|
||||
text-decoration: none;
|
||||
box-shadow: var(--boxShadowFilterButton);
|
||||
}
|
||||
}
|
||||
|
||||
.labelSelected {
|
||||
@apply --marketplaceButtonStyles;
|
||||
@apply --marketplaceSearchFilterLabelFontStyles;
|
||||
font-weight: var(--fontWeightSemiBold);
|
||||
|
||||
padding: 9px 16px 10px 16px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--marketplaceColor);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
border: 1px solid var(--marketplaceColorDark);
|
||||
}
|
||||
}
|
||||
|
||||
.labelText {
|
||||
display: inline-block;
|
||||
max-width: 250px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.field {
|
||||
@apply --marketplaceH4FontStyles;
|
||||
margin: 0;
|
||||
padding: 1px 0 13px 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.fieldPlain {
|
||||
@apply --marketplaceH4FontStyles;
|
||||
margin: 0;
|
||||
padding: 16px 0 30px 20px;
|
||||
border: none;
|
||||
}
|
||||
69
src/components/KeywordFilter/KeywordFilter.example.js
Normal file
69
src/components/KeywordFilter/KeywordFilter.example.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import KeywordFilter from './KeywordFilter';
|
||||
import { stringify, parse } from '../../util/urlHelpers';
|
||||
|
||||
const URL_PARAM = 'keywords';
|
||||
|
||||
const handleSubmit = (urlParam, values, history) => {
|
||||
console.log('Submitting values', values);
|
||||
const queryParams = values ? `?${stringify({ [urlParam]: values })}` : '';
|
||||
history.push(`${window.location.pathname}${queryParams}`);
|
||||
};
|
||||
|
||||
const KeywordFilterPopup = withRouter(props => {
|
||||
const { history, location } = props;
|
||||
|
||||
const params = parse(location.search);
|
||||
const keyword = params[URL_PARAM];
|
||||
const initialValues = !!keyword ? keyword : '';
|
||||
|
||||
return (
|
||||
<KeywordFilter
|
||||
id="KeywordFilterPopupExample"
|
||||
name="keyword"
|
||||
urlParam={URL_PARAM}
|
||||
label="Keyword"
|
||||
onSubmit={(urlParam, values) => handleSubmit(urlParam, values, history)}
|
||||
showAsPopup={true}
|
||||
liveEdit={false}
|
||||
initialValues={initialValues}
|
||||
contentPlacementOffset={-14}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const KeywordFilterPopupExample = {
|
||||
component: KeywordFilterPopup,
|
||||
props: {},
|
||||
group: 'filters',
|
||||
};
|
||||
|
||||
const KeywordFilterPlain = withRouter(props => {
|
||||
const { history, location } = props;
|
||||
|
||||
const params = parse(location.search);
|
||||
const keyword = params[URL_PARAM];
|
||||
const initialValues = !!keyword ? keyword : '';
|
||||
|
||||
return (
|
||||
<KeywordFilter
|
||||
id="KeywordFilterPlainExample"
|
||||
name="keyword"
|
||||
urlParam={URL_PARAM}
|
||||
label="Keyword"
|
||||
onSubmit={(urlParam, values) => {
|
||||
handleSubmit(urlParam, values, history);
|
||||
}}
|
||||
showAsPopup={false}
|
||||
liveEdit={true}
|
||||
initialValues={initialValues}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const KeywordFilterPlainExample = {
|
||||
component: KeywordFilterPlain,
|
||||
props: {},
|
||||
group: 'filters',
|
||||
};
|
||||
214
src/components/KeywordFilter/KeywordFilter.js
Normal file
214
src/components/KeywordFilter/KeywordFilter.js
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
import React, { Component } from 'react';
|
||||
import { func, number, string } from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { FieldTextInput } from '../../components';
|
||||
|
||||
import { FilterPopup, FilterPlain } from '../../components';
|
||||
import css from './KeywordFilter.css';
|
||||
|
||||
// When user types, we wait for new keystrokes a while before searching new content
|
||||
const DEBOUNCE_WAIT_TIME = 600;
|
||||
// Short search queries (e.g. 2 letters) have a longer timeout before search is made
|
||||
const TIMEOUT_FOR_SHORT_QUERIES = 2000;
|
||||
|
||||
class KeywordFilter extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.filter = null;
|
||||
this.filterContent = null;
|
||||
this.shortKeywordTimeout = null;
|
||||
this.mobileInputRef = React.createRef();
|
||||
|
||||
this.positionStyleForContent = this.positionStyleForContent.bind(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.clearTimeout(this.shortKeywordTimeout);
|
||||
}
|
||||
|
||||
positionStyleForContent() {
|
||||
if (this.filter && this.filterContent) {
|
||||
// Render the filter content to the right from the menu
|
||||
// unless there's no space in which case it is rendered
|
||||
// to the left
|
||||
const distanceToRight = window.innerWidth - this.filter.getBoundingClientRect().right;
|
||||
const labelWidth = this.filter.offsetWidth;
|
||||
const contentWidth = this.filterContent.offsetWidth;
|
||||
const contentWidthBiggerThanLabel = contentWidth - labelWidth;
|
||||
const renderToRight = distanceToRight > contentWidthBiggerThanLabel;
|
||||
const contentPlacementOffset = this.props.contentPlacementOffset;
|
||||
|
||||
const offset = renderToRight
|
||||
? { left: contentPlacementOffset }
|
||||
: { right: contentPlacementOffset };
|
||||
// set a min-width if the content is narrower than the label
|
||||
const minWidth = contentWidth < labelWidth ? { minWidth: labelWidth } : null;
|
||||
|
||||
return { ...offset, ...minWidth };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
rootClassName,
|
||||
className,
|
||||
id,
|
||||
name,
|
||||
label,
|
||||
initialValues,
|
||||
contentPlacementOffset,
|
||||
onSubmit,
|
||||
urlParam,
|
||||
intl,
|
||||
showAsPopup,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
|
||||
const hasInitialValues = !!initialValues && initialValues.length > 0;
|
||||
const labelForPopup = hasInitialValues
|
||||
? intl.formatMessage({ id: 'KeywordFilter.labelSelected' }, { labelText: initialValues })
|
||||
: label;
|
||||
|
||||
const labelForPlain = hasInitialValues
|
||||
? intl.formatMessage(
|
||||
{ id: 'KeywordFilterPlainForm.labelSelected' },
|
||||
{ labelText: initialValues }
|
||||
)
|
||||
: label;
|
||||
|
||||
const filterText = intl.formatMessage({ id: 'KeywordFilter.filterText' });
|
||||
|
||||
const placeholder = intl.formatMessage({ id: 'KeywordFilter.placeholder' });
|
||||
|
||||
const contentStyle = this.positionStyleForContent();
|
||||
|
||||
// pass the initial values with the name key so that
|
||||
// they can be passed to the correct field
|
||||
const namedInitialValues = { [name]: initialValues };
|
||||
|
||||
const handleSubmit = (urlParam, values) => {
|
||||
const usedValue = values ? values[name] : values;
|
||||
onSubmit(urlParam, usedValue);
|
||||
};
|
||||
|
||||
const debouncedSubmit = debounce(handleSubmit, DEBOUNCE_WAIT_TIME, {
|
||||
leading: false,
|
||||
trailing: true,
|
||||
});
|
||||
// Use timeout for shart queries and debounce for queries with any length
|
||||
const handleChangeWithDebounce = (urlParam, values) => {
|
||||
// handleSubmit gets urlParam and values as params.
|
||||
// If this field ("keyword") is short, create timeout
|
||||
const hasKeywordValue = values && values[name];
|
||||
const keywordValue = hasKeywordValue ? values && values[name] : '';
|
||||
if (urlParam && (!hasKeywordValue || (hasKeywordValue && keywordValue.length >= 3))) {
|
||||
if (this.shortKeywordTimeout) {
|
||||
window.clearTimeout(this.shortKeywordTimeout);
|
||||
}
|
||||
return debouncedSubmit(urlParam, values);
|
||||
} else {
|
||||
this.shortKeywordTimeout = window.setTimeout(() => {
|
||||
// if mobileInputRef exists, use the most up-to-date value from there
|
||||
return this.mobileInputRef && this.mobileInputRef.current
|
||||
? handleSubmit(urlParam, { ...values, [name]: this.mobileInputRef.current.value })
|
||||
: handleSubmit(urlParam, values);
|
||||
}, TIMEOUT_FOR_SHORT_QUERIES);
|
||||
}
|
||||
};
|
||||
|
||||
// Uncontrolled input needs to be cleared through the reference to DOM element.
|
||||
const handleClear = () => {
|
||||
if (this.mobileInputRef && this.mobileInputRef.current) {
|
||||
this.mobileInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
return showAsPopup ? (
|
||||
<FilterPopup
|
||||
className={classes}
|
||||
rootClassName={rootClassName}
|
||||
popupClassName={css.popupSize}
|
||||
name={name}
|
||||
label={labelForPopup}
|
||||
isSelected={hasInitialValues}
|
||||
id={`${id}.popup`}
|
||||
showAsPopup
|
||||
labelMaxWidth={250}
|
||||
contentPlacementOffset={contentPlacementOffset}
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={namedInitialValues}
|
||||
urlParam={urlParam}
|
||||
keepDirtyOnReinitialize
|
||||
{...rest}
|
||||
>
|
||||
<FieldTextInput
|
||||
className={css.field}
|
||||
name={name}
|
||||
id={`${id}-input`}
|
||||
type="text"
|
||||
label={filterText}
|
||||
placeholder={placeholder}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FilterPopup>
|
||||
) : (
|
||||
<FilterPlain
|
||||
className={className}
|
||||
rootClassName={rootClassName}
|
||||
label={labelForPlain}
|
||||
isSelected={hasInitialValues}
|
||||
id={`${id}.plain`}
|
||||
liveEdit
|
||||
contentPlacementOffset={contentStyle}
|
||||
onSubmit={handleChangeWithDebounce}
|
||||
onClear={handleClear}
|
||||
initialValues={namedInitialValues}
|
||||
urlParam={urlParam}
|
||||
{...rest}
|
||||
>
|
||||
<fieldset className={css.fieldPlain}>
|
||||
<label>{filterText}</label>
|
||||
<FieldTextInput
|
||||
name={name}
|
||||
id={`${id}-input`}
|
||||
isUncontrolled
|
||||
inputRef={this.mobileInputRef}
|
||||
type="text"
|
||||
placeholder={placeholder}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</fieldset>
|
||||
</FilterPlain>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
KeywordFilter.defaultProps = {
|
||||
rootClassName: null,
|
||||
className: null,
|
||||
initialValues: null,
|
||||
contentPlacementOffset: 0,
|
||||
};
|
||||
|
||||
KeywordFilter.propTypes = {
|
||||
rootClassName: string,
|
||||
className: string,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
urlParam: string.isRequired,
|
||||
label: string.isRequired,
|
||||
onSubmit: func.isRequired,
|
||||
initialValues: string,
|
||||
contentPlacementOffset: number,
|
||||
|
||||
// form injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(KeywordFilter);
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
SelectSingleFilter,
|
||||
SelectMultipleFilter,
|
||||
PriceFilter,
|
||||
KeywordFilter,
|
||||
} from '../../components';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { parseDateFromISO8601, stringifyDateToISO8601 } from '../../util/dates';
|
||||
|
|
@ -70,6 +71,7 @@ const SearchFiltersComponent = props => {
|
|||
amenitiesFilter,
|
||||
priceFilter,
|
||||
dateRangeFilter,
|
||||
keywordFilter,
|
||||
isSearchFiltersPanelOpen,
|
||||
toggleSearchFiltersPanel,
|
||||
searchFiltersPanelSelectedCount,
|
||||
|
|
@ -88,6 +90,10 @@ const SearchFiltersComponent = props => {
|
|||
id: 'SearchFilters.amenitiesLabel',
|
||||
});
|
||||
|
||||
const keywordLabel = intl.formatMessage({
|
||||
id: 'SearchFilters.keywordLabel',
|
||||
});
|
||||
|
||||
const initialAmenities = amenitiesFilter
|
||||
? initialValues(urlQueryParams, amenitiesFilter.paramName)
|
||||
: null;
|
||||
|
|
@ -104,6 +110,10 @@ const SearchFiltersComponent = props => {
|
|||
? initialDateRangeValue(urlQueryParams, dateRangeFilter.paramName)
|
||||
: null;
|
||||
|
||||
const initialKeyword = keywordFilter
|
||||
? initialValue(urlQueryParams, keywordFilter.paramName)
|
||||
: null;
|
||||
|
||||
const handleSelectOptions = (urlParam, options) => {
|
||||
const queryParams =
|
||||
options && options.length > 0
|
||||
|
|
@ -147,6 +157,14 @@ const SearchFiltersComponent = props => {
|
|||
history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, queryParams));
|
||||
};
|
||||
|
||||
const handleKeyword = (urlParam, values) => {
|
||||
const queryParams = values
|
||||
? { ...urlQueryParams, [urlParam]: values }
|
||||
: omit(urlQueryParams, urlParam);
|
||||
|
||||
history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, queryParams));
|
||||
};
|
||||
|
||||
const categoryFilterElement = categoryFilter ? (
|
||||
<SelectSingleFilter
|
||||
urlParam={categoryFilter.paramName}
|
||||
|
|
@ -197,6 +215,20 @@ const SearchFiltersComponent = props => {
|
|||
/>
|
||||
) : null;
|
||||
|
||||
const keywordFilterElement =
|
||||
keywordFilter && keywordFilter.config.active ? (
|
||||
<KeywordFilter
|
||||
id={'SearchFilters.keywordFilter'}
|
||||
name="keyword"
|
||||
urlParam={keywordFilter.paramName}
|
||||
label={keywordLabel}
|
||||
onSubmit={handleKeyword}
|
||||
showAsPopup
|
||||
initialValues={initialKeyword}
|
||||
contentPlacementOffset={FILTER_DROPDOWN_OFFSET}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const toggleSearchFiltersPanelButtonClasses =
|
||||
isSearchFiltersPanelOpen || searchFiltersPanelSelectedCount > 0
|
||||
? css.searchFiltersPanelOpen
|
||||
|
|
@ -221,6 +253,7 @@ const SearchFiltersComponent = props => {
|
|||
{amenitiesFilterElement}
|
||||
{priceFilterElement}
|
||||
{dateRangeFilterElement}
|
||||
{keywordFilterElement}
|
||||
{toggleSearchFiltersPanelButton}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { createResourceLocatorString } from '../../util/routes';
|
|||
import {
|
||||
ModalInMobile,
|
||||
Button,
|
||||
KeywordFilter,
|
||||
PriceFilter,
|
||||
SelectSingleFilter,
|
||||
SelectMultipleFilter,
|
||||
|
|
@ -34,6 +35,7 @@ class SearchFiltersMobileComponent extends Component {
|
|||
this.handleSelectMultiple = this.handleSelectMultiple.bind(this);
|
||||
this.handlePrice = this.handlePrice.bind(this);
|
||||
this.handleDateRange = this.handleDateRange.bind(this);
|
||||
this.handleKeyword = this.handleKeyword.bind(this);
|
||||
this.initialValue = this.initialValue.bind(this);
|
||||
this.initialValues = this.initialValues.bind(this);
|
||||
this.initialPriceRangeValue = this.initialPriceRangeValue.bind(this);
|
||||
|
|
@ -118,6 +120,15 @@ class SearchFiltersMobileComponent extends Component {
|
|||
history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, queryParams));
|
||||
}
|
||||
|
||||
handleKeyword(urlParam, keywords) {
|
||||
const { urlQueryParams, history } = this.props;
|
||||
const queryParams = urlParam
|
||||
? { ...urlQueryParams, [urlParam]: keywords }
|
||||
: omit(urlQueryParams, urlParam);
|
||||
|
||||
history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, queryParams));
|
||||
}
|
||||
|
||||
// Reset all filter query parameters
|
||||
resetAll(e) {
|
||||
const { urlQueryParams, history, filterParamNames } = this.props;
|
||||
|
|
@ -185,6 +196,7 @@ class SearchFiltersMobileComponent extends Component {
|
|||
amenitiesFilter,
|
||||
priceFilter,
|
||||
dateRangeFilter,
|
||||
keywordFilter,
|
||||
intl,
|
||||
} = this.props;
|
||||
|
||||
|
|
@ -258,7 +270,7 @@ class SearchFiltersMobileComponent extends Component {
|
|||
const dateRangeFilterElement =
|
||||
dateRangeFilter && dateRangeFilter.config.active ? (
|
||||
<BookingDateRangeFilter
|
||||
id="SearchFilters.dateRangeFilter"
|
||||
id="SearchFiltersMobile.dateRangeFilter"
|
||||
urlParam={dateRangeFilter.paramName}
|
||||
onSubmit={this.handleDateRange}
|
||||
liveEdit
|
||||
|
|
@ -267,6 +279,24 @@ class SearchFiltersMobileComponent extends Component {
|
|||
/>
|
||||
) : null;
|
||||
|
||||
const initialKeyword = this.initialValue(keywordFilter.paramName);
|
||||
const keywordLabel = intl.formatMessage({
|
||||
id: 'SearchFiltersMobile.keywordLabel',
|
||||
});
|
||||
const keywordFilterElement =
|
||||
keywordFilter && keywordFilter.config.active ? (
|
||||
<KeywordFilter
|
||||
id={'SearchFiltersMobile.keywordFilter'}
|
||||
name="keyword"
|
||||
urlParam={keywordFilter.paramName}
|
||||
label={keywordLabel}
|
||||
onSubmit={this.handleKeyword}
|
||||
liveEdit
|
||||
showAsPopup={false}
|
||||
initialValues={initialKeyword}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className={css.searchResultSummary}>
|
||||
|
|
@ -299,6 +329,7 @@ class SearchFiltersMobileComponent extends Component {
|
|||
</div>
|
||||
{this.state.isFiltersOpenOnMobile ? (
|
||||
<div className={css.filtersWrapper}>
|
||||
{keywordFilterElement}
|
||||
{categoryFilterElement}
|
||||
{amenitiesFilterElement}
|
||||
{priceFilterElement}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ export { default as BookingPanel } from './BookingPanel/BookingPanel';
|
|||
export { default as Discussion } from './Discussion/Discussion';
|
||||
export { default as FilterPlain } from './FilterPlain/FilterPlain';
|
||||
export { default as FilterPopup } from './FilterPopup/FilterPopup';
|
||||
export { default as KeywordFilter } from './KeywordFilter/KeywordFilter';
|
||||
export { default as ListingCard } from './ListingCard/ListingCard';
|
||||
export { default as ManageListingCard } from './ManageListingCard/ManageListingCard';
|
||||
export { default as Map } from './Map/Map';
|
||||
|
|
|
|||
|
|
@ -52,7 +52,13 @@ export class SearchPageComponent extends Component {
|
|||
}
|
||||
|
||||
filters() {
|
||||
const { categories, amenities, priceFilterConfig, dateRangeFilterConfig } = this.props;
|
||||
const {
|
||||
categories,
|
||||
amenities,
|
||||
priceFilterConfig,
|
||||
dateRangeFilterConfig,
|
||||
keywordFilterConfig,
|
||||
} = this.props;
|
||||
|
||||
// Note: "category" and "amenities" filters are not actually filtering anything by default.
|
||||
// Currently, if you want to use them, we need to manually configure them to be available
|
||||
|
|
@ -76,6 +82,10 @@ export class SearchPageComponent extends Component {
|
|||
paramName: 'dates',
|
||||
config: dateRangeFilterConfig,
|
||||
},
|
||||
keywordFilter: {
|
||||
paramName: 'keywords',
|
||||
config: keywordFilterConfig,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -220,6 +230,7 @@ export class SearchPageComponent extends Component {
|
|||
amenitiesFilter: filters.amenitiesFilter,
|
||||
priceFilter: filters.priceFilter,
|
||||
dateRangeFilter: filters.dateRangeFilter,
|
||||
keywordFilter: filters.keywordFilter,
|
||||
}}
|
||||
/>
|
||||
<ModalInMobile
|
||||
|
|
@ -267,6 +278,7 @@ SearchPageComponent.defaultProps = {
|
|||
amenities: config.custom.amenities,
|
||||
priceFilterConfig: config.custom.priceFilterConfig,
|
||||
dateRangeFilterConfig: config.custom.dateRangeFilterConfig,
|
||||
keywordFilterConfig: config.custom.keywordFilterConfig,
|
||||
activeListingId: null,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,12 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
},
|
||||
"paramName": "dates",
|
||||
},
|
||||
"keywordFilter": Object {
|
||||
"config": Object {
|
||||
"active": true,
|
||||
},
|
||||
"paramName": "keywords",
|
||||
},
|
||||
"priceFilter": Object {
|
||||
"config": Object {
|
||||
"max": 1000,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import * as IconSocialMediaInstagram from './components/IconSocialMediaInstagram
|
|||
import * as IconSocialMediaTwitter from './components/IconSocialMediaTwitter/IconSocialMediaTwitter.example';
|
||||
import * as IconSpinner from './components/IconSpinner/IconSpinner.example';
|
||||
import * as ImageCarousel from './components/ImageCarousel/ImageCarousel.example';
|
||||
import * as KeywordFilter from './components/KeywordFilter/KeywordFilter.example';
|
||||
import * as ListingCard from './components/ListingCard/ListingCard.example';
|
||||
import * as LocationAutocompleteInput from './components/LocationAutocompleteInput/LocationAutocompleteInput.example';
|
||||
import * as ManageListingCard from './components/ManageListingCard/ManageListingCard.example';
|
||||
|
|
@ -148,6 +149,7 @@ export {
|
|||
IconSocialMediaTwitter,
|
||||
IconSpinner,
|
||||
ImageCarousel,
|
||||
KeywordFilter,
|
||||
ListingCard,
|
||||
LocationAutocompleteInput,
|
||||
LoginForm,
|
||||
|
|
|
|||
|
|
@ -56,3 +56,8 @@ export const priceFilterConfig = {
|
|||
export const dateRangeFilterConfig = {
|
||||
active: true,
|
||||
};
|
||||
|
||||
// Activate keyword filter on search page
|
||||
export const keywordFilterConfig = {
|
||||
active: true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -310,6 +310,11 @@
|
|||
"InboxPage.statePendingPayment": "Pending payment",
|
||||
"InboxPage.stateRequested": "Requested",
|
||||
"InboxPage.title": "Inbox",
|
||||
"KeywordFilter.filterText": "Filter results by",
|
||||
"KeywordFilter.labelSelected": "\"{labelText}\"",
|
||||
"KeywordFilter.placeholder": "Enter a keyword…",
|
||||
"KeywordFilterPlainForm.labelSelected": "Keyword",
|
||||
"KeywordFilterPlainForm.placeholder": "Enter a keyword…",
|
||||
"LandingPage.schemaDescription": "Book a sauna using Saunatime or earn some income by sharing your sauna",
|
||||
"LandingPage.schemaTitle": "Book saunas everywhere | {siteTitle}",
|
||||
"ListingCard.hostedBy": "Hosted by {authorName}.",
|
||||
|
|
@ -682,6 +687,7 @@
|
|||
"SearchFilters.categoryLabel": "Category",
|
||||
"SearchFilters.filtersButtonLabel": "Filters",
|
||||
"SearchFilters.foundResults": "{count, number} {count, plural, one {result} other {results}}",
|
||||
"SearchFilters.keywordLabel": "Keyword",
|
||||
"SearchFilters.loadingResults": "Loading search results…",
|
||||
"SearchFilters.loadingResultsMobile": "Loading…",
|
||||
"SearchFilters.moreFiltersButton": "{count, plural, =0 {More filters} other {More filters • #}}",
|
||||
|
|
@ -692,6 +698,7 @@
|
|||
"SearchFiltersMobile.cancel": "CANCEL",
|
||||
"SearchFiltersMobile.categoryLabel": "Category",
|
||||
"SearchFiltersMobile.heading": "Filter saunas",
|
||||
"SearchFiltersMobile.keywordLabel": "Keyword",
|
||||
"SearchFiltersMobile.resetAll": "Reset all",
|
||||
"SearchFiltersMobile.showListings": "Show {count, number} {count, plural, one {sauna} other {saunas}}",
|
||||
"SearchFiltersPanel.apply": "Apply",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue