@remoteoss/json-schema-form 0.11.11-dev.20250220174843 → 1.0.0-alpha.1
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/.nvmrc +1 -0
- package/.vscode/settings.json +51 -0
- package/README.md +29 -24
- package/babel.config.mjs +14 -0
- package/eslint.config.mjs +3 -0
- package/jest.config.mjs +52 -0
- package/package.json +26 -71
- package/src/form.ts +3 -0
- package/src/index.ts +1 -0
- package/src/utils.ts +24 -0
- package/test/form.test.ts +11 -0
- package/tsconfig.json +13 -0
- package/tsup.config.json +10 -0
- package/CHANGELOG.md +0 -320
- package/LICENSE +0 -21
- package/dist/index.cjs +0 -2171
- package/dist/index.js +0 -2140
- package/dist/standalone.js +0 -13656
- package/json-schema-form.d.ts +0 -175
- package/json-schema-form.schema.json +0 -188
- package/src/tests/checkIfConditionMatches.test.js +0 -65
- package/src/tests/conditions.test.js +0 -938
- package/src/tests/const.test.js +0 -236
- package/src/tests/createHeadlessForm.customValidations.test.js +0 -803
- package/src/tests/createHeadlessForm.test.js +0 -4324
- package/src/tests/helpers.custom.js +0 -222
- package/src/tests/helpers.js +0 -2385
- package/src/tests/internals.helpers.test.js +0 -175
- package/src/tests/jsonLogic.fixtures.js +0 -986
- package/src/tests/jsonLogic.test.js +0 -588
- package/src/tests/modify.test.js +0 -939
- package/src/tests/testUtils.js +0 -11
- package/src/tests/utils.test.jsx +0 -32
|
@@ -1,938 +0,0 @@
|
|
|
1
|
-
import { createHeadlessForm } from '@/createHeadlessForm';
|
|
2
|
-
|
|
3
|
-
describe('Conditional attributes updated', () => {
|
|
4
|
-
it('Should allow check of a nested property in a conditional', () => {
|
|
5
|
-
const { handleValidation } = createHeadlessForm(
|
|
6
|
-
{
|
|
7
|
-
additionalProperties: false,
|
|
8
|
-
allOf: [
|
|
9
|
-
{
|
|
10
|
-
if: {
|
|
11
|
-
properties: {
|
|
12
|
-
parent: {
|
|
13
|
-
properties: {
|
|
14
|
-
child: {
|
|
15
|
-
const: 'yes',
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
required: ['child'],
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
required: ['parent'],
|
|
22
|
-
},
|
|
23
|
-
then: { required: ['parent_sibling'] },
|
|
24
|
-
},
|
|
25
|
-
],
|
|
26
|
-
properties: {
|
|
27
|
-
parent: {
|
|
28
|
-
additionalProperties: false,
|
|
29
|
-
properties: {
|
|
30
|
-
child: {
|
|
31
|
-
oneOf: [{ const: 'yes' }, { const: 'no' }],
|
|
32
|
-
type: 'string',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
required: ['child'],
|
|
36
|
-
type: 'object',
|
|
37
|
-
},
|
|
38
|
-
parent_sibling: {
|
|
39
|
-
type: 'integer',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
required: ['parent'],
|
|
43
|
-
type: 'object',
|
|
44
|
-
},
|
|
45
|
-
{ strictInputType: false }
|
|
46
|
-
);
|
|
47
|
-
expect(handleValidation({ parent: { child: 'no' } }).formErrors).toEqual(undefined);
|
|
48
|
-
expect(handleValidation({ parent: { child: 'yes' } }).formErrors).toEqual({
|
|
49
|
-
parent_sibling: 'Required field',
|
|
50
|
-
});
|
|
51
|
-
expect(handleValidation({ parent: { child: 'yes' }, parent_sibling: 1 }).formErrors).toEqual(
|
|
52
|
-
undefined
|
|
53
|
-
);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('Update basic case with const, default, maximum', () => {
|
|
57
|
-
const { fields, handleValidation } = createHeadlessForm(
|
|
58
|
-
{
|
|
59
|
-
properties: {
|
|
60
|
-
is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
|
|
61
|
-
hours: { type: 'number' },
|
|
62
|
-
},
|
|
63
|
-
allOf: [
|
|
64
|
-
{
|
|
65
|
-
if: {
|
|
66
|
-
properties: { is_full_time: { const: 'yes' } },
|
|
67
|
-
required: ['is_full_time'],
|
|
68
|
-
},
|
|
69
|
-
then: {
|
|
70
|
-
properties: {
|
|
71
|
-
hours: {
|
|
72
|
-
const: 8,
|
|
73
|
-
default: 8,
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
else: {
|
|
78
|
-
properties: {
|
|
79
|
-
hours: {
|
|
80
|
-
maximum: 4,
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
{ strictInputType: false }
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
// Given "Yes" it applies "const" and "default"
|
|
91
|
-
expect(handleValidation({ is_full_time: 'yes', hours: 4 }).formErrors).toEqual({
|
|
92
|
-
hours: 'The only accepted value is 8.',
|
|
93
|
-
});
|
|
94
|
-
expect(fields[1]).toMatchObject({ const: 8, default: 8 });
|
|
95
|
-
expect(fields[1].maximum).toBeUndefined();
|
|
96
|
-
|
|
97
|
-
// Changing to "No", applies the "maximum" and cleans "const" and "default"
|
|
98
|
-
expect(handleValidation({ is_full_time: 'no', hours: 4 }).formErrors).toBeUndefined();
|
|
99
|
-
expect(fields[1]).toMatchObject({ maximum: 4 });
|
|
100
|
-
expect(fields[1].const).toBeUndefined();
|
|
101
|
-
expect(fields[1].default).toBeUndefined();
|
|
102
|
-
|
|
103
|
-
// Changing back to "Yes", it removes "maximum", and applies "const" and "default"
|
|
104
|
-
expect(handleValidation({}).formErrors).toBeUndefined();
|
|
105
|
-
expect(handleValidation({ is_full_time: 'yes', hours: 8 }).formErrors).toBeUndefined();
|
|
106
|
-
expect(fields[1].maximum).toBeUndefined();
|
|
107
|
-
expect(fields[1]).toMatchObject({ const: 8, default: 8 });
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('Update a new attribute (eg description)', () => {
|
|
111
|
-
const { fields, handleValidation } = createHeadlessForm(
|
|
112
|
-
{
|
|
113
|
-
properties: {
|
|
114
|
-
is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
|
|
115
|
-
hours: {
|
|
116
|
-
type: 'number',
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
allOf: [
|
|
120
|
-
{
|
|
121
|
-
if: {
|
|
122
|
-
properties: { is_full_time: { const: 'yes' } },
|
|
123
|
-
required: ['is_full_time'],
|
|
124
|
-
},
|
|
125
|
-
then: {
|
|
126
|
-
properties: {
|
|
127
|
-
hours: {
|
|
128
|
-
description: 'We recommend 8 hours.',
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
},
|
|
135
|
-
{ strictInputType: false }
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
// By default the attribute is not set.
|
|
139
|
-
expect(fields[1].description).toBeUndefined();
|
|
140
|
-
|
|
141
|
-
// Given "Yes" it applies the conditional attribute
|
|
142
|
-
expect(handleValidation({ is_full_time: 'yes' }).formErrors).toBeUndefined();
|
|
143
|
-
expect(fields[1].description).toBe('We recommend 8 hours.');
|
|
144
|
-
|
|
145
|
-
// Changing to "No", removes the description
|
|
146
|
-
expect(handleValidation({ is_full_time: 'no' }).formErrors).toBeUndefined();
|
|
147
|
-
expect(fields[1].description).toBeUndefined();
|
|
148
|
-
|
|
149
|
-
// Changing back to "Yes", it sets the attribute again
|
|
150
|
-
expect(handleValidation({ is_full_time: 'yes' }).formErrors).toBeUndefined();
|
|
151
|
-
expect(fields[1].description).toBe('We recommend 8 hours.');
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('Update an existing attribute (eg description)', () => {
|
|
155
|
-
const { fields, handleValidation } = createHeadlessForm(
|
|
156
|
-
{
|
|
157
|
-
properties: {
|
|
158
|
-
is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
|
|
159
|
-
hours: {
|
|
160
|
-
type: 'number',
|
|
161
|
-
description: 'Any value works.',
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
allOf: [
|
|
165
|
-
{
|
|
166
|
-
if: {
|
|
167
|
-
properties: { is_full_time: { const: 'yes' } },
|
|
168
|
-
required: ['is_full_time'],
|
|
169
|
-
},
|
|
170
|
-
then: {
|
|
171
|
-
properties: {
|
|
172
|
-
hours: {
|
|
173
|
-
description: 'We recommend 8 hours.',
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
],
|
|
179
|
-
},
|
|
180
|
-
{ strictInputType: false }
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
// By default the attribute is set the base value.
|
|
184
|
-
expect(fields[1].description).toBe('Any value works.');
|
|
185
|
-
|
|
186
|
-
// Given "Yes" it applies the conditional attribute
|
|
187
|
-
expect(handleValidation({ is_full_time: 'yes', hours: 4 }).formErrors).toBeUndefined();
|
|
188
|
-
expect(fields[1].description).toBe('We recommend 8 hours.');
|
|
189
|
-
|
|
190
|
-
// Changing to "No", it applies the base value again.
|
|
191
|
-
expect(handleValidation({ is_full_time: 'no', hours: 4 }).formErrors).toBeUndefined();
|
|
192
|
-
expect(fields[1].description).toBe('Any value works.');
|
|
193
|
-
|
|
194
|
-
// Changing back to "Yes", it sets the attribute again
|
|
195
|
-
expect(handleValidation({ is_full_time: 'yes', hours: 8 }).formErrors).toBeUndefined();
|
|
196
|
-
expect(fields[1].description).toBe('We recommend 8 hours.');
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('Update a nested attribute', () => {
|
|
200
|
-
const { fields, handleValidation } = createHeadlessForm(
|
|
201
|
-
{
|
|
202
|
-
properties: {
|
|
203
|
-
is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
|
|
204
|
-
hours: {
|
|
205
|
-
type: 'number',
|
|
206
|
-
presentation: {
|
|
207
|
-
inputType: 'number',
|
|
208
|
-
anything: 'info',
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
allOf: [
|
|
213
|
-
{
|
|
214
|
-
if: {
|
|
215
|
-
properties: { is_full_time: { const: 'yes' } },
|
|
216
|
-
required: ['is_full_time'],
|
|
217
|
-
},
|
|
218
|
-
then: {
|
|
219
|
-
properties: {
|
|
220
|
-
hours: {
|
|
221
|
-
presentation: {
|
|
222
|
-
anything: 'danger',
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
},
|
|
230
|
-
{ strictInputType: false }
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
// By default the attribute is set the base value.
|
|
234
|
-
expect(fields[1].anything).toBe('info');
|
|
235
|
-
|
|
236
|
-
// Given "Yes" it applies the conditional attribute
|
|
237
|
-
expect(handleValidation({ is_full_time: 'yes' }).formErrors).toBeUndefined();
|
|
238
|
-
expect(fields[1].anything).toBe('danger');
|
|
239
|
-
|
|
240
|
-
// Changing to "No", it applies the base value again.
|
|
241
|
-
expect(handleValidation({ is_full_time: 'no' }).formErrors).toBeUndefined();
|
|
242
|
-
expect(fields[1].anything).toBe('info');
|
|
243
|
-
|
|
244
|
-
// Changing back to "Yes", it sets the attribute again
|
|
245
|
-
expect(handleValidation({ is_full_time: 'yes' }).formErrors).toBeUndefined();
|
|
246
|
-
expect(fields[1].anything).toBe('danger');
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it('Keeps existing attributes in matches that dont change the attr', () => {
|
|
250
|
-
const { fields, handleValidation } = createHeadlessForm(
|
|
251
|
-
{
|
|
252
|
-
properties: {
|
|
253
|
-
is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
|
|
254
|
-
hours: {
|
|
255
|
-
type: 'number',
|
|
256
|
-
description: 'Any value works.',
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
allOf: [
|
|
260
|
-
{
|
|
261
|
-
if: {
|
|
262
|
-
properties: { is_full_time: { const: 'yes' } },
|
|
263
|
-
required: ['is_full_time'],
|
|
264
|
-
},
|
|
265
|
-
then: {
|
|
266
|
-
required: ['hours'],
|
|
267
|
-
},
|
|
268
|
-
else: {
|
|
269
|
-
properties: {
|
|
270
|
-
hours: false,
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
],
|
|
275
|
-
},
|
|
276
|
-
{ strictInputType: false }
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
// By default the attribute is set the base value, even though the field is invisible.
|
|
280
|
-
expect(fields[1].description).toBe('Any value works.');
|
|
281
|
-
expect(fields[1].isVisible).toBe(false);
|
|
282
|
-
|
|
283
|
-
// Given "Yes" it keeps the base value
|
|
284
|
-
expect(handleValidation({ is_full_time: 'yes' }).formErrors).toEqual({
|
|
285
|
-
hours: 'Required field',
|
|
286
|
-
});
|
|
287
|
-
expect(fields[1].description).toBe('Any value works.');
|
|
288
|
-
expect(fields[1].isVisible).toBe(true);
|
|
289
|
-
|
|
290
|
-
// Changing to "No" it keeps the base value
|
|
291
|
-
expect(handleValidation({ is_full_time: 'no' }).formErrors).toBeUndefined();
|
|
292
|
-
expect(fields[1].description).toBe('Any value works.');
|
|
293
|
-
expect(fields[1].isVisible).toBe(false);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it('Keeps internal attributes (dynamicInternalJsfAttrs)', () => {
|
|
297
|
-
// This is necessary while we keep supporting "type", even if deprecated
|
|
298
|
-
// otherwise our Remote app will break because it didn't migrate
|
|
299
|
-
// from "type" to "inputType" yet.
|
|
300
|
-
const { fields, handleValidation } = createHeadlessForm(
|
|
301
|
-
{
|
|
302
|
-
properties: {
|
|
303
|
-
is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
|
|
304
|
-
salary_period: {
|
|
305
|
-
type: 'string',
|
|
306
|
-
title: 'Salary period',
|
|
307
|
-
oneOf: [
|
|
308
|
-
{ title: 'Weekly', const: 'weekly' },
|
|
309
|
-
{ title: 'Monthly', const: 'monthly' },
|
|
310
|
-
],
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
allOf: [
|
|
314
|
-
{
|
|
315
|
-
if: {
|
|
316
|
-
properties: { is_full_time: { const: 'yes' } },
|
|
317
|
-
required: ['is_full_time'],
|
|
318
|
-
},
|
|
319
|
-
then: {
|
|
320
|
-
properties: {
|
|
321
|
-
salary_period: {
|
|
322
|
-
description: 'We recommend montlhy.',
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
],
|
|
328
|
-
},
|
|
329
|
-
{ strictInputType: false }
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
// Given "Yes" it keeps the "type"
|
|
333
|
-
handleValidation({ is_full_time: 'yes' });
|
|
334
|
-
|
|
335
|
-
// All the following attrs are never removed
|
|
336
|
-
// during conditionals because they are core.
|
|
337
|
-
expect(fields[1]).toMatchObject({
|
|
338
|
-
name: 'salary_period',
|
|
339
|
-
label: 'Salary period',
|
|
340
|
-
required: false,
|
|
341
|
-
type: 'radio',
|
|
342
|
-
inputType: 'radio',
|
|
343
|
-
jsonType: 'string',
|
|
344
|
-
computedAttributes: {},
|
|
345
|
-
calculateConditionalProperties: expect.any(Function),
|
|
346
|
-
schema: expect.any(Object),
|
|
347
|
-
scopedJsonSchema: expect.any(Object),
|
|
348
|
-
isVisible: true,
|
|
349
|
-
options: [
|
|
350
|
-
{
|
|
351
|
-
label: 'Weekly',
|
|
352
|
-
value: 'weekly',
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
label: 'Monthly',
|
|
356
|
-
value: 'monthly',
|
|
357
|
-
},
|
|
358
|
-
],
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
it('Keeps custom attributes (dynamicInternalJsfAttrs) (hotfix temporary)', () => {
|
|
363
|
-
// This is necessary as hotfix because we (Remote) use it internally.
|
|
364
|
-
// Not cool, we'll need a better solution asap.
|
|
365
|
-
const { fields, handleValidation } = createHeadlessForm(
|
|
366
|
-
{
|
|
367
|
-
properties: {
|
|
368
|
-
is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
|
|
369
|
-
salary_period: {
|
|
370
|
-
type: 'string',
|
|
371
|
-
title: 'Salary period',
|
|
372
|
-
oneOf: [
|
|
373
|
-
{ title: 'Weekly', const: 'weekly' },
|
|
374
|
-
{ title: 'Monthly', const: 'monthly' },
|
|
375
|
-
],
|
|
376
|
-
},
|
|
377
|
-
},
|
|
378
|
-
allOf: [
|
|
379
|
-
{
|
|
380
|
-
if: {
|
|
381
|
-
properties: { is_full_time: { const: 'yes' } },
|
|
382
|
-
required: ['is_full_time'],
|
|
383
|
-
},
|
|
384
|
-
then: {
|
|
385
|
-
properties: {
|
|
386
|
-
salary_period: {
|
|
387
|
-
description: 'We recommend montlhy.',
|
|
388
|
-
},
|
|
389
|
-
},
|
|
390
|
-
},
|
|
391
|
-
},
|
|
392
|
-
],
|
|
393
|
-
},
|
|
394
|
-
{
|
|
395
|
-
strictInputType: false,
|
|
396
|
-
customProperties: {
|
|
397
|
-
salary_period: {
|
|
398
|
-
Component: '<A React Component>',
|
|
399
|
-
calculateDynamicProperties: () => true,
|
|
400
|
-
},
|
|
401
|
-
},
|
|
402
|
-
}
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
// It's there by default
|
|
406
|
-
expect(fields[1].Component).toBe('<A React Component>');
|
|
407
|
-
expect(fields[1].calculateDynamicProperties).toEqual(expect.any(Function));
|
|
408
|
-
|
|
409
|
-
// Given "Yes", it stays there too.
|
|
410
|
-
handleValidation({ is_full_time: 'yes' });
|
|
411
|
-
expect(fields[1].Component).toBe('<A React Component>');
|
|
412
|
-
expect(fields[1].calculateDynamicProperties).toEqual(expect.any(Function));
|
|
413
|
-
|
|
414
|
-
// Given "No", it stays there too.
|
|
415
|
-
handleValidation({ is_full_time: 'no' });
|
|
416
|
-
expect(fields[1].Component).toBe('<A React Component>');
|
|
417
|
-
expect(fields[1].calculateDynamicProperties).toEqual(expect.any(Function));
|
|
418
|
-
|
|
419
|
-
expect(fields[1].visibilityCondition).toEqual(undefined);
|
|
420
|
-
// visibilityCondition can be externally changed/updated/added, it stays there too;
|
|
421
|
-
fields[1].visibilityCondition = () => false;
|
|
422
|
-
handleValidation({ is_full_time: 'no' });
|
|
423
|
-
expect(fields[1].visibilityCondition).toEqual(expect.any(Function));
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
it('Group array nested condition', () => {
|
|
427
|
-
const { fields, handleValidation } = createHeadlessForm({
|
|
428
|
-
type: 'object',
|
|
429
|
-
additionalProperties: false,
|
|
430
|
-
properties: {
|
|
431
|
-
companies: {
|
|
432
|
-
type: 'array',
|
|
433
|
-
'x-jsf-presentation': {
|
|
434
|
-
inputType: 'group-array',
|
|
435
|
-
},
|
|
436
|
-
items: {
|
|
437
|
-
type: 'object',
|
|
438
|
-
properties: {
|
|
439
|
-
company: {
|
|
440
|
-
type: 'string',
|
|
441
|
-
oneOf: [
|
|
442
|
-
{
|
|
443
|
-
title: 'A',
|
|
444
|
-
const: 'A',
|
|
445
|
-
},
|
|
446
|
-
{
|
|
447
|
-
title: 'B',
|
|
448
|
-
const: 'B',
|
|
449
|
-
},
|
|
450
|
-
],
|
|
451
|
-
'x-jsf-presentation': {
|
|
452
|
-
inputType: 'select',
|
|
453
|
-
},
|
|
454
|
-
},
|
|
455
|
-
role: {
|
|
456
|
-
title: 'Role',
|
|
457
|
-
oneOf: [],
|
|
458
|
-
'x-jsf-presentation': {
|
|
459
|
-
inputType: 'select',
|
|
460
|
-
},
|
|
461
|
-
},
|
|
462
|
-
},
|
|
463
|
-
allOf: [
|
|
464
|
-
{
|
|
465
|
-
if: {
|
|
466
|
-
properties: {
|
|
467
|
-
company: {
|
|
468
|
-
const: 'A',
|
|
469
|
-
},
|
|
470
|
-
},
|
|
471
|
-
required: ['company'],
|
|
472
|
-
},
|
|
473
|
-
then: {
|
|
474
|
-
properties: {
|
|
475
|
-
role: {
|
|
476
|
-
oneOf: [
|
|
477
|
-
{
|
|
478
|
-
title: 'adminA',
|
|
479
|
-
const: 'adminA',
|
|
480
|
-
},
|
|
481
|
-
{
|
|
482
|
-
title: 'userA',
|
|
483
|
-
const: 'userA',
|
|
484
|
-
},
|
|
485
|
-
],
|
|
486
|
-
'x-jsf-presentation': {
|
|
487
|
-
inputType: 'select',
|
|
488
|
-
},
|
|
489
|
-
},
|
|
490
|
-
},
|
|
491
|
-
required: ['role'],
|
|
492
|
-
},
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
if: {
|
|
496
|
-
properties: {
|
|
497
|
-
company: {
|
|
498
|
-
const: 'B',
|
|
499
|
-
},
|
|
500
|
-
},
|
|
501
|
-
required: ['company'],
|
|
502
|
-
},
|
|
503
|
-
then: {
|
|
504
|
-
properties: {
|
|
505
|
-
role: {
|
|
506
|
-
oneOf: [
|
|
507
|
-
{
|
|
508
|
-
title: 'adminB',
|
|
509
|
-
const: 'adminB',
|
|
510
|
-
},
|
|
511
|
-
{
|
|
512
|
-
title: 'userB',
|
|
513
|
-
const: 'userB',
|
|
514
|
-
},
|
|
515
|
-
],
|
|
516
|
-
'x-jsf-presentation': {
|
|
517
|
-
inputType: 'select',
|
|
518
|
-
},
|
|
519
|
-
},
|
|
520
|
-
},
|
|
521
|
-
required: ['role'],
|
|
522
|
-
},
|
|
523
|
-
},
|
|
524
|
-
],
|
|
525
|
-
required: ['company', 'role'],
|
|
526
|
-
},
|
|
527
|
-
},
|
|
528
|
-
},
|
|
529
|
-
required: ['companies'],
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
handleValidation({ companies: [{ company: 'A' }, { company: 'B' }] });
|
|
533
|
-
expect(fields[0].dynamicFields[0][1].options).toEqual([
|
|
534
|
-
{ label: 'adminA', value: 'adminA' },
|
|
535
|
-
{ label: 'userA', value: 'userA' },
|
|
536
|
-
]);
|
|
537
|
-
expect(fields[0].dynamicFields[1][1].options).toEqual([
|
|
538
|
-
{ label: 'adminB', value: 'adminB' },
|
|
539
|
-
{ label: 'userB', value: 'userB' },
|
|
540
|
-
]);
|
|
541
|
-
handleValidation({ companies: [] });
|
|
542
|
-
expect(fields[0].dynamicFields).toEqual([]);
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
it('select multiple conditions', () => {
|
|
546
|
-
const { fields, handleValidation } = createHeadlessForm({
|
|
547
|
-
type: 'object',
|
|
548
|
-
additionalProperties: false,
|
|
549
|
-
properties: {
|
|
550
|
-
company: {
|
|
551
|
-
title: 'Select Company',
|
|
552
|
-
type: 'string',
|
|
553
|
-
description: 'Choose a company',
|
|
554
|
-
oneOf: [
|
|
555
|
-
{
|
|
556
|
-
title: 'A',
|
|
557
|
-
const: 'A',
|
|
558
|
-
},
|
|
559
|
-
{
|
|
560
|
-
title: 'B',
|
|
561
|
-
const: 'B',
|
|
562
|
-
},
|
|
563
|
-
],
|
|
564
|
-
'x-jsf-presentation': {
|
|
565
|
-
inputType: 'select',
|
|
566
|
-
},
|
|
567
|
-
},
|
|
568
|
-
role: {
|
|
569
|
-
title: 'Role',
|
|
570
|
-
oneOf: [],
|
|
571
|
-
'x-jsf-presentation': {
|
|
572
|
-
inputType: 'select',
|
|
573
|
-
},
|
|
574
|
-
},
|
|
575
|
-
},
|
|
576
|
-
allOf: [
|
|
577
|
-
{
|
|
578
|
-
if: {
|
|
579
|
-
properties: {
|
|
580
|
-
company: {
|
|
581
|
-
const: 'A',
|
|
582
|
-
},
|
|
583
|
-
},
|
|
584
|
-
required: ['company'],
|
|
585
|
-
},
|
|
586
|
-
then: {
|
|
587
|
-
properties: {
|
|
588
|
-
role: {
|
|
589
|
-
oneOf: [
|
|
590
|
-
{
|
|
591
|
-
title: 'adminA',
|
|
592
|
-
const: 'adminA',
|
|
593
|
-
},
|
|
594
|
-
{
|
|
595
|
-
title: 'userA',
|
|
596
|
-
const: 'userA',
|
|
597
|
-
},
|
|
598
|
-
],
|
|
599
|
-
'x-jsf-presentation': {
|
|
600
|
-
inputType: 'select',
|
|
601
|
-
},
|
|
602
|
-
},
|
|
603
|
-
},
|
|
604
|
-
required: ['role'],
|
|
605
|
-
},
|
|
606
|
-
},
|
|
607
|
-
{
|
|
608
|
-
if: {
|
|
609
|
-
properties: {
|
|
610
|
-
company: {
|
|
611
|
-
const: 'B',
|
|
612
|
-
},
|
|
613
|
-
},
|
|
614
|
-
required: ['company'],
|
|
615
|
-
},
|
|
616
|
-
then: {
|
|
617
|
-
properties: {
|
|
618
|
-
role: {
|
|
619
|
-
oneOf: [
|
|
620
|
-
{
|
|
621
|
-
title: 'adminB',
|
|
622
|
-
const: 'adminB',
|
|
623
|
-
},
|
|
624
|
-
{
|
|
625
|
-
title: 'userB',
|
|
626
|
-
const: 'userB',
|
|
627
|
-
},
|
|
628
|
-
],
|
|
629
|
-
'x-jsf-presentation': {
|
|
630
|
-
inputType: 'select',
|
|
631
|
-
},
|
|
632
|
-
},
|
|
633
|
-
},
|
|
634
|
-
required: ['role'],
|
|
635
|
-
},
|
|
636
|
-
},
|
|
637
|
-
],
|
|
638
|
-
required: ['company', 'role'],
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
handleValidation({ company: 'A' });
|
|
642
|
-
expect(fields[1].options).toEqual([
|
|
643
|
-
{ label: 'adminA', value: 'adminA' },
|
|
644
|
-
{ label: 'userA', value: 'userA' },
|
|
645
|
-
]);
|
|
646
|
-
handleValidation({ company: 'B' });
|
|
647
|
-
expect(fields[1].options).toEqual([
|
|
648
|
-
{ label: 'adminB', value: 'adminB' },
|
|
649
|
-
{ label: 'userB', value: 'userB' },
|
|
650
|
-
]);
|
|
651
|
-
});
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
describe('Conditional with a minimum value check', () => {
|
|
655
|
-
it('Should handle a maximum as a property field check', () => {
|
|
656
|
-
const schema = {
|
|
657
|
-
additionalProperties: false,
|
|
658
|
-
allOf: [
|
|
659
|
-
{
|
|
660
|
-
if: {
|
|
661
|
-
properties: {
|
|
662
|
-
salary: {
|
|
663
|
-
maximum: 119999,
|
|
664
|
-
},
|
|
665
|
-
},
|
|
666
|
-
required: ['salary'],
|
|
667
|
-
},
|
|
668
|
-
then: {
|
|
669
|
-
required: ['reason'],
|
|
670
|
-
},
|
|
671
|
-
else: {
|
|
672
|
-
properties: {
|
|
673
|
-
reason: false,
|
|
674
|
-
},
|
|
675
|
-
},
|
|
676
|
-
},
|
|
677
|
-
],
|
|
678
|
-
properties: {
|
|
679
|
-
salary: {
|
|
680
|
-
type: 'number',
|
|
681
|
-
'x-jsf-presentation': {
|
|
682
|
-
inputType: 'money',
|
|
683
|
-
},
|
|
684
|
-
},
|
|
685
|
-
reason: {
|
|
686
|
-
oneOf: [
|
|
687
|
-
{
|
|
688
|
-
const: 'reason_one',
|
|
689
|
-
},
|
|
690
|
-
{
|
|
691
|
-
const: 'reason_two',
|
|
692
|
-
},
|
|
693
|
-
],
|
|
694
|
-
type: 'string',
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
required: ['salary'],
|
|
698
|
-
type: 'object',
|
|
699
|
-
};
|
|
700
|
-
|
|
701
|
-
const { handleValidation } = createHeadlessForm(schema, { strictInputType: false });
|
|
702
|
-
expect(handleValidation({ salary: 120000 }).formErrors).toEqual(undefined);
|
|
703
|
-
expect(handleValidation({ salary: 1000 }).formErrors).toEqual({
|
|
704
|
-
reason: 'Required field',
|
|
705
|
-
});
|
|
706
|
-
expect(handleValidation({ salary: 1000, reason: 'reason_one' }).formErrors).toEqual(undefined);
|
|
707
|
-
});
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
describe('Conditional with literal booleans', () => {
|
|
711
|
-
it('handles true case', () => {
|
|
712
|
-
const schema = {
|
|
713
|
-
properties: {
|
|
714
|
-
is_full_time: {
|
|
715
|
-
type: 'boolean',
|
|
716
|
-
},
|
|
717
|
-
salary: {
|
|
718
|
-
type: 'number',
|
|
719
|
-
},
|
|
720
|
-
},
|
|
721
|
-
required: [],
|
|
722
|
-
if: true,
|
|
723
|
-
then: {
|
|
724
|
-
required: ['is_full_time'],
|
|
725
|
-
},
|
|
726
|
-
else: {
|
|
727
|
-
required: ['salary'],
|
|
728
|
-
},
|
|
729
|
-
};
|
|
730
|
-
const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
|
|
731
|
-
|
|
732
|
-
handleValidation({});
|
|
733
|
-
|
|
734
|
-
expect(fields[0]).toMatchObject({
|
|
735
|
-
name: 'is_full_time',
|
|
736
|
-
required: true,
|
|
737
|
-
});
|
|
738
|
-
expect(fields[1]).toMatchObject({
|
|
739
|
-
name: 'salary',
|
|
740
|
-
required: false,
|
|
741
|
-
});
|
|
742
|
-
});
|
|
743
|
-
|
|
744
|
-
it('handles false case', () => {
|
|
745
|
-
const schema = {
|
|
746
|
-
properties: {
|
|
747
|
-
is_full_time: {
|
|
748
|
-
type: 'boolean',
|
|
749
|
-
},
|
|
750
|
-
salary: {
|
|
751
|
-
type: 'number',
|
|
752
|
-
},
|
|
753
|
-
},
|
|
754
|
-
required: [],
|
|
755
|
-
if: false,
|
|
756
|
-
then: {
|
|
757
|
-
required: ['is_full_time'],
|
|
758
|
-
},
|
|
759
|
-
else: {
|
|
760
|
-
required: ['salary'],
|
|
761
|
-
},
|
|
762
|
-
};
|
|
763
|
-
const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
|
|
764
|
-
|
|
765
|
-
handleValidation({});
|
|
766
|
-
|
|
767
|
-
expect(fields[0]).toMatchObject({
|
|
768
|
-
name: 'is_full_time',
|
|
769
|
-
required: false,
|
|
770
|
-
});
|
|
771
|
-
expect(fields[1]).toMatchObject({
|
|
772
|
-
name: 'salary',
|
|
773
|
-
required: true,
|
|
774
|
-
});
|
|
775
|
-
});
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
describe('Conditionals - bugs and code-smells', () => {
|
|
779
|
-
// Why do we have these bugs?
|
|
780
|
-
// To be honest we never realized it much later later.
|
|
781
|
-
// We will fix them in the next major version.
|
|
782
|
-
|
|
783
|
-
const schemaHasPet = {
|
|
784
|
-
type: 'object',
|
|
785
|
-
additionalProperties: false,
|
|
786
|
-
properties: {
|
|
787
|
-
has_pet: {
|
|
788
|
-
title: 'Has Pet',
|
|
789
|
-
description: 'Do you have a pet?',
|
|
790
|
-
oneOf: [
|
|
791
|
-
{ title: 'Yes', const: 'yes' },
|
|
792
|
-
{ title: 'No', const: 'no' },
|
|
793
|
-
],
|
|
794
|
-
type: 'string',
|
|
795
|
-
},
|
|
796
|
-
pet_name: {
|
|
797
|
-
title: "Pet's name",
|
|
798
|
-
type: 'string',
|
|
799
|
-
},
|
|
800
|
-
},
|
|
801
|
-
required: ['has_pet'],
|
|
802
|
-
allOf: [
|
|
803
|
-
{
|
|
804
|
-
if: {
|
|
805
|
-
properties: {
|
|
806
|
-
has_pet: { const: 'yes' },
|
|
807
|
-
},
|
|
808
|
-
required: ['has_pet'],
|
|
809
|
-
},
|
|
810
|
-
then: {
|
|
811
|
-
required: ['pet_name'],
|
|
812
|
-
},
|
|
813
|
-
else: {
|
|
814
|
-
properties: {
|
|
815
|
-
pet_name: false,
|
|
816
|
-
},
|
|
817
|
-
},
|
|
818
|
-
},
|
|
819
|
-
],
|
|
820
|
-
};
|
|
821
|
-
|
|
822
|
-
it('Given values from hidden fields, it does not thrown an error (@bug)', () => {
|
|
823
|
-
const { fields, handleValidation } = createHeadlessForm(schemaHasPet, {
|
|
824
|
-
strictInputType: false,
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
const petNameField = fields[1];
|
|
828
|
-
|
|
829
|
-
const validation = handleValidation({ has_pet: 'no', pet_name: 'Max' });
|
|
830
|
-
expect(petNameField.isVisible).toBe(false);
|
|
831
|
-
|
|
832
|
-
// Bug: 🐛 It does not thrown an error,
|
|
833
|
-
// but it should to be compliant with JSON Schema specs.
|
|
834
|
-
expect(validation.formErrors).toBeUndefined();
|
|
835
|
-
// The error should be something like:
|
|
836
|
-
// expect(validation.formErrors).toEqual({ pet_name: 'Not allowed.'});
|
|
837
|
-
});
|
|
838
|
-
|
|
839
|
-
it('Given values from hidden fields, it mutates the values (@bug)', () => {
|
|
840
|
-
const { handleValidation } = createHeadlessForm(schemaHasPet, {
|
|
841
|
-
strictInputType: false,
|
|
842
|
-
});
|
|
843
|
-
|
|
844
|
-
const newValues = { has_pet: 'no', pet_name: 'Max' };
|
|
845
|
-
|
|
846
|
-
const validation = handleValidation(newValues);
|
|
847
|
-
|
|
848
|
-
expect(newValues).toEqual({
|
|
849
|
-
has_pet: 'no',
|
|
850
|
-
pet_name: null, // BUG! 🐛 Should still be "Max", should not be mutated.
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
// Same bug as explained in the previous test.
|
|
854
|
-
expect(validation.formErrors).toBeUndefined();
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
it('Given multiple conditionals to the same field, it only applies the last one (@bug) - case 1', () => {
|
|
858
|
-
const { handleValidation } = createHeadlessForm(
|
|
859
|
-
{
|
|
860
|
-
additionalProperties: false,
|
|
861
|
-
properties: {
|
|
862
|
-
field_a: { type: 'string' },
|
|
863
|
-
field_b: { type: 'string' },
|
|
864
|
-
field_c: { type: 'number' },
|
|
865
|
-
},
|
|
866
|
-
allOf: [
|
|
867
|
-
{
|
|
868
|
-
if: {
|
|
869
|
-
properties: { field_a: { const: 'yes' } },
|
|
870
|
-
required: ['field_a'],
|
|
871
|
-
},
|
|
872
|
-
then: { properties: { field_c: { minimum: 30 } } },
|
|
873
|
-
},
|
|
874
|
-
{
|
|
875
|
-
if: {
|
|
876
|
-
properties: { field_b: { const: 'yes' } },
|
|
877
|
-
required: ['field_b'],
|
|
878
|
-
},
|
|
879
|
-
then: { properties: { field_c: { minimum: 10 } } },
|
|
880
|
-
},
|
|
881
|
-
],
|
|
882
|
-
},
|
|
883
|
-
{
|
|
884
|
-
strictInputType: false,
|
|
885
|
-
}
|
|
886
|
-
);
|
|
887
|
-
|
|
888
|
-
const validation = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 5 });
|
|
889
|
-
expect(validation.formErrors).toEqual({
|
|
890
|
-
// BUG: 🐛 it should be "Must be greater or equal to 30"
|
|
891
|
-
field_c: 'Must be greater or equal to 10',
|
|
892
|
-
});
|
|
893
|
-
});
|
|
894
|
-
|
|
895
|
-
it('Given multiple conditionals to the same field, it only applies the last one (@bug) - case 2', () => {
|
|
896
|
-
const { handleValidation } = createHeadlessForm(
|
|
897
|
-
{
|
|
898
|
-
additionalProperties: false,
|
|
899
|
-
properties: {
|
|
900
|
-
field_a: { type: 'string' },
|
|
901
|
-
field_b: { type: 'string' },
|
|
902
|
-
field_c: { type: 'number' },
|
|
903
|
-
},
|
|
904
|
-
allOf: [
|
|
905
|
-
{
|
|
906
|
-
if: {
|
|
907
|
-
properties: { field_a: { const: 'yes' } },
|
|
908
|
-
required: ['field_a'],
|
|
909
|
-
},
|
|
910
|
-
then: { properties: { field_c: { minimum: 5 } } },
|
|
911
|
-
},
|
|
912
|
-
{
|
|
913
|
-
if: {
|
|
914
|
-
properties: { field_b: { const: 'yes' } },
|
|
915
|
-
required: ['field_b'],
|
|
916
|
-
},
|
|
917
|
-
then: { properties: { field_c: { maximum: 10 } } },
|
|
918
|
-
},
|
|
919
|
-
],
|
|
920
|
-
},
|
|
921
|
-
{
|
|
922
|
-
strictInputType: false,
|
|
923
|
-
}
|
|
924
|
-
);
|
|
925
|
-
|
|
926
|
-
const validation1 = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 12 });
|
|
927
|
-
expect(validation1.formErrors).toEqual({
|
|
928
|
-
field_c: 'Must be smaller or equal to 10',
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
const validation2 = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 3 });
|
|
932
|
-
// BUG: 🐛 it should be "Must be greater or equal to 5"
|
|
933
|
-
expect(validation2.formErrors).toBeUndefined();
|
|
934
|
-
// expect(validation1.formErrors).toEqual({
|
|
935
|
-
// field_c: 'Must be greater or equal to 5',
|
|
936
|
-
// });
|
|
937
|
-
});
|
|
938
|
-
});
|