@truedat/df 4.28.1 → 4.28.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.
@@ -5,18 +5,19 @@ import { useIntl } from "react-intl";
5
5
  import { Form, Button, Divider, Segment } from "semantic-ui-react";
6
6
  import ValuesField from "./ValuesField";
7
7
  import ConditionalFieldForm from "./ConditionalFieldForm";
8
+ import MandatoryConditional from "./MandatoryConditional";
8
9
  import {
9
10
  getCardinalityOptions,
10
11
  getWidgetOptions,
11
12
  getTypeOptions,
12
- WIDGETS
13
+ WIDGETS,
13
14
  } from "./widgetDefinitions";
14
15
  import { getValues } from "./valueDefinitions";
15
16
 
16
- const typeFromKey = keys =>
17
+ const typeFromKey = (keys) =>
17
18
  _.includes("role_users")(keys) ? "role_users" : _.first(keys);
18
19
 
19
- const defaultType = values => _.flow(_.map("value"), _.first)(values);
20
+ const defaultType = (values) => _.flow(_.map("value"), _.first)(values);
20
21
 
21
22
  export const FieldForm = ({
22
23
  allFields,
@@ -27,7 +28,7 @@ export const FieldForm = ({
27
28
  groupNamePrefix,
28
29
  onChange,
29
30
  onDelete,
30
- onMove
31
+ onMove,
31
32
  }) => {
32
33
  const { formatMessage } = useIntl();
33
34
  const {
@@ -38,7 +39,7 @@ export const FieldForm = ({
38
39
  type,
39
40
  widget,
40
41
  disabled,
41
- errors
42
+ errors,
42
43
  } = field;
43
44
 
44
45
  const fieldNamePrefix = `${groupNamePrefix}.fields[${fieldIndex}]`;
@@ -49,22 +50,22 @@ export const FieldForm = ({
49
50
  const keyType = _.flow([_.getOr({}, "values"), _.keys, typeFromKey])(field);
50
51
  const hasDependencies = _.negate(_.isEmpty)(dependencies);
51
52
 
52
- const typeCheckOnChange = widgetOptions => {
53
+ const typeCheckOnChange = (widgetOptions) => {
53
54
  if (widgetOptions && _.indexOf(type)(widgetOptions.types) < 0)
54
55
  onChange(null, {
55
56
  name: `${fieldNamePrefix}.type`,
56
- value: widgetOptions.types[0]
57
+ value: widgetOptions.types[0],
57
58
  });
58
59
  };
59
60
 
60
- const cardinalityCheckOnChange = widgetOptions => {
61
+ const cardinalityCheckOnChange = (widgetOptions) => {
61
62
  if (
62
63
  widgetOptions &&
63
64
  _.indexOf(cardinality)(widgetOptions.cardinalities) < 0
64
65
  )
65
66
  onChange(null, {
66
67
  name: `${fieldNamePrefix}.cardinality`,
67
- value: widgetOptions.cardinalities[0]
68
+ value: widgetOptions.cardinalities[0],
68
69
  });
69
70
  };
70
71
 
@@ -73,9 +74,16 @@ export const FieldForm = ({
73
74
  ? onChange(null, { name, value: null })
74
75
  : onChange(null, {
75
76
  name,
76
- value: { [value]: null }
77
+ value: { [value]: null },
77
78
  });
78
79
 
80
+ const handleCardinalityChange = (e, data) => {
81
+ const value = data?.value;
82
+ onChange(e, data);
83
+ if (_.includes(value)(["1", "+"]) && !_.isEmpty(field?.mandatory))
84
+ onChange(e, { name: `${fieldNamePrefix}.mandatory`, value: null });
85
+ };
86
+
79
87
  const handleWidgetChange = (e, data) => {
80
88
  const { value } = data;
81
89
  const types = _.find({ value })(WIDGETS).types;
@@ -122,20 +130,20 @@ export const FieldForm = ({
122
130
  key: "up",
123
131
  icon: "arrow up",
124
132
  onClick: () => onMove(fieldIndex, "up"),
125
- disabled: fieldIndex === 0
133
+ disabled: fieldIndex === 0,
126
134
  },
127
135
  {
128
136
  key: "down",
129
137
  icon: "arrow down",
130
138
  onClick: () => onMove(fieldIndex, "down"),
131
- disabled: fieldIndex === fieldCount - 1
139
+ disabled: fieldIndex === fieldCount - 1,
132
140
  },
133
141
  {
134
142
  key: "delete",
135
143
  disabled: hasDependencies,
136
144
  icon: "trash alternate outline",
137
- onClick: onDelete
138
- }
145
+ onClick: onDelete,
146
+ },
139
147
  ]}
140
148
  />
141
149
  <Divider hidden clearing />
@@ -161,8 +169,8 @@ export const FieldForm = ({
161
169
  errors &&
162
170
  errors.nameDuplicated && {
163
171
  content: formatMessage({
164
- id: "template.form.validation.name_duplicated"
165
- })
172
+ id: "template.form.validation.name_duplicated",
173
+ }),
166
174
  }
167
175
  }
168
176
  />
@@ -200,7 +208,7 @@ export const FieldForm = ({
200
208
  fluid
201
209
  selection
202
210
  label={formatMessage({ id: "template.field.cardinality" })}
203
- onChange={onChange}
211
+ onChange={handleCardinalityChange}
204
212
  name={`${fieldNamePrefix}.cardinality`}
205
213
  value={cardinality}
206
214
  required
@@ -224,6 +232,14 @@ export const FieldForm = ({
224
232
  field={field}
225
233
  onChange={onChange}
226
234
  />
235
+ {_.includes(field?.cardinality)(["*", "?"]) && (
236
+ <MandatoryConditional
237
+ allFields={allFields}
238
+ field={field}
239
+ fieldNamePrefix={fieldNamePrefix}
240
+ onChange={onChange}
241
+ />
242
+ )}
227
243
  </Segment>
228
244
  );
229
245
  };
@@ -237,7 +253,7 @@ FieldForm.propTypes = {
237
253
  groupNamePrefix: PropTypes.string,
238
254
  onChange: PropTypes.func.isRequired,
239
255
  onDelete: PropTypes.func.isRequired,
240
- onMove: PropTypes.func.isRequired
256
+ onMove: PropTypes.func.isRequired,
241
257
  };
242
258
 
243
259
  export default FieldForm;
@@ -0,0 +1,61 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import DependentFormField from "./DependentFormField";
6
+
7
+ export const MandatoryConditional = ({
8
+ allFields,
9
+ field,
10
+ fieldNamePrefix,
11
+ onChange,
12
+ }) => {
13
+ const { formatMessage } = useIntl();
14
+ const name = field?.name;
15
+ const depends = field?.mandatory?.on;
16
+ const fieldOptions = _.flow(
17
+ _.reject({ name }),
18
+ _.reject((field) => _.isEmpty(field?.values?.fixed)),
19
+ _.map(({ label, name }) => ({ text: label, value: name }))
20
+ )(allFields);
21
+ const valueOptions = _.flow(
22
+ _.find({ name: depends }),
23
+ _.propOr([], "values.fixed"),
24
+ _.reject(_.isNil),
25
+ _.map((value) => ({ text: value, value }))
26
+ )(allFields);
27
+ const handleChange = (e, { name, value }) => {
28
+ if (_.includes("mandatory.on")(name) && !_.isEmpty(field?.mandatory?.to_be))
29
+ onChange(e, { name: `${fieldNamePrefix}.mandatory.to_be`, value: [] });
30
+
31
+ onChange(e, { name, value });
32
+ };
33
+
34
+ return (
35
+ <DependentFormField
36
+ label={formatMessage({ id: "template.field.mandatory.depends" })}
37
+ fieldName={`${fieldNamePrefix}.mandatory.on`}
38
+ fieldOptions={fieldOptions}
39
+ fieldPlaceholder={formatMessage({
40
+ id: "template.field.mandatory.depends.on",
41
+ })}
42
+ fieldValue={field?.mandatory?.on}
43
+ onChange={handleChange}
44
+ value={field?.mandatory?.to_be}
45
+ valueName={`${fieldNamePrefix}.mandatory.to_be`}
46
+ valueOptions={valueOptions}
47
+ valuePlaceholder={formatMessage({
48
+ id: "template.field.mandatory.depends.to_be",
49
+ })}
50
+ />
51
+ );
52
+ };
53
+
54
+ MandatoryConditional.propTypes = {
55
+ allFields: PropTypes.array,
56
+ fieldNamePrefix: PropTypes.string,
57
+ field: PropTypes.object,
58
+ onChange: PropTypes.func,
59
+ };
60
+
61
+ export default MandatoryConditional;
@@ -64,6 +64,7 @@ export const TemplateForm = ({ loading, template, onSubmit, onDelete }) => {
64
64
  "values",
65
65
  "default",
66
66
  "subscribable",
67
+ "mandatory",
67
68
  ];
68
69
 
69
70
  const dependsProperty = _.isEmpty(_.path("depends.on")(field))
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { DependentFormField } from "../DependentFormField";
4
+
5
+ describe("<DependentFormField />", () => {
6
+ const label = "label";
7
+ const fieldName = "field";
8
+ const fieldOptions = [{ text: "bar", value: "bar" }];
9
+ const fieldPlaceholder = "field placeholder";
10
+ const fieldValue = "bar";
11
+ const value = "foo";
12
+ const valueName = "value";
13
+ const valueOptions = [{ text: "foo", value: "foo" }];
14
+ const valuePlaceholder = "value placeholder";
15
+ const onChange = jest.fn();
16
+
17
+ const props = {
18
+ label,
19
+ fieldName,
20
+ fieldOptions,
21
+ fieldPlaceholder,
22
+ fieldValue,
23
+ value,
24
+ valueName,
25
+ valueOptions,
26
+ valuePlaceholder,
27
+ onChange,
28
+ };
29
+
30
+ it("matches the latest snapshot", () => {
31
+ const { container } = render(<DependentFormField {...props} />, {});
32
+ expect(container).toMatchSnapshot();
33
+ });
34
+ });
@@ -20,7 +20,7 @@ describe("<FieldForm />", () => {
20
20
  label: "Field 1",
21
21
  description: "A field for testing",
22
22
  meta: { role: "Data Owner" },
23
- widget: "string"
23
+ widget: "string",
24
24
  };
25
25
  const fieldIndex = 0;
26
26
  const fieldCount = 1;
@@ -42,7 +42,7 @@ describe("<FieldForm />", () => {
42
42
  description: "A field for testing",
43
43
  meta: { role: "Data Owner" },
44
44
  disabled: true,
45
- widget: "string"
45
+ widget: "string",
46
46
  };
47
47
  const fieldIndex = 0;
48
48
  const fieldCount = 1;
@@ -54,15 +54,10 @@ describe("<FieldForm />", () => {
54
54
  onDelete,
55
55
  onMove,
56
56
  onChange,
57
- editMode
57
+ editMode,
58
58
  };
59
59
  const wrapper = shallow(<FieldForm {...props} />);
60
- expect(
61
- wrapper
62
- .find("FormInput")
63
- .at(1)
64
- .prop("disabled")
65
- ).toBe(true);
60
+ expect(wrapper.find("FormInput").at(1).prop("disabled")).toBe(true);
66
61
  });
67
62
 
68
63
  it("renders ValuesField and manages onChange", () => {
@@ -76,7 +71,7 @@ describe("<FieldForm />", () => {
76
71
  name: "field1",
77
72
  label: "Field 1",
78
73
  description: "A field for testing",
79
- widget: "dropdown"
74
+ widget: "dropdown",
80
75
  };
81
76
  const fieldIndex = 0;
82
77
  const fieldCount = 1;
@@ -87,7 +82,7 @@ describe("<FieldForm />", () => {
87
82
  groupNamePrefix: "Group",
88
83
  onDelete,
89
84
  onMove,
90
- onChange
85
+ onChange,
91
86
  };
92
87
  const wrapper = shallow(<FieldForm {...props} />);
93
88
  expect(wrapper).toMatchSnapshot();
@@ -99,11 +94,11 @@ describe("<FieldForm />", () => {
99
94
  dropdown.simulate("change", null, { value: "fixed_tuple" });
100
95
  expect(onChange).toBeCalledWith(null, {
101
96
  name: "Group.fields[0].subscribable",
102
- value: false
97
+ value: false,
103
98
  });
104
99
  expect(onChange).toBeCalledWith(null, {
105
100
  name: "Group.fields[0].values",
106
- value: { fixed_tuple: null }
101
+ value: { fixed_tuple: null },
107
102
  });
108
103
  });
109
104
 
@@ -118,7 +113,7 @@ describe("<FieldForm />", () => {
118
113
  name: "field1",
119
114
  label: "Field 1",
120
115
  description: "A field for testing",
121
- widget: "dropdown"
116
+ widget: "dropdown",
122
117
  };
123
118
  const fieldIndex = 0;
124
119
  const fieldCount = 1;
@@ -129,7 +124,7 @@ describe("<FieldForm />", () => {
129
124
  groupNamePrefix: "Group",
130
125
  onDelete,
131
126
  onMove,
132
- onChange
127
+ onChange,
133
128
  };
134
129
  const wrapper = shallow(<FieldForm {...props} />);
135
130
  const widgetDropdown = wrapper.find({ name: "Group.fields[0].widget" });
@@ -137,15 +132,15 @@ describe("<FieldForm />", () => {
137
132
  widgetDropdown.simulate("change", null, { value: "string" });
138
133
  expect(onChange).toBeCalledWith(null, {
139
134
  name: "Group.fields[0].values",
140
- value: null
135
+ value: null,
141
136
  });
142
137
  expect(onChange).toBeCalledWith(null, {
143
138
  name: "Group.fields[0].default",
144
- value: ""
139
+ value: "",
145
140
  });
146
141
  expect(onChange).toBeCalledWith(null, {
147
142
  name: "Group.fields[0].subscribable",
148
- value: false
143
+ value: false,
149
144
  });
150
145
  expect(onChange).toBeCalledWith(null, { value: "string" });
151
146
  });
@@ -161,7 +156,7 @@ describe("<FieldForm />", () => {
161
156
  name: "field1",
162
157
  label: "Field 1",
163
158
  description: "A field for testing",
164
- widget: "dropdown"
159
+ widget: "dropdown",
165
160
  };
166
161
  const fieldIndex = 0;
167
162
  const fieldCount = 1;
@@ -172,7 +167,7 @@ describe("<FieldForm />", () => {
172
167
  groupNamePrefix: "Group",
173
168
  onDelete,
174
169
  onMove,
175
- onChange
170
+ onChange,
176
171
  };
177
172
  const wrapper = shallow(<FieldForm {...props} />);
178
173
  const typeDropdown = wrapper.find({ name: "Group.fields[0].type" });
@@ -180,16 +175,141 @@ describe("<FieldForm />", () => {
180
175
  typeDropdown.simulate("change", null, { value: "string" });
181
176
  expect(onChange).toBeCalledWith(null, {
182
177
  name: "Group.fields[0].values",
183
- value: { fixed: null }
178
+ value: { fixed: null },
184
179
  });
185
180
  expect(onChange).toBeCalledWith(null, {
186
181
  name: "Group.fields[0].default",
187
- value: ""
182
+ value: "",
188
183
  });
189
184
  expect(onChange).toBeCalledWith(null, {
190
185
  name: "Group.fields[0].subscribable",
191
- value: false
186
+ value: false,
192
187
  });
193
188
  expect(onChange).toBeCalledWith(null, { value: "string" });
194
189
  });
190
+
191
+ it("renders MandatoryConditional", () => {
192
+ const allFields = [
193
+ {
194
+ name: "foo",
195
+ label: "Foo",
196
+ type: "string",
197
+ cardinality: "+",
198
+ values: { fixed: ["1", "2"] },
199
+ },
200
+ {
201
+ name: "bar",
202
+ label: "Bar",
203
+ type: "string",
204
+ cardinality: "?",
205
+ values: { fixed: ["3", "4"] },
206
+ },
207
+ {
208
+ name: "xyz",
209
+ label: "Xyz",
210
+ type: "string",
211
+ cardinality: "?",
212
+ },
213
+ {
214
+ name: "name",
215
+ label: "Name",
216
+ type: "string",
217
+ cardinality: "?",
218
+ },
219
+ ];
220
+ const onDelete = jest.fn();
221
+ const onMove = jest.fn();
222
+ const onChange = jest.fn();
223
+ const field = {
224
+ name: "name",
225
+ label: "Name",
226
+ mandatory: { on: "bar", to_be: ["3"] },
227
+ cardinality: "?",
228
+ };
229
+ const fieldIndex = 0;
230
+ const fieldCount = 1;
231
+ const props = {
232
+ allFields,
233
+ field,
234
+ fieldIndex,
235
+ fieldCount,
236
+ groupNamePrefix: "Group",
237
+ onDelete,
238
+ onMove,
239
+ onChange,
240
+ };
241
+ const wrapper = shallow(<FieldForm {...props} />);
242
+ expect(wrapper).toMatchSnapshot();
243
+ const MandatoryConditional = wrapper.find("MandatoryConditional").first();
244
+ expect(MandatoryConditional).toBeTruthy();
245
+ });
246
+
247
+ it("manages handleCardinalityChange", () => {
248
+ const allFields = [
249
+ {
250
+ name: "foo",
251
+ label: "Foo",
252
+ type: "string",
253
+ cardinality: "+",
254
+ values: { fixed: ["1", "2"] },
255
+ },
256
+ {
257
+ name: "bar",
258
+ label: "Bar",
259
+ type: "string",
260
+ cardinality: "?",
261
+ values: { fixed: ["3", "4"] },
262
+ },
263
+ {
264
+ name: "xyz",
265
+ label: "Xyz",
266
+ type: "string",
267
+ cardinality: "?",
268
+ },
269
+ {
270
+ name: "name",
271
+ label: "Name",
272
+ type: "string",
273
+ cardinality: "?",
274
+ },
275
+ ];
276
+ const onDelete = jest.fn();
277
+ const onMove = jest.fn();
278
+ const onChange = jest.fn();
279
+ const field = {
280
+ name: "name",
281
+ label: "Name",
282
+ mandatory: { on: "bar", to_be: ["3"] },
283
+ cardinality: "?",
284
+ };
285
+ const fieldIndex = 0;
286
+ const fieldCount = 1;
287
+ const props = {
288
+ allFields,
289
+ field,
290
+ fieldIndex,
291
+ fieldCount,
292
+ groupNamePrefix: "Group",
293
+ onDelete,
294
+ onMove,
295
+ onChange,
296
+ };
297
+ const wrapper = shallow(<FieldForm {...props} />);
298
+ expect(wrapper).toMatchSnapshot();
299
+ const CardinalityDropdown = wrapper.find({
300
+ name: "Group.fields[0].cardinality",
301
+ });
302
+ CardinalityDropdown.simulate("change", null, {
303
+ name: "Group.fields[0].cardinality",
304
+ value: "1",
305
+ });
306
+ expect(onChange.mock.calls[0][1]).toEqual({
307
+ name: "Group.fields[0].cardinality",
308
+ value: "1",
309
+ });
310
+ expect(onChange.mock.calls[1][1]).toEqual({
311
+ name: "Group.fields[0].mandatory",
312
+ value: null,
313
+ });
314
+ });
195
315
  });