datajunction-ui 0.0.11 → 0.0.12
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
CHANGED
|
@@ -2,59 +2,86 @@
|
|
|
2
2
|
* Custom metadata field component for nodes
|
|
3
3
|
*/
|
|
4
4
|
import { ErrorMessage, Field } from 'formik';
|
|
5
|
-
import {
|
|
5
|
+
import { useEffect } from 'react';
|
|
6
6
|
|
|
7
|
-
export const CustomMetadataField = (
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
export const CustomMetadataField = () => {
|
|
8
|
+
const formatValue = value => {
|
|
9
|
+
if (value === null || value === undefined) {
|
|
10
|
+
return '';
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === 'string') {
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
return JSON.stringify(value, null, 2);
|
|
16
|
+
};
|
|
12
17
|
|
|
13
18
|
const handleChange = (e, setFieldValue) => {
|
|
14
|
-
|
|
15
|
-
setJsonString(value);
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
if (value.trim() === '') {
|
|
19
|
-
setFieldValue('custom_metadata', null);
|
|
20
|
-
setError('');
|
|
21
|
-
} else {
|
|
22
|
-
const parsed = JSON.parse(value);
|
|
23
|
-
setFieldValue('custom_metadata', parsed);
|
|
24
|
-
setError('');
|
|
25
|
-
}
|
|
26
|
-
} catch (err) {
|
|
27
|
-
setError('Invalid JSON format');
|
|
28
|
-
}
|
|
19
|
+
setFieldValue('custom_metadata', e.target.value);
|
|
29
20
|
};
|
|
30
21
|
|
|
31
22
|
return (
|
|
32
23
|
<div className="NodeCreationInput" style={{ marginTop: '20px' }}>
|
|
33
|
-
<ErrorMessage name="custom_metadata" component="span" />
|
|
34
24
|
<label htmlFor="CustomMetadata">Custom Metadata (JSON)</label>
|
|
35
|
-
<Field
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
25
|
+
<Field
|
|
26
|
+
name="custom_metadata"
|
|
27
|
+
validate={value => {
|
|
28
|
+
if (!value || value.trim() === '') {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(value);
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
typeof parsed === 'object' &&
|
|
36
|
+
parsed !== null &&
|
|
37
|
+
!Array.isArray(parsed)
|
|
38
|
+
) {
|
|
39
|
+
const keys = Object.keys(parsed);
|
|
40
|
+
const originalKeyMatches = value.match(/"([^"]+)"\s*:/g);
|
|
41
|
+
if (
|
|
42
|
+
originalKeyMatches &&
|
|
43
|
+
originalKeyMatches.length > keys.length
|
|
44
|
+
) {
|
|
45
|
+
return 'Duplicate keys detected';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return undefined;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return 'Invalid JSON format';
|
|
52
|
+
}
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{({ field, form }) => {
|
|
56
|
+
const errorMessage = form.errors.custom_metadata;
|
|
57
|
+
const hasError = errorMessage && form.touched.custom_metadata;
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div>
|
|
61
|
+
<textarea
|
|
62
|
+
id="CustomMetadata"
|
|
63
|
+
value={formatValue(field.value)}
|
|
64
|
+
onChange={e => handleChange(e, form.setFieldValue)}
|
|
65
|
+
onBlur={() => form.setFieldTouched('custom_metadata', true)}
|
|
66
|
+
style={{
|
|
67
|
+
width: '100%',
|
|
68
|
+
minHeight: '100px',
|
|
69
|
+
fontFamily: 'monospace',
|
|
70
|
+
fontSize: '12px',
|
|
71
|
+
padding: '8px',
|
|
72
|
+
border: hasError ? '1px solid red' : '1px solid #ccc',
|
|
73
|
+
borderRadius: '4px',
|
|
74
|
+
}}
|
|
75
|
+
placeholder="Define custom node metadata here..."
|
|
76
|
+
/>
|
|
77
|
+
{hasError && (
|
|
78
|
+
<span style={{ color: 'red', fontSize: '12px' }}>
|
|
79
|
+
{errorMessage}
|
|
80
|
+
</span>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}}
|
|
58
85
|
</Field>
|
|
59
86
|
</div>
|
|
60
87
|
);
|
|
@@ -203,7 +203,7 @@ describe('AddEditNodePage submission succeeded', () => {
|
|
|
203
203
|
'',
|
|
204
204
|
undefined,
|
|
205
205
|
['dj'],
|
|
206
|
-
|
|
206
|
+
null,
|
|
207
207
|
);
|
|
208
208
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledTimes(1);
|
|
209
209
|
expect(mockDjClient.DataJunctionAPI.tagsNode).toBeCalledWith(
|
|
@@ -109,6 +109,17 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
109
109
|
return primaryKey.map(columnName => columnName.trim());
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
+
const parseCustomMetadata = customMetadata => {
|
|
113
|
+
if (!customMetadata || customMetadata.trim() === '') {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return JSON.parse(customMetadata);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
112
123
|
const createNode = async (values, setStatus) => {
|
|
113
124
|
const { status, json } = await djClient.createNode(
|
|
114
125
|
nodeType,
|
|
@@ -124,7 +135,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
124
135
|
values.metric_direction,
|
|
125
136
|
values.metric_unit,
|
|
126
137
|
values.required_dimensions,
|
|
127
|
-
values.custom_metadata,
|
|
138
|
+
parseCustomMetadata(values.custom_metadata),
|
|
128
139
|
);
|
|
129
140
|
if (status === 200 || status === 201) {
|
|
130
141
|
if (values.tags) {
|
|
@@ -160,7 +171,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
160
171
|
values.significant_digits,
|
|
161
172
|
values.required_dimensions,
|
|
162
173
|
values.owners,
|
|
163
|
-
values.custom_metadata,
|
|
174
|
+
parseCustomMetadata(values.custom_metadata),
|
|
164
175
|
);
|
|
165
176
|
const tagsResponse = await djClient.tagsNode(
|
|
166
177
|
values.name,
|
|
@@ -283,6 +294,9 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
283
294
|
field,
|
|
284
295
|
data[field].map(owner => owner.username),
|
|
285
296
|
);
|
|
297
|
+
} else if (field === 'custom_metadata') {
|
|
298
|
+
const value = data[field] ? JSON.stringify(data[field], null, 2) : '';
|
|
299
|
+
setFieldValue(field, value, false);
|
|
286
300
|
} else {
|
|
287
301
|
setFieldValue(field, data[field] || '', false);
|
|
288
302
|
}
|
|
@@ -352,6 +366,8 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
352
366
|
<Formik
|
|
353
367
|
initialValues={initialValues}
|
|
354
368
|
validate={validator}
|
|
369
|
+
validateOnChange={true}
|
|
370
|
+
validateOnBlur={true}
|
|
355
371
|
onSubmit={async (values, { setSubmitting, setStatus }) => {
|
|
356
372
|
try {
|
|
357
373
|
for (const handler of submitHandlers) {
|
|
@@ -364,7 +380,16 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
364
380
|
}
|
|
365
381
|
}}
|
|
366
382
|
>
|
|
367
|
-
{function Render(
|
|
383
|
+
{function Render(formikProps) {
|
|
384
|
+
const {
|
|
385
|
+
isSubmitting,
|
|
386
|
+
status,
|
|
387
|
+
setFieldValue,
|
|
388
|
+
errors,
|
|
389
|
+
touched,
|
|
390
|
+
isValid,
|
|
391
|
+
dirty,
|
|
392
|
+
} = formikProps;
|
|
368
393
|
const [node, setNode] = useState([]);
|
|
369
394
|
const [selectPrimaryKey, setSelectPrimaryKey] = useState(null);
|
|
370
395
|
const [selectRequiredDims, setSelectRequiredDims] =
|
|
@@ -449,9 +474,7 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
449
474
|
) : (
|
|
450
475
|
''
|
|
451
476
|
)}
|
|
452
|
-
<CustomMetadataField
|
|
453
|
-
initialValue={node.custom_metadata || {}}
|
|
454
|
-
/>
|
|
477
|
+
<CustomMetadataField />
|
|
455
478
|
{nodeType !== 'metric' && node.type !== 'metric' ? (
|
|
456
479
|
action === Action.Edit ? (
|
|
457
480
|
selectPrimaryKey
|
|
@@ -493,7 +516,10 @@ export function AddEditNodePage({ extensions = {} }) {
|
|
|
493
516
|
{action === Action.Edit ? selectTags : <TagsField />}
|
|
494
517
|
<NodeModeField />
|
|
495
518
|
|
|
496
|
-
<button
|
|
519
|
+
<button
|
|
520
|
+
type="submit"
|
|
521
|
+
disabled={isSubmitting || !isValid}
|
|
522
|
+
>
|
|
497
523
|
{isSubmitting ? (
|
|
498
524
|
<LoadingIcon />
|
|
499
525
|
) : (
|