@projectcaluma/ember-form 14.8.0 → 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.
- package/addon/components/cf-field/info.hbs +5 -1
- package/addon/components/cf-field/input/static.hbs +1 -0
- package/addon/components/document-validity.js +63 -30
- package/addon/gql/queries/document-validity.graphql +16 -0
- package/addon/initializers/register-showdown-extensions.js +20 -0
- package/addon/lib/answer.js +1 -0
- package/addon/lib/document.js +16 -1
- package/addon/lib/field.js +17 -8
- package/addon/services/caluma-store.js +4 -0
- package/app/initializers/register-showdown-extensions.js +4 -0
- package/package.json +6 -5
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
as |modal|
|
|
17
17
|
>
|
|
18
18
|
<modal.body>
|
|
19
|
-
<MarkdownToHtml
|
|
19
|
+
<MarkdownToHtml
|
|
20
|
+
@markdown={{@text}}
|
|
21
|
+
@openLinksInNewWindow={{true}}
|
|
22
|
+
@extensions="DOMPurify"
|
|
23
|
+
/>
|
|
20
24
|
</modal.body>
|
|
21
25
|
</UkModal>
|
|
22
26
|
</div>
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { action } from "@ember/object";
|
|
2
|
+
import { service } from "@ember/service";
|
|
2
3
|
import Component from "@glimmer/component";
|
|
4
|
+
import { queryManager } from "ember-apollo-client";
|
|
3
5
|
import { task } from "ember-concurrency";
|
|
4
6
|
import { cached } from "tracked-toolbox";
|
|
5
7
|
|
|
8
|
+
import documentValidityQuery from "@projectcaluma/ember-form/gql/queries/document-validity.graphql";
|
|
9
|
+
|
|
6
10
|
/**
|
|
7
11
|
* Component to check the validity of a document
|
|
8
12
|
*
|
|
@@ -20,6 +24,10 @@ import { cached } from "tracked-toolbox";
|
|
|
20
24
|
* @yield {Function} validate
|
|
21
25
|
*/
|
|
22
26
|
export default class DocumentValidity extends Component {
|
|
27
|
+
@queryManager apollo;
|
|
28
|
+
|
|
29
|
+
@service calumaStore;
|
|
30
|
+
|
|
23
31
|
/**
|
|
24
32
|
* The document to be validated
|
|
25
33
|
*
|
|
@@ -43,39 +51,64 @@ export default class DocumentValidity extends Component {
|
|
|
43
51
|
return this._validate.isRunning;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
_validate = task({ restartable: true }, async () => {
|
|
55
|
+
try {
|
|
56
|
+
const saveTasks = this.args.document.fields
|
|
57
|
+
.flatMap((field) => [
|
|
58
|
+
...[...(field._components ?? [])].map((c) => c.save.last),
|
|
59
|
+
field.save?.last,
|
|
60
|
+
])
|
|
61
|
+
.filter(Boolean);
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
63
|
+
// Wait until all currently running save tasks in the UI and in the field
|
|
64
|
+
// itself are finished
|
|
65
|
+
await Promise.all(saveTasks);
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
await Promise.all(
|
|
68
|
+
this.args.document.fields.map((field) =>
|
|
69
|
+
field.validate.linked().perform(),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const { isValid, errors } = await this.apollo.query(
|
|
74
|
+
{
|
|
75
|
+
query: documentValidityQuery,
|
|
76
|
+
fetchPolicy: "network-only",
|
|
77
|
+
variables: { id: this.args.document.uuid },
|
|
78
|
+
},
|
|
79
|
+
"documentValidity.edges.0.node",
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (!isValid) {
|
|
83
|
+
errors
|
|
84
|
+
.filter(({ errorCode }) => errorCode === "format_validation_failed")
|
|
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);
|
|
77
93
|
|
|
78
|
-
|
|
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
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.isValid) {
|
|
103
|
+
this.args.onValid?.();
|
|
104
|
+
} else {
|
|
105
|
+
this.args.onInvalid?.();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return this.isValid;
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
79
112
|
});
|
|
80
113
|
|
|
81
114
|
@action
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
query DocumentValidity($id: ID!, $dataSourceContext: JSONString) {
|
|
2
|
+
documentValidity(id: $id, dataSourceContext: $dataSourceContext) {
|
|
3
|
+
edges {
|
|
4
|
+
node {
|
|
5
|
+
id
|
|
6
|
+
isValid
|
|
7
|
+
errors {
|
|
8
|
+
slug
|
|
9
|
+
errorMsg
|
|
10
|
+
errorCode
|
|
11
|
+
documentId
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -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
|
+
};
|
package/addon/lib/answer.js
CHANGED
package/addon/lib/document.js
CHANGED
|
@@ -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({
|
|
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
|
*
|
package/addon/lib/field.js
CHANGED
|
@@ -665,14 +665,11 @@ export default class Field extends Base {
|
|
|
665
665
|
await this.onSaveError(e);
|
|
666
666
|
|
|
667
667
|
if (validationError) {
|
|
668
|
-
this.
|
|
669
|
-
|
|
670
|
-
{
|
|
671
|
-
|
|
672
|
-
|
|
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.
|
|
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.
|
|
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-
|
|
78
|
-
"@projectcaluma/ember-
|
|
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.
|
|
83
|
+
"@projectcaluma/ember-workflow": "^14.8.2"
|
|
83
84
|
},
|
|
84
85
|
"dependenciesMeta": {
|
|
85
86
|
"@projectcaluma/ember-core": {
|