From 5b59337ec1dfccc4a11d7f76c152a6fc9aff3f29 Mon Sep 17 00:00:00 2001 From: Jean-Michel FRANCOIS Date: Thu, 15 Feb 2018 09:55:18 +0100 Subject: [PATCH 1/6] feat(cmf): add common expressions --- packages/cmf/src/api.js | 2 + packages/cmf/src/expressions/collections.js | 9 ++ packages/cmf/src/expressions/components.js | 9 ++ packages/cmf/src/expressions/immutable.js | 24 ++++ packages/cmf/src/expressions/index.js | 12 ++ packages/cmf/src/expressions/index.md | 121 ++++++++++++++++++++ 6 files changed, 177 insertions(+) create mode 100644 packages/cmf/src/expressions/collections.js create mode 100644 packages/cmf/src/expressions/components.js create mode 100644 packages/cmf/src/expressions/immutable.js create mode 100644 packages/cmf/src/expressions/index.js create mode 100644 packages/cmf/src/expressions/index.md diff --git a/packages/cmf/src/api.js b/packages/cmf/src/api.js index a238dd6f95..805218c99f 100644 --- a/packages/cmf/src/api.js +++ b/packages/cmf/src/api.js @@ -27,6 +27,7 @@ import action from './action'; import actions from './actions'; import actionCreator from './actionCreator'; import expression from './expression'; +import expressions from './expressions'; import saga from './saga'; import component from './component'; @@ -36,6 +37,7 @@ export default { actionCreator, component, expression, + expressions, route, registry, saga, diff --git a/packages/cmf/src/expressions/collections.js b/packages/cmf/src/expressions/collections.js new file mode 100644 index 0000000000..85483a0d21 --- /dev/null +++ b/packages/cmf/src/expressions/collections.js @@ -0,0 +1,9 @@ + +export function getIn({ context }, path, defaultValue) { + return context.store.getState().cmf.collections.getIn(path, defaultValue); +} + +export function get(fullContext, id, defaultValue) { + const path = id.split('.'); + return getIn(fullContext, path, defaultValue); +} diff --git a/packages/cmf/src/expressions/components.js b/packages/cmf/src/expressions/components.js new file mode 100644 index 0000000000..58c40ccc4d --- /dev/null +++ b/packages/cmf/src/expressions/components.js @@ -0,0 +1,9 @@ + +export function getIn({ context }, path, defaultValue) { + return context.store.getState().cmf.components.getIn(path, defaultValue); +} + +export function get(fullContext, id, defaultValue) { + const path = id.split('.'); + return getIn(fullContext, path, defaultValue); +} diff --git a/packages/cmf/src/expressions/immutable.js b/packages/cmf/src/expressions/immutable.js new file mode 100644 index 0000000000..2e091027b5 --- /dev/null +++ b/packages/cmf/src/expressions/immutable.js @@ -0,0 +1,24 @@ +import _get from 'lodash/get'; +import Immutable from 'immutable'; + +/** + * + * @param {Object} contextAndPayload is automaticly injected by CMF + * @param {String} propsPath must be the path of the props you want to getIn + * @param {String} immutablePath is the path to getIn must be an array + * @param {Any} defaultValue the value you want to get back if no value found + * @example + * "titleExpression": { + "id": "Immutable.getIn", + "args": ["model", ["label"]] + }, + + */ +export function getIn({ context, payload }, propsPath, immutablePath, defaultValue) { + return _get(payload, propsPath, new Immutable.Map()).getIn(immutablePath, defaultValue); +} + +export function get(fullContext, propsPath, id, defaultValue) { + const immutablePath = id.split('.') + return getIn(fullContext, propsPath, immutablePath, defaultValue); +} diff --git a/packages/cmf/src/expressions/index.js b/packages/cmf/src/expressions/index.js new file mode 100644 index 0000000000..7865834622 --- /dev/null +++ b/packages/cmf/src/expressions/index.js @@ -0,0 +1,12 @@ +import * as collections from './collections'; +import * as components from './components'; +import * as immutable from './immutable'; + +export default { + 'Immutable.get': immutable.get, + 'Immutable.getIn': immutable.getIn, + 'cmf.collections.get': collections.get, + 'cmf.collections.getIn': collections.getIn, + 'cmf.components.get': components.get, + 'cmf.components.getIn': components.getIn, +}; diff --git a/packages/cmf/src/expressions/index.md b/packages/cmf/src/expressions/index.md new file mode 100644 index 0000000000..80a65af1b8 --- /dev/null +++ b/packages/cmf/src/expressions/index.md @@ -0,0 +1,121 @@ +# CMF Expressions + +## setup + +you have to do the following in your configure.js to activate this + +```javascript +import { api } from '@talend/react-cmf'; + +api.registerInternals(); +``` + +Then you can use all internal expressions. +For all the following example we take this component as example: + + +```javascript +import React from 'react'; +import PropTypes from 'prop-types'; +import Immutable from 'immutable'; +import { api, cmfConnect } from '@talend/react-cmf'; + +const DEFAULT_STATE = new Immutable.Map({ + like: false, +}); + +class Article extends React.Component { + static propTypes = { + ...cmfConnect.propsTypes, + title: PropTypes.string, + description: PropTypes.string, + } + constructor(props) { + super(props); + this.onLike = this.onLike.bind(this); + } + + onLike() { + this.props.setState({ like: !this.props.state.get('like') }); + } + + render() { + const like = this.props.state.get('like'); + return ( +
+

{props.title}

+

{props.description} + +

+ ); + } +} +function mapStateToProps(state) { + return { + model: state.cmf.collections.get('article'); + }; +} +export api.cmfConnect({mapStateToProps})(MyComponent) +``` + +## cmf.immutable + +This is nice to read content in existing props (here model) + + +```json + "props": { + "MyArticle#default": { + "titleExpression": { + "id": "Immutable.get", + "args": ["model", "label"] + }, + "descriptionExpression": { + "id": "Immutable.getIn", + "args": ["model", ["meta", "description"]] + }, + // variante + "descriptionExpression": { + "id": "Immutable.get", + "args": ["model", "meta.description"] + }, + } + } +``` + +## cmf.collections + +```json + "props": { + "MyArticle#default": { + "titleExpression": { + "id": "cmf.collections.getIn", + "args": [["article", "label"]] + }, + "descriptionExpression": { + "id": "cmf.collections.getIn", + "args": [["article", "meta", "description"]] + }, + // variante + "descriptionExpression": { + "id": "cmf.collections.get", + "args": ["article.meta.description"] + }, + } + } +``` + +## cmf.components + +let say you want to know the state of component + +```json + "props": { + "AnOtherComponent#default": { + "active": { + "id": "cmf.components.getIn", + "args": [["MyArticle", "default", "like"]] + } + } + } +``` From a624f6720e3e6092e3f599483bceace8c92971c9 Mon Sep 17 00:00:00 2001 From: travis Date: Thu, 15 Feb 2018 09:06:42 +0000 Subject: [PATCH 2/6] test(ci): prettier --- packages/cmf/src/expressions/collections.js | 1 - packages/cmf/src/expressions/components.js | 1 - packages/cmf/src/expressions/immutable.js | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cmf/src/expressions/collections.js b/packages/cmf/src/expressions/collections.js index 85483a0d21..675a64cf57 100644 --- a/packages/cmf/src/expressions/collections.js +++ b/packages/cmf/src/expressions/collections.js @@ -1,4 +1,3 @@ - export function getIn({ context }, path, defaultValue) { return context.store.getState().cmf.collections.getIn(path, defaultValue); } diff --git a/packages/cmf/src/expressions/components.js b/packages/cmf/src/expressions/components.js index 58c40ccc4d..b4c605ccde 100644 --- a/packages/cmf/src/expressions/components.js +++ b/packages/cmf/src/expressions/components.js @@ -1,4 +1,3 @@ - export function getIn({ context }, path, defaultValue) { return context.store.getState().cmf.components.getIn(path, defaultValue); } diff --git a/packages/cmf/src/expressions/immutable.js b/packages/cmf/src/expressions/immutable.js index 2e091027b5..01e4286e3d 100644 --- a/packages/cmf/src/expressions/immutable.js +++ b/packages/cmf/src/expressions/immutable.js @@ -19,6 +19,6 @@ export function getIn({ context, payload }, propsPath, immutablePath, defaultVal } export function get(fullContext, propsPath, id, defaultValue) { - const immutablePath = id.split('.') + const immutablePath = id.split('.'); return getIn(fullContext, propsPath, immutablePath, defaultValue); } From b8cb0f04c086b7e8ea150b64093c4fa36c1f5df4 Mon Sep 17 00:00:00 2001 From: Jean-Michel FRANCOIS Date: Thu, 15 Feb 2018 11:08:31 +0100 Subject: [PATCH 3/6] chore: refactor and add tests --- .../cmf/__tests__/expressions/index.test.js | 56 +++++++++++++++++++ packages/cmf/src/api.md | 24 ++++---- packages/cmf/src/expressions/collections.js | 9 --- packages/cmf/src/expressions/components.js | 9 --- packages/cmf/src/expressions/immutable.js | 27 +++------ packages/cmf/src/expressions/index.js | 12 +--- packages/cmf/src/expressions/index.md | 40 ++----------- 7 files changed, 84 insertions(+), 93 deletions(-) create mode 100644 packages/cmf/__tests__/expressions/index.test.js delete mode 100644 packages/cmf/src/expressions/collections.js delete mode 100644 packages/cmf/src/expressions/components.js diff --git a/packages/cmf/__tests__/expressions/index.test.js b/packages/cmf/__tests__/expressions/index.test.js new file mode 100644 index 0000000000..0877293cda --- /dev/null +++ b/packages/cmf/__tests__/expressions/index.test.js @@ -0,0 +1,56 @@ +import Immutable from 'immutable'; +import expressions from '../../src/expressions'; +import mock from '../../src/mock'; + +describe('expressions', () => { + it('should export some expressions', () => { + expect(expressions['cmf.collections.get']).toBeDefined(); + expect(expressions['cmf.components.get']).toBeDefined(); + }); + describe('cmf.collections.get', () => { + it('should get collection content', () => { + const context = mock.context(); + const state = mock.state(); + state.cmf.collections = new Immutable.Map({ + article: new Immutable.Map({ + title: 'my title', + }), + }); + context.store.getState = () => state; + expect(expressions['cmf.collections.get']({ context }, 'article.title', 'no title')) + .toBe('my title'); + }); + it('should return default value if collection doesn\'t exists', () => { + const context = mock.context(); + const state = mock.state(); + context.store.getState = () => state; + state.cmf.collections = new Immutable.Map({}); + expect(expressions['cmf.collections.get']({ context }, 'article.title', 'no title')) + .toBe('no title'); + }); + }); + describe('cmf.components.get', () => { + it('should get component state', () => { + const context = mock.context(); + const state = mock.state(); + state.cmf.components = new Immutable.Map({ + MyComponent: new Immutable.Map({ + default: new Immutable.Map({ + show: true, + }), + }), + }); + context.store.getState = () => state; + expect(expressions['cmf.components.get']({ context }, 'MyComponent.default.show', false)) + .toBe(true); + }); + it('should return default value if no component state', () => { + const context = mock.context(); + const state = mock.state(); + state.cmf.components = new Immutable.Map({}); + context.store.getState = () => state; + expect(expressions['cmf.components.get']({ context }, 'MyComponent.default.show', false)) + .toBe(false); + }); + }); +}); diff --git a/packages/cmf/src/api.md b/packages/cmf/src/api.md index 16993b48f3..7c81e2c7c7 100644 --- a/packages/cmf/src/api.md +++ b/packages/cmf/src/api.md @@ -1,5 +1,5 @@ -CMF API -== +# CMF API + ```javascript import { api } from '@talend/react-cmf'; @@ -14,13 +14,11 @@ The API is the most used items accessible. Here is the list of the first level a * `expression` to register your expressions * `saga` to use CMF in redux-saga -api.actionCreator --- +## api.actionCreator the can be found [here](actionCreator.md). -api.actions --- +## api.actions ```javascript import { api, cmfConnect } from '@talend/react-cmf'; @@ -72,8 +70,7 @@ export default cmfConnect({})(MyCollectionmanager); -api.component --- +## api.component ```javascript import * as components from '@talend/containers'; @@ -85,8 +82,7 @@ api.component.registerMany({ }); ``` -api.expression --- +## api.expression ```javascript function myexpression({ context, payload}, ...args) { @@ -100,8 +96,12 @@ api.expressions.register('myexpression', myexpression); Using cmfConnect for example expression can be used for props resolution. in this case the payload is the current props. -api.saga --- + +## api.expressions + +(./expressions/index.md) + +## api.saga ```javascript api.saga.putActionCreator('myaction', event, data, optionalContext); diff --git a/packages/cmf/src/expressions/collections.js b/packages/cmf/src/expressions/collections.js deleted file mode 100644 index 85483a0d21..0000000000 --- a/packages/cmf/src/expressions/collections.js +++ /dev/null @@ -1,9 +0,0 @@ - -export function getIn({ context }, path, defaultValue) { - return context.store.getState().cmf.collections.getIn(path, defaultValue); -} - -export function get(fullContext, id, defaultValue) { - const path = id.split('.'); - return getIn(fullContext, path, defaultValue); -} diff --git a/packages/cmf/src/expressions/components.js b/packages/cmf/src/expressions/components.js deleted file mode 100644 index 58c40ccc4d..0000000000 --- a/packages/cmf/src/expressions/components.js +++ /dev/null @@ -1,9 +0,0 @@ - -export function getIn({ context }, path, defaultValue) { - return context.store.getState().cmf.components.getIn(path, defaultValue); -} - -export function get(fullContext, id, defaultValue) { - const path = id.split('.'); - return getIn(fullContext, path, defaultValue); -} diff --git a/packages/cmf/src/expressions/immutable.js b/packages/cmf/src/expressions/immutable.js index 2e091027b5..2315fc93df 100644 --- a/packages/cmf/src/expressions/immutable.js +++ b/packages/cmf/src/expressions/immutable.js @@ -1,24 +1,13 @@ import _get from 'lodash/get'; import Immutable from 'immutable'; +import curry from 'lodash/curry'; -/** - * - * @param {Object} contextAndPayload is automaticly injected by CMF - * @param {String} propsPath must be the path of the props you want to getIn - * @param {String} immutablePath is the path to getIn must be an array - * @param {Any} defaultValue the value you want to get back if no value found - * @example - * "titleExpression": { - "id": "Immutable.getIn", - "args": ["model", ["label"]] - }, - - */ -export function getIn({ context, payload }, propsPath, immutablePath, defaultValue) { - return _get(payload, propsPath, new Immutable.Map()).getIn(immutablePath, defaultValue); +function getInState(statePath, { context }, immutablePath, defaultValue) { + return _get( + context.store.getState(), + statePath, + new Immutable.Map() + ).getIn(immutablePath.split('.'), defaultValue); } -export function get(fullContext, propsPath, id, defaultValue) { - const immutablePath = id.split('.') - return getIn(fullContext, propsPath, immutablePath, defaultValue); -} +export default curry(getInState); diff --git a/packages/cmf/src/expressions/index.js b/packages/cmf/src/expressions/index.js index 7865834622..e39ac03e16 100644 --- a/packages/cmf/src/expressions/index.js +++ b/packages/cmf/src/expressions/index.js @@ -1,12 +1,6 @@ -import * as collections from './collections'; -import * as components from './components'; -import * as immutable from './immutable'; +import immutableGet from './immutable'; export default { - 'Immutable.get': immutable.get, - 'Immutable.getIn': immutable.getIn, - 'cmf.collections.get': collections.get, - 'cmf.collections.getIn': collections.getIn, - 'cmf.components.get': components.get, - 'cmf.components.getIn': components.getIn, + 'cmf.collections.get': immutableGet('cmf.collections'), + 'cmf.components.get': immutableGet('cmf.components'), }; diff --git a/packages/cmf/src/expressions/index.md b/packages/cmf/src/expressions/index.md index 80a65af1b8..e0e5e6b673 100644 --- a/packages/cmf/src/expressions/index.md +++ b/packages/cmf/src/expressions/index.md @@ -58,48 +58,18 @@ function mapStateToProps(state) { export api.cmfConnect({mapStateToProps})(MyComponent) ``` -## cmf.immutable - -This is nice to read content in existing props (here model) - - -```json - "props": { - "MyArticle#default": { - "titleExpression": { - "id": "Immutable.get", - "args": ["model", "label"] - }, - "descriptionExpression": { - "id": "Immutable.getIn", - "args": ["model", ["meta", "description"]] - }, - // variante - "descriptionExpression": { - "id": "Immutable.get", - "args": ["model", "meta.description"] - }, - } - } -``` - ## cmf.collections ```json "props": { "MyArticle#default": { "titleExpression": { - "id": "cmf.collections.getIn", - "args": [["article", "label"]] - }, - "descriptionExpression": { - "id": "cmf.collections.getIn", - "args": [["article", "meta", "description"]] + "id": "cmf.collections.get", + "args": ["article.label", "no title"] }, - // variante "descriptionExpression": { "id": "cmf.collections.get", - "args": ["article.meta.description"] + "args": ["article.meta.description", "no description"] }, } } @@ -113,8 +83,8 @@ let say you want to know the state of component "props": { "AnOtherComponent#default": { "active": { - "id": "cmf.components.getIn", - "args": [["MyArticle", "default", "like"]] + "id": "cmf.components.get", + "args": ["MyArticle.default.like", false] } } } From fa64df0d1f639ca9be6095f9eef9fa1a63eda8d9 Mon Sep 17 00:00:00 2001 From: travis Date: Thu, 15 Feb 2018 10:19:45 +0000 Subject: [PATCH 4/6] test(ci): prettier --- packages/cmf/src/expressions/immutable.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/cmf/src/expressions/immutable.js b/packages/cmf/src/expressions/immutable.js index 2315fc93df..67eadc66f2 100644 --- a/packages/cmf/src/expressions/immutable.js +++ b/packages/cmf/src/expressions/immutable.js @@ -3,11 +3,10 @@ import Immutable from 'immutable'; import curry from 'lodash/curry'; function getInState(statePath, { context }, immutablePath, defaultValue) { - return _get( - context.store.getState(), - statePath, - new Immutable.Map() - ).getIn(immutablePath.split('.'), defaultValue); + return _get(context.store.getState(), statePath, new Immutable.Map()).getIn( + immutablePath.split('.'), + defaultValue, + ); } export default curry(getInState); From 7706fe72e9dd86aa811ee7580351ddd554413efd Mon Sep 17 00:00:00 2001 From: Jean-Michel FRANCOIS Date: Thu, 15 Feb 2018 11:21:35 +0100 Subject: [PATCH 5/6] doc: fix link --- packages/cmf/src/api.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cmf/src/api.md b/packages/cmf/src/api.md index 7c81e2c7c7..cee2e4d45a 100644 --- a/packages/cmf/src/api.md +++ b/packages/cmf/src/api.md @@ -97,9 +97,7 @@ Using cmfConnect for example expression can be used for props resolution. in this case the payload is the current props. -## api.expressions - -(./expressions/index.md) +## [api.expressions](./expressions/index.md) ## api.saga From 457a63242f0eedfc6635dbf4ee71f5529fccd347 Mon Sep 17 00:00:00 2001 From: Jean-Michel FRANCOIS Date: Tue, 20 Feb 2018 18:05:02 +0100 Subject: [PATCH 6/6] chore: follow review --- .../cmf/src/expressions/{immutable.js => getInState.js} | 0 packages/cmf/src/expressions/index.js | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename packages/cmf/src/expressions/{immutable.js => getInState.js} (100%) diff --git a/packages/cmf/src/expressions/immutable.js b/packages/cmf/src/expressions/getInState.js similarity index 100% rename from packages/cmf/src/expressions/immutable.js rename to packages/cmf/src/expressions/getInState.js diff --git a/packages/cmf/src/expressions/index.js b/packages/cmf/src/expressions/index.js index e39ac03e16..f10631aca7 100644 --- a/packages/cmf/src/expressions/index.js +++ b/packages/cmf/src/expressions/index.js @@ -1,6 +1,6 @@ -import immutableGet from './immutable'; +import getInState from './getInState'; export default { - 'cmf.collections.get': immutableGet('cmf.collections'), - 'cmf.components.get': immutableGet('cmf.components'), + 'cmf.collections.get': getInState('cmf.collections'), + 'cmf.components.get': getInState('cmf.components'), };