Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8623d6e
feat(infinite-hits): implement `transformItems`
francoischalifour Jul 20, 2018
56da733
feat(hits): implement `transformItems`
francoischalifour Jul 20, 2018
85033ec
feat(breadcrumb): implement `transformItems`
francoischalifour Jul 20, 2018
f5aec2e
feat(hierarchical-menu): implement `transformItems`
francoischalifour Jul 20, 2018
e9b2d4c
feat(menu): implement `transformItems`
francoischalifour Jul 20, 2018
e55dba2
feat(menu-select): implement `transformItems`
francoischalifour Jul 20, 2018
1818edb
feat(refinement-list): implement `transformItems`
francoischalifour Jul 23, 2018
526d308
feat(geo-search): implement `transformItems`
francoischalifour Jul 23, 2018
c898c4a
feat(sort-by): implement `transformItems`
francoischalifour Jul 23, 2018
ee19dd7
feat(hits-per-page): implement `transformItems`
francoischalifour Jul 23, 2018
93e7af4
feat(current-refined-values): implement `transformItems`
francoischalifour Jul 23, 2018
b0f8611
feat(numeric-refinement-list): implement `transformItems`
francoischalifour Jul 23, 2018
63840dc
feat(numeric-selector): implement `transformItems`
francoischalifour Jul 23, 2018
01e3bb4
refactor(hits): use default identity function for `transformItems`
francoischalifour Jul 27, 2018
ca82ede
refactor(infinite-hits): use default identity function for `transform…
francoischalifour Jul 27, 2018
547f028
test(hits-per-page): migrate to Jest
francoischalifour Jul 30, 2018
b23df37
test(menu): migrate to Jest
francoischalifour Jul 30, 2018
7ffc09b
test(numeric-refinement-list): migrate to Jest
francoischalifour Jul 30, 2018
d74fe50
test(sort-by-selector): migrate to Jest
francoischalifour Jul 30, 2018
9dd9064
test(infinite-hits): migrate to Jest
francoischalifour Jul 30, 2018
54b30cf
test(refinement-list): migrate to Jest
francoischalifour Jul 30, 2018
80ad6e5
test(hits): migrate to Jest
francoischalifour Jul 30, 2018
a8e58b3
test(hierarchical-menu): migrate to Jest
francoischalifour Jul 30, 2018
dcaf3ca
test(current-refined-values): migrate to Jest and remove Lodash imports
francoischalifour Jul 30, 2018
4be30e9
Merge branch 'develop' into feat/transform-items
bobylito Jul 30, 2018
7c10a17
test(refinement-list): update snapshots
francoischalifour Jul 30, 2018
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
Prev Previous commit
Next Next commit
feat(menu): implement transformItems
  • Loading branch information
francoischalifour committed Jul 25, 2018
commit e9b2d4c7dee65c22c982fc9b7f4a76738fc6e13f
16 changes: 16 additions & 0 deletions dev/app/builtin/stories/menu.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ export default () => {
);
})
)
.add(
'with transformed items',
wrapWithHits(container => {
window.search.addWidget(
instantsearch.widgets.menu({
container,
attributeName: 'categories',
transformItems: items =>
items.map(item => ({
...item,
label: `${item.label} (transformed)`,
})),
})
);
})
)
.add(
'with show more and header',
wrapWithHits(container => {
Expand Down
53 changes: 53 additions & 0 deletions src/connectors/menu/__tests__/connectMenu-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,59 @@ describe('connectMenu', () => {
]);
});

it('provides the correct transformed facet values', () => {
const widget = makeWidget({
attributeName: 'category',
transformItems: items =>
items.map(item => ({
...item,
label: 'transformed',
})),
});

const helper = jsHelper({}, '', widget.getConfiguration({}));
helper.search = sinon.stub();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we touch a test file that is still using sinon, we try to take the opportunity to replace it with Jest mocks. Can you do that? 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure! I initially didn't want to edit too much code as it was intended to be merged on the yet-to-be consequent feat/3.0 branch.


helper.toggleRefinement('category', 'Decoration');

widget.init({
helper,
state: helper.state,
});

const firstRenderingOptions = rendering.lastCall.args[0];
expect(firstRenderingOptions.items).toEqual([]);

widget.render({
results: new SearchResults(helper.state, [
{
hits: [],
facets: {
category: {
Decoration: 880,
},
},
},
{
facets: {
category: {
Decoration: 880,
Outdoor: 47,
},
},
},
]),
state: helper.state,
helper,
});

const secondRenderingOptions = rendering.lastCall.args[0];
expect(secondRenderingOptions.items).toEqual([
expect.objectContaining({ label: 'transformed' }),
expect.objectContaining({ label: 'transformed' }),
]);
});

describe('showMore', () => {
it('should throw when `showMoreLimit` is lower than `limit`', () => {
expect(() =>
Expand Down
19 changes: 12 additions & 7 deletions src/connectors/menu/connectMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ search.addWidget(
[ limit ],
[ showMoreLimit ]
[ sortBy = ['name:asc'] ]
[ transformItems ]
})
);
Full documentation available at https://community.algolia.com/instantsearch.js/v2/connectors/connectMenu.html
Expand All @@ -40,6 +41,7 @@ Full documentation available at https://community.algolia.com/instantsearch.js/v
* @property {string[]|function} [sortBy = ['name:asc']] How to sort refinements. Possible values: `count|isRefined|name:asc|name:desc`.
*
* You can also use a sort function that behaves like the standard Javascript [compareFunction](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Syntax).
* @property {function(object[]):object[]} [transformItems] Function to transform the items passed to the templates.
*/

/**
Expand Down Expand Up @@ -113,6 +115,7 @@ export default function connectMenu(renderFn, unmountFn) {
limit = 10,
sortBy = ['name:asc'],
showMoreLimit,
transformItems = items => items,
} = widgetParams;

if (!attributeName || (!isNaN(showMoreLimit) && showMoreLimit < limit)) {
Expand Down Expand Up @@ -200,13 +203,15 @@ export default function connectMenu(renderFn, unmountFn) {
render({ results, instantSearchInstance }) {
const facetItems =
results.getFacetValues(attributeName, { sortBy }).data || [];
const items = facetItems
.slice(0, this.getLimit())
.map(({ name: label, path: value, ...item }) => ({
...item,
label,
value,
}));
const items = transformItems(
facetItems
.slice(0, this.getLimit())
.map(({ name: label, path: value, ...item }) => ({
...item,
label,
value,
}))
);

this.toggleShowMore = this.createToggleShowMore({
results,
Expand Down
60 changes: 59 additions & 1 deletion src/widgets/menu/__tests__/__snapshots__/menu-test.js.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,64 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`menu snapshot 1`] = `
exports[`menu render renders transformed items 1`] = `
<HeaderFooter-AutoHide
canToggleShowMore={false}
collapsible={false}
createURL={[Function]}
cssClasses={
Object {
"active": "ais-menu--item__active",
"body": "ais-menu--body",
"count": "ais-menu--count",
"footer": "ais-menu--footer",
"header": "ais-menu--header",
"item": "ais-menu--item",
"link": "ais-menu--link",
"list": "ais-menu--list",
"root": "ais-menu",
}
}
facetValues={
Array [
Object {
"label": "foo",
"transformed": true,
"url": "#",
"value": undefined,
},
Object {
"label": "bar",
"transformed": true,
"url": "#",
"value": undefined,
},
]
}
isShowingMore={false}
shouldAutoHideContainer={false}
showMore={false}
templateProps={
Object {
"templates": Object {
"footer": "",
"header": "",
"item": "<a class=\\"{{cssClasses.link}}\\" href=\\"{{url}}\\">{{label}} <span class=\\"{{cssClasses.count}}\\">{{#helpers.formatNumber}}{{count}}{{/helpers.formatNumber}}</span></a>",
},
"templatesConfig": undefined,
"transformData": undefined,
"useCustomCompileOptions": Object {
"footer": false,
"header": false,
"item": false,
},
}
}
toggleRefinement={[Function]}
toggleShowMore={[Function]}
/>
`;

exports[`menu render snapshot 1`] = `
<HeaderFooter-AutoHide
canToggleShowMore={false}
collapsible={false}
Expand Down
79 changes: 57 additions & 22 deletions src/widgets/menu/__tests__/menu-test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import menu from '../menu';

import sinon from 'sinon';

describe('menu', () => {
it('throws an exception when no attributeName', () => {
const container = document.createElement('div');
Expand All @@ -13,26 +11,63 @@ describe('menu', () => {
expect(menu.bind(null, { attributeName })).toThrow(/^Usage/);
});

it('snapshot', () => {
const data = { data: [{ name: 'foo' }, { name: 'bar' }] };
const results = { getFacetValues: sinon.spy(() => data) };
const state = { toggleRefinement: sinon.spy() };
const helper = {
toggleRefinement: sinon.stub().returnsThis(),
search: sinon.spy(),
state,
};
const createURL = () => '#';
const ReactDOM = { render: sinon.spy() };
menu.__Rewire__('render', ReactDOM.render);
const widget = menu({
container: document.createElement('div'),
attributeName: 'test',
describe('render', () => {
let ReactDOM;
let data;
let results;
let state;
let helper;

beforeEach(() => {
ReactDOM = { render: jest.fn() };
menu.__Rewire__('render', ReactDOM.render);

data = { data: [{ name: 'foo' }, { name: 'bar' }] };
results = { getFacetValues: jest.fn(() => data) };
state = { toggleRefinement: jest.fn() };
helper = {
toggleRefinement: jest.fn().mockReturnThis(),
search: jest.fn(),
state,
};
});

it('snapshot', () => {
const widget = menu({
container: document.createElement('div'),
attributeName: 'test',
});

widget.init({
helper,
createURL: () => '#',
instantSearchInstance: { templatesConfig: undefined },
});
widget.render({ results, state });

expect(ReactDOM.render.mock.calls[0][0]).toMatchSnapshot();
});

it('renders transformed items', () => {
const widget = menu({
container: document.createElement('div'),
attributeName: 'test',
transformItems: items =>
items.map(item => ({ ...item, transformed: true })),
});

widget.init({
helper,
createURL: () => '#',
instantSearchInstance: { templatesConfig: undefined },
});
widget.render({ results, state });

expect(ReactDOM.render.mock.calls[0][0]).toMatchSnapshot();
});

afterEach(() => {
menu.__ResetDependency__('render');
});
const instantSearchInstance = { templatesConfig: undefined };
widget.init({ helper, createURL, instantSearchInstance });
widget.render({ results, createURL, state });
expect(ReactDOM.render.firstCall.args[0]).toMatchSnapshot();
menu.__ResetDependency__('render');
});
});
13 changes: 11 additions & 2 deletions src/widgets/menu/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ menu({
[ transformData.{item} ],
[ autoHideContainer ],
[ showMore.{templates: {active, inactive}, limit} ],
[ collapsible=false ]
[ collapsible=false ],
[ transformItems ]
})`;

/**
Expand Down Expand Up @@ -136,6 +137,7 @@ menu({
* @property {boolean} [autoHideContainer=true] Hide the container when there are no items in the menu.
* @property {MenuCSSClasses} [cssClasses] CSS classes to add to the wrapping elements.
* @property {boolean|{collapsible: boolean}} [collapsible=false] Hide the widget body and footer when clicking on header.
* @property {function(object[]):object[]} [transformItems] Function to transform the items passed to the templates.
*/

/**
Expand Down Expand Up @@ -174,6 +176,7 @@ export default function menu({
transformData,
autoHideContainer = true,
showMore = false,
transformItems,
}) {
if (!container) {
throw new Error(usage);
Expand Down Expand Up @@ -220,7 +223,13 @@ export default function menu({
const makeWidget = connectMenu(specializedRenderer, () =>
unmountComponentAtNode(containerNode)
);
return makeWidget({ attributeName, limit, sortBy, showMoreLimit });
return makeWidget({
attributeName,
limit,
sortBy,
showMoreLimit,
transformItems,
});
} catch (e) {
throw new Error(usage);
}
Expand Down