@projectcaluma/ember-form 11.0.0-beta.5 → 11.0.0-beta.9

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,50 @@
1
+ # [@projectcaluma/ember-form-v11.0.0-beta.9](https://github.com/projectcaluma/ember-caluma/compare/@projectcaluma/ember-form-v11.0.0-beta.8...@projectcaluma/ember-form-v11.0.0-beta.9) (2022-02-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **action-button:** only hide action button visually if disabled ([e292f09](https://github.com/projectcaluma/ember-caluma/commit/e292f093c33efd0b99a73674f8f83de5f496bf40))
7
+ * **deps:** remove moment altogether and update ember-pikday ([b2f7fa2](https://github.com/projectcaluma/ember-caluma/commit/b2f7fa28fa076897addd36e5964c926c671508ff))
8
+ * **form:** change file download actions to query and network-only ([aa4458e](https://github.com/projectcaluma/ember-caluma/commit/aa4458e944263f0f00ba1684a4f7bbfa83d2efea))
9
+
10
+
11
+ ### BREAKING CHANGES
12
+
13
+ * **deps:** The host app now needs to opt-in to use the default
14
+ pikaday styles: https://github.com/adopted-ember-addons/ember-pikaday#styles
15
+
16
+ # [@projectcaluma/ember-form-v11.0.0-beta.8](https://github.com/projectcaluma/ember-caluma/compare/@projectcaluma/ember-form-v11.0.0-beta.7...@projectcaluma/ember-form-v11.0.0-beta.8) (2022-02-03)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * dye file upload button in default color ([ad9e1b8](https://github.com/projectcaluma/ember-caluma/commit/ad9e1b8ed18c8228c495142af1bbe1f2c219c088))
22
+ * **form:** fix isNew state of answers without backend object ([c10129f](https://github.com/projectcaluma/ember-caluma/commit/c10129ffe76d7751cd0105c71fc4ba010323e2e4))
23
+
24
+ # [@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)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **validation:** sync format validator validation with backend ([ee66968](https://github.com/projectcaluma/ember-caluma/commit/ee66968230b9f0e4c5a4df8bdb3f8e58b44b5b82))
30
+
31
+
32
+ ### BREAKING CHANGES
33
+
34
+ * **validation:** Use the `formatValidators` property of the backend to store and read
35
+ format validators instead of the `meta.formatValidators` so the backend
36
+ validates as well. For more information on how to migrate check the
37
+ migration guide to v11.
38
+
39
+ # [@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)
40
+
41
+
42
+ ### Bug Fixes
43
+
44
+ * **action-button:** hide field wrapper if action button is not displayed ([1d4b701](https://github.com/projectcaluma/ember-caluma/commit/1d4b701af35d48d072001f3295b905b1065daffe))
45
+ * render static questions ([28fff94](https://github.com/projectcaluma/ember-caluma/commit/28fff9487e4ad3153fe267db4b66208ad858aaac))
46
+ * **table:** fix styling of table fields ([30c5903](https://github.com/projectcaluma/ember-caluma/commit/30c590389ed82c37ebf666f1484501c66ca8f0bb))
47
+
1
48
  # [@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)
2
49
 
3
50
 
@@ -1,19 +1,18 @@
1
- {{#unless @disabled}}
2
- <DocumentValidity
3
- @document={{@field.document}}
4
- @validateOnEnter={{this.validateOnEnter}}
5
- as |isValid validate|
6
- >
7
- <WorkItemButton
8
- @workItemId={{this.workItem}}
9
- @mutation={{this.action}}
10
- @label={{@field.question.raw.label}}
11
- @disabled={{and (not-eq isValid null) (not isValid)}}
12
- @color={{this.color}}
13
- @beforeMutate={{fn this.beforeMutate validate}}
14
- @onSuccess={{this.onSuccess}}
15
- @onError={{this.onError}}
16
- @type={{this.type}}
17
- />
18
- </DocumentValidity>
19
- {{/unless}}
1
+ <DocumentValidity
2
+ @document={{@field.document}}
3
+ @validateOnEnter={{this.validateOnEnter}}
4
+ as |isValid validate|
5
+ >
6
+ <WorkItemButton
7
+ @workItemId={{this.workItem}}
8
+ @mutation={{this.action}}
9
+ @label={{@field.question.raw.label}}
10
+ @disabled={{or (and (not-eq isValid null) (not isValid)) @disabled}}
11
+ @color={{this.color}}
12
+ @beforeMutate={{fn this.beforeMutate validate}}
13
+ @onSuccess={{this.onSuccess}}
14
+ @onError={{this.onError}}
15
+ @type={{this.type}}
16
+ class={{if @disabled "uk-hidden"}}
17
+ />
18
+ </DocumentValidity>
@@ -13,13 +13,16 @@
13
13
  readonly
14
14
  />
15
15
  {{else}}
16
- <PikadayInput
16
+ <input
17
17
  class="uk-input"
18
+ type="text"
18
19
  name={{@field.pk}}
19
20
  id={{@field.pk}}
20
- @format="L"
21
- @onSelection={{this.onChange}}
22
- @useUTC={{true}}
23
- @value={{@field.answer.value}}
21
+ {{pikaday
22
+ toString=this.formatDate
23
+ i18n=this.pikadayTranslations
24
+ value=@field.answer.value
25
+ onSelect=this.onChange
26
+ }}
24
27
  />
25
28
  {{/if}}
@@ -1,19 +1,37 @@
1
1
  import { action } from "@ember/object";
2
+ import { inject as service } from "@ember/service";
2
3
  import Component from "@glimmer/component";
3
- import moment from "moment";
4
+ import { DateTime, Info } from "luxon";
5
+ import { cached } from "tracked-toolbox";
4
6
 
5
7
  export default class CfFieldInputDateComponent extends Component {
8
+ @service intl;
9
+
6
10
  @action
7
11
  onChange(date) {
8
12
  // Change Javascript date to ISO string if not null.
9
- this.args.onSave(
10
- date
11
- ? moment({
12
- day: date.getUTCDate(),
13
- month: date.getUTCMonth(),
14
- year: date.getUTCFullYear(),
15
- }).format(moment.HTML5_FMT.DATE)
16
- : null
17
- );
13
+ this.args.onSave(date ? DateTime.fromJSDate(date).toISODate() : null);
14
+ }
15
+
16
+ @action
17
+ formatDate(date) {
18
+ return this.intl.formatDate(date, {
19
+ day: "2-digit",
20
+ month: "2-digit",
21
+ year: "numeric",
22
+ });
23
+ }
24
+
25
+ @cached
26
+ get pikadayTranslations() {
27
+ const locale = this.intl.primaryLocale;
28
+
29
+ return {
30
+ previousMonth: this.intl.t("caluma.form.pikaday.month-previous"),
31
+ nextMonth: this.intl.t("caluma.form.pikaday.month-next"),
32
+ months: Info.months("long", { locale }),
33
+ weekdays: Info.weekdays("long", { locale }),
34
+ weekdaysShort: Info.weekdays("short", { locale }),
35
+ };
18
36
  }
19
37
  }
@@ -8,7 +8,7 @@
8
8
  disabled={{@disabled}}
9
9
  {{on "change" this.save}}
10
10
  />
11
- <UkButton @color="primary" @disabled={{@disabled}}>
11
+ <UkButton disabled={{@disabled}}>
12
12
  {{t "caluma.form.selectFile"}}
13
13
  </UkButton>
14
14
  </div>
@@ -22,11 +22,11 @@ export default class CfFieldInputFileComponent extends Component {
22
22
 
23
23
  @action
24
24
  async download() {
25
- const { downloadUrl } = await this.apollo.watchQuery(
25
+ const { downloadUrl } = await this.apollo.query(
26
26
  {
27
27
  query: getFileAnswerInfoQuery,
28
28
  variables: { id: this.args.field.answer.raw.id },
29
- fetchPolicy: "cache-and-network",
29
+ fetchPolicy: "network-only",
30
30
  },
31
31
  "node.fileValue"
32
32
  );
@@ -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,12 +57,13 @@
57
57
  <tr>
58
58
  <td colspan={{add this.columns.length 1}} class="uk-text-center">
59
59
  <UkButton
60
- @size="small"
60
+ @color="link"
61
61
  @onClick={{perform this.add}}
62
+ title={{t "caluma.form.addRow"}}
62
63
  data-test-add-row
63
64
  >
64
65
  <UkIcon @icon="plus" />
65
- <span class="uk-hidden">{{t "caluma.form.addRow"}}</span>
66
+ <span hidden>{{t "caluma.form.addRow"}}</span>
66
67
  </UkButton>
67
68
  </td>
68
69
  </tr>
@@ -85,7 +86,7 @@
85
86
  />
86
87
  </modal.body>
87
88
 
88
- <modal.footer @class="uk-text-right">
89
+ <modal.footer class="uk-text-right">
89
90
  {{#if @disabled}}
90
91
  <UkButton
91
92
  @label={{t "caluma.form.close"}}
@@ -1,9 +1,24 @@
1
- {{#if this.value.fileAnswerId}}
2
- <UkButton
3
- @color="link"
4
- @label={{this.value.label}}
5
- @onClick={{fn this.download this.value.fileAnswerId}}
6
- />
1
+ {{#if (has-question-type @field.question "choice" "dynamic-choice")}}
2
+ {{@field.selected.label}}
3
+ {{else if (has-question-type
4
+ @field.question "multiple-choice" "multiple-dynamic-choice"
5
+ )}}
6
+ {{#each @field.selected as |opt i|}}{{if (gt i 0) ", "}}{{opt.label}}{{/each}}
7
+ {{else if (has-question-type @field.question "date")}}
8
+ {{format-date
9
+ @field.answer.value
10
+ day="2-digit"
11
+ month="2-digit"
12
+ year="numeric"
13
+ }}
14
+ {{else if (has-question-type @field.question "file")}}
15
+ {{#if @field.answer.value}}
16
+ <UkButton
17
+ @color="link"
18
+ @label={{@field.answer.value.name}}
19
+ @onClick={{fn this.download @field.answer.raw.id}}
20
+ />
21
+ {{/if}}
7
22
  {{else}}
8
- {{this.value.label}}
23
+ {{@field.answer.value}}
9
24
  {{/if}}
@@ -1,50 +1,25 @@
1
1
  import { action } from "@ember/object";
2
2
  import Component from "@glimmer/component";
3
3
  import { queryManager } from "ember-apollo-client";
4
- import moment from "moment";
5
4
 
6
5
  import getFileAnswerInfoQuery from "@projectcaluma/ember-form/gql/queries/fileanswer-info.graphql";
7
6
 
8
7
  export default class CfFieldValueComponent extends Component {
9
8
  @queryManager apollo;
10
9
 
11
- get value() {
12
- const field = this.args.field;
13
-
14
- switch (field.questionType) {
15
- case "ChoiceQuestion":
16
- case "DynamicChoiceQuestion": {
17
- return field.selected;
18
- }
19
- case "MultipleChoiceQuestion":
20
- case "DynamicMultipleChoiceQuestion": {
21
- return { label: field.selected.map(({ label }) => label).join(", ") };
22
- }
23
- case "FileQuestion": {
24
- const answerValue = field.answer.value;
25
- return {
26
- fileAnswerId: answerValue && field.answer.raw.id,
27
- label: answerValue?.name,
28
- };
29
- }
30
- case "DateQuestion": {
31
- return {
32
- label: field.answer.value && moment(field.answer.value).format("L"),
33
- };
34
- }
35
-
36
- default:
37
- return { label: field.answer.value };
38
- }
39
- }
40
-
41
10
  @action
42
11
  async download(id) {
43
12
  const { downloadUrl } = await this.apollo.query(
44
- { query: getFileAnswerInfoQuery, variables: { id } },
13
+ {
14
+ query: getFileAnswerInfoQuery,
15
+ variables: { id },
16
+ fetchPolicy: "network-only",
17
+ },
45
18
  "node.fileValue"
46
19
  );
47
20
 
48
- window.open(downloadUrl, "_blank");
21
+ if (downloadUrl) {
22
+ window.open(downloadUrl, "_blank");
23
+ }
49
24
  }
50
25
  }
@@ -1,6 +1,10 @@
1
1
  {{#if this.visible}}
2
2
  <div
3
- class="uk-margin"
3
+ class="uk-margin
4
+ {{if
5
+ (and @disabled (has-question-type @field.question 'action-button'))
6
+ 'uk-hidden'
7
+ }}"
4
8
  {{did-insert this.registerComponent}}
5
9
  {{will-destroy this.unregisterComponent}}
6
10
  >
@@ -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
 
@@ -113,6 +113,7 @@ export default class Field extends Base {
113
113
 
114
114
  answer = new Answer({
115
115
  raw: {
116
+ id: null,
116
117
  __typename: answerType,
117
118
  question: { slug: this.raw.question.slug },
118
119
  [camelize(answerType.replace(/Answer$/, "Value"))]: null,
@@ -628,6 +629,35 @@ export default class Field extends Base {
628
629
  this._errors = errors;
629
630
  }
630
631
 
632
+ /**
633
+ * Validate the value against the regexes of the given format validators.
634
+ *
635
+ * @method _validateFormatValidators
636
+ * @return {Array<Boolean|Object>} An array of error objects or `true`
637
+ * @private
638
+ */
639
+ _validateFormatValidators() {
640
+ const validators =
641
+ this.question.raw.formatValidators?.edges.map((edge) => edge.node) ?? [];
642
+ const value = this.answer.value;
643
+
644
+ if (isEmpty(value)) {
645
+ // empty values should not be validated since they are handled by the
646
+ // requiredness validation
647
+ return validators.map(() => true);
648
+ }
649
+
650
+ return validators.map((validator) => {
651
+ return (
652
+ new RegExp(validator.regex).test(value) || {
653
+ type: "format",
654
+ context: { errorMsg: validator.errorMsg },
655
+ value,
656
+ }
657
+ );
658
+ });
659
+ }
660
+
631
661
  /**
632
662
  * Method to validate if a question is required or not.
633
663
  *
@@ -647,15 +677,12 @@ export default class Field extends Base {
647
677
  * predefined by the question.
648
678
  *
649
679
  * @method _validateTextQuestion
650
- * @return {Promise<Boolean|Object>} A promise which resolves into an object if invalid or true if valid
680
+ * @return {Array<Boolean|Object>} An array of error objects or `true`
651
681
  * @private
652
682
  */
653
- async _validateTextQuestion() {
683
+ _validateTextQuestion() {
654
684
  return [
655
- ...(await this.validator.validate(
656
- this.answer.value,
657
- this.question.raw.meta.formatValidators ?? []
658
- )),
685
+ ...this._validateFormatValidators(),
659
686
  validate("length", this.answer.value, {
660
687
  min: this.question.raw.textMinLength || 0,
661
688
  max: this.question.raw.textMaxLength || Number.POSITIVE_INFINITY,
@@ -668,15 +695,12 @@ export default class Field extends Base {
668
695
  * than predefined by the question.
669
696
  *
670
697
  * @method _validateTextareaQuestion
671
- * @return {Promise<Boolean|Object>} A promise which resolves into an object if invalid or true if valid
698
+ * @return {Array<Boolean|Object>} An array of error objects or `true`
672
699
  * @private
673
700
  */
674
- async _validateTextareaQuestion() {
701
+ _validateTextareaQuestion() {
675
702
  return [
676
- ...(await this.validator.validate(
677
- this.answer.value,
678
- this.question.raw.meta.formatValidators ?? []
679
- )),
703
+ ...this._validateFormatValidators(),
680
704
  validate("length", this.answer.value, {
681
705
  min: this.question.raw.textareaMinLength || 0,
682
706
  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.5",
3
+ "version": "11.0.0-beta.9",
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.4",
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,19 +24,20 @@
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
31
  "ember-math-helpers": "^2.18.1",
31
- "ember-pikaday": "^3.0.0",
32
+ "ember-pikaday": "^4.0.0",
32
33
  "ember-power-select": "^5.0.3",
33
- "ember-resources": "^4.2.0",
34
- "ember-uikit": "^5.0.0-beta.9",
34
+ "ember-resources": "^4.3.1",
35
+ "ember-uikit": "^5.0.0",
35
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",
39
- "moment": "^2.29.1",
40
+ "luxon": "^2.3.0",
40
41
  "tracked-toolbox": "^1.2.3"
41
42
  },
42
43
  "devDependencies": {
@@ -44,7 +45,7 @@
44
45
  "@ember/test-helpers": "2.6.0",
45
46
  "@embroider/test-setup": "1.0.0",
46
47
  "@faker-js/faker": "6.0.0-alpha.5",
47
- "@projectcaluma/ember-testing": "10.2.0-beta.2",
48
+ "@projectcaluma/ember-testing": "11.0.0-beta.2",
48
49
  "@projectcaluma/ember-workflow": "11.0.0-beta.3",
49
50
  "broccoli-asset-rev": "3.0.0",
50
51
  "ember-cli": "3.28.5",
@@ -1,35 +0,0 @@
1
- import EmberObject from "@ember/object";
2
- import { inject as service } from "@ember/service";
3
- import moment from "moment";
4
-
5
- class Translations extends EmberObject {
6
- @service intl;
7
-
8
- get previousMonth() {
9
- return this.intl.t("caluma.form.pikaday.month-previous");
10
- }
11
-
12
- get nextMonth() {
13
- return this.intl.t("caluma.form.pikaday.month-next");
14
- }
15
-
16
- months = moment.localeData().months();
17
- weekdays = moment.localeData().weekdays();
18
- weekdaysShort = moment.localeData().weekdaysShort();
19
- }
20
-
21
- export function initialize(applicationInstance) {
22
- applicationInstance.register("pikaday-i18n:main", Translations, {
23
- singleton: true,
24
- });
25
- applicationInstance.inject(
26
- "component:pikaday-input",
27
- "i18n",
28
- "pikaday-i18n:main"
29
- );
30
- }
31
-
32
- export default {
33
- name: "setup-pikaday-i18n",
34
- initialize,
35
- };