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

Commit

Permalink
fix(ScrollTo): scroll to only if change triggered by the widget obser…
Browse files Browse the repository at this point in the history
…ved (#202)

* fix(ScrollTo): with pagination scroll to only if pagination widget refined

* improve

* fix: move to shallowEqual instead of deepEqual

* docs: add hasNotChanged props

* fix lint

* fix: make algorithm compatible with multi index

* docs: improve
  • Loading branch information
mthuret committed Aug 3, 2017
1 parent 3875da5 commit 2d76022
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 18 deletions.
5 changes: 3 additions & 2 deletions packages/react-instantsearch/src/components/ScrollTo.js
Expand Up @@ -6,11 +6,12 @@ class ScrollTo extends Component {
static propTypes = {
value: PropTypes.any,
children: PropTypes.node,
hasNotChanged: PropTypes.bool,
};

componentDidUpdate(prevProps) {
const { value } = this.props;
if (value !== prevProps.value) {
const { value, hasNotChanged } = this.props;
if (value !== prevProps.value && hasNotChanged) {
const el = findDOMNode(this);
el.scrollIntoView();
}
Expand Down
43 changes: 40 additions & 3 deletions packages/react-instantsearch/src/connectors/connectScrollTo.js
@@ -1,15 +1,21 @@
import PropTypes from 'prop-types';

import createConnector from '../core/createConnector';
import { getCurrentRefinementValue } from '../core/indexUtils';

import {
getCurrentRefinementValue,
hasMultipleIndex,
getIndex,
} from '../core/indexUtils';
import { shallowEqual } from '../core/utils';
import { omit } from 'lodash';
/**
* connectScrollTo connector provides the logic to build a widget that will
* let the page scroll to a certain point.
* @name connectScrollTo
* @kind connector
* @propType {string} [scrollOn="page"] - Widget searchState key on which to listen for changes, default to the pagination widget.
* @providedPropType {any} value - the current refinement applied to the widget listened by scrollTo
* @providedPropType {boolean} hasNotChanged - indicates whether the refinement came from the scrollOn argument (for instance page by default)
*/
export default createConnector({
displayName: 'AlgoliaScrollTo',
Expand All @@ -32,6 +38,37 @@ export default createConnector({
null,
currentRefinement => currentRefinement
);
return { value };

if (!this._prevSearchState) {
this._prevSearchState = {};
}

/* Get the subpart of the state that interest us*/
if (hasMultipleIndex(this.context)) {
const index = getIndex(this.context);
searchState = searchState.indices ? searchState.indices[index] : {};
}

/*
if there is a change in the app that has been triggered by another element than
"props.scrollOn (id) or the Configure widget, we need to keep track of the search state to
know if there's a change in the app that was not triggered by the props.scrollOn (id)
or the Configure widget. This is useful when using ScrollTo in combination of Pagination.
As pagination can be change by every widget, we want to scroll only if it cames from the pagination
widget itself. We also remove the configure key from the search state to do this comparaison because for
now configure values are not present in the search state before a first refinement has been made
and will false the results.
See: https://github.com/algolia/react-instantsearch/issues/164
*/
const cleanedSearchState = omit(omit(searchState, 'configure'), id);

const hasNotChanged = shallowEqual(
this._prevSearchState,
cleanedSearchState
);

this._prevSearchState = cleanedSearchState;

return { value, hasNotChanged };
},
});
47 changes: 34 additions & 13 deletions packages/react-instantsearch/src/connectors/connectScrollTo.test.js
Expand Up @@ -9,11 +9,26 @@ describe('connectScrollTo', () => {
const context = { context: { ais: { mainTargetedIndex: 'index' } } };
const getProvidedProps = connect.getProvidedProps.bind(context);
it('provides the correct props to the component', () => {
props = getProvidedProps({ scrollOn: 'p' }, { p: 1 });
expect(props).toEqual({ value: 1 });
props = getProvidedProps(
{ scrollOn: 'p' },
{ p: 1, configure: 3, refinementList: 'ok' }
);
expect(props).toEqual({ value: 1, hasNotChanged: false });

props = getProvidedProps(
{ scrollOn: 'p' },
{ p: 1, configure: 3, refinementList: 'not ok' }
);
expect(props).toEqual({ value: 1, hasNotChanged: false });

props = getProvidedProps(
{ scrollOn: 'p' },
{ p: 2, configure: 3, refinementList: 'not ok' }
);
expect(props).toEqual({ value: 2, hasNotChanged: true });

props = getProvidedProps({ scrollOn: 'anything' }, { anything: 2 });
expect(props).toEqual({ value: 2 });
expect(props).toEqual({ value: 2, hasNotChanged: false });
});
});
describe('multi index', () => {
Expand All @@ -25,17 +40,23 @@ describe('connectScrollTo', () => {
};
const getProvidedProps = connect.getProvidedProps.bind(context);
it('provides the correct props to the component', () => {
props = getProvidedProps(
{ scrollOn: 'p' },
{ indices: { second: { p: 1 } } }
);
expect(props).toEqual({ value: 1 });
const searchState = { indices: { second: { p: 1 } } };

props = getProvidedProps(
{ scrollOn: 'anything' },
{ indices: { second: { anything: 2 } } }
);
expect(props).toEqual({ value: 2 });
props = getProvidedProps({ scrollOn: 'p' }, searchState);
expect(props).toEqual({ value: 1, hasNotChanged: true });

searchState.indices.second = { ...searchState.indices.second, p: 2 };

props = getProvidedProps({ scrollOn: 'p' }, searchState);
expect(props).toEqual({ value: 2, hasNotChanged: true });

searchState.indices.second = {
...searchState.indices.second,
anything: 'ok',
};

props = getProvidedProps({ scrollOn: 'p' }, searchState);
expect(props).toEqual({ value: 2, hasNotChanged: false });
});
});
});

0 comments on commit 2d76022

Please sign in to comment.