Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pricing filter #944

Merged
merged 16 commits into from
Oct 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@ way to update this template, but currently, we follow a pattern:

---

## v.2.1.1 2018-10-23
## v2.2.0 2018-11-XX

* [add] SearchPage: adds PriceFilter (and RangeSlider, FieldRangeSlider, PriceFilterForm).

**Note:** You must define min and max for the filter in `src/marketplace-custom-config.js`.
Current maximum value for the range is set to 1000 (USD/EUR/currency units). In addition, this
fixes or removes component examples that don't work in StyleguidePage.

[#944](https://github.com/sharetribe/flex-template-web/pull/944)

## v2.1.1 2018-10-23

* [add] Added initial documentation about routing and loading data.
[#941](https://github.com/sharetribe/flex-template-web/pull/941)
Expand Down
15 changes: 10 additions & 5 deletions docs/search-filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ filters rely on listing's indexed data.

## Filter types

The Flex template for web has two different filter types: _select single_ and _select multiple_. The
_select single_ one can be used to filter out search result with only one value per search
parameter. The _select multiple_ filters on the other hand can take multiple values for a single
search parameter.
The Flex template for web has three different filter types: _price filter_, _select single_ and
_select multiple_. The _price filter_ is for the specific case of filtering listings with a price
range.

These two filter types are implemented with four different components, a standard and a plain one:
> NOTE: price filter should be configured from `src/marketplace-custom-config.js`. Current maximum
> value for the range is set to 1000 (USD/EUR).

Other two filter types can be used with extended data. The _select single_ one can be used to filter
out search result with only one value per search parameter. The _select multiple_ filters on the
other hand can take multiple values for a single search parameter. These two filter types for
extended data are implemented with four different components, a standard and a plain one:

* Select single filter: `SelectSingleFilter` and `SelectSingleFilterPlain`
* Select multiple filter: `SelectMultipleFilter` and `SelectMultipleFilterPlain`
Expand Down
73 changes: 73 additions & 0 deletions src/components/FieldRangeSlider/FieldRangeSlider.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* eslint-disable no-console */
import React from 'react';
import { Form as FinalForm, FormSpy } from 'react-final-form';
import { Button } from '../../components';
import FieldRangeSlider from './FieldRangeSlider';

const formName = 'Styleguide.FieldRangeSlider.Form';

const FormComponent = props => (
<FinalForm
{...props}
formId={formName}
render={fieldRenderProps => {
const {
formId,
handleSubmit,
onChange,
invalid,
pristine,
submitting,
min,
max,
step,
handles,
} = fieldRenderProps;
const submitDisabled = invalid || pristine || submitting;

return (
<form
onSubmit={e => {
e.preventDefault();
handleSubmit(e);
}}
>
<FormSpy onChange={onChange} />

<FieldRangeSlider
id={`${formId}.range`}
name="range"
label="Select range"
min={min}
max={max}
step={step}
handles={handles}
/>

<Button style={{ marginTop: 24 }} type="submit" disabled={submitDisabled}>
Submit
</Button>
</form>
);
}}
/>
);

export const FieldRangeSliderForm = {
component: FormComponent,
props: {
min: 0,
max: 1000,
step: 5,
handles: [333, 666],
onChange: formState => {
if (formState.dirty) {
console.log('form values changed to:', formState.values);
}
},
onSubmit: values => {
console.log('submit values:', values);
},
},
group: 'custom inputs',
};
32 changes: 32 additions & 0 deletions src/components/FieldRangeSlider/FieldRangeSlider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { Field } from 'react-final-form';
import classNames from 'classnames';
import { RangeSlider } from '../../components';

const RangeSliderInput = props => {
const { input, handles, ...rest } = props;
const { value, ...inputProps } = input;

const currentHandles = Array.isArray(value) ? value : handles;
return <RangeSlider {...inputProps} {...rest} handles={currentHandles} />;
};

const FieldRangeSlider = props => {
const { rootClassName, className, id, label, ...rest } = props;

if (label && !id) {
throw new Error('id required when a label is given');
}

const inputProps = { id, ...rest };
const classes = classNames(rootClassName, className);

return (
<div className={classes}>
{label ? <label htmlFor={id}>{label}</label> : null}
<Field component={RangeSliderInput} {...inputProps} />
</div>
);
};

export default FieldRangeSlider;
3 changes: 3 additions & 0 deletions src/components/Footer/Footer.example.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.example {
overflow-x: hidden;
}
3 changes: 2 additions & 1 deletion src/components/Footer/Footer.example.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Footer from './Footer';
import css from './Footer.example.css';

export const Default = {
component: Footer,
props: {},
props: { className: css.example },
};
12 changes: 12 additions & 0 deletions src/components/ImageCarousel/ImageCarousel.example.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@import '../../marketplace.css';

.root {
position: relative;
width: 100%;
height: 100%;
padding: 0;

@media (--viewportMedium) {
padding: 100px 10vw;
}
}
7 changes: 4 additions & 3 deletions src/components/ImageCarousel/ImageCarousel.example.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { types as sdkTypes } from '../../util/sdkLoader';
import ImageCarousel from './ImageCarousel';
import css from './ImageCarousel.example.css';

const { UUID } = sdkTypes;

Expand Down Expand Up @@ -122,15 +123,15 @@ const ImageCarouselWrapper = props => {

export const NoImages = {
component: ImageCarouselWrapper,
props: { images: [] },
props: { images: [], rootClassName: css.root },
};

export const SingleImage = {
component: ImageCarouselWrapper,
props: { images: [imageSquare] },
props: { images: [imageSquare], rootClassName: css.root },
};

export const MultipleImages = {
component: ImageCarouselWrapper,
props: { images: [imageLandscape, imagePortrait, imageSquare] },
props: { images: [imageLandscape, imagePortrait, imageSquare], rootClassName: css.root },
};
2 changes: 1 addition & 1 deletion src/components/Menu/Menu.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const MenuBasic = {

const MenuOnRight = () => {
return (
<div style={{ width: '50px', marginLeft: 'auto', marginRight: '36px' }}>
<div style={{ width: '68px', marginLeft: 'auto', marginRight: '36x' }}>
<MenuWrapper />
</div>
);
Expand Down
77 changes: 77 additions & 0 deletions src/components/PriceFilter/PriceFilter.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';
import { withRouter } from 'react-router-dom';
import { stringify, parse } from '../../util/urlHelpers';

import PriceFilter from './PriceFilter';

const URL_PARAM = 'pub_price';
const RADIX = 10;

// Helper for submitting example
const handleSubmit = (urlParam, values, history) => {
const { minPrice, maxPrice } = values || {};
const queryParams =
minPrice != null && maxPrice != null
? `?${stringify({ [urlParam]: [minPrice, maxPrice].join(',') })}`
: '';
history.push(`${window.location.pathname}${queryParams}`);
};

const PriceFilterWrapper = withRouter(props => {
const { history, location } = props;

const params = parse(location.search);
const price = params[URL_PARAM];
const valuesFromParams = !!price ? price.split(',').map(v => Number.parseInt(v, RADIX)) : [];
const initialValues = !!price
? {
minPrice: valuesFromParams[0],
maxPrice: valuesFromParams[1],
}
: null;

return (
<PriceFilter
{...props}
initialValues={initialValues}
onSubmit={(urlParam, values) => {
console.log('Submit PriceFilterForm with (unformatted) values:', values);
handleSubmit(urlParam, values, history);
}}
/>
);
});

export const PriceFilterPopup = {
component: PriceFilterWrapper,
props: {
id: 'PriceFilterPopupExample',
urlParam: URL_PARAM,
min: 0,
max: 1000,
step: 5,
liveEdit: false,
showAsPopup: true,
contentPlacementOffset: -14,
// initialValues: handled inside wrapper
// onSubmit: handled inside wrapper
},
group: 'misc',
};

export const PriceFilterPlain = {
component: PriceFilterWrapper,
props: {
id: 'PriceFilterPlainExample',
urlParam: URL_PARAM,
min: 0,
max: 1000,
step: 5,
liveEdit: true,
showAsPopup: false,
contentPlacementOffset: -14,
// initialValues: handled inside wrapper
// onSubmit: handled inside wrapper
},
group: 'misc',
};
19 changes: 19 additions & 0 deletions src/components/PriceFilter/PriceFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { bool } from 'prop-types';
import PriceFilterPlain from './PriceFilterPlain';
import PriceFilterPopup from './PriceFilterPopup';

const PriceFilter = props => {
const { showAsPopup, ...rest } = props;
return showAsPopup ? <PriceFilterPopup {...rest} /> : <PriceFilterPlain {...rest} />;
};

PriceFilter.defaultProps = {
showAsPopup: false,
};

PriceFilter.propTypes = {
showAsPopup: bool,
};

export default PriceFilter;
57 changes: 57 additions & 0 deletions src/components/PriceFilter/PriceFilterPlain.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@import '../../marketplace.css';

.root {
position: relative;
padding-top: 24px;
padding-bottom: 17px;
border-bottom: 1px solid var(--matterColorNegative);
}

.filterLabel,
.filterLabelSelected {
@apply --marketplaceH3FontStyles;

/* Baseline adjustment for label text */
margin-top: 0;
margin-bottom: 12px;
padding: 4px 0 2px 0;
}

.filterLabel {
color: var(--matterColorDark);
}

.filterLabelSelected {
color: var(--marketplaceColor);
}

.labelButton {
/* Override button styles */
outline: none;
text-align: left;
border: none;
padding: 0;
cursor: pointer;
}

.clearButton {
@apply --marketplaceH5FontStyles;
font-weight: var(--fontWeightMedium);
color: var(--matterColorAnti);

/* Layout */
display: inline;
float: right;
margin-top: 6px;
padding: 0;

/* Override button styles */
outline: none;
text-align: left;
border: none;

&:focus,
&:hover {
color: var(--matterColor);
}
}
Loading