@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.
- package/README.md +11 -0
- package/docs/assets/js/search.json +1 -1
- package/docs/enums/ruleeffect.html +4 -4
- package/docs/globals.html +307 -89
- package/docs/index.html +47 -4
- package/docs/interfaces/addcellrendereraction.html +3 -3
- package/docs/interfaces/addrendereraction.html +3 -3
- package/docs/interfaces/adduischemaaction.html +3 -3
- package/docs/interfaces/andcondition.html +2 -2
- package/docs/interfaces/arraylayoutprops.html +2 -2
- package/docs/interfaces/categorization.html +24 -16
- package/docs/interfaces/category.html +24 -16
- package/docs/interfaces/combinatorrendererprops.html +6 -6
- package/docs/interfaces/composablecondition.html +2 -2
- package/docs/interfaces/condition.html +1 -1
- package/docs/interfaces/controlelement.html +38 -15
- package/docs/interfaces/grouplayout.html +24 -12
- package/docs/interfaces/horizontallayout.html +4 -4
- package/docs/interfaces/initactionoptions.html +14 -0
- package/docs/interfaces/internationalizable.html +158 -0
- package/docs/interfaces/jsonformscore.html +17 -3
- package/docs/interfaces/labeldescription.html +2 -2
- package/docs/interfaces/labeled.html +182 -0
- package/docs/interfaces/labelelement.html +4 -4
- package/docs/interfaces/lableable.html +184 -0
- package/docs/interfaces/layout.html +4 -4
- package/docs/interfaces/leafcondition.html +9 -8
- package/docs/interfaces/orcondition.html +2 -2
- package/docs/interfaces/registerdefaultdataaction.html +3 -3
- package/docs/interfaces/removecellrendereraction.html +3 -3
- package/docs/interfaces/removerendereraction.html +3 -3
- package/docs/interfaces/removeuischemaaction.html +2 -2
- package/docs/interfaces/rule.html +2 -2
- package/docs/interfaces/schemabasedcondition.html +9 -8
- package/docs/interfaces/scopable.html +3 -9
- package/docs/interfaces/scoped.html +183 -0
- package/docs/interfaces/setajvaction.html +2 -2
- package/docs/interfaces/setconfigaction.html +2 -2
- package/docs/interfaces/setlocaleaction.html +2 -2
- package/docs/interfaces/setschemaaction.html +2 -2
- package/docs/interfaces/settranslatoraction.html +3 -3
- package/docs/interfaces/setuischemaaction.html +2 -2
- package/docs/interfaces/setvalidationmodeaction.html +2 -2
- package/docs/interfaces/statepropsofarraylayout.html +2 -2
- package/docs/interfaces/statepropsofcombinator.html +6 -6
- package/docs/interfaces/uischemaelement.html +3 -3
- package/docs/interfaces/unregisterdefaultdataaction.html +2 -2
- package/docs/interfaces/updatei18naction.html +4 -4
- package/docs/interfaces/verticallayout.html +4 -4
- package/lib/actions/actions.d.ts +1 -0
- package/lib/jsonforms-core.cjs.js +74 -31
- package/lib/jsonforms-core.cjs.js.map +1 -1
- package/lib/jsonforms-core.esm.js +60 -29
- package/lib/jsonforms-core.esm.js.map +1 -1
- package/lib/models/uischema.d.ts +40 -23
- package/lib/reducers/core.d.ts +1 -0
- package/lib/util/runtime.d.ts +1 -2
- package/lib/util/util.d.ts +6 -6
- package/package.json +2 -2
- package/src/actions/actions.ts +1 -0
- package/src/i18n/i18nUtil.ts +10 -7
- package/src/models/uischema.ts +59 -23
- package/src/reducers/core.ts +30 -3
- package/src/testers/testers.ts +10 -13
- package/src/util/path.ts +9 -5
- package/src/util/renderer.ts +6 -3
- package/src/util/runtime.ts +1 -1
- package/src/util/util.ts +8 -8
- package/stats.html +1 -1
- package/test/i18n/i18nUtil.test.ts +41 -1
- package/test/reducers/core.test.ts +203 -1
- package/test/util/renderer.test.ts +1 -1
package/lib/models/uischema.d.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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,
|
|
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,
|
|
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>;
|
package/lib/reducers/core.d.ts
CHANGED
package/lib/util/runtime.d.ts
CHANGED
|
@@ -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;
|
package/lib/util/util.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JsonSchema,
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
91
|
+
"gitHead": "b66023cf081e56baa3194ac3d6658e23fd267bf9"
|
|
92
92
|
}
|
package/src/actions/actions.ts
CHANGED
package/src/i18n/i18nUtil.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/src/models/uischema.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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,
|
|
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,
|
|
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);
|
package/src/reducers/core.ts
CHANGED
|
@@ -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
|
-
|
|
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);
|
package/src/testers/testers.ts
CHANGED
|
@@ -144,7 +144,7 @@ export const formatIs = (expectedFormat: string): Tester =>
|
|
|
144
144
|
schema =>
|
|
145
145
|
!isEmpty(schema) &&
|
|
146
146
|
schema.format === expectedFormat &&
|
|
147
|
-
schema
|
|
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
|
-
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
459
|
+
if (hasType(val, 'object')) {
|
|
460
|
+
objectDepth++;
|
|
461
|
+
if (objectDepth === 2) {
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
465
464
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
return false;
|
|
465
|
+
if (hasType(val, 'array')) {
|
|
466
|
+
return true;
|
|
469
467
|
}
|
|
470
|
-
|
|
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)
|
|
87
|
-
return '';
|
|
90
|
+
if (isEmpty(segments)) {
|
|
91
|
+
return path ?? '';
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
return
|
|
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/, '~');
|
package/src/util/renderer.ts
CHANGED
|
@@ -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);
|
package/src/util/runtime.ts
CHANGED
|
@@ -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,
|
|
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
|
|
111
|
+
const fromScoped = (scopable: Scoped): string =>
|
|
112
112
|
toDataPathSegments(scopable.scope).join('.');
|
|
113
113
|
|
|
114
114
|
export const Paths = {
|
|
115
115
|
compose: composePaths,
|
|
116
|
-
|
|
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);
|