@jsonforms/core 3.1.0 → 3.2.0-alpha.0

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.
@@ -93,6 +93,18 @@ export interface LeafCondition extends Condition, Scoped {
93
93
  }
94
94
  export interface SchemaBasedCondition extends Condition, Scoped {
95
95
  schema: JsonSchema;
96
+ /**
97
+ * When the scope resolves to undefined and `failWhenUndefined` is set to `true`, the condition
98
+ * will fail. Therefore the reverse effect will be applied.
99
+ *
100
+ * Background:
101
+ * Most JSON Schemas will successfully validate against `undefined` data. Specifying that a
102
+ * condition shall fail when data is `undefined` requires to lift the scope to being able to use
103
+ * JSON Schema's `required`.
104
+ *
105
+ * Using `failWhenUndefined` allows to more conveniently express this condition.
106
+ */
107
+ failWhenUndefined?: boolean;
96
108
  }
97
109
  /**
98
110
  * A composable condition.
@@ -6,7 +6,7 @@ import type { AnyAction, Dispatch } from './type';
6
6
  import { CoreActions } from '../actions';
7
7
  import type { ErrorObject } from 'ajv';
8
8
  import type { JsonFormsState } from '../store';
9
- import { Translator } from '../i18n';
9
+ import { Translator, CombinatorTranslations } from '../i18n';
10
10
  import { ArrayTranslations } from '../i18n/arrayTranslations';
11
11
  /**
12
12
  * Adds an asterisk to the given label string based
@@ -375,6 +375,7 @@ export interface StatePropsOfCombinator extends StatePropsOfControl {
375
375
  indexOfFittingSchema: number;
376
376
  uischemas: JsonFormsUISchemaRegistryEntry[];
377
377
  data: any;
378
+ translations: CombinatorTranslations;
378
379
  }
379
380
  export declare const mapStateToCombinatorRendererProps: (state: JsonFormsState, ownProps: OwnPropsOfControl, keyword: CombinatorKeyword) => StatePropsOfCombinator;
380
381
  export interface CombinatorRendererProps extends StatePropsOfCombinator, DispatchPropsOfControl {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonforms/core",
3
- "version": "3.1.0",
3
+ "version": "3.2.0-alpha.0",
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",
@@ -42,9 +42,9 @@
42
42
  "lint": "eslint .",
43
43
  "lint:fix": "eslint --fix .",
44
44
  "report": "nyc report --reporter=html",
45
- "test": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\",\\\"target\\\":\\\"es5\\\"} ava",
46
- "test-cov": "rimraf -rf .nyc_output && cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\",\\\"target\\\":\\\"es5\\\"} nyc ava",
47
- "doc": "typedoc --name 'JSON Forms Core' --mode file --excludeExternals --theme ../../typedoc-jsonforms --out docs src"
45
+ "test": "ava",
46
+ "test-cov": "rimraf -rf .nyc_output && nyc ava",
47
+ "doc": "typedoc --name 'JSON Forms Core' --excludeExternals --theme ../../typedoc-jsonforms --out docs src"
48
48
  },
49
49
  "ava": {
50
50
  "files": [
@@ -55,7 +55,7 @@
55
55
  "ts"
56
56
  ],
57
57
  "require": [
58
- "ts-node/register",
58
+ "./test-config/ts-node.config.js",
59
59
  "source-map-support/register"
60
60
  ]
61
61
  },
@@ -69,7 +69,7 @@
69
69
  "@types/json-schema": "^7.0.3",
70
70
  "ajv": "^8.6.1",
71
71
  "ajv-formats": "^2.1.0",
72
- "lodash": "^4.17.15"
72
+ "lodash": "^4.17.21"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@istanbuljs/nyc-config-typescript": "^1.0.2",
@@ -77,13 +77,12 @@
77
77
  "@typescript-eslint/eslint-plugin": "^5.54.1",
78
78
  "@typescript-eslint/parser": "^5.54.1",
79
79
  "ava": "~2.4.0",
80
- "cross-env": "^7.0.2",
81
80
  "document-register-element": "^1.14.3",
82
81
  "eslint": "^7.32.0",
83
82
  "eslint-config-prettier": "^8.7.0",
84
83
  "eslint-plugin-import": "^2.27.5",
85
84
  "eslint-plugin-prettier": "^4.2.1",
86
- "jsdom": "^15.2.1",
85
+ "jsdom": "^22.0.0",
87
86
  "jsdom-global": "^3.0.2",
88
87
  "nyc": "^15.1.0",
89
88
  "prettier": "^2.8.4",
@@ -92,12 +91,13 @@
92
91
  "rimraf": "^3.0.2",
93
92
  "rollup": "^2.78.0",
94
93
  "rollup-plugin-cleanup": "^3.2.1",
95
- "rollup-plugin-typescript2": "^0.31.1",
94
+ "rollup-plugin-typescript2": "^0.34.1",
96
95
  "rollup-plugin-visualizer": "^5.4.1",
97
96
  "source-map-support": "0.5.16",
98
97
  "ts-node": "^10.4.0",
99
- "typedoc": "^0.19.2",
98
+ "tslib": "^2.5.0",
99
+ "typedoc": "~0.21.9",
100
100
  "typescript": "4.2.3"
101
101
  },
102
- "gitHead": "7e0115feeced7711b0768d325566aaa6b054c32b"
102
+ "gitHead": "9146b0fdf5913d2251fadd5435d2c7e813db6f71"
103
103
  }
@@ -0,0 +1,28 @@
1
+ export interface CombinatorDefaultTranslation {
2
+ key: CombinatorTranslationEnum;
3
+ default: (variable?: string) => string;
4
+ }
5
+
6
+ export enum CombinatorTranslationEnum {
7
+ clearDialogTitle = 'clearDialogTitle',
8
+ clearDialogMessage = 'clearDialogMessage',
9
+ clearDialogAccept = 'clearDialogAccept',
10
+ clearDialogDecline = 'clearDialogDecline',
11
+ }
12
+
13
+ export type CombinatorTranslations = {
14
+ [key in CombinatorTranslationEnum]?: string;
15
+ };
16
+
17
+ export const combinatorDefaultTranslations: CombinatorDefaultTranslation[] = [
18
+ {
19
+ key: CombinatorTranslationEnum.clearDialogTitle,
20
+ default: () => 'Clear form?',
21
+ },
22
+ {
23
+ key: CombinatorTranslationEnum.clearDialogMessage,
24
+ default: () => 'Your data will be cleared. Do you want to proceed?',
25
+ },
26
+ { key: CombinatorTranslationEnum.clearDialogAccept, default: () => 'Yes' },
27
+ { key: CombinatorTranslationEnum.clearDialogDecline, default: () => 'No' },
28
+ ];
@@ -7,6 +7,10 @@ import {
7
7
  ArrayDefaultTranslation,
8
8
  ArrayTranslations,
9
9
  } from './arrayTranslations';
10
+ import {
11
+ CombinatorDefaultTranslation,
12
+ CombinatorTranslations,
13
+ } from './combinatorTranslations';
10
14
 
11
15
  export const getI18nKeyPrefixBySchema = (
12
16
  schema: i18nJsonSchema | undefined,
@@ -173,3 +177,17 @@ export const getArrayTranslations = (
173
177
  });
174
178
  return translations;
175
179
  };
180
+
181
+ export const getCombinatorTranslations = (
182
+ t: Translator,
183
+ defaultTranslations: CombinatorDefaultTranslation[],
184
+ i18nKeyPrefix: string,
185
+ label: string
186
+ ): CombinatorTranslations => {
187
+ const translations: CombinatorTranslations = {};
188
+ defaultTranslations.forEach((controlElement) => {
189
+ const key = addI18nKeyToPrefix(i18nKeyPrefix, controlElement.key);
190
+ translations[controlElement.key] = t(key, controlElement.default(label));
191
+ });
192
+ return translations;
193
+ };
package/src/i18n/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './i18nTypes';
2
2
  export * from './i18nUtil';
3
3
  export * from './arrayTranslations';
4
+ export * from './combinatorTranslations';
@@ -135,6 +135,19 @@ export interface LeafCondition extends Condition, Scoped {
135
135
 
136
136
  export interface SchemaBasedCondition extends Condition, Scoped {
137
137
  schema: JsonSchema;
138
+
139
+ /**
140
+ * When the scope resolves to undefined and `failWhenUndefined` is set to `true`, the condition
141
+ * will fail. Therefore the reverse effect will be applied.
142
+ *
143
+ * Background:
144
+ * Most JSON Schemas will successfully validate against `undefined` data. Specifying that a
145
+ * condition shall fail when data is `undefined` requires to lift the scope to being able to use
146
+ * JSON Schema's `required`.
147
+ *
148
+ * Using `failWhenUndefined` allows to more conveniently express this condition.
149
+ */
150
+ failWhenUndefined?: boolean;
138
151
  }
139
152
 
140
153
  /**
@@ -25,6 +25,7 @@
25
25
 
26
26
  import cloneDeep from 'lodash/cloneDeep';
27
27
  import setFp from 'lodash/fp/set';
28
+ import unsetFp from 'lodash/fp/unset';
28
29
  import get from 'lodash/get';
29
30
  import filter from 'lodash/filter';
30
31
  import isEqual from 'lodash/isEqual';
@@ -285,11 +286,19 @@ export const coreReducer: Reducer<JsonFormsCore, CoreActions> = (
285
286
  } else {
286
287
  const oldData: any = get(state.data, action.path);
287
288
  const newData = action.updater(cloneDeep(oldData));
288
- const newState: any = setFp(
289
- action.path,
290
- newData,
291
- state.data === undefined ? {} : state.data
292
- );
289
+ let newState: any;
290
+ if (newData !== undefined) {
291
+ newState = setFp(
292
+ action.path,
293
+ newData,
294
+ state.data === undefined ? {} : state.data
295
+ );
296
+ } else {
297
+ newState = unsetFp(
298
+ action.path,
299
+ state.data === undefined ? {} : state.data
300
+ );
301
+ }
293
302
  const errors = validate(state.validator, newState);
294
303
  return {
295
304
  ...state,
@@ -35,18 +35,6 @@ export interface CombinatorSubSchemaRenderInfo {
35
35
 
36
36
  export type CombinatorKeyword = 'anyOf' | 'oneOf' | 'allOf';
37
37
 
38
- const createLabel = (
39
- subSchema: JsonSchema,
40
- subSchemaIndex: number,
41
- keyword: CombinatorKeyword
42
- ): string => {
43
- if (subSchema.title) {
44
- return subSchema.title;
45
- } else {
46
- return keyword + '-' + subSchemaIndex;
47
- }
48
- };
49
-
50
38
  export const createCombinatorRenderInfos = (
51
39
  combinatorSubSchemas: JsonSchema[],
52
40
  rootSchema: JsonSchema,
@@ -56,9 +44,11 @@ export const createCombinatorRenderInfos = (
56
44
  uischemas: JsonFormsUISchemaRegistryEntry[]
57
45
  ): CombinatorSubSchemaRenderInfo[] =>
58
46
  combinatorSubSchemas.map((subSchema, subSchemaIndex) => {
59
- const schema = subSchema.$ref
60
- ? Resolve.schema(rootSchema, subSchema.$ref, rootSchema)
61
- : subSchema;
47
+ const resolvedSubSchema =
48
+ subSchema.$ref && Resolve.schema(rootSchema, subSchema.$ref, rootSchema);
49
+
50
+ const schema = resolvedSubSchema ?? subSchema;
51
+
62
52
  return {
63
53
  schema,
64
54
  uischema: findUISchema(
@@ -70,6 +60,9 @@ export const createCombinatorRenderInfos = (
70
60
  control,
71
61
  rootSchema
72
62
  ),
73
- label: createLabel(subSchema, subSchemaIndex, keyword),
63
+ label:
64
+ subSchema.title ??
65
+ resolvedSubSchema?.title ??
66
+ `${keyword}-${subSchemaIndex}`,
74
67
  };
75
68
  });
@@ -70,6 +70,9 @@ import {
70
70
  getI18nKeyPrefixBySchema,
71
71
  getArrayTranslations,
72
72
  Translator,
73
+ CombinatorTranslations,
74
+ getCombinatorTranslations,
75
+ combinatorDefaultTranslations,
73
76
  } from '../i18n';
74
77
  import {
75
78
  arrayDefaultTranslations,
@@ -796,7 +799,7 @@ export const mapDispatchToArrayControlProps = (
796
799
  dispatch(
797
800
  update(path, (array) => {
798
801
  toDelete
799
- .sort()
802
+ .sort((a, b) => a - b)
800
803
  .reverse()
801
804
  .forEach((s) => array.splice(s, 1));
802
805
  return array;
@@ -970,6 +973,7 @@ export interface StatePropsOfCombinator extends StatePropsOfControl {
970
973
  indexOfFittingSchema: number;
971
974
  uischemas: JsonFormsUISchemaRegistryEntry[];
972
975
  data: any;
976
+ translations: CombinatorTranslations;
973
977
  }
974
978
 
975
979
  export const mapStateToCombinatorRendererProps = (
@@ -977,12 +981,17 @@ export const mapStateToCombinatorRendererProps = (
977
981
  ownProps: OwnPropsOfControl,
978
982
  keyword: CombinatorKeyword
979
983
  ): StatePropsOfCombinator => {
980
- const { data, schema, rootSchema, ...props } = mapStateToControlProps(
981
- state,
982
- ownProps
983
- );
984
+ const { data, schema, rootSchema, i18nKeyPrefix, label, ...props } =
985
+ mapStateToControlProps(state, ownProps);
984
986
 
985
987
  const ajv = state.jsonforms.core.ajv;
988
+ const t = getTranslator()(state);
989
+ const translations = getCombinatorTranslations(
990
+ t,
991
+ combinatorDefaultTranslations,
992
+ i18nKeyPrefix,
993
+ label
994
+ );
986
995
  const structuralKeywords = [
987
996
  'required',
988
997
  'additionalProperties',
@@ -1025,8 +1034,11 @@ export const mapStateToCombinatorRendererProps = (
1025
1034
  schema,
1026
1035
  rootSchema,
1027
1036
  ...props,
1037
+ i18nKeyPrefix,
1038
+ label,
1028
1039
  indexOfFittingSchema,
1029
1040
  uischemas: getUISchemas(state),
1041
+ translations,
1030
1042
  };
1031
1043
  };
1032
1044
 
@@ -79,6 +79,9 @@ const evaluateCondition = (
79
79
  return value === condition.expectedValue;
80
80
  } else if (isSchemaCondition(condition)) {
81
81
  const value = resolveData(data, getConditionScope(condition, path));
82
+ if (condition.failWhenUndefined && value === undefined) {
83
+ return false;
84
+ }
82
85
  return ajv.validate(condition.schema, value) as boolean;
83
86
  } else {
84
87
  // unknown condition