Skip to content
56 changes: 56 additions & 0 deletions packages/cmf/__tests__/expressions/index.test.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
});
2 changes: 2 additions & 0 deletions packages/cmf/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -42,6 +43,7 @@ export default {
actionCreator,
component,
expression,
expressions,
route,
registry,
registerInternals,
Expand Down
23 changes: 11 additions & 12 deletions packages/cmf/src/api.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CMF API
==
# CMF API


```javascript
import { api } from '@talend/react-cmf';
Expand All @@ -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';
Expand Down Expand Up @@ -75,8 +73,8 @@ function mapStateToProps(state) {
export default cmfConnect({})(MyCollectionmanager);
```

api.component
--

## api.component

```javascript
import * as components from '@talend/containers';
Expand All @@ -88,8 +86,7 @@ api.component.registerMany({
});
```

api.expression
--
## api.expression

```javascript
function myExpression({ context, payload}, ...args) {
Expand All @@ -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`.
Expand Down
12 changes: 12 additions & 0 deletions packages/cmf/src/expressions/getInState.js
Original file line number Diff line number Diff line change
@@ -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);
6 changes: 6 additions & 0 deletions packages/cmf/src/expressions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import getInState from './getInState';

export default {
'cmf.collections.get': getInState('cmf.collections'),
'cmf.components.get': getInState('cmf.components'),
};
91 changes: 91 additions & 0 deletions packages/cmf/src/expressions/index.md
Original file line number Diff line number Diff line change
@@ -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 (
<article>
<h1>{props.title}</h1>
<p>{props.description}</h1>
<button onClick={this.onLike}>{like ? 'unlike': 'like'}</button>
</article>
);
}
}
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]
}
}
}
```