@projectcaluma/ember-form 14.8.1 → 14.8.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.
@@ -118,6 +118,7 @@
118
118
  />
119
119
  <DocumentValidity
120
120
  @document={{this.documentToEdit}}
121
+ @skipBackendValidation={{true}}
121
122
  as |isValid validate|
122
123
  >
123
124
  <UkButton
@@ -1,4 +1,5 @@
1
1
  import { action } from "@ember/object";
2
+ import { service } from "@ember/service";
2
3
  import Component from "@glimmer/component";
3
4
  import { queryManager } from "ember-apollo-client";
4
5
  import { task } from "ember-concurrency";
@@ -25,6 +26,8 @@ import documentValidityQuery from "@projectcaluma/ember-form/gql/queries/documen
25
26
  export default class DocumentValidity extends Component {
26
27
  @queryManager apollo;
27
28
 
29
+ @service calumaStore;
30
+
28
31
  /**
29
32
  * The document to be validated
30
33
  *
@@ -67,31 +70,7 @@ export default class DocumentValidity extends Component {
67
70
  ),
68
71
  );
69
72
 
70
- const { isValid, errors } = await this.apollo.query(
71
- {
72
- query: documentValidityQuery,
73
- fetchPolicy: "network-only",
74
- variables: { id: this.args.document.uuid },
75
- },
76
- "documentValidity.edges.0.node",
77
- );
78
-
79
- if (!isValid) {
80
- errors
81
- .filter(({ errorCode }) => errorCode === "format_validation_failed")
82
- .forEach(({ slug, errorMsg }) => {
83
- const field = this.args.document.findField(slug);
84
-
85
- field._errors = [
86
- ...field._errors,
87
- {
88
- type: "format",
89
- context: { errorMsg },
90
- value: field.value,
91
- },
92
- ];
93
- });
94
- }
73
+ await this.#runBackendValidation();
95
74
 
96
75
  if (this.isValid) {
97
76
  this.args.onValid?.();
@@ -105,6 +84,39 @@ export default class DocumentValidity extends Component {
105
84
  }
106
85
  });
107
86
 
87
+ async #runBackendValidation() {
88
+ if (this.args.skipBackendValidation) return;
89
+
90
+ const { isValid, errors } = await this.apollo.query(
91
+ {
92
+ query: documentValidityQuery,
93
+ fetchPolicy: "network-only",
94
+ variables: { id: this.args.document.uuid },
95
+ },
96
+ "documentValidity.edges.0.node",
97
+ );
98
+
99
+ if (!isValid) {
100
+ errors
101
+ .filter(({ errorCode }) => errorCode === "format_validation_failed")
102
+ .forEach(({ slug, errorMsg, documentId }) => {
103
+ const pk = `Document:${documentId}:Question:${slug}`;
104
+ const field = this.calumaStore.findByPk(pk);
105
+ const parentField = field.document.parentField;
106
+
107
+ // Add the error manually as the frontend does not validate format
108
+ // validators - only the backend.
109
+ field.addManualError("format", { errorMsg }, field.value);
110
+
111
+ if (parentField) {
112
+ // If the affected field is in a table row, we need to mark the
113
+ // table answer as invalid as well.
114
+ parentField.addManualError("table", {}, null);
115
+ }
116
+ });
117
+ }
118
+ }
119
+
108
120
  @action
109
121
  validate() {
110
122
  return this._validate.perform();
@@ -8,6 +8,7 @@ query DocumentValidity($id: ID!, $dataSourceContext: JSONString) {
8
8
  slug
9
9
  errorMsg
10
10
  errorCode
11
+ documentId
11
12
  }
12
13
  }
13
14
  }
@@ -136,6 +136,7 @@ export default class Answer extends Base {
136
136
  new Document({
137
137
  raw: parseDocument(document),
138
138
  parentDocument: this.field.document,
139
+ parentField: this.field,
139
140
  owner,
140
141
  })
141
142
  );
@@ -22,7 +22,13 @@ const sum = (nums) => nums.reduce((num, base) => base + num, 0);
22
22
  * @class Document
23
23
  */
24
24
  export default class Document extends Base {
25
- constructor({ raw, parentDocument, dataSourceContext, ...args }) {
25
+ constructor({
26
+ raw,
27
+ parentDocument,
28
+ parentField,
29
+ dataSourceContext,
30
+ ...args
31
+ }) {
26
32
  assert(
27
33
  "A graphql document `raw` must be passed",
28
34
  raw?.__typename === "Document",
@@ -31,6 +37,7 @@ export default class Document extends Base {
31
37
  super({ raw, ...args });
32
38
 
33
39
  this.parentDocument = parentDocument;
40
+ this.parentField = parentField;
34
41
  this.dataSourceContext =
35
42
  dataSourceContext ?? parentDocument?.dataSourceContext;
36
43
 
@@ -75,6 +82,14 @@ export default class Document extends Base {
75
82
  */
76
83
  parentDocument = null;
77
84
 
85
+ /**
86
+ * The parent field of this document. If this is set, the document is most
87
+ * likely a table row.
88
+ *
89
+ * @property {Field} parentField
90
+ */
91
+ parentField = null;
92
+
78
93
  /**
79
94
  * The root form of this document
80
95
  *
@@ -665,14 +665,11 @@ export default class Field extends Base {
665
665
  await this.onSaveError(e);
666
666
 
667
667
  if (validationError) {
668
- this._errors = [
669
- ...this._errors,
670
- {
671
- type: "format",
672
- context: { errorMsg: validationError.message },
673
- value,
674
- },
675
- ];
668
+ this.addManualError(
669
+ "format",
670
+ { errorMsg: validationError.message },
671
+ value,
672
+ );
676
673
  } else {
677
674
  throw e;
678
675
  }
@@ -704,6 +701,18 @@ export default class Field extends Base {
704
701
  // eslint-disable-next-line no-unused-vars
705
702
  async onSaveError(error) {}
706
703
 
704
+ /**
705
+ * Add manual validation error message
706
+ *
707
+ * @method addManualError
708
+ * @param {String} type The error type (e.g. "table" or "format")
709
+ * @param {Object} context The error context object - mostly consists of `errorMsg`
710
+ * @param {*} value The invalid value
711
+ */
712
+ addManualError(type, context = null, value = null) {
713
+ this._errors = [...this._errors, { type, context, value }];
714
+ }
715
+
707
716
  /**
708
717
  * The translated error messages
709
718
  *
@@ -37,6 +37,10 @@ export default class CalumaStoreService extends Service {
37
37
  return this._store.get(storeKey) || null;
38
38
  }
39
39
 
40
+ findByPk(pk) {
41
+ return this._store.values().find((item) => item.pk === pk);
42
+ }
43
+
40
44
  delete(storeKey) {
41
45
  this._store.delete(storeKey);
42
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectcaluma/ember-form",
3
- "version": "14.8.1",
3
+ "version": "14.8.3",
4
4
  "description": "Ember addon for rendering Caluma forms.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -39,7 +39,7 @@
39
39
  "luxon": "^3.5.0",
40
40
  "reactiveweb": "^1.3.0",
41
41
  "tracked-toolbox": "^2.0.0",
42
- "@projectcaluma/ember-core": "^14.8.1"
42
+ "@projectcaluma/ember-core": "^14.8.3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@ember/optional-features": "2.3.0",
@@ -74,12 +74,12 @@
74
74
  "uikit": "3.25.6",
75
75
  "uuid": "13.0.0",
76
76
  "webpack": "5.104.1",
77
- "@projectcaluma/ember-workflow": "14.8.1",
78
- "@projectcaluma/ember-testing": "14.8.1"
77
+ "@projectcaluma/ember-testing": "14.8.3",
78
+ "@projectcaluma/ember-workflow": "14.8.3"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "ember-source": ">= 4.0.0",
82
- "@projectcaluma/ember-workflow": "^14.8.1"
82
+ "@projectcaluma/ember-workflow": "^14.8.3"
83
83
  },
84
84
  "dependenciesMeta": {
85
85
  "@projectcaluma/ember-core": {