@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
@@ -0,0 +1,257 @@
1
+ import React from "react";
2
+ import { render, waitForLoad } from "@truedat/test/render";
3
+ import { screen, fireEvent, within } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import DynamicTableField from "../DynamicTableField";
6
+
7
+ jest.mock("../FieldByWidget", () => ({
8
+ __esModule: true,
9
+ default: ({ field, onChange }) => {
10
+ const testId = `cell-input-${field.name}`;
11
+ return (
12
+ <input
13
+ data-testid={testId}
14
+ defaultValue={field.value ?? ""}
15
+ onChange={(e) =>
16
+ onChange(e, { name: field.name, value: e.target.value })
17
+ }
18
+ />
19
+ );
20
+ },
21
+ }));
22
+
23
+ const baseColumns = [
24
+ { name: "col1", widget: "text" },
25
+ { name: "col2", widget: "text" },
26
+ ];
27
+
28
+ describe("DynamicTableField", () => {
29
+ test("renders header cells (including required asterisk when cardinality is '+' or '1')", async () => {
30
+ const onChange = jest.fn();
31
+ const table_columns = [
32
+ { name: "col1", widget: "text", cardinality: "+" },
33
+ { name: "col2", widget: "text" },
34
+ { name: "col3", widget: "text", cardinality: "1" },
35
+ ];
36
+
37
+ const props = {
38
+ field: {
39
+ name: "tbl",
40
+ value: [
41
+ {
42
+ col1: { value: "a" },
43
+ col2: { value: "b" },
44
+ col3: { value: "c" },
45
+ },
46
+ ],
47
+ values: { table_columns },
48
+ },
49
+ onChange,
50
+ scope: "test",
51
+ };
52
+
53
+ const rendered = render(<DynamicTableField {...props} />);
54
+ await waitForLoad(rendered);
55
+ expect(rendered.container).toMatchSnapshot();
56
+
57
+ table_columns.forEach((c) => {
58
+ expect(rendered.getByText(c.name)).toBeInTheDocument();
59
+ });
60
+
61
+ const requiredMarks = rendered.getAllByText("*");
62
+ expect(requiredMarks.length).toBe(2);
63
+ });
64
+
65
+ test("clicking add row calls onChange with a new empty row appended", async () => {
66
+ const user = userEvent.setup({ delay: null });
67
+ const onChange = jest.fn();
68
+
69
+ const initialValue = [
70
+ { col1: { value: "a" }, col2: { value: "1" } },
71
+ { col1: { value: "b" }, col2: { value: "2" } },
72
+ ];
73
+
74
+ const props = {
75
+ field: {
76
+ name: "tbl",
77
+ value: initialValue,
78
+ values: { table_columns: baseColumns },
79
+ },
80
+ onChange,
81
+ scope: "test",
82
+ };
83
+
84
+ const rendered = render(<DynamicTableField {...props} />);
85
+ await waitForLoad(rendered);
86
+ const addButton = rendered.getByTestId("add-button");
87
+ await user.click(addButton);
88
+
89
+ expect(onChange).toHaveBeenCalledTimes(1);
90
+ const [_e, payload] = onChange.mock.calls[0];
91
+ expect(payload.name).toBe("tbl");
92
+ expect(Array.isArray(payload.value)).toBe(true);
93
+ expect(payload.value).toHaveLength(initialValue.length + 1);
94
+
95
+ expect(payload.value[payload.value.length - 1]).toEqual({});
96
+ });
97
+
98
+ test("clicking the trash icon removes the corresponding row and calls onChange", async () => {
99
+ const onChange = jest.fn();
100
+
101
+ const initialValue = [
102
+ { col1: { value: "a" }, col2: { value: "1" } },
103
+ { col1: { value: "b" }, col2: { value: "2" } },
104
+ { col1: { value: "c" }, col2: { value: "3" } },
105
+ ];
106
+
107
+ const props = {
108
+ field: {
109
+ name: "tbl",
110
+ value: initialValue,
111
+ values: { table_columns: baseColumns },
112
+ },
113
+ onChange,
114
+ scope: "test",
115
+ };
116
+
117
+ const rendered = render(<DynamicTableField {...props} />);
118
+ await waitForLoad(rendered);
119
+
120
+ const firstRow = rendered.container.querySelector("tbody tr");
121
+ expect(firstRow).toBeTruthy();
122
+
123
+ const trashIcon = firstRow.querySelector(".selectable");
124
+ expect(trashIcon).toBeTruthy();
125
+
126
+ fireEvent.click(trashIcon);
127
+
128
+ expect(onChange).toHaveBeenCalledTimes(1);
129
+ const [, payload] = onChange.mock.calls[0];
130
+
131
+ expect(payload.name).toBe("tbl");
132
+ expect(payload.value).toHaveLength(2);
133
+
134
+ expect(payload.value[0]).toEqual(initialValue[1]);
135
+ expect(payload.value[1]).toEqual(initialValue[2]);
136
+ });
137
+
138
+ test("editing a cell calls onChange with updated structure { origin: 'user', value }", async () => {
139
+ const user = userEvent.setup({ delay: null });
140
+ const onChange = jest.fn();
141
+
142
+ const initialValue = [
143
+ { col1: { value: "old-a" }, col2: { value: "1" } },
144
+ { col1: { value: "old-b" }, col2: { value: "2" } },
145
+ ];
146
+
147
+ const props = {
148
+ field: {
149
+ name: "tbl",
150
+ value: initialValue,
151
+ values: { table_columns: baseColumns },
152
+ },
153
+ onChange,
154
+ scope: "test",
155
+ };
156
+
157
+ const rendered = render(<DynamicTableField {...props} />);
158
+ await waitForLoad(rendered);
159
+
160
+ const input = rendered.getAllByTestId("cell-input-col1")[0];
161
+ await user.clear(input);
162
+ await user.type(input, "new-a");
163
+
164
+ // Last call should have the updated row 0 col1 shape
165
+ expect(onChange).toHaveBeenCalled();
166
+ const [, payload] = onChange.mock.calls[onChange.mock.calls.length - 1];
167
+
168
+ expect(payload.name).toBe("tbl");
169
+ expect(payload.value).toHaveLength(2);
170
+
171
+ expect(payload.value[0].col1).toEqual({
172
+ origin: "user",
173
+ value: "new-a",
174
+ });
175
+
176
+ expect(payload.value[0].col2).toEqual({ value: "1" });
177
+ expect(payload.value[1]).toEqual(initialValue[1]);
178
+ });
179
+
180
+ test("removeDeletedColumns strips keys not present in table_columns before emitting onChange", async () => {
181
+ const user = userEvent.setup({ delay: null });
182
+ const onChange = jest.fn();
183
+
184
+ const columns = [
185
+ { name: "col1", widget: "text" },
186
+ { name: "col2", widget: "text" },
187
+ // Note: no 'ghost' column
188
+ ];
189
+
190
+ const initialValue = [
191
+ // Row contains an extra 'ghost' key that should be stripped on change
192
+ { col1: { value: "x" }, col2: { value: "y" }, ghost: { value: "zzz" } },
193
+ ];
194
+
195
+ const props = {
196
+ field: {
197
+ name: "tbl",
198
+ value: initialValue,
199
+ values: { table_columns: columns },
200
+ },
201
+ onChange,
202
+ scope: "test",
203
+ };
204
+
205
+ const rendered = render(<DynamicTableField {...props} />);
206
+ await waitForLoad(rendered);
207
+
208
+ // Trigger a change on col2; this will call handleChange → removeDeletedColumns
209
+ const input = rendered.getByTestId("cell-input-col2");
210
+ await user.clear(input);
211
+ await user.type(input, "updated");
212
+
213
+ expect(onChange).toHaveBeenCalled();
214
+ const [, payload] = onChange.mock.calls[onChange.mock.calls.length - 1];
215
+
216
+ // 'ghost' should be omitted from the emitted row
217
+ expect(payload.value[0]).toEqual({
218
+ col1: { value: "x" },
219
+ col2: { origin: "user", value: "updated" },
220
+ });
221
+ expect(payload.value[0].ghost).toBeUndefined();
222
+ });
223
+
224
+ test("delete button exists per row and add button exists in footer", async () => {
225
+ const onChange = jest.fn();
226
+
227
+ const initialValue = [
228
+ { col1: { value: "a" }, col2: { value: "1" } },
229
+ { col1: { value: "b" }, col2: { value: "2" } },
230
+ ];
231
+
232
+ const props = {
233
+ field: {
234
+ name: "tbl",
235
+ value: initialValue,
236
+ values: { table_columns: baseColumns },
237
+ },
238
+ onChange,
239
+ scope: "test",
240
+ };
241
+
242
+ const rendered = render(<DynamicTableField {...props} />);
243
+ await waitForLoad(rendered);
244
+
245
+ // Footer add button
246
+ expect(rendered.getByTestId("add-button")).toBeInTheDocument();
247
+
248
+ // Each row should render a Button containing the trash Icon
249
+ const rows = rendered.container.querySelectorAll("tbody tr");
250
+ expect(rows.length).toBe(2);
251
+
252
+ rows.forEach((row) => {
253
+ const trashIcon = row.querySelector(".selectable");
254
+ expect(trashIcon).toBeTruthy();
255
+ });
256
+ });
257
+ });
@@ -391,6 +391,103 @@ exports[`<DynamicField /> matches the latest snapshot (dropdown widget) 1`] = `
391
391
  </div>
392
392
  `;
393
393
 
394
+ exports[`<DynamicField /> matches the latest snapshot (dynamic_table widget) 1`] = `
395
+ <div>
396
+ <div
397
+ class="field"
398
+ data-testid="form-field"
399
+ >
400
+ <label />
401
+ <table
402
+ class="ui celled table no_padding"
403
+ >
404
+ <thead
405
+ class=""
406
+ >
407
+ <tr
408
+ class=""
409
+ >
410
+ <th
411
+ class=""
412
+ >
413
+ col
414
+ <span
415
+ class="is_required"
416
+ >
417
+ *
418
+ </span>
419
+ </th>
420
+ <th
421
+ class=""
422
+ />
423
+ </tr>
424
+ </thead>
425
+ <tbody
426
+ class=""
427
+ >
428
+ <tr
429
+ class=""
430
+ >
431
+ <td
432
+ class="five wide"
433
+ >
434
+ <div
435
+ class="field"
436
+ >
437
+ <div
438
+ class="ui input"
439
+ >
440
+ <input
441
+ name="col"
442
+ type="text"
443
+ value="foo"
444
+ />
445
+ </div>
446
+ </div>
447
+ </td>
448
+ <td
449
+ class="one wide"
450
+ style="text-align: center; padding: 1px;"
451
+ >
452
+ <button
453
+ class="ui red mini basic icon button"
454
+ >
455
+ <i
456
+ aria-hidden="true"
457
+ class="red trash alternate outline icon selectable"
458
+ />
459
+ </button>
460
+ </td>
461
+ </tr>
462
+ </tbody>
463
+ <tfoot
464
+ class=""
465
+ >
466
+ <tr
467
+ class=""
468
+ >
469
+ <th
470
+ class=""
471
+ colspan="2"
472
+ >
473
+ <button
474
+ class="ui small compact icon right floated button"
475
+ data-testid="add-button"
476
+ name="add_button"
477
+ >
478
+ <i
479
+ aria-hidden="true"
480
+ class="green add icon"
481
+ />
482
+ </button>
483
+ </th>
484
+ </tr>
485
+ </tfoot>
486
+ </table>
487
+ </div>
488
+ </div>
489
+ `;
490
+
394
491
  exports[`<DynamicField /> matches the latest snapshot (image widget) 1`] = `
395
492
  <div>
396
493
  <div
@@ -0,0 +1,114 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`DynamicTableField renders header cells (including required asterisk when cardinality is '+' or '1') 1`] = `
4
+ <div>
5
+ <table
6
+ class="ui celled table no_padding"
7
+ >
8
+ <thead
9
+ class=""
10
+ >
11
+ <tr
12
+ class=""
13
+ >
14
+ <th
15
+ class=""
16
+ >
17
+ col1
18
+ <span
19
+ class="is_required"
20
+ >
21
+ *
22
+ </span>
23
+ </th>
24
+ <th
25
+ class=""
26
+ >
27
+ col2
28
+ </th>
29
+ <th
30
+ class=""
31
+ >
32
+ col3
33
+ <span
34
+ class="is_required"
35
+ >
36
+ *
37
+ </span>
38
+ </th>
39
+ <th
40
+ class=""
41
+ />
42
+ </tr>
43
+ </thead>
44
+ <tbody
45
+ class=""
46
+ >
47
+ <tr
48
+ class=""
49
+ >
50
+ <td
51
+ class="five wide"
52
+ >
53
+ <input
54
+ data-testid="cell-input-col1"
55
+ value="a"
56
+ />
57
+ </td>
58
+ <td
59
+ class="five wide"
60
+ >
61
+ <input
62
+ data-testid="cell-input-col2"
63
+ value="b"
64
+ />
65
+ </td>
66
+ <td
67
+ class="five wide"
68
+ >
69
+ <input
70
+ data-testid="cell-input-col3"
71
+ value="c"
72
+ />
73
+ </td>
74
+ <td
75
+ class="one wide"
76
+ style="text-align: center; padding: 1px;"
77
+ >
78
+ <button
79
+ class="ui red mini basic icon button"
80
+ >
81
+ <i
82
+ aria-hidden="true"
83
+ class="red trash alternate outline icon selectable"
84
+ />
85
+ </button>
86
+ </td>
87
+ </tr>
88
+ </tbody>
89
+ <tfoot
90
+ class=""
91
+ >
92
+ <tr
93
+ class=""
94
+ >
95
+ <th
96
+ class=""
97
+ colspan="4"
98
+ >
99
+ <button
100
+ class="ui small compact icon right floated button"
101
+ data-testid="add-button"
102
+ name="add_button"
103
+ >
104
+ <i
105
+ aria-hidden="true"
106
+ class="green add icon"
107
+ />
108
+ </button>
109
+ </th>
110
+ </tr>
111
+ </tfoot>
112
+ </table>
113
+ </div>
114
+ `;
@@ -10,9 +10,10 @@ import {
10
10
  Input,
11
11
  Icon,
12
12
  } from "semantic-ui-react";
13
- import { FormattedMessage, useIntl } from "react-intl";
13
+ import { useIntl } from "react-intl";
14
14
  import { dropAt, swap } from "@truedat/core/services/arrays";
15
15
  import FieldForm from "./FieldForm";
16
+ import { defaultFieldDefinition } from "./widgetDefinitions";
16
17
 
17
18
  export const ActiveGroupForm = ({
18
19
  activeGroup,
@@ -52,20 +53,8 @@ export const ActiveGroupForm = ({
52
53
  };
53
54
 
54
55
  const handleAddField = () => {
55
- const newField = {
56
- name: "new_field",
57
- label: "New Field",
58
- widget: "string",
59
- cardinality: "?",
60
- type: "string",
61
- default: {
62
- value: "",
63
- origin: "default",
64
- },
65
- values: null,
66
- };
67
56
  const name = `${groupNamePrefix}.fields`;
68
- onChange(null, { name, value: [...fields, newField] });
57
+ onChange(null, { name, value: [...fields, defaultFieldDefinition] });
69
58
  setActiveField(fields.length);
70
59
  };
71
60
 
@@ -166,8 +155,8 @@ export const ActiveGroupForm = ({
166
155
  {_.isEmpty(_.prop(field.name)(dependencies))
167
156
  ? null
168
157
  : _.map(({ label, name }) => (
169
- <Label key={name}>{label}</Label>
170
- ))(_.prop(field.name)(dependencies))}
158
+ <Label key={name}>{label}</Label>
159
+ ))(_.prop(field.name)(dependencies))}
171
160
  </>
172
161
  ),
173
162
  },
@@ -0,0 +1,158 @@
1
+ import _ from "lodash/fp";
2
+ import PropTypes from "prop-types";
3
+ import { Form } from "semantic-ui-react";
4
+ import { useIntl } from "react-intl";
5
+ import { useEffect } from "react";
6
+
7
+ import {
8
+ getCardinalityOptions,
9
+ getWidgetOptions,
10
+ getTypeOptions,
11
+ WIDGETS,
12
+ } from "./widgetDefinitions";
13
+ import { getValues } from "./valueDefinitions";
14
+
15
+ const defaultType = (values) => _.flow(_.map("value"), _.first)(values);
16
+
17
+ const FieldDefinition = ({
18
+ allowedTypes = [],
19
+ allowedWidgets = [],
20
+ cardinality,
21
+ defaultField,
22
+ fieldNamePrefix,
23
+ mandatory,
24
+ onChange,
25
+ subscribableField,
26
+ type,
27
+ valueName,
28
+ widget,
29
+ }) => {
30
+ const { formatMessage } = useIntl();
31
+
32
+ const changeValue = (name, value) =>
33
+ value == "null"
34
+ ? onChange(null, { name, value: null })
35
+ : onChange(null, {
36
+ name,
37
+ value: { [value]: null },
38
+ });
39
+
40
+ const handleTypeChange = (e, data) => {
41
+ const { value } = data;
42
+ changeValue(valueName, defaultType(getValues(widget, value)) || "null");
43
+ onChange(e, {
44
+ name: defaultField,
45
+ value: { value: "", origin: "default" },
46
+ });
47
+ onChange(e, { name: subscribableField, value: false });
48
+ onChange(e, data);
49
+ };
50
+
51
+ const handleCardinalityChange = (e, data) => {
52
+ const value = data?.value;
53
+ onChange(e, data);
54
+ if (_.includes(value)(["1", "+"]) && !_.isEmpty(mandatory))
55
+ onChange(e, { name: `${fieldNamePrefix}.mandatory`, value: null });
56
+ };
57
+
58
+ const handleWidgetChange = (e, data) => {
59
+ const { value } = data;
60
+ const types = _.find({ value })(WIDGETS).types;
61
+ const updatedType = _.indexOf(type)(types) < 0 ? _.head(types) : type;
62
+ changeValue(
63
+ valueName,
64
+ defaultType(getValues(value, updatedType)) || "null",
65
+ );
66
+ onChange(e, {
67
+ name: defaultField,
68
+ value: { value: "", origin: "default" },
69
+ });
70
+ onChange(e, { name: subscribableField, value: false });
71
+ onChange(e, data);
72
+ };
73
+
74
+ const typeCheckOnChange = (widgetOption) => {
75
+ if (widgetOption && _.indexOf(type)(widgetOption.types) < 0)
76
+ onChange(null, {
77
+ name: `${fieldNamePrefix}.type`,
78
+ value: widgetOption.types[0],
79
+ });
80
+ };
81
+
82
+ const cardinalityCheckOnChange = (widgetOption) => {
83
+ if (
84
+ widgetOption &&
85
+ _.indexOf(cardinality)(widgetOption.cardinalities) < 0
86
+ )
87
+ onChange(null, {
88
+ name: `${fieldNamePrefix}.cardinality`,
89
+ value: widgetOption.cardinalities[0],
90
+ });
91
+ };
92
+
93
+ //On widget change, verify if type and cardinality are still valid
94
+ useEffect(() => {
95
+ const widgetOption = _.find({ value: widget })(WIDGETS);
96
+ typeCheckOnChange(widgetOption);
97
+ cardinalityCheckOnChange(widgetOption);
98
+ // eslint-disable-next-line react-hooks/exhaustive-deps
99
+ }, [widget, type]);
100
+
101
+ const widgetOptions = _.isEmpty(allowedWidgets)
102
+ ? getWidgetOptions()
103
+ : _.filter((widget) => allowedWidgets.includes(widget.value))(
104
+ getWidgetOptions(),
105
+ );
106
+ const typeOptions = _.isEmpty(allowedTypes)
107
+ ? getTypeOptions(widget)
108
+ : _.filter((type) => allowedTypes.includes(type.value))(
109
+ getTypeOptions(widget),
110
+ );
111
+
112
+ return (
113
+ <Form.Group size="small" widths="equal">
114
+ <Form.Dropdown
115
+ name={`${fieldNamePrefix}.widget`}
116
+ fluid
117
+ selection
118
+ label={formatMessage({ id: "template.field.widget" })}
119
+ value={widget}
120
+ options={widgetOptions}
121
+ required
122
+ onChange={handleWidgetChange}
123
+ />
124
+ <Form.Dropdown
125
+ fluid
126
+ selection
127
+ label={formatMessage({ id: "template.field.type" })}
128
+ onChange={handleTypeChange}
129
+ name={`${fieldNamePrefix}.type`}
130
+ value={type}
131
+ required
132
+ options={typeOptions}
133
+ />
134
+ <Form.Dropdown
135
+ fluid
136
+ selection
137
+ label={formatMessage({ id: "template.field.cardinality" })}
138
+ name={`${fieldNamePrefix}.cardinality`}
139
+ value={cardinality}
140
+ onChange={handleCardinalityChange}
141
+ required
142
+ options={getCardinalityOptions(widget)}
143
+ />
144
+ </Form.Group>
145
+ );
146
+ };
147
+
148
+ FieldDefinition.propTypes = {
149
+ fieldNamePrefix: PropTypes.string,
150
+ handleWidgetChange: PropTypes.func,
151
+ handleTypeChange: PropTypes.func,
152
+ handleCardinalityChange: PropTypes.func,
153
+ type: PropTypes.string,
154
+ widget: PropTypes.string,
155
+ cardinality: PropTypes.string,
156
+ };
157
+
158
+ export default FieldDefinition;