diff --git a/.changeset/modern-cars-hope.md b/.changeset/modern-cars-hope.md
new file mode 100644
index 00000000000..6a2d84e49c2
--- /dev/null
+++ b/.changeset/modern-cars-hope.md
@@ -0,0 +1,14 @@
+---
+'@talend/react-cmf': major
+'@talend/react-cmf-cqrs': major
+'@talend/react-cmf-router': major
+'@talend/react-forms': major
+'@talend/react-containers': major
+'@talend/react-datagrid': major
+'@talend/react-sagas': major
+'@talend/react-stepper': major
+'@talend/react-storybook-cmf': major
+'@talend/react-flow-designer': major
+---
+
+Redux major upgrade with saga
diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml
index 7a38181566b..a11a292d66c 100644
--- a/.github/workflows/pr-lint.yml
+++ b/.github/workflows/pr-lint.yml
@@ -11,6 +11,9 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v2
+ with:
+ persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token
+ fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
- name: Use Node.js 14
uses: actions/setup-node@v2
@@ -60,3 +63,4 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
message: "chore: apply prettier on modified files"
+ branch: ${{ github.head_ref }}
diff --git a/.github/workflows/yarn-deduplicate.yml b/.github/workflows/yarn-deduplicate.yml
index 84840910682..7652234a9fa 100644
--- a/.github/workflows/yarn-deduplicate.yml
+++ b/.github/workflows/yarn-deduplicate.yml
@@ -3,7 +3,7 @@ name: yarn-deduplicate
on:
pull_request:
paths:
- - 'yarn.lock'
+ - "yarn.lock"
jobs:
dedupe:
@@ -22,9 +22,9 @@ jobs:
uses: actions/setup-node@v2
with:
node-version: 14
- registry-url: 'https://registry.npmjs.org/'
- scope: '@talend'
- cache: 'yarn'
+ registry-url: "https://registry.npmjs.org/"
+ scope: "@talend"
+ cache: "yarn"
- name: yarn-deduplicate
id: deduplicate
@@ -38,5 +38,5 @@ jobs:
uses: actions-js/push@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- message: 'chore: yarn-deduplicate'
+ message: "chore: yarn-deduplicate"
branch: ${{ github.head_ref }}
diff --git a/packages/cmf-cqrs/package.json b/packages/cmf-cqrs/package.json
index f39c9d457ef..427cd40883b 100644
--- a/packages/cmf-cqrs/package.json
+++ b/packages/cmf-cqrs/package.json
@@ -33,7 +33,7 @@
"dependencies": {
"@talend/react-cmf": "^6.39.1",
"immutable": "^3.8.2",
- "redux-saga": "^0.15.6"
+ "redux-saga": "^1.1.3"
},
"devDependencies": {
"@talend/scripts-core": "^11.3.0",
diff --git a/packages/cmf-cqrs/src/components/ACKDispatcher/ACKDispatcher.test.js b/packages/cmf-cqrs/src/components/ACKDispatcher/ACKDispatcher.test.js
index 0f3ceaf1f3f..0dfa4ea52e3 100644
--- a/packages/cmf-cqrs/src/components/ACKDispatcher/ACKDispatcher.test.js
+++ b/packages/cmf-cqrs/src/components/ACKDispatcher/ACKDispatcher.test.js
@@ -42,7 +42,7 @@ describe('Container ACKDispatcher', () => {
}
registry['actionCreator:myActionCreator'] = myActionCreator;
- mount(, { context: { registry } });
+ mount(, mock.Provider.getEnzymeOption({ registry }));
expect(mockProcessACK).toHaveBeenCalled();
});
it('should processACK call dispatch', () => {
diff --git a/packages/cmf-router/package.json b/packages/cmf-router/package.json
index 7b87fe27b3f..af43fbeed0e 100644
--- a/packages/cmf-router/package.json
+++ b/packages/cmf-router/package.json
@@ -22,16 +22,17 @@
"lodash": "^4.17.21",
"path-to-regexp": "^2.4.0",
"prop-types": "^15.8.1",
- "react-redux": "^5.1.2",
+ "react-redux": "^7.2.2",
"react-router": "^3.2.6",
"react-router-redux": "^4.0.8",
- "redux-saga": "^0.15.6"
+ "redux-saga": "^1.1.3"
},
"peerDependencies": {
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"devDependencies": {
+ "@redux-saga/testing-utils": "^1.1.3",
"@talend/scripts-core": "^11.3.0",
"@talend/scripts-preset-react-lib": "^9.9.3",
"enzyme": "^3.11.0",
diff --git a/packages/cmf-router/src/index.test.js b/packages/cmf-router/src/index.test.js
index df432ac65f9..8c88f14cad1 100644
--- a/packages/cmf-router/src/index.test.js
+++ b/packages/cmf-router/src/index.test.js
@@ -33,7 +33,7 @@ describe('getModule', () => {
const generator = mod.cmfModule.saga();
generator.next();
const result = generator.next();
- expect(result.value.FORK.args[1]).toEqual({
+ expect(result.value.payload.args[1]).toEqual({
'/foo': config.sagaRouterConfig['/foo'],
'/foo/bar': configBis.sagaRouterConfig['/foo/bar'],
});
diff --git a/packages/cmf-router/src/sagaRouter.test.js b/packages/cmf-router/src/sagaRouter.test.js
index 55eea1e7922..c12cbbf1aa8 100644
--- a/packages/cmf-router/src/sagaRouter.test.js
+++ b/packages/cmf-router/src/sagaRouter.test.js
@@ -1,5 +1,5 @@
import { spawn, take, cancel } from 'redux-saga/effects';
-import { createMockTask } from 'redux-saga/utils';
+import { createMockTask } from '@redux-saga/testing-utils';
import sagaRouter from './sagaRouter';
describe('sagaRouter import', () => {
diff --git a/packages/cmf/__tests__/Dispatcher.test.js b/packages/cmf/__tests__/Dispatcher.test.js
index d01ef28e17f..588803e3052 100644
--- a/packages/cmf/__tests__/Dispatcher.test.js
+++ b/packages/cmf/__tests__/Dispatcher.test.js
@@ -1,43 +1,24 @@
import React from 'react';
-import PropTypes from 'prop-types';
-import { shallow, mount } from 'enzyme';
-import { Dispatcher } from '../src/Dispatcher';
+import { mount } from 'enzyme';
+import { mock } from '../src';
+import ConnectedDispatcher, { Dispatcher } from '../src/Dispatcher';
import CONST from '../src/constant';
-const mockContext = {
- registry: {
- [`${CONST.REGISTRY_ACTION_CREATOR_PREFIX}:actionCreator:id`]: jest.fn(),
- [`${CONST.REGISTRY_ACTION_CREATOR_PREFIX}:another:actionCreator:id`]: jest.fn(),
- },
-};
-
-jest.mock('../src/action', () => ({
- getOnProps() {
- return ['onClick', 'onDoubleClick'];
- },
-}));
-jest.mock('../src/actionCreator', () => ({
- get(context, id) {
- if (
- id !== 'existingActionCreator:id' &&
- id !== 'actionCreator:id' &&
- id !== 'noOp' &&
- id !== 'another:actionCreator:id'
- ) {
- throw new Error(`action not found id: ${id}`);
- }
- },
-}));
+const noopRId = `${CONST.REGISTRY_ACTION_CREATOR_PREFIX}:noOp`;
describe('Testing ', () => {
- function replacer(k, v) {
- let val = v;
- if (typeof v === 'function') {
- val = '[Function]';
- }
- return val;
- }
- const noOp = () => {};
+ let registry;
+ const onError = jest.fn();
+ beforeEach(() => {
+ registry = {
+ [`${CONST.REGISTRY_ACTION_CREATOR_PREFIX}:existingActionCreator:id`]: jest.fn(),
+ [`${CONST.REGISTRY_ACTION_CREATOR_PREFIX}:actionCreator:id`]: jest.fn(),
+ [`${CONST.REGISTRY_ACTION_CREATOR_PREFIX}:noOp`]: jest.fn(),
+ [`${CONST.REGISTRY_ACTION_CREATOR_PREFIX}:another:actionCreator:id`]: jest.fn(),
+ };
+
+ jest.resetAllMocks();
+ });
it('should inject dispatchable on(event) props into its children', () => {
const dispatchActionCreator = jest.fn();
@@ -47,85 +28,63 @@ describe('Testing ', () => {
onDoubleClick="another:actionCreator:id"
dispatchActionCreator={dispatchActionCreator}
>
-
+
,
- {
- context: mockContext,
- },
- );
- expect(
- JSON.stringify(wrapper.find('button').props(), replacer).replace(/(\\t|\\n)/g, ''),
- ).toEqual(
- JSON.stringify({ onClick: noOp, onDoubleClick: noOp }, replacer).replace(/(\\t|\\n)/g, ''),
+ mock.Provider.getEnzymeOption({ registry, onError }),
);
+ expect(typeof wrapper.find('button').props().onClick).toEqual('function');
+ expect(typeof wrapper.find('button').props().onDoubleClick).toEqual('function');
});
it('should throw with unknown action', () => {
- expect(() => {
- shallow(
+ const opts = mock.Provider.getEnzymeOption({ registry });
+ mount(
+
-
- ,
- {
- context: mockContext,
- },
- );
- }).toThrow('action not found id: unknnown:actionCreator:id');
+
+
+ ,
+ opts,
+ );
+
+ expect(onError).toHaveBeenCalled();
+ expect(onError.mock.calls[0][0].message).toBe(
+ 'actionCreator not found in the registry: unknnown:actionCreator:id',
+ );
});
it('should have its method onEvent called when children handle an event', () => {
- const wrapper = shallow(
-
-
- ,
- {
- context: mockContext,
- },
+ const wrapper = mount(
+
+
+ ,
+ mock.Provider.getEnzymeOption({ registry, onError }),
);
- const buttonWrapper = wrapper.find('button').at(0);
- const instance = wrapper.instance();
- spyOn(instance, 'onEvent');
+ const buttonWrapper = wrapper.find('button');
buttonWrapper.simulate('click');
- expect(instance.onEvent).toHaveBeenCalled();
- expect(instance.onEvent).toHaveBeenCalledWith(undefined, 'onClick');
+ expect(registry[noopRId]).toHaveBeenCalled();
});
it(
'should call cmf.actionCreator.get and reThrow at mount time' +
"if action info bind onto on[eventName] can't be found in settings",
() => {
- expect(() => {
- mount(
+ mount(
+
-
- ,
- {
- context: mockContext,
- },
- );
- }).toThrowError('action not found id: error:actionCreator:id');
- },
- );
-
- it(
- 'should call cmf.actionCreator.get and reThrow at willreceivePropsTime' +
- "if action info bind onto on[eventName] can't be found in settings",
- () => {
- const wrapper = mount(
-
-
- ,
- {
- context: mockContext,
- },
+
+
+ ,
+ mock.Provider.getEnzymeOption({ registry }),
+ );
+ expect(onError).toHaveBeenCalled();
+ expect(onError.mock.calls[0][0].message).toBe(
+ 'actionCreator not found in the registry: error:actionCreator:id',
);
- expect(() => {
- wrapper.setProps({ onClick: 'error:another:actionCreator:id' });
- }).toThrowError('action not found id: error:another:actionCreator:id');
},
);
@@ -142,12 +101,7 @@ describe('Testing ', () => {
,
- {
- context: mockContext,
- childContextTypes: {
- registry: PropTypes.object.isRequired,
- },
- },
+ mock.Provider.getEnzymeOption({ registry, onError }),
);
wrapper.find('a').simulate('click');
expect(onClick).toHaveBeenCalled();
@@ -167,12 +121,7 @@ describe('Testing ', () => {
,
- {
- context: mockContext,
- childContextTypes: {
- registry: PropTypes.object.isRequired,
- },
- },
+ mock.Provider.getEnzymeOption({ registry, onError }),
);
wrapper.find('a').simulate('click');
expect(onClick).not.toHaveBeenCalled();
@@ -184,13 +133,11 @@ describe('Testing ', () => {
type: 'click',
preventDefault: jest.fn(),
};
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- context: mockContext,
- },
+ mock.Provider.getEnzymeOption({ registry, onError }),
);
wrapper.find('a').simulate('click', event);
expect(event.preventDefault).toHaveBeenCalled();
@@ -210,10 +157,15 @@ describe('Testing ', () => {
extra: 'foo',
children: ,
};
- const wrapper = shallow(, {
- context: mockContext,
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption({ registry, onError }),
+ );
wrapper.find('a').simulate('click', event);
- expect(dispatchActionCreator).toHaveBeenCalledWith('noOp', event, props);
+ expect(dispatchActionCreator).toHaveBeenCalledWith(
+ 'noOp',
+ expect.objectContaining(event),
+ expect.objectContaining(props),
+ );
});
});
diff --git a/packages/cmf/__tests__/Inject.test.js b/packages/cmf/__tests__/Inject.test.js
index 9382a88d12f..d8ad4abe718 100644
--- a/packages/cmf/__tests__/Inject.test.js
+++ b/packages/cmf/__tests__/Inject.test.js
@@ -1,40 +1,47 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
import Inject from '../src/Inject.component';
+import { mock } from '../src';
describe('Inject', () => {
it('should render', () => {
// given
- const MyComponent = jest.fn();
+ const MyComponent = jest.fn(() => Hello);
MyComponent.displayName = 'MyComponent';
const context = {
registry: {
'_.route.component:MyComponent': MyComponent,
},
};
+ const Provider = mock.Provider;
// when
- const wrapper = shallow(, { context });
+ const wrapper = mount(
+ ,
+ Provider.getEnzymeOption(context),
+ );
// then
- expect(wrapper.equals()).toBe(true);
+ expect(wrapper.find(MyComponent).equals()).toBe(true);
});
it('should render error if component not found', () => {
// given
const MyComponent = jest.fn();
MyComponent.displayName = 'MyComponent';
- const context = { registry: {} };
+ const Provider = mock.Provider;
// when
- const wrapper = shallow(, { context });
+ const wrapper = mount(, Provider.getEnzymeOption());
// then
- expect(wrapper.equals(
-
- )).toBe(true);
+ expect(
+ wrapper
+ .find(Inject.NotFoundComponent)
+ .equals(
+ ,
+ ),
+ ).toBe(true);
});
});
diff --git a/packages/cmf/__tests__/action.test.js b/packages/cmf/__tests__/action.test.js
index bcc3b1c6187..6d70df4c024 100644
--- a/packages/cmf/__tests__/action.test.js
+++ b/packages/cmf/__tests__/action.test.js
@@ -1,5 +1,5 @@
import actionAPI from '../src/action';
-import mock from '../src/mock';
+import { mock } from '../src';
describe('CMF action', () => {
let state;
@@ -8,11 +8,11 @@ describe('CMF action', () => {
let settings;
beforeEach(() => {
- settings = mock.settings();
- state = mock.state();
+ settings = mock.store.settings();
+ state = mock.store.state();
state.cmf.settings = settings;
- context = mock.context(state);
- emptyContext = mock.emptyContext();
+ context = mock.store.context(state);
+ emptyContext = mock.store.emptyContext();
});
it('getActionsById should return action from settings', () => {
const actions = actionAPI.getActionsById(context);
diff --git a/packages/cmf/__tests__/actionCreator.test.js b/packages/cmf/__tests__/actionCreator.test.js
index cfb42c3300e..93c3b3eacdb 100644
--- a/packages/cmf/__tests__/actionCreator.test.js
+++ b/packages/cmf/__tests__/actionCreator.test.js
@@ -1,12 +1,11 @@
-import mock from '../src/mock';
+import { mock } from '../src';
import actionCreatorAPI from '../src/actionCreator';
-import { create } from 'domain';
describe('CMF action', () => {
let context;
beforeEach(() => {
- context = mock.context();
+ context = mock.store.context();
});
it('get should return a function', () => {
diff --git a/packages/cmf/__tests__/bootstrap.test.js b/packages/cmf/__tests__/bootstrap.test.js
index 7dec37febff..01ed0f1ee49 100644
--- a/packages/cmf/__tests__/bootstrap.test.js
+++ b/packages/cmf/__tests__/bootstrap.test.js
@@ -34,6 +34,7 @@ jest.mock('../src/onError', () => ({
}));
jest.mock('../src/registry', () => ({
registerMany: jest.fn(),
+ getRegistry: jest.fn(),
}));
jest.mock('../src/actionCreator', () => ({
registerMany: jest.fn(),
diff --git a/packages/cmf/__tests__/cmfConnect.test.js b/packages/cmf/__tests__/cmfConnect.test.js
index 63c07177d44..13d2c743770 100644
--- a/packages/cmf/__tests__/cmfConnect.test.js
+++ b/packages/cmf/__tests__/cmfConnect.test.js
@@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { fromJS, Map } from 'immutable';
-import { shallow, mount } from 'enzyme';
-import uuid from 'uuid';
+import { mount } from 'enzyme';
+
+// eslint-disable-next-line
import omit from 'lodash/omit';
import expression from '../src/expression';
-import mock from '../src/mock';
+import { mock } from '../src';
import { mapStateToViewProps } from '../src/settings';
import cmfConnect, {
@@ -53,7 +54,7 @@ describe('cmfConnect', () => {
describe('#getStateToProps', () => {
it('should call getStateProps', () => {
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.components = fromJS({
TestComponent: {
testId: {
@@ -71,7 +72,7 @@ describe('cmfConnect', () => {
});
it('should inject view settings using props.view', () => {
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.components = fromJS({});
const props = getStateToProps({
componentId: 'testId',
@@ -83,7 +84,7 @@ describe('cmfConnect', () => {
});
it('should inject view settings using displayName and componentId', () => {
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.components = fromJS({});
state.cmf.settings.props['TestComponent#default'] = { foo: 'from-displayName' };
state.cmf.settings.props['TestComponent#props-id'] = { foo: 'from-props-componentId' };
@@ -114,7 +115,7 @@ describe('cmfConnect', () => {
delete state.cmf.settings.props['TestComponent#connect-id'];
});
it('should evaluate expression using all props', () => {
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.components = fromJS({});
expression.register('hasModel', ({ payload }) => payload.model !== undefined);
const props = getStateToProps({
@@ -129,7 +130,7 @@ describe('cmfConnect', () => {
expect(props.model).toBeUndefined();
});
it('should pass view settings together with own props when calling mapStateToProps', () => {
- const state = mock.state();
+ const state = mock.store.state();
const mapStateToProps = jest.fn();
const ownProps = { view: 'simple' };
getStateToProps({
@@ -215,19 +216,19 @@ describe('cmfConnect', () => {
});
it('should create a connected component', () => {
- const TestComponent = jest.fn();
+ const TestComponent = jest.fn(() => null);
TestComponent.displayName = 'TestComponent';
mapStateToViewProps.cache.clear();
const CMFConnected = cmfConnect({})(TestComponent);
expect(CMFConnected.displayName).toBe('Connect(CMF(TestComponent))');
expect(CMFConnected.WrappedComponent).toBe(TestComponent);
- const wrapper = shallow(, { context: mock.context() });
- expect(wrapper.props()).toMatchSnapshot();
+ const wrapper = mount(, mock.Provider.getEnzymeOption(mock.store.context()));
+ expect(wrapper.find('CMF(TestComponent)').props()).toMatchSnapshot();
});
it('should expose getState static function to get the state', () => {
expect(typeof CMFConnectedButton.getState).toBe('function');
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.components = fromJS({
Button: {
default: { foo: 'bar' },
@@ -259,7 +260,7 @@ describe('cmfConnect', () => {
it('should expose setStateAction static function to get the redux action to setState', () => {
expect(typeof CMFConnectedButton.setStateAction).toBe('function');
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.components = fromJS({
Button: {
default: { foo: 'foo' },
@@ -294,7 +295,7 @@ describe('cmfConnect', () => {
});
it('should support no context in dispatchActionCreator', () => {
const TestComponent = props => {
- const rest = Object.assign({}, omit(props, cmfConnect.INJECTED_PROPS));
+ const rest = { ...omit(props, cmfConnect.INJECTED_PROPS) };
return
;
};
TestComponent.displayName = 'TestComponent';
@@ -304,8 +305,11 @@ describe('cmfConnect', () => {
const props = {
dispatchActionCreator: jest.fn(),
};
- const context = mock.context();
- const wrapper = mount(, { context });
+ const context = mock.store.context();
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
const injectedProps = wrapper.find(TestComponent).props();
expect(injectedProps.dispatchActionCreator).not.toBe(props.dispatchActionCreator);
const event = {};
@@ -317,7 +321,7 @@ describe('cmfConnect', () => {
expect(call[1]).toBe(event);
expect(call[2]).toBe(data);
expect(call[3].registry).toBe(context.registry);
- expect(call[3].store).toBe(context.store);
+ expect(call[3].store).toMatchObject(context.store);
});
it('should pass defaultState when there is no component state in store', () => {
@@ -326,18 +330,13 @@ describe('cmfConnect', () => {
const defaultState = new Map({ toto: 'lol' });
const CMFConnected = cmfConnect({ defaultState })(TestComponent);
- const wrapper = mount(, {
- context: mock.context(),
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(, mock.Provider.getEnzymeOption(mock.store.context()));
expect(wrapper.find(TestComponent).props().state).toBe(defaultState);
});
it('should componentDidMount initState and dispatchActionCreator after the saga', () => {
- const TestComponent = jest.fn();
+ const TestComponent = jest.fn(() => null);
TestComponent.displayName = 'TestComponent';
const STATE = new Map();
const CMFConnected = cmfConnect({})(TestComponent);
@@ -349,9 +348,8 @@ describe('cmfConnect', () => {
foo: 'bar',
saga: 'saga',
};
- const context = mock.context();
- const instance = new CMFConnected.CMFContainer(props, context);
- instance.componentDidMount();
+ const context = mock.store.context();
+ mount(, mock.Provider.getEnzymeOption(context));
expect(props.dispatchActionCreator).toHaveBeenCalled();
const callSagaActionCreator = props.dispatchActionCreator.mock.calls[0];
const callDidMountActionCreator = props.dispatchActionCreator.mock.calls[1];
@@ -362,25 +360,24 @@ describe('cmfConnect', () => {
});
expect(callDidMountActionCreator[0]).toBe('hello');
expect(callDidMountActionCreator[1]).toBe(null);
- expect(callDidMountActionCreator[2]).toBe(props);
- expect(callDidMountActionCreator[3].registry).toBe(instance.context.registry);
- expect(callDidMountActionCreator[3].store).toBe(instance.context.store);
+ expect(callDidMountActionCreator[2]).toEqual(props);
+ expect(callDidMountActionCreator[3].registry).toBe(context.registry);
+ expect(callDidMountActionCreator[3].store).toMatchObject(context.store);
expect(props.initState).toHaveBeenCalled();
expect(props.initState.mock.calls[0][0]).toBe(props.initialState);
});
it('should componentDidMount support saga', () => {
- const TestComponent = jest.fn();
+ const TestComponent = jest.fn(() => null);
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();
+ const context = mock.store.context();
+ mount(, mock.Provider.getEnzymeOption(context));
expect(props.dispatchActionCreator).toHaveBeenCalledWith(
'cmf.saga.start',
{ type: 'DID_MOUNT', componentId: '42' },
@@ -388,12 +385,15 @@ describe('cmfConnect', () => {
componentId: 'default',
saga: 'hello',
}),
- instance.context,
+ expect.objectContaining({
+ store: context.store,
+ registry: context.registry,
+ }),
);
});
it('should componentWillUnmount support saga', () => {
- const TestComponent = jest.fn();
+ const TestComponent = jest.fn(() => null);
TestComponent.displayName = 'TestComponent';
const CMFConnected = cmfConnect({})(TestComponent);
const props = {
@@ -401,19 +401,25 @@ describe('cmfConnect', () => {
dispatchActionCreator: jest.fn(),
deleteState: jest.fn(),
};
- const context = mock.context();
- const instance = new CMFConnected.CMFContainer(props, context);
- instance.componentWillUnmount();
+ const context = mock.store.context();
+ const instance = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
+ instance.unmount();
expect(props.dispatchActionCreator).toHaveBeenCalledWith(
'cmf.saga.stop',
{ type: 'WILL_UNMOUNT', componentId: '42' },
- instance.props,
- instance.context,
+ props,
+ expect.objectContaining({
+ store: context.store,
+ registry: context.registry,
+ }),
);
});
it('should componentWillUnMount dispatchActionCreator', () => {
- const TestComponent = jest.fn();
+ const TestComponent = jest.fn(() => null);
TestComponent.displayName = 'TestComponent';
const CMFConnected = cmfConnect({})(TestComponent);
const props = {
@@ -422,16 +428,22 @@ describe('cmfConnect', () => {
deleteState: jest.fn(),
foo: 'bar',
};
- const context = mock.context();
- const instance = new CMFConnected.CMFContainer(props, context);
- instance.componentWillUnmount();
+ const context = mock.store.context();
+ context.registry = {
+ 'actionCreator:bye': jest.fn(),
+ };
+ const instance = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
+ instance.unmount();
expect(props.dispatchActionCreator).toHaveBeenCalled();
const call = props.dispatchActionCreator.mock.calls[0];
expect(call[0]).toBe('bye');
expect(call[1]).toBe(null);
- expect(call[2]).toBe(props);
- expect(call[3].registry).toBe(instance.context.registry);
- expect(call[3].store).toBe(instance.context.store);
+ expect(call[2]).toEqual(props);
+ expect(call[3].registry).toBe(context.registry);
+ expect(call[3].store).toBe(context.store);
expect(props.deleteState).toHaveBeenCalled();
expect(props.deleteState.mock.calls[0][0]).toBe();
@@ -446,15 +458,10 @@ describe('cmfConnect', () => {
})(TestComponent);
expect(CMFConnected.displayName).toBe('Connect(CMF(TestComponent))');
expect(CMFConnected.WrappedComponent).toBe(TestComponent);
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
- const wrapper = mount(, {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(, mock.Provider.getEnzymeOption(context));
// when
wrapper.unmount();
@@ -474,15 +481,10 @@ describe('cmfConnect', () => {
})(TestComponent);
expect(CMFConnected.displayName).toBe('Connect(CMF(TestComponent))');
expect(CMFConnected.WrappedComponent).toBe(TestComponent);
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
- const wrapper = mount(, {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(, mock.Provider.getEnzymeOption(context));
// when
wrapper.unmount();
@@ -501,15 +503,13 @@ describe('cmfConnect', () => {
})(TestComponent);
expect(CMFConnected.displayName).toBe('Connect(CMF(TestComponent))');
expect(CMFConnected.WrappedComponent).toBe(TestComponent);
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
- const wrapper = mount(, {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
// when
wrapper.unmount();
@@ -528,15 +528,13 @@ describe('cmfConnect', () => {
})(TestComponent);
expect(CMFConnected.displayName).toBe('Connect(CMF(TestComponent))');
expect(CMFConnected.WrappedComponent).toBe(TestComponent);
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
- const wrapper = mount(, {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
// when
wrapper.unmount();
@@ -558,15 +556,10 @@ describe('cmfConnect', () => {
const TestComponent = () => ;
TestComponent.displayName = 'TestComponent';
const CMFConnected = cmfConnect({})(TestComponent);
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
- const wrapper = mount(, {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(, mock.Provider.getEnzymeOption(context));
const props = wrapper.find(TestComponent).props();
// then
@@ -579,10 +572,17 @@ describe('cmfConnect', () => {
it('should expose displayName', () => {
const ArrowComponent = () => ;
+ ArrowComponent.displayName = 'ArrowComponent';
function FunctionComponent() {
return ;
}
- class ClassComponent extends React.Component {}
+ FunctionComponent.displayName = 'FunctionComponent';
+
+ // eslint-disable-next-line react/prefer-stateless-function
+ class ClassComponent extends React.Component {
+ static displayName = 'ClassComponent';
+ }
+
const CMFConnectedArrow = cmfConnect({})(ArrowComponent);
const CMFConnectedFunction = cmfConnect({})(FunctionComponent);
const CMFConnectedClass = cmfConnect({})(ClassComponent);
@@ -594,15 +594,13 @@ describe('cmfConnect', () => {
const onClickDispatch = {
type: 'MY_BUTTON_CLICKED',
};
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
- const wrapper = mount(, {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
const props = wrapper.find(Button).props();
expect(props.onClickDispatch).toBeUndefined();
expect(props.onClick).toBeDefined();
@@ -617,18 +615,16 @@ describe('cmfConnect', () => {
});
it('should transform onEventActionCreator props to onEvent handler', () => {
const onClickActionCreator = 'myactionCreator';
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
context.registry = {
'actionCreator:myactionCreator': event => ({ type: 'FETCH_STUFF', event }),
};
- const wrapper = mount(, {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
const props = wrapper.find(Button).props();
expect(props.onClick).toBeDefined();
expect(props.onClickActionCreator).toBeUndefined();
@@ -649,7 +645,7 @@ describe('cmfConnect', () => {
cmf: { collectionId: 'foo' },
},
};
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
context.registry = {
'actionCreator:myfetch': (event, data) => ({
@@ -659,12 +655,10 @@ describe('cmfConnect', () => {
}),
};
- const wrapper = mount(, {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
const props = wrapper.find(Button).props();
expect(props.onClick).toBeDefined();
expect(context.store.dispatch).not.toHaveBeenCalled();
@@ -680,17 +674,12 @@ describe('cmfConnect', () => {
const config = {
disabled: true,
};
- const context = mock.context();
+ const context = mock.store.context();
context.store.dispatch = jest.fn();
const wrapper = mount(
,
- {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- },
+ mock.Provider.getEnzymeOption(context),
);
const props = wrapper.find(Button).props();
expect(props.onClick).toBeDefined();
@@ -712,8 +701,8 @@ describe('cmfConnect', () => {
});
it('should spread cmf state when onEventSetState is set', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
context.store.getState = () => {
return {
cmf: {
@@ -731,21 +720,19 @@ describe('cmfConnect', () => {
const wrapper = mount(
,
- {
- context,
- childContextTypes: {
- registry: PropTypes.object,
- },
- },
+ mock.Provider.getEnzymeOption(context),
);
const props = wrapper.find(Button).props();
expect(props.inProgress).toBe(false);
});
it('should check that component will not be rendered if renderIf equals false', () => {
- const context = mock.context();
+ const context = mock.store.context();
const CMFConnected = cmfConnect({})(Button);
- const mounted = mount();
+ const mounted = mount(
+ ,
+ mock.Provider.getEnzymeOption(),
+ );
expect(mounted.html()).toBe('');
});
@@ -774,15 +761,17 @@ describe('cmfConnect', () => {
});
describe('#omitCMFProps', () => {
it('should cmfConnect({ omitCMFProps: false }) keep compatibility', () => {
- const context = mock.context();
+ const store = mock.store.store();
const TestComponent = props => ;
+ TestComponent.displayName = 'TestComponent';
const WithCMFProps = cmfConnect({ omitCMFProps: false })(TestComponent);
- const wrapperWithCMFProps = shallow(
- shallow(, {
- context: { store: context.store },
- }).getElement(),
- );
+ const wrapperWithCMFProps = mount(
+ ,
+ mock.Provider.getEnzymeOption(),
+ ).find('div');
+
expect(Object.keys(wrapperWithCMFProps.props())).toEqual([
+ 'store',
'className',
'id',
'setState',
@@ -796,34 +785,37 @@ describe('cmfConnect', () => {
]);
});
it('should cmfConnect({ omitCMFProps: true }) remove all internals', () => {
- const context = mock.context();
const TestComponent = props => ;
+ TestComponent.displayName = 'TestComponent';
const WithoutCMFProps = cmfConnect({ omitCMFProps: true })(TestComponent);
- const wrapperWithoutCMFProps = shallow(
- shallow(, {
- context: { store: context.store },
- }).getElement(),
- );
+ const store = mock.store.store();
+ const wrapperWithoutCMFProps = mount(
+ ,
+ mock.Provider.getEnzymeOption(),
+ ).find('div');
expect(wrapperWithoutCMFProps.props()).toEqual({
className: 'foo',
id: 'bar',
+ store,
});
});
it('should cmfConnect({ omitCMFProps: true, withComponentRegistry: true }) add getComponent', () => {
- const context = mock.context();
const TestComponent = props => ;
+ TestComponent.displayName = 'TestComponent';
const WithoutCMFProps = cmfConnect({ omitCMFProps: true, withComponentRegistry: true })(
TestComponent,
);
- const wrapperWithoutCMFProps = shallow(
- shallow(, {
- context: { store: context.store },
- }).getElement(),
- );
+ const store = mock.store.store();
+ const wrapperWithoutCMFProps = mount(
+ ,
+ mock.Provider.getEnzymeOption(),
+ ).find('div');
+
expect(wrapperWithoutCMFProps.props()).toEqual({
className: 'foo',
id: 'bar',
getComponent: component.get,
+ store,
});
});
});
diff --git a/packages/cmf/__tests__/cmfModule.merge.test.js b/packages/cmf/__tests__/cmfModule.merge.test.js
index 5e6802c7730..9402e4d3f20 100644
--- a/packages/cmf/__tests__/cmfModule.merge.test.js
+++ b/packages/cmf/__tests__/cmfModule.merge.test.js
@@ -6,6 +6,22 @@ import { mount } from 'enzyme';
import mergeModules from '../src/cmfModule.merge';
describe('mergeModule', () => {
+ // eslint-disable-next-line no-console
+ const originalLog = console;
+ beforeEach(() => {
+ // eslint-disable-next-line no-console
+ global.console = {
+ warn: jest.fn(),
+ log: jest.fn(),
+ };
+ });
+ afterEach( () => {
+ // eslint-disable-next-line no-console
+ global.console = originalLog;
+ });
+ afterAll(() => {
+ console.log('test ####');
+ });
it('should merge components config', () => {
const a = {
components: {
diff --git a/packages/cmf/__tests__/expressions/index.test.js b/packages/cmf/__tests__/expressions/index.test.js
index 759eb3cec48..f13a854920d 100644
--- a/packages/cmf/__tests__/expressions/index.test.js
+++ b/packages/cmf/__tests__/expressions/index.test.js
@@ -1,6 +1,6 @@
import Immutable from 'immutable';
import expressions from '../../src/expressions';
-import mock from '../../src/mock';
+import { mock } from '../../src';
describe('expressions', () => {
it('should export some expressions', () => {
@@ -11,8 +11,8 @@ describe('expressions', () => {
});
describe('cmf.collections.get', () => {
it('should get collection content', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
title: 'my title',
@@ -24,8 +24,8 @@ describe('expressions', () => {
);
});
it("should return default value if collection doesn't exists", () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
context.store.getState = () => state;
state.cmf.collections = new Immutable.Map({});
expect(expressions['cmf.collections.get']({ context }, 'article.title', 'no title')).toBe(
@@ -36,8 +36,8 @@ describe('expressions', () => {
describe('cmf.collections.includes', () => {
it('should return true if the value is present in the list', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
title: 'title',
@@ -50,8 +50,8 @@ describe('expressions', () => {
);
});
it('should return false if the value is not present in the list', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
title: 'title',
@@ -64,8 +64,8 @@ describe('expressions', () => {
);
});
it("should return false if collection doesn't exists", () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
context.store.getState = () => state;
state.cmf.collections = new Immutable.Map({});
expect(expressions['cmf.collections.includes']({ context }, 'article.tags', 'test')).toBe(
@@ -75,8 +75,8 @@ describe('expressions', () => {
});
describe('cmf.collections.oneOf', () => {
it('should return true if one of the values is present in the list', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
title: 'title',
@@ -89,8 +89,8 @@ describe('expressions', () => {
).toBe(true);
});
it('should return false if all values are not present in the list', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
title: 'title',
@@ -103,8 +103,8 @@ describe('expressions', () => {
).toBe(false);
});
it("should return false if collection doesn't exist", () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
context.store.getState = () => state;
state.cmf.collections = new Immutable.Map({});
expect(
@@ -112,8 +112,8 @@ describe('expressions', () => {
).toBe(false);
});
it('should throw an error if values are not an array', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
context.store.getState = () => state;
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
@@ -128,8 +128,8 @@ describe('expressions', () => {
});
describe('cmf.collections.allOf', () => {
it('should return true if all of the values are present in the list', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
title: 'title',
@@ -146,8 +146,8 @@ describe('expressions', () => {
).toBe(true);
});
it('should return false if not all values are not present in the list', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
title: 'title',
@@ -160,8 +160,8 @@ describe('expressions', () => {
).toBe(false);
});
it("should return false if collection doesn't exist", () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
context.store.getState = () => state;
state.cmf.collections = new Immutable.Map({});
expect(
@@ -169,8 +169,8 @@ describe('expressions', () => {
).toBe(false);
});
it('should throw an error if values are not an array', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
context.store.getState = () => state;
state.cmf.collections = new Immutable.Map({
article: new Immutable.Map({
@@ -186,8 +186,8 @@ describe('expressions', () => {
describe('cmf.components.get', () => {
it('should get component state', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.components = new Immutable.Map({
MyComponent: new Immutable.Map({
default: new Immutable.Map({
@@ -201,8 +201,8 @@ describe('expressions', () => {
).toBe(true);
});
it('should return default value if no component state', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.components = new Immutable.Map({});
context.store.getState = () => state;
expect(
@@ -213,8 +213,8 @@ describe('expressions', () => {
describe('cmf.components.includes', () => {
it('should return true if the value is present in the list', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.components = new Immutable.Map({
MyComponent: new Immutable.Map({
default: new Immutable.Map({
@@ -229,8 +229,8 @@ describe('expressions', () => {
).toBe(true);
});
it('should return default false if there is no component state', () => {
- const context = mock.context();
- const state = mock.state();
+ const context = mock.store.context();
+ const state = mock.store.state();
state.cmf.components = new Immutable.Map({});
context.store.getState = () => state;
expect(
diff --git a/packages/cmf/__tests__/onEvent.test.js b/packages/cmf/__tests__/onEvent.test.js
index 0092d0682af..4d7a0d3d769 100644
--- a/packages/cmf/__tests__/onEvent.test.js
+++ b/packages/cmf/__tests__/onEvent.test.js
@@ -17,37 +17,37 @@ describe('onEvent', () => {
currentHandler = jest.fn();
});
it('should return a function', () => {
- const handler = onEvent.getOnEventSetStateHandler(instance, config, currentHandler);
+ const handler = onEvent.getOnEventSetStateHandler(instance, {}, config, currentHandler);
expect(typeof handler).toBe('function');
});
it('the handler should call the currentHandler with the same args', () => {
- const handler = onEvent.getOnEventSetStateHandler(instance, config, currentHandler);
+ const handler = onEvent.getOnEventSetStateHandler(instance, {}, config, currentHandler);
const args = [{ type: 'click' }, { foo: 'bar' }];
handler(...args);
expect(currentHandler).toHaveBeenCalledWith(...args);
});
it('the handler should call props.setState with static value', () => {
- const handler = onEvent.getOnEventSetStateHandler(instance, config, currentHandler);
+ const handler = onEvent.getOnEventSetStateHandler(instance, {}, config, currentHandler);
config.foo = true;
handler();
expect(instance.props.setState).toHaveBeenCalledWith(config);
});
it('the handler should call props.setState with parsed arguments', () => {
- const handler = onEvent.getOnEventSetStateHandler(instance, config, currentHandler);
+ const handler = onEvent.getOnEventSetStateHandler(instance, {}, config, currentHandler);
const args = [{ type: 'click' }, { foo: 'bar' }];
config.inProgress = [1, 'foo'];
handler(...args);
expect(instance.props.setState).toHaveBeenCalledWith({ inProgress: 'bar' });
});
it('the handler should call props.setState with parsed arguments', () => {
- const handler = onEvent.getOnEventSetStateHandler(instance, config, currentHandler);
+ const handler = onEvent.getOnEventSetStateHandler(instance, {}, config, currentHandler);
const args = [{ type: 'click' }, { foo: 'bar' }];
config.inProgress = [1];
handler(...args);
expect(instance.props.setState).toHaveBeenCalledWith({ inProgress: args[1] });
});
it('the handler should call props.setState and toggle a value', () => {
- const handler = onEvent.getOnEventSetStateHandler(instance, config, currentHandler);
+ const handler = onEvent.getOnEventSetStateHandler(instance, {}, config, currentHandler);
const args = [{ type: 'click' }, { foo: 'bar' }];
config.docked = 'toggle';
handler(...args);
diff --git a/packages/cmf/__tests__/reduxstorage/reduxLocalStorage.test.js b/packages/cmf/__tests__/reduxstorage/reduxLocalStorage.test.js
index 5731bbb8cc8..b5d84d87c8f 100644
--- a/packages/cmf/__tests__/reduxstorage/reduxLocalStorage.test.js
+++ b/packages/cmf/__tests__/reduxstorage/reduxLocalStorage.test.js
@@ -1,6 +1,19 @@
import reduxLocalStorage from '../../src/reduxstorage/reduxLocalStorage';
describe('reduxLocalStorage', () => {
+ // eslint-disable-next-line no-console
+ const originalLog = console;
+ beforeEach(() => {
+ // eslint-disable-next-line no-console
+ global.console = {
+ warn: jest.fn(),
+ log: jest.fn(),
+ };
+ });
+ afterEach( () => {
+ // eslint-disable-next-line no-console
+ global.console = originalLog;
+ });
it('should expose API', () => {
expect(typeof reduxLocalStorage.loadInitialState).toBe('function');
expect(typeof reduxLocalStorage.saveOnReload).toBe('function');
diff --git a/packages/cmf/__tests__/sagas/collection.test.js b/packages/cmf/__tests__/sagas/collection.test.js
index 4432d9c19d0..3cc85213091 100644
--- a/packages/cmf/__tests__/sagas/collection.test.js
+++ b/packages/cmf/__tests__/sagas/collection.test.js
@@ -1,5 +1,4 @@
-import { call, select } from 'redux-saga/effects';
-import { delay } from 'redux-saga';
+import { delay, call, select } from 'redux-saga/effects';
import Immutable from 'immutable';
import selectors from '../../src/selectors';
import {
@@ -16,7 +15,7 @@ describe('waitFor', () => {
const withCollection = withoutCollection.cmf.collections.set('foo', new Immutable.Map({}));
const gen = waitFor('foo');
expect(gen.next().value).toEqual(select(selectors.collections.get, 'foo'));
- expect(gen.next().value).toEqual(call(delay, 10));
+ expect(gen.next().value).toEqual(delay(10));
expect(gen.next().value).toEqual(select(selectors.collections.get, 'foo'));
expect(gen.next(withCollection).value).toBeUndefined();
});
diff --git a/packages/cmf/__tests__/sagas/component.test.js b/packages/cmf/__tests__/sagas/component.test.js
index cab23cb18a2..abd36ef0adf 100644
--- a/packages/cmf/__tests__/sagas/component.test.js
+++ b/packages/cmf/__tests__/sagas/component.test.js
@@ -1,5 +1,5 @@
import { spawn, takeEvery, cancel } from 'redux-saga/effects';
-import { createMockTask } from 'redux-saga/utils';
+import { createMockTask } from '@redux-saga/testing-utils';
import registry from '../../src/registry';
import { onSagaStart, handle } from '../../src/sagas/component';
import CONST from '../../src/constant';
@@ -23,9 +23,9 @@ describe('sagas.component', () => {
// then
expect(gen.next().value).toEqual(spawn(saga, { componentId: 'myComponent' }));
const next = gen.next(task).value;
- expect(next.TAKE).toBeDefined();
+ expect(next.payload).toBeDefined();
expect(
- next.TAKE.pattern({
+ next.payload.pattern({
type: `${CONST.WILL_UNMOUNT_SAGA_STOP}_my-saga`,
event: {
componentId: 41,
@@ -33,7 +33,7 @@ describe('sagas.component', () => {
}),
).toBeFalsy();
expect(
- next.TAKE.pattern({
+ next.payload.pattern({
type: `${CONST.WILL_UNMOUNT_SAGA_STOP}_my-saga2`,
event: {
componentId: 42,
@@ -41,7 +41,7 @@ describe('sagas.component', () => {
}),
).toBeFalsy();
expect(
- next.TAKE.pattern({
+ next.payload.pattern({
type: `${CONST.WILL_UNMOUNT_SAGA_STOP}_my-saga`,
event: {
componentId: 42,
@@ -70,7 +70,7 @@ describe('sagas.component', () => {
// then
expect(gen.next().value).toEqual(spawn(saga, { componentId: 'myComponent' }));
const next = gen.next(task).value;
- expect(next.TAKE).toBeDefined();
+ expect(next.payload).toBeDefined();
expect(gen.next({ event: { componentId: 42 } }).value).toEqual(cancel(task));
});
diff --git a/packages/cmf/__tests__/settings.test.js b/packages/cmf/__tests__/settings.test.js
index 7e4029c53e8..b8e9304953f 100644
--- a/packages/cmf/__tests__/settings.test.js
+++ b/packages/cmf/__tests__/settings.test.js
@@ -1,30 +1,40 @@
import React from 'react';
import { mount } from 'enzyme';
+import { Provider } from 'react-redux';
import { generateDefaultViewId, mapStateToViewProps, WaitForSettings } from '../src/settings';
-import mock, { store } from '../src/mock';
-
+import { mock } from '../src';
describe('settings', () => {
describe('mapStateToViewProps', () => {
it('should apply default props from displayName if no view are passed', () => {
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.settings.props.MyComponent = { foo: 'bar' };
const props = mapStateToViewProps(state, { views: undefined }, 'MyComponent');
expect(props.foo).toBe('bar');
});
it('should apply default props from displayName and componentId if no view are passed', () => {
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.settings.props.MyComponent = { foo: 'bar' };
state.cmf.settings.props['MyComponent#my-component-id'] = { foo: 'baz' };
- const props = mapStateToViewProps(state, { view: undefined }, 'MyComponent', 'my-component-id');
+ const props = mapStateToViewProps(
+ state,
+ { view: undefined },
+ 'MyComponent',
+ 'my-component-id',
+ );
expect(props.foo).toBe('baz');
});
it('should apply default props from displayName and componentId without HOC', () => {
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.settings.props.MyComponent = { foo: 'bar' };
state.cmf.settings.props['MyComponent#my-component-id'] = { foo: 'baz' };
- const props = mapStateToViewProps(state, { view: undefined }, 'Translate(Container(MyComponent))', 'my-component-id');
+ const props = mapStateToViewProps(
+ state,
+ { view: undefined },
+ 'Translate(Container(MyComponent))',
+ 'my-component-id',
+ );
expect(props.foo).toBe('baz');
});
});
@@ -42,9 +52,7 @@ describe('settings', () => {
});
it('return componentName if the only given parameter', () => {
- expect(generateDefaultViewId(undefined, 'componentName')).toBe(
- 'componentName',
- );
+ expect(generateDefaultViewId(undefined, 'componentName')).toBe('componentName');
});
it('return undefined if all parameter are undefined', () => {
@@ -57,32 +65,29 @@ describe('settings', () => {
});
describe('WaitForSettings', () => {
it('should display using loader if state settings is not initialized', () => {
- const state = mock.state();
+ const state = mock.store.state();
const wrapper = mount(Hello, {
- context: {
- store: store.store(state),
- },
+ wrappingComponent: Provider,
+ wrappingComponentProps: { store: mock.store.store(state) },
});
expect(wrapper.text()).toBe('loading');
});
it('should display loading using AppLoader', () => {
const AppLoader = () => custom loader
;
- const state = mock.state();
+ const state = mock.store.state();
const wrapper = mount(Hello, {
- context: {
- store: store.store(state),
- },
+ wrappingComponent: Provider,
+ wrappingComponentProps: { store: mock.store.store(state) },
});
expect(wrapper.text()).not.toBe('loading');
expect(wrapper.text()).toBe('custom loader');
});
it('should display children when settings are initialized', () => {
- const state = mock.state();
+ const state = mock.store.state();
state.cmf.settings.initialized = true;
const wrapper = mount(Hello, {
- context: {
- store: store.store(state),
- },
+ wrappingComponent: Provider,
+ wrappingComponentProps: { store: mock.store.store(state) },
});
expect(wrapper.text()).not.toBe('loading');
expect(wrapper.text()).toBe('Hello');
diff --git a/packages/cmf/package.json b/packages/cmf/package.json
index fd1abae1f3a..7478c10419d 100644
--- a/packages/cmf/package.json
+++ b/packages/cmf/package.json
@@ -47,11 +47,11 @@
"path-to-regexp": "^2.4.0",
"prop-types": "^15.8.1",
"react-immutable-proptypes": "^2.2.0",
- "react-redux": "^5.1.2",
- "redux": "^3.7.2",
- "redux-batched-actions": "^0.2.1",
+ "react-redux": "^7.2.2",
+ "redux": "^4.0.5",
+ "redux-batched-actions": "^0.5.0",
"redux-batched-subscribe": "^0.1.6",
- "redux-saga": "^0.15.6",
+ "redux-saga": "^1.1.3",
"redux-storage": "^4.1.2",
"redux-storage-decorator-filter": "^1.1.8",
"redux-storage-decorator-immutablejs": "^1.0.4",
@@ -60,6 +60,7 @@
"uuid": "^3.4.0"
},
"devDependencies": {
+ "@redux-saga/testing-utils": "^1.1.3",
"@talend/scripts-core": "^11.3.0",
"@talend/scripts-preset-react-lib": "^9.9.3",
"enzyme": "^3.11.0",
diff --git a/packages/cmf/src/App.js b/packages/cmf/src/App.js
index 0076f6c1140..967b568985f 100644
--- a/packages/cmf/src/App.js
+++ b/packages/cmf/src/App.js
@@ -23,7 +23,7 @@ export default function App(props) {
}
return (
-
+
{content}
@@ -33,6 +33,7 @@ export default function App(props) {
App.displayName = 'CMFApp';
App.propTypes = {
store: PropTypes.object.isRequired,
+ registry: PropTypes.object,
children: PropTypes.node,
withSettings: PropTypes.bool,
loading: PropTypes.func,
diff --git a/packages/cmf/src/Dispatcher.js b/packages/cmf/src/Dispatcher.js
index 7af118f81d3..e6c63ef8022 100644
--- a/packages/cmf/src/Dispatcher.js
+++ b/packages/cmf/src/Dispatcher.js
@@ -9,6 +9,7 @@ import React from 'react';
import cmfConnect from './cmfConnect';
import action from './action';
import actionCreator from './actionCreator';
+import { RegistryContext } from './RegistryProvider';
/**
* This component purpose is to decorate any component and map an user event
@@ -20,27 +21,9 @@ function myfunc(event, props, context) {
*/
-export class Dispatcher extends React.Component {
- static displayName = 'Dispatcher';
-
- static propTypes = {
- children: PropTypes.node.isRequired,
- stopPropagation: PropTypes.bool,
- preventDefault: PropTypes.bool,
- dispatchActionCreator: PropTypes.func,
- };
-
- static contextTypes = {
- registry: PropTypes.object.isRequired,
- };
-
- /**
- * @param {object} props only one child under children
- */
- constructor(props) {
- super(props);
- this.onEvent = this.onEvent.bind(this);
- }
+export function Dispatcher(props) {
+ const registry = React.useContext(RegistryContext);
+ // console.log('@@@ registry', registry);
/**
* on any even just try to find a onTHEEVENT props.
@@ -49,43 +32,45 @@ export class Dispatcher extends React.Component {
* @param {object} event the react event dispatched event
* @param {string} eventName the name of the event
*/
- onEvent(event, eventName) {
- if (this.props.stopPropagation) {
+ function onEvent(event, eventName) {
+ if (props.stopPropagation) {
event.stopPropagation();
}
- if (this.props.preventDefault) {
+ if (props.preventDefault) {
event.preventDefault();
}
- if (this.props[eventName]) {
- this.props.dispatchActionCreator(this.props[eventName], event, this.props);
+ if (props[eventName]) {
+ props.dispatchActionCreator(props[eventName], event, props);
}
}
- checkIfActionInfoExist() {
- action.getOnProps(this.props).forEach(name => {
- if (typeof this.props[name] === 'string') {
- actionCreator.get(this.context, this.props[name]);
+ function checkIfActionInfoExist() {
+ action.getOnProps(props).forEach(name => {
+ if (typeof props[name] === 'string') {
+ actionCreator.get({ registry }, props[name]);
}
});
}
- /**
- * @return {object} ReactElement
- */
- render() {
- this.checkIfActionInfoExist();
- const onProps = action.getOnProps(this.props);
- const childrenWithProps = React.Children.map(this.props.children, child => {
- const props = {};
- onProps.forEach(name => {
- props[name] = event => this.onEvent(event, name);
- });
- return React.cloneElement(child, props);
+ checkIfActionInfoExist();
+ const onProps = action.getOnProps(props);
+ const childrenWithProps = React.Children.map(props.children, child => {
+ const newProps = {};
+ onProps.forEach(name => {
+ newProps[name] = event => onEvent(event, name);
});
- return React.Children.only(childrenWithProps[0]);
- }
+ return React.cloneElement(child, newProps);
+ });
+ return React.Children.only(childrenWithProps[0]);
}
+Dispatcher.propTypes = {
+ children: PropTypes.node.isRequired,
+ stopPropagation: PropTypes.bool,
+ preventDefault: PropTypes.bool,
+ dispatchActionCreator: PropTypes.func,
+};
+Dispatcher.displayName = 'Dispatcher';
Dispatcher.defaultProps = {
stopPropagation: false,
preventDefault: false,
diff --git a/packages/cmf/src/Inject.component.js b/packages/cmf/src/Inject.component.js
index 685d7e1b36c..a5f8b85a3ad 100644
--- a/packages/cmf/src/Inject.component.js
+++ b/packages/cmf/src/Inject.component.js
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import componentAPI from './component';
+import { useCMFContext } from './useContext';
/**
* The Inject component let you use the registry to render named component
@@ -28,7 +29,8 @@ NotFoundComponent.propTypes = {
error: PropTypes.string.isRequired,
};
-function Inject({ component, ...props }, context) {
+function Inject({ component, ...props }) {
+ const context = useCMFContext();
try {
const Component = componentAPI.get(component, context);
return ;
@@ -36,9 +38,6 @@ function Inject({ component, ...props }, context) {
return ;
}
}
-Inject.contextTypes = {
- registry: PropTypes.object.isRequired,
-};
Inject.propTypes = {
component: PropTypes.string.isRequired,
};
diff --git a/packages/cmf/src/RegistryProvider.js b/packages/cmf/src/RegistryProvider.js
index 5d10e96a81e..f44ccc44271 100644
--- a/packages/cmf/src/RegistryProvider.js
+++ b/packages/cmf/src/RegistryProvider.js
@@ -4,39 +4,10 @@
* @module react-cmf/lib/RegistryProvider
* @see module:react-cmf/lib/App
*/
-import PropTypes from 'prop-types';
-import React, { Children } from 'react';
+import React from 'react';
import Registry from './registry';
-/**
- * The provider is a JSX wrapper to inject the registry as a context var
- * You should never need to use this, it's an internal component
- */
-export default class RegistryProvider extends React.Component {
- constructor(props) {
- super(props);
- this.registry = Registry.getRegistry();
- }
-
- /**
- * @return {object} child with registry as only key
- */
- getChildContext() {
- return { registry: this.registry };
- }
-
- /**
- * react rendering
- * @return {object} ReactElement
- */
- render() {
- return Children.only(this.props.children);
- }
-}
+export const RegistryContext = React.createContext(Registry.getRegistry());
+export const RegistryProvider = RegistryContext.Provider;
-RegistryProvider.propTypes = {
- children: PropTypes.element.isRequired,
-};
-RegistryProvider.childContextTypes = {
- registry: PropTypes.object,
-};
+export default RegistryContext.Provider;
diff --git a/packages/cmf/src/actionCreator.js b/packages/cmf/src/actionCreator.js
index f41c17f0c43..c63c5891b4f 100644
--- a/packages/cmf/src/actionCreator.js
+++ b/packages/cmf/src/actionCreator.js
@@ -10,6 +10,7 @@ import CONST from './constant';
function get(context, id) {
const creator = context.registry[`${CONST.REGISTRY_ACTION_CREATOR_PREFIX}:${id}`];
if (!creator) {
+ // console.log('@@ registry', id, Object.keys(context.registry));
throw new Error(`actionCreator not found in the registry: ${id}`);
}
return creator;
diff --git a/packages/cmf/src/bootstrap.js b/packages/cmf/src/bootstrap.js
index 4d08edef4e2..84bf408eac8 100644
--- a/packages/cmf/src/bootstrap.js
+++ b/packages/cmf/src/bootstrap.js
@@ -149,7 +149,12 @@ export default async function bootstrap(appOptions = {}) {
saga.run();
render(
-
+
,
element,
diff --git a/packages/cmf/src/cmfConnect.js b/packages/cmf/src/cmfConnect.js
index 47787b76017..3609c8a0b16 100644
--- a/packages/cmf/src/cmfConnect.js
+++ b/packages/cmf/src/cmfConnect.js
@@ -4,18 +4,11 @@
* @example
import { cmfConnect } from '@talend/react-cmf';
-class MyComponent extends React.Component {
- static displayName = 'MyComponent';
- constructor(props) {
- super(props);
- this.onClick = this.onClick.bind(this);
- }
- onClick(event) {
- return this.props.dispatchActionCreator('myaction', event, { props: this.props });
- }
- render() {
- return ;
- }
+function MyComponent(props) {
+ const onClick = (event) => {
+ props.dispatchActionCreator('myaction', event, { props: props });
+ };
+ return ;
}
function mapStateToProps(state) {
@@ -32,7 +25,7 @@ import PropTypes from 'prop-types';
import React, { createElement } from 'react';
import hoistStatics from 'hoist-non-react-statics';
import ImmutablePropTypes from 'react-immutable-proptypes';
-import { connect } from 'react-redux';
+import { connect, useStore } from 'react-redux';
import { v4 as uuidv4 } from 'uuid';
import actions from './actions';
import actionCreator from './actionCreator';
@@ -43,6 +36,7 @@ import onEvent from './onEvent';
import { initState, getStateAccessors, getStateProps } from './componentState';
import { mapStateToViewProps } from './settings';
import omit from './omit';
+import { RegistryContext } from './RegistryProvider';
export function getComponentName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
@@ -215,6 +209,7 @@ export default function cmfConnect({
}
}
let displayNameWarning = true;
+
return function wrapWithCMF(WrappedComponent) {
if (!WrappedComponent.displayName && displayNameWarning) {
displayNameWarning = false;
@@ -238,125 +233,123 @@ export default function cmfConnect({
},
};
}
- class CMFContainer extends React.Component {
- static displayName = `CMF(${getComponentName(WrappedComponent)})`;
-
- static propTypes = {
- ...cmfConnect.propTypes,
- };
-
- static contextTypes = {
- store: PropTypes.object,
- registry: PropTypes.object,
- router: PropTypes.object,
- };
- static WrappedComponent = WrappedComponent;
+ function CMFContainer(props, ref) {
+ const [instanceId] = React.useState(uuidv4());
+ const registry = React.useContext(RegistryContext);
+ const store = useStore();
- static getState = getState;
-
- static setStateAction = function setStateAction(state, id = 'default', type) {
- if (typeof state !== 'function') {
- return getSetStateAction(state, id, type);
- }
- return (_, getReduxState) =>
- getSetStateAction(state(getState(getReduxState(), id)), id, type);
- };
-
- constructor(props, context) {
- super(props, context);
- this.dispatchActionCreator = this.dispatchActionCreator.bind(this);
- this.getOnEventProps = this.getOnEventProps.bind(this);
- this.id = uuidv4();
+ function dispatchActionCreator(actionCreatorId, event, data, extraContext) {
+ const extendedContext = { registry, store, ...extraContext };
+ props.dispatchActionCreator(actionCreatorId, event, data, extendedContext);
}
- componentDidMount() {
- initState(this.props);
- if (this.props.saga) {
- this.dispatchActionCreator(
+ React.useEffect(() => {
+ initState(props);
+ if (props.saga) {
+ dispatchActionCreator(
'cmf.saga.start',
- { type: 'DID_MOUNT', componentId: this.id },
+ { type: 'DID_MOUNT', componentId: instanceId },
{
- ...this.props, // DEPRECATED
- componentId: getComponentId(componentId, this.props),
+ ...props, // DEPRECATED
+ componentId: getComponentId(componentId, props),
},
);
}
- if (this.props.didMountActionCreator) {
- this.dispatchActionCreator(this.props.didMountActionCreator, null, this.props);
+ if (props.didMountActionCreator) {
+ dispatchActionCreator(props.didMountActionCreator, null, props);
}
+ return () => {
+ if (props.willUnmountActionCreator) {
+ dispatchActionCreator(props.willUnmountActionCreator, null, props);
+ }
+ // if the props.keepComponentState is present we have to stick to it
+ if (
+ props.keepComponentState === false ||
+ (props.keepComponentState === undefined && !keepComponentState)
+ ) {
+ props.deleteState(props.initialState);
+ }
+ if (props.saga) {
+ dispatchActionCreator(
+ 'cmf.saga.stop',
+ { type: 'WILL_UNMOUNT', componentId: instanceId },
+ props,
+ );
+ }
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+ function getOnEventProps() {
+ return Object.keys(props).reduce(
+ (acc, key) => {
+ // TODO check how to replace the this
+ onEvent.addOnEventSupport(onEvent.DISPATCH, { props }, acc, key);
+ onEvent.addOnEventSupport(onEvent.ACTION_CREATOR, { props }, acc, key);
+ onEvent.addOnEventSupport(onEvent.SETSTATE, { props }, acc, key);
+ return acc;
+ },
+ { toOmit: [], dispatchActionCreator },
+ );
}
- componentWillUnmount() {
- if (this.props.willUnmountActionCreator) {
- this.dispatchActionCreator(this.props.willUnmountActionCreator, null, this.props);
- }
- // if the props.keepComponentState is present we have to stick to it
- if (
- this.props.keepComponentState === false ||
- (this.props.keepComponentState === undefined && !keepComponentState)
- ) {
- this.props.deleteState(this.props.initialState);
- }
- if (this.props.saga) {
- this.dispatchActionCreator(
- 'cmf.saga.stop',
- { type: 'WILL_UNMOUNT', componentId: this.id },
- this.props,
- );
- }
+ if (props.renderIf === false) {
+ return null;
}
+ const { toOmit, spreadCMFState, ...handlers } = getOnEventProps();
- getOnEventProps() {
- return Object.keys(this.props).reduce(
- (props, key) => {
- onEvent.addOnEventSupport(onEvent.DISPATCH, this, props, key);
- onEvent.addOnEventSupport(onEvent.ACTION_CREATOR, this, props, key);
- onEvent.addOnEventSupport(onEvent.SETSTATE, this, props, key);
- return props;
- },
- { toOmit: [] },
- );
+ // remove all internal props already used by the container
+ delete handlers.dispatchActionCreator;
+ toOmit.push(...CONST.CMF_PROPS, ...propsToOmit);
+ if (props.omitRouterProps) {
+ toOmit.push('omitRouterProps', ...CONST.INJECTED_ROUTER_PROPS);
+ }
+ let spreadedState = {};
+ if ((spreadCMFState || props.spreadCMFState) && props.state) {
+ spreadedState = props.state.toJS();
}
- dispatchActionCreator(actionCreatorId, event, data, context) {
- const extendedContext = { ...this.context, ...context };
- this.props.dispatchActionCreator(actionCreatorId, event, data, extendedContext);
+ const newProps = {
+ ...omit(props, toOmit),
+ ...handlers,
+ ...spreadedState,
+ };
+ if (newProps.dispatchActionCreator && toOmit.indexOf('dispatchActionCreator') === -1) {
+ // override to inject CMFContainer context
+ newProps.dispatchActionCreator = dispatchActionCreator;
+ }
+ if (!newProps.state && defaultState && toOmit.indexOf('state') === -1) {
+ newProps.state = defaultState;
}
+ if (rest.forwardRef) {
+ return ;
+ }
+ return ;
+ }
+ let CMFWithRef = hoistStatics(CMFContainer, WrappedComponent);
+ CMFContainer.displayName = `CMF(${getComponentName(WrappedComponent)})`;
- render() {
- if (this.props.renderIf === false) {
- return null;
- }
- const { toOmit, spreadCMFState, ...handlers } = this.getOnEventProps();
- // remove all internal props already used by the container
- toOmit.push(...CONST.CMF_PROPS, ...propsToOmit);
- if (this.props.omitRouterProps) {
- toOmit.push('omitRouterProps', ...CONST.INJECTED_ROUTER_PROPS);
- }
- let spreadedState = {};
- if ((spreadCMFState || this.props.spreadCMFState) && this.props.state) {
- spreadedState = this.props.state.toJS();
- }
- const props = {
- ...omit(this.props, toOmit),
- ...handlers,
- ...spreadedState,
- };
- if (
- props.dispatchActionCreator &&
- props.dispatchActionCreator &&
- toOmit.indexOf('dispatchActionCreator') === -1
- ) {
- // override to inject CMFContainer context
- props.dispatchActionCreator = this.dispatchActionCreator;
- }
- if (!props.state && defaultState && toOmit.indexOf('state') === -1) {
- props.state = defaultState;
- }
- return createElement(WrappedComponent, props);
+ CMFContainer.propTypes = {
+ ...cmfConnect.propTypes,
+ };
+ CMFContainer.WrappedComponent = WrappedComponent;
+ CMFContainer.getState = getState;
+
+ CMFContainer.setStateAction = function setStateAction(state, id = 'default', type) {
+ if (typeof state !== 'function') {
+ return getSetStateAction(state, id, type);
}
+ return (_, getReduxState) =>
+ getSetStateAction(state(getState(getReduxState(), id)), id, type);
+ };
+ if (rest.forwardRef) {
+ CMFWithRef = React.forwardRef(CMFWithRef);
+ CMFWithRef.displayName = `ForwardRef(${CMFContainer.displayName})`;
+ CMFWithRef.WrappedComponent = CMFContainer.WrappedComponent;
+ CMFWithRef.getState = CMFContainer.getState;
+ CMFWithRef.setStateAction = CMFContainer.setStateAction;
}
+
const Connected = connect(
(state, ownProps) =>
getStateToProps({
@@ -385,7 +378,7 @@ export default function cmfConnect({
ownProps,
}),
{ ...rest },
- )(hoistStatics(CMFContainer, WrappedComponent));
+ )(CMFWithRef);
Connected.CMFContainer = CMFContainer;
return Connected;
};
diff --git a/packages/cmf/src/components/ErrorBoundary/ErrorBoundary.component.test.js b/packages/cmf/src/components/ErrorBoundary/ErrorBoundary.component.test.js
index 2b356f41204..77149a96400 100644
--- a/packages/cmf/src/components/ErrorBoundary/ErrorBoundary.component.test.js
+++ b/packages/cmf/src/components/ErrorBoundary/ErrorBoundary.component.test.js
@@ -4,7 +4,6 @@ import { mount } from 'enzyme';
import ErrorBoundary from './ErrorBoundary.component';
// missing in jsdom: https://github.com/jsdom/jsdom/issues/1721
-global.window.URL.createObjectURL = jest.fn();
function TestChildren(props) {
if (props.breaking) {
@@ -17,6 +16,13 @@ TestChildren.propTypes = {
};
describe('Component ErrorBoundary', () => {
+ beforeEach(() => {
+ global.window.URL.createObjectURL = jest.fn();
+ global.console = {
+ log: jest.fn(),
+ error: jest.fn(),
+ };
+ });
it('should render children', () => {
const wrapper = mount(
@@ -34,5 +40,6 @@ describe('Component ErrorBoundary', () => {
);
expect(wrapper.text()).not.toEqual('hello world');
expect(wrapper.find('ErrorPanel').length).toBe(1);
+ expect(global.console.error).toHaveBeenCalled();
});
});
diff --git a/packages/cmf/src/index.js b/packages/cmf/src/index.js
index 7dfffcfc0ce..b7021ea3939 100644
--- a/packages/cmf/src/index.js
+++ b/packages/cmf/src/index.js
@@ -23,6 +23,7 @@ import localStorage from './localStorage';
import onError from './onError';
import reduxStorage from './reduxstorage';
import * as mock from './mock';
+import { useCMFContext } from './useContext';
// DEPRECATED APIs
import action from './action';
@@ -66,6 +67,7 @@ export {
Saga,
CmfRegisteredSaga,
store,
+ useCMFContext,
};
/**
diff --git a/packages/cmf/src/mock/index.js b/packages/cmf/src/mock/index.js
index 0749a3007f0..b9cc6507b9e 100644
--- a/packages/cmf/src/mock/index.js
+++ b/packages/cmf/src/mock/index.js
@@ -13,9 +13,4 @@
import store from './store';
import Provider from './provider';
-export default store;
-
-export {
- store,
- Provider,
-};
+export { Provider, store };
diff --git a/packages/cmf/src/mock/provider.js b/packages/cmf/src/mock/provider.js
index 268b37ca186..624f4c3038b 100644
--- a/packages/cmf/src/mock/provider.js
+++ b/packages/cmf/src/mock/provider.js
@@ -1,7 +1,30 @@
import PropTypes from 'prop-types';
import React from 'react';
+import { Provider } from 'react-redux';
+import { RegistryProvider } from '../RegistryProvider';
import mock from './store';
+class ErrorBoundary extends React.Component {
+ static propTypes = {
+ children: PropTypes.any,
+ onError: PropTypes.func,
+ };
+
+ componentDidCatch(error, errorInfo) {
+ if (this.props.onError) {
+ this.props.onError(error, errorInfo);
+ }
+ this.setState({ hasError: true });
+ }
+
+ render() {
+ if (this.state && this.state.hasError) {
+ return Error
;
+ }
+ return this.props.children;
+ }
+}
+
const store = mock.store();
/**
* This component help you to mock the provider.
@@ -25,26 +48,26 @@ describe('AppMenu', () => {
});
});
*/
-class MockProvider extends React.Component {
- getChildContext() {
- let st = this.props.store;
- if (!st) {
- st = store;
- }
- if (this.props.state) {
- st.state = this.props.state;
- st.getState = () => this.props.state;
- }
- const context = {
- store: st,
- registry: this.props.registry || {},
- };
- return context;
+function MockProvider(props) {
+ let st = props.store;
+ if (!st) {
+ st = store;
}
-
- render() {
- return {this.props.children}
;
+ if (props.state) {
+ st.state = props.state;
+ st.getState = () => props.state;
}
+ const context = {
+ store: st,
+ registry: props.registry || {},
+ };
+ return (
+
+ );
}
MockProvider.propTypes = {
@@ -54,9 +77,10 @@ MockProvider.propTypes = {
registry: PropTypes.object,
};
-MockProvider.childContextTypes = {
- store: PropTypes.object,
- registry: PropTypes.object,
-};
+MockProvider.getEnzymeOption = context => ({
+ wrappingComponent: MockProvider,
+ wrappingComponentProps: context,
+});
+MockProvider.ErrorBoundary = ErrorBoundary;
export default MockProvider;
diff --git a/packages/cmf/src/onEvent.js b/packages/cmf/src/onEvent.js
index dcfdcb260c1..9540e1c6c6b 100644
--- a/packages/cmf/src/onEvent.js
+++ b/packages/cmf/src/onEvent.js
@@ -9,14 +9,14 @@ function serializeEvent(event) {
return event;
}
-function getOnEventActionCreatorHandler(instance, config, currentHandler) {
+function getOnEventActionCreatorHandler(instance, props, config, currentHandler) {
let actionCreator = config;
if (typeof config === 'object') {
actionCreator = config.id;
}
return function onEventActionCreator(...args) {
- instance.dispatchActionCreator(actionCreator, serializeEvent(args[0]), {
- props: instance.props,
+ props.dispatchActionCreator(actionCreator, serializeEvent(args[0]), {
+ props,
...args[1],
...(config.data || {}),
});
@@ -26,7 +26,7 @@ function getOnEventActionCreatorHandler(instance, config, currentHandler) {
};
}
-function getOnEventDispatchHandler(instance, config, currentHandler) {
+function getOnEventDispatchHandler(instance, props, config, currentHandler) {
return function onEventDispatch(...args) {
const payload = {
event: serializeEvent(args[0]),
@@ -40,7 +40,7 @@ function getOnEventDispatchHandler(instance, config, currentHandler) {
};
}
-function getOnEventSetStateHandler(instance, config, currentHandler) {
+function getOnEventSetStateHandler(instance, props, config, currentHandler) {
return function onEventSetState(...args) {
if (typeof currentHandler === 'function') {
currentHandler(...args);
@@ -59,7 +59,9 @@ function getOnEventSetStateHandler(instance, config, currentHandler) {
}
} else if (value === 'toggle') {
// because toggle need to read the state we dispatch it with a function
- instance.props.setState(props => instance.props.setState({ [key]: !props.state.get(key) }));
+ instance.props.setState(_props =>
+ instance.props.setState({ [key]: !_props.state.get(key) }),
+ );
} else {
// eslint-disable-next-line no-param-reassign
acc[key] = value;
@@ -102,6 +104,7 @@ function addOnEventSupport(handlerType, instance, props, key) {
// eslint-disable-next-line no-param-reassign
props[handlerKey] = GET_HANDLER[handlerType](
instance,
+ props,
instance.props[key],
originalEventHandler,
);
diff --git a/packages/cmf/src/sagas/collection.js b/packages/cmf/src/sagas/collection.js
index 0abbe57de32..8b132944eea 100644
--- a/packages/cmf/src/sagas/collection.js
+++ b/packages/cmf/src/sagas/collection.js
@@ -1,5 +1,4 @@
-import { call, select } from 'redux-saga/effects';
-import { delay } from 'redux-saga';
+import { delay, select } from 'redux-saga/effects';
import selectors from '../selectors';
/**
@@ -14,6 +13,6 @@ export function* waitFor(id, interval = 10) {
if (collection !== undefined) {
break;
}
- yield call(delay, interval);
+ yield delay(interval);
}
}
diff --git a/packages/cmf/src/useContext.js b/packages/cmf/src/useContext.js
new file mode 100644
index 00000000000..e79841cef9f
--- /dev/null
+++ b/packages/cmf/src/useContext.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import { useStore } from 'react-redux';
+import { RegistryContext } from './RegistryProvider';
+
+export function useCMFContext() {
+ const store = useStore();
+ const registry = React.useContext(RegistryContext);
+ return {
+ store,
+ registry,
+ };
+}
diff --git a/packages/containers/.storybook/config.js b/packages/containers/.storybook/config.js
index 037aab8b81f..1a02e1f0f45 100644
--- a/packages/containers/.storybook/config.js
+++ b/packages/containers/.storybook/config.js
@@ -1,3 +1,4 @@
+import '@talend/bootstrap-theme/src/theme/theme.scss';
import React from 'react';
import { storiesOf, configure, addDecorator, addParameters } from '@storybook/react';
import { action } from '@storybook/addon-actions';
diff --git a/packages/containers/package.json b/packages/containers/package.json
index 9010f2c8e41..ec208a992b0 100644
--- a/packages/containers/package.json
+++ b/packages/containers/package.json
@@ -44,7 +44,7 @@
"memoize-one": "^5.2.1",
"react-bootstrap": "0.32.4",
"react-immutable-proptypes": "^2.2.0",
- "redux-saga": "^0.15.6",
+ "redux-saga": "^1.1.3",
"reselect": "^2.5.4",
"uuid": "^3.4.0"
},
diff --git a/packages/containers/src/AboutDialog/AboutDialog.sagas.test.js b/packages/containers/src/AboutDialog/AboutDialog.sagas.test.js
index 516d4c4b287..1a037c96af2 100644
--- a/packages/containers/src/AboutDialog/AboutDialog.sagas.test.js
+++ b/packages/containers/src/AboutDialog/AboutDialog.sagas.test.js
@@ -57,22 +57,22 @@ describe('AboutDialog sagas', () => {
// Toggle loading flag
let effect = gen.next().value;
let expected = Connected.setStateAction({ loading: true });
- expect(effect.PUT.action).toEqual(expected);
+ expect(effect.payload.action).toEqual(expected);
// HTTP call
effect = gen.next().value;
- expect(effect.CALL.fn).toEqual(cmf.sagas.http.get);
- expect(effect.CALL.args).toEqual([url]);
+ expect(effect.payload.fn).toEqual(cmf.sagas.http.get);
+ expect(effect.payload.args).toEqual([url]);
// Update CMF collections
effect = gen.next(httpResponse).value;
expected = cmf.actions.collections.addOrReplace(Constants.COLLECTION_ID, converted);
- expect(effect.PUT.action).toEqual(expected);
+ expect(effect.payload.action).toEqual(expected);
// Toggle fetching flag
effect = gen.next().value;
expected = Connected.setStateAction({ loading: false });
- expect(effect.PUT.action).toEqual(expected);
+ expect(effect.payload.action).toEqual(expected);
const { done } = gen.next();
@@ -85,7 +85,7 @@ describe('AboutDialog sagas', () => {
const effect = gen.next().value;
const expected = Connected.setStateAction({ show: false });
- expect(effect.PUT.action).toEqual(expected);
+ expect(effect.payload.action).toEqual(expected);
expect(gen.next().done).toBe(true);
});
@@ -96,9 +96,9 @@ describe('AboutDialog sagas', () => {
const effect = gen.next().value;
const expected = Connected.setStateAction({ show: true });
- expect(effect.ALL[0].PUT.action).toEqual(expected);
+ expect(effect.payload[0].payload.action).toEqual(expected);
- expect(effect.ALL[1].CALL.args).toEqual([{ payload: { url: 'hey' } }]);
+ expect(effect.payload[1].payload.args).toEqual([{ payload: { url: 'hey' } }]);
expect(gen.next().done).toBe(true);
});
diff --git a/packages/containers/src/Action/Action.test.js b/packages/containers/src/Action/Action.test.js
index 9099ead3372..bc1a1b4e0df 100644
--- a/packages/containers/src/Action/Action.test.js
+++ b/packages/containers/src/Action/Action.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
import { mock } from '@talend/react-cmf';
import Action, { mapStateToProps, mergeProps } from './Action.connect';
@@ -7,7 +7,10 @@ import Action, { mapStateToProps, mergeProps } from './Action.connect';
describe('Action', () => {
it('should render from name props keeping extra props', () => {
const context = mock.store.context();
- const wrapper = shallow(, { context });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
expect(wrapper.getElement()).toMatchSnapshot();
});
});
diff --git a/packages/containers/src/Action/__snapshots__/Action.test.js.snap b/packages/containers/src/Action/__snapshots__/Action.test.js.snap
index 3e032b0eb62..c9a671592dc 100644
--- a/packages/containers/src/Action/__snapshots__/Action.test.js.snap
+++ b/packages/containers/src/Action/__snapshots__/Action.test.js.snap
@@ -1,26 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Action should render from name props keeping extra props 1`] = `
-
`;
diff --git a/packages/containers/src/ActionBar/ActionBar.test.js b/packages/containers/src/ActionBar/ActionBar.test.js
index f818ea504f4..8b01120c50e 100644
--- a/packages/containers/src/ActionBar/ActionBar.test.js
+++ b/packages/containers/src/ActionBar/ActionBar.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
import { mock } from '@talend/react-cmf';
import Container from './ActionBar.connect';
@@ -74,15 +74,20 @@ const actionIds = {
},
],
};
-
+const context = mock.store.context();
describe('Container ActionBar', () => {
it('should pass the props', () => {
+ context.registry = {};
const props = { actions };
- const wrapper = shallow(, { context: mock.store.context() });
- expect(wrapper.props()).toMatchSnapshot();
+ const wrapper = mount(, mock.Provider.getEnzymeOption(context));
+ expect(wrapper.find(Container.CMFContainer).props()).toMatchSnapshot();
});
it('should compute props using CMF with array of string', () => {
- const wrapper = shallow(, { context: mock.store.context() });
- expect(wrapper.props()).toMatchSnapshot();
+ context.registry = {};
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
+ expect(wrapper.find(Container.CMFContainer).props()).toMatchSnapshot();
});
});
diff --git a/packages/containers/src/ActionBar/__snapshots__/ActionBar.test.js.snap b/packages/containers/src/ActionBar/__snapshots__/ActionBar.test.js.snap
index 66fbe98b5c6..4814dc9f8fa 100644
--- a/packages/containers/src/ActionBar/__snapshots__/ActionBar.test.js.snap
+++ b/packages/containers/src/ActionBar/__snapshots__/ActionBar.test.js.snap
@@ -34,10 +34,50 @@ Object {
"getComponent": [Function],
"initState": [Function],
"renderers": Object {
- "Action": [Function],
- "ActionDropdown": [Function],
- "ActionSplitDropdown": [Function],
- "Actions": [Function],
+ "Action": Object {
+ "$$typeof": Symbol(react.memo),
+ "CMFContainer": [Function],
+ "DISPLAY_MODE_DROPDOWN": "dropdown",
+ "DISPLAY_MODE_FILE": "file",
+ "DISPLAY_MODE_ICON_TOGGLE": "iconToggle",
+ "DISPLAY_MODE_SPLIT_DROPDOWN": "splitDropdown",
+ "WrappedComponent": [Function],
+ "compare": null,
+ "displayName": "Connect(CMF(Action))",
+ "getState": [Function],
+ "setStateAction": [Function],
+ "type": [Function],
+ },
+ "ActionDropdown": Object {
+ "$$typeof": Symbol(react.memo),
+ "CMFContainer": [Function],
+ "WrappedComponent": [Function],
+ "compare": null,
+ "displayName": "Connect(CMF(Container(ActionDropdown)))",
+ "getState": [Function],
+ "setStateAction": [Function],
+ "type": [Function],
+ },
+ "ActionSplitDropdown": Object {
+ "$$typeof": Symbol(react.memo),
+ "CMFContainer": [Function],
+ "WrappedComponent": [Function],
+ "compare": null,
+ "displayName": "Connect(CMF(Container(ActionSplitDropdown)))",
+ "getState": [Function],
+ "setStateAction": [Function],
+ "type": [Function],
+ },
+ "Actions": Object {
+ "$$typeof": Symbol(react.memo),
+ "CMFContainer": [Function],
+ "WrappedComponent": [Function],
+ "compare": null,
+ "displayName": "Connect(CMF(Actions))",
+ "getState": [Function],
+ "setStateAction": [Function],
+ "type": [Function],
+ },
},
"setState": [Function],
"state": undefined,
@@ -103,10 +143,50 @@ Object {
"getComponent": [Function],
"initState": [Function],
"renderers": Object {
- "Action": [Function],
- "ActionDropdown": [Function],
- "ActionSplitDropdown": [Function],
- "Actions": [Function],
+ "Action": Object {
+ "$$typeof": Symbol(react.memo),
+ "CMFContainer": [Function],
+ "DISPLAY_MODE_DROPDOWN": "dropdown",
+ "DISPLAY_MODE_FILE": "file",
+ "DISPLAY_MODE_ICON_TOGGLE": "iconToggle",
+ "DISPLAY_MODE_SPLIT_DROPDOWN": "splitDropdown",
+ "WrappedComponent": [Function],
+ "compare": null,
+ "displayName": "Connect(CMF(Action))",
+ "getState": [Function],
+ "setStateAction": [Function],
+ "type": [Function],
+ },
+ "ActionDropdown": Object {
+ "$$typeof": Symbol(react.memo),
+ "CMFContainer": [Function],
+ "WrappedComponent": [Function],
+ "compare": null,
+ "displayName": "Connect(CMF(Container(ActionDropdown)))",
+ "getState": [Function],
+ "setStateAction": [Function],
+ "type": [Function],
+ },
+ "ActionSplitDropdown": Object {
+ "$$typeof": Symbol(react.memo),
+ "CMFContainer": [Function],
+ "WrappedComponent": [Function],
+ "compare": null,
+ "displayName": "Connect(CMF(Container(ActionSplitDropdown)))",
+ "getState": [Function],
+ "setStateAction": [Function],
+ "type": [Function],
+ },
+ "Actions": Object {
+ "$$typeof": Symbol(react.memo),
+ "CMFContainer": [Function],
+ "WrappedComponent": [Function],
+ "compare": null,
+ "displayName": "Connect(CMF(Actions))",
+ "getState": [Function],
+ "setStateAction": [Function],
+ "type": [Function],
+ },
},
"setState": [Function],
"state": undefined,
diff --git a/packages/containers/src/Actions/Actions.test.js b/packages/containers/src/Actions/Actions.test.js
index 4bba17e0d1c..5580d5c5e9b 100644
--- a/packages/containers/src/Actions/Actions.test.js
+++ b/packages/containers/src/Actions/Actions.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
import { mock } from '@talend/react-cmf';
import Actions from './Actions.connect';
@@ -7,7 +7,12 @@ import Actions from './Actions.connect';
describe('Actions', () => {
it('should render', () => {
const context = mock.store.context();
- const wrapper = shallow(, { context });
- expect(wrapper.getElement()).toMatchSnapshot();
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(context),
+ );
+ expect(wrapper.find(Actions.CMFContainer).props().actions[0]).toEqual({
+ actionId: 'menu:demo',
+ });
});
});
diff --git a/packages/containers/src/Actions/__snapshots__/Actions.test.js.snap b/packages/containers/src/Actions/__snapshots__/Actions.test.js.snap
deleted file mode 100644
index 53e20db84a9..00000000000
--- a/packages/containers/src/Actions/__snapshots__/Actions.test.js.snap
+++ /dev/null
@@ -1,25 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Actions should render 1`] = `
-
-`;
diff --git a/packages/containers/src/AppLoader/AppLoader.saga.js b/packages/containers/src/AppLoader/AppLoader.saga.js
index 449cb0d4482..3063f98bf64 100644
--- a/packages/containers/src/AppLoader/AppLoader.saga.js
+++ b/packages/containers/src/AppLoader/AppLoader.saga.js
@@ -1,6 +1,5 @@
import api from '@talend/react-cmf';
-import { call, select, all, take } from 'redux-saga/effects';
-import { delay } from 'redux-saga';
+import { delay, call, select, all, take } from 'redux-saga/effects';
import invariant from 'invariant';
export const ACTION_CREATORS = 'actionCreators';
@@ -20,7 +19,7 @@ export function* waitFor(collectionName, interval = 10) {
if (collection !== undefined) {
break;
}
- yield call(delay, interval);
+ yield delay(interval);
}
}
/**
@@ -52,6 +51,7 @@ export function* handleStep(step) {
* @param {array} props.steps an array of steps to handle
*/
export function* appLoaderSaga({ steps }) {
+ // eslint-disable-next-line no-restricted-syntax
for (const step of steps) {
yield call(handleStep, step);
}
diff --git a/packages/containers/src/ComponentForm/ComponentForm.saga.test.js b/packages/containers/src/ComponentForm/ComponentForm.saga.test.js
index 4a64e96ef96..7af55f47d89 100644
--- a/packages/containers/src/ComponentForm/ComponentForm.saga.test.js
+++ b/packages/containers/src/ComponentForm/ComponentForm.saga.test.js
@@ -67,8 +67,9 @@ describe('ComponentForm saga', () => {
// when
const selectJsonSchema = gen.next().value;
- expect(selectJsonSchema.SELECT).toBeDefined();
- const selector = selectJsonSchema.SELECT.selector;
+ expect(selectJsonSchema.payload).toBeDefined();
+ expect(selectJsonSchema.type).toBe('SELECT');
+ const selector = selectJsonSchema.payload.selector;
const jsonSchemaSelection = selector({
cmf: {
components: fromJS({
@@ -161,7 +162,7 @@ describe('ComponentForm saga', () => {
gen.next(); // select
// then
- expect(gen.next().value.PUT.action.cmf.componentState.componentState).toEqual({
+ expect(gen.next().value.payload.action.cmf.componentState.componentState).toEqual({
initialState: jsonSchema,
...jsonSchema,
});
@@ -228,8 +229,9 @@ describe('ComponentForm saga', () => {
// when
gen.next(); // fetch step
const errorStep = gen.next({ response }).value;
- expect(errorStep.PUT).toBeDefined();
- const setStateAction = errorStep.PUT.action(null, getReduxStore);
+ expect(errorStep.payload).toBeDefined();
+ expect(errorStep.type).toBe('PUT');
+ const setStateAction = errorStep.payload.action(null, getReduxStore);
// then
expect(setStateAction).toEqual({
@@ -265,7 +267,7 @@ describe('ComponentForm saga', () => {
const nextStep = gen.next({ response, data }).value;
// then
- expect(nextStep.PUT.action).toEqual({
+ expect(nextStep.payload.action).toEqual({
cmf: {
componentState: {
componentName: 'ComponentForm',
@@ -300,7 +302,7 @@ describe('ComponentForm saga', () => {
const nextStep = gen.next({ response, data }).value;
// then
- expect(nextStep.PUT.action).toEqual({
+ expect(nextStep.payload.action).toEqual({
cmf: {
componentState: {
componentName: 'ComponentForm',
@@ -329,7 +331,7 @@ describe('ComponentForm saga', () => {
const nextStep = gen.next({ response, data }).value;
// then
- expect(nextStep.PUT.action).toEqual({
+ expect(nextStep.payload.action).toEqual({
cmf: {
componentState: {
componentName: 'ComponentForm',
diff --git a/packages/containers/src/ConfirmDialog/ConfirmDialog.container.js b/packages/containers/src/ConfirmDialog/ConfirmDialog.container.js
index 74aa5a22cec..1b69ff8cee4 100644
--- a/packages/containers/src/ConfirmDialog/ConfirmDialog.container.js
+++ b/packages/containers/src/ConfirmDialog/ConfirmDialog.container.js
@@ -1,9 +1,8 @@
-import PropTypes from 'prop-types';
import React from 'react';
import { Map } from 'immutable';
import omit from 'lodash/omit';
import Component from '@talend/react-components/lib/ConfirmDialog';
-import { cmfConnect } from '@talend/react-cmf';
+import { cmfConnect, useCMFContext } from '@talend/react-cmf';
import { getActionsProps } from '../actionAPI';
@@ -11,42 +10,35 @@ export const DEFAULT_STATE = new Map({
show: false,
});
-// This uses old react context, so no way to switch to stateless function instead of class
// eslint-disable-next-line react/prefer-stateless-function
-class ConfirmDialog extends React.Component {
- static displayName = 'CMFContainer(ConfirmDialog)';
-
- static propTypes = {
- ...cmfConnect.propTypes,
- };
-
- static contextTypes = {
- store: PropTypes.object,
- registry: PropTypes.object,
- };
-
- render() {
- const state = (this.props.state || DEFAULT_STATE).toJS();
- if (!state.validateAction || !state.cancelAction) {
- return null;
- }
- // this should be enough
- /* const props = Object.assign(
- {},
- state,
- omit(this.props, cmfConnect.INJECTED_PROPS),
- ); */
- // but as we don't have access to dispatch in the created context of mapStateToProps
- // we're having an issue on the setup of the onClick on button
- // for now we'll recompute them here where the context has dispatch
- // so the connect is only here to force the refresh for now
-
- state.validateAction = getActionsProps(this.context, state.validateAction, state.model);
- state.cancelAction = getActionsProps(this.context, state.cancelAction, state.model);
- const props = { ...omit(this.props, cmfConnect.INJECTED_PROPS), ...state };
-
- return ;
+function ConfirmDialog(props) {
+ const context = useCMFContext();
+ const state = (props.state || DEFAULT_STATE).toJS();
+ if (!state.validateAction || !state.cancelAction) {
+ return null;
}
+ // this should be enough
+ /* const props = Object.assign(
+ {},
+ state,
+ omit(props, cmfConnect.INJECTED_PROPS),
+ ); */
+ // but as we don't have access to dispatch in the created context of mapStateToProps
+ // we're having an issue on the setup of the onClick on button
+ // for now we'll recompute them here where the context has dispatch
+ // so the connect is only here to force the refresh for now
+
+ state.validateAction = getActionsProps(context, state.validateAction, state.model);
+ state.cancelAction = getActionsProps(context, state.cancelAction, state.model);
+ const newProps = { ...omit(props, cmfConnect.INJECTED_PROPS), ...state };
+
+ return ;
}
+ConfirmDialog.displayName = 'CMFContainer(ConfirmDialog)';
+
+ConfirmDialog.propTypes = {
+ ...cmfConnect.propTypes,
+};
+
export default ConfirmDialog;
diff --git a/packages/containers/src/ConfirmDialog/ConfirmDialog.test.js b/packages/containers/src/ConfirmDialog/ConfirmDialog.test.js
index cd8cadf904e..bd5e7545666 100644
--- a/packages/containers/src/ConfirmDialog/ConfirmDialog.test.js
+++ b/packages/containers/src/ConfirmDialog/ConfirmDialog.test.js
@@ -1,7 +1,7 @@
import React from 'react';
-import renderer from 'react-test-renderer';
import { fromJS, Map } from 'immutable';
import { mock } from '@talend/react-cmf';
+import { mount } from 'enzyme';
import Container from './ConfirmDialog.container';
import Connected, { mapStateToProps } from './ConfirmDialog.connect';
@@ -20,8 +20,11 @@ describe('Container ConfirmDialog', () => {
show: true,
children: 'Confirm this !',
});
- const instance = new Container({ state });
- expect(instance.render()).toBe(null);
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
+ expect(wrapper.instance()).toBe(null);
});
it('should render', () => {
const state = new Map({
@@ -33,15 +36,11 @@ describe('Container ConfirmDialog', () => {
cancelAction: 'menu:demo',
model: { foo: 'bar' },
});
- const { Provider } = mock;
- const wrapper = renderer
- .create(
-
- ,
- ,
- )
- .toJSON();
- expect(wrapper).toMatchSnapshot();
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
+ expect(wrapper.html()).toMatchSnapshot();
});
});
diff --git a/packages/containers/src/ConfirmDialog/__snapshots__/ConfirmDialog.test.js.snap b/packages/containers/src/ConfirmDialog/__snapshots__/ConfirmDialog.test.js.snap
index 876918af109..41d0a462392 100644
--- a/packages/containers/src/ConfirmDialog/__snapshots__/ConfirmDialog.test.js.snap
+++ b/packages/containers/src/ConfirmDialog/__snapshots__/ConfirmDialog.test.js.snap
@@ -1,50 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Container ConfirmDialog should render 1`] = `
-
-
- Confirm this !
-
- ,
+ Confirm this !
`;
diff --git a/packages/containers/src/DeleteResource/sagas.test.js b/packages/containers/src/DeleteResource/sagas.test.js
index 689e44428da..0e988b34d9d 100644
--- a/packages/containers/src/DeleteResource/sagas.test.js
+++ b/packages/containers/src/DeleteResource/sagas.test.js
@@ -23,7 +23,7 @@ describe('internals', () => {
it('should put a redirect action', () => {
const gen = internals.redirect('/foo');
const effect = gen.next().value;
- expect(effect.PUT.action.cmf.routerReplace).toBe('/foo');
+ expect(effect.payload.action.cmf.routerReplace).toBe('/foo');
});
it('should throw if no redirectUrl provided', () => {
const gen = internals.redirect();
@@ -36,8 +36,9 @@ describe('internals', () => {
let effect = gen.next().value;
expect(effect).toEqual(take(CONSTANTS.DIALOG_BOX_DELETE_RESOURCE_OK));
effect = gen.next().value;
- expect(effect.SELECT.args[0]).not.toBeDefined(); // resourceLocator
- expect(effect.SELECT.args[1]).not.toBeDefined(); // safeId
+ expect(effect.type).toBe('SELECT');
+ expect(effect.payload.args[0]).not.toBeDefined(); // resourceLocator
+ expect(effect.payload.args[1]).not.toBeDefined(); // safeId
effect = gen.next().value;
expect(effect).not.toBeDefined(); // no http delete called
});
@@ -59,11 +60,11 @@ describe('internals', () => {
let effect = gen.next().value;
expect(effect).toEqual(take(CONSTANTS.DIALOG_BOX_DELETE_RESOURCE_OK));
effect = gen.next(action).value;
- expect(effect.SELECT.args[0]).toBe('datasets'); // resourceLocator
- expect(effect.SELECT.args[1]).toBe('123'); // safeId
+ expect(effect.payload.args[0]).toBe('datasets'); // resourceLocator
+ expect(effect.payload.args[1]).toBe('123'); // safeId
effect = gen.next(resource).value;
- expect(effect.CALL).toBeDefined();
- const httpAction = effect.CALL;
+ expect(effect.payload).toBeDefined();
+ const httpAction = effect.payload;
expect(httpAction.fn).toBe(cmf.sagas.http.delete);
expect(httpAction.args[0]).toBe('/api/datasets/123');
effect = gen.next({ response: { ok: true } }).value;
@@ -81,8 +82,8 @@ describe('internals', () => {
);
effect = gen.next().value;
- expect(effect.CALL.fn).toBe(internals.redirect);
- expect(effect.CALL.args[0]).toBe('/resources');
+ expect(effect.payload.fn).toBe(internals.redirect);
+ expect(effect.payload.args[0]).toBe('/resources');
});
it('should use resourceUri as backend api to delete resource if provided', () => {
const action = {
@@ -102,8 +103,8 @@ describe('internals', () => {
gen.next();
gen.next(action);
const effect = gen.next(resource).value;
- expect(effect.CALL).toBeDefined();
- const httpAction = effect.CALL;
+ expect(effect.payload).toBeDefined();
+ const httpAction = effect.payload;
expect(httpAction.fn).toBe(cmf.sagas.http.delete);
expect(httpAction.args[0]).toBe('/run-profiles/advanced/profileId');
});
@@ -131,8 +132,8 @@ describe('internals', () => {
gen.next();
gen.next(action);
const effect = gen.next(resource).value;
- expect(effect.CALL).toBeDefined();
- const httpAction = effect.CALL;
+ expect(effect.payload).toBeDefined();
+ const httpAction = effect.payload;
expect(httpAction.fn).toBe(cmf.sagas.http.delete);
expect(httpAction.args[0]).toBe('/services/run-profiles/runProfileId');
},
@@ -151,8 +152,8 @@ describe('internals', () => {
const gen = internals.deleteResourceValidate();
gen.next();
const effect = gen.next(action).value;
- expect(effect.SELECT.args[0]).toBe('myCollection');
- expect(effect.SELECT.args[1]).toBe('runProfileId');
+ expect(effect.payload.args[0]).toBe('myCollection');
+ expect(effect.payload.args[1]).toBe('runProfileId');
});
it('should use resourceType as collection to remove resource in state, if no collectionId provided', () => {
const action = {
@@ -167,8 +168,8 @@ describe('internals', () => {
const gen = internals.deleteResourceValidate();
gen.next();
const effect = gen.next(action).value;
- expect(effect.SELECT.args[0]).toBe('myResource');
- expect(effect.SELECT.args[1]).toBe('runProfileId');
+ expect(effect.payload.args[0]).toBe('myResource');
+ expect(effect.payload.args[1]).toBe('runProfileId');
});
it('should dispatch DIALOG_BOX_DELETE_RESOURCE_ERROR event when delete request fails', () => {
const action = {
@@ -208,11 +209,12 @@ describe('internals', () => {
it('should call redirect ', () => {
const gen = internals.deleteResourceCancel();
let effect = gen.next().value;
- expect(effect.TAKE.pattern).toBe(CONSTANTS.DIALOG_BOX_DELETE_RESOURCE_CANCEL);
+ expect(effect.type).toBe('TAKE');
+ expect(effect.payload.pattern).toBe(CONSTANTS.DIALOG_BOX_DELETE_RESOURCE_CANCEL);
const action = { data: { model: { onCancelRedirectUrl: '/cancel' } } };
effect = gen.next(action).value;
- expect(effect.CALL.fn).toBe(internals.redirect);
- expect(effect.CALL.args[0]).toBe('/cancel');
+ expect(effect.payload.fn).toBe(internals.redirect);
+ expect(effect.payload.args[0]).toBe('/cancel');
});
});
});
@@ -227,9 +229,13 @@ describe('sagas', () => {
// eslint-disable-next-line new-cap
const gen = sagas['DeleteResource#handle']();
const effect = gen.next().value;
- expect(effect.RACE).toMatchObject({
- deleteConfirmationCancel: { CALL: { fn: internals.deleteResourceCancel } },
- deleteConfirmationValidate: { CALL: { fn: internals.deleteResourceValidate } },
+ expect(effect.type).toBe('RACE');
+ expect(effect.payload).toMatchObject({
+ deleteConfirmationCancel: { type: 'CALL', payload: { fn: internals.deleteResourceCancel } },
+ deleteConfirmationValidate: {
+ type: 'CALL',
+ payload: { fn: internals.deleteResourceValidate },
+ },
});
});
it('should throw a specific error if sth goes bad', () => {
diff --git a/packages/containers/src/GuidedTour/GuidedTour.sagas.test.js b/packages/containers/src/GuidedTour/GuidedTour.sagas.test.js
index be2b147d1b8..68d4485faff 100644
--- a/packages/containers/src/GuidedTour/GuidedTour.sagas.test.js
+++ b/packages/containers/src/GuidedTour/GuidedTour.sagas.test.js
@@ -7,8 +7,7 @@ describe('Guided Tour sagas', () => {
const effect = gen.next().value;
const expected = Connected.setStateAction({ show: true });
- expect(effect.PUT.action).toEqual(expected);
-
+ expect(effect.payload.action).toEqual(expected);
expect(gen.next().done).toBe(true);
});
@@ -17,7 +16,7 @@ describe('Guided Tour sagas', () => {
const effect = gen.next().value;
const expected = Connected.setStateAction({ show: false });
- expect(effect.PUT.action).toEqual(expected);
+ expect(effect.payload.action).toEqual(expected);
expect(gen.next().done).toBe(true);
});
diff --git a/packages/containers/src/HeaderBar/HeaderBar.sagas.test.js b/packages/containers/src/HeaderBar/HeaderBar.sagas.test.js
index a4786264b2a..a19216c9f01 100644
--- a/packages/containers/src/HeaderBar/HeaderBar.sagas.test.js
+++ b/packages/containers/src/HeaderBar/HeaderBar.sagas.test.js
@@ -19,22 +19,22 @@ describe('HeaderBar sagas', () => {
// Toggle fetching flag (enable)
let effect = gen.next().value;
let expected = Connected.setStateAction({ productsFetchState: Constants.FETCHING_PRODUCTS });
- expect(effect.PUT.action).toEqual(expected);
+ expect(effect.payload.action).toEqual(expected);
// HTTP call
effect = gen.next().value;
- expect(effect.CALL.fn).toEqual(cmf.sagas.http.get);
- expect(effect.CALL.args).toEqual([url]);
+ expect(effect.payload.fn).toEqual(cmf.sagas.http.get);
+ expect(effect.payload.args).toEqual([url]);
// Toggle fetching flag (enable)
effect = gen.next(httpResponse).value;
expected = Connected.setStateAction({ productsFetchState: Constants.FETCH_PRODUCTS_SUCCESS });
- expect(effect.PUT.action).toEqual(expected);
+ expect(effect.payload.action).toEqual(expected);
// Update CMF collections
effect = gen.next().value;
expected = cmf.actions.collections.addOrReplace(Constants.COLLECTION_ID, data);
- expect(effect.PUT.action).toEqual(expected);
+ expect(effect.payload.action).toEqual(expected);
const { done } = gen.next();
@@ -48,18 +48,18 @@ describe('HeaderBar sagas', () => {
// Toggle fetching flag (enable)
let effect = gen.next().value;
- expect(effect.PUT.action).toEqual(
+ expect(effect.payload.action).toEqual(
Connected.setStateAction({ productsFetchState: Constants.FETCHING_PRODUCTS }),
);
// HTTP call
effect = gen.next().value;
- expect(effect.CALL.fn).toEqual(cmf.sagas.http.get);
- expect(effect.CALL.args).toEqual([url]);
+ expect(effect.payload.fn).toEqual(cmf.sagas.http.get);
+ expect(effect.payload.args).toEqual([url]);
// Toggle fetching flag (enable)
effect = gen.next(httpResponse).value;
- expect(effect.PUT.action).toEqual(
+ expect(effect.payload.action).toEqual(
Connected.setStateAction({ productsFetchState: Constants.FETCH_PRODUCTS_ERROR }),
);
diff --git a/packages/containers/src/HomeListView/__snapshots__/HomeListView.test.js.snap b/packages/containers/src/HomeListView/__snapshots__/HomeListView.test.js.snap
index 336bfb59cd7..8b9a655b4de 100644
--- a/packages/containers/src/HomeListView/__snapshots__/HomeListView.test.js.snap
+++ b/packages/containers/src/HomeListView/__snapshots__/HomeListView.test.js.snap
@@ -107,7 +107,7 @@ exports[`Component HomeListView should render with object props 1`] = `
}
mode="TwoColumns"
one={
-
}
>
- -1) {
- this.props.setState({
+ props.setState({
selectedItems: selectedItems.splice(dataIndex, 1),
});
} else {
- this.props.setState({
- selectedItems: selectedItems.push(data[this.props.idKey]),
+ props.setState({
+ selectedItems: selectedItems.push(data[props.idKey]),
});
}
}
- onToggleAllMultiSelection() {
- const selectedItems = this.getSelectedItems();
- const items = this.props.items;
+ function onToggleAllMultiSelection() {
+ const selectedItems = getSelectedItems();
+ const items = props.items;
if (selectedItems.size !== items.size) {
- this.props.setState({
- selectedItems: items.map(item => item.get(this.props.idKey)),
+ props.setState({
+ selectedItems: items.map(item => item.get(props.idKey)),
});
} else {
- this.props.setState({
+ props.setState({
selectedItems: new ImmutableList([]),
});
}
}
- getSelectedItems() {
- return this.props.state.get('selectedItems', new ImmutableList());
- }
-
- getGenericDispatcher(property) {
+ function getGenericDispatcher(property) {
return (event, data) => {
- this.props.dispatchActionCreator(property, event, data, this.context);
+ props.dispatchActionCreator(property, event, data, context);
};
}
- isSelected(item) {
- const selectedItems = this.getSelectedItems();
- return selectedItems.some(itemKey => itemKey === item[this.props.idKey]);
+ function isSelected(item) {
+ const selectedItems = getSelectedItems();
+ return selectedItems.some(itemKey => itemKey === item[props.idKey]);
}
- render() {
- const state = this.props.state.toJS();
- const items = getItems(this.context, this.props);
- const props = { ...omit(this.props, cmfConnect.INJECTED_PROPS) };
- if (!props.displayMode) {
- props.displayMode = state.displayMode;
- }
- if (!props.list) {
- props.list = {};
- }
- if (!props.list.id) {
- props.list.id = 'list';
+ const items = getItems(context, props);
+ const newProps = { ...omit(props, cmfConnect.INJECTED_PROPS) };
+ if (!newProps.displayMode) {
+ newProps.displayMode = state.displayMode;
+ }
+ if (!newProps.list) {
+ newProps.list = {};
+ }
+ if (!newProps.list.id) {
+ newProps.list.id = 'list';
+ }
+ newProps.list.items = items;
+ if (!newProps.list.columns) {
+ newProps.list.columns = [];
+ }
+ newProps.list.sort = {
+ field: state.sortOn,
+ isDescending: !state.sortAsc,
+ onChange: (event, data) => {
+ props.dispatch({
+ type: Constants.LIST_CHANGE_SORT_ORDER,
+ payload: data,
+ collectionId: props.collectionId,
+ event,
+ });
+ },
+ };
+ if (!newProps.list.itemProps) {
+ newProps.list.itemProps = {};
+ }
+
+ if (newProps.rowHeight) {
+ newProps.rowHeight = newProps.rowHeight[newProps.displayMode];
+ }
+ if (newProps.list.titleProps && newProps.actions.title) {
+ if (newProps.actions.title) {
+ newProps.list.titleProps.onClick = getGenericDispatcher(newProps.actions.title);
}
- props.list.items = items;
- if (!props.list.columns) {
- props.list.columns = [];
+ if (newProps.actions.editSubmit) {
+ newProps.list.titleProps.onEditSubmit = getGenericDispatcher(newProps.actions.editSubmit);
}
- props.list.sort = {
- field: state.sortOn,
- isDescending: !state.sortAsc,
- onChange: (event, data) => {
- this.props.dispatch({
- type: Constants.LIST_CHANGE_SORT_ORDER,
- payload: data,
- collectionId: props.collectionId,
- event,
- });
- },
- };
- if (!props.list.itemProps) {
- props.list.itemProps = {};
+ if (newProps.actions.editCancel) {
+ newProps.list.titleProps.onEditCancel = getGenericDispatcher(newProps.actions.editCancel);
}
+ }
- if (this.props.rowHeight) {
- props.rowHeight = this.props.rowHeight[props.displayMode];
- }
- if (props.list.titleProps && this.props.actions.title) {
- if (this.props.actions.title) {
- props.list.titleProps.onClick = this.getGenericDispatcher(this.props.actions.title);
- }
- if (this.props.actions.editSubmit) {
- props.list.titleProps.onEditSubmit = this.getGenericDispatcher(
- this.props.actions.editSubmit,
- );
- }
- if (this.props.actions.editCancel) {
- props.list.titleProps.onEditCancel = this.getGenericDispatcher(
- this.props.actions.editCancel,
- );
- }
- }
+ const cellDictionary = { ...connectedCellDictionary };
+ if (newProps.cellDictionary) {
+ Object.keys(newProps.cellDictionary).reduce((accumulator, key) => {
+ const current = newProps.cellDictionary[key];
+ // eslint-disable-next-line no-param-reassign
+ accumulator[key] = {
+ ...omit(current, ['component']),
+ cellRenderer: props.getComponent(current.component),
+ };
+ return accumulator;
+ }, cellDictionary);
+ }
+ newProps.list.cellDictionary = cellDictionary;
- const cellDictionary = { ...connectedCellDictionary };
- if (props.cellDictionary) {
- Object.keys(props.cellDictionary).reduce((accumulator, key) => {
- const current = props.cellDictionary[key];
+ if (newProps.headerDictionary) {
+ newProps.list.headerDictionary = Object.keys(newProps.headerDictionary).reduce(
+ (accumulator, key) => {
+ const current = newProps.headerDictionary[key];
// eslint-disable-next-line no-param-reassign
accumulator[key] = {
...omit(current, ['component']),
- cellRenderer: props.getComponent(current.component),
+ headerRenderer: props.getComponent(current.component),
};
return accumulator;
- }, cellDictionary);
- }
- props.list.cellDictionary = cellDictionary;
+ },
+ {},
+ );
+ }
- if (props.headerDictionary) {
- props.list.headerDictionary = Object.keys(props.headerDictionary).reduce(
- (accumulator, key) => {
- const current = props.headerDictionary[key];
- // eslint-disable-next-line no-param-reassign
- accumulator[key] = {
- ...omit(current, ['component']),
- headerRenderer: props.getComponent(current.component),
- };
- return accumulator;
+ // toolbar
+ if (newProps.toolbar) {
+ if (newProps.toolbar.display) {
+ newProps.toolbar.display = {
+ ...newProps.toolbar.display,
+ onChange: (event, data) => {
+ onSelectDisplayMode(event, data);
},
- {},
- );
+ };
+ }
+ if (newProps.toolbar.sort) {
+ newProps.toolbar.sort.isDescending = !state.sortAsc;
+ newProps.toolbar.sort.field = state.sortOn;
+ newProps.toolbar.sort.onChange = (event, data) => {
+ props.dispatch({
+ type: Constants.LIST_CHANGE_SORT_ORDER,
+ payload: data,
+ collectionId: props.collectionId,
+ event,
+ });
+ };
}
- // toolbar
- if (props.toolbar) {
- if (props.toolbar.display) {
- props.toolbar.display = {
- ...props.toolbar.display,
- onChange: (event, data) => {
- this.onSelectDisplayMode(event, data);
- },
- };
- }
- if (props.toolbar.sort) {
- props.toolbar.sort.isDescending = !state.sortAsc;
- props.toolbar.sort.field = state.sortOn;
- props.toolbar.sort.onChange = (event, data) => {
- this.props.dispatch({
- type: Constants.LIST_CHANGE_SORT_ORDER,
- payload: data,
- collectionId: props.collectionId,
- event,
- });
- };
- }
-
- if (props.toolbar.filter) {
- props.toolbar.filter.onToggle = (event, data) => {
- this.props.dispatch({
- type: Constants.LIST_TOGGLE_FILTER,
- payload: { ...data, filterDocked: state.filterDocked, searchQuery: state.searchQuery },
- collectionId: props.collectionId,
- event,
- });
- };
- props.toolbar.filter.onFilter = (event, data) => {
- this.props.dispatch({
- type: Constants.LIST_FILTER_CHANGE,
- payload: data,
- collectionId: props.collectionId,
- event,
- });
- };
- props.toolbar.filter.docked = state.filterDocked;
- props.toolbar.filter.value = state.searchQuery;
- }
+ if (newProps.toolbar.filter) {
+ newProps.toolbar.filter.onToggle = (event, data) => {
+ props.dispatch({
+ type: Constants.LIST_TOGGLE_FILTER,
+ payload: { ...data, filterDocked: state.filterDocked, searchQuery: state.searchQuery },
+ collectionId: props.collectionId,
+ event,
+ });
+ };
+ newProps.toolbar.filter.onFilter = (event, data) => {
+ props.dispatch({
+ type: Constants.LIST_FILTER_CHANGE,
+ payload: data,
+ collectionId: props.collectionId,
+ event,
+ });
+ };
+ newProps.toolbar.filter.docked = state.filterDocked;
+ newProps.toolbar.filter.value = state.searchQuery;
+ }
- props.toolbar.actionBar = { actions: {}, multiSelectActions: {} };
+ newProps.toolbar.actionBar = { actions: {}, multiSelectActions: {} };
- // settings up multi selection
- if (props.multiSelectActions && props.idKey) {
- props.list.itemProps.onToggle = this.onToggleMultiSelection;
- props.list.itemProps.onToggleAll = this.onToggleAllMultiSelection;
- props.list.itemProps.isSelected = this.isSelected;
- props.toolbar.actionBar.selected = this.getSelectedItems().size;
- }
+ // settings up multi selection
+ if (newProps.multiSelectActions && props.idKey) {
+ newProps.list.itemProps.onToggle = onToggleMultiSelection;
+ newProps.list.itemProps.onToggleAll = onToggleAllMultiSelection;
+ newProps.list.itemProps.isSelected = isSelected;
+ newProps.toolbar.actionBar.selected = getSelectedItems().size;
+ }
- const actions = this.props.actions;
- const multiSelectActions = this.props.multiSelectActions;
- if (multiSelectActions) {
- if (multiSelectActions.left) {
- props.toolbar.actionBar.multiSelectActions.left = multiSelectActions.left.map(action => ({
+ const actions = newProps.actions;
+ const multiSelectActions = newProps.multiSelectActions;
+ if (multiSelectActions) {
+ if (multiSelectActions.left) {
+ newProps.toolbar.actionBar.multiSelectActions.left = multiSelectActions.left.map(
+ action => ({
actionId: action,
- }));
- }
- if (multiSelectActions.right) {
- props.toolbar.actionBar.multiSelectActions.right = multiSelectActions.right.map(
- action => ({
- actionId: action,
- }),
- );
- }
+ }),
+ );
}
- if (actions) {
- if (actions.left) {
- props.toolbar.actionBar.actions.left = actions.left.map(action => ({ actionId: action }));
- }
- if (actions.right) {
- props.toolbar.actionBar.actions.right = actions.right.map(action => ({
+ if (multiSelectActions.right) {
+ newProps.toolbar.actionBar.multiSelectActions.right = multiSelectActions.right.map(
+ action => ({
actionId: action,
- }));
- }
+ }),
+ );
+ }
+ }
+ if (actions) {
+ if (actions.left) {
+ newProps.toolbar.actionBar.actions.left = actions.left.map(action => ({
+ actionId: action,
+ }));
}
+ if (actions.right) {
+ newProps.toolbar.actionBar.actions.right = actions.right.map(action => ({
+ actionId: action,
+ }));
+ }
+ }
- if (props.toolbar.pagination) {
- const pagination = props.toolbar.pagination;
- Object.assign(props.toolbar.pagination, {
- ...pick(state, ['totalResults', 'itemsPerPage', 'startIndex']),
- });
- if (!pagination.onChange) {
- pagination.onChange = (startIndex, itemsPerPage) => {
- this.onChangePage(startIndex, itemsPerPage);
- };
- } else if (typeof pagination.onChange === 'string') {
- const onChangeActionCreator = pagination.onChange;
- pagination.onChange = (startIndex, itemsPerPage) => {
- this.props.dispatchActionCreator(
- onChangeActionCreator,
- null,
- { startIndex, itemsPerPage },
- this.context,
- );
- };
- }
+ if (newProps.toolbar.pagination) {
+ const pagination = newProps.toolbar.pagination;
+ Object.assign(newProps.toolbar.pagination, {
+ ...pick(state, ['totalResults', 'itemsPerPage', 'startIndex']),
+ });
+ if (!pagination.onChange) {
+ pagination.onChange = (startIndex, itemsPerPage) => {
+ onChangePage(startIndex, itemsPerPage);
+ };
+ } else if (typeof pagination.onChange === 'string') {
+ const onChangeActionCreator = pagination.onChange;
+ pagination.onChange = (startIndex, itemsPerPage) => {
+ props.dispatchActionCreator(
+ onChangeActionCreator,
+ null,
+ { startIndex, itemsPerPage },
+ context,
+ );
+ };
}
}
- return ;
}
+ return ;
}
+List.displayName = 'Container(List)';
+List.propTypes = {
+ actions: PropTypes.shape({
+ title: PropTypes.string,
+ left: PropTypes.arrayOf(PropTypes.string),
+ right: PropTypes.arrayOf(PropTypes.string),
+ }),
+ multiSelectActions: PropTypes.shape({
+ title: PropTypes.string,
+ left: PropTypes.arrayOf(PropTypes.string),
+ right: PropTypes.arrayOf(PropTypes.string),
+ }),
+ idKey: PropTypes.string,
+ list: PropTypes.shape({
+ columns: PropTypes.array,
+ titleProps: PropTypes.object,
+ }),
+ toolbar: PropTypes.shape({
+ sort: PropTypes.object,
+ filter: PropTypes.object,
+ pagination: PropTypes.shape({
+ onChange: PropTypes.func,
+ }),
+ }),
+ cellDictionary: PropTypes.object,
+ displayMode: PropTypes.string,
+ items: ImmutablePropTypes.list.isRequired,
+ state: cmfConnect.propTypes.state,
+ ...cmfConnect.propTypes,
+};
+
+List.defaultProps = {
+ state: DEFAULT_STATE,
+};
+
export default List;
diff --git a/packages/containers/src/List/List.sagas.test.js b/packages/containers/src/List/List.sagas.test.js
index 21437d150be..4aa9a5fb8cf 100644
--- a/packages/containers/src/List/List.sagas.test.js
+++ b/packages/containers/src/List/List.sagas.test.js
@@ -1,8 +1,8 @@
import { put } from 'redux-saga/effects';
import { fromJS } from 'immutable';
+import { mock } from '@talend/react-cmf';
import { onToggleFilter, onFilterChange, onChangeSortChange } from './List.sagas';
import Connected from './List.connect';
-import mock, { store } from '../../../cmf/lib/mock';
const localConfig = {
collectionId: 'default',
@@ -19,19 +19,25 @@ const localConfig = {
},
]),
list: {
- columns: [{ key: 'id', name: 'ID' }, { key: 'value', name: 'Value' }],
+ columns: [
+ { key: 'id', name: 'ID' },
+ { key: 'value', name: 'Value' },
+ ],
},
};
-const state = store.state();
+const state = mock.store.state();
state.cmf.collections = fromJS({
default: {
- columns: [{ key: 'id', name: 'ID' }, { key: 'value', name: 'Value' }],
+ columns: [
+ { key: 'id', name: 'ID' },
+ { key: 'value', name: 'Value' },
+ ],
items: localConfig.items,
},
});
-const context = mock.context();
+const context = mock.store.context();
const event = { type: 'click' };
const data = {
diff --git a/packages/containers/src/List/List.test.js b/packages/containers/src/List/List.test.js
index 71aa4922ed0..57a70f0cf1c 100644
--- a/packages/containers/src/List/List.test.js
+++ b/packages/containers/src/List/List.test.js
@@ -1,8 +1,8 @@
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
import React from 'react';
import { Map, fromJS, List as ImmutableList } from 'immutable';
import cloneDeep from 'lodash/cloneDeep';
-
+import { mock } from '@talend/react-cmf';
import Container, { DEFAULT_STATE } from './List.container';
import Connected, { mapStateToProps } from './List.connect';
@@ -24,7 +24,10 @@ const toolbar = {
placeholder: 'find an object',
},
sort: {
- options: [{ id: 'id', name: 'Id' }, { id: 'name', name: 'Name' }],
+ options: [
+ { id: 'id', name: 'Id' },
+ { id: 'name', name: 'Name' },
+ ],
field: 'id',
isDescending: false,
},
@@ -85,8 +88,11 @@ const items = fromJS([
describe('Container List', () => {
it('should put default props', () => {
- const wrapper = shallow();
- const props = wrapper.props();
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context),
+ );
+ const props = wrapper.find('List').props();
expect(props.displayMode).toBe('table');
expect(props.list.items.length).toBe(3);
expect(props.list.items[0].id).toBe(1);
@@ -107,14 +113,15 @@ describe('Container List', () => {
it('should define the cellDictionary props', () => {
const getComponent = jest.fn(() => 'my custom component');
- const wrapper = shallow(
+ const wrapper = mount(
,
+ mock.Provider.getEnzymeOption(mock.store.context),
);
- const props = wrapper.props();
+ const props = wrapper.find('List').props();
expect(props.list.cellDictionary).toEqual({
custom: { cellRenderer: 'my custom component' },
@@ -129,14 +136,15 @@ describe('Container List', () => {
it('should define the headerDictionary props', () => {
const getComponent = jest.fn(() => 'my custom component');
- const wrapper = shallow(
+ const wrapper = mount(
,
+ mock.Provider.getEnzymeOption(mock.store.context),
);
- const props = wrapper.props();
+ const props = wrapper.find('List').props();
expect(props.list.headerDictionary).toEqual({
custom: { headerRenderer: 'my custom component' },
@@ -150,7 +158,10 @@ describe('Container List', () => {
multiSelectionSetting.multiSelectActions = {
left: ['object:remove'],
};
- const wrapper = shallow();
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
const props = wrapper.props();
expect(typeof props.list.itemProps.onToggle).toBe('function');
expect(typeof props.list.itemProps.onToggleAll).toBe('function');
@@ -158,13 +169,20 @@ describe('Container List', () => {
});
it('should render without toolbar', () => {
- const wrapper = shallow(, { lifecycleExperimental: true });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
const props = wrapper.props();
expect(props.toolbar).toBe(undefined);
});
it('should support displayMode as props', () => {
- const wrapper = shallow();
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
const props = wrapper.props();
expect(props.displayMode).toBe('large');
});
@@ -177,16 +195,13 @@ describe('Container List', () => {
'actionCreator:object:open': actionCreator,
},
};
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- lifecycleExperimental: true,
- context,
- },
+ mock.Provider.getEnzymeOption(context),
);
const props = wrapper.props();
const onClick = props.list.titleProps.onClick;
@@ -210,16 +225,13 @@ describe('Container List', () => {
'actionCreator:object:edit:submit': actionCreator,
},
};
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- lifecycleExperimental: true,
- context,
- },
+ mock.Provider.getEnzymeOption(context),
);
const props = wrapper.props();
const onEditSubmit = props.list.titleProps.onEditSubmit;
@@ -243,16 +255,13 @@ describe('Container List', () => {
'actionCreator:object:edit:cancel': actionCreator,
},
};
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- lifecycleExperimental: true,
- context,
- },
+ mock.Provider.getEnzymeOption(context),
);
const props = wrapper.props();
const onEditCancel = props.list.titleProps.onEditCancel;
@@ -280,16 +289,13 @@ describe('Container List', () => {
...cloneDeep(settings),
actions: {},
};
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- lifecycleExperimental: true,
- context,
- },
+ mock.Provider.getEnzymeOption(context),
);
const props = wrapper.props();
expect(props.list.titleProps.onClick).toBeUndefined();
@@ -305,17 +311,14 @@ describe('Container List', () => {
'actionCreator:pagination:change': actionCreator,
},
};
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- lifecycleExperimental: true,
- context,
- },
+ mock.Provider.getEnzymeOption(context),
);
const props = wrapper.props();
const event = null;
@@ -327,7 +330,12 @@ describe('Container List', () => {
props.toolbar.pagination.onChange(data.startIndex, data.itemsPerPage);
// then
- expect(dispatchActionCreator).toBeCalledWith('pagination:change', event, data, context);
+ expect(dispatchActionCreator).toBeCalledWith(
+ 'pagination:change',
+ event,
+ data,
+ expect.objectContaining(context),
+ );
});
it('should set the proper rowHeight', () => {
@@ -335,11 +343,11 @@ describe('Container List', () => {
table: 3,
large: 2,
};
- const wrapper = shallow(
+ const wrapper = mount(
,
- { lifecycleExperimental: true },
+ mock.Provider.getEnzymeOption(mock.store.context()),
);
- const props = wrapper.props();
+ const props = wrapper.find('List').props();
expect(props.displayMode).toBe('table');
expect(props.rowHeight).toBe(3);
});
@@ -348,11 +356,9 @@ describe('Container List', () => {
// given
const dispatch = jest.fn();
const setState = jest.fn();
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- lifecycleExperimental: true,
- },
+ mock.Provider.getEnzymeOption(mock.store.context()),
);
const props = wrapper.props();
const event = { type: 'click' };
@@ -369,11 +375,9 @@ describe('Container List', () => {
// given
const dispatch = jest.fn();
const setState = jest.fn();
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- lifecycleExperimental: true,
- },
+ mock.Provider.getEnzymeOption(mock.store.context()),
);
const props = wrapper.props();
const event = { type: 'click' };
@@ -390,11 +394,9 @@ describe('Container List', () => {
// given
const dispatch = jest.fn();
const setState = jest.fn();
- const wrapper = shallow(
+ const wrapper = mount(
,
- {
- lifecycleExperimental: true,
- },
+ mock.Provider.getEnzymeOption(mock.store.context()),
);
const props = wrapper.props();
const event = { type: 'click' };
@@ -418,11 +420,12 @@ describe('Container List', () => {
multiSelectionSetting.setState = jest.fn();
const state = fromJS({ selectedItems: [] });
multiSelectionSetting.state = state;
- const wrapper = shallow(, {
- lifecycleExperimental: true,
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
// when
- wrapper.instance().onToggleMultiSelection({}, { id: 1 });
+ wrapper.find('List').props().list.itemProps.onToggle({}, { id: 1 });
// then
expect(multiSelectionSetting.setState.mock.calls[0][0]).toEqual({
selectedItems: new ImmutableList([1]),
@@ -439,11 +442,12 @@ describe('Container List', () => {
multiSelectionSetting.setState = jest.fn();
const state = fromJS({ selectedItems: [1] });
multiSelectionSetting.state = state;
- const wrapper = shallow(, {
- lifecycleExperimental: true,
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
// when
- wrapper.instance().onToggleMultiSelection({}, { id: 1 });
+ wrapper.find('List').props().list.itemProps.onToggle({}, { id: 1 });
// then
expect(multiSelectionSetting.setState.mock.calls[0][0]).toEqual({
selectedItems: new ImmutableList([]),
@@ -459,11 +463,12 @@ describe('Container List', () => {
multiSelectionSetting.setState = jest.fn();
const state = fromJS({ selectedItems: [] });
multiSelectionSetting.state = state;
- const wrapper = shallow(, {
- lifecycleExperimental: true,
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
// when
- wrapper.instance().onToggleAllMultiSelection();
+ wrapper.find('List').props().list.itemProps.onToggleAll();
// then
expect(multiSelectionSetting.setState.mock.calls[0][0]).toEqual({
@@ -481,11 +486,12 @@ describe('Container List', () => {
multiSelectionSetting.setState = jest.fn();
const state = fromJS({ selectedItems: [1, 2, 3] });
multiSelectionSetting.state = state;
- const wrapper = shallow(, {
- lifecycleExperimental: true,
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
// when
- wrapper.instance().onToggleAllMultiSelection();
+ wrapper.find('List').props().list.itemProps.onToggleAll();
// then
expect(multiSelectionSetting.setState.mock.calls[0][0]).toEqual({
selectedItems: new ImmutableList([]),
@@ -504,9 +510,10 @@ describe('Container List', () => {
multiSelectionSetting.state = state;
// when
- const wrapper = shallow(, {
- lifecycleExperimental: true,
- });
+ const wrapper = mount(
+ ,
+ mock.Provider.getEnzymeOption(mock.store.context()),
+ );
// then
expect(wrapper.props().toolbar.actionBar.selected).toBe(3);
});
diff --git a/packages/containers/src/List/__snapshots__/List.test.js.snap b/packages/containers/src/List/__snapshots__/List.test.js.snap
index 3538fdf94d8..29d44450168 100644
--- a/packages/containers/src/List/__snapshots__/List.test.js.snap
+++ b/packages/containers/src/List/__snapshots__/List.test.js.snap
@@ -212,10 +212,12 @@ Object {
"onChange": [Function],
},
"titleProps": Object {
+ "actionsKey": "actions",
"key": "label",
"onClick": [Function],
"onEditCancel": [Function],
"onEditSubmit": [Function],
+ "persistentActionsKey": "persistentActions",
},
},
"toolbar": Object {
@@ -228,6 +230,7 @@ Object {
"large",
"table",
],
+ "mode": "table",
"onChange": [Function],
},
"filter": Object {
diff --git a/packages/containers/src/Notification/Notification.test.js b/packages/containers/src/Notification/Notification.test.js
index b81f2738f67..4265434cd36 100644
--- a/packages/containers/src/Notification/Notification.test.js
+++ b/packages/containers/src/Notification/Notification.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import renderer from 'react-test-renderer';
+import { mount } from 'enzyme';
import { mock } from '@talend/react-cmf';
import Immutable, { fromJS } from 'immutable';
import Container from './Notification.container';
@@ -13,15 +13,9 @@ jest.mock('@talend/react-components/lib/Notification', () => props => (
describe('Container Notification', () => {
it('should render', () => {
- const { Provider } = mock;
- const wrapper = renderer
- .create(
-
-
- ,
- )
- .toJSON();
- expect(wrapper).toMatchSnapshot();
+ const context = mock.store.context();
+ const wrapper = mount(, mock.Provider.getEnzymeOption(context));
+ expect(wrapper.html()).toMatchSnapshot();
});
});
@@ -64,9 +58,7 @@ describe('Connected Notification', () => {
const stateProps = {
state: fromJS({ notifications: [ok] }),
};
- expect(deleteNotification(ko)(stateProps)).toEqual(
- stateProps.state
- );
+ expect(deleteNotification(ko)(stateProps)).toEqual(stateProps.state);
});
});
diff --git a/packages/containers/src/Notification/__snapshots__/Notification.test.js.snap b/packages/containers/src/Notification/__snapshots__/Notification.test.js.snap
index c17e27ab43a..2a047a66015 100644
--- a/packages/containers/src/Notification/__snapshots__/Notification.test.js.snap
+++ b/packages/containers/src/Notification/__snapshots__/Notification.test.js.snap
@@ -1,12 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Container Notification should render 1`] = `
-
`;
diff --git a/packages/containers/src/SelectObject/__snapshots__/SelectObject.test.js.snap b/packages/containers/src/SelectObject/__snapshots__/SelectObject.test.js.snap
index 859747cd42d..1ac3ef622ba 100644
--- a/packages/containers/src/SelectObject/__snapshots__/SelectObject.test.js.snap
+++ b/packages/containers/src/SelectObject/__snapshots__/SelectObject.test.js.snap
@@ -4,7 +4,7 @@ exports[`Component SelectObject should render 1`] = `
-
- {
};
});
-describe('#register contianers', () => {
+describe('#register containers', () => {
it('should register all component', () => {
cmf.component.registerMany = jest.fn();
cmf.component.register = jest.fn();
diff --git a/packages/datagrid/src/containers/DataGrid/DataGrid.connect.test.js b/packages/datagrid/src/containers/DataGrid/DataGrid.connect.test.js
index af59b97559c..d2ba4982aac 100644
--- a/packages/datagrid/src/containers/DataGrid/DataGrid.connect.test.js
+++ b/packages/datagrid/src/containers/DataGrid/DataGrid.connect.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
import { mock } from '@talend/react-cmf';
import ConnectedDataGrid from './DataGrid.connect';
@@ -7,7 +7,9 @@ import ConnectedDataGrid from './DataGrid.connect';
describe('#ConnectedDataGrid', () => {
it('should render a connected DataGrid', () => {
const context = mock.store.context();
- const wrapper = shallow(, { context });
- expect(wrapper.getElement().type.displayName).toBe('CMF(Container(DataGrid))');
+ const wrapper = mount(, mock.Provider.getEnzymeOption(context));
+ expect(wrapper.find(ConnectedDataGrid.CMFContainer).type().displayName).toBe(
+ 'CMF(Container(DataGrid))',
+ );
});
});
diff --git a/packages/flow-designer/package.json b/packages/flow-designer/package.json
index 639f0b7440c..f85351bbd7a 100644
--- a/packages/flow-designer/package.json
+++ b/packages/flow-designer/package.json
@@ -26,9 +26,9 @@
"@types/invariant": "^2.2.35",
"@types/lodash": "^4.14.178",
"@types/react": "^16.14.21",
- "@types/react-redux": "^5.0.24",
+ "@types/react-redux": "^7.1.20",
"@types/react-test-renderer": "^16.9.5",
- "@types/redux-mock-store": "^0.0.15",
+ "@types/redux-mock-store": "^1.0.3",
"@types/redux-thunk": "^2.1.0",
"@typescript-eslint/parser": "^4.33.0",
"enzyme": "^3.11.0",
@@ -39,9 +39,9 @@
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-i18next": "^11.15.3",
- "react-redux": "^5.1.2",
+ "react-redux": "^7.2.4",
"react-test-renderer": "^17.0.2",
- "redux": "^3.7.2",
+ "redux": "^4.1.0",
"redux-mock-store": "^1.5.4",
"redux-thunk": "^2.4.1",
"reselect": "^4.1.5"
diff --git a/packages/sagas/package.json b/packages/sagas/package.json
index dcae04fdeda..9e66b964730 100644
--- a/packages/sagas/package.json
+++ b/packages/sagas/package.json
@@ -34,7 +34,7 @@
"dependencies": {
"@talend/react-cmf": "^6.39.1",
"immutable": "^3.8.2",
- "redux-saga": "^0.15.6"
+ "redux-saga": "^1.1.3"
},
"peerDependencies": {
"prop-types": "^15.5.10",
diff --git a/packages/sagas/src/pending/pending.js b/packages/sagas/src/pending/pending.js
index 8f18c370516..1530c6b40b6 100644
--- a/packages/sagas/src/pending/pending.js
+++ b/packages/sagas/src/pending/pending.js
@@ -1,5 +1,4 @@
-import { delay } from 'redux-saga';
-import { call, put, select, take } from 'redux-saga/effects';
+import { delay, call, put, select, take } from 'redux-saga/effects';
import cmf from '@talend/react-cmf';
import { Map } from 'immutable';
@@ -40,7 +39,7 @@ export default function* pendingMaybeNeeded(asyncCallerId, actionId) {
let pending = false;
try {
- yield call(delay, PENDING_DELAY_TO_SHOW);
+ yield delay(PENDING_DELAY_TO_SHOW);
pending = true;
yield call(ensurePendersCollectionExists);
let pendersCollection = yield select(findPenders);
diff --git a/packages/sagas/src/pending/pending.test.js b/packages/sagas/src/pending/pending.test.js
index e157ae9f233..4cf9171587b 100644
--- a/packages/sagas/src/pending/pending.test.js
+++ b/packages/sagas/src/pending/pending.test.js
@@ -1,5 +1,4 @@
-import { delay } from 'redux-saga';
-import { select, put, call, take } from 'redux-saga/effects';
+import { delay, select, put, call, take } from 'redux-saga/effects';
import cmf from '@talend/react-cmf';
import { Map } from 'immutable';
import pendingMaybeNeeded, {
@@ -34,7 +33,7 @@ describe('test pending status', () => {
const gen = pendingMaybeNeeded('', 'streams:create');
let pendersCollection = new Map();
- expect(gen.next().value).toEqual(call(delay, PENDING_DELAY_TO_SHOW));
+ expect(gen.next().value).toEqual(delay(PENDING_DELAY_TO_SHOW));
expect(gen.next().value).toEqual(call(ensurePendersCollectionExists));
expect(gen.next().value).toEqual(select(findPenders));
diff --git a/packages/stepper/package.json b/packages/stepper/package.json
index f7d5a0ecdbd..19af02f7b7a 100644
--- a/packages/stepper/package.json
+++ b/packages/stepper/package.json
@@ -58,7 +58,7 @@
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-i18next": "^11.15.3",
- "react-redux": "^5.1.2",
+ "react-redux": "^7.2.2",
"react-transition-group": "^2.9.0",
"style-loader": "^0.23.1"
},
@@ -67,7 +67,7 @@
"prop-types": "^15.5.10",
"react": "^16.8.6",
"react-i18next": "^11.8.13",
- "react-redux": "^5.0.7",
+ "react-redux": "^7.2.2",
"react-transition-group": "^2.3.1"
},
"publishConfig": {
diff --git a/packages/storybook-cmf/package.json b/packages/storybook-cmf/package.json
index f09549d1a77..fd562ad1edb 100644
--- a/packages/storybook-cmf/package.json
+++ b/packages/storybook-cmf/package.json
@@ -30,8 +30,8 @@
"enzyme": "^3.11.0",
"react": "^16.14.0",
"react-dom": "^16.14.0",
- "react-redux": "^5.1.2",
- "redux-saga": "^0.15.6"
+ "react-redux": "^7.2.2",
+ "redux-saga": "^1.1.3"
},
"dependencies": {
"@storybook/addons": "^6.4.9",
@@ -40,7 +40,7 @@
"peerDependencies": {
"@talend/react-cmf": "^6.0.0",
"react": "^16.8.6",
- "react-redux": "^5.0.7",
- "redux-saga": "^0.15.4"
+ "react-redux": "^7.2.2",
+ "redux-saga": "^1.1.3"
}
}
diff --git a/packages/storybook-cmf/src/CMFStory/CMFStory.component.js b/packages/storybook-cmf/src/CMFStory/CMFStory.component.js
index fa9eb560d0e..6671378c823 100644
--- a/packages/storybook-cmf/src/CMFStory/CMFStory.component.js
+++ b/packages/storybook-cmf/src/CMFStory/CMFStory.component.js
@@ -3,7 +3,7 @@ import React from 'react';
import { all, fork } from 'redux-saga/effects';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
-import api, { store, RegistryProvider, mock } from '@talend/react-cmf';
+import api, { store, registry, RegistryProvider, mock } from '@talend/react-cmf';
function* initSagaMiddleWare() {
yield all([fork(api.sagas.component.handle)]);
@@ -34,6 +34,11 @@ class CMFStory extends React.Component {
api.registerInternals();
props.sagaMiddleware.run(initSagaMiddleWare);
}
+ if (props.registry) {
+ this.registry = { ...registry.getRegistry, ...props.registry };
+ } else {
+ this.registry = registry.getRegistry() || {};
+ }
}
getChildContext() {
@@ -54,7 +59,7 @@ class CMFStory extends React.Component {
render() {
return (
- {this.props.children}
+ {this.props.children}
);
}
@@ -62,6 +67,7 @@ class CMFStory extends React.Component {
CMFStory.propTypes = {
state: PropTypes.object,
+ registry: PropTypes.object,
children: PropTypes.node,
reducer: PropTypes.func,
enhancer: PropTypes.func,
@@ -71,9 +77,7 @@ CMFStory.propTypes = {
CMFStory.defaultProps = {
middleware: [],
};
-CMFStory.contextTypes = {
- registry: PropTypes.object,
-};
+
CMFStory.childContextTypes = {
router: PropTypes.object,
};
diff --git a/packages/storybook-cmf/src/CMFStory/__snapshots__/CMFStory.test.js.snap b/packages/storybook-cmf/src/CMFStory/__snapshots__/CMFStory.test.js.snap
index 4773b0741ff..1d52acb6d41 100644
--- a/packages/storybook-cmf/src/CMFStory/__snapshots__/CMFStory.test.js.snap
+++ b/packages/storybook-cmf/src/CMFStory/__snapshots__/CMFStory.test.js.snap
@@ -4,18 +4,20 @@ exports[`CMFStory should render its name 1`] = `
-
+
My Story
-
+
`;
diff --git a/versions/dependencies.json b/versions/dependencies.json
index a61498d4150..00c997acd74 100644
--- a/versions/dependencies.json
+++ b/versions/dependencies.json
@@ -60,7 +60,7 @@
"react-hook-form": "^6.9.2",
"react-i18next": "^10.11.4",
"react-popper": "^1.3.7",
- "react-redux": "^5.0.7",
+ "react-redux": "^7.2.2",
"react-router": "^3.2.0",
"react-router-redux": "^4.0.8",
"react-router-dom": "^5.2.0",
@@ -69,13 +69,13 @@
"react-use": "^17.3.1",
"reactour": "^1.13.4",
"recharts": "^2.1.6",
- "redux": "^3.7.2",
- "redux-batched-actions": "^0.2.0",
+ "redux": "^4.0.5",
+ "redux-batched-actions": "^0.5.0",
"redux-batched-subscribe": "^0.1.6",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.5.4",
- "redux-saga": "^0.15.4",
- "redux-thunk": "^2.2.0",
+ "redux-saga": "^1.1.3",
+ "redux-thunk": "^2.3.0",
"redux-undo": "beta",
"reselect": "^2.5.4",
"@sentry/browser": "^5.11.1",
@@ -108,16 +108,16 @@
"babel-eslint": "^10.0.3",
"babel-jest": "^24.7.1",
"//comment_babel": "// babel 7",
- "@babel/cli": "^7.11.5",
- "@babel/core": "^7.11.5",
+ "@babel/cli": "^7.11.6",
+ "@babel/core": "^7.11.6",
"@babel/polyfill": "^7.8.7",
- "@babel/plugin-proposal-class-properties": "^7.11.5",
- "@babel/plugin-proposal-object-rest-spread": "^7.11.5",
- "@babel/plugin-transform-object-assign": "^7.11.5",
- "@babel/plugin-proposal-export-namespace-from": "^7.11.5",
- "@babel/plugin-proposal-export-default-from": "^7.11.5",
- "@babel/preset-env": "^7.11.5",
- "@babel/preset-react": "^7.11.5",
+ "@babel/plugin-proposal-class-properties": "^7.11.6",
+ "@babel/plugin-proposal-object-rest-spread": "^7.11.6",
+ "@babel/plugin-transform-object-assign": "^7.11.6",
+ "@babel/plugin-proposal-export-namespace-from": "^7.11.6",
+ "@babel/plugin-proposal-export-default-from": "^7.11.6",
+ "@babel/preset-env": "^7.11.6",
+ "@babel/preset-react": "^7.11.6",
"enzyme": "^3.11.0",
"enzyme-adapter-react-15": "^1.3.1",
"enzyme-adapter-react-16": "^1.11.2",
diff --git a/yarn.lock b/yarn.lock
index c6146851f22..4c937440c9e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1264,7 +1264,7 @@
core-js-pure "^3.19.0"
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
@@ -2053,6 +2053,58 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9"
integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==
+"@redux-saga/core@^1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.1.3.tgz#3085097b57a4ea8db5528d58673f20ce0950f6a4"
+ integrity sha512-8tInBftak8TPzE6X13ABmEtRJGjtK17w7VUs7qV17S8hCO5S3+aUTWZ/DBsBJPdE8Z5jOPwYALyvofgq1Ws+kg==
+ dependencies:
+ "@babel/runtime" "^7.6.3"
+ "@redux-saga/deferred" "^1.1.2"
+ "@redux-saga/delay-p" "^1.1.2"
+ "@redux-saga/is" "^1.1.2"
+ "@redux-saga/symbols" "^1.1.2"
+ "@redux-saga/types" "^1.1.0"
+ redux "^4.0.4"
+ typescript-tuple "^2.2.1"
+
+"@redux-saga/deferred@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@redux-saga/deferred/-/deferred-1.1.2.tgz#59937a0eba71fff289f1310233bc518117a71888"
+ integrity sha512-908rDLHFN2UUzt2jb4uOzj6afpjgJe3MjICaUNO3bvkV/kN/cNeI9PMr8BsFXB/MR8WTAZQq/PlTq8Kww3TBSQ==
+
+"@redux-saga/delay-p@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@redux-saga/delay-p/-/delay-p-1.1.2.tgz#8f515f4b009b05b02a37a7c3d0ca9ddc157bb355"
+ integrity sha512-ojc+1IoC6OP65Ts5+ZHbEYdrohmIw1j9P7HS9MOJezqMYtCDgpkoqB5enAAZrNtnbSL6gVCWPHaoaTY5KeO0/g==
+ dependencies:
+ "@redux-saga/symbols" "^1.1.2"
+
+"@redux-saga/is@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@redux-saga/is/-/is-1.1.2.tgz#ae6c8421f58fcba80faf7cadb7d65b303b97e58e"
+ integrity sha512-OLbunKVsCVNTKEf2cH4TYyNbbPgvmZ52iaxBD4I1fTif4+MTXMa4/Z07L83zW/hTCXwpSZvXogqMqLfex2Tg6w==
+ dependencies:
+ "@redux-saga/symbols" "^1.1.2"
+ "@redux-saga/types" "^1.1.0"
+
+"@redux-saga/symbols@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@redux-saga/symbols/-/symbols-1.1.2.tgz#216a672a487fc256872b8034835afc22a2d0595d"
+ integrity sha512-EfdGnF423glv3uMwLsGAtE6bg+R9MdqlHEzExnfagXPrIiuxwr3bdiAwz3gi+PsrQ3yBlaBpfGLtDG8rf3LgQQ==
+
+"@redux-saga/testing-utils@^1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@redux-saga/testing-utils/-/testing-utils-1.1.3.tgz#0148b7bfa541cb69ee144e1a2e79a53813c54560"
+ integrity sha512-MGMcBHgt80CoC8s8i0Mc7svGJPysS9qkJuAINlg+NvudLZcV23myd+H4uaXA4zmiLf16C4M+97b+e6wFoTaGcw==
+ dependencies:
+ "@redux-saga/symbols" "^1.1.2"
+ "@redux-saga/types" "^1.1.0"
+
+"@redux-saga/types@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.1.0.tgz#0e81ce56b4883b4b2a3001ebe1ab298b84237204"
+ integrity sha512-afmTuJrylUU/0OtqzaRkbyYFFNgCF73Bvel/sw90pvGrWIZ+vyoIJqA6eMSoA6+nb443kTmulmBtC9NerXboNg==
+
"@rooks/use-mutation-observer@4.11.2":
version "4.11.2"
resolved "https://registry.yarnpkg.com/@rooks/use-mutation-observer/-/use-mutation-observer-4.11.2.tgz#a0466c4338e0a4487ea19253c86bcd427c29f4af"
@@ -3911,7 +3963,7 @@
dependencies:
"@types/unist" "*"
-"@types/hoist-non-react-statics@*":
+"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
@@ -4119,13 +4171,15 @@
dependencies:
"@types/react" "^16"
-"@types/react-redux@^5.0.24":
- version "5.0.24"
- resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-5.0.24.tgz#8567dbd87bdfbb8a2b17bcdbb064d678f86c91e2"
- integrity sha512-EJfdjLtxceQmrVZrcwSZR6V97e6qyG/gaJ2MQrsVzGYiXO2cN+oHBSizB1/SnReTkorpEgQulhVdYcFee8abqQ==
+"@types/react-redux@^7.1.20":
+ version "7.1.20"
+ resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.20.tgz#42f0e61ababb621e12c66c96dda94c58423bd7df"
+ integrity sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw==
dependencies:
+ "@types/hoist-non-react-statics" "^3.3.0"
"@types/react" "*"
- redux "^3.6.0"
+ hoist-non-react-statics "^3.3.0"
+ redux "^4.0.0"
"@types/react-syntax-highlighter@11.0.5":
version "11.0.5"
@@ -4159,12 +4213,12 @@
"@types/scheduler" "*"
csstype "^3.0.2"
-"@types/redux-mock-store@^0.0.15":
- version "0.0.15"
- resolved "https://registry.yarnpkg.com/@types/redux-mock-store/-/redux-mock-store-0.0.15.tgz#94298dfd8395648e8c1f18705be2a5d4967ff3df"
- integrity sha512-w7bLLE8Luu/MX7BNaoWODeI3YRIjYTr9J3cnzmiNfiwjfay2PWoGoBpUifYA8Y5CXdoacKAx8WyRtw5Xaiku1g==
+"@types/redux-mock-store@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@types/redux-mock-store/-/redux-mock-store-1.0.3.tgz#895de4a364bc4836661570aec82f2eef5989d1fb"
+ integrity sha512-Wqe3tJa6x9MxMN4DJnMfZoBRBRak1XTPklqj4qkVm5VBpZnC8PSADf4kLuFQ9NAdHaowfWoEeUMz7NWc2GMtnA==
dependencies:
- redux "^3.6.0"
+ redux "^4.0.5"
"@types/redux-thunk@^2.1.0":
version "2.1.0"
@@ -13132,11 +13186,6 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
-lodash-es@^4.2.1:
- version "4.17.21"
- resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
- integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
-
lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@@ -13272,7 +13321,7 @@ lodash.unset@^4.1.0:
resolved "https://registry.yarnpkg.com/lodash.unset/-/lodash.unset-4.5.2.tgz#370d1d3e85b72a7e1b0cdf2d272121306f23e4ed"
integrity sha1-Nw0dPoW3Kn4bDN8tJyEhMG8j5O0=
-lodash@4.x, lodash@^4, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.2.1, lodash@^4.7.0:
+lodash@4.x, lodash@^4, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -16614,7 +16663,7 @@ react-jsonschema-form@0.51.0:
prop-types "^15.5.8"
setimmediate "^1.0.5"
-react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
+react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
@@ -16655,18 +16704,17 @@ react-prop-types@^0.4.0:
dependencies:
warning "^3.0.0"
-react-redux@^5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57"
- integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q==
+react-redux@^7.2.2, react-redux@^7.2.4:
+ version "7.2.6"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa"
+ integrity sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==
dependencies:
- "@babel/runtime" "^7.1.2"
- hoist-non-react-statics "^3.3.0"
- invariant "^2.2.4"
- loose-envify "^1.1.0"
- prop-types "^15.6.1"
- react-is "^16.6.0"
- react-lifecycles-compat "^3.0.0"
+ "@babel/runtime" "^7.15.4"
+ "@types/react-redux" "^7.1.20"
+ hoist-non-react-statics "^3.3.2"
+ loose-envify "^1.4.0"
+ prop-types "^15.7.2"
+ react-is "^17.0.2"
react-refresh@^0.10.0:
version "0.10.0"
@@ -17122,10 +17170,10 @@ redux-actions@^0.10.1:
dependencies:
reduce-reducers "^0.1.0"
-redux-batched-actions@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/redux-batched-actions/-/redux-batched-actions-0.2.1.tgz#d0c41d495a934004cabafbe0e851746d1795e9dd"
- integrity sha512-7lyD7tDdggZTi0v/7WQ3rvvHSk+JxGF4udoGGvw1/0gwjTUCRpD466Og/dm/daegEofmDKMqMWLv03DhgKuNDA==
+redux-batched-actions@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/redux-batched-actions/-/redux-batched-actions-0.5.0.tgz#d3f0e359b2a95c7d80bab442df450bfafd57d122"
+ integrity sha512-6orZWyCnIQXMGY4DUGM0oj0L7oYnwTACsfsru/J7r94RM3P9eS7SORGpr3LCeRCMoIMQcpfKZ7X4NdyFHBS8Eg==
redux-batched-subscribe@^0.1.6:
version "0.1.6"
@@ -17153,10 +17201,12 @@ redux-saga-tester@^1.0.874:
dependencies:
babel-runtime "^6.11.6"
-redux-saga@^0.15.6:
- version "0.15.6"
- resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-0.15.6.tgz#8638dc522de6c6c0a496fe8b2b5466287ac2dc4d"
- integrity sha1-hjjcUi3mxsCklv6LK1RmKHrC3E0=
+redux-saga@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.1.3.tgz#9f3e6aebd3c994bbc0f6901a625f9a42b51d1112"
+ integrity sha512-RkSn/z0mwaSa5/xH/hQLo8gNf4tlvT18qXDNvedihLcfzh+jMchDgaariQoehCpgRltEm4zHKJyINEz6aqswTw==
+ dependencies:
+ "@redux-saga/core" "^1.1.3"
redux-storage-decorator-filter@^1.1.8:
version "1.1.8"
@@ -17206,15 +17256,12 @@ redux-thunk@*, redux-thunk@^2.4.1:
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==
-redux@^3.6.0, redux@^3.7.2:
- version "3.7.2"
- resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
- integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==
+redux@^4.0.0, redux@^4.0.4, redux@^4.0.5, redux@^4.1.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104"
+ integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==
dependencies:
- lodash "^4.2.1"
- lodash-es "^4.2.1"
- loose-envify "^1.1.0"
- symbol-observable "^1.0.3"
+ "@babel/runtime" "^7.9.2"
reflect.ownkeys@^0.2.0:
version "0.2.0"
@@ -19268,11 +19315,6 @@ symbol-observable@1.0.1:
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=
-symbol-observable@^1.0.3:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
- integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
-
symbol-tree@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
@@ -19883,6 +19925,25 @@ typeface-source-sans-pro@^1.1.13:
resolved "https://registry.yarnpkg.com/typeface-source-sans-pro/-/typeface-source-sans-pro-1.1.13.tgz#a7757542b8c301c26322acb0c8b97cc1a12cf91e"
integrity sha512-eCczLh0FYByjVoMxZVDfSSTI8A6qJOBJftgWBVJL63AuemQTTvMU8DEk6ud/TQhkbIwWZ3A7ll/NfS9yI0lIrQ==
+typescript-compare@^0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425"
+ integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==
+ dependencies:
+ typescript-logic "^0.0.0"
+
+typescript-logic@^0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196"
+ integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==
+
+typescript-tuple@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2"
+ integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==
+ dependencies:
+ typescript-compare "^0.0.2"
+
typescript@^3.0.0:
version "3.9.10"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"