Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
791593c
feat(cmf): add redux-saga integration at component level
jmfrancois Feb 8, 2018
9f15f4e
chore cleanum
jmfrancois Feb 8, 2018
4adc1d9
test(ci): update code style outputs
Feb 8, 2018
28cd8a9
test(ci): prettier
Feb 8, 2018
7fe7106
chore: refactor follow review
jmfrancois Feb 8, 2018
0a10074
Merge branch 'jmfrancois/feat/cmf-add-saga-integration' of github.com…
jmfrancois Feb 8, 2018
5a8e49d
test(ci): update code style outputs
Feb 8, 2018
ea9ab3f
test: add some
jmfrancois Feb 12, 2018
7f07eac
test(ci): update code style outputs
Feb 12, 2018
2b4d358
Merge branch 'master' into jmfrancois/feat/cmf-add-saga-integration
talend-glorieux Feb 12, 2018
f431807
test(ci): update code style outputs
Feb 12, 2018
9f6865e
Merge branch 'master' into jmfrancois/feat/cmf-add-saga-integration
jmfrancois Feb 13, 2018
7250763
test(ci): update code style outputs [skip ci]
Feb 13, 2018
b122a78
test: add some + doc
jmfrancois Feb 14, 2018
609a326
test(ci): prettier [skip ci]
Feb 14, 2018
3dbed53
chore: wip
jmfrancois Feb 14, 2018
c7a9186
Merge branch 'master' into jmfrancois/feat/cmf-add-saga-integration
jmfrancois Feb 14, 2018
ad0b0ce
Merge branch 'jmfrancois/feat/cmf-add-saga-integration' of github.com…
jmfrancois Feb 14, 2018
e4c9fa9
test(ci): update code style outputs
Feb 14, 2018
5a42f82
test(ci): prettier
Feb 14, 2018
8728364
test: move them and fix them all
jmfrancois Feb 14, 2018
3b64347
Merge branch 'jmfrancois/feat/cmf-add-saga-integration' of github.com…
jmfrancois Feb 14, 2018
f1ae245
test(ci): update code style outputs
Feb 14, 2018
44b11c8
add comments here
romainseb Feb 15, 2018
6e407ea
Merge branch 'master' into jmfrancois/feat/cmf-add-saga-integration
acateland Feb 15, 2018
ad86beb
chore: follow review
jmfrancois Feb 16, 2018
1b13f4a
Merge branch 'jmfrancois/feat/cmf-add-saga-integration' of github.com…
jmfrancois Feb 16, 2018
f345662
Merge branch 'master' into jmfrancois/feat/cmf-add-saga-integration
jmfrancois Feb 16, 2018
a4e97c4
Merge branch 'master' into jmfrancois/feat/cmf-add-saga-integration
jmfrancois Feb 16, 2018
8ee46bc
fix: test
jmfrancois Feb 16, 2018
1176be7
doc: update
jmfrancois Feb 16, 2018
7647972
Merge branch 'jmfrancois/feat/cmf-add-saga-integration' of github.com…
jmfrancois Feb 16, 2018
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
13 changes: 13 additions & 0 deletions BREAKING_CHANGES_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ Before 1.0, the stack do NOT follow semver version in releases.
This document aims to ease the WIP migration from a version to another by providing intels about what to do to migrate.

## v0.156.0
* cmf: selectors
* PR: https://github.com/Talend/ui/pull/1055
* Change: move to collections

| name | new location |
|---|---|
| getCollectionFromPath | selectors.collections.find
| findCollectionPathListItem | selectors.collections.findListItem

* cmf: putActionCreator
* PR: https://github.com/Talend/ui/pull/1055
* Change: move from api.saga.putActionCreator to api.sagas.putActionCreator

* Container: DeleteResource
* PR: https://github.com/Talend/ui/pull/1053
* Changes: deleteResource Saga params has changed
Expand Down
7 changes: 5 additions & 2 deletions output/cmf.eslint.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
The react/require-extension rule is deprecated. Please use the import/extensions rule from eslint-plugin-import instead.

/home/travis/build/Talend/ui/packages/cmf/src/cmfConnect.js
151:4 warning Unexpected console statement no-console
152:4 warning Unexpected console statement no-console
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe use invariant instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this one is for a deprecation warning, invariant throw an error, so it block the app in dev.


/home/travis/build/Talend/ui/packages/cmf/src/componentState.js
88:3 warning Unexpected console statement no-console
Expand All @@ -21,5 +21,8 @@ The react/require-extension rule is deprecated. Please use the import/extensions
163:54 error There should be no spaces inside this paren space-in-parens
177:51 error There should be no spaces inside this paren space-in-parens

✖ 7 problems (2 errors, 5 warnings)
/home/travis/build/Talend/ui/packages/cmf/src/sagas/collection.js
9:1 error Prefer default export import/prefer-default-export

✖ 8 problems (3 errors, 5 warnings)

25 changes: 25 additions & 0 deletions packages/cmf/__tests__/actions/saga.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
start,
stop,
} from '../../src/actions/saga';
import CONST from '../../src/constant';

describe('actions.saga', () => {
it('start should return action object with DID_MOUNT_SAGA_START', () => {
const event = { type: 'DID_MOUNT' };
const data = { saga: 'mySaga' };
expect(start(event, data)).toEqual({
type: CONST.DID_MOUNT_SAGA_START,
saga: data.saga,
event,
});
});
it('start should return action object with WILL_UNMUNT_SAGA_STOP', () => {
const event = { type: 'WILL_UNMOUNT' };
const data = { saga: 'mySaga' };
expect(stop(event, data)).toEqual({
type: `${CONST.WILL_UNMOUNT_SAGA_STOP}_${data.saga}`,
event,
});
});
});
20 changes: 20 additions & 0 deletions packages/cmf/__tests__/api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,25 @@ import api from '../src/api';
describe('CMF api', () => {
it('provide action, route access', () => {
expect(typeof api.action).toBe('object');
expect(api.action).toBeDefined();
expect(api.actions).toBeDefined();
expect(api.actionCreator).toBeDefined();
expect(api.component).toBeDefined();
expect(api.expression).toBeDefined();
expect(api.route).toBeDefined();
expect(api.registry).toBeDefined();
expect(api.registerInternals).toBeDefined();
expect(api.sagas).toBeDefined();
expect(api.saga).toBeDefined();
});
it('registerInternals should add internal actionCreators to the registry', () => {
const context = {
registry: {},
};
expect(() => api.actionCreator.get(context, 'cmf.saga.start')).toThrow();
expect(() => api.actionCreator.get(context, 'cmf.saga.stop')).toThrow();
api.registerInternals(context);
expect(api.actionCreator.get(context, 'cmf.saga.start')).toBeDefined();
expect(api.actionCreator.get(context, 'cmf.saga.stop')).toBeDefined();
});
});
39 changes: 39 additions & 0 deletions packages/cmf/__tests__/cmfConnect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,45 @@ describe('cmfConnect', () => {
expect(props.initState.mock.calls[0][0]).toBe(props.initialState);
});

it('should componentDidMount support saga', () => {
const TestComponent = jest.fn();
TestComponent.displayName = 'TestComponent';
const CMFConnected = cmfConnect({})(TestComponent);
const props = {
saga: 'hello',
dispatchActionCreator: jest.fn(),
};
const context = mock.context();
const instance = new CMFConnected.CMFContainer(props, context);
instance.componentDidMount();
expect(props.dispatchActionCreator).toHaveBeenCalledWith(
'cmf.saga.start',
{ type: 'DID_MOUNT' },
instance.props,
instance.context
);
});

it('should componentWillUnmount support saga', () => {
const TestComponent = jest.fn();
TestComponent.displayName = 'TestComponent';
const CMFConnected = cmfConnect({})(TestComponent);
const props = {
saga: 'hello',
dispatchActionCreator: jest.fn(),
deleteState: jest.fn(),
};
const context = mock.context();
const instance = new CMFConnected.CMFContainer(props, context);
instance.componentWillUnmount();
expect(props.dispatchActionCreator).toHaveBeenCalledWith(
'cmf.saga.stop',
{ type: 'WILL_UNMOUNT' },
instance.props,
instance.context
);
});

it('should componentWillUnMount dispatchActionCreator', () => {
const TestComponent = jest.fn();
TestComponent.displayName = 'TestComponent';
Expand Down
33 changes: 33 additions & 0 deletions packages/cmf/__tests__/sagas/component.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { fork, take, takeEvery, cancel } from 'redux-saga/effects';
import { createMockTask } from 'redux-saga/utils';
import registry from '../../src/registry';
import { onSagaStart, handle } from '../../src/sagas/component';
import CONST from '../../src/constant';

describe('sagas.component', () => {
it('should onSagaStart fork action.saga and wait for unmount to cancel', () => {
// given
const testAction = { type: 'TEST', saga: 'my-saga' };
function* saga() {}
const reg = registry.getRegistry();
reg['SAGA:my-saga'] = saga;
const task = createMockTask();
// when
const gen = onSagaStart(testAction);

// then
expect(gen.next().value).toEqual(fork(saga));
expect(gen.next(task).value).toEqual(take(`${CONST.WILL_UNMOUNT_SAGA_STOP}_my-saga`));
expect(gen.next().value).toEqual(cancel(task));
});
it('should handle takeEvery didmount', () => {
// given
const gen = handle();
const didMountAction = { type: CONST.DID_MOUNT_SAGA_START };

// then
expect(gen.next(didMountAction).value).toEqual(
takeEvery(CONST.DID_MOUNT_SAGA_START, onSagaStart)
);
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { put, select } from 'redux-saga/effects';
import registry from './registry';
import saga from './saga';
import registry from '../../src/registry';
import putActionCreator from '../../src/sagas/putActionCreator';

describe('saga', () => {
it('should putActionCreator call put of a registred actionCreator without context', () => {
Expand All @@ -13,7 +13,7 @@ describe('saga', () => {
const event = { type: 'click', source: 'MyComponent' };

// when
const gen = saga.putActionCreator('myActionCreator', event, data);
const gen = putActionCreator('myActionCreator', event, data);

// then
expect(gen.next().value).toEqual(select());
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('saga', () => {
const event = { type: 'click', source: 'MyComponent' };

// when
const gen = saga.putActionCreator('myActionCreator', event, data, context);
const gen = putActionCreator('myActionCreator', event, data, context);

// then
expect(gen.next().value).toEqual(select());
Expand Down
22 changes: 11 additions & 11 deletions packages/cmf/__tests__/selectors/index.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Map, List } from 'immutable';
import cases from 'jest-in-case';
import { getCollectionFromPath, findCollectionPathListItem } from '../../src/selectors';
import selectors from '../../src/selectors';

describe('getCollectionFromPath', () => {
describe('selectors.collections.get', () => {
const collection = new Map({ id: 'id' });
const collectionSubset = new Map({ subset: 'subset' });
const collectionWithSubset = new Map({ collectionSubset });
Expand All @@ -15,18 +15,18 @@ describe('getCollectionFromPath', () => {
},
};
it('try to find the collection if collectionPath is a string', () => {
expect(getCollectionFromPath(state, 'collection')).toEqual(collection);
expect(selectors.collections.get(state, 'collection')).toEqual(collection);
});

it('try to find the collection subset if collectionPath is an array', () => {
expect(getCollectionFromPath(state, ['collectionWithSubset', 'collectionSubset'])).toEqual(
expect(selectors.collections.get(state, ['collectionWithSubset', 'collectionSubset'])).toEqual(
collectionSubset,
);
});

it('throw an exception if collection path is neither a string or an array', () => {
expect(() => {
getCollectionFromPath(state, {});
selectors.collections.get(state, {});
}).toThrowError(`Type mismatch: collectionPath should be a string or an array of string
got [object Object]`);
});
Expand All @@ -47,9 +47,9 @@ const state = {
};

cases(
'findCollectionPathListItem(state, pathDescriptor, resourceId)',
'find(state, pathDescriptor, resourceId)',
opts => {
expect(findCollectionPathListItem(opts.state, opts.pathDescriptor, opts.resourceId)).toBe(
expect(selectors.collections.findListItem(opts.state, opts.pathDescriptor, opts.resourceId)).toBe(
opts.result,
);
},
Expand Down Expand Up @@ -79,10 +79,10 @@ cases(
);

cases(
'findCollectionPathListItem(state, pathDescriptor, resourceId)',
'selectors.collections.findListItem(state, pathDescriptor, resourceId)',
opts => {
expect(() => {
findCollectionPathListItem(opts.state, opts.pathDescriptor, opts.resourceId);
selectors.collections.findListItem(opts.state, opts.pathDescriptor, opts.resourceId);
}).toThrow(opts.result);
},
[
Expand All @@ -91,15 +91,15 @@ cases(
state,
pathDescriptor: 'isNotList',
resourceId: id,
result: `Type mismatch: isNotList does not resolve as an instance of Immutable.List,
result: `Type mismatch: isNotList does not resolve as an instance of Immutable.List,
got Map { "id": Map { "id": "id" } }`,
},
{
name: 'throw if collection can\'t be found',
state,
pathDescriptor: 'notFound',
resourceId: id,
result: `Type mismatch: notFound does not resolve as an instance of Immutable.List,
result: `Type mismatch: notFound does not resolve as an instance of Immutable.List,
got undefined`,
},
],
Expand Down
2 changes: 2 additions & 0 deletions packages/cmf/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import * as collectionsActions from './collectionsActions';
import * as componentsActions from './componentsActions';
import * as settingsActions from './settingsActions';
import * as saga from './saga';
import http from './http';

/**
Expand All @@ -27,4 +28,5 @@ export default {
collections: collectionsActions,
components: componentsActions,
settings: settingsActions,
saga,
};
16 changes: 16 additions & 0 deletions packages/cmf/src/actions/saga.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import CONST from '../constant';

export function start(event = {}, data) {
return {
type: CONST.DID_MOUNT_SAGA_START,
saga: data.saga,
event,
};
}

export function stop(event, data) {
return {
type: `${CONST.WILL_UNMOUNT_SAGA_STOP}_${data.saga}`,
event,
};
}
13 changes: 11 additions & 2 deletions packages/cmf/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ import action from './action';
import actions from './actions';
import actionCreator from './actionCreator';
import expression from './expression';
import saga from './saga';
import sagas from './sagas';
import selectors from './selectors';
import component from './component';

function registerInternals(context) {
actionCreator.register('cmf.saga.start', actions.saga.start, context);
actionCreator.register('cmf.saga.stop', actions.saga.stop, context);
}

export default {
action,
actions,
Expand All @@ -38,5 +44,8 @@ export default {
expression,
route,
registry,
saga,
registerInternals,
saga: sagas,
sagas,
selectors,
};
18 changes: 16 additions & 2 deletions packages/cmf/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,25 @@ api.expressions.register('myExpression', myExpression);
Expressions can be used for props resolution.
In this case, the payload is the current props.

api.saga
api.sagas
--

You can register your saga in the cmf registry to be able to use the saga props
supported by `cmfConnect`.

```javascript
function* mySaga(action) {
//...
}
api.sagas.register('mySaga', mySaga);
```

This is related to the `component` saga that you must initialize.

Most of them are documented [here](sagas/index.md)

```javascript
api.saga.putActionCreator('myAction', event, data, optionalContext);
api.sagas.putActionCreator('myaction', event, data, optionalContext);
```

This will call the registered `myAction` action creator.
Expand Down
7 changes: 7 additions & 0 deletions packages/cmf/src/cmfConnect.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const CMF_PROPS = [
'didMountActionCreator', // componentDidMount action creator id in registry
'keepComponentState', // redux state management on unmount
'view', // view component id in registry
'saga',
'willUnMountActionCreator', // componentWillUnmount action creator id in registry
];

Expand Down Expand Up @@ -247,6 +248,9 @@ export default function cmfConnect({
if (this.props.didMountActionCreator) {
this.dispatchActionCreator(this.props.didMountActionCreator, null, this.props);
}
if (this.props.saga) {
this.dispatchActionCreator('cmf.saga.start', { type: 'DID_MOUNT' }, this.props);
}
}

componentWillUnmount() {
Expand All @@ -260,6 +264,9 @@ export default function cmfConnect({
) {
this.props.deleteState();
}
if (this.props.saga) {
this.dispatchActionCreator('cmf.saga.stop', { type: 'WILL_UNMOUNT' }, this.props);
}
}

dispatchActionCreator(actionCreatorId, event, data, context) {
Expand Down
3 changes: 3 additions & 0 deletions packages/cmf/src/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ export default {
REGISTRY_COMPONENT_PREFIX: '_.route.component',
REGISTRY_HOOK_PREFIX: '_.route.hook',
REGISTRY_ACTION_CREATOR_PREFIX: 'actionCreator',
SAGA_PREFIX: 'saga',
DID_MOUNT_SAGA_START: 'DID_MOUNT_SAGA_START',
WILL_UNMOUNT_SAGA_STOP: 'WILL_UNMOUNT_SAGA_STOP',
};
Loading