@jsonforms/core 3.0.0-beta.1 → 3.0.0-beta.2

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 (67) hide show
  1. package/docs/assets/js/search.json +1 -1
  2. package/docs/globals.html +203 -161
  3. package/docs/index.html +7 -4
  4. package/docs/interfaces/arraycontrolprops.html +21 -21
  5. package/docs/interfaces/arraylayoutprops.html +21 -21
  6. package/docs/interfaces/cellprops.html +12 -12
  7. package/docs/interfaces/combinatorrendererprops.html +143 -36
  8. package/docs/interfaces/controlprops.html +16 -16
  9. package/docs/interfaces/controlstate.html +2 -2
  10. package/docs/interfaces/controlwithdetailprops.html +17 -17
  11. package/docs/interfaces/dispatchcellprops.html +10 -10
  12. package/docs/interfaces/dispatchcellstateprops.html +10 -10
  13. package/docs/interfaces/dispatchpropsofarraycontrol.html +4 -4
  14. package/docs/interfaces/dispatchpropsofcontrol.html +1 -1
  15. package/docs/interfaces/dispatchpropsofmultienumcontrol.html +2 -2
  16. package/docs/interfaces/enumcellprops.html +13 -13
  17. package/docs/interfaces/enumoption.html +2 -2
  18. package/docs/interfaces/jsonformsprops.html +9 -9
  19. package/docs/interfaces/layoutprops.html +10 -10
  20. package/docs/interfaces/ownpropsofcell.html +9 -9
  21. package/docs/interfaces/ownpropsofcontrol.html +9 -12
  22. package/docs/interfaces/ownpropsofenum.html +1 -1
  23. package/docs/interfaces/ownpropsofenumcell.html +10 -10
  24. package/docs/interfaces/ownpropsofjsonformsrenderer.html +8 -8
  25. package/docs/interfaces/ownpropsoflayout.html +9 -9
  26. package/docs/interfaces/ownpropsofmasterlistitem.html +6 -6
  27. package/docs/interfaces/ownpropsofrenderer.html +8 -8
  28. package/docs/interfaces/rendererprops.html +9 -9
  29. package/docs/interfaces/statepropsofarraycontrol.html +17 -17
  30. package/docs/interfaces/statepropsofarraylayout.html +17 -17
  31. package/docs/interfaces/statepropsofcell.html +11 -11
  32. package/docs/interfaces/statepropsofcombinator.html +147 -40
  33. package/docs/interfaces/statepropsofcontrol.html +18 -15
  34. package/docs/interfaces/statepropsofcontrolwithdetail.html +16 -16
  35. package/docs/interfaces/statepropsofenumcell.html +12 -12
  36. package/docs/interfaces/statepropsofjsonformsrenderer.html +9 -9
  37. package/docs/interfaces/statepropsoflayout.html +10 -10
  38. package/docs/interfaces/statepropsofmasteritem.html +7 -7
  39. package/docs/interfaces/statepropsofrenderer.html +9 -9
  40. package/docs/interfaces/statepropsofscopedrenderer.html +12 -12
  41. package/docs/interfaces/withclassname.html +1 -1
  42. package/lib/jsonforms-core.cjs.js +160 -164
  43. package/lib/jsonforms-core.cjs.js.map +1 -1
  44. package/lib/jsonforms-core.esm.js +142 -154
  45. package/lib/jsonforms-core.esm.js.map +1 -1
  46. package/lib/reducers/reducers.d.ts +2 -2
  47. package/lib/testers/testers.d.ts +8 -7
  48. package/lib/util/combinators.d.ts +0 -1
  49. package/lib/util/path.d.ts +10 -0
  50. package/lib/util/renderer.d.ts +2 -2
  51. package/lib/util/resolvers.d.ts +1 -1
  52. package/lib/util/util.d.ts +1 -1
  53. package/package.json +2 -2
  54. package/src/generators/uischema.ts +4 -4
  55. package/src/reducers/reducers.ts +12 -4
  56. package/src/testers/testers.ts +60 -33
  57. package/src/util/combinators.ts +17 -32
  58. package/src/util/label.ts +2 -2
  59. package/src/util/path.ts +18 -6
  60. package/src/util/renderer.ts +14 -29
  61. package/src/util/resolvers.ts +57 -68
  62. package/src/util/util.ts +1 -1
  63. package/stats.html +1 -1
  64. package/test/generators/uischema.test.ts +18 -0
  65. package/test/testers.test.ts +208 -120
  66. package/test/util/path.test.ts +37 -20
  67. package/test/util/resolvers.test.ts +99 -8
@@ -17,10 +17,10 @@ export declare const jsonFormsReducerConfig: {
17
17
  * @param schema the JSON schema describing the data to be rendered
18
18
  * @param schemaPath the according schema path
19
19
  * @param path the instance path
20
- * @param fallbackLayoutType the type of the layout to use
20
+ * @param fallback the type of the layout to use or a UI-schema-generator function
21
21
  * @param control may be checked for embedded inline uischema options
22
22
  */
23
- export declare const findUISchema: (uischemas: JsonFormsUISchemaRegistryEntry[], schema: JsonSchema, schemaPath: string, path: string, fallbackLayoutType?: string, control?: ControlElement, rootSchema?: JsonSchema) => UISchemaElement;
23
+ export declare const findUISchema: (uischemas: JsonFormsUISchemaRegistryEntry[], schema: JsonSchema, schemaPath: string, path: string, fallback?: string | (() => UISchemaElement), control?: ControlElement, rootSchema?: JsonSchema) => UISchemaElement;
24
24
  export declare const getErrorAt: (instancePath: string, schema: JsonSchema) => (state: JsonFormsState) => import("ajv").ErrorObject<string, Record<string, any>, unknown>[];
25
25
  export declare const getSubErrorsAt: (instancePath: string, schema: JsonSchema) => (state: JsonFormsState) => import("ajv").ErrorObject<string, Record<string, any>, unknown>[];
26
26
  export declare const getConfig: (state: JsonFormsState) => any;
@@ -7,12 +7,13 @@ import { Categorization, ControlElement, JsonSchema, UISchemaElement } from '../
7
7
  export declare const NOT_APPLICABLE = -1;
8
8
  /**
9
9
  * A tester is a function that receives an UI schema and a JSON schema and returns a boolean.
10
+ * The rootSchema is handed over as context. Can be used to resolve references.
10
11
  */
11
- export declare type Tester = (uischema: UISchemaElement, schema: JsonSchema) => boolean;
12
+ export declare type Tester = (uischema: UISchemaElement, schema: JsonSchema, rootSchema: JsonSchema) => boolean;
12
13
  /**
13
14
  * A ranked tester associates a tester with a number.
14
15
  */
15
- export declare type RankedTester = (uischema: UISchemaElement, schema: JsonSchema) => number;
16
+ export declare type RankedTester = (uischema: UISchemaElement, schema: JsonSchema, rootSchema: JsonSchema) => number;
16
17
  export declare const isControl: (uischema: any) => uischema is ControlElement;
17
18
  /**
18
19
  * Only applicable for Controls.
@@ -24,8 +25,8 @@ export declare const isControl: (uischema: any) => uischema is ControlElement;
24
25
  * @param {(JsonSchema) => boolean} predicate the predicate that should be
25
26
  * applied to the resolved sub-schema
26
27
  */
27
- export declare const schemaMatches: (predicate: (schema: JsonSchema) => boolean) => Tester;
28
- export declare const schemaSubPathMatches: (subPath: string, predicate: (schema: JsonSchema) => boolean) => Tester;
28
+ export declare const schemaMatches: (predicate: (schema: JsonSchema, rootSchema: JsonSchema) => boolean) => Tester;
29
+ export declare const schemaSubPathMatches: (subPath: string, predicate: (schema: JsonSchema, rootSchema: JsonSchema) => boolean) => Tester;
29
30
  /**
30
31
  * Only applicable for Controls.
31
32
  *
@@ -96,8 +97,8 @@ export declare const or: (...testers: Tester[]) => Tester;
96
97
  * @param {number} rank the rank to be returned in case the tester returns true
97
98
  * @param {Tester} tester a tester
98
99
  */
99
- export declare const rankWith: (rank: number, tester: Tester) => (uischema: UISchemaElement, schema: JsonSchema) => number;
100
- export declare const withIncreasedRank: (by: number, rankedTester: RankedTester) => (uischema: UISchemaElement, schema: JsonSchema) => number;
100
+ export declare const rankWith: (rank: number, tester: Tester) => (uischema: UISchemaElement, schema: JsonSchema, rootSchema: JsonSchema) => number;
101
+ export declare const withIncreasedRank: (by: number, rankedTester: RankedTester) => (uischema: UISchemaElement, schema: JsonSchema, rootSchema: JsonSchema) => number;
101
102
  /**
102
103
  * Default tester for boolean.
103
104
  * @type {RankedTester}
@@ -172,7 +173,7 @@ export declare const isObjectArray: Tester;
172
173
  * @type {Tester}
173
174
  */
174
175
  export declare const isObjectArrayControl: Tester;
175
- export declare const isObjectArrayWithNesting: (uischema: UISchemaElement, schema: JsonSchema) => boolean;
176
+ export declare const isObjectArrayWithNesting: (uischema: UISchemaElement, schema: JsonSchema, rootSchema: JsonSchema) => boolean;
176
177
  /**
177
178
  * Synonym for isObjectArrayControl
178
179
  */
@@ -6,5 +6,4 @@ export interface CombinatorSubSchemaRenderInfo {
6
6
  label: string;
7
7
  }
8
8
  export declare type CombinatorKeyword = 'anyOf' | 'oneOf' | 'allOf';
9
- export declare const resolveSubSchemas: (schema: JsonSchema, rootSchema: JsonSchema, keyword: CombinatorKeyword) => JsonSchema;
10
9
  export declare const createCombinatorRenderInfos: (combinatorSubSchemas: JsonSchema[], rootSchema: JsonSchema, keyword: CombinatorKeyword, control: ControlElement, path: string, uischemas: JsonFormsUISchemaRegistryEntry[]) => CombinatorSubSchemaRenderInfo[];
@@ -23,3 +23,13 @@ export declare const toDataPathSegments: (schemaPath: string) => string[];
23
23
  */
24
24
  export declare const toDataPath: (schemaPath: string) => string;
25
25
  export declare const composeWithUi: (scopableUi: Scopable, path: string) => string;
26
+ /**
27
+ * Encodes the given segment to be used as part of a JSON Pointer
28
+ *
29
+ * JSON Pointer has special meaning for "/" and "~", therefore these must be encoded
30
+ */
31
+ export declare const encode: (segment: string) => string;
32
+ /**
33
+ * Decodes a given JSON Pointer segment to its "normal" representation
34
+ */
35
+ export declare const decode: (pointerSegment: string) => string;
@@ -1,5 +1,5 @@
1
1
  import { ControlElement, JsonSchema, UISchemaElement } from '../models';
2
- import type { JsonFormsCellRendererRegistryEntry, JsonFormsRendererRegistryEntry } from '../reducers';
2
+ import { JsonFormsCellRendererRegistryEntry, JsonFormsRendererRegistryEntry } from '../reducers';
3
3
  import { JsonFormsUISchemaRegistryEntry } from '../reducers';
4
4
  import { RankedTester } from '../testers';
5
5
  import { CombinatorKeyword } from './combinators';
@@ -359,7 +359,7 @@ export declare const controlDefaultProps: {
359
359
  path: string;
360
360
  direction: 'row' | 'column';
361
361
  };
362
- export interface StatePropsOfCombinator extends OwnPropsOfControl {
362
+ export interface StatePropsOfCombinator extends StatePropsOfControl {
363
363
  rootSchema: JsonSchema;
364
364
  path: string;
365
365
  id: string;
@@ -22,4 +22,4 @@ export declare const findAllRefs: (schema: JsonSchema, result?: ReferenceSchemaM
22
22
  * @param {JsonSchema} rootSchema the actual root schema
23
23
  * @returns {JsonSchema} the resolved sub-schema
24
24
  */
25
- export declare const resolveSchema: (schema: JsonSchema, schemaPath: string, rootSchema?: JsonSchema) => JsonSchema;
25
+ export declare const resolveSchema: (schema: JsonSchema, schemaPath: string, rootSchema: JsonSchema) => JsonSchema;
@@ -18,7 +18,7 @@ export declare const deriveTypes: (jsonSchema: JsonSchema) => string[];
18
18
  * Convenience wrapper around resolveData and resolveSchema.
19
19
  */
20
20
  export declare const Resolve: {
21
- schema(schema: JsonSchema, schemaPath: string, rootSchema?: JsonSchema): JsonSchema;
21
+ schema(schema: JsonSchema, schemaPath: string, rootSchema: JsonSchema): JsonSchema;
22
22
  data(data: any, path: string): any;
23
23
  };
24
24
  export declare const Paths: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonforms/core",
3
- "version": "3.0.0-beta.1",
3
+ "version": "3.0.0-beta.2",
4
4
  "description": "Core module of JSON Forms",
5
5
  "repository": "https://github.com/eclipsesource/jsonforms",
6
6
  "bugs": "https://github.com/eclipsesource/jsonforms/issues",
@@ -88,5 +88,5 @@
88
88
  "typedoc": "^0.19.2",
89
89
  "typescript": "4.2.3"
90
90
  },
91
- "gitHead": "8c3b6263f360ca192c0a20a343a19c5ffb3a10f7"
91
+ "gitHead": "8e9c14dcf049ee4b31baf9c40a86b394cba33139"
92
92
  }
@@ -35,7 +35,7 @@ import {
35
35
  Layout,
36
36
  UISchemaElement
37
37
  } from '../models';
38
- import { deriveTypes, resolveSchema } from '../util';
38
+ import { deriveTypes, encode, resolveSchema } from '../util';
39
39
 
40
40
  /**
41
41
  * Creates a new ILayout.
@@ -122,7 +122,7 @@ const generateUISchema = (
122
122
  ): UISchemaElement => {
123
123
  if (!isEmpty(jsonSchema) && jsonSchema.$ref !== undefined) {
124
124
  return generateUISchema(
125
- resolveSchema(rootSchema, jsonSchema.$ref),
125
+ resolveSchema(rootSchema, jsonSchema.$ref, rootSchema),
126
126
  schemaElements,
127
127
  currentRef,
128
128
  schemaName,
@@ -162,9 +162,9 @@ const generateUISchema = (
162
162
  const nextRef: string = currentRef + '/properties';
163
163
  Object.keys(jsonSchema.properties).map(propName => {
164
164
  let value = jsonSchema.properties[propName];
165
- const ref = `${nextRef}/${propName}`;
165
+ const ref = `${nextRef}/${encode(propName)}`;
166
166
  if (value.$ref !== undefined) {
167
- value = resolveSchema(rootSchema, value.$ref);
167
+ value = resolveSchema(rootSchema, value.$ref, rootSchema);
168
168
  }
169
169
  generateUISchema(
170
170
  value,
@@ -67,7 +67,7 @@ export const jsonFormsReducerConfig = {
67
67
  * @param schema the JSON schema describing the data to be rendered
68
68
  * @param schemaPath the according schema path
69
69
  * @param path the instance path
70
- * @param fallbackLayoutType the type of the layout to use
70
+ * @param fallback the type of the layout to use or a UI-schema-generator function
71
71
  * @param control may be checked for embedded inline uischema options
72
72
  */
73
73
  export const findUISchema = (
@@ -75,7 +75,7 @@ export const findUISchema = (
75
75
  schema: JsonSchema,
76
76
  schemaPath: string,
77
77
  path: string,
78
- fallbackLayoutType = 'VerticalLayout',
78
+ fallback: string | (() => UISchemaElement) = 'VerticalLayout',
79
79
  control?: ControlElement,
80
80
  rootSchema?: JsonSchema
81
81
  ): UISchemaElement => {
@@ -83,8 +83,12 @@ export const findUISchema = (
83
83
  if (control && control.options && control.options.detail) {
84
84
  if (typeof control.options.detail === 'string') {
85
85
  if (control.options.detail.toUpperCase() === 'GENERATE') {
86
+ //use fallback generation function
87
+ if(typeof fallback === "function"){
88
+ return fallback();
89
+ }
86
90
  // force generation of uischema
87
- return Generate.uiSchema(schema, fallbackLayoutType);
91
+ return Generate.uiSchema(schema, fallback);
88
92
  }
89
93
  } else if (typeof control.options.detail === 'object') {
90
94
  // check if detail is a valid uischema
@@ -99,7 +103,11 @@ export const findUISchema = (
99
103
  // default
100
104
  const uiSchema = findMatchingUISchema(uischemas)(schema, schemaPath, path);
101
105
  if (uiSchema === undefined) {
102
- return Generate.uiSchema(schema, fallbackLayoutType, '#', rootSchema);
106
+ //use fallback generation function
107
+ if(typeof fallback === 'function'){
108
+ return fallback();
109
+ }
110
+ return Generate.uiSchema(schema, fallback, '#', rootSchema);
103
111
  }
104
112
  return uiSchema;
105
113
  };
@@ -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
  /**
@@ -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
- ) => testers.reduce((acc, tester) => acc && tester(uischema, schema), true);
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
- ) => testers.reduce((acc, tester) => acc || tester(uischema, schema), false);
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.items) // we don't care about tuples
389
+ (schema, rootSchema) => hasType(schema, 'array') && !Array.isArray(resolveSchema(schema, 'items', rootSchema)) // we don't care about tuples
384
390
  ),
385
- schemaSubPathMatches('items', schema => hasType(schema, 'object'))
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
- schema => deriveTypes(schema).length !== 0 && !Array.isArray(schema.items) // we don't care about tuples
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 types = deriveTypes(schema);
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
- ) => !tester(uischema, schema);
571
+ schema: JsonSchema,
572
+ rootSchema: JsonSchema
573
+
574
+ ) => !tester(uischema, schema, rootSchema);
@@ -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
- uischema: findUISchema(
79
- uischemas,
80
- subSchema,
81
- control.scope,
82
- path,
83
- undefined,
84
- control,
85
- rootSchema
86
- ),
87
- label: createLabel(subSchema, subSchemaIndex, keyword)
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(/allOf\/[\d]\//g, '')
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 startFromRoot = segments[0] === '#' || segments[0] === '';
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, segments.length, 2).map(idx => segments[idx]);
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/, '~');