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

global: updateQueryState support for multiple filters #102

Merged
merged 2 commits into from
Apr 2, 2020
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
23 changes: 23 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,74 @@
# Changes

Version 0.18.0 (released 2020-04-02)

* Fixed axios url config override.
* Updated axios dependency to 0.19.2.
* Fixed documentation typos.
* Replaced arrow functions with class methods.
* Set initialLoading to true
* Exposed function`updateQueryState` with `withState` HOC.
* Support for multiple filters in `updateQueryState`.

Version 0.17.0 (released 2020-02-19)

* Added event listener to trigger search from external app
* Removed `history` prop and related code/doc
* Updated react deps

Version 0.16.0 (released 2019-12-04)

* added configuration for passing axios interceptors in `InvenioSearchApi`

Version 0.14.0 (released 2019-10-15)

* added multi-layout support fo search results
* changed params passing to pagination and display of search result card

Version 0.13.0 (released 2019-10-01)

* Removed Semantic-UI import from the library and moved to the demo app
* Upgraded create-react-app to 3.1.2

Version 0.12.0 (released 2019-09-10)

* Fixed bug to prevent state mutation when getting URL args
* Renamed component withQueryState to withState

Version 0.11.0 (released 2019-08-30)

* Moved all dev dependencies to peer dependencies
* Removed for the time being the `redux-devtools-extension` because it is wrongly transformed from CommonJS to ES modules

Version 0.10.0 (released 2019-08-30)

* Changed lib build system to use Rollup instead of Babel to be able to build the CommonJS version too

Version 0.9.0 (released 2019-08-26)

* Fixed babel absolute runtimes when publishing the library on npmjs

Version 0.8.0 (released 2019-08-26) -- deprecated on npmjs

* Dependencies upgrade
* Fixed build with babel
* Changed configuration for Invenio backend APIs

Version 0.7.0 (released 2019-08-13)

* Replaced nwb with create-react-app since nwb is not anymore maintained and it comes with many security vulnerabilities
* create-react-app is now used for development, babel to build the library

Version 0.6.0 (released 2019-08-07)

* Added withQueryState component exposing redux query state to external components

Version 0.5.0 (released 2019-06-27)

* Added SearchBar component with autocompletion

Version 0.4.0 (released 2019-04-24)

* Updated nested aggregation code
* Change in how selected aggregations are displayed
* Updated response/request serializers
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-searchkit",
"version": "0.17.0",
"version": "0.18.0",
"description": "React components to build your search UI application",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
26 changes: 24 additions & 2 deletions src/lib/state/selectors/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
*/

import Qs from 'qs';
import _forEach from 'lodash/forEach';
import _head from 'lodash/head';
import _isArray from 'lodash/isArray';
import _isEmpty from 'lodash/isEmpty';
import _pick from 'lodash/pick';

/**
* Return true if the first string starts and contains the second.
* @param {string} first a string
Expand Down Expand Up @@ -39,8 +43,7 @@ function removeLastChild(arr) {
return [];
}

export const updateQueryFilters = (queryFilter, stateFilters) => {
if (_isEmpty(queryFilter)) return;
function updateFilter(queryFilter, stateFilters) {
/**
* convert query and state to strings so they can be compared
*/
Expand Down Expand Up @@ -101,6 +104,25 @@ export const updateQueryFilters = (queryFilter, stateFilters) => {
* convert back to lists
*/
return filteredStrStates.map(strState => parse(strState));
}

export const updateQueryFilters = (queryFilter, stateFilters) => {
if (_isEmpty(queryFilter)) return;

/** If we have one filter as query = ['file_type', 'pdf'] */
if (!_isArray(_head(queryFilter))) {
return updateFilter(queryFilter, stateFilters);
}

/** If we have an array of filters as query we apply the filters one by one.
* e.g. query = [['file_type', 'pdf'], ['file_type', 'txt']]
*/
let tempStateFilters = stateFilters;
_forEach(
Copy link
Contributor

Choose a reason for hiding this comment

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

can you do simply:

return queryFilters.filter(queryFilter => updateFilter(queryFilter. stateFilters))

?

Copy link
Member Author

@topless topless Apr 2, 2020

Choose a reason for hiding this comment

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

This was my initial approach but as we call updateFilter our stateFilters change, so our next updateFilter should be applied on the altered stateFilters and not the initial ones. I can explain in a call if needed.

queryFilter,
filter => (tempStateFilters = updateFilter(filter, tempStateFilters))
);
return tempStateFilters;
};

export const updateQueryState = (oldState, newState, storeKeys) => {
Expand Down
75 changes: 70 additions & 5 deletions src/lib/state/selectors/query.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@ describe('queries with first level filters.', () => {

const newState = updateQueryFilters(query, state);

expect(newState).toEqual([['file_type', 'pdf'], ['type', 'Publication']]);
expect(newState).toEqual([
['file_type', 'pdf'],
['type', 'Publication'],
]);
});

test('query with `type: Publication` should be added when another `type` is already in the state.', () => {
const state = [['file_type', 'pdf'], ['type', 'Image']];
const state = [
['file_type', 'pdf'],
['type', 'Image'],
];
const query = ['type', 'Publication'];

const newState = updateQueryFilters(query, state);
Expand All @@ -61,13 +67,19 @@ describe('queries with first level filters.', () => {

const newState = updateQueryFilters(query, state);

expect(newState).toEqual([['file_type', 'pdf'], ['type', 'Publication']]);
expect(newState).toEqual([
['file_type', 'pdf'],
['type', 'Publication'],
]);
});
});

describe('queries with second level filters.', () => {
test('query with `subtype: Other` should be added when not in the state.', () => {
const state = [['file_type', 'pdf'], ['type', 'Image']];
const state = [
['file_type', 'pdf'],
['type', 'Image'],
];
const query = ['type', 'Publication', ['subtype', 'Other']];

const newState = updateQueryFilters(query, state);
Expand All @@ -91,7 +103,10 @@ describe('queries with second level filters.', () => {

const newState = updateQueryFilters(query, state);

expect(newState).toEqual([['file_type', 'pdf'], ['type', 'Image']]);
expect(newState).toEqual([
['file_type', 'pdf'],
['type', 'Image'],
]);
});

test('query with `subtype: Other` should remove any query with the parent `type: Publication`.', () => {
Expand Down Expand Up @@ -233,3 +248,53 @@ describe('user clears previous selections', () => {
expect(newState).toEqual([['file_type', 'pdf']]);
});
});

describe('user submits multiple filters as input', () => {
test('query with `file_type: txt` should be added when not in the state.', () => {
const state = [];
const query = [['file_type', 'txt']];
const newState = updateQueryFilters(query, state);
expect(newState).toEqual([['file_type', 'txt']]);
});

test('query removes existing `file_type: pdf` and adds `file_type: txt`.', () => {
const state = [['file_type', 'pdf']];
const query = [
['file_type', 'pdf'],
['file_type', 'txt'],
];
const newState = updateQueryFilters(query, state);
expect(newState).toEqual([['file_type', 'txt']]);
});

test('query removes existing `file_type: pdf` from state.', () => {
const state = [['file_type', 'pdf']];
const query = [['file_type', 'pdf']];
const newState = updateQueryFilters(query, state);
expect(newState).toEqual([]);
});

test('query with `subtype: Report` should remove from the state any child query with `subsubtype`', () => {
const state = [
['file_type', 'png'],
['file_type', 'pdf'],
['type', 'Image'],
[
'type',
'Publication',
['subtype', 'Report', ['subsubtype', 'Restricted']],
],
['type', 'Publication', ['subtype', 'Report', ['subsubtype', 'Public']]],
];
const query = [['type', 'Publication', ['subtype', 'Report']]];

const newState = updateQueryFilters(query, state);

expect(newState).toEqual([
['file_type', 'png'],
['file_type', 'pdf'],
['type', 'Image'],
['type', 'Publication'],
]);
});
});