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

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 (72) hide show
  1. package/README.md +11 -0
  2. package/docs/assets/js/search.json +1 -1
  3. package/docs/enums/ruleeffect.html +4 -4
  4. package/docs/globals.html +307 -89
  5. package/docs/index.html +47 -4
  6. package/docs/interfaces/addcellrendereraction.html +3 -3
  7. package/docs/interfaces/addrendereraction.html +3 -3
  8. package/docs/interfaces/adduischemaaction.html +3 -3
  9. package/docs/interfaces/andcondition.html +2 -2
  10. package/docs/interfaces/arraylayoutprops.html +2 -2
  11. package/docs/interfaces/categorization.html +24 -16
  12. package/docs/interfaces/category.html +24 -16
  13. package/docs/interfaces/combinatorrendererprops.html +6 -6
  14. package/docs/interfaces/composablecondition.html +2 -2
  15. package/docs/interfaces/condition.html +1 -1
  16. package/docs/interfaces/controlelement.html +38 -15
  17. package/docs/interfaces/grouplayout.html +24 -12
  18. package/docs/interfaces/horizontallayout.html +4 -4
  19. package/docs/interfaces/initactionoptions.html +14 -0
  20. package/docs/interfaces/internationalizable.html +158 -0
  21. package/docs/interfaces/jsonformscore.html +17 -3
  22. package/docs/interfaces/labeldescription.html +2 -2
  23. package/docs/interfaces/labeled.html +182 -0
  24. package/docs/interfaces/labelelement.html +4 -4
  25. package/docs/interfaces/lableable.html +184 -0
  26. package/docs/interfaces/layout.html +4 -4
  27. package/docs/interfaces/leafcondition.html +9 -8
  28. package/docs/interfaces/orcondition.html +2 -2
  29. package/docs/interfaces/registerdefaultdataaction.html +3 -3
  30. package/docs/interfaces/removecellrendereraction.html +3 -3
  31. package/docs/interfaces/removerendereraction.html +3 -3
  32. package/docs/interfaces/removeuischemaaction.html +2 -2
  33. package/docs/interfaces/rule.html +2 -2
  34. package/docs/interfaces/schemabasedcondition.html +9 -8
  35. package/docs/interfaces/scopable.html +3 -9
  36. package/docs/interfaces/scoped.html +183 -0
  37. package/docs/interfaces/setajvaction.html +2 -2
  38. package/docs/interfaces/setconfigaction.html +2 -2
  39. package/docs/interfaces/setlocaleaction.html +2 -2
  40. package/docs/interfaces/setschemaaction.html +2 -2
  41. package/docs/interfaces/settranslatoraction.html +3 -3
  42. package/docs/interfaces/setuischemaaction.html +2 -2
  43. package/docs/interfaces/setvalidationmodeaction.html +2 -2
  44. package/docs/interfaces/statepropsofarraylayout.html +2 -2
  45. package/docs/interfaces/statepropsofcombinator.html +6 -6
  46. package/docs/interfaces/uischemaelement.html +3 -3
  47. package/docs/interfaces/unregisterdefaultdataaction.html +2 -2
  48. package/docs/interfaces/updatei18naction.html +4 -4
  49. package/docs/interfaces/verticallayout.html +4 -4
  50. package/lib/actions/actions.d.ts +1 -0
  51. package/lib/jsonforms-core.cjs.js +74 -31
  52. package/lib/jsonforms-core.cjs.js.map +1 -1
  53. package/lib/jsonforms-core.esm.js +60 -29
  54. package/lib/jsonforms-core.esm.js.map +1 -1
  55. package/lib/models/uischema.d.ts +40 -23
  56. package/lib/reducers/core.d.ts +1 -0
  57. package/lib/util/runtime.d.ts +1 -2
  58. package/lib/util/util.d.ts +6 -6
  59. package/package.json +2 -2
  60. package/src/actions/actions.ts +1 -0
  61. package/src/i18n/i18nUtil.ts +10 -7
  62. package/src/models/uischema.ts +59 -23
  63. package/src/reducers/core.ts +30 -3
  64. package/src/testers/testers.ts +10 -13
  65. package/src/util/path.ts +9 -5
  66. package/src/util/renderer.ts +6 -3
  67. package/src/util/runtime.ts +1 -1
  68. package/src/util/util.ts +8 -8
  69. package/stats.html +1 -1
  70. package/test/i18n/i18nUtil.test.ts +41 -1
  71. package/test/reducers/core.test.ts +203 -1
  72. package/test/util/renderer.test.ts +1 -1
@@ -1,14 +1,42 @@
1
1
  import { JsonSchema } from './jsonSchema';
2
2
  /**
3
3
  * Interface for describing an UI schema element that is referencing
4
- * a subschema. The value of the scope must be a JSON Pointer.
4
+ * a subschema. The value of the scope may be a JSON Pointer.
5
5
  */
6
6
  export interface Scopable {
7
+ /**
8
+ * The scope that determines to which part this element should be bound to.
9
+ */
10
+ scope?: string;
11
+ }
12
+ /**
13
+ * Interface for describing an UI schema element that is referencing
14
+ * a subschema. The value of the scope must be a JSON Pointer.
15
+ */
16
+ export interface Scoped extends Scopable {
7
17
  /**
8
18
  * The scope that determines to which part this element should be bound to.
9
19
  */
10
20
  scope: string;
11
21
  }
22
+ /**
23
+ * Interface for describing an UI schema element that may be labeled.
24
+ */
25
+ export interface Lableable<T = string> {
26
+ /**
27
+ * Label for UI schema element.
28
+ */
29
+ label?: string | T;
30
+ }
31
+ /**
32
+ * Interface for describing an UI schema element that is labeled.
33
+ */
34
+ export interface Labeled<T = string> extends Lableable<T> {
35
+ label: string | T;
36
+ }
37
+ export interface Internationalizable {
38
+ i18n?: string;
39
+ }
12
40
  /**
13
41
  * A rule that may be attached to any UI schema element.
14
42
  */
@@ -56,14 +84,14 @@ export interface Condition {
56
84
  /**
57
85
  * A leaf condition.
58
86
  */
59
- export interface LeafCondition extends Condition, Scopable {
87
+ export interface LeafCondition extends Condition, Scoped {
60
88
  type: 'LEAF';
61
89
  /**
62
90
  * The expected value when evaluating the condition
63
91
  */
64
92
  expectedValue: any;
65
93
  }
66
- export interface SchemaBasedCondition extends Condition, Scopable {
94
+ export interface SchemaBasedCondition extends Condition, Scoped {
67
95
  schema: JsonSchema;
68
96
  }
69
97
  /**
@@ -129,12 +157,8 @@ export interface HorizontalLayout extends Layout {
129
157
  * A group resembles a vertical layout, but additionally might have a label.
130
158
  * This layout is useful when grouping different elements by a certain criteria.
131
159
  */
132
- export interface GroupLayout extends Layout {
160
+ export interface GroupLayout extends Layout, Lableable {
133
161
  type: 'Group';
134
- /**
135
- * The label of this group layout.
136
- */
137
- label?: string;
138
162
  }
139
163
  /**
140
164
  * Represents an object that can be used to configure a label.
@@ -163,39 +187,32 @@ export interface LabelElement extends UISchemaElement {
163
187
  * A control element. The scope property of the control determines
164
188
  * to which part of the schema the control should be bound.
165
189
  */
166
- export interface ControlElement extends UISchemaElement, Scopable {
190
+ export interface ControlElement extends UISchemaElement, Scoped, Lableable<string | boolean | LabelDescription>, Internationalizable {
167
191
  type: 'Control';
168
- /**
169
- * An optional label that will be associated with the control
170
- */
171
- label?: string | boolean | LabelDescription;
172
192
  }
173
193
  /**
174
194
  * The category layout.
175
195
  */
176
- export interface Category extends Layout {
196
+ export interface Category extends Layout, Labeled {
177
197
  type: 'Category';
178
- /**
179
- * The label associated with this category layout.
180
- */
181
- label: string;
182
198
  }
183
199
  /**
184
200
  * The categorization element, which may have children elements.
185
201
  * A child element may either be itself a Categorization or a Category, hence
186
202
  * the categorization element can be used to represent recursive structures like trees.
187
203
  */
188
- export interface Categorization extends UISchemaElement {
204
+ export interface Categorization extends UISchemaElement, Labeled {
189
205
  type: 'Categorization';
190
- /**
191
- * The label of this categorization.
192
- */
193
- label: string;
194
206
  /**
195
207
  * The child elements of this categorization which are either of type
196
208
  * {@link Category} or {@link Categorization}.
197
209
  */
198
210
  elements: (Category | Categorization)[];
199
211
  }
212
+ export declare const isInternationalized: (element: unknown) => element is Required<Internationalizable>;
200
213
  export declare const isGroup: (layout: Layout) => layout is GroupLayout;
201
214
  export declare const isLayout: (uischema: UISchemaElement) => uischema is Layout;
215
+ export declare const isScopable: (obj: unknown) => obj is Scopable;
216
+ export declare const isScoped: (obj: unknown) => obj is Scoped;
217
+ export declare const isLabelable: (obj: unknown) => obj is Lableable<string>;
218
+ export declare const isLabeled: (obj: unknown) => obj is Labeled<string>;
@@ -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;
@@ -1,7 +1,6 @@
1
- import { UISchemaElement } from '../models';
1
+ import { JsonSchema, UISchemaElement } from '../models';
2
2
  import Ajv from 'ajv';
3
3
  import { JsonFormsState } from '../store';
4
- import { JsonSchema } from '../models/jsonSchema';
5
4
  export declare const evalVisibility: (uischema: UISchemaElement, data: any, path: string, ajv: Ajv) => boolean;
6
5
  export declare const evalEnablement: (uischema: UISchemaElement, data: any, path: string, ajv: Ajv) => boolean;
7
6
  export declare const hasShowRule: (uischema: UISchemaElement) => boolean;
@@ -1,4 +1,4 @@
1
- import { JsonSchema, Scopable, UISchemaElement } from '..';
1
+ import { JsonSchema, Scoped, UISchemaElement } from '..';
2
2
  import Ajv from 'ajv';
3
3
  /**
4
4
  * Escape the given string such that it can be used as a class name,
@@ -11,19 +11,19 @@ export declare const convertToValidClassName: (s: string) => string;
11
11
  export declare const formatErrorMessage: (errors: string[]) => string;
12
12
  export declare const hasType: (jsonSchema: JsonSchema, expected: string) => boolean;
13
13
  /**
14
- * Derives the type of the jsonSchema element
15
- */
14
+ * Derives the type of the jsonSchema element
15
+ */
16
16
  export declare const deriveTypes: (jsonSchema: JsonSchema) => string[];
17
17
  /**
18
- * Convenience wrapper around resolveData and resolveSchema.
19
- */
18
+ * Convenience wrapper around resolveData and resolveSchema.
19
+ */
20
20
  export declare const Resolve: {
21
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: {
25
25
  compose: (path1: string, path2: string) => string;
26
- fromScopable: (scopable: Scopable) => string;
26
+ fromScoped: (scopable: Scoped) => string;
27
27
  };
28
28
  export declare const Runtime: {
29
29
  isEnabled(uischema: UISchemaElement, data: any, ajv: Ajv): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonforms/core",
3
- "version": "3.0.0-beta.2",
3
+ "version": "3.0.0-beta.5",
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": "8e9c14dcf049ee4b31baf9c40a86b394cba33139"
91
+ "gitHead": "b66023cf081e56baa3194ac3d6658e23fd267bf9"
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 {
@@ -1,5 +1,5 @@
1
1
  import { ErrorObject } from 'ajv';
2
- import { UISchemaElement } from '../models';
2
+ import { isInternationalized, UISchemaElement } from '../models';
3
3
  import { getControlPath } from '../reducers';
4
4
  import { formatErrorMessage } from '../util';
5
5
  import { i18nJsonSchema, ErrorTranslator, Translator } from './i18nTypes';
@@ -8,7 +8,10 @@ export const getI18nKeyPrefixBySchema = (
8
8
  schema: i18nJsonSchema | undefined,
9
9
  uischema: UISchemaElement | undefined
10
10
  ): string | undefined => {
11
- return uischema?.options?.i18n ?? schema?.i18n ?? undefined;
11
+ if (isInternationalized(uischema)) {
12
+ return uischema.i18n;
13
+ }
14
+ return schema?.i18n ?? undefined;
12
15
  };
13
16
 
14
17
  /**
@@ -54,26 +57,26 @@ export const defaultErrorTranslator: ErrorTranslator = (error, t, uischema) => {
54
57
  getControlPath(error),
55
58
  `error.${error.keyword}`
56
59
  );
57
- const specializedKeywordMessage = t(i18nKey, undefined);
60
+ const specializedKeywordMessage = t(i18nKey, undefined, { error } );
58
61
  if (specializedKeywordMessage !== undefined) {
59
62
  return specializedKeywordMessage;
60
63
  }
61
64
 
62
65
  // check whether there is a generic keyword message
63
- const genericKeywordMessage = t(`error.${error.keyword}`, undefined);
66
+ const genericKeywordMessage = t(`error.${error.keyword}`, undefined, { error });
64
67
  if (genericKeywordMessage !== undefined) {
65
68
  return genericKeywordMessage;
66
69
  }
67
70
 
68
71
  // check whether there is a customization for the default message
69
- const messageCustomization = t(error.message, undefined);
72
+ const messageCustomization = t(error.message, undefined, { error });
70
73
  if (messageCustomization !== undefined) {
71
74
  return messageCustomization;
72
75
  }
73
76
 
74
77
  // rewrite required property messages (if they were not customized) as we place them next to the respective input
75
78
  if (error.keyword === 'required' && error.message?.startsWith('must have required property')) {
76
- return t('is a required property', 'is a required property');
79
+ return t('is a required property', 'is a required property', { error });
77
80
  }
78
81
 
79
82
  return error.message;
@@ -94,7 +97,7 @@ export const getCombinedErrorMessage = (
94
97
  if (errors.length > 0 && t) {
95
98
  // check whether there is a special message which overwrites all others
96
99
  const customErrorKey = getI18nKey(schema, uischema, path, 'error.custom');
97
- const specializedErrorMessage = t(customErrorKey, undefined);
100
+ const specializedErrorMessage = t(customErrorKey, undefined, {schema, uischema, path, errors});
98
101
  if (specializedErrorMessage !== undefined) {
99
102
  return specializedErrorMessage;
100
103
  }
@@ -27,15 +27,52 @@ import { JsonSchema } from './jsonSchema';
27
27
 
28
28
  /**
29
29
  * Interface for describing an UI schema element that is referencing
30
- * a subschema. The value of the scope must be a JSON Pointer.
30
+ * a subschema. The value of the scope may be a JSON Pointer.
31
31
  */
32
32
  export interface Scopable {
33
+ /**
34
+ * The scope that determines to which part this element should be bound to.
35
+ */
36
+ scope?: string;
37
+ }
38
+
39
+ /**
40
+ * Interface for describing an UI schema element that is referencing
41
+ * a subschema. The value of the scope must be a JSON Pointer.
42
+ */
43
+ export interface Scoped extends Scopable {
33
44
  /**
34
45
  * The scope that determines to which part this element should be bound to.
35
46
  */
36
47
  scope: string;
37
48
  }
38
49
 
50
+ /**
51
+ * Interface for describing an UI schema element that may be labeled.
52
+ */
53
+ export interface Lableable<T = string> {
54
+ /**
55
+ * Label for UI schema element.
56
+ */
57
+ label?: string|T;
58
+ }
59
+
60
+ /**
61
+ * Interface for describing an UI schema element that is labeled.
62
+ */
63
+ export interface Labeled<T = string> extends Lableable<T> {
64
+ label: string | T;
65
+ }
66
+
67
+ /*
68
+ * Interface for describing an UI schema element that can provide an internationalization base key.
69
+ * If defined, this key is suffixed to derive applicable message keys for the UI schema element.
70
+ * For example, such suffixes are `.label` or `.description` to derive the corresponding message keys for a control element.
71
+ */
72
+ export interface Internationalizable {
73
+ i18n?: string;
74
+ }
75
+
39
76
  /**
40
77
  * A rule that may be attached to any UI schema element.
41
78
  */
@@ -87,7 +124,7 @@ export interface Condition {
87
124
  /**
88
125
  * A leaf condition.
89
126
  */
90
- export interface LeafCondition extends Condition, Scopable {
127
+ export interface LeafCondition extends Condition, Scoped {
91
128
  type: 'LEAF';
92
129
 
93
130
  /**
@@ -96,7 +133,7 @@ export interface LeafCondition extends Condition, Scopable {
96
133
  expectedValue: any;
97
134
  }
98
135
 
99
- export interface SchemaBasedCondition extends Condition, Scopable {
136
+ export interface SchemaBasedCondition extends Condition, Scoped {
100
137
  schema: JsonSchema;
101
138
  }
102
139
 
@@ -170,12 +207,8 @@ export interface HorizontalLayout extends Layout {
170
207
  * A group resembles a vertical layout, but additionally might have a label.
171
208
  * This layout is useful when grouping different elements by a certain criteria.
172
209
  */
173
- export interface GroupLayout extends Layout {
210
+ export interface GroupLayout extends Layout, Lableable {
174
211
  type: 'Group';
175
- /**
176
- * The label of this group layout.
177
- */
178
- label?: string;
179
212
  }
180
213
 
181
214
  /**
@@ -207,23 +240,15 @@ export interface LabelElement extends UISchemaElement {
207
240
  * A control element. The scope property of the control determines
208
241
  * to which part of the schema the control should be bound.
209
242
  */
210
- export interface ControlElement extends UISchemaElement, Scopable {
243
+ export interface ControlElement extends UISchemaElement, Scoped, Lableable<string | boolean | LabelDescription>, Internationalizable {
211
244
  type: 'Control';
212
- /**
213
- * An optional label that will be associated with the control
214
- */
215
- label?: string | boolean | LabelDescription;
216
245
  }
217
246
 
218
247
  /**
219
248
  * The category layout.
220
249
  */
221
- export interface Category extends Layout {
250
+ export interface Category extends Layout, Labeled {
222
251
  type: 'Category';
223
- /**
224
- * The label associated with this category layout.
225
- */
226
- label: string;
227
252
  }
228
253
 
229
254
  /**
@@ -231,12 +256,8 @@ export interface Category extends Layout {
231
256
  * A child element may either be itself a Categorization or a Category, hence
232
257
  * the categorization element can be used to represent recursive structures like trees.
233
258
  */
234
- export interface Categorization extends UISchemaElement {
259
+ export interface Categorization extends UISchemaElement, Labeled {
235
260
  type: 'Categorization';
236
- /**
237
- * The label of this categorization.
238
- */
239
- label: string;
240
261
  /**
241
262
  * The child elements of this categorization which are either of type
242
263
  * {@link Category} or {@link Categorization}.
@@ -244,8 +265,23 @@ export interface Categorization extends UISchemaElement {
244
265
  elements: (Category | Categorization)[];
245
266
  }
246
267
 
268
+ export const isInternationalized = (element: unknown): element is Required<Internationalizable> =>
269
+ typeof element === 'object' && element !== null && typeof (element as Internationalizable).i18n === 'string';
270
+
247
271
  export const isGroup = (layout: Layout): layout is GroupLayout =>
248
272
  layout.type === 'Group';
249
273
 
250
274
  export const isLayout = (uischema: UISchemaElement): uischema is Layout =>
251
275
  (uischema as Layout).elements !== undefined;
276
+
277
+ export const isScopable = (obj: unknown): obj is Scopable =>
278
+ obj && typeof obj === 'object';
279
+
280
+ export const isScoped = (obj: unknown): obj is Scoped =>
281
+ isScopable(obj) && typeof obj.scope === 'string';
282
+
283
+ export const isLabelable = (obj: unknown): obj is Lableable =>
284
+ obj && typeof obj === 'object';
285
+
286
+ export const isLabeled = (obj: unknown): obj is Labeled =>
287
+ isLabelable(obj) && ['string', 'object'].includes(typeof obj.label);
@@ -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);
@@ -144,7 +144,7 @@ export const formatIs = (expectedFormat: string): Tester =>
144
144
  schema =>
145
145
  !isEmpty(schema) &&
146
146
  schema.format === expectedFormat &&
147
- schema.type === 'string'
147
+ hasType(schema, 'string')
148
148
  );
149
149
 
150
150
  /**
@@ -445,10 +445,7 @@ export const isObjectArrayWithNesting = (
445
445
  }
446
446
  const schemaPath = (uischema as ControlElement).scope;
447
447
  const resolvedSchema = resolveSchema(schema, schemaPath, rootSchema ?? schema);
448
- const wantedNestingByType: { [key: string]: number } = {
449
- object: 2,
450
- array: 1
451
- };
448
+ let objectDepth = 0;
452
449
  if (resolvedSchema !== undefined && resolvedSchema.items !== undefined) {
453
450
  // check if nested arrays
454
451
  if (
@@ -459,16 +456,16 @@ export const isObjectArrayWithNesting = (
459
456
  if (val.$ref !== undefined) {
460
457
  return false;
461
458
  }
462
- // we don't support multiple types
463
- if (typeof val.type !== 'string') {
464
- return true;
459
+ if (hasType(val, 'object')) {
460
+ objectDepth++;
461
+ if (objectDepth === 2) {
462
+ return true;
463
+ }
465
464
  }
466
- const typeCount = wantedNestingByType[val.type];
467
- if (typeCount === undefined) {
468
- return false;
465
+ if (hasType(val, 'array')) {
466
+ return true;
469
467
  }
470
- wantedNestingByType[val.type] = typeCount - 1;
471
- return wantedNestingByType[val.type] === 0;
468
+ return false;
472
469
  }, rootSchema)
473
470
  ) {
474
471
  return true;
package/src/util/path.ts CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  import isEmpty from 'lodash/isEmpty';
27
27
  import range from 'lodash/range';
28
- import { Scopable } from '../models';
28
+ import { isScoped, Scopable } from '../models';
29
29
 
30
30
  export const compose = (path1: string, path2: string) => {
31
31
  let p1 = path1;
@@ -81,13 +81,17 @@ export const toDataPath = (schemaPath: string): string => {
81
81
  };
82
82
 
83
83
  export const composeWithUi = (scopableUi: Scopable, path: string): string => {
84
+ if (!isScoped(scopableUi)) {
85
+ return path ?? '';
86
+ }
87
+
84
88
  const segments = toDataPathSegments(scopableUi.scope);
85
89
 
86
- if (isEmpty(segments) && path === undefined) {
87
- return '';
90
+ if (isEmpty(segments)) {
91
+ return path ?? '';
88
92
  }
89
93
 
90
- return isEmpty(segments) ? path : compose(path, segments.join('.'));
94
+ return compose(path, segments.join('.'));
91
95
  };
92
96
 
93
97
  /**
@@ -99,4 +103,4 @@ export const encode = (segment: string) => segment?.replace(/~/g, '~0').replace(
99
103
  /**
100
104
  * Decodes a given JSON Pointer segment to its "normal" representation
101
105
  */
102
- export const decode = (pointerSegment: string) => pointerSegment?.replace(/~1/g, '/').replace(/~0/, '~');
106
+ export const decode = (pointerSegment: string) => pointerSegment?.replace(/~1/g, '/').replace(/~0/, '~');
@@ -465,8 +465,8 @@ export const mapStateToControlProps = (
465
465
  const schema = resolvedSchema ?? rootSchema;
466
466
  const t = getTranslator()(state);
467
467
  const te = getErrorTranslator()(state);
468
- const i18nLabel = t(getI18nKey(schema, uischema, path, 'label'), label);
469
- 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});
470
470
  const i18nErrorMessage = getCombinedErrorMessage(errors, te, t, schema, uischema, path);
471
471
 
472
472
  return {
@@ -894,7 +894,10 @@ export const mapStateToJsonFormsRendererProps = (
894
894
  state.jsonforms.uischemas,
895
895
  ownProps.schema,
896
896
  undefined,
897
- ownProps.path
897
+ ownProps.path,
898
+ undefined,
899
+ undefined,
900
+ state.jsonforms.core.schema
898
901
  );
899
902
  } else {
900
903
  uischema = getUiSchema(state);
@@ -27,6 +27,7 @@ import has from 'lodash/has';
27
27
  import {
28
28
  AndCondition,
29
29
  Condition,
30
+ JsonSchema,
30
31
  LeafCondition,
31
32
  OrCondition,
32
33
  RuleEffect,
@@ -39,7 +40,6 @@ import { composeWithUi } from './path';
39
40
  import Ajv from 'ajv';
40
41
  import { getAjv } from '../reducers';
41
42
  import { JsonFormsState } from '../store';
42
- import { JsonSchema } from '../models/jsonSchema';
43
43
 
44
44
  const isOrCondition = (condition: Condition): condition is OrCondition =>
45
45
  condition.type === 'OR';
package/src/util/util.ts CHANGED
@@ -27,7 +27,7 @@ import isEmpty from 'lodash/isEmpty';
27
27
  import isArray from 'lodash/isArray';
28
28
  import includes from 'lodash/includes';
29
29
  import find from 'lodash/find';
30
- import { JsonSchema, Scopable, UISchemaElement } from '..';
30
+ import { JsonSchema, Scoped, UISchemaElement } from '..';
31
31
  import { resolveData, resolveSchema } from './resolvers';
32
32
  import { composePaths, toDataPathSegments } from './path';
33
33
  import { isEnabled, isVisible } from './runtime';
@@ -56,8 +56,8 @@ export const hasType = (jsonSchema: JsonSchema, expected: string): boolean => {
56
56
  };
57
57
 
58
58
  /**
59
- * Derives the type of the jsonSchema element
60
- */
59
+ * Derives the type of the jsonSchema element
60
+ */
61
61
  export const deriveTypes = (jsonSchema: JsonSchema): string[] => {
62
62
  if (isEmpty(jsonSchema)) {
63
63
  return [];
@@ -93,8 +93,8 @@ export const deriveTypes = (jsonSchema: JsonSchema): string[] => {
93
93
  };
94
94
 
95
95
  /**
96
- * Convenience wrapper around resolveData and resolveSchema.
97
- */
96
+ * Convenience wrapper around resolveData and resolveSchema.
97
+ */
98
98
  export const Resolve: {
99
99
  schema(
100
100
  schema: JsonSchema,
@@ -108,18 +108,18 @@ export const Resolve: {
108
108
  };
109
109
 
110
110
  // Paths --
111
- const fromScopable = (scopable: Scopable) =>
111
+ const fromScoped = (scopable: Scoped): string =>
112
112
  toDataPathSegments(scopable.scope).join('.');
113
113
 
114
114
  export const Paths = {
115
115
  compose: composePaths,
116
- fromScopable
116
+ fromScoped
117
117
  };
118
118
 
119
119
  // Runtime --
120
120
  export const Runtime = {
121
121
  isEnabled(uischema: UISchemaElement, data: any, ajv: Ajv): boolean {
122
- return isEnabled(uischema, data,undefined, ajv);
122
+ return isEnabled(uischema, data, undefined, ajv);
123
123
  },
124
124
  isVisible(uischema: UISchemaElement, data: any, ajv: Ajv): boolean {
125
125
  return isVisible(uischema, data, undefined, ajv);