@projectcaluma/ember-form 14.13.0 → 14.14.1

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.
@@ -158,9 +158,9 @@ export default class CfContentComponent extends Component {
158
158
  const Document = owner.factoryFor("caluma-model:document").class;
159
159
  const Navigation = owner.factoryFor("caluma-model:navigation").class;
160
160
 
161
- let answerDocument = null;
162
- let historicalDocument = null;
163
- let jexlContext = null;
161
+ let answerDocument;
162
+ let historicalDocument;
163
+ let jexlContext;
164
164
 
165
165
  if (!this.args.compare) {
166
166
  const data = await this.apollo.query({
@@ -70,6 +70,7 @@
70
70
  <UkButton
71
71
  @color="link"
72
72
  @onClick={{fn this.edit document}}
73
+ @disabled={{or this.add.isRunning this.delete.isRunning}}
73
74
  title={{t "caluma.form.edit"}}
74
75
  class="uk-flex-inline uk-margin-small-left table-controls"
75
76
  data-test-edit-row
@@ -80,6 +81,7 @@
80
81
  <UkButton
81
82
  @color="link"
82
83
  @onClick={{fn this.delete.perform document}}
84
+ @disabled={{or this.add.isRunning this.delete.isRunning}}
83
85
  title={{t "caluma.form.delete"}}
84
86
  class="uk-flex-inline uk-margin-small-left table-controls"
85
87
  data-test-delete-row
@@ -100,6 +102,8 @@
100
102
  <UkButton
101
103
  @color="link"
102
104
  @onClick={{this.add.perform}}
105
+ @disabled={{this.add.isRunning}}
106
+ @loading={{this.add.isRunning}}
103
107
  title={{t "caluma.form.addRow"}}
104
108
  data-test-add-row
105
109
  >
@@ -123,7 +127,7 @@
123
127
  <CfFormWrapper
124
128
  @document={{this.documentToEdit}}
125
129
  @fieldset={{object-at 0 this.documentToEdit.fieldsets}}
126
- @disabled={{@disabled}}
130
+ @disabled={{or @disabled this.add.isRunning}}
127
131
  @context={{@context}}
128
132
  @compare={{@compare}}
129
133
  />
@@ -143,7 +147,7 @@
143
147
  <UkButton
144
148
  @label={{t "caluma.form.cancel"}}
145
149
  @onClick={{this.close.perform}}
146
- @disabled={{this.close.isRunning}}
150
+ @disabled={{or this.close.isRunning this.add.isRunning}}
147
151
  @loading={{this.close.isRunning}}
148
152
  data-test-cancel
149
153
  />
@@ -74,22 +74,69 @@ export default class CfFieldInputTableComponent extends Component {
74
74
  owner,
75
75
  });
76
76
 
77
+ // Open the modal immediately - attaching the row to the table (below) is
78
+ // debounced and would otherwise noticeably delay opening the modal. While
79
+ // this task runs, the row's fields and the cancel button are disabled in
80
+ // the modal, so no answer can be saved to (and validated by) the backend
81
+ // and the row cannot be removed again before it is attached to the table.
77
82
  this.documentToEditIsNew = true;
78
83
  this.documentToEdit = newDocument;
79
84
  this.showAddModal = true;
80
- });
81
85
 
82
- delete = task({ drop: true }, async (document) => {
83
- if (!(await confirm(this.intl.t("caluma.form.deleteRow")))) {
84
- return;
86
+ // Attach the row document to the table answer, so the backend has the
87
+ // full context to validate and evaluate the row's fields while editing.
88
+ // `documentToEditIsNew` marks the row as preliminary, so it will be
89
+ // removed again if the edit dialog is cancelled.
90
+ try {
91
+ const rows = this.args.field.answer.value ?? [];
92
+ this.args.field.answer.value = [...rows, newDocument];
93
+ await this.args.field.save.perform();
94
+ } catch {
95
+ this.notification.danger(
96
+ this.intl.t("caluma.form.notification.table.add.error"),
97
+ );
98
+
99
+ // Attaching failed - remove the preliminary row and close the modal.
100
+ // `close` cannot be performed here, as it waits for this task to finish.
101
+ await this.deleteRow(this.documentToEdit);
102
+ this.documentToEditIsNew = false;
103
+ this.showAddModal = false;
104
+ this.documentToEdit = null;
85
105
  }
106
+ });
86
107
 
87
- const remainingDocuments = this.args.field.answer.value.filter(
108
+ /**
109
+ * Delete row without asking. Remove from the table, then remove row document
110
+ *
111
+ * @async
112
+ * @method deleteRow
113
+ * @param {Document} document The row document to delete
114
+ * @return {Promise<Void>}
115
+ */
116
+ async deleteRow(document) {
117
+ const remainingDocuments = (this.args.field.answer.value ?? []).filter(
88
118
  (doc) => doc.pk !== document.pk,
89
119
  );
90
120
 
121
+ // remove row from table
91
122
  await this.args.onSave(remainingDocuments);
92
- await this.removeOrphan(document);
123
+
124
+ // delete row document
125
+ await this.apollo.mutate({
126
+ mutation: removeDocumentMutation,
127
+ variables: { input: { document: document.uuid } },
128
+ });
129
+
130
+ // Remove orphaned document from Caluma store.
131
+ this.calumaStore.delete(document.pk);
132
+ }
133
+
134
+ delete = task({ drop: true }, async (document) => {
135
+ if (!(await confirm(this.intl.t("caluma.form.deleteRow")))) {
136
+ return;
137
+ }
138
+
139
+ await this.deleteRow(document);
93
140
  });
94
141
 
95
142
  save = task({ drop: true }, async (validate) => {
@@ -106,19 +153,13 @@ export default class CfFieldInputTableComponent extends Component {
106
153
  return;
107
154
  }
108
155
 
109
- const rows = this.args.field.answer.value ?? [];
110
-
111
- if (!rows.find((doc) => doc.pk === newDocument.pk)) {
112
- // add document to table
113
- await this.args.onSave([...rows, newDocument]);
114
-
156
+ if (this.documentToEditIsNew) {
115
157
  this.notification.success(
116
158
  this.intl.t("caluma.form.notification.table.add.success"),
117
159
  );
160
+ this.documentToEditIsNew = false;
118
161
  }
119
162
 
120
- this.documentToEditIsNew = false;
121
-
122
163
  await this.close.perform();
123
164
  } catch {
124
165
  this.notification.danger(
@@ -128,10 +169,20 @@ export default class CfFieldInputTableComponent extends Component {
128
169
  });
129
170
 
130
171
  close = task({ drop: true }, async () => {
131
- if (this.documentToEditIsNew) {
132
- await this.removeOrphan(this.documentToEdit);
172
+ // Wait for a pending "add" to finish attaching the new row to the table
173
+ // before closing - otherwise removing or validating the row would race
174
+ // with the debounced save of the table answer.
175
+ if (this.add.isRunning) {
176
+ await this.add.last;
177
+ }
133
178
 
179
+ if (this.documentToEditIsNew) {
180
+ await this.deleteRow(this.documentToEdit);
134
181
  this.documentToEditIsNew = false;
182
+ this.showAddModal = false;
183
+ this.documentToEdit = null;
184
+ // No validation for new documents that are deleted
185
+ return;
135
186
  }
136
187
 
137
188
  if (!this.args.disabled) {
@@ -142,17 +193,6 @@ export default class CfFieldInputTableComponent extends Component {
142
193
  this.documentToEdit = null;
143
194
  });
144
195
 
145
- async removeOrphan(calumaDocument) {
146
- // Remove orphaned document from database.
147
- await this.apollo.mutate({
148
- mutation: removeDocumentMutation,
149
- variables: { input: { document: calumaDocument.uuid } },
150
- });
151
-
152
- // Remove orphaned document from Caluma store.
153
- this.calumaStore.delete(calumaDocument.pk);
154
- }
155
-
156
196
  @action
157
197
  edit(document) {
158
198
  this.documentToEdit = document;
@@ -467,6 +467,7 @@ export default class Field extends Base {
467
467
  } catch (error) {
468
468
  throw new Error(
469
469
  `Error while evaluating \`isHidden\` expression on Option\`${option.slug}\`: ${error.message}`,
470
+ { cause: error },
470
471
  );
471
472
  }
472
473
  })
@@ -646,6 +647,7 @@ export default class Field extends Base {
646
647
  } catch (error) {
647
648
  throw new Error(
648
649
  `Error while evaluating \`isHidden\` expression on field \`${this.pk}\`: ${error.message}`,
650
+ { cause: error },
649
651
  );
650
652
  }
651
653
  }
@@ -681,6 +683,7 @@ export default class Field extends Base {
681
683
  } catch (error) {
682
684
  throw new Error(
683
685
  `Error while evaluating \`isRequired\` expression on field \`${this.pk}\`: ${error.message}`,
686
+ { cause: error },
684
687
  );
685
688
  }
686
689
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectcaluma/ember-form",
3
- "version": "14.13.0",
3
+ "version": "14.14.1",
4
4
  "description": "Ember addon for rendering Caluma forms.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -39,10 +39,10 @@
39
39
  "luxon": "^3.5.0",
40
40
  "reactiveweb": "^1.3.0",
41
41
  "tracked-toolbox": "^2.0.0",
42
- "@projectcaluma/ember-core": "^14.13.0"
42
+ "@projectcaluma/ember-core": "^14.14.1"
43
43
  },
44
44
  "devDependencies": {
45
- "@ember/optional-features": "2.3.0",
45
+ "@ember/optional-features": "3.0.0",
46
46
  "@ember/test-helpers": "4.0.4",
47
47
  "@embroider/test-setup": "4.0.0",
48
48
  "@faker-js/faker": "10.2.0",
@@ -59,7 +59,7 @@
59
59
  "ember-cli-sri": "2.1.1",
60
60
  "ember-cli-terser": "4.0.2",
61
61
  "ember-load-initializers": "3.0.1",
62
- "ember-qunit": "9.0.4",
62
+ "ember-qunit": "9.1.0",
63
63
  "ember-resolver": "13.1.1",
64
64
  "ember-sinon-qunit": "7.5.0",
65
65
  "ember-source": "6.1.0",
@@ -67,19 +67,19 @@
67
67
  "ember-try": "4.0.0",
68
68
  "loader.js": "4.7.0",
69
69
  "miragejs": "0.1.48",
70
- "qunit": "2.25.0",
71
- "qunit-dom": "3.5.0",
72
- "sass": "1.97.3",
73
- "sinon": "21.0.1",
70
+ "qunit": "2.26.0",
71
+ "qunit-dom": "3.6.0",
72
+ "sass": "1.102.0",
73
+ "sinon": "22.1.0",
74
74
  "uikit": "3.25.6",
75
- "uuid": "13.0.0",
76
- "webpack": "5.104.1",
77
- "@projectcaluma/ember-testing": "14.13.0",
78
- "@projectcaluma/ember-workflow": "14.13.0"
75
+ "uuid": "14.0.1",
76
+ "webpack": "5.109.2",
77
+ "@projectcaluma/ember-workflow": "14.14.1",
78
+ "@projectcaluma/ember-testing": "14.14.1"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "ember-source": ">= 4.0.0",
82
- "@projectcaluma/ember-workflow": "^14.13.0"
82
+ "@projectcaluma/ember-workflow": "^14.14.1"
83
83
  },
84
84
  "dependenciesMeta": {
85
85
  "@projectcaluma/ember-core": {