@processmaker/screen-builder 2.84.1 → 2.84.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@processmaker/screen-builder",
3
- "version": "2.84.1",
3
+ "version": "2.84.3",
4
4
  "scripts": {
5
5
  "dev": "VITE_COVERAGE=true vite",
6
6
  "build": "vite build",
@@ -8,4 +8,7 @@
8
8
  }
9
9
  .page-dropdown-menu {
10
10
  min-width: 333px;
11
+ max-height: 26rem;
12
+ overflow-y: auto;
13
+ scrollbar-width: thin;
11
14
  }
@@ -10,10 +10,12 @@
10
10
  /* Override Bootstrap default tab styles */
11
11
  .nav-tabs {
12
12
  border-bottom: 1px solid var(--tabs-border) !important;
13
+ }
14
+ .nav-tabs-nowrap {
13
15
  flex-wrap: nowrap !important;
14
16
  overflow: hidden !important;
15
17
  }
16
-
18
+
17
19
  .nav-tabs .nav-item .nav-link {
18
20
  display: flex;
19
21
  align-items: center;
@@ -4,6 +4,7 @@
4
4
  v-model="activeTab"
5
5
  class="h-100 w-100 flat-tabs"
6
6
  content-class="h-tab"
7
+ nav-class="nav-tabs-nowrap"
7
8
  lazy
8
9
  @changed="tabsUpdated"
9
10
  @input="tabOpened"
@@ -196,9 +197,10 @@ export default {
196
197
  }, visualThreshold);
197
198
  });
198
199
  },
199
- closeTab(pageId) {
200
- this.localOpenedPages.splice(this.localOpenedPages.indexOf(pageId), 1);
201
- this.$emit("tab-closed", this.pages[pageId], this.localOpenedPages);
200
+ closeTab(tabIndex) {
201
+ const pageIndex = this.localOpenedPages[tabIndex];
202
+ this.localOpenedPages.splice(tabIndex, 1);
203
+ this.$emit("tab-closed", this.pages[pageIndex], this.localOpenedPages);
202
204
  },
203
205
  updateTabsReferences(pageDelete) {
204
206
  this.localOpenedPages = this.localOpenedPages.map((page) =>
@@ -24,15 +24,24 @@
24
24
  <div class="rounded sortable-item-name">
25
25
  <b-form-input
26
26
  v-if="editRowIndex === index"
27
- v-model="item.name"
27
+ v-model="newName"
28
28
  type="text"
29
29
  autofocus
30
- @blur.stop="onBlur()"
30
+ required
31
+ :state="validateState(newName, item)"
32
+ :error="validateError(newName, item)"
33
+ @blur.stop="onBlur(newName, item)"
34
+ @keydown.enter.stop="onBlur(newName, item)"
35
+ @keydown.esc.stop="onCancel(item)"
36
+ @focus="onFocus(item)"
31
37
  />
32
38
  <span v-else>{{ item.name }}</span>
33
39
  </div>
34
40
  <div class="border rounded-lg sortable-item-action">
35
- <button class="btn" @click.stop="onClick(item, index)">
41
+ <button v-if="editRowIndex === index" class="btn">
42
+ <i class="fas fa-check"></i>
43
+ </button>
44
+ <button v-else class="btn" @click.stop="onClick(item, index)">
36
45
  <i class="fas fa-edit"></i>
37
46
  </button>
38
47
  <div class="sortable-item-vr"></div>
@@ -55,6 +64,7 @@ export default {
55
64
  },
56
65
  data() {
57
66
  return {
67
+ newName: '',
58
68
  draggedItem: 0,
59
69
  draggedOverItem: 0,
60
70
  editRowIndex: null,
@@ -69,14 +79,46 @@ export default {
69
79
  }
70
80
  },
71
81
  methods: {
72
- onBlur() {
73
- this.editRowIndex = -1;
82
+ validateState(name, item) {
83
+ const isEmpty = !name?.trim();
84
+ const isDuplicated = this.items
85
+ .filter((i) => i !== item)
86
+ .find((i) => i.name === name);
87
+ return isEmpty || isDuplicated ? false : null;
74
88
  },
75
- onClick(item, index) {
76
- if (this.editRowIndex === -1 || this.editRowIndex === index) {
77
- this.editRowIndex = null;
78
- return;
89
+ validateError(name, item) {
90
+ const isEmpty = !name?.trim();
91
+ if (!isEmpty) {
92
+ return this.$t("The Page Name field is required.");
93
+ }
94
+ const isDuplicated = this.items
95
+ .filter((i) => i !== item)
96
+ .find((i) => i.name === name);
97
+ if (isDuplicated) {
98
+ return this.$t('Must be unique.');
99
+ }
100
+ return '';
101
+ },
102
+ onFocus(item) {
103
+ this.newName = item.name;
104
+ },
105
+ async onBlur(name, item) {
106
+ if (this.validateState(name, item) === false) {
107
+ this.newName = item.name;
108
+ } else {
109
+ // eslint-disable-next-line no-param-reassign
110
+ item.name = name;
79
111
  }
112
+ await this.$nextTick();
113
+ setTimeout(() => {
114
+ this.editRowIndex = null;
115
+ }, 250);
116
+ },
117
+ async onCancel(item) {
118
+ this.newName = item.name;
119
+ this.editRowIndex = null;
120
+ },
121
+ onClick(item, index) {
80
122
  this.editRowIndex = index;
81
123
  this.$emit("item-edit", item);
82
124
  },
@@ -127,6 +169,14 @@ export default {
127
169
 
128
170
  itemsSortedClone[draggedItemIndex].order = tempOrder;
129
171
  }
172
+
173
+ // Update order of the items
174
+ const clone = [...itemsSortedClone];
175
+ clone.sort((a, b) => a.order - b.order);
176
+ clone.forEach((item, index) => {
177
+ // eslint-disable-next-line no-param-reassign
178
+ item.order = index + 1;
179
+ });
130
180
  }
131
181
 
132
182
  this.$emit('ordered', itemsSortedClone);
@@ -881,6 +881,7 @@ export default {
881
881
  config.forEach((page) => this.replaceFormText(page.items));
882
882
  config.forEach((page) => this.migrateFormSubmit(page.items));
883
883
  config.forEach((page) => this.updateFieldNameValidation(page.items));
884
+ this.updatePageOrder(config);
884
885
  config.forEach((page) =>
885
886
  this.removeDataVariableFromNestedScreens(page.items)
886
887
  );
@@ -889,6 +890,18 @@ export default {
889
890
  page.order = page.order || index + 1;
890
891
  });
891
892
  },
893
+ updatePageOrder(pages) {
894
+ const clone = [...pages];
895
+ clone.sort((a, b) => {
896
+ const aOrder = a.order || pages.indexOf(a) + 1;
897
+ const bOrder = b.order || pages.indexOf(b) + 1;
898
+ return aOrder - bOrder;
899
+ });
900
+ clone.forEach((item, index) => {
901
+ // eslint-disable-next-line no-param-reassign
902
+ item.order = index + 1;
903
+ });
904
+ },
892
905
  updateFieldNameValidation(items) {
893
906
  items.forEach((item) => {
894
907
  if (item.inspector) {
@@ -1148,9 +1161,9 @@ export default {
1148
1161
  this.updateState();
1149
1162
  },
1150
1163
  addPage(e) {
1151
- this.showAddPageValidations = true;
1152
1164
  const error = this.checkPageName(this.addPageName, true);
1153
1165
  if (error) {
1166
+ this.showAddPageValidations = true;
1154
1167
  e.preventDefault();
1155
1168
  return;
1156
1169
  }
@@ -1227,6 +1240,7 @@ export default {
1227
1240
  globalObject.ProcessMaker.alert(error.message, "danger");
1228
1241
  return;
1229
1242
  }
1243
+ this.updatePageOrder(this.config);
1230
1244
  this.$store.dispatch("undoRedoModule/pushState", {
1231
1245
  config: JSON.stringify(this.config),
1232
1246
  currentPage: this.currentPage,
@@ -80,7 +80,6 @@ export const OpenPageUsingDropdown = {
80
80
  "[data-test=page-dropdown] button"
81
81
  );
82
82
  let selectorAddPage = canvasElement.querySelector("[data-test=page-Page3]");
83
- console.log(selectorAddPage);
84
83
  await selector.click(selector);
85
84
  await selectorAddPage.click(selectorAddPage);
86
85
  // Open Page 3 (index=2)
@@ -111,3 +110,37 @@ export const OpenPageUsingDropdown = {
111
110
  });
112
111
  }
113
112
  };
113
+
114
+ // Open a page using the PageDropdown(index)
115
+ export const ScrollWithMoreThanTenPages = {
116
+ args: {
117
+ pages: [
118
+ { name: "Page1" },
119
+ { name: "Page2" },
120
+ { name: "Page3" },
121
+ { name: "Page4" },
122
+ { name: "Page5" },
123
+ { name: "Page6" },
124
+ { name: "Page7" },
125
+ { name: "Page8" },
126
+ { name: "Page9" },
127
+ { name: "Page10" },
128
+ { name: "Page11" },
129
+ { name: "Page12" }
130
+ ],
131
+ initialOpenedPages: [0]
132
+ },
133
+ play: async ({ canvasElement }) => {
134
+ const selector = canvasElement.querySelector(
135
+ "[data-test=page-dropdown] button"
136
+ );
137
+ await selector.click(selector);
138
+ // Test .page-dropdown-menu has scroll (scrollHeight > clientHeight)
139
+ await waitFor(() => {
140
+ const dropdownMenu = canvasElement.querySelector(".page-dropdown-menu");
141
+ expect(dropdownMenu.scrollHeight).toBeGreaterThan(
142
+ dropdownMenu.clientHeight
143
+ );
144
+ });
145
+ }
146
+ };
@@ -336,3 +336,60 @@ export const WithoutAnyPageOpened = {
336
336
  );
337
337
  }
338
338
  };
339
+
340
+ // User can close tabs
341
+ export const UserCanCloseTabs = {
342
+ args: {
343
+ pages: [
344
+ { name: "Page 1" },
345
+ { name: "Page 2" },
346
+ { name: "Page 3" },
347
+ { name: "Page 4" },
348
+ { name: "Page 5" }
349
+ ],
350
+ initialOpenedPages: [0, 1, 2, 3, 4]
351
+ },
352
+ play: async ({ canvasElement, step }) => {
353
+ const canvas = within(canvasElement);
354
+
355
+ // Close Tab #1 = Page 1 (tab index=0)
356
+ await step("Close Page 1 (tab index=0)", async () => {
357
+ canvas.getByTestId("close-tab-0").click();
358
+ await waitFor(
359
+ () => {
360
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
361
+ "Here comes content of Page 2 (#1)"
362
+ );
363
+ },
364
+ { timeout: 1000 }
365
+ );
366
+ });
367
+
368
+ // Close Tab #1 = Page 2 (tab index=0)
369
+ await step("Close Page 2 (tab index=0)", async () => {
370
+ canvas.getByTestId("close-tab-0").click();
371
+ await waitFor(
372
+ () => {
373
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
374
+ "Here comes content of Page 3 (#2)"
375
+ );
376
+ },
377
+ { timeout: 1000 }
378
+ );
379
+ });
380
+
381
+ // Close Tab #2 = Page 4 (tab index=1)
382
+ await step("Close Page 4 (tab index=1)", async () => {
383
+ canvas.getByTestId("close-tab-1").click();
384
+ await waitFor(
385
+ () => {
386
+ // keep focus in the tab #1
387
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
388
+ "Here comes content of Page 3 (#2)"
389
+ );
390
+ },
391
+ { timeout: 1000 }
392
+ );
393
+ });
394
+ }
395
+ };