@jsonforms/core 3.0.0-beta.0 → 3.0.0-beta.3

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 (91) hide show
  1. package/docs/assets/js/search.json +1 -1
  2. package/docs/globals.html +318 -209
  3. package/docs/index.html +13 -4
  4. package/docs/interfaces/addcellrendereraction.html +3 -3
  5. package/docs/interfaces/addrendereraction.html +3 -3
  6. package/docs/interfaces/adduischemaaction.html +3 -3
  7. package/docs/interfaces/arraycontrolprops.html +21 -21
  8. package/docs/interfaces/arraylayoutprops.html +21 -21
  9. package/docs/interfaces/cellprops.html +12 -12
  10. package/docs/interfaces/combinatorrendererprops.html +143 -36
  11. package/docs/interfaces/controlprops.html +16 -16
  12. package/docs/interfaces/controlstate.html +2 -2
  13. package/docs/interfaces/controlwithdetailprops.html +17 -17
  14. package/docs/interfaces/dispatchcellprops.html +10 -10
  15. package/docs/interfaces/dispatchcellstateprops.html +10 -10
  16. package/docs/interfaces/dispatchpropsofarraycontrol.html +4 -4
  17. package/docs/interfaces/dispatchpropsofcontrol.html +1 -1
  18. package/docs/interfaces/dispatchpropsofmultienumcontrol.html +2 -2
  19. package/docs/interfaces/enumcellprops.html +13 -13
  20. package/docs/interfaces/enumoption.html +2 -2
  21. package/docs/interfaces/initactionoptions.html +14 -0
  22. package/docs/interfaces/jsonformscore.html +17 -3
  23. package/docs/interfaces/jsonformsprops.html +9 -9
  24. package/docs/interfaces/layoutprops.html +10 -10
  25. package/docs/interfaces/ownpropsofcell.html +9 -9
  26. package/docs/interfaces/ownpropsofcontrol.html +9 -12
  27. package/docs/interfaces/ownpropsofenum.html +1 -1
  28. package/docs/interfaces/ownpropsofenumcell.html +10 -10
  29. package/docs/interfaces/ownpropsofjsonformsrenderer.html +8 -8
  30. package/docs/interfaces/ownpropsoflayout.html +9 -9
  31. package/docs/interfaces/ownpropsofmasterlistitem.html +6 -6
  32. package/docs/interfaces/ownpropsofrenderer.html +8 -8
  33. package/docs/interfaces/registerdefaultdataaction.html +3 -3
  34. package/docs/interfaces/removecellrendereraction.html +3 -3
  35. package/docs/interfaces/removerendereraction.html +3 -3
  36. package/docs/interfaces/removeuischemaaction.html +2 -2
  37. package/docs/interfaces/rendererprops.html +9 -9
  38. package/docs/interfaces/setajvaction.html +2 -2
  39. package/docs/interfaces/setconfigaction.html +2 -2
  40. package/docs/interfaces/setlocaleaction.html +2 -2
  41. package/docs/interfaces/setschemaaction.html +2 -2
  42. package/docs/interfaces/settranslatoraction.html +3 -3
  43. package/docs/interfaces/setuischemaaction.html +2 -2
  44. package/docs/interfaces/setvalidationmodeaction.html +2 -2
  45. package/docs/interfaces/statepropsofarraycontrol.html +17 -17
  46. package/docs/interfaces/statepropsofarraylayout.html +17 -17
  47. package/docs/interfaces/statepropsofcell.html +11 -11
  48. package/docs/interfaces/statepropsofcombinator.html +147 -40
  49. package/docs/interfaces/statepropsofcontrol.html +18 -15
  50. package/docs/interfaces/statepropsofcontrolwithdetail.html +16 -16
  51. package/docs/interfaces/statepropsofenumcell.html +12 -12
  52. package/docs/interfaces/statepropsofjsonformsrenderer.html +9 -9
  53. package/docs/interfaces/statepropsoflayout.html +10 -10
  54. package/docs/interfaces/statepropsofmasteritem.html +7 -7
  55. package/docs/interfaces/statepropsofrenderer.html +9 -9
  56. package/docs/interfaces/statepropsofscopedrenderer.html +12 -12
  57. package/docs/interfaces/unregisterdefaultdataaction.html +2 -2
  58. package/docs/interfaces/updatei18naction.html +4 -4
  59. package/docs/interfaces/withclassname.html +1 -1
  60. package/lib/actions/actions.d.ts +1 -0
  61. package/lib/jsonforms-core.cjs.js +191 -176
  62. package/lib/jsonforms-core.cjs.js.map +1 -1
  63. package/lib/jsonforms-core.esm.js +174 -164
  64. package/lib/jsonforms-core.esm.js.map +1 -1
  65. package/lib/reducers/core.d.ts +1 -0
  66. package/lib/reducers/reducers.d.ts +2 -2
  67. package/lib/testers/testers.d.ts +8 -7
  68. package/lib/util/combinators.d.ts +0 -1
  69. package/lib/util/path.d.ts +10 -0
  70. package/lib/util/renderer.d.ts +2 -2
  71. package/lib/util/resolvers.d.ts +1 -1
  72. package/lib/util/util.d.ts +1 -1
  73. package/package.json +2 -2
  74. package/src/actions/actions.ts +1 -0
  75. package/src/generators/uischema.ts +4 -4
  76. package/src/i18n/i18nUtil.ts +5 -5
  77. package/src/reducers/core.ts +30 -3
  78. package/src/reducers/reducers.ts +12 -4
  79. package/src/testers/testers.ts +60 -33
  80. package/src/util/combinators.ts +17 -32
  81. package/src/util/label.ts +2 -2
  82. package/src/util/path.ts +18 -6
  83. package/src/util/renderer.ts +20 -32
  84. package/src/util/resolvers.ts +57 -68
  85. package/src/util/util.ts +1 -1
  86. package/stats.html +1 -1
  87. package/test/generators/uischema.test.ts +18 -0
  88. package/test/reducers/core.test.ts +203 -1
  89. package/test/testers.test.ts +208 -120
  90. package/test/util/path.test.ts +37 -20
  91. package/test/util/resolvers.test.ts +99 -8
@@ -9,6 +9,7 @@ export interface JsonFormsCore {
9
9
  schema: JsonSchema;
10
10
  uischema: UISchemaElement;
11
11
  errors?: ErrorObject[];
12
+ additionalErrors?: ErrorObject[];
12
13
  validator?: ValidateFunction;
13
14
  ajv?: Ajv;
14
15
  validationMode?: ValidationMode;
@@ -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.0",
3
+ "version": "3.0.0-beta.3",
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": "8540d2f63abe25ee079192bc7d45e7ba4be00eac"
91
+ "gitHead": "78ef46d070f9c5ac1db1f04772db1293858ce2a4"
92
92
  }
@@ -100,6 +100,7 @@ export interface UpdateCoreAction {
100
100
  export interface InitActionOptions {
101
101
  ajv?: AJV;
102
102
  validationMode?: ValidationMode;
103
+ additionalErrors?: ErrorObject[];
103
104
  }
104
105
 
105
106
  export interface SetValidationModeAction {
@@ -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,
@@ -54,26 +54,26 @@ export const defaultErrorTranslator: ErrorTranslator = (error, t, uischema) => {
54
54
  getControlPath(error),
55
55
  `error.${error.keyword}`
56
56
  );
57
- const specializedKeywordMessage = t(i18nKey, undefined);
57
+ const specializedKeywordMessage = t(i18nKey, undefined, { error } );
58
58
  if (specializedKeywordMessage !== undefined) {
59
59
  return specializedKeywordMessage;
60
60
  }
61
61
 
62
62
  // check whether there is a generic keyword message
63
- const genericKeywordMessage = t(`error.${error.keyword}`, undefined);
63
+ const genericKeywordMessage = t(`error.${error.keyword}`, undefined, { error });
64
64
  if (genericKeywordMessage !== undefined) {
65
65
  return genericKeywordMessage;
66
66
  }
67
67
 
68
68
  // check whether there is a customization for the default message
69
- const messageCustomization = t(error.message, undefined);
69
+ const messageCustomization = t(error.message, undefined, { error });
70
70
  if (messageCustomization !== undefined) {
71
71
  return messageCustomization;
72
72
  }
73
73
 
74
74
  // rewrite required property messages (if they were not customized) as we place them next to the respective input
75
75
  if (error.keyword === 'required' && error.message?.startsWith('must have required property')) {
76
- return t('is a required property', 'is a required property');
76
+ return t('is a required property', 'is a required property', { error });
77
77
  }
78
78
 
79
79
  return error.message;
@@ -94,7 +94,7 @@ export const getCombinedErrorMessage = (
94
94
  if (errors.length > 0 && t) {
95
95
  // check whether there is a special message which overwrites all others
96
96
  const customErrorKey = getI18nKey(schema, uischema, path, 'error.custom');
97
- const specializedErrorMessage = t(customErrorKey, undefined);
97
+ const specializedErrorMessage = t(customErrorKey, undefined, {schema, uischema, path, errors});
98
98
  if (specializedErrorMessage !== undefined) {
99
99
  return specializedErrorMessage;
100
100
  }
@@ -65,6 +65,7 @@ export interface JsonFormsCore {
65
65
  schema: JsonSchema;
66
66
  uischema: UISchemaElement;
67
67
  errors?: ErrorObject[];
68
+ additionalErrors?: ErrorObject[];
68
69
  validator?: ValidateFunction;
69
70
  ajv?: Ajv;
70
71
  validationMode?: ValidationMode;
@@ -78,6 +79,7 @@ const initState: JsonFormsCore = {
78
79
  validator: undefined,
79
80
  ajv: undefined,
80
81
  validationMode: 'ValidateAndShow',
82
+ additionalErrors: []
81
83
  };
82
84
 
83
85
  const reuseAjvForSchema = (ajv: Ajv, schema: JsonSchema): Ajv => {
@@ -133,6 +135,23 @@ const hasValidationModeOption = (option: any): option is InitActionOptions => {
133
135
  return false;
134
136
  };
135
137
 
138
+ const hasAdditionalErrorsOption = (option: any): option is InitActionOptions => {
139
+ if (option) {
140
+ return option.additionalErrors !== undefined;
141
+ }
142
+ return false;
143
+ };
144
+
145
+ const getAdditionalErrors = (
146
+ state: JsonFormsCore,
147
+ action?: InitAction | UpdateCoreAction
148
+ ): ErrorObject[] => {
149
+ if (action && hasAdditionalErrorsOption(action.options)) {
150
+ return action.options.additionalErrors;
151
+ }
152
+ return state.additionalErrors;
153
+ };
154
+
136
155
  // tslint:disable-next-line: cyclomatic-complexity
137
156
  export const coreReducer: Reducer<JsonFormsCore, CoreActions> = (
138
157
  state = initState,
@@ -145,12 +164,14 @@ export const coreReducer: Reducer<JsonFormsCore, CoreActions> = (
145
164
  const validationMode = getValidationMode(state, action);
146
165
  const v = validationMode === 'NoValidation' ? undefined : thisAjv.compile(action.schema);
147
166
  const e = validate(v, action.data);
167
+ const additionalErrors = getAdditionalErrors(state, action);
148
168
 
149
169
  return {
150
170
  ...state,
151
171
  data: action.data,
152
172
  schema: action.schema,
153
173
  uischema: action.uischema,
174
+ additionalErrors,
154
175
  errors: e,
155
176
  validator: v,
156
177
  ajv: thisAjv,
@@ -176,6 +197,7 @@ export const coreReducer: Reducer<JsonFormsCore, CoreActions> = (
176
197
  } else if (state.data !== action.data) {
177
198
  errors = validate(validator, action.data);
178
199
  }
200
+ const additionalErrors = getAdditionalErrors(state, action);
179
201
 
180
202
  const stateChanged =
181
203
  state.data !== action.data ||
@@ -184,7 +206,8 @@ export const coreReducer: Reducer<JsonFormsCore, CoreActions> = (
184
206
  state.ajv !== thisAjv ||
185
207
  state.errors !== errors ||
186
208
  state.validator !== validator ||
187
- state.validationMode !== validationMode
209
+ state.validationMode !== validationMode ||
210
+ state.additionalErrors !== additionalErrors
188
211
  return stateChanged
189
212
  ? {
190
213
  ...state,
@@ -195,6 +218,7 @@ export const coreReducer: Reducer<JsonFormsCore, CoreActions> = (
195
218
  errors: isEqual(errors, state.errors) ? state.errors : errors,
196
219
  validator: validator,
197
220
  validationMode: validationMode,
221
+ additionalErrors
198
222
  }
199
223
  : state;
200
224
  }
@@ -391,8 +415,11 @@ const getErrorsAt = (
391
415
  instancePath: string,
392
416
  schema: JsonSchema,
393
417
  matchPath: (path: string) => boolean
394
- ) => (state: JsonFormsCore): ErrorObject[] =>
395
- errorsAt(instancePath, schema, matchPath)(state.validationMode === 'ValidateAndHide' ? [] : state.errors);
418
+ ) => (state: JsonFormsCore): ErrorObject[] => {
419
+ const errors = state.errors ?? [];
420
+ const additionalErrors = state.additionalErrors ?? [];
421
+ return errorsAt(instancePath, schema, matchPath)(state.validationMode === 'ValidateAndHide' ? additionalErrors : [...errors, ...additionalErrors]);
422
+ }
396
423
 
397
424
  export const errorAt = (instancePath: string, schema: JsonSchema) =>
398
425
  getErrorsAt(instancePath, schema, path => path === instancePath);
@@ -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
+ });