@truedat/df 7.12.5 → 7.12.7
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/package.json +4 -4
- package/src/components/DynamicFieldValue.js +1 -1
- package/src/components/DynamicFormViewer.js +4 -3
- package/src/components/DynamicFormWithTranslations.js +3 -3
- package/src/components/EditableDynamicFieldValue.js +1 -1
- package/src/components/FieldViewerValue.js +44 -3
- package/src/components/__tests__/FieldViewerValue.spec.js +10 -6
- package/src/components/__tests__/__snapshots__/FieldViewerValue.spec.js.snap +53 -0
- package/src/components/widgets/DynamicField.js +6 -62
- package/src/components/widgets/DynamicTableField.js +150 -0
- package/src/components/widgets/FieldByWidget.js +63 -0
- package/src/components/widgets/StandardDropdown.js +2 -2
- package/src/components/widgets/StringField.js +2 -1
- package/src/components/widgets/__tests__/DynamicField.spec.js +10 -1
- package/src/components/widgets/__tests__/DynamicTableField.spec.js +257 -0
- package/src/components/widgets/__tests__/__snapshots__/DynamicField.spec.js.snap +97 -0
- package/src/components/widgets/__tests__/__snapshots__/DynamicTableField.spec.js.snap +114 -0
- package/src/templates/components/templateForm/ActiveGroupForm.js +5 -16
- package/src/templates/components/templateForm/FieldDefinition.js +158 -0
- package/src/templates/components/templateForm/FieldForm.js +32 -135
- package/src/templates/components/templateForm/TableValuesForm.js +258 -0
- package/src/templates/components/templateForm/TemplateForm.js +43 -26
- package/src/templates/components/templateForm/ValuesConfiguration.js +67 -0
- package/src/templates/components/templateForm/ValuesField.js +60 -96
- package/src/templates/components/templateForm/ValuesListForm.js +1 -3
- package/src/templates/components/templateForm/__tests__/FieldDefinition.spec.js +227 -0
- package/src/templates/components/templateForm/__tests__/TableValuesForm.spec.js +215 -0
- package/src/templates/components/templateForm/__tests__/ValuesField.spec.js +28 -83
- package/src/templates/components/templateForm/__tests__/__snapshots__/ActiveGroupForm.spec.js.snap +17 -0
- package/src/templates/components/templateForm/__tests__/__snapshots__/FieldDefinition.spec.js.snap +443 -0
- package/src/templates/components/templateForm/__tests__/__snapshots__/FieldForm.spec.js.snap +51 -0
- package/src/templates/components/templateForm/__tests__/__snapshots__/TemplateForm.spec.js.snap +17 -0
- package/src/templates/components/templateForm/__tests__/__snapshots__/ValuesField.spec.js.snap +61 -387
- package/src/templates/components/templateForm/contentValidation.js +22 -3
- package/src/templates/components/templateForm/valueDefinitions.js +16 -2
- package/src/templates/components/templateForm/widgetDefinitions.js +28 -2
- package/src/templates/utils/__tests__/validateContent.spec.js +6 -6
- package/src/templates/utils/applyTemplate.js +72 -23
- package/src/templates/utils/filterValues.js +3 -3
- package/src/templates/utils/parseFieldOptions.js +73 -58
- package/src/templates/utils/parseGroups.js +47 -48
- package/src/templates/utils/validateContent.js +70 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/df",
|
|
3
|
-
"version": "7.12.
|
|
3
|
+
"version": "7.12.7",
|
|
4
4
|
"description": "Truedat Web Data Quality Module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -48,14 +48,14 @@
|
|
|
48
48
|
"@testing-library/jest-dom": "^6.6.3",
|
|
49
49
|
"@testing-library/react": "^16.3.0",
|
|
50
50
|
"@testing-library/user-event": "^14.6.1",
|
|
51
|
-
"@truedat/test": "7.12.
|
|
51
|
+
"@truedat/test": "7.12.7",
|
|
52
52
|
"identity-obj-proxy": "^3.0.0",
|
|
53
53
|
"jest": "^29.7.0",
|
|
54
54
|
"redux-saga-test-plan": "^4.0.6"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@apollo/client": "^3.13.8",
|
|
58
|
-
"@truedat/core": "7.12.
|
|
58
|
+
"@truedat/core": "7.12.7",
|
|
59
59
|
"axios": "^1.12.0",
|
|
60
60
|
"graphql": "^16.11.0",
|
|
61
61
|
"is-hotkey": "^0.2.0",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"semantic-ui-react": "^3.0.0-beta.2",
|
|
85
85
|
"swr": "^2.3.3"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "4fcf1a96c366bb13af6261808308a00cc7480a82"
|
|
88
88
|
}
|
|
@@ -56,7 +56,7 @@ export const DynamicFieldValue = ({
|
|
|
56
56
|
) : type == "hierarchy" ? (
|
|
57
57
|
<HierarchyPreview hierarchyValue={_.castArray(value)} />
|
|
58
58
|
) : _.isArray(value) ? (
|
|
59
|
-
|
|
59
|
+
["table", "dynamic_table"].includes(type) ? (
|
|
60
60
|
<FieldViewerValue type={type} value={value} values={values} />
|
|
61
61
|
) : type == "system" ? (
|
|
62
62
|
<SystemPreview value={value} />
|
|
@@ -24,15 +24,16 @@ export const DynamicFormViewer = ({
|
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
const parsedGroups = parseGroups(formatMessage)(template, content);
|
|
27
|
+
|
|
27
28
|
return parsedGroups.map(({ name, fields }, i) => (
|
|
28
29
|
<FieldGroupDetail
|
|
29
30
|
key={i}
|
|
30
31
|
name={
|
|
31
32
|
name
|
|
32
33
|
? formatMessage({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
id: `templates.groups.${name}`,
|
|
35
|
+
defaultMessage: name,
|
|
36
|
+
})
|
|
36
37
|
: null
|
|
37
38
|
}
|
|
38
39
|
fields={fields}
|
|
@@ -69,9 +69,9 @@ export const DynamicFormWithTranslations = ({
|
|
|
69
69
|
name={
|
|
70
70
|
name
|
|
71
71
|
? formatMessage({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
id: `templates.groups.${name}`,
|
|
73
|
+
defaultMessage: name,
|
|
74
|
+
})
|
|
75
75
|
: null
|
|
76
76
|
}
|
|
77
77
|
scope={template?.scope}
|
|
@@ -109,7 +109,7 @@ export const EditableDynamicFieldValue = (props) => {
|
|
|
109
109
|
) : type == "domain" ? (
|
|
110
110
|
<DomainPreview domainIds={_.castArray(value)} />
|
|
111
111
|
) : _.isArray(value) ? (
|
|
112
|
-
|
|
112
|
+
["table", "dynamic_table"].includes(type) ? (
|
|
113
113
|
<FieldViewerValue type={type} value={value} values={values} />
|
|
114
114
|
) : type == "system" ? (
|
|
115
115
|
<SystemPreview value={value} />
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
1
2
|
import PropTypes from "prop-types";
|
|
2
3
|
import { useIntl } from "react-intl";
|
|
3
4
|
import { Icon, Label, Table } from "semantic-ui-react";
|
|
@@ -19,6 +20,21 @@ LocalizedFieldValue.propTypes = {
|
|
|
19
20
|
value: PropTypes.any,
|
|
20
21
|
};
|
|
21
22
|
|
|
23
|
+
const DynamicTableField = (field) => {
|
|
24
|
+
const { value } = field;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Table.Cell>
|
|
28
|
+
{_.isNil(value) || _.isEmpty(value) ? null : _.isArray(value) ? (
|
|
29
|
+
value
|
|
30
|
+
.filter((v) => !_.isNil(v) && !_.isEmpty(v))
|
|
31
|
+
.map((v, i) => <FieldViewerValue {...field} value={v} key={i} multiple />)
|
|
32
|
+
) : (
|
|
33
|
+
<FieldViewerValue {...field} value={value} />
|
|
34
|
+
)}
|
|
35
|
+
</Table.Cell>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
22
38
|
export const FieldViewerValue = ({
|
|
23
39
|
label,
|
|
24
40
|
multiple,
|
|
@@ -27,6 +43,8 @@ export const FieldViewerValue = ({
|
|
|
27
43
|
values,
|
|
28
44
|
widget,
|
|
29
45
|
}) => {
|
|
46
|
+
if (_.isNil(value)) return null;
|
|
47
|
+
|
|
30
48
|
switch (type) {
|
|
31
49
|
case "url":
|
|
32
50
|
return value.url_value ? (
|
|
@@ -39,12 +57,11 @@ export const FieldViewerValue = ({
|
|
|
39
57
|
case "enriched_text":
|
|
40
58
|
return <RichTextEditor readOnly value={value} />;
|
|
41
59
|
case "table":
|
|
42
|
-
const { table_columns } = values;
|
|
43
60
|
return (
|
|
44
61
|
<Table style={{ marginTop: "10px" }} celled striped compact fixed>
|
|
45
62
|
<Table.Header>
|
|
46
63
|
<Table.Row>
|
|
47
|
-
{table_columns.map(({ name }, i) => (
|
|
64
|
+
{values?.table_columns.map(({ name }, i) => (
|
|
48
65
|
<Table.HeaderCell key={i}>{name}</Table.HeaderCell>
|
|
49
66
|
))}
|
|
50
67
|
</Table.Row>
|
|
@@ -52,7 +69,7 @@ export const FieldViewerValue = ({
|
|
|
52
69
|
<Table.Body>
|
|
53
70
|
{value.map((row, i) => (
|
|
54
71
|
<Table.Row key={i}>
|
|
55
|
-
{table_columns.map(({ name }, i) => (
|
|
72
|
+
{values?.table_columns.map(({ name }, i) => (
|
|
56
73
|
<Table.Cell key={i}>{row[name]}</Table.Cell>
|
|
57
74
|
))}
|
|
58
75
|
</Table.Row>
|
|
@@ -60,6 +77,30 @@ export const FieldViewerValue = ({
|
|
|
60
77
|
</Table.Body>
|
|
61
78
|
</Table>
|
|
62
79
|
);
|
|
80
|
+
case "dynamic_table":
|
|
81
|
+
return (
|
|
82
|
+
<Table style={{ marginTop: "10px" }} celled striped compact fixed>
|
|
83
|
+
<Table.Header>
|
|
84
|
+
<Table.Row>
|
|
85
|
+
{values?.table_columns.map(({ name }, i) => (
|
|
86
|
+
<Table.HeaderCell key={i}>{name}</Table.HeaderCell>
|
|
87
|
+
))}
|
|
88
|
+
</Table.Row>
|
|
89
|
+
</Table.Header>
|
|
90
|
+
<Table.Body>
|
|
91
|
+
{value.map((row, i) => (
|
|
92
|
+
<Table.Row key={i}>
|
|
93
|
+
{values?.table_columns.map((column, i) => (
|
|
94
|
+
<DynamicTableField
|
|
95
|
+
key={i}
|
|
96
|
+
{...{ ...column, value: row[column.name]?.value }}
|
|
97
|
+
/>
|
|
98
|
+
))}
|
|
99
|
+
</Table.Row>
|
|
100
|
+
))}
|
|
101
|
+
</Table.Body>
|
|
102
|
+
</Table>
|
|
103
|
+
);
|
|
63
104
|
case "image":
|
|
64
105
|
return <ImagePreview value={value} />;
|
|
65
106
|
case "system":
|
|
@@ -54,15 +54,19 @@ describe("<FieldViewerValue />", () => {
|
|
|
54
54
|
value: "datetime",
|
|
55
55
|
type: "datetime",
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
values: { table_columns: [{ name: "col1", cardinality: "?", type: "string" }] },
|
|
59
|
+
value: [{ col1: { value: "v2", origin: "user" } }, { col1: { value: "v2", origin: "user" } }],
|
|
60
|
+
type: "dynamic_table",
|
|
61
|
+
}
|
|
57
62
|
];
|
|
58
63
|
|
|
59
64
|
testTypes.forEach((props) =>
|
|
60
|
-
it(`matches the latest snapshot with type: ${props.type}${
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
})
|
|
65
|
+
it(`matches the latest snapshot with type: ${props.type}${props.alternative ? " " + props.alternative : ""
|
|
66
|
+
}`, () => {
|
|
67
|
+
const { container } = render(<FieldViewerValue {...props} />);
|
|
68
|
+
expect(container).toMatchSnapshot();
|
|
69
|
+
})
|
|
66
70
|
);
|
|
67
71
|
|
|
68
72
|
it("matches the latest snapshot for multiple", () => {
|
|
@@ -30,6 +30,59 @@ exports[`<FieldViewerValue /> matches the latest snapshot with type: datetime 1`
|
|
|
30
30
|
</div>
|
|
31
31
|
`;
|
|
32
32
|
|
|
33
|
+
exports[`<FieldViewerValue /> matches the latest snapshot with type: dynamic_table 1`] = `
|
|
34
|
+
<div>
|
|
35
|
+
<table
|
|
36
|
+
class="ui celled fixed striped compact table"
|
|
37
|
+
style="margin-top: 10px;"
|
|
38
|
+
>
|
|
39
|
+
<thead
|
|
40
|
+
class=""
|
|
41
|
+
>
|
|
42
|
+
<tr
|
|
43
|
+
class=""
|
|
44
|
+
>
|
|
45
|
+
<th
|
|
46
|
+
class=""
|
|
47
|
+
>
|
|
48
|
+
col1
|
|
49
|
+
</th>
|
|
50
|
+
</tr>
|
|
51
|
+
</thead>
|
|
52
|
+
<tbody
|
|
53
|
+
class=""
|
|
54
|
+
>
|
|
55
|
+
<tr
|
|
56
|
+
class=""
|
|
57
|
+
>
|
|
58
|
+
<td
|
|
59
|
+
class=""
|
|
60
|
+
>
|
|
61
|
+
<div
|
|
62
|
+
class="default-value"
|
|
63
|
+
>
|
|
64
|
+
v2
|
|
65
|
+
</div>
|
|
66
|
+
</td>
|
|
67
|
+
</tr>
|
|
68
|
+
<tr
|
|
69
|
+
class=""
|
|
70
|
+
>
|
|
71
|
+
<td
|
|
72
|
+
class=""
|
|
73
|
+
>
|
|
74
|
+
<div
|
|
75
|
+
class="default-value"
|
|
76
|
+
>
|
|
77
|
+
v2
|
|
78
|
+
</div>
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
81
|
+
</tbody>
|
|
82
|
+
</table>
|
|
83
|
+
</div>
|
|
84
|
+
`;
|
|
85
|
+
|
|
33
86
|
exports[`<FieldViewerValue /> matches the latest snapshot with type: image 1`] = `
|
|
34
87
|
<div>
|
|
35
88
|
<img
|
|
@@ -3,67 +3,11 @@ import PropTypes from "prop-types";
|
|
|
3
3
|
import { Form, Icon, Popup, Label } from "semantic-ui-react";
|
|
4
4
|
import { FormattedMessage } from "react-intl";
|
|
5
5
|
import OriginLabel from "@truedat/core/components/OriginLabel";
|
|
6
|
-
import
|
|
7
|
-
import CheckboxField from "./CheckboxField";
|
|
8
|
-
import ColorPickerField from "./ColorPickerField";
|
|
9
|
-
import DropdownField from "./DropdownField";
|
|
10
|
-
import EnrichedTextField from "./EnrichedTextField";
|
|
11
|
-
import IdentifierField from "./IdentifierField";
|
|
12
|
-
import ImageField from "./ImageField";
|
|
13
|
-
import PairListField from "./PairListField";
|
|
14
|
-
import PasswordField from "./PasswordField";
|
|
15
|
-
import RadioField from "./RadioField";
|
|
16
|
-
import StringField from "./StringField";
|
|
17
|
-
import TableField from "./TableField";
|
|
18
|
-
import TextField from "./TextField";
|
|
19
|
-
import NumberField from "./NumberField";
|
|
20
|
-
import DateField from "./DateField";
|
|
6
|
+
import FieldByWidget from "./FieldByWidget";
|
|
21
7
|
|
|
22
8
|
const isNullOrEmpty = (value) =>
|
|
23
9
|
!_.isNumber(value) && (!value || _.isEmpty(value));
|
|
24
10
|
|
|
25
|
-
const FieldByWidget = ({ scope, widget, onChange, field }) => {
|
|
26
|
-
switch (widget) {
|
|
27
|
-
case "identifier":
|
|
28
|
-
return <IdentifierField field={field} />;
|
|
29
|
-
case "dropdown":
|
|
30
|
-
return <DropdownField field={field} scope={scope} onChange={onChange} />;
|
|
31
|
-
case "radio":
|
|
32
|
-
return <RadioField field={field} onChange={onChange} />;
|
|
33
|
-
case "checkbox":
|
|
34
|
-
return <CheckboxField field={field} onChange={onChange} />;
|
|
35
|
-
case "textarea":
|
|
36
|
-
return <TextField field={field} onChange={onChange} />;
|
|
37
|
-
case "color_picker":
|
|
38
|
-
return <ColorPickerField field={field} onChange={onChange} />;
|
|
39
|
-
case "pair_list":
|
|
40
|
-
return <PairListField field={field} onChange={onChange} />;
|
|
41
|
-
case "enriched_text":
|
|
42
|
-
return <EnrichedTextField field={field} onChange={onChange} />;
|
|
43
|
-
case "table":
|
|
44
|
-
return <TableField field={field} onChange={onChange} />;
|
|
45
|
-
case "password":
|
|
46
|
-
return <PasswordField field={field} onChange={onChange} />;
|
|
47
|
-
case "image":
|
|
48
|
-
return <ImageField field={field} onChange={onChange} />;
|
|
49
|
-
case "number":
|
|
50
|
-
return <NumberField field={field} onChange={onChange} />;
|
|
51
|
-
case "date":
|
|
52
|
-
return <DateField field={field} onChange={onChange} />;
|
|
53
|
-
case "datetime":
|
|
54
|
-
return <DateTimeField field={field} onChange={onChange} />;
|
|
55
|
-
default:
|
|
56
|
-
return <StringField field={field} onChange={onChange} />;
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
FieldByWidget.propTypes = {
|
|
61
|
-
onChange: PropTypes.func.isRequired,
|
|
62
|
-
field: PropTypes.object.isRequired,
|
|
63
|
-
scope: PropTypes.string,
|
|
64
|
-
widget: PropTypes.string,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
11
|
export const DynamicField = ({
|
|
68
12
|
field: {
|
|
69
13
|
label,
|
|
@@ -89,14 +33,14 @@ export const DynamicField = ({
|
|
|
89
33
|
>
|
|
90
34
|
<label>
|
|
91
35
|
{(label && fieldProps.type != "hierarchy") ||
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
36
|
+
(fieldProps.type == "hierarchy" &&
|
|
37
|
+
(fieldProps.values.hierarchy.min_depth == 0 ||
|
|
38
|
+
!fieldProps.values.hierarchy.min_depth)) ? (
|
|
95
39
|
<FormattedMessage id={`fields.${label}`} defaultMessage={label} />
|
|
96
40
|
) : null}
|
|
97
41
|
{label &&
|
|
98
|
-
|
|
99
|
-
|
|
42
|
+
fieldProps.type == "hierarchy" &&
|
|
43
|
+
fieldProps.values.hierarchy.min_depth > 0 ? (
|
|
100
44
|
<>
|
|
101
45
|
<FormattedMessage id={`fields.${label}`} defaultMessage={label} />
|
|
102
46
|
<Label pointing="left">
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { lazy } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { Button, Icon, Label, Table } from "semantic-ui-react";
|
|
5
|
+
import { useIntl } from "react-intl";
|
|
6
|
+
|
|
7
|
+
const FieldByWidget = lazy(() => import("./FieldByWidget"));
|
|
8
|
+
|
|
9
|
+
export const DynamicTableField = ({
|
|
10
|
+
field: { name, value: fieldValue = [], values },
|
|
11
|
+
onChange,
|
|
12
|
+
scope,
|
|
13
|
+
}) => {
|
|
14
|
+
const value = fieldValue || [];
|
|
15
|
+
const table_columns = _.get("table_columns")(values) || [];
|
|
16
|
+
const { formatMessage } = useIntl();
|
|
17
|
+
|
|
18
|
+
const handleAddRow = (e) => {
|
|
19
|
+
e && e.preventDefault();
|
|
20
|
+
onChange(e, {
|
|
21
|
+
name,
|
|
22
|
+
value: [...value, {}],
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const handleRemoveRow = (e, id) => {
|
|
27
|
+
e && e.preventDefault();
|
|
28
|
+
const newValue = [...value];
|
|
29
|
+
newValue.splice(id, 1);
|
|
30
|
+
onChange(e, { name, value: newValue });
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleChange = (idx, e, data) => {
|
|
34
|
+
const currentValue = removeDeletedColumns(table_columns, [...value]);
|
|
35
|
+
const input = {
|
|
36
|
+
...currentValue[idx],
|
|
37
|
+
[data.name]: { origin: "user", value: data.value },
|
|
38
|
+
};
|
|
39
|
+
onChange(e, {
|
|
40
|
+
name,
|
|
41
|
+
value: currentValue.map((newElem, i) => (i == idx ? input : newElem)),
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const removeDeletedColumns = (table_columns, content) => {
|
|
46
|
+
const columns_name = _.map((column) => column.name)(table_columns);
|
|
47
|
+
return content.map((row) => {
|
|
48
|
+
const field_deleted = _.keys(row).filter(
|
|
49
|
+
(x) => !columns_name.includes(x),
|
|
50
|
+
);
|
|
51
|
+
return _.omit(field_deleted)(row);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const rows = value.map((row) => {
|
|
56
|
+
return table_columns.map((field) => {
|
|
57
|
+
return { ...field, value: row[field.name]?.value };
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const requiredErrors = _.flow(
|
|
62
|
+
_.flatten,
|
|
63
|
+
_.filter(_.prop("required")),
|
|
64
|
+
_.groupBy("name"),
|
|
65
|
+
_.toPairs,
|
|
66
|
+
_.map(([key, values]) => [
|
|
67
|
+
key,
|
|
68
|
+
_.any((value) => _.isEmpty(value?.value))(values),
|
|
69
|
+
]),
|
|
70
|
+
_.fromPairs
|
|
71
|
+
)(rows);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<Table celled className="no_padding">
|
|
75
|
+
<Table.Header>
|
|
76
|
+
<Table.Row>
|
|
77
|
+
{table_columns.map((v, i) => (
|
|
78
|
+
<Table.HeaderCell key={i}>
|
|
79
|
+
{v.name}
|
|
80
|
+
{["+", "1"].includes(v.cardinality) && (
|
|
81
|
+
<span className="is_required">*</span>
|
|
82
|
+
)}
|
|
83
|
+
{requiredErrors[v.name] && (<Label pointing="left">
|
|
84
|
+
{formatMessage({
|
|
85
|
+
id: "template.form.validation.empty_required",
|
|
86
|
+
})}
|
|
87
|
+
</Label>)
|
|
88
|
+
}
|
|
89
|
+
</Table.HeaderCell>
|
|
90
|
+
))}
|
|
91
|
+
<Table.HeaderCell />
|
|
92
|
+
</Table.Row>
|
|
93
|
+
</Table.Header>
|
|
94
|
+
<Table.Body>
|
|
95
|
+
{rows.map((row, i) => (
|
|
96
|
+
<Table.Row key={i}>
|
|
97
|
+
{row.map((field, k) => (
|
|
98
|
+
<Table.Cell key={k} width={5}>
|
|
99
|
+
<FieldByWidget
|
|
100
|
+
field={field}
|
|
101
|
+
widget={field.widget}
|
|
102
|
+
onChange={(e, data) => handleChange(i, e, data)}
|
|
103
|
+
scope={scope}
|
|
104
|
+
/>
|
|
105
|
+
</Table.Cell>
|
|
106
|
+
))}
|
|
107
|
+
<Table.Cell style={{ textAlign: "center", padding: "1px" }} width={1}>
|
|
108
|
+
<Button
|
|
109
|
+
icon={
|
|
110
|
+
<Icon
|
|
111
|
+
className="selectable"
|
|
112
|
+
name="trash alternate outline"
|
|
113
|
+
color="red"
|
|
114
|
+
onClick={(e) => handleRemoveRow(e, i)}
|
|
115
|
+
/>
|
|
116
|
+
}
|
|
117
|
+
color="red"
|
|
118
|
+
size="mini"
|
|
119
|
+
basic
|
|
120
|
+
/>
|
|
121
|
+
</Table.Cell>
|
|
122
|
+
</Table.Row>
|
|
123
|
+
))}
|
|
124
|
+
</Table.Body>
|
|
125
|
+
<Table.Footer>
|
|
126
|
+
<Table.Row>
|
|
127
|
+
<Table.HeaderCell colSpan={`${table_columns.length + 1}`}>
|
|
128
|
+
<Button
|
|
129
|
+
compact
|
|
130
|
+
data-testid="add-button"
|
|
131
|
+
floated="right"
|
|
132
|
+
icon={{ name: "add", color: "green" }}
|
|
133
|
+
name="add_button"
|
|
134
|
+
onClick={handleAddRow}
|
|
135
|
+
size="small"
|
|
136
|
+
/>
|
|
137
|
+
</Table.HeaderCell>
|
|
138
|
+
</Table.Row>
|
|
139
|
+
</Table.Footer>
|
|
140
|
+
</Table>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
DynamicTableField.propTypes = {
|
|
145
|
+
field: PropTypes.object,
|
|
146
|
+
onChange: PropTypes.func,
|
|
147
|
+
scope: PropTypes.string,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default DynamicTableField;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import PropTypes from "prop-types";
|
|
2
|
+
import DateTimeField from "./DateTimeField";
|
|
3
|
+
import CheckboxField from "./CheckboxField";
|
|
4
|
+
import ColorPickerField from "./ColorPickerField";
|
|
5
|
+
import DropdownField from "./DropdownField";
|
|
6
|
+
import EnrichedTextField from "./EnrichedTextField";
|
|
7
|
+
import IdentifierField from "./IdentifierField";
|
|
8
|
+
import ImageField from "./ImageField";
|
|
9
|
+
import PairListField from "./PairListField";
|
|
10
|
+
import PasswordField from "./PasswordField";
|
|
11
|
+
import RadioField from "./RadioField";
|
|
12
|
+
import StringField from "./StringField";
|
|
13
|
+
import TableField from "./TableField";
|
|
14
|
+
import DynamicTableField from "./DynamicTableField";
|
|
15
|
+
import TextField from "./TextField";
|
|
16
|
+
import NumberField from "./NumberField";
|
|
17
|
+
import DateField from "./DateField";
|
|
18
|
+
|
|
19
|
+
const FieldByWidget = ({ scope, widget, onChange, field }) => {
|
|
20
|
+
switch (widget) {
|
|
21
|
+
case "identifier":
|
|
22
|
+
return <IdentifierField field={field} />;
|
|
23
|
+
case "dropdown":
|
|
24
|
+
return <DropdownField field={field} scope={scope} onChange={onChange} />;
|
|
25
|
+
case "radio":
|
|
26
|
+
return <RadioField field={field} onChange={onChange} />;
|
|
27
|
+
case "checkbox":
|
|
28
|
+
return <CheckboxField field={field} onChange={onChange} />;
|
|
29
|
+
case "textarea":
|
|
30
|
+
return <TextField field={field} onChange={onChange} />;
|
|
31
|
+
case "color_picker":
|
|
32
|
+
return <ColorPickerField field={field} onChange={onChange} />;
|
|
33
|
+
case "pair_list":
|
|
34
|
+
return <PairListField field={field} onChange={onChange} />;
|
|
35
|
+
case "enriched_text":
|
|
36
|
+
return <EnrichedTextField field={field} onChange={onChange} />;
|
|
37
|
+
case "table":
|
|
38
|
+
return <TableField field={field} onChange={onChange} />;
|
|
39
|
+
case "dynamic_table":
|
|
40
|
+
return <DynamicTableField field={field} onChange={onChange} />;
|
|
41
|
+
case "password":
|
|
42
|
+
return <PasswordField field={field} onChange={onChange} />;
|
|
43
|
+
case "image":
|
|
44
|
+
return <ImageField field={field} onChange={onChange} />;
|
|
45
|
+
case "number":
|
|
46
|
+
return <NumberField field={field} onChange={onChange} />;
|
|
47
|
+
case "date":
|
|
48
|
+
return <DateField field={field} onChange={onChange} />;
|
|
49
|
+
case "datetime":
|
|
50
|
+
return <DateTimeField field={field} onChange={onChange} />;
|
|
51
|
+
default:
|
|
52
|
+
return <StringField field={field} onChange={onChange} />;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
FieldByWidget.propTypes = {
|
|
57
|
+
onChange: PropTypes.func.isRequired,
|
|
58
|
+
field: PropTypes.object.isRequired,
|
|
59
|
+
scope: PropTypes.string,
|
|
60
|
+
widget: PropTypes.string,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default FieldByWidget;
|
|
@@ -35,8 +35,8 @@ const makeOptions = (formatMessage, label) => (cardinality, values) =>
|
|
|
35
35
|
isMultiple(cardinality)
|
|
36
36
|
? translateValues(formatMessage, label)(values)
|
|
37
37
|
: _.union(translateValues(formatMessage, label)(values), [
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
{ text: formatMessage({ id: "selector.no.selection" }), value: null },
|
|
39
|
+
]);
|
|
40
40
|
|
|
41
41
|
const singleValueType = (name, value, options) =>
|
|
42
42
|
_.flow(
|
|
@@ -3,9 +3,10 @@ import { Form } from "semantic-ui-react";
|
|
|
3
3
|
import { useIntl } from "react-intl";
|
|
4
4
|
|
|
5
5
|
export const StringField = ({
|
|
6
|
-
field
|
|
6
|
+
field,
|
|
7
7
|
onChange,
|
|
8
8
|
}) => {
|
|
9
|
+
const { name, cardinality, placeholder, value } = field;
|
|
9
10
|
const { formatMessage } = useIntl();
|
|
10
11
|
return cardinality == "+" || cardinality == "*" ? (
|
|
11
12
|
<Form.Dropdown
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { render } from "@truedat/test/render";
|
|
1
|
+
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
2
|
import { within } from "@testing-library/react";
|
|
3
3
|
import { DynamicField } from "../DynamicField";
|
|
4
4
|
|
|
@@ -146,4 +146,13 @@ describe("<DynamicField />", () => {
|
|
|
146
146
|
const input = within(formField).getByDisplayValue("input_placeholder");
|
|
147
147
|
expect(input).toBeInTheDocument();
|
|
148
148
|
});
|
|
149
|
+
|
|
150
|
+
it("matches the latest snapshot (dynamic_table widget)", async () => {
|
|
151
|
+
const table_columns = [{ name: "col", type: "string", widget: "string", cardinality: "1" }];
|
|
152
|
+
const value = { value: [{ col: { origin: "user", value: "foo" } }], origin: "user" };
|
|
153
|
+
const props = { field: { widget: "dynamic_table", values: { table_columns }, value }, onChange };
|
|
154
|
+
const rendered = render(<DynamicField {...props} />);
|
|
155
|
+
await waitForLoad(rendered);
|
|
156
|
+
expect(rendered.container).toMatchSnapshot();
|
|
157
|
+
});
|
|
149
158
|
});
|