Skip to content
This repository has been archived by the owner on Dec 30, 2022. It is now read-only.

feat(infiniteHits): add previous button #2296

Merged
merged 40 commits into from
Apr 25, 2019
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
51b4acb
feat(infiniteHits): add previous button
yannickcr Apr 9, 2019
646e3ff
Fix missing refine method
yannickcr Apr 9, 2019
b377f98
Increase bundlesize limit
yannickcr Apr 9, 2019
07c9dec
Fix tests
yannickcr Apr 9, 2019
d3f2dfa
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 9, 2019
6ea2fdf
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 10, 2019
33b2f02
Clear cache on query or refinement changes
yannickcr Apr 11, 2019
dea4466
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 12, 2019
b51e096
Increase bundlesize limit (again...)
yannickcr Apr 12, 2019
f9ede9b
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 15, 2019
a11df53
Replace JSON.stringify by lodash.isEqual
yannickcr Apr 15, 2019
2ec59fc
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 16, 2019
0db0f38
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 17, 2019
0676a22
Update packages/react-instantsearch-dom/src/components/__tests__/Infi…
samouss Apr 18, 2019
2c44b53
Update packages/react-instantsearch-dom/src/components/__tests__/Infi…
samouss Apr 18, 2019
eeacfe2
Update stories/util.js
samouss Apr 18, 2019
6924415
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 18, 2019
5124854
Add test for showPrevious prop
yannickcr Apr 18, 2019
ff6ff95
Keep configure in searchState
yannickcr Apr 18, 2019
8bbae41
Update WrapWithHits component to use hooks
yannickcr Apr 18, 2019
5086454
Prettify
yannickcr Apr 18, 2019
9d28c2f
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 19, 2019
3bb78d0
Rename variables for more clarity
yannickcr Apr 19, 2019
bd93cfe
Add comments for test clarity
yannickcr Apr 19, 2019
5258ab7
Fix prevState initial value
yannickcr Apr 19, 2019
59fa34d
Restore refine function
yannickcr Apr 19, 2019
6449b80
Wording
yannickcr Apr 19, 2019
9de42b8
Move refine function
yannickcr Apr 19, 2019
273706f
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 19, 2019
d2022c9
Prefer the logical expression over ternary in JSX
yannickcr Apr 23, 2019
ece0fca
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 23, 2019
67a03ba
Update test expectation for more clarity
yannickcr Apr 23, 2019
549ff5a
Rename variables for more clarity
yannickcr Apr 23, 2019
c91b8c0
Fix original refine method
yannickcr Apr 23, 2019
c668edb
Delete duplicated test
yannickcr Apr 23, 2019
de0c49d
Fix test expectation
yannickcr Apr 23, 2019
30e244e
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 24, 2019
48faedd
Fix refine function (for real)
yannickcr Apr 24, 2019
1dadd95
Merge branch 'master' into feat/infinitehits-previous-button
yannickcr Apr 25, 2019
7c0250e
Inline index default value
yannickcr Apr 25, 2019
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
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,19 @@
},
{
"path": "packages/react-instantsearch/dist/umd/Connectors.min.js",
"maxSize": "40.50 kB"
"maxSize": "40.75 kB"
},
{
"path": "packages/react-instantsearch/dist/umd/Dom.min.js",
"maxSize": "63.00 kB"
},
{
"path": "packages/react-instantsearch-core/dist/umd/ReactInstantSearchCore.min.js",
"maxSize": "41.25 kB"
"maxSize": "41.50 kB"
},
{
"path": "packages/react-instantsearch-dom/dist/umd/ReactInstantSearchDOM.min.js",
"maxSize": "62.75 kB"
"maxSize": "63.00 kB"
},
{
"path": "packages/react-instantsearch-dom-maps/dist/umd/ReactInstantSearchDOMMaps.min.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ describe('connectInfiniteHits', () => {
context: {
ais: {
mainTargetedIndex: 'index',
onInternalStateUpdate: jest.fn(),
},
},
refine: jest.fn(),
});

it('provides the current hits to the component', () => {
Expand All @@ -23,7 +25,11 @@ describe('connectInfiniteHits', () => {

expect(props).toEqual({
hits: hits.map(hit => expect.objectContaining(hit)),
hasPrevious: false,
hasMore: true,
refine: expect.any(Function),
refinePrevious: expect.any(Function),
refineNext: expect.any(Function),
});
});

Expand Down Expand Up @@ -55,6 +61,43 @@ describe('connectInfiniteHits', () => {
expect(res2.hasMore).toBe(true);
});

it('prepend hits internally', () => {
const context = createSingleIndexContext();
const getProvidedProps = connect.getProvidedProps.bind(context);

const initialPageHits = [{}, {}];
const previousPageHits = [{}, {}];
const initialPageProps = getProvidedProps(null, null, {
results: {
hits: initialPageHits,
page: 1,
hitsPerPage: 2,
nbPages: 3,
},
});

expect(initialPageProps.hits).toEqual(
initialPageHits.map(hit => expect.objectContaining(hit))
);
expect(initialPageProps.hasPrevious).toBe(true);

const previousPageProps = getProvidedProps(null, null, {
results: {
hits: previousPageHits,
page: 0,
hitsPerPage: 2,
nbPages: 3,
},
});

expect(previousPageProps.hits).toEqual(
[...previousPageHits, ...initialPageHits].map(hit =>
expect.objectContaining(hit)
)
);
expect(previousPageProps.hasPrevious).toBe(false);
});

it('accumulate hits internally while changing hitsPerPage configuration', () => {
const context = createSingleIndexContext();
const getProvidedProps = connect.getProvidedProps.bind(context);
Expand Down Expand Up @@ -373,7 +416,55 @@ describe('connectInfiniteHits', () => {
expect(props.hasMore).toBe(false);
});

it('adds 1 to page when calling refine', () => {
it('calls refine with next page when calling refineNext', () => {
const context = createSingleIndexContext();
const getProvidedProps = connect.getProvidedProps.bind(context);

const hits = [{}, {}];

const props = getProvidedProps(
{},
{},
{
results: {
hits,
page: 2,
hitsPerPage: 2,
nbPages: 3,
},
}
);

props.refineNext.apply(context);

expect(context.refine.mock.calls[0][0]).toEqual(3);
samouss marked this conversation as resolved.
Show resolved Hide resolved
});

it('calls refine with previous page when calling refinePrevious', () => {
const context = createSingleIndexContext();
const getProvidedProps = connect.getProvidedProps.bind(context);

const hits = [{}, {}];

const props = getProvidedProps(
{},
{},
{
results: {
hits,
page: 2,
hitsPerPage: 2,
nbPages: 3,
},
}
);

props.refinePrevious.apply(context);

expect(context.refine.mock.calls[0][0]).toEqual(1);
samouss marked this conversation as resolved.
Show resolved Hide resolved
});

it('adds 1 to page when calling refine without index', () => {
const context = createSingleIndexContext();
const refine = connect.refine.bind(context);

Expand All @@ -387,6 +478,19 @@ describe('connectInfiniteHits', () => {
expect(state2).toEqual({ page: 3 });
});

it('set page to the corresponding index', () => {
const context = createSingleIndexContext();
const refine = connect.refine.bind(context);

const props = {};
const state0 = {};
const index = 5;

const state1 = refine(props, state0, index);
// `index` is indexed from 0 but page number is indexed from 1
expect(state1).toEqual({ page: 6 });
});

it('automatically converts String state to Number', () => {
const context = createSingleIndexContext();
const refine = connect.refine.bind(context);
Expand Down Expand Up @@ -418,7 +522,11 @@ describe('connectInfiniteHits', () => {

const expectation = {
hits: [{}, {}, {}].map(hit => expect.objectContaining(hit)),
hasPrevious: true,
hasMore: true,
refine: expect.any(Function),
refinePrevious: expect.any(Function),
refineNext: expect.any(Function),
};

const actual = getProvidedProps(props, searchState, searchResults);
Expand Down Expand Up @@ -450,7 +558,11 @@ describe('connectInfiniteHits', () => {

expect(props).toEqual({
hits: hits.map(hit => expect.objectContaining(hit)),
hasPrevious: false,
hasMore: true,
refine: expect.any(Function),
refinePrevious: expect.any(Function),
refineNext: expect.any(Function),
});
});

Expand Down Expand Up @@ -480,6 +592,32 @@ describe('connectInfiniteHits', () => {
expect(res2.hasMore).toBe(true);
});

it('prepend hits internally', () => {
const context = createMultiIndexContext();
const getProvidedProps = connect.getProvidedProps.bind(context);

const hits = [{}, {}];
const hits0 = [{}, {}];

const res1 = getProvidedProps(null, null, {
samouss marked this conversation as resolved.
Show resolved Hide resolved
results: { second: { hits, page: 2, hitsPerPage: 2, nbPages: 3 } },
});

expect(res1.hits).toEqual(hits.map(hit => expect.objectContaining(hit)));
expect(res1.hasPrevious).toBe(true);

const res0 = getProvidedProps(null, null, {
results: {
second: { hits: hits0, page: 1, hitsPerPage: 2, nbPages: 3 },
},
});

expect(res0.hits).toEqual(
[...hits0, ...hits].map(hit => expect.objectContaining(hit))
);
expect(res0.hasPrevious).toBe(true);
});

it('accumulate hits internally while changing hitsPerPage configuration', () => {
const context = createMultiIndexContext();
const getProvidedProps = connect.getProvidedProps.bind(context);
Expand Down Expand Up @@ -530,6 +668,44 @@ describe('connectInfiniteHits', () => {
expect(res3.hasMore).toBe(true);
});

it('should not accumulate hits internally while changing query', () => {
const context = createMultiIndexContext();
const getProvidedProps = connect.getProvidedProps.bind(context);

const hits = [{}, {}, {}, {}, {}, {}];
const hits2 = [{}, {}, {}, {}, {}, {}];

const res1 = getProvidedProps(null, null, {
results: {
second: {
hits,
page: 0,
hitsPerPage: 6,
nbPages: 10,
_state: { page: 0, query: 'a' },
},
},
});

expect(res1.hits).toEqual(hits.map(hit => expect.objectContaining(hit)));
expect(res1.hasMore).toBe(true);

const res2 = getProvidedProps(null, null, {
results: {
second: {
hits: hits2,
page: 0,
hitsPerPage: 6,
nbPages: 10,
_state: { page: 0, query: 'b' },
},
},
});

expect(res2.hits).toEqual(hits2.map(hit => expect.objectContaining(hit)));
expect(res2.hasMore).toBe(true);
});

it('should not reset while accumulating results', () => {
const context = createMultiIndexContext();
const getProvidedProps = connect.getProvidedProps.bind(context);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isEqual } from 'lodash';

import createConnector from '../core/createConnector';
import {
getCurrentRefinementValue,
Expand Down Expand Up @@ -45,39 +47,63 @@ export default createConnector({
const results = getResults(searchResults, this.context);

this._allResults = this._allResults || [];
this._previousPage = this._previousPage || 0;
this._prevState = this._prevState || {};

if (!results) {
return {
hits: [],
hasPrevious: false,
hasMore: false,
refine: () => {},
refinePrevious: () => {},
refineNext: () => {},
};
}

const { hits, hitsPerPage, page, nbPages } = results;
const {
page,
hits,
hitsPerPage,
nbPages,
_state: { page: p, ...currentState } = {},
samouss marked this conversation as resolved.
Show resolved Hide resolved
} = results;

const hitsWithPositions = addAbsolutePositions(hits, hitsPerPage, page);
const hitsWithPositionsAndQueryID = addQueryID(
hitsWithPositions,
results.queryID
);

if (page === 0) {
this._allResults = hitsWithPositionsAndQueryID;
} else if (page > this._previousPage) {
if (
this._firstReceivedPage === undefined ||
!isEqual(currentState, this._prevState)
samouss marked this conversation as resolved.
Show resolved Hide resolved
) {
this._allResults = [...hitsWithPositionsAndQueryID];
this._firstReceivedPage = page;
this._lastReceivedPage = page;
} else if (this._lastReceivedPage < page) {
this._allResults = [...this._allResults, ...hitsWithPositionsAndQueryID];
} else if (page < this._previousPage) {
this._allResults = hitsWithPositionsAndQueryID;
this._lastReceivedPage = page;
} else if (this._firstReceivedPage > page) {
this._allResults = [...hitsWithPositionsAndQueryID, ...this._allResults];
this._firstReceivedPage = page;
}

this._prevState = currentState;

const hasPrevious = this._firstReceivedPage > 0;
const lastPageIndex = nbPages - 1;
const hasMore = page < lastPageIndex;

this._previousPage = page;
const refinePrevious = () => this.refine(this._firstReceivedPage - 1);
const refineNext = () => this.refine(this._lastReceivedPage + 1);

return {
hits: this._allResults,
hasPrevious,
hasMore,
refine: refineNext,
samouss marked this conversation as resolved.
Show resolved Hide resolved
refinePrevious,
refineNext,
};
},

Expand All @@ -87,10 +113,13 @@ export default createConnector({
});
},

refine(props, searchState) {
refine(
props,
searchState,
index = getCurrentRefinement(props, searchState, this.context)
) {
const id = getId();
const nextPage = getCurrentRefinement(props, searchState, this.context) + 1;
const nextValue = { [id]: nextPage };
const nextValue = { [id]: index + 1 }; // `index` is indexed from 0 but page number is indexed from 1
const resetPage = false;
return refineValue(searchState, nextValue, this.context, resetPage);
},
Expand Down
Loading