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.js b/packages/cmf/src/api.js index 32d4c3c0bb..a6f55ca8d1 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 sagas from './sagas'; import selectors from './selectors'; import component from './component'; @@ -42,6 +43,7 @@ export default { actionCreator, component, expression, + expressions, route, registry, registerInternals, diff --git a/packages/cmf/src/api.md b/packages/cmf/src/api.md index 14cefbdf72..1170188b1e 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'; @@ -15,13 +15,11 @@ Here is the list of the first level access: * `expression` to register your expressions * `saga` to use CMF with redux-saga -api.actionCreator --- +## api.actionCreator Documentation can be found [here](actionCreator.md). -api.actions --- +## api.actions ```javascript import { api, cmfConnect } from '@talend/react-cmf'; @@ -75,8 +73,8 @@ function mapStateToProps(state) { export default cmfConnect({})(MyCollectionmanager); ``` -api.component --- + +## api.component ```javascript import * as components from '@talend/containers'; @@ -88,8 +86,7 @@ api.component.registerMany({ }); ``` -api.expression --- +## api.expression ```javascript function myExpression({ context, payload}, ...args) { @@ -103,8 +100,10 @@ api.expressions.register('myExpression', myExpression); Expressions can be used for props resolution. In this case, the payload is the current props. -api.sagas --- + +## [api.expressions](./expressions/index.md) + +## api.saga You can register your saga in the cmf registry to be able to use the saga props supported by `cmfConnect`. diff --git a/packages/cmf/src/expressions/getInState.js b/packages/cmf/src/expressions/getInState.js new file mode 100644 index 0000000000..67eadc66f2 --- /dev/null +++ b/packages/cmf/src/expressions/getInState.js @@ -0,0 +1,12 @@ +import _get from 'lodash/get'; +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, + ); +} + +export default curry(getInState); diff --git a/packages/cmf/src/expressions/index.js b/packages/cmf/src/expressions/index.js new file mode 100644 index 0000000000..f10631aca7 --- /dev/null +++ b/packages/cmf/src/expressions/index.js @@ -0,0 +1,6 @@ +import getInState from './getInState'; + +export default { + 'cmf.collections.get': getInState('cmf.collections'), + 'cmf.components.get': getInState('cmf.components'), +}; diff --git a/packages/cmf/src/expressions/index.md b/packages/cmf/src/expressions/index.md new file mode 100644 index 0000000000..e0e5e6b673 --- /dev/null +++ b/packages/cmf/src/expressions/index.md @@ -0,0 +1,91 @@ +# 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.collections + +```json + "props": { + "MyArticle#default": { + "titleExpression": { + "id": "cmf.collections.get", + "args": ["article.label", "no title"] + }, + "descriptionExpression": { + "id": "cmf.collections.get", + "args": ["article.meta.description", "no description"] + }, + } + } +``` + +## cmf.components + +let say you want to know the state of component + +```json + "props": { + "AnOtherComponent#default": { + "active": { + "id": "cmf.components.get", + "args": ["MyArticle.default.like", false] + } + } + } +```