From 90e015e775528e73fea5a93dd1e199f56490d65a Mon Sep 17 00:00:00 2001 From: Chris Topaloudis Date: Mon, 30 Mar 2020 02:34:49 +0200 Subject: [PATCH] global: expose updateQueryState - updateQueryState exposed withState HOC - test for exposed props form withState - documentation - fix for config expecting axios property - fix circular dependency src/lib/store.js -> src/lib/state/reducers/index.js -> src/lib/state/reducers/query.js -> src/lib/store.js - changed initialResultsState.loading to true to avoid double children render in ResultsLoader closes #101 - closes #99 --- README.md | 34 +++++++++++++----------- docs/docs/components/with_state.md | 12 ++++++--- package.json | 4 +-- src/demos/cern-videos/App.js | 6 +++-- src/demos/elasticsearch/App.js | 6 +++-- src/demos/zenodo/App.js | 8 +++--- src/lib/components/HOC/withState.js | 10 ++++--- src/lib/components/HOC/withState.test.js | 33 +++++++++++++++++++++++ src/lib/state/reducers/query.js | 2 +- src/lib/store.js | 16 ++--------- src/lib/storeConfig.js | 20 ++++++++++++++ 11 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 src/lib/components/HOC/withState.test.js create mode 100644 src/lib/storeConfig.js diff --git a/README.md b/README.md index 4fca070c..216c13a6 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ React-SearchKit is a React library that allows you to build in an easy way your Main features: -* ready-to-use collection of UI components -* configurable REST API endpoint and serialization -* configurable URL parameters handling for deep linking +- ready-to-use collection of UI components +- configurable REST API endpoint and serialization +- configurable URL parameters handling for deep linking ![React-SearchKit screenshot](docs/website/static/img/screenshot.png) @@ -28,9 +28,9 @@ Main features: You can find a collection of examples in the `src/demos` folder: -* Elasticsearch, an example on how to query Elasticsearch (see below) -* Zenodo.org, an example on how to query an Invenio 3 instance -* CERN Videos, another Invenio 3 example +- Elasticsearch, an example on how to query Elasticsearch (see below) +- Zenodo.org, an example on how to query an Invenio 3 instance +- CERN Videos, another Invenio 3 example Install dependencies and run the React app to try them out (see steps below). @@ -40,44 +40,46 @@ To run the Elasticsearch backend for the demo, you can use Docker. A `docker-com Run the services: ```bash -$ cd src/demos/elasticsearch/docker -$ docker-compose up +cd src/demos/elasticsearch/docker +docker-compose up ``` Then, init the demo data: ```bash -$ curl -XPUT 'http://localhost:9200/random?pretty' -H 'Content-Type: application/json' -d @es7-mappings.json -$ curl -XPOST 'http://localhost:9200/random/_bulk' -H 'Content-Type: application/json' --data-binary @es-random-data.json -$ curl -XGET 'http://localhost:9200/random/_count?pretty' +curl -XPUT 'http://localhost:9200/random?pretty' -H 'Content-Type: application/json' -d @es7-mappings.json +curl -XPOST 'http://localhost:9200/random/_bulk' -H 'Content-Type: application/json' --data-binary @es-random-data.json +curl -XGET 'http://localhost:9200/random/_count?pretty' ``` Demo data have been randomly generated using . +> In case you want to clear your elastic search from data you can use `curl -X DELETE 'http://localhost:9200/_all'` + ## Developer guide React-SearchKit uses [create-react-app](https://create-react-app.dev/) as development toolkit. Install the library: -``` +```bash npm install ``` Start the demo application: -``` +```bash npm start ``` The library uses [Jest](https://jestjs.io/) as test runner. To run the tests: -``` +```bash npm test ``` The library uses `rollup` to build a final version inside the `/dist` folder and it will build CommonJS and ES Modules versions: -``` -npm build +```bash +npm run build ``` diff --git a/docs/docs/components/with_state.md b/docs/docs/components/with_state.md index 7ae7f788..42eff16a 100644 --- a/docs/docs/components/with_state.md +++ b/docs/docs/components/with_state.md @@ -3,11 +3,17 @@ id: with-state title: withState --- -`withState` is a HOC component that passes the redux state to an external component. +`withState` is a high order component, which is used to expose the redux state and +actions to external components. -The component receives the up-to-date state every time something is changed. +The component receives the up-to-date state every time something is changed and +through its props it gains access to -> Do **not** mutate the state inside your wrapped component! +- `currentResultsState` +- `currentQueryState` +- `updateQueryState` + +> Do **not** mutate directly the state inside your wrapped component. Instead, use the function `updateQueryState` to pass your new query. ## Usage diff --git a/package.json b/package.json index d023fe55..57c9761d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "dependencies": {}, "peerDependencies": { - "axios": "^0.19.0", + "axios": "^0.19.2", "lodash": "^4.17.15", "node-sass": "^4.12.0", "qs": "^6.8.0", @@ -32,7 +32,7 @@ "devDependencies": { "@babel/cli": "^7.5.5", "@svgr/rollup": "^4.3.2", - "axios": "^0.19.0", + "axios": "^0.19.2", "axios-mock-adapter": "^1.17.0", "coveralls": "^3.0.7", "enzyme": "^3.10.0", diff --git a/src/demos/cern-videos/App.js b/src/demos/cern-videos/App.js index 182af80d..de25a787 100644 --- a/src/demos/cern-videos/App.js +++ b/src/demos/cern-videos/App.js @@ -48,8 +48,10 @@ const resultsPerPageValues = [ ]; const searchApi = new InvenioSearchApi({ - url: 'https://videos.cern.ch/api/records/', - timeout: 5000, + axios: { + url: 'https://videos.cern.ch/api/records/', + timeout: 5000, + }, }); export class App extends Component { diff --git a/src/demos/elasticsearch/App.js b/src/demos/elasticsearch/App.js index 97bf18ef..d7f7f476 100644 --- a/src/demos/elasticsearch/App.js +++ b/src/demos/elasticsearch/App.js @@ -24,8 +24,10 @@ import { ESSearchApi } from '../../lib/api/contrib/elasticsearch'; const OnResults = withState(Results); const searchApi = new ESSearchApi({ - url: 'http://localhost:5000/random/_search', - timeout: 5000, + axios: { + url: 'http://localhost:5000/random/_search', + timeout: 5000, + }, es: { requestSerializer: DemoESRequestSerializer, }, diff --git a/src/demos/zenodo/App.js b/src/demos/zenodo/App.js index b96b102f..3783895e 100644 --- a/src/demos/zenodo/App.js +++ b/src/demos/zenodo/App.js @@ -63,9 +63,11 @@ const resultsPerPageValues = [ ]; const searchApi = new InvenioSearchApi({ - url: 'https://zenodo.org/api/records/', - timeout: 5000, - headers: { Accept: 'application/vnd.zenodo.v1+json' }, + axios: { + url: 'https://zenodo.org/api/records/', + timeout: 5000, + headers: { Accept: 'application/vnd.zenodo.v1+json' }, + }, }); export class App extends Component { diff --git a/src/lib/components/HOC/withState.js b/src/lib/components/HOC/withState.js index eb470985..cab1d1cb 100644 --- a/src/lib/components/HOC/withState.js +++ b/src/lib/components/HOC/withState.js @@ -8,6 +8,7 @@ import React from 'react'; import { connect } from '../../store'; +import { updateQueryState } from '../../state/actions'; export function withState(Component) { const WrappedComponent = ({ dispatch, ...props }) => ; @@ -16,8 +17,9 @@ export function withState(Component) { currentResultsState: state.results, }); - return connect( - mapStateToProps, - null - )(WrappedComponent); + const mapDispatchToProps = dispatch => ({ + updateQueryState: queryState => dispatch(updateQueryState(queryState)), + }); + + return connect(mapStateToProps, mapDispatchToProps)(WrappedComponent); } diff --git a/src/lib/components/HOC/withState.test.js b/src/lib/components/HOC/withState.test.js new file mode 100644 index 00000000..7c6ae5cf --- /dev/null +++ b/src/lib/components/HOC/withState.test.js @@ -0,0 +1,33 @@ +/* + * This file is part of React-SearchKit. + * Copyright (C) 2020 CERN. + * + * React-SearchKit is free software; you can redistribute it and/or modify it + * under the terms of the MIT License; see LICENSE file for more details. + */ + +import React from 'react'; +import configureMockStore from 'redux-mock-store'; +import { shallow } from 'enzyme'; +import { withState } from './withState'; + +const mockStore = configureMockStore(); + +describe('withState tests', () => { + let WithStateComponent; + let store; + beforeEach(() => { + store = mockStore({}); + store.clearActions(); + const mockedComponent = jest.fn(); + WithStateComponent = withState(mockedComponent); + }); + + it('should find the props exposed by withState', async () => { + const wrapper = shallow(); + const props = wrapper.children(0).props(); + expect(props.hasOwnProperty('currentResultsState')).toBe(true); + expect(props.hasOwnProperty('currentQueryState')).toBe(true); + expect(props.hasOwnProperty('updateQueryState')).toBe(true); + }); +}); diff --git a/src/lib/state/reducers/query.js b/src/lib/state/reducers/query.js index 011d8235..8edf1688 100644 --- a/src/lib/state/reducers/query.js +++ b/src/lib/state/reducers/query.js @@ -23,7 +23,7 @@ import { RESET_QUERY, } from '../types'; import { updateQueryFilters, updateQueryState } from '../selectors'; -import { STORE_KEYS } from '../../store'; +import { STORE_KEYS } from '../../storeConfig'; export default (state = {}, action) => { switch (action.type) { diff --git a/src/lib/store.js b/src/lib/store.js index bbda9438..8b835a1c 100644 --- a/src/lib/store.js +++ b/src/lib/store.js @@ -11,25 +11,13 @@ import { connect } from 'react-redux'; import thunk from 'redux-thunk'; import rootReducer from './state/reducers'; - -export const INITIAL_STORE_STATE = { - queryString: '', - suggestions: [], - sortBy: null, - sortOrder: null, - page: 1, - size: 10, - filters: [], - layout: null, -}; - -export const STORE_KEYS = Object.keys(INITIAL_STORE_STATE); +import { INITIAL_STORE_STATE } from './storeConfig'; export function configureStore(appConfig) { const initialQueryState = INITIAL_STORE_STATE; const initialResultsState = { - loading: false, + loading: true, data: { hits: [], total: 0, diff --git a/src/lib/storeConfig.js b/src/lib/storeConfig.js new file mode 100644 index 00000000..de1ee6e8 --- /dev/null +++ b/src/lib/storeConfig.js @@ -0,0 +1,20 @@ +/* + * This file is part of React-SearchKit. + * Copyright (C) 2018-2020 CERN. + * + * React-SearchKit is free software; you can redistribute it and/or modify it + * under the terms of the MIT License; see LICENSE file for more details. + */ + +export const INITIAL_STORE_STATE = { + queryString: '', + suggestions: [], + sortBy: null, + sortOrder: null, + page: 1, + size: 10, + filters: [], + layout: null, + }; + +export const STORE_KEYS = Object.keys(INITIAL_STORE_STATE);