@projectcaluma/ember-form 14.8.1 → 14.8.2

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.
@@ -16,7 +16,11 @@
16
16
  as |modal|
17
17
  >
18
18
  <modal.body>
19
- <MarkdownToHtml @markdown={{@text}} @openLinksInNewWindow={{true}} />
19
+ <MarkdownToHtml
20
+ @markdown={{@text}}
21
+ @openLinksInNewWindow={{true}}
22
+ @extensions="DOMPurify"
23
+ />
20
24
  </modal.body>
21
25
  </UkModal>
22
26
  </div>
@@ -1,4 +1,5 @@
1
1
  <MarkdownToHtml
2
2
  @markdown={{@field.question.raw.staticContent}}
3
3
  @openLinksInNewWindow={{true}}
4
+ @extensions="DOMPurify"
4
5
  />
@@ -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
  *
@@ -79,17 +82,20 @@ export default class DocumentValidity extends Component {
79
82
  if (!isValid) {
80
83
  errors
81
84
  .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
- ];
85
+ .forEach(({ slug, errorMsg, documentId }) => {
86
+ const pk = `Document:${documentId}:Question:${slug}`;
87
+ const field = this.calumaStore.findByPk(pk);
88
+ const parentField = field.document.parentField;
89
+
90
+ // Add the error manually as the frontend does not validate format
91
+ // validators - only the backend.
92
+ field.addManualError("format", { errorMsg }, field.value);
93
+
94
+ if (parentField) {
95
+ // If the affected field is in a table row, we need to mark the
96
+ // table answer as invalid as well.
97
+ parentField.addManualError("table", {}, null);
98
+ }
93
99
  });
94
100
  }
95
101
 
@@ -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
  }
@@ -0,0 +1,20 @@
1
+ import DOMPurify from "dompurify";
2
+ import showdown from "showdown";
3
+
4
+ export function initialize() {
5
+ showdown.extension("DOMPurify", function () {
6
+ return [
7
+ {
8
+ type: "output",
9
+ filter(dirty) {
10
+ return DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
11
+ },
12
+ },
13
+ ];
14
+ });
15
+ }
16
+
17
+ export default {
18
+ name: "register-showdown-extensions",
19
+ initialize,
20
+ };
@@ -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
  }
@@ -0,0 +1,4 @@
1
+ export {
2
+ default,
3
+ initialize,
4
+ } from "@projectcaluma/ember-form/initializers/register-showdown-extensions";
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.2",
4
4
  "description": "Ember addon for rendering Caluma forms.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -14,6 +14,7 @@
14
14
  "@ember/test-waiters": "^4.1.1",
15
15
  "@embroider/macros": "^1.16.10",
16
16
  "@embroider/util": "^1.13.2",
17
+ "dompurify": "^3.3.1",
17
18
  "ember-apollo-client": "^5.0.0",
18
19
  "ember-auto-import": "^2.10.0",
19
20
  "ember-autoresize-modifier": "^0.7.0 || ^0.8.0",
@@ -39,7 +40,7 @@
39
40
  "luxon": "^3.5.0",
40
41
  "reactiveweb": "^1.3.0",
41
42
  "tracked-toolbox": "^2.0.0",
42
- "@projectcaluma/ember-core": "^14.8.1"
43
+ "@projectcaluma/ember-core": "^14.8.2"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@ember/optional-features": "2.3.0",
@@ -74,12 +75,12 @@
74
75
  "uikit": "3.25.6",
75
76
  "uuid": "13.0.0",
76
77
  "webpack": "5.104.1",
77
- "@projectcaluma/ember-workflow": "14.8.1",
78
- "@projectcaluma/ember-testing": "14.8.1"
78
+ "@projectcaluma/ember-testing": "14.8.2",
79
+ "@projectcaluma/ember-workflow": "14.8.2"
79
80
  },
80
81
  "peerDependencies": {
81
82
  "ember-source": ">= 4.0.0",
82
- "@projectcaluma/ember-workflow": "^14.8.1"
83
+ "@projectcaluma/ember-workflow": "^14.8.2"
83
84
  },
84
85
  "dependenciesMeta": {
85
86
  "@projectcaluma/ember-core": {