@truedat/df 4.44.2 → 4.44.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/package.json +24 -17
- package/src/components/DynamicForm.js +21 -19
- package/src/components/SelectableDynamicForm.js +66 -0
- package/src/components/__tests__/SelectableDynamicForm.spec.js +122 -0
- package/src/components/__tests__/__snapshots__/SelectableDynamicForm.spec.js.snap +114 -0
- package/src/components/widgets/DateTimeField.js +14 -16
- package/src/messages/en.js +1 -1
- package/src/reducers/__tests__/selectedDomains.spec.js +30 -0
- package/src/reducers/index.js +1 -0
- package/src/reducers/selectedDomains.js +15 -0
- package/src/routines.js +3 -1
- package/src/selectors/__tests__/templates.spec.js +6 -17
- package/src/selectors/templates.js +5 -12
- package/src/templates/components/TemplateLoader.js +36 -19
- package/src/templates/components/__tests__/TemplateLoader.spec.js +15 -7
- package/src/templates/components/index.js +1 -2
- package/src/templates/sagas/fetchTemplate.js +6 -2
- package/src/templates/components/TemplateSelector.js +0 -59
- package/src/templates/components/__tests__/TemplateSelector.spec.js +0 -29
- package/src/templates/components/__tests__/__snapshots__/TemplateSelector.spec.js.snap +0 -54
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.44.5] 2022-05-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- [TD-4230] Created `SelectableDynamicForm` that wraps `TemplateSelector` and `DynamicForm` with common business logic
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- [TD-4230] `TemplateLoader` works with multiple domain ids
|
|
12
|
+
|
|
3
13
|
## [4.38.4] 2022-02-20
|
|
4
14
|
|
|
5
15
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/df",
|
|
3
|
-
"version": "4.44.
|
|
3
|
+
"version": "4.44.5",
|
|
4
4
|
"description": "Truedat Web Data Quality Module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -16,22 +16,26 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"clean": "rimraf yarn-error.log",
|
|
18
18
|
"debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
|
|
19
|
-
"test": "jest --coverage",
|
|
20
|
-
"test:watch": "jest --watch",
|
|
19
|
+
"test": "TZ=UTC jest --coverage",
|
|
20
|
+
"test:watch": "TZ=UTC jest --watch",
|
|
21
21
|
"eslint": "eslint src/**",
|
|
22
22
|
"eslint:fix": "eslint --fix src/**"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@babel/cli": "^7.
|
|
26
|
-
"@babel/core": "^7.
|
|
27
|
-
"@babel/plugin-proposal-class-properties": "^7.
|
|
28
|
-
"@babel/plugin-proposal-object-rest-spread": "^7.
|
|
25
|
+
"@babel/cli": "^7.17.10",
|
|
26
|
+
"@babel/core": "^7.18.0",
|
|
27
|
+
"@babel/plugin-proposal-class-properties": "^7.17.12",
|
|
28
|
+
"@babel/plugin-proposal-object-rest-spread": "^7.18.0",
|
|
29
|
+
"@babel/plugin-proposal-optional-chaining": "^7.17.12",
|
|
29
30
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
30
|
-
"@babel/plugin-transform-modules-commonjs": "^7.
|
|
31
|
-
"@babel/preset-env": "^7.
|
|
32
|
-
"@babel/preset-react": "^7.
|
|
33
|
-
"@
|
|
34
|
-
"
|
|
31
|
+
"@babel/plugin-transform-modules-commonjs": "^7.18.0",
|
|
32
|
+
"@babel/preset-env": "^7.18.0",
|
|
33
|
+
"@babel/preset-react": "^7.17.12",
|
|
34
|
+
"@testing-library/jest-dom": "^5.16.4",
|
|
35
|
+
"@testing-library/react": "^12.0.0",
|
|
36
|
+
"@testing-library/user-event": "^13.2.1",
|
|
37
|
+
"@truedat/test": "4.44.5",
|
|
38
|
+
"babel-jest": "^28.1.0",
|
|
35
39
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
36
40
|
"babel-plugin-lodash": "^3.3.4",
|
|
37
41
|
"babel-plugin-react-intl": "^5.1.18",
|
|
@@ -40,7 +44,8 @@
|
|
|
40
44
|
"enzyme-adapter-react-16": "^1.15.6",
|
|
41
45
|
"enzyme-to-json": "^3.6.2",
|
|
42
46
|
"identity-obj-proxy": "^3.0.0",
|
|
43
|
-
"jest": "^
|
|
47
|
+
"jest": "^28.1.0",
|
|
48
|
+
"jest-environment-jsdom": "^28.1.0",
|
|
44
49
|
"react": "^16.14.0",
|
|
45
50
|
"react-dom": "^16.14.0",
|
|
46
51
|
"redux-saga-test-plan": "^4.0.4",
|
|
@@ -48,6 +53,8 @@
|
|
|
48
53
|
"semantic-ui-react": "^2.0.3"
|
|
49
54
|
},
|
|
50
55
|
"jest": {
|
|
56
|
+
"maxWorkers": "50%",
|
|
57
|
+
"testTimeout": 10000,
|
|
51
58
|
"moduleDirectories": [
|
|
52
59
|
"<rootDir>/src",
|
|
53
60
|
"../../node_modules"
|
|
@@ -80,11 +87,11 @@
|
|
|
80
87
|
]
|
|
81
88
|
},
|
|
82
89
|
"dependencies": {
|
|
83
|
-
"@truedat/auth": "4.44.
|
|
84
|
-
"@truedat/core": "4.44.
|
|
90
|
+
"@truedat/auth": "4.44.5",
|
|
91
|
+
"@truedat/core": "4.44.5",
|
|
85
92
|
"axios": "^0.19.2",
|
|
86
93
|
"path-to-regexp": "^1.7.0",
|
|
87
|
-
"prop-types": "^15.
|
|
94
|
+
"prop-types": "^15.8.1",
|
|
88
95
|
"react-color": "^2.17.3",
|
|
89
96
|
"react-intl": "^5.20.10",
|
|
90
97
|
"react-redux": "^7.2.4",
|
|
@@ -100,5 +107,5 @@
|
|
|
100
107
|
"react-dom": ">= 16.8.6 < 17",
|
|
101
108
|
"semantic-ui-react": ">= 0.88.2 < 2.1"
|
|
102
109
|
},
|
|
103
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "5a339468198c803592b285eddd0dd0c0b0eced93"
|
|
104
111
|
}
|
|
@@ -15,12 +15,13 @@ export const DynamicForm = ({
|
|
|
15
15
|
fieldsToOmit,
|
|
16
16
|
selectedDomain,
|
|
17
17
|
}) => {
|
|
18
|
+
const domainId = selectedDomain?.id;
|
|
18
19
|
useEffect(() => {
|
|
19
|
-
if (!_.isNil(
|
|
20
|
-
onChange(applyTemplate(content,
|
|
20
|
+
if (!_.isNil(domainId) && !_.isEmpty(template)) {
|
|
21
|
+
onChange(applyTemplate(content, domainId), {});
|
|
21
22
|
}
|
|
22
23
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
|
-
}, [applyTemplate,
|
|
24
|
+
}, [applyTemplate, domainId, template]);
|
|
24
25
|
|
|
25
26
|
if (
|
|
26
27
|
loading ||
|
|
@@ -33,10 +34,7 @@ export const DynamicForm = ({
|
|
|
33
34
|
|
|
34
35
|
const handleChange = (e, { name, value }) => {
|
|
35
36
|
e && e.preventDefault();
|
|
36
|
-
const newContent = applyTemplate(
|
|
37
|
-
{ ...content, [name]: value },
|
|
38
|
-
selectedDomain?.id
|
|
39
|
-
);
|
|
37
|
+
const newContent = applyTemplate({ ...content, [name]: value }, domainId);
|
|
40
38
|
onChange(newContent, {});
|
|
41
39
|
};
|
|
42
40
|
|
|
@@ -49,17 +47,15 @@ export const DynamicForm = ({
|
|
|
49
47
|
|
|
50
48
|
return (
|
|
51
49
|
<>
|
|
52
|
-
{parsedGroups.map(({ name, fields }, i) =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
);
|
|
62
|
-
})}
|
|
50
|
+
{parsedGroups.map(({ name, fields }, i) => (
|
|
51
|
+
<FieldGroupSegment
|
|
52
|
+
key={i}
|
|
53
|
+
onFieldChange={handleChange}
|
|
54
|
+
name={name}
|
|
55
|
+
fields={fields}
|
|
56
|
+
scope={_.prop("scope")(template)}
|
|
57
|
+
/>
|
|
58
|
+
))}
|
|
63
59
|
</>
|
|
64
60
|
);
|
|
65
61
|
};
|
|
@@ -81,7 +77,13 @@ const makeMapStateToProps = () => {
|
|
|
81
77
|
const mapStateToProps = (state, props) => ({
|
|
82
78
|
applyTemplate: getApplyTemplate(state, props),
|
|
83
79
|
loading: state.loading,
|
|
84
|
-
selectedDomain:
|
|
80
|
+
selectedDomain:
|
|
81
|
+
props.selectedDomain ||
|
|
82
|
+
(!_.isEmpty(state.selectedDomain)
|
|
83
|
+
? state.selectedDomain
|
|
84
|
+
: _.isNil(_.head(state.selectedDomains))
|
|
85
|
+
? null
|
|
86
|
+
: { id: _.head(state.selectedDomains) }),
|
|
85
87
|
template: getTemplate(state, props),
|
|
86
88
|
});
|
|
87
89
|
return mapStateToProps;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
import { TemplateSelector } from "@truedat/core/components";
|
|
4
|
+
import { applyTemplate, validateContent } from "@truedat/df/utils";
|
|
5
|
+
import DynamicForm from "./DynamicForm";
|
|
6
|
+
|
|
7
|
+
export default function SelectableDynamicForm({
|
|
8
|
+
domainIds,
|
|
9
|
+
scope,
|
|
10
|
+
required,
|
|
11
|
+
content,
|
|
12
|
+
header,
|
|
13
|
+
name,
|
|
14
|
+
onChange,
|
|
15
|
+
onNameChange,
|
|
16
|
+
}) {
|
|
17
|
+
const [template, setTemplate] = useState();
|
|
18
|
+
const domain = _.size(domainIds) > 0 ? { id: _.head(domainIds) } : null;
|
|
19
|
+
|
|
20
|
+
const handleChange = (content) => {
|
|
21
|
+
onChange({ content, valid: validateContent(template)(content) });
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const handleLoad = ({ templates }) => {
|
|
25
|
+
const template = name
|
|
26
|
+
? _.find(_.propEq("name", name))(templates)
|
|
27
|
+
: _.size(templates) == 1
|
|
28
|
+
? _.head(templates)
|
|
29
|
+
: null;
|
|
30
|
+
if (template?.name !== name) {
|
|
31
|
+
onNameChange(template?.name);
|
|
32
|
+
}
|
|
33
|
+
handleChange(template ? applyTemplate(template)(content, domain?.id) : {});
|
|
34
|
+
setTemplate(template);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const handleSelected = (_e, { template }) => {
|
|
38
|
+
onNameChange(template?.name);
|
|
39
|
+
handleChange(template ? applyTemplate(template)(content, domain?.id) : {});
|
|
40
|
+
setTemplate(template);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<TemplateSelector
|
|
46
|
+
scope={scope}
|
|
47
|
+
domainIds={domainIds}
|
|
48
|
+
required={required}
|
|
49
|
+
selectedValue={template?.id}
|
|
50
|
+
onChange={handleSelected}
|
|
51
|
+
onLoad={handleLoad}
|
|
52
|
+
/>
|
|
53
|
+
{template ? (
|
|
54
|
+
<>
|
|
55
|
+
{header}
|
|
56
|
+
<DynamicForm
|
|
57
|
+
onChange={handleChange}
|
|
58
|
+
content={content}
|
|
59
|
+
template={template}
|
|
60
|
+
selectedDomain={domain}
|
|
61
|
+
/>
|
|
62
|
+
</>
|
|
63
|
+
) : null}
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { waitFor, waitForElementToBeRemoved } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { render } from "@truedat/test/render";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
errorTemplateMock,
|
|
8
|
+
multipleTemplatesMock,
|
|
9
|
+
singleTemplateMock,
|
|
10
|
+
} from "@truedat/test/mocks";
|
|
11
|
+
import SelectableDynamicForm from "../SelectableDynamicForm";
|
|
12
|
+
|
|
13
|
+
const variables = {
|
|
14
|
+
scope: "foo",
|
|
15
|
+
domainIds: [1],
|
|
16
|
+
};
|
|
17
|
+
const variablesEmptyDomains = { scope: "foo" };
|
|
18
|
+
const props = {
|
|
19
|
+
...variables,
|
|
20
|
+
onChange: jest.fn(),
|
|
21
|
+
onNameChange: jest.fn(),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
describe("<SelectableDynamicForm />", () => {
|
|
25
|
+
describe("with multiple templates", () => {
|
|
26
|
+
const renderOpts = {
|
|
27
|
+
mocks: [multipleTemplatesMock(variables)],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
it("matches the latest snapshot", async () => {
|
|
31
|
+
const { container, queryByText } = render(
|
|
32
|
+
<SelectableDynamicForm {...props} />,
|
|
33
|
+
renderOpts
|
|
34
|
+
);
|
|
35
|
+
await waitForElementToBeRemoved(() => queryByText(/loading/i));
|
|
36
|
+
expect(container).toMatchSnapshot();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("matches the latest snapshot for error query", async () => {
|
|
40
|
+
const renderOpts = {
|
|
41
|
+
mocks: [errorTemplateMock(variables)],
|
|
42
|
+
};
|
|
43
|
+
const { container, queryByText } = render(
|
|
44
|
+
<SelectableDynamicForm {...props} />,
|
|
45
|
+
renderOpts
|
|
46
|
+
);
|
|
47
|
+
await waitForElementToBeRemoved(() => queryByText(/loading/i));
|
|
48
|
+
expect(container).toMatchSnapshot();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("calls onNameChange and onChange functions when selecting template", async () => {
|
|
52
|
+
const onChange = jest.fn();
|
|
53
|
+
const onNameChange = jest.fn();
|
|
54
|
+
const thisProps = {
|
|
55
|
+
...props,
|
|
56
|
+
onChange,
|
|
57
|
+
onNameChange,
|
|
58
|
+
};
|
|
59
|
+
const { container, findByText, queryByText } = render(
|
|
60
|
+
<SelectableDynamicForm {...thisProps} />,
|
|
61
|
+
renderOpts
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
await waitForElementToBeRemoved(() => queryByText(/loading/i));
|
|
65
|
+
userEvent.click(await findByText("template1"));
|
|
66
|
+
|
|
67
|
+
expect(onChange).toHaveBeenCalledWith({ content: {}, valid: [] });
|
|
68
|
+
expect(onNameChange).toHaveBeenCalledWith("template1");
|
|
69
|
+
|
|
70
|
+
const input = container.querySelector('[name="field1"]');
|
|
71
|
+
userEvent.type(input, "A");
|
|
72
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
73
|
+
content: { field1: "A" },
|
|
74
|
+
valid: [],
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("with a single template", () => {
|
|
80
|
+
const renderOpts = {
|
|
81
|
+
mocks: [singleTemplateMock(variables)],
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
it("matches the latest snapshot", async () => {
|
|
85
|
+
const { container, queryByText } = render(
|
|
86
|
+
<SelectableDynamicForm {...props} />,
|
|
87
|
+
renderOpts
|
|
88
|
+
);
|
|
89
|
+
await waitForElementToBeRemoved(() => queryByText(/loading/i));
|
|
90
|
+
expect(queryByText("template1")).not.toBeInTheDocument();
|
|
91
|
+
expect(container).toMatchSnapshot();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("calls onNameChange function to select the unique template", async () => {
|
|
95
|
+
const onNameChange = jest.fn();
|
|
96
|
+
const thisProps = {
|
|
97
|
+
...props,
|
|
98
|
+
onNameChange,
|
|
99
|
+
};
|
|
100
|
+
render(<SelectableDynamicForm {...thisProps} />, renderOpts);
|
|
101
|
+
await waitFor(() =>
|
|
102
|
+
expect(onNameChange).toHaveBeenCalledWith("template1")
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("with no domains", () => {
|
|
108
|
+
const renderOpts = {
|
|
109
|
+
mocks: [multipleTemplatesMock(variablesEmptyDomains)],
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
it("matches the latest snapshot", async () => {
|
|
113
|
+
const { container, queryByText } = render(
|
|
114
|
+
<SelectableDynamicForm {...props} />,
|
|
115
|
+
renderOpts
|
|
116
|
+
);
|
|
117
|
+
await waitForElementToBeRemoved(() => queryByText(/loading/i));
|
|
118
|
+
expect(queryByText("template1")).not.toBeInTheDocument();
|
|
119
|
+
expect(container).toMatchSnapshot();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<SelectableDynamicForm /> with a single template matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui segment"
|
|
7
|
+
>
|
|
8
|
+
<h4
|
|
9
|
+
class="ui header"
|
|
10
|
+
>
|
|
11
|
+
g1
|
|
12
|
+
</h4>
|
|
13
|
+
<div
|
|
14
|
+
class="field"
|
|
15
|
+
>
|
|
16
|
+
<label>
|
|
17
|
+
field1
|
|
18
|
+
</label>
|
|
19
|
+
<div
|
|
20
|
+
class="field"
|
|
21
|
+
>
|
|
22
|
+
<div
|
|
23
|
+
class="ui input"
|
|
24
|
+
>
|
|
25
|
+
<input
|
|
26
|
+
name="field1"
|
|
27
|
+
type="text"
|
|
28
|
+
value=""
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
exports[`<SelectableDynamicForm /> with multiple templates matches the latest snapshot 1`] = `
|
|
38
|
+
<div>
|
|
39
|
+
<div
|
|
40
|
+
class="field"
|
|
41
|
+
>
|
|
42
|
+
<label>
|
|
43
|
+
Template
|
|
44
|
+
</label>
|
|
45
|
+
<div
|
|
46
|
+
class="field"
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
aria-busy="false"
|
|
50
|
+
aria-expanded="false"
|
|
51
|
+
class="ui search selection dropdown"
|
|
52
|
+
name="template"
|
|
53
|
+
role="combobox"
|
|
54
|
+
>
|
|
55
|
+
<input
|
|
56
|
+
aria-autocomplete="list"
|
|
57
|
+
autocomplete="off"
|
|
58
|
+
class="search"
|
|
59
|
+
tabindex="0"
|
|
60
|
+
type="text"
|
|
61
|
+
value=""
|
|
62
|
+
/>
|
|
63
|
+
<div
|
|
64
|
+
aria-atomic="true"
|
|
65
|
+
aria-live="polite"
|
|
66
|
+
class="divider default text"
|
|
67
|
+
role="alert"
|
|
68
|
+
>
|
|
69
|
+
Select a template...
|
|
70
|
+
</div>
|
|
71
|
+
<i
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
class="dropdown icon"
|
|
74
|
+
/>
|
|
75
|
+
<div
|
|
76
|
+
class="menu transition"
|
|
77
|
+
role="listbox"
|
|
78
|
+
>
|
|
79
|
+
<div
|
|
80
|
+
aria-checked="false"
|
|
81
|
+
aria-selected="true"
|
|
82
|
+
class="selected item"
|
|
83
|
+
role="option"
|
|
84
|
+
style="pointer-events: all;"
|
|
85
|
+
>
|
|
86
|
+
<span
|
|
87
|
+
class="text"
|
|
88
|
+
>
|
|
89
|
+
template1
|
|
90
|
+
</span>
|
|
91
|
+
</div>
|
|
92
|
+
<div
|
|
93
|
+
aria-checked="false"
|
|
94
|
+
aria-selected="false"
|
|
95
|
+
class="item"
|
|
96
|
+
role="option"
|
|
97
|
+
style="pointer-events: all;"
|
|
98
|
+
>
|
|
99
|
+
<span
|
|
100
|
+
class="text"
|
|
101
|
+
>
|
|
102
|
+
template2
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
`;
|
|
111
|
+
|
|
112
|
+
exports[`<SelectableDynamicForm /> with multiple templates matches the latest snapshot for error query 1`] = `<div />`;
|
|
113
|
+
|
|
114
|
+
exports[`<SelectableDynamicForm /> with no domains matches the latest snapshot 1`] = `<div />`;
|
|
@@ -3,25 +3,23 @@ import PropTypes from "prop-types";
|
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { DateTimeInput } from "semantic-ui-calendar-react";
|
|
5
5
|
|
|
6
|
-
export const DateTimeField = ({ field: { name, value }, onChange }) =>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
);
|
|
20
|
-
};
|
|
6
|
+
export const DateTimeField = ({ field: { name, value } = {}, onChange }) => (
|
|
7
|
+
<DateTimeInput
|
|
8
|
+
animation=""
|
|
9
|
+
closable
|
|
10
|
+
dateTimeFormat={"YYYY-MM-DD HH:mm"}
|
|
11
|
+
duration={0}
|
|
12
|
+
iconPosition="left"
|
|
13
|
+
name={name}
|
|
14
|
+
onChange={(e, { value }) => onChange(e, { name: name, value: value })}
|
|
15
|
+
placeholder="Date Time"
|
|
16
|
+
value={value || ""}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
21
19
|
|
|
22
20
|
DateTimeField.propTypes = {
|
|
23
21
|
field: PropTypes.object,
|
|
24
|
-
onChange: PropTypes.func
|
|
22
|
+
onChange: PropTypes.func,
|
|
25
23
|
};
|
|
26
24
|
|
|
27
25
|
export default DateTimeField;
|
package/src/messages/en.js
CHANGED
|
@@ -137,7 +137,7 @@ export default {
|
|
|
137
137
|
"template.error.content.invalidtype":
|
|
138
138
|
"Field {name} with type {type} already exists in another template",
|
|
139
139
|
"widget.image.error.too_big":
|
|
140
|
-
"
|
|
140
|
+
"File is too big. Must be smaller then {size_mb}MB",
|
|
141
141
|
"widget.image.error.invalidtype": "Invalid File",
|
|
142
142
|
"widget.copy.modaltitle": "Copy fields",
|
|
143
143
|
"widget.copy.error.jsonformat": "Error: copy has wrong format",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { selectedDomains } from "../../reducers";
|
|
2
|
+
import { selectDomains, clearSelectedDomains } from "../../routines";
|
|
3
|
+
|
|
4
|
+
const fooState = [{ foo: "bar" }];
|
|
5
|
+
|
|
6
|
+
describe("reducers: selectedDomains", () => {
|
|
7
|
+
const initialState = [];
|
|
8
|
+
|
|
9
|
+
it("should provide the initial state", () => {
|
|
10
|
+
expect(selectedDomains(undefined, {})).toEqual(initialState);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should handle the clearSelectedDomains.TRIGGER action", () => {
|
|
14
|
+
expect(
|
|
15
|
+
selectedDomains(fooState, { type: clearSelectedDomains.TRIGGER })
|
|
16
|
+
).toEqual(initialState);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should handle the selectDomains.TRIGGER action", () => {
|
|
20
|
+
const id = 1;
|
|
21
|
+
const payload = [{ id }];
|
|
22
|
+
expect(
|
|
23
|
+
selectedDomains(fooState, { type: selectDomains.TRIGGER, payload })
|
|
24
|
+
).toEqual(payload);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should ignore unknown actions", () => {
|
|
28
|
+
expect(selectedDomains(fooState, { type: "FOO" })).toBe(fooState);
|
|
29
|
+
});
|
|
30
|
+
});
|
package/src/reducers/index.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { selectDomains, clearSelectedDomains } from "../routines";
|
|
3
|
+
|
|
4
|
+
const initialState = [];
|
|
5
|
+
|
|
6
|
+
export const selectedDomains = (state = initialState, { type, payload }) => {
|
|
7
|
+
switch (type) {
|
|
8
|
+
case selectDomains.TRIGGER:
|
|
9
|
+
return payload;
|
|
10
|
+
case clearSelectedDomains.TRIGGER:
|
|
11
|
+
return initialState;
|
|
12
|
+
default:
|
|
13
|
+
return state;
|
|
14
|
+
}
|
|
15
|
+
};
|
package/src/routines.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createRoutine } from "redux-saga-routines";
|
|
2
2
|
|
|
3
|
-
export const selectDomain = createRoutine("SELECT_DOMAIN");
|
|
4
3
|
export const clearSelectedDomain = createRoutine("CLEAR_SELECTED_DOMAIN");
|
|
4
|
+
export const clearSelectedDomains = createRoutine("CLEAR_SELECTED_DOMAINS");
|
|
5
|
+
export const selectDomain = createRoutine("SELECT_DOMAIN");
|
|
6
|
+
export const selectDomains = createRoutine("SELECT_DOMAINS");
|
|
5
7
|
export * from "./templates/routines";
|
|
@@ -1,42 +1,31 @@
|
|
|
1
1
|
import { makeGetApplyTemplate, makeGetTemplate } from "..";
|
|
2
2
|
|
|
3
|
+
const templateFromProps = { id: 1 };
|
|
4
|
+
const templateFromState = { id: 2 };
|
|
5
|
+
|
|
3
6
|
describe("selectors: makeGetTemplate", () => {
|
|
4
|
-
const templateFromProps = { id: 1 };
|
|
5
|
-
const templateFromState = { id: 2 };
|
|
6
7
|
const props = { template: templateFromProps };
|
|
7
8
|
const state = { template: templateFromState };
|
|
8
9
|
it("template from props prevails to template from state", () => {
|
|
9
10
|
const getTemplate = makeGetTemplate();
|
|
10
11
|
const t = getTemplate(state, props);
|
|
11
|
-
expect(t).
|
|
12
|
+
expect(t).toBe(templateFromProps);
|
|
12
13
|
});
|
|
13
14
|
|
|
14
15
|
it("gets template from state if not props provided", () => {
|
|
15
16
|
const getTemplate = makeGetTemplate();
|
|
16
17
|
const t = getTemplate(state, {});
|
|
17
|
-
expect(t).
|
|
18
|
+
expect(t).toBe(templateFromState);
|
|
18
19
|
});
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
describe("selectors: makeGetApplyTemplate", () => {
|
|
22
|
-
const applyTemplate = jest.fn();
|
|
23
|
-
const templateFromProps = { id: 1 };
|
|
24
|
-
const templateFromState = { id: 2 };
|
|
25
|
-
|
|
26
23
|
it("applyTemplate from props prevails to applyTemplate from utils", () => {
|
|
24
|
+
const applyTemplate = jest.fn();
|
|
27
25
|
const props = { template: templateFromProps, applyTemplate };
|
|
28
26
|
const state = { template: templateFromState };
|
|
29
27
|
const getApplyTemplate = makeGetApplyTemplate();
|
|
30
28
|
getApplyTemplate(state, props);
|
|
31
29
|
expect(applyTemplate).toHaveBeenCalledTimes(1);
|
|
32
30
|
});
|
|
33
|
-
|
|
34
|
-
it("get applyTemplate utils when not applyTemplate provided in props", () => {
|
|
35
|
-
const props = { template: templateFromProps };
|
|
36
|
-
const state = { template: templateFromState };
|
|
37
|
-
applyTemplate.mockClear();
|
|
38
|
-
const getApplyTemplate = makeGetApplyTemplate();
|
|
39
|
-
getApplyTemplate(state, props);
|
|
40
|
-
expect(applyTemplate).toHaveBeenCalledTimes(0);
|
|
41
|
-
});
|
|
42
31
|
});
|
|
@@ -8,6 +8,7 @@ const applyTemplateFromProps = (_state, props) =>
|
|
|
8
8
|
_.prop("applyTemplate")(props);
|
|
9
9
|
const selectTemplate = (propTemplate, stateTemplate) =>
|
|
10
10
|
_.defaultTo(stateTemplate)(propTemplate);
|
|
11
|
+
|
|
11
12
|
const selectApplyTemplate = (
|
|
12
13
|
propTemplate,
|
|
13
14
|
stateTemplate,
|
|
@@ -17,21 +18,13 @@ const selectApplyTemplate = (
|
|
|
17
18
|
return _.defaultTo(applyTemplate)(applyTemplateFromProps)(template);
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
export const makeGetTemplate = () =>
|
|
21
|
-
|
|
22
|
-
templateFromProps,
|
|
23
|
-
templateFromState,
|
|
24
|
-
selectTemplate
|
|
25
|
-
);
|
|
26
|
-
return getTemplate;
|
|
27
|
-
};
|
|
21
|
+
export const makeGetTemplate = () =>
|
|
22
|
+
createSelector(templateFromProps, templateFromState, selectTemplate);
|
|
28
23
|
|
|
29
|
-
export const makeGetApplyTemplate = () =>
|
|
30
|
-
|
|
24
|
+
export const makeGetApplyTemplate = () =>
|
|
25
|
+
createSelector(
|
|
31
26
|
templateFromProps,
|
|
32
27
|
templateFromState,
|
|
33
28
|
applyTemplateFromProps,
|
|
34
29
|
selectApplyTemplate
|
|
35
30
|
);
|
|
36
|
-
return getApplyTemplate;
|
|
37
|
-
};
|
|
@@ -6,49 +6,61 @@ import { connect } from "react-redux";
|
|
|
6
6
|
import { withRouter } from "react-router-dom";
|
|
7
7
|
import { Loading } from "@truedat/core/components";
|
|
8
8
|
import { clearTemplate, fetchTemplate } from "../routines";
|
|
9
|
-
import { clearSelectedDomain } from "../../routines";
|
|
9
|
+
import { clearSelectedDomain, clearSelectedDomains } from "../../routines";
|
|
10
10
|
|
|
11
11
|
export class TemplateLoader extends React.Component {
|
|
12
12
|
static propTypes = {
|
|
13
|
-
clearTemplate: PropTypes.func,
|
|
14
|
-
fetchTemplate: PropTypes.func,
|
|
15
13
|
clearSelectedDomain: PropTypes.func,
|
|
14
|
+
clearSelectedDomains: PropTypes.func,
|
|
15
|
+
clearTemplate: PropTypes.func,
|
|
16
16
|
domainId: PropTypes.number,
|
|
17
|
+
domainIds: PropTypes.array,
|
|
18
|
+
fetchTemplate: PropTypes.func,
|
|
19
|
+
match: PropTypes.object.isRequired,
|
|
17
20
|
templateId: PropTypes.number,
|
|
18
21
|
templateLoading: PropTypes.bool,
|
|
19
|
-
match: PropTypes.object.isRequired,
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
componentDidMount() {
|
|
23
|
-
const { fetchTemplate, domainId, templateId, match } =
|
|
24
|
-
|
|
25
|
+
const { fetchTemplate, domainId, domainIds, templateId, match } =
|
|
26
|
+
this.props;
|
|
27
|
+
if (match?.params?.templateId) {
|
|
25
28
|
fetchTemplate({ templateId: match.params.templateId });
|
|
26
29
|
} else if (templateId) {
|
|
27
|
-
fetchTemplate({ domainId, templateId });
|
|
30
|
+
fetchTemplate({ domainId, domainIds, templateId });
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
componentDidUpdate(prevProps) {
|
|
32
|
-
const {
|
|
33
|
-
|
|
35
|
+
const {
|
|
36
|
+
clearTemplate,
|
|
37
|
+
domainId,
|
|
38
|
+
domainIds,
|
|
39
|
+
fetchTemplate,
|
|
40
|
+
match,
|
|
41
|
+
templateId,
|
|
42
|
+
} = this.props;
|
|
34
43
|
const {
|
|
35
44
|
domainId: prevDomainId,
|
|
36
|
-
|
|
45
|
+
domainIds: prevDomainIds,
|
|
37
46
|
match: prevMatch,
|
|
47
|
+
templateId: prevTemplateId,
|
|
38
48
|
} = prevProps;
|
|
39
|
-
if (match
|
|
40
|
-
if (
|
|
41
|
-
prevMatch.params &&
|
|
42
|
-
match.params.templateId === prevMatch.params.templateId
|
|
43
|
-
) {
|
|
49
|
+
if (match?.params?.templateId) {
|
|
50
|
+
if (match.params.templateId === prevMatch?.params?.templateId) {
|
|
44
51
|
// No change
|
|
45
52
|
} else {
|
|
46
53
|
fetchTemplate({ templateId: match.params.templateId });
|
|
47
54
|
}
|
|
48
55
|
} else if (templateId) {
|
|
49
|
-
if (
|
|
56
|
+
if (
|
|
57
|
+
domainId !== prevDomainId ||
|
|
58
|
+
domainIds !== prevDomainIds ||
|
|
59
|
+
templateId !== prevTemplateId
|
|
60
|
+
) {
|
|
50
61
|
fetchTemplate({
|
|
51
62
|
domainId,
|
|
63
|
+
domainIds,
|
|
52
64
|
templateId,
|
|
53
65
|
});
|
|
54
66
|
}
|
|
@@ -58,9 +70,11 @@ export class TemplateLoader extends React.Component {
|
|
|
58
70
|
}
|
|
59
71
|
|
|
60
72
|
componentWillUnmount() {
|
|
61
|
-
const { clearTemplate, clearSelectedDomain } =
|
|
73
|
+
const { clearTemplate, clearSelectedDomain, clearSelectedDomains } =
|
|
74
|
+
this.props;
|
|
62
75
|
clearTemplate();
|
|
63
76
|
clearSelectedDomain();
|
|
77
|
+
clearSelectedDomains();
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
render() {
|
|
@@ -75,19 +89,22 @@ export class TemplateLoader extends React.Component {
|
|
|
75
89
|
|
|
76
90
|
const mapStateToProps = ({
|
|
77
91
|
selectedDomain,
|
|
92
|
+
selectedDomains,
|
|
78
93
|
selectedTemplate,
|
|
79
94
|
templateLoading,
|
|
80
95
|
}) => ({
|
|
81
|
-
templateLoading,
|
|
82
96
|
domainId: _.prop("id")(selectedDomain),
|
|
97
|
+
domainIds: selectedDomains,
|
|
83
98
|
templateId: _.prop("id")(selectedTemplate),
|
|
99
|
+
templateLoading,
|
|
84
100
|
});
|
|
85
101
|
|
|
86
102
|
export default compose(
|
|
87
103
|
withRouter,
|
|
88
104
|
connect(mapStateToProps, {
|
|
105
|
+
clearSelectedDomain,
|
|
106
|
+
clearSelectedDomains,
|
|
89
107
|
clearTemplate,
|
|
90
108
|
fetchTemplate,
|
|
91
|
-
clearSelectedDomain,
|
|
92
109
|
})
|
|
93
110
|
)(TemplateLoader);
|
|
@@ -7,16 +7,18 @@ describe("<TemplateLoader />", () => {
|
|
|
7
7
|
const clearTemplate = jest.fn();
|
|
8
8
|
const fetchTemplate = jest.fn();
|
|
9
9
|
const clearSelectedDomain = jest.fn();
|
|
10
|
+
const clearSelectedDomains = jest.fn();
|
|
10
11
|
const match = {};
|
|
11
12
|
|
|
12
13
|
it("matches the latest snapshot", () => {
|
|
13
14
|
const templateLoading = true;
|
|
14
15
|
const props = {
|
|
15
16
|
clearSelectedDomain,
|
|
17
|
+
clearSelectedDomains,
|
|
16
18
|
clearTemplate,
|
|
17
19
|
fetchTemplate,
|
|
18
20
|
templateLoading,
|
|
19
|
-
match
|
|
21
|
+
match,
|
|
20
22
|
};
|
|
21
23
|
const wrapper = shallow(<TemplateLoader {...props} />);
|
|
22
24
|
expect(wrapper).toMatchSnapshot();
|
|
@@ -26,10 +28,11 @@ describe("<TemplateLoader />", () => {
|
|
|
26
28
|
const templateLoading = true;
|
|
27
29
|
const props = {
|
|
28
30
|
clearSelectedDomain,
|
|
31
|
+
clearSelectedDomains,
|
|
29
32
|
clearTemplate,
|
|
30
33
|
fetchTemplate,
|
|
31
34
|
templateLoading,
|
|
32
|
-
match
|
|
35
|
+
match,
|
|
33
36
|
};
|
|
34
37
|
const wrapper = shallow(<TemplateLoader {...props} />);
|
|
35
38
|
expect(wrapper.find("Loading").length).toBe(1);
|
|
@@ -39,10 +42,11 @@ describe("<TemplateLoader />", () => {
|
|
|
39
42
|
const templateLoading = false;
|
|
40
43
|
const props = {
|
|
41
44
|
clearSelectedDomain,
|
|
45
|
+
clearSelectedDomains,
|
|
42
46
|
clearTemplate,
|
|
43
47
|
fetchTemplate,
|
|
44
48
|
templateLoading,
|
|
45
|
-
match
|
|
49
|
+
match,
|
|
46
50
|
};
|
|
47
51
|
const wrapper = shallow(<TemplateLoader {...props} />);
|
|
48
52
|
expect(wrapper.getElement()).toBeNull();
|
|
@@ -52,17 +56,19 @@ describe("<TemplateLoader />", () => {
|
|
|
52
56
|
const fetchTemplate = jest.fn();
|
|
53
57
|
const clearTemplate = jest.fn();
|
|
54
58
|
const clearSelectedDomain = jest.fn();
|
|
59
|
+
const clearSelectedDomains = jest.fn();
|
|
55
60
|
const templateLoading = false;
|
|
56
61
|
const domainId = 1;
|
|
57
62
|
const templateId = 2;
|
|
58
63
|
const props = {
|
|
59
64
|
clearSelectedDomain,
|
|
65
|
+
clearSelectedDomains,
|
|
60
66
|
fetchTemplate,
|
|
61
67
|
templateLoading,
|
|
62
68
|
clearTemplate,
|
|
63
69
|
domainId,
|
|
64
70
|
templateId,
|
|
65
|
-
match
|
|
71
|
+
match,
|
|
66
72
|
};
|
|
67
73
|
jest.spyOn(TemplateLoader.prototype, "componentDidMount");
|
|
68
74
|
const wrapper = shallow(<TemplateLoader {...props} />);
|
|
@@ -72,13 +78,15 @@ describe("<TemplateLoader />", () => {
|
|
|
72
78
|
expect(clearTemplate.mock.calls.length).toBe(0);
|
|
73
79
|
expect(fetchTemplate.mock.calls.length).toBe(1);
|
|
74
80
|
expect(fetchTemplate.mock.calls[0]).toEqual([
|
|
75
|
-
{ domainId: 1, templateId: 2 }
|
|
81
|
+
{ domainId: 1, templateId: 2 },
|
|
76
82
|
]);
|
|
77
83
|
expect(clearSelectedDomain.mock.calls.length).toBe(0);
|
|
84
|
+
expect(clearSelectedDomains.mock.calls.length).toBe(0);
|
|
78
85
|
wrapper.unmount();
|
|
79
86
|
expect(clearTemplate.mock.calls.length).toBe(1);
|
|
80
87
|
expect(fetchTemplate.mock.calls.length).toBe(1);
|
|
81
88
|
expect(clearSelectedDomain.mock.calls.length).toBe(1);
|
|
89
|
+
expect(clearSelectedDomains.mock.calls.length).toBe(1);
|
|
82
90
|
});
|
|
83
91
|
|
|
84
92
|
it("calls fetchTemplate when component updates, clearTemplate when component receives null templateId", () => {
|
|
@@ -93,7 +101,7 @@ describe("<TemplateLoader />", () => {
|
|
|
93
101
|
clearTemplate,
|
|
94
102
|
domainId,
|
|
95
103
|
templateId,
|
|
96
|
-
match
|
|
104
|
+
match,
|
|
97
105
|
};
|
|
98
106
|
const newProps = { ...props, templateId: 3 };
|
|
99
107
|
const newNullProps = { ...props, templateId: null };
|
|
@@ -109,7 +117,7 @@ describe("<TemplateLoader />", () => {
|
|
|
109
117
|
expect(clearTemplate.mock.calls.length).toBe(0);
|
|
110
118
|
expect(fetchTemplate.mock.calls.length).toBe(2);
|
|
111
119
|
expect(fetchTemplate.mock.calls[1]).toEqual([
|
|
112
|
-
{ domainId: 1, templateId: 3 }
|
|
120
|
+
{ domainId: 1, templateId: 3 },
|
|
113
121
|
]);
|
|
114
122
|
|
|
115
123
|
ReactDOM.render(<TemplateLoader {...newNullProps} />, node);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import TemplateLoader from "./TemplateLoader";
|
|
2
2
|
import TemplateRoutes from "./TemplateRoutes";
|
|
3
|
-
import TemplateSelector from "./TemplateSelector";
|
|
4
3
|
import TemplatesLoader from "./TemplatesLoader";
|
|
5
4
|
|
|
6
|
-
export { TemplateLoader, TemplateRoutes,
|
|
5
|
+
export { TemplateLoader, TemplateRoutes, TemplatesLoader };
|
|
@@ -8,10 +8,14 @@ const toApiPath = compile(API_TEMPLATE);
|
|
|
8
8
|
|
|
9
9
|
export function* fetchTemplateSaga({ payload }) {
|
|
10
10
|
try {
|
|
11
|
-
const { templateId, domainId } = payload;
|
|
11
|
+
const { templateId, domainId, domainIds } = payload;
|
|
12
|
+
const domainIdsString = domainIds ? domainIds.join(",") : undefined;
|
|
12
13
|
const url = toApiPath({ id: templateId });
|
|
13
14
|
yield put(fetchTemplate.request());
|
|
14
|
-
const json_opts = {
|
|
15
|
+
const json_opts = {
|
|
16
|
+
...JSON_OPTS,
|
|
17
|
+
params: { domain_id: domainId, domain_ids: domainIdsString },
|
|
18
|
+
};
|
|
15
19
|
const { data } = yield call(apiJson, url, json_opts);
|
|
16
20
|
yield put(fetchTemplate.success({ ...data }));
|
|
17
21
|
} catch (error) {
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import PropTypes from "prop-types";
|
|
4
|
-
import { connect } from "react-redux";
|
|
5
|
-
import { Form, Label } from "semantic-ui-react";
|
|
6
|
-
import { useIntl, FormattedMessage } from "react-intl";
|
|
7
|
-
|
|
8
|
-
export const TemplateSelector = ({
|
|
9
|
-
hidden,
|
|
10
|
-
onChange,
|
|
11
|
-
options,
|
|
12
|
-
selectedValue,
|
|
13
|
-
isOptional,
|
|
14
|
-
}) => {
|
|
15
|
-
const { formatMessage } = useIntl();
|
|
16
|
-
const required = !isOptional;
|
|
17
|
-
|
|
18
|
-
return hidden ? null : (
|
|
19
|
-
<Form.Field required={required}>
|
|
20
|
-
<label>
|
|
21
|
-
<FormattedMessage id="template.selector.label" />
|
|
22
|
-
{!selectedValue && required ? (
|
|
23
|
-
<Label pointing="left">
|
|
24
|
-
<FormattedMessage id="template.form.validation.empty_required" />
|
|
25
|
-
</Label>
|
|
26
|
-
) : null}
|
|
27
|
-
</label>
|
|
28
|
-
<Form.Dropdown
|
|
29
|
-
placeholder={formatMessage({ id: "template.selector.placeholder" })}
|
|
30
|
-
name="template"
|
|
31
|
-
search
|
|
32
|
-
selection
|
|
33
|
-
clearable={isOptional}
|
|
34
|
-
options={options}
|
|
35
|
-
onChange={onChange}
|
|
36
|
-
value={selectedValue || null}
|
|
37
|
-
/>
|
|
38
|
-
</Form.Field>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
TemplateSelector.propTypes = {
|
|
43
|
-
onChange: PropTypes.func,
|
|
44
|
-
options: PropTypes.array,
|
|
45
|
-
hidden: PropTypes.bool,
|
|
46
|
-
selectedValue: PropTypes.number,
|
|
47
|
-
isOptional: PropTypes.bool,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const mapStateToProps = ({ templates }) => ({
|
|
51
|
-
hidden: _.size(templates) <= 1,
|
|
52
|
-
options: _.map(({ label: text, id: value }, i) => ({
|
|
53
|
-
key: i,
|
|
54
|
-
value,
|
|
55
|
-
text,
|
|
56
|
-
}))(templates),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
export default connect(mapStateToProps)(TemplateSelector);
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { shallow } from "enzyme";
|
|
3
|
-
import { intl } from "@truedat/test/intl-stub";
|
|
4
|
-
import { TemplateSelector } from "../TemplateSelector";
|
|
5
|
-
|
|
6
|
-
// workaround for enzyme issue with React.useContext
|
|
7
|
-
// see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
|
|
8
|
-
jest.spyOn(React, "useContext").mockImplementation(() => intl);
|
|
9
|
-
|
|
10
|
-
describe("<TemplateSelector />", () => {
|
|
11
|
-
const options = [0, 1, 2, 3].map(v => ({
|
|
12
|
-
key: v,
|
|
13
|
-
value: v,
|
|
14
|
-
text: `text ${v}`
|
|
15
|
-
}));
|
|
16
|
-
const onChange = jest.fn();
|
|
17
|
-
const props = { options, onChange };
|
|
18
|
-
|
|
19
|
-
it("matches the latest snapshot", () => {
|
|
20
|
-
const wrapper = shallow(<TemplateSelector {...props} />);
|
|
21
|
-
expect(wrapper).toMatchSnapshot();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("renders null if hidden is true", () => {
|
|
25
|
-
const props = { options, onChange, hidden: true };
|
|
26
|
-
const wrapper = shallow(<TemplateSelector {...props} />);
|
|
27
|
-
expect(wrapper.getElement()).toBeNull();
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`<TemplateSelector /> matches the latest snapshot 1`] = `
|
|
4
|
-
<FormField
|
|
5
|
-
required={true}
|
|
6
|
-
>
|
|
7
|
-
<label>
|
|
8
|
-
<MemoizedFormattedMessage
|
|
9
|
-
id="template.selector.label"
|
|
10
|
-
/>
|
|
11
|
-
<Label
|
|
12
|
-
pointing="left"
|
|
13
|
-
>
|
|
14
|
-
<MemoizedFormattedMessage
|
|
15
|
-
id="template.form.validation.empty_required"
|
|
16
|
-
/>
|
|
17
|
-
</Label>
|
|
18
|
-
</label>
|
|
19
|
-
<FormDropdown
|
|
20
|
-
as={[Function]}
|
|
21
|
-
control={[Function]}
|
|
22
|
-
name="template"
|
|
23
|
-
onChange={[MockFunction]}
|
|
24
|
-
options={
|
|
25
|
-
Array [
|
|
26
|
-
Object {
|
|
27
|
-
"key": 0,
|
|
28
|
-
"text": "text 0",
|
|
29
|
-
"value": 0,
|
|
30
|
-
},
|
|
31
|
-
Object {
|
|
32
|
-
"key": 1,
|
|
33
|
-
"text": "text 1",
|
|
34
|
-
"value": 1,
|
|
35
|
-
},
|
|
36
|
-
Object {
|
|
37
|
-
"key": 2,
|
|
38
|
-
"text": "text 2",
|
|
39
|
-
"value": 2,
|
|
40
|
-
},
|
|
41
|
-
Object {
|
|
42
|
-
"key": 3,
|
|
43
|
-
"text": "text 3",
|
|
44
|
-
"value": 3,
|
|
45
|
-
},
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
placeholder="template.selector.placeholder"
|
|
49
|
-
search={true}
|
|
50
|
-
selection={true}
|
|
51
|
-
value={null}
|
|
52
|
-
/>
|
|
53
|
-
</FormField>
|
|
54
|
-
`;
|