Add custom sorting option
Componens
To add a new custom sorting option, you would need to update three components, for the desktop sorting and for mobile sorting. For instance, if you want to create a custom option for alphabetical sorting, you would need to update item props in both components mentioned above. Here is the example:
/**
* @module components/Sorting
*/
import React from 'react';
import { connectSort } from '@findify/react-connect';
import { compose, withPropsOnChange, setDisplayName, withHandlers, branch, renderNothing } from 'recompose';
import withTheme from 'helpers/withTheme';
import { is, Map } from 'immutable';
import view from 'components/Sorting/view';
import styles from 'components/Sorting/styles.css';
export default compose(
setDisplayName('Sorting'),
withTheme(styles),
connectSort,
withPropsOnChange(['config'], ({ config, selected }) => {
const labels = config.getIn(['sorting', 'i18n', 'options']);
if (!labels) return;
const items = config.getIn(['sorting', 'options']).map(i =>
i.set('label', labels.get([i.get('field'), i.get('order')].filter(i => i).join('|')))
)
// here are the two options
// field: by which field the items should be sorted
// order: either 'asc' or 'desc'
// label: how those options should be named
.push(new Map({ field: 'title', order: 'asc', label: 'Name A-Z' }))
.push(new Map({ field: 'title', order: 'desc', label: 'Name Z-A' }));
return { items }
}),
withPropsOnChange(['selected'], ({ items, selected }) => ({
selectedItem: selected && items && items.find(i =>
is(i.get('order'), selected.get('order')) &&
is(i.get('field'), selected.get('field'))
)
})),
withHandlers({
onChangeSort: ({ onChangeSort }) => item => onChangeSort(item.get('field', 'default'), item.get('order', ''))
}),
branch(
({ items }) => !items,
renderNothing
)
)(view);
/**
* @module components/search/MobileSorting
*/
import React from 'react';
import { compose, setDisplayName, withProps, withHandlers } from "recompose";
import { connectSort } from '@findify/react-connect';
import withTheme from 'helpers/withTheme';
import { is, Map } from 'immutable';
import pure from 'helpers/pure';
import view from 'components/search/MobileSorting/view';
import styles from 'components/search/MobileSorting/styles.css';
export default compose(
pure,
setDisplayName('MobileSorting'),
withTheme(styles),
connectSort,
withProps(({ config, meta }) => {
const selected = meta.getIn(['sort', 0]);
const labels = config.getIn(['sorting', 'i18n', 'options']);
const items = config.getIn(['sorting', 'options']).map(i => i
.set('label',
labels.get([i.get('field'), i.get('order')].filter(i => i).join('|'))
)
.set('selected',
!!selected
? is(i.get('order'), selected.get('order')) && is(i.get('field'), selected.get('field'))
: i.get('field') === 'default'
)
)
//add these here
.push(new Map({ field: 'title', order: 'asc', label: 'Name A-Z', selected: selected && is('asc', selected.get('order')) && is('discount', selected.get('field'))}))
.push(new Map({ field: 'title', order: 'desc', label: 'Name Z-A', selected: selected && is('desc', selected.get('order')) && is('discount', selected.get('field'))}));
return { items }
}),
withHandlers({
setSorting: ({ items, onChangeSort }) => index =>
onChangeSort(items.getIn([index, 'field'], 'default'), items.getIn([index, 'order'], '')),
})
)(view)
/**
* @module components/search/MobileActions
*/
import React from 'react';
import { connectSort, connectQuery } from '@findify/react-connect';
import { compose, withHandlers, withPropsOnChange } from 'recompose';
import withEvents from 'helpers/withEvents';
import withTheme from 'helpers/withTheme';
import view from 'components/search/MobileActions/view';
import styles from 'components/search/MobileActions/styles.css';
// in MobileActions we can adjust the labels to update the labels on the mobile button
const labels = {
"default": "Popularity",
"created_at|desc": "Newest",
"price|desc": "Price: High to low",
"price|asc": "Price: Low to high",
"title|asc": "Name A-Z",
"title|desc": "Name Z-A",
};
export default compose(
withTheme(styles),
connectSort,
connectQuery,
withEvents(),
withHandlers({
showFacets: ({ emit }) => () => emit('showMobileFacets'),
showSort: ({ emit }) => () => emit('showMobileSort')
}),
withPropsOnChange(['selected'], ({ selected, config }) => ({
sorting: labels[!!selected
&& [selected.get('field'), selected.get('order')].join('|')
|| 'default'
]
})),
withPropsOnChange(['query'], ({ query }) => query.get('filters') && ({
total: query.get('filters').reduce(
// The workaround to not sum the nested category filters
(acc, filter, key) => acc + (/category[2-9]/.test(key) ? 0 : filter.size)
, 0)
}))
)(view);
Alternatively, you could create a new items list instead of pushing new items to the existing list within the components components/Sorting/index.ts
and components/search/MobileSorting/index.ts
.
Let's try it with an example were we want to change the default order of the labels:
/**
* @module components/Sorting
*/
import React from 'react';
import { connectSort } from '@findify/react-connect';
import { compose, withPropsOnChange, setDisplayName, withHandlers, branch, renderNothing } from 'recompose';
import withTheme from 'helpers/withTheme';
// import Map and List from immutable
import { is, Map, List } from 'immutable';
import view from 'components/Sorting/view';
import styles from 'components/Sorting/styles.css';
export default compose(
setDisplayName('Sorting'),
withTheme(styles),
connectSort,
withPropsOnChange(['config'], ({ config, selected }) => {
const labels = config.getIn(['sorting', 'i18n', 'options']);
if (!labels) return;
// replace config items with a new List
const items = new List([
new Map({ field: 'default', order: '', label: 'Best Selling' }),
new Map({ field: 'created_at', order: 'desc', label: 'Newest' }),
new Map({ field: 'price', order: 'asc', label: "Price $ - $" + "$" }),
new Map({ field: 'price', order: 'desc', label: "Price $" + "$ - $" }),
])
return { items }
}),
withPropsOnChange(['selected'], ({ items, selected }) => ({
selectedItem: selected && items && items.find(i =>
is(i.get('order'), selected.get('order')) &&
is(i.get('field'), selected.get('field'))
)
})),
withHandlers({
onChangeSort: ({ onChangeSort }) => item => onChangeSort(item.get('field', 'default'), item.get('order', ''))
}),
branch(
({ items }) => !items,
renderNothing
)
)(view);
/**
* @module components/search/MobileSorting
*/
import React from 'react';
import { compose, setDisplayName, withProps, withHandlers } from "recompose";
import { connectSort } from '@findify/react-connect';
import withTheme from 'helpers/withTheme';
// import List and Map from immutable
import { is, List, Map } from 'immutable';
import pure from 'helpers/pure';
import view from 'components/search/MobileSorting/view';
import styles from 'components/search/MobileSorting/styles.css';
export default compose(
pure,
setDisplayName('MobileSorting'),
withTheme(styles),
connectSort,
withProps(({ config, meta }) => {
const selected = meta.getIn(['sort', 0]);
const labels = config.getIn(['sorting', 'i18n', 'options']);
// replace config items with a new List
const items = new List([
new Map({ field: 'default', order: '', label: 'Best Selling' }),
new Map({ field: 'created_at', order: 'desc', label: 'Newest' }),
new Map({ field: 'price', order: 'asc', label: 'Price $ - $$' }),
new Map({ field: 'price', order: 'desc', label: 'Price $$ - $' }),
]).map(i => i
.set('selected',
!!selected
? is(i.get('order'), selected.get('order')) && is(i.get('field'), selected.get('field'))
: i.get('field') === 'default'
)
);
return { items }
}),
withHandlers({
setSorting: ({ items, onChangeSort, update }) => index =>
update('sort', [{field: items.getIn([index, 'field'], 'default'), order: items.getIn([index, 'order'])}, {field: 'title', order: 'asc'}])
})
)(view)
onChangeSort: ({ update }) => item => update('sort', [{field: item.get('field', 'default'), order: item.get('order', '')}, {field: 'title', order: 'asc'}])
/**
* @module components/search/MobileActions
*/
import React from 'react';
import { connectSort, connectQuery } from '@findify/react-connect';
import { compose, withHandlers, withPropsOnChange } from 'recompose';
import withEvents from 'helpers/withEvents';
import withTheme from 'helpers/withTheme';
import view from 'components/search/MobileActions/view';
import styles from 'components/search/MobileActions/styles.css';
// // in MobileActions we can adjust the labels to update the labels on the mobile button
const labels = {
"default": "Best Selling",
"created_at|desc": "Newest",
"price|desc": "Price $" + "$ - $",
"price|asc": "Price $ - $" + "$",
};
export default compose(
withTheme(styles),
connectSort,
connectQuery,
withEvents(),
withHandlers({
showFacets: ({ emit }) => () => emit('showMobileFacets'),
showSort: ({ emit }) => () => emit('showMobileSort')
}),
withPropsOnChange(['selected'], ({ selected, config }) => ({
sorting: labels[!!selected && selected.get('order') && [selected.get('field'), selected.get('order')].join('|')
|| 'default'
]
})),
withPropsOnChange(['query'], ({ query }) => query.get('filters') && ({
total: query.get('filters').reduce(
// The workaround to not sum the nested category filters
(acc, filter, key) => acc + (/category[2-9]/.test(key) ? 0 : filter.size)
, 0)
}))
)(view);
As shown in the above example, if you would like the price labels to say something like "Price $ - $$" or "Price $$ - $", please use the following snippet as a reference. It is important to use "Price $" + "$ - $" instead of putting two $ signs directly after another, which would cause the store to break.
const labels = {
“default”: “Best Selling”,
“created_at|desc”: “Newest”,
“price|desc”: “Price $” + “$ - $“,
“price|asc”: “Price $ - $” + “$”
};
If you would like to additionally sort the items within the sorting options, to, for example, sort items in ascending order of prices alphabetically, you would need to rewrite onChangeSort/setSorting functions on desktop and mobile sortings:
withHandlers({
onChangeSort: ({ update }) => item => update('sort', [{field: item.get('field', 'default'), order: item.get('order', '')}, {field: 'title', order: 'asc'}])
}),
withHandlers({
setSorting: ({ items, onChangeSort, update }) => index =>
update('sort', [{field: items.getIn([index, 'field'], 'default'), order: items.getIn([index, 'order'])}, {field: 'title', order: 'asc'}])
})
Updated almost 4 years ago