@jsonforms/core 3.0.0-beta.1 → 3.0.0-beta.4
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/README.md +11 -0
- package/docs/assets/js/search.json +1 -1
- package/docs/enums/ruleeffect.html +4 -4
- package/docs/globals.html +357 -217
- package/docs/index.html +29 -4
- package/docs/interfaces/addcellrendereraction.html +3 -3
- package/docs/interfaces/addrendereraction.html +3 -3
- package/docs/interfaces/adduischemaaction.html +3 -3
- package/docs/interfaces/andcondition.html +2 -2
- package/docs/interfaces/arraycontrolprops.html +21 -21
- package/docs/interfaces/arraylayoutprops.html +21 -21
- package/docs/interfaces/categorization.html +5 -5
- package/docs/interfaces/category.html +5 -5
- package/docs/interfaces/cellprops.html +12 -12
- package/docs/interfaces/combinatorrendererprops.html +143 -36
- package/docs/interfaces/composablecondition.html +2 -2
- package/docs/interfaces/condition.html +1 -1
- package/docs/interfaces/controlelement.html +22 -4
- package/docs/interfaces/controlprops.html +16 -16
- package/docs/interfaces/controlstate.html +2 -2
- package/docs/interfaces/controlwithdetailprops.html +17 -17
- package/docs/interfaces/dispatchcellprops.html +10 -10
- package/docs/interfaces/dispatchcellstateprops.html +10 -10
- package/docs/interfaces/dispatchpropsofarraycontrol.html +4 -4
- package/docs/interfaces/dispatchpropsofcontrol.html +1 -1
- package/docs/interfaces/dispatchpropsofmultienumcontrol.html +2 -2
- package/docs/interfaces/enumcellprops.html +13 -13
- package/docs/interfaces/enumoption.html +2 -2
- package/docs/interfaces/grouplayout.html +5 -5
- package/docs/interfaces/horizontallayout.html +4 -4
- package/docs/interfaces/initactionoptions.html +14 -0
- package/docs/interfaces/internationalizable.html +167 -0
- package/docs/interfaces/jsonformscore.html +17 -3
- package/docs/interfaces/jsonformsprops.html +9 -9
- package/docs/interfaces/labeldescription.html +2 -2
- package/docs/interfaces/labelelement.html +4 -4
- package/docs/interfaces/layout.html +4 -4
- package/docs/interfaces/layoutprops.html +10 -10
- package/docs/interfaces/leafcondition.html +2 -2
- package/docs/interfaces/orcondition.html +2 -2
- package/docs/interfaces/ownpropsofcell.html +9 -9
- package/docs/interfaces/ownpropsofcontrol.html +9 -12
- package/docs/interfaces/ownpropsofenum.html +1 -1
- package/docs/interfaces/ownpropsofenumcell.html +10 -10
- package/docs/interfaces/ownpropsofjsonformsrenderer.html +8 -8
- package/docs/interfaces/ownpropsoflayout.html +9 -9
- package/docs/interfaces/ownpropsofmasterlistitem.html +6 -6
- package/docs/interfaces/ownpropsofrenderer.html +8 -8
- package/docs/interfaces/registerdefaultdataaction.html +3 -3
- package/docs/interfaces/removecellrendereraction.html +3 -3
- package/docs/interfaces/removerendereraction.html +3 -3
- package/docs/interfaces/removeuischemaaction.html +2 -2
- package/docs/interfaces/rendererprops.html +9 -9
- package/docs/interfaces/rule.html +2 -2
- package/docs/interfaces/schemabasedcondition.html +2 -2
- package/docs/interfaces/setajvaction.html +2 -2
- package/docs/interfaces/setconfigaction.html +2 -2
- package/docs/interfaces/setlocaleaction.html +2 -2
- package/docs/interfaces/setschemaaction.html +2 -2
- package/docs/interfaces/settranslatoraction.html +3 -3
- package/docs/interfaces/setuischemaaction.html +2 -2
- package/docs/interfaces/setvalidationmodeaction.html +2 -2
- package/docs/interfaces/statepropsofarraycontrol.html +17 -17
- package/docs/interfaces/statepropsofarraylayout.html +17 -17
- package/docs/interfaces/statepropsofcell.html +11 -11
- package/docs/interfaces/statepropsofcombinator.html +147 -40
- package/docs/interfaces/statepropsofcontrol.html +18 -15
- package/docs/interfaces/statepropsofcontrolwithdetail.html +16 -16
- package/docs/interfaces/statepropsofenumcell.html +12 -12
- package/docs/interfaces/statepropsofjsonformsrenderer.html +9 -9
- package/docs/interfaces/statepropsoflayout.html +10 -10
- package/docs/interfaces/statepropsofmasteritem.html +7 -7
- package/docs/interfaces/statepropsofrenderer.html +9 -9
- package/docs/interfaces/statepropsofscopedrenderer.html +12 -12
- package/docs/interfaces/uischemaelement.html +3 -3
- package/docs/interfaces/unregisterdefaultdataaction.html +2 -2
- package/docs/interfaces/updatei18naction.html +4 -4
- package/docs/interfaces/verticallayout.html +4 -4
- package/docs/interfaces/withclassname.html +1 -1
- package/lib/actions/actions.d.ts +1 -0
- package/lib/jsonforms-core.cjs.js +201 -179
- package/lib/jsonforms-core.cjs.js.map +1 -1
- package/lib/jsonforms-core.esm.js +182 -166
- package/lib/jsonforms-core.esm.js.map +1 -1
- package/lib/models/uischema.d.ts +10 -1
- package/lib/reducers/core.d.ts +1 -0
- package/lib/reducers/reducers.d.ts +2 -2
- package/lib/testers/testers.d.ts +8 -7
- package/lib/util/combinators.d.ts +0 -1
- package/lib/util/path.d.ts +10 -0
- package/lib/util/renderer.d.ts +2 -2
- package/lib/util/resolvers.d.ts +1 -1
- package/lib/util/util.d.ts +1 -1
- package/package.json +2 -2
- package/src/actions/actions.ts +1 -0
- package/src/generators/uischema.ts +4 -4
- package/src/i18n/i18nUtil.ts +10 -7
- package/src/models/uischema.ts +14 -1
- package/src/reducers/core.ts +30 -3
- package/src/reducers/reducers.ts +12 -4
- package/src/testers/testers.ts +61 -34
- package/src/util/combinators.ts +17 -32
- package/src/util/label.ts +2 -2
- package/src/util/path.ts +18 -6
- package/src/util/renderer.ts +20 -32
- package/src/util/resolvers.ts +57 -68
- package/src/util/util.ts +1 -1
- package/stats.html +1 -1
- package/test/generators/uischema.test.ts +18 -0
- package/test/i18n/i18nUtil.test.ts +41 -1
- package/test/reducers/core.test.ts +203 -1
- package/test/testers.test.ts +208 -120
- package/test/util/path.test.ts +37 -20
- package/test/util/renderer.test.ts +1 -1
- package/test/util/resolvers.test.ts +99 -8
package/src/testers/testers.ts
CHANGED
|
@@ -47,15 +47,17 @@ import { deriveTypes, hasType, resolveSchema } from '../util';
|
|
|
47
47
|
export const NOT_APPLICABLE = -1;
|
|
48
48
|
/**
|
|
49
49
|
* A tester is a function that receives an UI schema and a JSON schema and returns a boolean.
|
|
50
|
+
* The rootSchema is handed over as context. Can be used to resolve references.
|
|
50
51
|
*/
|
|
51
|
-
export type Tester = (uischema: UISchemaElement, schema: JsonSchema) => boolean;
|
|
52
|
+
export type Tester = (uischema: UISchemaElement, schema: JsonSchema, rootSchema: JsonSchema) => boolean;
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* A ranked tester associates a tester with a number.
|
|
55
56
|
*/
|
|
56
57
|
export type RankedTester = (
|
|
57
58
|
uischema: UISchemaElement,
|
|
58
|
-
schema: JsonSchema
|
|
59
|
+
schema: JsonSchema,
|
|
60
|
+
rootSchema: JsonSchema
|
|
59
61
|
) => number;
|
|
60
62
|
|
|
61
63
|
export const isControl = (uischema: any): uischema is ControlElement =>
|
|
@@ -72,8 +74,8 @@ export const isControl = (uischema: any): uischema is ControlElement =>
|
|
|
72
74
|
* applied to the resolved sub-schema
|
|
73
75
|
*/
|
|
74
76
|
export const schemaMatches = (
|
|
75
|
-
predicate: (schema: JsonSchema) => boolean
|
|
76
|
-
): Tester => (uischema: UISchemaElement, schema: JsonSchema): boolean => {
|
|
77
|
+
predicate: (schema: JsonSchema, rootSchema: JsonSchema) => boolean
|
|
78
|
+
): Tester => (uischema: UISchemaElement, schema: JsonSchema, rootSchema: JsonSchema): boolean => {
|
|
77
79
|
if (isEmpty(uischema) || !isControl(uischema)) {
|
|
78
80
|
return false;
|
|
79
81
|
}
|
|
@@ -86,26 +88,26 @@ export const schemaMatches = (
|
|
|
86
88
|
}
|
|
87
89
|
let currentDataSchema = schema;
|
|
88
90
|
if (hasType(schema, 'object')) {
|
|
89
|
-
currentDataSchema = resolveSchema(schema, schemaPath);
|
|
91
|
+
currentDataSchema = resolveSchema(schema, schemaPath, rootSchema);
|
|
90
92
|
}
|
|
91
93
|
if (currentDataSchema === undefined) {
|
|
92
94
|
return false;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
return predicate(currentDataSchema);
|
|
97
|
+
return predicate(currentDataSchema, rootSchema);
|
|
96
98
|
};
|
|
97
99
|
|
|
98
100
|
export const schemaSubPathMatches = (
|
|
99
101
|
subPath: string,
|
|
100
|
-
predicate: (schema: JsonSchema) => boolean
|
|
101
|
-
): Tester => (uischema: UISchemaElement, schema: JsonSchema): boolean => {
|
|
102
|
+
predicate: (schema: JsonSchema, rootSchema: JsonSchema) => boolean
|
|
103
|
+
): Tester => (uischema: UISchemaElement, schema: JsonSchema, rootSchema: JsonSchema): boolean => {
|
|
102
104
|
if (isEmpty(uischema) || !isControl(uischema)) {
|
|
103
105
|
return false;
|
|
104
106
|
}
|
|
105
107
|
const schemaPath = uischema.scope;
|
|
106
108
|
let currentDataSchema: JsonSchema = schema;
|
|
107
109
|
if (hasType(schema, 'object')) {
|
|
108
|
-
currentDataSchema = resolveSchema(schema, schemaPath);
|
|
110
|
+
currentDataSchema = resolveSchema(schema, schemaPath, rootSchema);
|
|
109
111
|
}
|
|
110
112
|
currentDataSchema = get(currentDataSchema, subPath);
|
|
111
113
|
|
|
@@ -113,7 +115,7 @@ export const schemaSubPathMatches = (
|
|
|
113
115
|
return false;
|
|
114
116
|
}
|
|
115
117
|
|
|
116
|
-
return predicate(currentDataSchema);
|
|
118
|
+
return predicate(currentDataSchema, rootSchema);
|
|
117
119
|
};
|
|
118
120
|
|
|
119
121
|
/**
|
|
@@ -142,7 +144,7 @@ export const formatIs = (expectedFormat: string): Tester =>
|
|
|
142
144
|
schema =>
|
|
143
145
|
!isEmpty(schema) &&
|
|
144
146
|
schema.format === expectedFormat &&
|
|
145
|
-
schema
|
|
147
|
+
hasType(schema, 'string')
|
|
146
148
|
);
|
|
147
149
|
|
|
148
150
|
/**
|
|
@@ -215,8 +217,9 @@ export const scopeEndIs = (expected: string): Tester => (
|
|
|
215
217
|
*/
|
|
216
218
|
export const and = (...testers: Tester[]): Tester => (
|
|
217
219
|
uischema: UISchemaElement,
|
|
218
|
-
schema: JsonSchema
|
|
219
|
-
|
|
220
|
+
schema: JsonSchema,
|
|
221
|
+
rootSchema: JsonSchema
|
|
222
|
+
) => testers.reduce((acc, tester) => acc && tester(uischema, schema, rootSchema), true);
|
|
220
223
|
|
|
221
224
|
/**
|
|
222
225
|
* A tester that allow composing other testers by || them.
|
|
@@ -225,8 +228,9 @@ export const and = (...testers: Tester[]): Tester => (
|
|
|
225
228
|
*/
|
|
226
229
|
export const or = (...testers: Tester[]): Tester => (
|
|
227
230
|
uischema: UISchemaElement,
|
|
228
|
-
schema: JsonSchema
|
|
229
|
-
|
|
231
|
+
schema: JsonSchema,
|
|
232
|
+
rootSchema: JsonSchema
|
|
233
|
+
) => testers.reduce((acc, tester) => acc || tester(uischema, schema, rootSchema), false);
|
|
230
234
|
/**
|
|
231
235
|
* Create a ranked tester that will associate a number with a given tester, if the
|
|
232
236
|
* latter returns true.
|
|
@@ -236,9 +240,10 @@ export const or = (...testers: Tester[]): Tester => (
|
|
|
236
240
|
*/
|
|
237
241
|
export const rankWith = (rank: number, tester: Tester) => (
|
|
238
242
|
uischema: UISchemaElement,
|
|
239
|
-
schema: JsonSchema
|
|
243
|
+
schema: JsonSchema,
|
|
244
|
+
rootSchema: JsonSchema
|
|
240
245
|
): number => {
|
|
241
|
-
if (tester(uischema, schema)) {
|
|
246
|
+
if (tester(uischema, schema, rootSchema)) {
|
|
242
247
|
return rank;
|
|
243
248
|
}
|
|
244
249
|
|
|
@@ -247,9 +252,10 @@ export const rankWith = (rank: number, tester: Tester) => (
|
|
|
247
252
|
|
|
248
253
|
export const withIncreasedRank = (by: number, rankedTester: RankedTester) => (
|
|
249
254
|
uischema: UISchemaElement,
|
|
250
|
-
schema: JsonSchema
|
|
255
|
+
schema: JsonSchema,
|
|
256
|
+
rootSchema: JsonSchema
|
|
251
257
|
): number => {
|
|
252
|
-
const rank = rankedTester(uischema, schema);
|
|
258
|
+
const rank = rankedTester(uischema, schema, rootSchema);
|
|
253
259
|
if (rank === NOT_APPLICABLE) {
|
|
254
260
|
return NOT_APPLICABLE;
|
|
255
261
|
}
|
|
@@ -380,9 +386,12 @@ export const isDateTimeControl = and(
|
|
|
380
386
|
*/
|
|
381
387
|
export const isObjectArray = and(
|
|
382
388
|
schemaMatches(
|
|
383
|
-
schema => hasType(schema, 'array') && !Array.isArray(schema
|
|
389
|
+
(schema, rootSchema) => hasType(schema, 'array') && !Array.isArray(resolveSchema(schema, 'items', rootSchema)) // we don't care about tuples
|
|
384
390
|
),
|
|
385
|
-
schemaSubPathMatches('items',
|
|
391
|
+
schemaSubPathMatches('items', (schema, rootSchema) => {
|
|
392
|
+
const resolvedSchema = schema.$ref ? resolveSchema(rootSchema, schema.$ref, rootSchema) : schema;
|
|
393
|
+
return hasType(resolvedSchema, 'object')
|
|
394
|
+
})
|
|
386
395
|
);
|
|
387
396
|
|
|
388
397
|
/**
|
|
@@ -394,22 +403,31 @@ export const isObjectArrayControl = and(uiTypeIs('Control'), isObjectArray);
|
|
|
394
403
|
|
|
395
404
|
const traverse = (
|
|
396
405
|
any: JsonSchema | JsonSchema[],
|
|
397
|
-
pred: (obj: JsonSchema) => boolean
|
|
406
|
+
pred: (obj: JsonSchema) => boolean,
|
|
407
|
+
rootSchema: JsonSchema
|
|
398
408
|
): boolean => {
|
|
399
409
|
if (isArray(any)) {
|
|
400
|
-
return reduce(any, (acc, el) => acc || traverse(el, pred), false);
|
|
410
|
+
return reduce(any, (acc, el) => acc || traverse(el, pred, rootSchema), false);
|
|
401
411
|
}
|
|
402
412
|
|
|
403
413
|
if (pred(any)) {
|
|
404
414
|
return true;
|
|
405
415
|
}
|
|
416
|
+
|
|
417
|
+
if (any.$ref) {
|
|
418
|
+
const toTraverse = resolveSchema(rootSchema, any.$ref, rootSchema);
|
|
419
|
+
if (toTraverse && !toTraverse.$ref) {
|
|
420
|
+
return traverse(toTraverse, pred, rootSchema);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
406
424
|
if (any.items) {
|
|
407
|
-
return traverse(any.items, pred);
|
|
425
|
+
return traverse(any.items, pred, rootSchema);
|
|
408
426
|
}
|
|
409
427
|
if (any.properties) {
|
|
410
428
|
return reduce(
|
|
411
429
|
toPairs(any.properties),
|
|
412
|
-
(acc, [_key, val]) => acc || traverse(val, pred),
|
|
430
|
+
(acc, [_key, val]) => acc || traverse(val, pred, rootSchema),
|
|
413
431
|
false
|
|
414
432
|
);
|
|
415
433
|
}
|
|
@@ -419,13 +437,14 @@ const traverse = (
|
|
|
419
437
|
|
|
420
438
|
export const isObjectArrayWithNesting = (
|
|
421
439
|
uischema: UISchemaElement,
|
|
422
|
-
schema: JsonSchema
|
|
440
|
+
schema: JsonSchema,
|
|
441
|
+
rootSchema: JsonSchema
|
|
423
442
|
): boolean => {
|
|
424
|
-
if (!uiTypeIs('Control')(uischema, schema)) {
|
|
443
|
+
if (!uiTypeIs('Control')(uischema, schema, rootSchema)) {
|
|
425
444
|
return false;
|
|
426
445
|
}
|
|
427
446
|
const schemaPath = (uischema as ControlElement).scope;
|
|
428
|
-
const resolvedSchema = resolveSchema(schema, schemaPath);
|
|
447
|
+
const resolvedSchema = resolveSchema(schema, schemaPath, rootSchema ?? schema);
|
|
429
448
|
const wantedNestingByType: { [key: string]: number } = {
|
|
430
449
|
object: 2,
|
|
431
450
|
array: 1
|
|
@@ -437,6 +456,9 @@ export const isObjectArrayWithNesting = (
|
|
|
437
456
|
if (val === schema) {
|
|
438
457
|
return false;
|
|
439
458
|
}
|
|
459
|
+
if (val.$ref !== undefined) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
440
462
|
// we don't support multiple types
|
|
441
463
|
if (typeof val.type !== 'string') {
|
|
442
464
|
return true;
|
|
@@ -447,7 +469,7 @@ export const isObjectArrayWithNesting = (
|
|
|
447
469
|
}
|
|
448
470
|
wantedNestingByType[val.type] = typeCount - 1;
|
|
449
471
|
return wantedNestingByType[val.type] === 0;
|
|
450
|
-
})
|
|
472
|
+
}, rootSchema)
|
|
451
473
|
) {
|
|
452
474
|
return true;
|
|
453
475
|
}
|
|
@@ -479,10 +501,13 @@ export const isArrayObjectControl = isObjectArrayControl;
|
|
|
479
501
|
export const isPrimitiveArrayControl = and(
|
|
480
502
|
uiTypeIs('Control'),
|
|
481
503
|
schemaMatches(
|
|
482
|
-
|
|
504
|
+
(schema, rootSchema) =>
|
|
505
|
+
deriveTypes(schema).length !== 0 &&
|
|
506
|
+
!Array.isArray(resolveSchema(schema, 'items', rootSchema)) // we don't care about tuples
|
|
483
507
|
),
|
|
484
|
-
schemaSubPathMatches('items', schema => {
|
|
485
|
-
const
|
|
508
|
+
schemaSubPathMatches('items', (schema, rootSchema) => {
|
|
509
|
+
const resolvedSchema = schema.$ref ? resolveSchema(rootSchema, schema.$ref, rootSchema) : schema;
|
|
510
|
+
const types = deriveTypes(resolvedSchema);
|
|
486
511
|
return (
|
|
487
512
|
types.length === 1 &&
|
|
488
513
|
includes(['integer', 'number', 'boolean', 'string'], types[0])
|
|
@@ -543,5 +568,7 @@ export const categorizationHasCategory = (uischema: UISchemaElement) =>
|
|
|
543
568
|
|
|
544
569
|
export const not = (tester: Tester): Tester => (
|
|
545
570
|
uischema: UISchemaElement,
|
|
546
|
-
schema: JsonSchema
|
|
547
|
-
|
|
571
|
+
schema: JsonSchema,
|
|
572
|
+
rootSchema: JsonSchema
|
|
573
|
+
|
|
574
|
+
) => !tester(uischema, schema, rootSchema);
|
package/src/util/combinators.ts
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { ControlElement, JsonSchema, UISchemaElement } from '../models';
|
|
27
|
-
import { resolveSchema } from './resolvers';
|
|
28
27
|
import { findUISchema, JsonFormsUISchemaRegistryEntry } from '../reducers';
|
|
28
|
+
import { Resolve } from './util';
|
|
29
29
|
|
|
30
30
|
export interface CombinatorSubSchemaRenderInfo {
|
|
31
31
|
schema: JsonSchema;
|
|
@@ -47,24 +47,6 @@ const createLabel = (
|
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
export const resolveSubSchemas = (
|
|
51
|
-
schema: JsonSchema,
|
|
52
|
-
rootSchema: JsonSchema,
|
|
53
|
-
keyword: CombinatorKeyword
|
|
54
|
-
) => {
|
|
55
|
-
// resolve any $refs, otherwise the generated UI schema can't match the schema???
|
|
56
|
-
const schemas = schema[keyword] as any[];
|
|
57
|
-
if (schemas.findIndex(e => e.$ref !== undefined) !== -1) {
|
|
58
|
-
return {
|
|
59
|
-
...schema,
|
|
60
|
-
[keyword]: (schema[keyword] as any[]).map(e =>
|
|
61
|
-
e.$ref ? resolveSchema(rootSchema, e.$ref) : e
|
|
62
|
-
)
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
return schema;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
50
|
export const createCombinatorRenderInfos = (
|
|
69
51
|
combinatorSubSchemas: JsonSchema[],
|
|
70
52
|
rootSchema: JsonSchema,
|
|
@@ -73,16 +55,19 @@ export const createCombinatorRenderInfos = (
|
|
|
73
55
|
path: string,
|
|
74
56
|
uischemas: JsonFormsUISchemaRegistryEntry[]
|
|
75
57
|
): CombinatorSubSchemaRenderInfo[] =>
|
|
76
|
-
combinatorSubSchemas.map((subSchema, subSchemaIndex) =>
|
|
77
|
-
schema: subSchema
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
58
|
+
combinatorSubSchemas.map((subSchema, subSchemaIndex) => {
|
|
59
|
+
const schema = subSchema.$ref ? Resolve.schema(rootSchema, subSchema.$ref, rootSchema) : subSchema;
|
|
60
|
+
return {
|
|
61
|
+
schema,
|
|
62
|
+
uischema: findUISchema(
|
|
63
|
+
uischemas,
|
|
64
|
+
schema,
|
|
65
|
+
control.scope,
|
|
66
|
+
path,
|
|
67
|
+
undefined,
|
|
68
|
+
control,
|
|
69
|
+
rootSchema
|
|
70
|
+
),
|
|
71
|
+
label: createLabel(subSchema, subSchemaIndex, keyword)
|
|
72
|
+
}
|
|
73
|
+
});
|
package/src/util/label.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
import startCase from 'lodash/startCase';
|
|
27
27
|
|
|
28
28
|
import { ControlElement, JsonSchema, LabelDescription } from '../models';
|
|
29
|
+
import { decode } from './path';
|
|
29
30
|
|
|
30
31
|
const deriveLabel = (
|
|
31
32
|
controlElement: ControlElement,
|
|
@@ -36,8 +37,7 @@ const deriveLabel = (
|
|
|
36
37
|
}
|
|
37
38
|
if (typeof controlElement.scope === 'string') {
|
|
38
39
|
const ref = controlElement.scope;
|
|
39
|
-
const label = ref.substr(ref.lastIndexOf('/') + 1);
|
|
40
|
-
|
|
40
|
+
const label = decode(ref.substr(ref.lastIndexOf('/') + 1));
|
|
41
41
|
return startCase(label);
|
|
42
42
|
}
|
|
43
43
|
|
package/src/util/path.ts
CHANGED
|
@@ -57,14 +57,15 @@ export { compose as composePaths };
|
|
|
57
57
|
*/
|
|
58
58
|
export const toDataPathSegments = (schemaPath: string): string[] => {
|
|
59
59
|
const s = schemaPath
|
|
60
|
-
.replace(/anyOf\/[\d]\//g, '')
|
|
61
|
-
.replace(/
|
|
62
|
-
.replace(/oneOf\/[\d]\//g, '');
|
|
60
|
+
.replace(/(anyOf|allOf|oneOf)\/[\d]\//g, '')
|
|
61
|
+
.replace(/(then|else)\//g, '');
|
|
63
62
|
const segments = s.split('/');
|
|
64
63
|
|
|
65
|
-
const
|
|
64
|
+
const decodedSegments = segments.map(decode);
|
|
65
|
+
|
|
66
|
+
const startFromRoot = decodedSegments[0] === '#' || decodedSegments[0] === '';
|
|
66
67
|
const startIndex = startFromRoot ? 2 : 1;
|
|
67
|
-
return range(startIndex,
|
|
68
|
+
return range(startIndex, decodedSegments.length, 2).map(idx => decodedSegments[idx]);
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
/**
|
|
@@ -77,7 +78,7 @@ export const toDataPathSegments = (schemaPath: string): string[] => {
|
|
|
77
78
|
*/
|
|
78
79
|
export const toDataPath = (schemaPath: string): string => {
|
|
79
80
|
return toDataPathSegments(schemaPath).join('.');
|
|
80
|
-
};
|
|
81
|
+
};
|
|
81
82
|
|
|
82
83
|
export const composeWithUi = (scopableUi: Scopable, path: string): string => {
|
|
83
84
|
const segments = toDataPathSegments(scopableUi.scope);
|
|
@@ -88,3 +89,14 @@ export const composeWithUi = (scopableUi: Scopable, path: string): string => {
|
|
|
88
89
|
|
|
89
90
|
return isEmpty(segments) ? path : compose(path, segments.join('.'));
|
|
90
91
|
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Encodes the given segment to be used as part of a JSON Pointer
|
|
95
|
+
*
|
|
96
|
+
* JSON Pointer has special meaning for "/" and "~", therefore these must be encoded
|
|
97
|
+
*/
|
|
98
|
+
export const encode = (segment: string) => segment?.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
99
|
+
/**
|
|
100
|
+
* Decodes a given JSON Pointer segment to its "normal" representation
|
|
101
|
+
*/
|
|
102
|
+
export const decode = (pointerSegment: string) => pointerSegment?.replace(/~1/g, '/').replace(/~0/, '~');
|
package/src/util/renderer.ts
CHANGED
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
import get from 'lodash/get';
|
|
27
27
|
import { ControlElement, JsonSchema, UISchemaElement } from '../models';
|
|
28
28
|
import find from 'lodash/find';
|
|
29
|
-
import
|
|
29
|
+
import {
|
|
30
|
+
getUISchemas,
|
|
30
31
|
JsonFormsCellRendererRegistryEntry,
|
|
31
32
|
JsonFormsRendererRegistryEntry,
|
|
32
33
|
} from '../reducers';
|
|
@@ -48,7 +49,7 @@ import {
|
|
|
48
49
|
import { RankedTester } from '../testers';
|
|
49
50
|
import { hasShowRule, isInherentlyEnabled, isVisible } from './runtime';
|
|
50
51
|
import { createLabelDescriptionFrom } from './label';
|
|
51
|
-
import { CombinatorKeyword
|
|
52
|
+
import { CombinatorKeyword } from './combinators';
|
|
52
53
|
import { moveDown, moveUp } from './array';
|
|
53
54
|
import { AnyAction, Dispatch } from './type';
|
|
54
55
|
import { Resolve } from './util';
|
|
@@ -65,6 +66,7 @@ const isRequired = (
|
|
|
65
66
|
): boolean => {
|
|
66
67
|
const pathSegments = schemaPath.split('/');
|
|
67
68
|
const lastSegment = pathSegments[pathSegments.length - 1];
|
|
69
|
+
// Skip "properties", "items" etc. to resolve the parent
|
|
68
70
|
const nextHigherSchemaSegments = pathSegments.slice(
|
|
69
71
|
0,
|
|
70
72
|
pathSegments.length - 2
|
|
@@ -463,8 +465,8 @@ export const mapStateToControlProps = (
|
|
|
463
465
|
const schema = resolvedSchema ?? rootSchema;
|
|
464
466
|
const t = getTranslator()(state);
|
|
465
467
|
const te = getErrorTranslator()(state);
|
|
466
|
-
const i18nLabel = t(getI18nKey(schema, uischema, path, 'label'), label);
|
|
467
|
-
const i18nDescription = t(getI18nKey(schema, uischema, path, 'description'), description);
|
|
468
|
+
const i18nLabel = t(getI18nKey(schema, uischema, path, 'label'), label, {schema, uischema, path, errors} );
|
|
469
|
+
const i18nDescription = t(getI18nKey(schema, uischema, path, 'description'), description, {schema, uischema, path, errors});
|
|
468
470
|
const i18nErrorMessage = getCombinedErrorMessage(errors, te, t, schema, uischema, path);
|
|
469
471
|
|
|
470
472
|
return {
|
|
@@ -865,7 +867,8 @@ export const mapStateToLayoutProps = (
|
|
|
865
867
|
data,
|
|
866
868
|
uischema: ownProps.uischema,
|
|
867
869
|
schema: ownProps.schema,
|
|
868
|
-
direction: ownProps.direction ?? getDirection(uischema)
|
|
870
|
+
direction: ownProps.direction ?? getDirection(uischema),
|
|
871
|
+
config
|
|
869
872
|
};
|
|
870
873
|
};
|
|
871
874
|
|
|
@@ -891,7 +894,10 @@ export const mapStateToJsonFormsRendererProps = (
|
|
|
891
894
|
state.jsonforms.uischemas,
|
|
892
895
|
ownProps.schema,
|
|
893
896
|
undefined,
|
|
894
|
-
ownProps.path
|
|
897
|
+
ownProps.path,
|
|
898
|
+
undefined,
|
|
899
|
+
undefined,
|
|
900
|
+
state.jsonforms.core.schema
|
|
895
901
|
);
|
|
896
902
|
} else {
|
|
897
903
|
uischema = getUiSchema(state);
|
|
@@ -913,7 +919,7 @@ export const controlDefaultProps = {
|
|
|
913
919
|
errors: [] as string[]
|
|
914
920
|
};
|
|
915
921
|
|
|
916
|
-
export interface StatePropsOfCombinator extends
|
|
922
|
+
export interface StatePropsOfCombinator extends StatePropsOfControl {
|
|
917
923
|
rootSchema: JsonSchema;
|
|
918
924
|
path: string;
|
|
919
925
|
id: string;
|
|
@@ -927,25 +933,12 @@ export const mapStateToCombinatorRendererProps = (
|
|
|
927
933
|
ownProps: OwnPropsOfControl,
|
|
928
934
|
keyword: CombinatorKeyword
|
|
929
935
|
): StatePropsOfCombinator => {
|
|
930
|
-
const {
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const resolvedSchema = Resolve.schema(
|
|
934
|
-
ownProps.schema || rootSchema,
|
|
935
|
-
uischema.scope,
|
|
936
|
-
rootSchema
|
|
936
|
+
const { data, schema, ...props } = mapStateToControlProps(
|
|
937
|
+
state,
|
|
938
|
+
ownProps
|
|
937
939
|
);
|
|
938
|
-
const visible: boolean =
|
|
939
|
-
ownProps.visible === undefined || hasShowRule(uischema)
|
|
940
|
-
? isVisible(uischema, getData(state), ownProps.path, getAjv(state))
|
|
941
|
-
: ownProps.visible;
|
|
942
|
-
const id = ownProps.id;
|
|
943
|
-
|
|
944
|
-
const data = Resolve.data(getData(state), path);
|
|
945
940
|
|
|
946
941
|
const ajv = state.jsonforms.core.ajv;
|
|
947
|
-
const schema = resolvedSchema || rootSchema;
|
|
948
|
-
const _schema = resolveSubSchemas(schema, rootSchema, keyword);
|
|
949
942
|
const structuralKeywords = [
|
|
950
943
|
'required',
|
|
951
944
|
'additionalProperties',
|
|
@@ -964,9 +957,9 @@ export const mapStateToCombinatorRendererProps = (
|
|
|
964
957
|
// TODO instead of compiling the combinator subschemas we can compile the original schema
|
|
965
958
|
// without the combinator alternatives and then revalidate and check the errors for the
|
|
966
959
|
// element
|
|
967
|
-
for (let i = 0; i <
|
|
960
|
+
for (let i = 0; i < schema[keyword]?.length; i++) {
|
|
968
961
|
try {
|
|
969
|
-
const valFn = ajv.compile(
|
|
962
|
+
const valFn = ajv.compile(schema[keyword][i]);
|
|
970
963
|
valFn(data);
|
|
971
964
|
if (dataIsValid(valFn.errors)) {
|
|
972
965
|
indexOfFittingSchema = i;
|
|
@@ -979,14 +972,10 @@ export const mapStateToCombinatorRendererProps = (
|
|
|
979
972
|
|
|
980
973
|
return {
|
|
981
974
|
data,
|
|
982
|
-
path,
|
|
983
975
|
schema,
|
|
984
|
-
|
|
985
|
-
visible,
|
|
986
|
-
id,
|
|
976
|
+
...props,
|
|
987
977
|
indexOfFittingSchema,
|
|
988
|
-
uischemas: state
|
|
989
|
-
uischema
|
|
978
|
+
uischemas: getUISchemas(state)
|
|
990
979
|
};
|
|
991
980
|
};
|
|
992
981
|
|
|
@@ -1044,7 +1033,6 @@ export const mapStateToArrayLayoutProps = (
|
|
|
1044
1033
|
|
|
1045
1034
|
const resolvedSchema = Resolve.schema(schema, 'items', props.rootSchema);
|
|
1046
1035
|
|
|
1047
|
-
// TODO Does not consider a specialized '.custom' error message overriding all other error messages
|
|
1048
1036
|
// TODO Does not consider 'i18n' keys which are specified in the ui schemas of the sub errors
|
|
1049
1037
|
const childErrors = getCombinedErrorMessage(
|
|
1050
1038
|
getSubErrorsAt(path, resolvedSchema)(state),
|
package/src/util/resolvers.ts
CHANGED
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
|
|
26
26
|
import isEmpty from 'lodash/isEmpty';
|
|
27
27
|
import get from 'lodash/get';
|
|
28
|
-
import { JsonSchema } from '../models';
|
|
28
|
+
import { JsonSchema, JsonSchema7 } from '../models';
|
|
29
|
+
import { decode } from './path';
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Map for storing refs and the respective schemas they are pointing to.
|
|
@@ -110,80 +111,68 @@ const invalidSegment = (pathSegment: string) =>
|
|
|
110
111
|
export const resolveSchema = (
|
|
111
112
|
schema: JsonSchema,
|
|
112
113
|
schemaPath: string,
|
|
113
|
-
rootSchema
|
|
114
|
+
rootSchema: JsonSchema
|
|
115
|
+
): JsonSchema => {
|
|
116
|
+
const segments = schemaPath?.split('/').map(decode);
|
|
117
|
+
return resolveSchemaWithSegments(schema, segments, rootSchema);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const resolveSchemaWithSegments = (
|
|
121
|
+
schema: JsonSchema,
|
|
122
|
+
pathSegments: string[],
|
|
123
|
+
rootSchema: JsonSchema
|
|
114
124
|
): JsonSchema => {
|
|
115
125
|
if (isEmpty(schema)) {
|
|
116
126
|
return undefined;
|
|
117
127
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
let pathSegment = validPathSegments[i];
|
|
122
|
-
resultSchema =
|
|
123
|
-
resultSchema === undefined || resultSchema.$ref === undefined
|
|
124
|
-
? resultSchema
|
|
125
|
-
: resolveSchema(schema, resultSchema.$ref);
|
|
126
|
-
if (invalidSegment(pathSegment)) {
|
|
127
|
-
// skip invalid segments
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
let curSchema = get(resultSchema, pathSegment);
|
|
131
|
-
if (!curSchema) {
|
|
132
|
-
// resolving was not successful, check whether the scope omitted an oneOf, allOf or anyOf and resolve anyway
|
|
133
|
-
const schemas = [].concat(
|
|
134
|
-
resultSchema?.oneOf ?? [],
|
|
135
|
-
resultSchema?.allOf ?? [],
|
|
136
|
-
resultSchema?.anyOf ?? []
|
|
137
|
-
);
|
|
138
|
-
for (let item of schemas) {
|
|
139
|
-
curSchema = resolveSchema(item, validPathSegments.slice(i).join('/'));
|
|
140
|
-
if (curSchema) {
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (curSchema) {
|
|
145
|
-
// already resolved rest of the path
|
|
146
|
-
resultSchema = curSchema;
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
resultSchema = curSchema;
|
|
128
|
+
|
|
129
|
+
if (schema.$ref) {
|
|
130
|
+
schema = resolveSchema(rootSchema, schema.$ref, rootSchema);
|
|
151
131
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// schemas around
|
|
156
|
-
if (resultSchema !== undefined && resultSchema.$ref !== undefined) {
|
|
157
|
-
try {
|
|
158
|
-
return retrieveResolvableSchema(schema, resultSchema.$ref);
|
|
159
|
-
} catch (e) {
|
|
160
|
-
return retrieveResolvableSchema(rootSchema, resultSchema.$ref);
|
|
161
|
-
}
|
|
132
|
+
|
|
133
|
+
if (!pathSegments || pathSegments.length === 0) {
|
|
134
|
+
return schema;
|
|
162
135
|
}
|
|
163
136
|
|
|
164
|
-
|
|
165
|
-
};
|
|
137
|
+
const [segment, ...remainingSegments] = pathSegments;
|
|
166
138
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
*
|
|
170
|
-
* @param {JsonSchema} full the JSON schema to resolved the reference against
|
|
171
|
-
* @param {string} reference the reference to be resolved
|
|
172
|
-
* @returns {JsonSchema} the resolved sub-schema
|
|
173
|
-
*/
|
|
174
|
-
// disable rule because resolve is mutually recursive
|
|
175
|
-
// tslint:disable:only-arrow-functions
|
|
176
|
-
function retrieveResolvableSchema(
|
|
177
|
-
full: JsonSchema,
|
|
178
|
-
reference: string
|
|
179
|
-
): JsonSchema {
|
|
180
|
-
// tslint:enable:only-arrow-functions
|
|
181
|
-
const child = resolveSchema(full, reference);
|
|
182
|
-
const allRefs = findAllRefs(child);
|
|
183
|
-
const innerSelfReference = allRefs[reference];
|
|
184
|
-
if (innerSelfReference !== undefined) {
|
|
185
|
-
innerSelfReference.$ref = '#';
|
|
139
|
+
if (invalidSegment(segment)) {
|
|
140
|
+
return resolveSchemaWithSegments(schema, remainingSegments, rootSchema);
|
|
186
141
|
}
|
|
187
142
|
|
|
188
|
-
|
|
189
|
-
|
|
143
|
+
const singleSegmentResolveSchema = get(schema, segment);
|
|
144
|
+
|
|
145
|
+
const resolvedSchema = resolveSchemaWithSegments(singleSegmentResolveSchema, remainingSegments, rootSchema);
|
|
146
|
+
if (resolvedSchema) {
|
|
147
|
+
return resolvedSchema;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (segment === 'properties' || segment === 'items') {
|
|
151
|
+
// Let's try to resolve the path, assuming oneOf/allOf/anyOf/then/else was omitted.
|
|
152
|
+
// We only do this when traversing an object or array as we want to avoid
|
|
153
|
+
// following a property which is named oneOf, allOf, anyOf, then or else.
|
|
154
|
+
let alternativeResolveResult = undefined;
|
|
155
|
+
|
|
156
|
+
const subSchemas = [].concat(
|
|
157
|
+
schema.oneOf ?? [],
|
|
158
|
+
schema.allOf ?? [],
|
|
159
|
+
schema.anyOf ?? [],
|
|
160
|
+
(schema as JsonSchema7).then ?? [],
|
|
161
|
+
(schema as JsonSchema7).else ?? []
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
for (const subSchema of subSchemas) {
|
|
165
|
+
alternativeResolveResult = resolveSchemaWithSegments(
|
|
166
|
+
subSchema,
|
|
167
|
+
[segment, ...remainingSegments],
|
|
168
|
+
rootSchema
|
|
169
|
+
);
|
|
170
|
+
if (alternativeResolveResult) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return alternativeResolveResult;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|