@projectcaluma/ember-form 11.0.0-beta.3 → 11.0.0-beta.7

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,42 @@
1
+ # [@projectcaluma/ember-form-v11.0.0-beta.7](https://github.com/projectcaluma/ember-caluma/compare/@projectcaluma/ember-form-v11.0.0-beta.6...@projectcaluma/ember-form-v11.0.0-beta.7) (2022-02-03)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **validation:** sync format validator validation with backend ([ee66968](https://github.com/projectcaluma/ember-caluma/commit/ee66968230b9f0e4c5a4df8bdb3f8e58b44b5b82))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * **validation:** Use the `formatValidators` property of the backend to store and read
12
+ format validators instead of the `meta.formatValidators` so the backend
13
+ validates as well. For more information on how to migrate check the
14
+ migration guide to v11.
15
+
16
+ # [@projectcaluma/ember-form-v11.0.0-beta.6](https://github.com/projectcaluma/ember-caluma/compare/@projectcaluma/ember-form-v11.0.0-beta.5...@projectcaluma/ember-form-v11.0.0-beta.6) (2022-02-02)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * **action-button:** hide field wrapper if action button is not displayed ([1d4b701](https://github.com/projectcaluma/ember-caluma/commit/1d4b701af35d48d072001f3295b905b1065daffe))
22
+ * render static questions ([28fff94](https://github.com/projectcaluma/ember-caluma/commit/28fff9487e4ad3153fe267db4b66208ad858aaac))
23
+ * **table:** fix styling of table fields ([30c5903](https://github.com/projectcaluma/ember-caluma/commit/30c590389ed82c37ebf666f1484501c66ca8f0bb))
24
+
25
+ # [@projectcaluma/ember-form-v11.0.0-beta.5](https://github.com/projectcaluma/ember-caluma/compare/@projectcaluma/ember-form-v11.0.0-beta.4...@projectcaluma/ember-form-v11.0.0-beta.5) (2022-02-01)
26
+
27
+
28
+ ### Bug Fixes
29
+
30
+ * **document-validity:** wait to validate until the UI has finished saving values ([9ec5330](https://github.com/projectcaluma/ember-caluma/commit/9ec5330905046604f95ab42985a29a5e0dc369a4))
31
+ * **form:** use component save task instead of field for loading indicator ([6de510d](https://github.com/projectcaluma/ember-caluma/commit/6de510d19a3608b7cd40092908d562a152ef03bd))
32
+
33
+ # [@projectcaluma/ember-form-v11.0.0-beta.4](https://github.com/projectcaluma/ember-caluma/compare/@projectcaluma/ember-form-v11.0.0-beta.3...@projectcaluma/ember-form-v11.0.0-beta.4) (2022-02-01)
34
+
35
+
36
+ ### Bug Fixes
37
+
38
+ * **form:** fix closing of info modal ([b46095a](https://github.com/projectcaluma/ember-caluma/commit/b46095aa3b31bc1b2035a0818778edaa8fd9c745))
39
+
1
40
  # [@projectcaluma/ember-form-v11.0.0-beta.3](https://github.com/projectcaluma/ember-caluma/compare/@projectcaluma/ember-form-v11.0.0-beta.2...@projectcaluma/ember-form-v11.0.0-beta.3) (2022-01-24)
2
41
 
3
42
 
@@ -4,7 +4,7 @@
4
4
  class="uk-icon-button"
5
5
  uk-icon="info"
6
6
  title={{t "caluma.form.info"}}
7
- {{on "click" this.showModal}}
7
+ {{on "click" (fn (mut this.modalVisible) true)}}
8
8
  >
9
9
  <span class="uk-hidden">{{t "caluma.form.info"}}</span>
10
10
  </button>
@@ -12,7 +12,7 @@
12
12
  <UkModal
13
13
  @visible={{this.modalVisible}}
14
14
  @stack={{true}}
15
- @onHide={{this.hideModal}}
15
+ @onHide={{fn (mut this.modalVisible) false}}
16
16
  as |modal|
17
17
  >
18
18
  <modal.body>
@@ -1,21 +1,6 @@
1
- import { action } from "@ember/object";
2
1
  import Component from "@glimmer/component";
3
2
  import { tracked } from "@glimmer/tracking";
4
3
 
5
4
  export default class CfFieldInfoComponent extends Component {
6
5
  @tracked modalVisible = false;
7
-
8
- @action
9
- showModal(e) {
10
- e.preventDefault();
11
-
12
- this.modalVisible = true;
13
- }
14
-
15
- @action
16
- hideModal(e) {
17
- e.preventDefault();
18
-
19
- this.modalVisible = false;
20
- }
21
6
  }
@@ -1,4 +1,4 @@
1
1
  <MarkdownToHtml
2
- @markdown={{@field.question.staticContent}}
2
+ @markdown={{@field.question.raw.staticContent}}
3
3
  @openLinksInNewWindow={{true}}
4
4
  />
@@ -1,4 +1,4 @@
1
- <table class="uk-table uk-table-divider">
1
+ <table class="uk-table uk-table-divider uk-margin-remove-vertical">
2
2
  <thead>
3
3
  <tr>
4
4
  {{#each this.columns as |column|}}
@@ -25,27 +25,27 @@
25
25
  class="uk-animation-fade uk-text-danger"
26
26
  />
27
27
  {{/if}}
28
- <button
29
- data-test-edit-row
30
- type="button"
31
- class="uk-button uk-button-link uk-flex-inline uk-margin-small-left"
28
+ <UkButton
29
+ @color="link"
30
+ @onClick={{fn this.edit document}}
32
31
  title={{t "caluma.form.edit"}}
33
- {{on "click" (fn this.edit document)}}
32
+ class="uk-flex-inline uk-margin-small-left"
33
+ data-test-edit-row
34
34
  >
35
35
  <UkIcon @icon="pencil" />
36
- <span class="uk-hidden">{{t "caluma.form.edit"}}</span>
37
- </button>
36
+ <span hidden>{{t "caluma.form.edit"}}</span>
37
+ </UkButton>
38
38
  {{#unless @disabled}}
39
- <button
40
- data-test-delete-row
41
- type="button"
42
- class="uk-button uk-button-link uk-flex-inline uk-margin-small-left"
39
+ <UkButton
40
+ @color="link"
41
+ @onClick={{fn (perform this.delete) document}}
43
42
  title={{t "caluma.form.delete"}}
44
- {{on "click" (fn (perform this.delete) document)}}
43
+ class="uk-flex-inline uk-margin-small-left"
44
+ data-test-delete-row
45
45
  >
46
46
  <UkIcon @icon="trash" />
47
- <span class="uk-hidden">{{t "caluma.form.delete"}}</span>
48
- </button>
47
+ <span hidden>{{t "caluma.form.delete"}}</span>
48
+ </UkButton>
49
49
  {{/unless}}
50
50
  </div>
51
51
  </td>
@@ -57,13 +57,13 @@
57
57
  <tr>
58
58
  <td colspan={{add this.columns.length 1}} class="uk-text-center">
59
59
  <UkButton
60
- @size="small"
61
- @color="default"
60
+ @color="link"
62
61
  @onClick={{perform this.add}}
62
+ title={{t "caluma.form.addRow"}}
63
63
  data-test-add-row
64
64
  >
65
65
  <UkIcon @icon="plus" />
66
- <span class="uk-hidden">{{t "caluma.form.addRow"}}</span>
66
+ <span hidden>{{t "caluma.form.addRow"}}</span>
67
67
  </UkButton>
68
68
  </td>
69
69
  </tr>
@@ -86,7 +86,7 @@
86
86
  />
87
87
  </modal.body>
88
88
 
89
- <modal.footer @class="uk-text-right">
89
+ <modal.footer class="uk-text-right">
90
90
  {{#if @disabled}}
91
91
  <UkButton
92
92
  @label={{t "caluma.form.close"}}
@@ -1,5 +1,9 @@
1
1
  {{#if this.visible}}
2
- <div class="uk-margin">
2
+ <div
3
+ class="uk-margin"
4
+ {{did-insert this.registerComponent}}
5
+ {{will-destroy this.unregisterComponent}}
6
+ >
3
7
  {{#if this.labelVisible}}
4
8
  <CfField::label @field={{@field}} />
5
9
  {{/if}}
@@ -24,11 +28,11 @@
24
28
  <div
25
29
  class="cf-field__icon uk-padding-remove-vertical uk-flex uk-flex-middle uk-flex-center"
26
30
  >
27
- {{#if @field.save.isRunning}}
31
+ {{#if this.save.isRunning}}
28
32
  <UkSpinner class="uk-animation-fade" />
29
- {{else if (or @field.save.last.isError @field.isInvalid)}}
33
+ {{else if (or this.save.last.isError @field.isInvalid)}}
30
34
  <UkIcon @icon="warning" class="uk-animation-fade uk-text-danger" />
31
- {{else if @field.save.last.isSuccessful}}
35
+ {{else if this.save.last.isSuccessful}}
32
36
  <UkIcon @icon="check" class="uk-animation-fade uk-text-success" />
33
37
  {{/if}}
34
38
  </div>
@@ -1,5 +1,5 @@
1
1
  import { getOwner } from "@ember/application";
2
- import { set } from "@ember/object";
2
+ import { action } from "@ember/object";
3
3
  import Component from "@glimmer/component";
4
4
  import { timeout, restartableTask } from "ember-concurrency";
5
5
 
@@ -18,10 +18,24 @@ import { hasQuestionType } from "@projectcaluma/ember-core/helpers/has-question-
18
18
  * @argument {Field} field The field data model to render
19
19
  */
20
20
  export default class CfFieldComponent extends Component {
21
+ @action
22
+ registerComponent() {
23
+ this.args.field._components.add(this);
24
+ }
25
+
26
+ @action
27
+ unregisterComponent() {
28
+ this.args.field._components.delete(this);
29
+ }
30
+
21
31
  get visible() {
22
32
  return (
23
33
  !this.args.field?.hidden &&
24
- !hasQuestionType(this.args.field?.question, "form")
34
+ !hasQuestionType(this.args.field?.question, "form") &&
35
+ !(
36
+ hasQuestionType(this.args.field?.question, "action-button") &&
37
+ this.args.disabled
38
+ )
25
39
  );
26
40
  }
27
41
 
@@ -41,8 +55,9 @@ export default class CfFieldComponent extends Component {
41
55
  }
42
56
 
43
57
  /**
44
- * Task to save a field. This will set the passed value to the answer and
45
- * save the field to the API after a timeout off 500 milliseconds.
58
+ * Task to save a field. This will set the passed value to the answer and save
59
+ * the field to the API after a timeout of 500 milliseconds which intends to
60
+ * reduce the amount of saved values when changed rapidly.
46
61
  *
47
62
  * @method save
48
63
  * @param {String|Number|String[]} value
@@ -57,14 +72,18 @@ export default class CfFieldComponent extends Component {
57
72
  yield timeout(500);
58
73
  }
59
74
 
60
- set(this.args.field.answer, "value", value);
75
+ if (this.args.field.answer) {
76
+ this.args.field.answer.value = value;
77
+ }
61
78
 
62
79
  yield this.args.field.validate.perform();
63
80
 
64
81
  try {
82
+ // Save the new field value unlinked so the fields save task is not
83
+ // aborted when this component is destroyed
65
84
  return yield this.args.field.save.unlinked().perform();
66
85
  } catch (e) {
67
- // that's ok
86
+ // The component was destroyed before the fields save task was finished
68
87
  }
69
88
  }
70
89
  }
@@ -1,6 +1,7 @@
1
1
  import { action } from "@ember/object";
2
2
  import Component from "@glimmer/component";
3
3
  import { restartableTask } from "ember-concurrency";
4
+ import { cached } from "tracked-toolbox";
4
5
 
5
6
  /**
6
7
  * Component to check the validity of a document
@@ -31,12 +32,26 @@ export default class DocumentValidity extends Component {
31
32
  * @argument {Boolean} validateOnEnter
32
33
  */
33
34
 
35
+ @cached
34
36
  get isValid() {
35
- return this.args.document.fields.every((f) => f.isValid);
37
+ return this.args.document.fields
38
+ .filter((f) => !f.hidden)
39
+ .every((f) => f.isValid);
36
40
  }
37
41
 
38
42
  @restartableTask
39
43
  *_validate() {
44
+ const saveTasks = this.args.document.fields
45
+ .flatMap((field) => [
46
+ ...[...(field._components ?? [])].map((c) => c.save.last),
47
+ field.save?.last,
48
+ ])
49
+ .filter(Boolean);
50
+
51
+ // Wait until all currently running save tasks in the UI and in the field
52
+ // itself are finished
53
+ yield Promise.all(saveTasks);
54
+
40
55
  for (const field of this.args.document.fields) {
41
56
  yield field.validate.linked().perform();
42
57
  }
@@ -19,6 +19,15 @@ fragment SimpleQuestion on Question {
19
19
  value
20
20
  }
21
21
  placeholder
22
+ formatValidators {
23
+ edges {
24
+ node {
25
+ slug
26
+ regex
27
+ errorMsg
28
+ }
29
+ }
30
+ }
22
31
  }
23
32
  ... on TextareaQuestion {
24
33
  textareaMinLength: minLength
@@ -28,6 +37,15 @@ fragment SimpleQuestion on Question {
28
37
  value
29
38
  }
30
39
  placeholder
40
+ formatValidators {
41
+ edges {
42
+ node {
43
+ slug
44
+ regex
45
+ errorMsg
46
+ }
47
+ }
48
+ }
31
49
  }
32
50
  ... on IntegerQuestion {
33
51
  integerMinValue: minValue
@@ -3,6 +3,7 @@ import { assert } from "@ember/debug";
3
3
  import { associateDestroyableChild } from "@ember/destroyable";
4
4
  import { inject as service } from "@ember/service";
5
5
  import { camelize } from "@ember/string";
6
+ import { isEmpty } from "@ember/utils";
6
7
  import { tracked } from "@glimmer/tracking";
7
8
  import { queryManager } from "ember-apollo-client";
8
9
  import { restartableTask, lastValue, dropTask } from "ember-concurrency";
@@ -63,7 +64,6 @@ const fieldIsHiddenOrEmpty = (field) => {
63
64
  */
64
65
  export default class Field extends Base {
65
66
  @service intl;
66
- @service validator;
67
67
 
68
68
  @queryManager apollo;
69
69
 
@@ -152,6 +152,16 @@ export default class Field extends Base {
152
152
  */
153
153
  @tracked _errors = [];
154
154
 
155
+ /**
156
+ * Currently rendered field components that use this field. This is used in
157
+ * the document validity component to await all current save tasks before
158
+ * validating.
159
+ *
160
+ * @property {Set} _components
161
+ * @private
162
+ */
163
+ _components = new Set();
164
+
155
165
  /**
156
166
  * The primary key of the field. Consists of the document and question primary
157
167
  * keys.
@@ -618,6 +628,35 @@ export default class Field extends Base {
618
628
  this._errors = errors;
619
629
  }
620
630
 
631
+ /**
632
+ * Validate the value against the regexes of the given format validators.
633
+ *
634
+ * @method _validateFormatValidators
635
+ * @return {Array<Boolean|Object>} An array of error objects or `true`
636
+ * @private
637
+ */
638
+ _validateFormatValidators() {
639
+ const validators =
640
+ this.question.raw.formatValidators?.edges.map((edge) => edge.node) ?? [];
641
+ const value = this.answer.value;
642
+
643
+ if (isEmpty(value)) {
644
+ // empty values should not be validated since they are handled by the
645
+ // requiredness validation
646
+ return validators.map(() => true);
647
+ }
648
+
649
+ return validators.map((validator) => {
650
+ return (
651
+ new RegExp(validator.regex).test(value) || {
652
+ type: "format",
653
+ context: { errorMsg: validator.errorMsg },
654
+ value,
655
+ }
656
+ );
657
+ });
658
+ }
659
+
621
660
  /**
622
661
  * Method to validate if a question is required or not.
623
662
  *
@@ -637,15 +676,12 @@ export default class Field extends Base {
637
676
  * predefined by the question.
638
677
  *
639
678
  * @method _validateTextQuestion
640
- * @return {Promise<Boolean|Object>} A promise which resolves into an object if invalid or true if valid
679
+ * @return {Array<Boolean|Object>} An array of error objects or `true`
641
680
  * @private
642
681
  */
643
- async _validateTextQuestion() {
682
+ _validateTextQuestion() {
644
683
  return [
645
- ...(await this.validator.validate(
646
- this.answer.value,
647
- this.question.raw.meta.formatValidators ?? []
648
- )),
684
+ ...this._validateFormatValidators(),
649
685
  validate("length", this.answer.value, {
650
686
  min: this.question.raw.textMinLength || 0,
651
687
  max: this.question.raw.textMaxLength || Number.POSITIVE_INFINITY,
@@ -658,15 +694,12 @@ export default class Field extends Base {
658
694
  * than predefined by the question.
659
695
  *
660
696
  * @method _validateTextareaQuestion
661
- * @return {Promise<Boolean|Object>} A promise which resolves into an object if invalid or true if valid
697
+ * @return {Array<Boolean|Object>} An array of error objects or `true`
662
698
  * @private
663
699
  */
664
- async _validateTextareaQuestion() {
700
+ _validateTextareaQuestion() {
665
701
  return [
666
- ...(await this.validator.validate(
667
- this.answer.value,
668
- this.question.raw.meta.formatValidators ?? []
669
- )),
702
+ ...this._validateFormatValidators(),
670
703
  validate("length", this.answer.value, {
671
704
  min: this.question.raw.textareaMinLength || 0,
672
705
  max: this.question.raw.textareaMaxLength || Number.POSITIVE_INFINITY,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectcaluma/ember-form",
3
- "version": "11.0.0-beta.3",
3
+ "version": "11.0.0-beta.7",
4
4
  "description": "Ember addon for rendering Caluma forms.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "@glimmer/component": "^1.0.4",
18
18
  "@glimmer/tracking": "^1.0.4",
19
- "@projectcaluma/ember-core": "^11.0.0-beta.2",
19
+ "@projectcaluma/ember-core": "^11.0.0-beta.3",
20
20
  "ember-apollo-client": "^3.2.0",
21
21
  "ember-auto-import": "^2.4.0",
22
22
  "ember-autoresize-modifier": "^0.5.0",
@@ -24,15 +24,16 @@
24
24
  "ember-cli-htmlbars": "^6.0.1",
25
25
  "ember-cli-showdown": "^6.0.1",
26
26
  "ember-composable-helpers": "^5.0.0",
27
+ "ember-concurrency": "^2.2.0",
27
28
  "ember-fetch": "^8.1.1",
28
29
  "ember-in-viewport": "^4.0.0",
29
30
  "ember-intl": "^5.7.2",
30
- "ember-math-helpers": "^2.18.0",
31
+ "ember-math-helpers": "^2.18.1",
31
32
  "ember-pikaday": "^3.0.0",
32
33
  "ember-power-select": "^5.0.3",
33
- "ember-resources": "^4.1.3",
34
- "ember-uikit": "^5.0.0-beta.3",
35
- "ember-validators": "^4.0.1",
34
+ "ember-resources": "^4.2.0",
35
+ "ember-uikit": "^5.0.0-beta.11",
36
+ "ember-validators": "^4.1.0",
36
37
  "graphql": "^15.8.0",
37
38
  "jexl": "^2.3.0",
38
39
  "lodash.isequal": "^4.5.0",
@@ -43,9 +44,9 @@
43
44
  "@ember/optional-features": "2.0.0",
44
45
  "@ember/test-helpers": "2.6.0",
45
46
  "@embroider/test-setup": "1.0.0",
46
- "@faker-js/faker": "6.0.0-alpha.3",
47
- "@projectcaluma/ember-testing": "10.2.0-beta.2",
48
- "@projectcaluma/ember-workflow": "11.0.0-beta.1",
47
+ "@faker-js/faker": "6.0.0-alpha.5",
48
+ "@projectcaluma/ember-testing": "11.0.0-beta.1",
49
+ "@projectcaluma/ember-workflow": "11.0.0-beta.3",
49
50
  "broccoli-asset-rev": "3.0.0",
50
51
  "ember-cli": "3.28.5",
51
52
  "ember-cli-code-coverage": "1.0.3",
@@ -69,7 +70,7 @@
69
70
  "qunit": "2.17.2",
70
71
  "qunit-dom": "2.0.0",
71
72
  "uuid": "8.3.2",
72
- "webpack": "5.67.0"
73
+ "webpack": "5.68.0"
73
74
  },
74
75
  "engines": {
75
76
  "node": "12.* || 14.* || >= 16"