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

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/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
- };