@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.
Files changed (42) hide show
  1. package/package.json +4 -4
  2. package/src/components/DynamicFieldValue.js +1 -1
  3. package/src/components/DynamicFormViewer.js +4 -3
  4. package/src/components/DynamicFormWithTranslations.js +3 -3
  5. package/src/components/EditableDynamicFieldValue.js +1 -1
  6. package/src/components/FieldViewerValue.js +44 -3
  7. package/src/components/__tests__/FieldViewerValue.spec.js +10 -6
  8. package/src/components/__tests__/__snapshots__/FieldViewerValue.spec.js.snap +53 -0
  9. package/src/components/widgets/DynamicField.js +6 -62
  10. package/src/components/widgets/DynamicTableField.js +150 -0
  11. package/src/components/widgets/FieldByWidget.js +63 -0
  12. package/src/components/widgets/StandardDropdown.js +2 -2
  13. package/src/components/widgets/StringField.js +2 -1
  14. package/src/components/widgets/__tests__/DynamicField.spec.js +10 -1
  15. package/src/components/widgets/__tests__/DynamicTableField.spec.js +257 -0
  16. package/src/components/widgets/__tests__/__snapshots__/DynamicField.spec.js.snap +97 -0
  17. package/src/components/widgets/__tests__/__snapshots__/DynamicTableField.spec.js.snap +114 -0
  18. package/src/templates/components/templateForm/ActiveGroupForm.js +5 -16
  19. package/src/templates/components/templateForm/FieldDefinition.js +158 -0
  20. package/src/templates/components/templateForm/FieldForm.js +32 -135
  21. package/src/templates/components/templateForm/TableValuesForm.js +258 -0
  22. package/src/templates/components/templateForm/TemplateForm.js +43 -26
  23. package/src/templates/components/templateForm/ValuesConfiguration.js +67 -0
  24. package/src/templates/components/templateForm/ValuesField.js +60 -96
  25. package/src/templates/components/templateForm/ValuesListForm.js +1 -3
  26. package/src/templates/components/templateForm/__tests__/FieldDefinition.spec.js +227 -0
  27. package/src/templates/components/templateForm/__tests__/TableValuesForm.spec.js +215 -0
  28. package/src/templates/components/templateForm/__tests__/ValuesField.spec.js +28 -83
  29. package/src/templates/components/templateForm/__tests__/__snapshots__/ActiveGroupForm.spec.js.snap +17 -0
  30. package/src/templates/components/templateForm/__tests__/__snapshots__/FieldDefinition.spec.js.snap +443 -0
  31. package/src/templates/components/templateForm/__tests__/__snapshots__/FieldForm.spec.js.snap +51 -0
  32. package/src/templates/components/templateForm/__tests__/__snapshots__/TemplateForm.spec.js.snap +17 -0
  33. package/src/templates/components/templateForm/__tests__/__snapshots__/ValuesField.spec.js.snap +61 -387
  34. package/src/templates/components/templateForm/contentValidation.js +22 -3
  35. package/src/templates/components/templateForm/valueDefinitions.js +16 -2
  36. package/src/templates/components/templateForm/widgetDefinitions.js +28 -2
  37. package/src/templates/utils/__tests__/validateContent.spec.js +6 -6
  38. package/src/templates/utils/applyTemplate.js +72 -23
  39. package/src/templates/utils/filterValues.js +3 -3
  40. package/src/templates/utils/parseFieldOptions.js +73 -58
  41. package/src/templates/utils/parseGroups.js +47 -48
  42. 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.5",
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.5",
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.5",
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": "7d60ce1b280a2815d9b441f38d27b6b8d5b7a694"
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
- type == "table" ? (
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
- id: `templates.groups.${name}`,
34
- defaultMessage: name,
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
- id: `templates.groups.${name}`,
73
- defaultMessage: name,
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
- type == "table" ? (
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
- props.alternative ? " " + props.alternative : ""
62
- }`, () => {
63
- const { container } = render(<FieldViewerValue {...props} />);
64
- expect(container).toMatchSnapshot();
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 DateTimeField from "./DateTimeField";
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
- (fieldProps.type == "hierarchy" &&
93
- (fieldProps.values.hierarchy.min_depth == 0 ||
94
- !fieldProps.values.hierarchy.min_depth)) ? (
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
- fieldProps.type == "hierarchy" &&
99
- fieldProps.values.hierarchy.min_depth > 0 ? (
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
- { text: formatMessage({ id: "selector.no.selection" }), value: null },
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: { name, cardinality, placeholder, value },
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
  });