diff --git a/sonar-project.properties b/sonar-project.properties
index 37fec5778..34446f8a6 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -3,3 +3,5 @@ sonar.javascript.lcov.reportPaths=./coverage/lcov.info
sonar.exclusions=node_modules/**, dist/**, public/**, home/**, storybook-static/**
sonar.sources=src
sonar.tests=tests
+sonar.cpd.exclusions=src/stories/**
+sonar.coverage.exclusions=src/stories/**
diff --git a/src/assets/css/custom.css b/src/assets/css/custom.css
index 38ec225ce..09fecf462 100644
--- a/src/assets/css/custom.css
+++ b/src/assets/css/custom.css
@@ -8,4 +8,7 @@
}
.page-dropdown-menu {
min-width: 333px;
+ max-height: 26rem;
+ overflow-y: auto;
+ scrollbar-width: thin;
}
\ No newline at end of file
diff --git a/src/components/TabsBar.vue b/src/components/TabsBar.vue
index bfbd9cd51..ce4359648 100644
--- a/src/components/TabsBar.vue
+++ b/src/components/TabsBar.vue
@@ -196,9 +196,10 @@ export default {
}, visualThreshold);
});
},
- closeTab(pageId) {
- this.localOpenedPages.splice(this.localOpenedPages.indexOf(pageId), 1);
- this.$emit("tab-closed", this.pages[pageId], this.localOpenedPages);
+ closeTab(tabIndex) {
+ const pageIndex = this.localOpenedPages[tabIndex];
+ this.localOpenedPages.splice(tabIndex, 1);
+ this.$emit("tab-closed", this.pages[pageIndex], this.localOpenedPages);
},
updateTabsReferences(pageDelete) {
this.localOpenedPages = this.localOpenedPages.map((page) =>
diff --git a/src/components/sortable/sortableList/SortableList.vue b/src/components/sortable/sortableList/SortableList.vue
index e712ab005..18e044df7 100644
--- a/src/components/sortable/sortableList/SortableList.vue
+++ b/src/components/sortable/sortableList/SortableList.vue
@@ -24,15 +24,16 @@
{{ item.name }}
@@ -63,7 +64,7 @@ export default {
},
data() {
return {
- originalName: '',
+ newName: '',
draggedItem: 0,
draggedOverItem: 0,
editRowIndex: null,
@@ -79,14 +80,14 @@ export default {
},
methods: {
validateState(name, item) {
- const isEmpty = !name;
+ const isEmpty = !name?.trim();
const isDuplicated = this.items
.filter((i) => i !== item)
.find((i) => i.name === name);
return isEmpty || isDuplicated ? false : null;
},
validateError(name, item) {
- const isEmpty = !name;
+ const isEmpty = !name?.trim();
if (!isEmpty) {
return this.$t("The Page Name field is required.");
}
@@ -98,19 +99,25 @@ export default {
}
return '';
},
- onFocus(name, item) {
- this.originalName = name;
+ onFocus(item) {
+ this.newName = item.name;
},
async onBlur(name, item) {
if (this.validateState(name, item) === false) {
+ this.newName = item.name;
+ } else {
// eslint-disable-next-line no-param-reassign
- item.name = this.originalName;
+ item.name = name;
}
await this.$nextTick();
setTimeout(() => {
this.editRowIndex = null;
}, 250);
},
+ async onCancel(item) {
+ this.newName = item.name;
+ this.editRowIndex = null;
+ },
onClick(item, index) {
this.editRowIndex = index;
this.$emit("item-edit", item);
@@ -162,6 +169,14 @@ export default {
itemsSortedClone[draggedItemIndex].order = tempOrder;
}
+
+ // Update order of the items
+ const clone = [...itemsSortedClone];
+ clone.sort((a, b) => a.order - b.order);
+ clone.forEach((item, index) => {
+ // eslint-disable-next-line no-param-reassign
+ item.order = index + 1;
+ });
}
this.$emit('ordered', itemsSortedClone);
diff --git a/src/components/vue-form-builder.vue b/src/components/vue-form-builder.vue
index 964efb35c..d8926f559 100644
--- a/src/components/vue-form-builder.vue
+++ b/src/components/vue-form-builder.vue
@@ -881,6 +881,7 @@ export default {
config.forEach((page) => this.replaceFormText(page.items));
config.forEach((page) => this.migrateFormSubmit(page.items));
config.forEach((page) => this.updateFieldNameValidation(page.items));
+ this.updatePageOrder(config);
config.forEach((page) =>
this.removeDataVariableFromNestedScreens(page.items)
);
@@ -889,6 +890,18 @@ export default {
page.order = page.order || index + 1;
});
},
+ updatePageOrder(pages) {
+ const clone = [...pages];
+ clone.sort((a, b) => {
+ const aOrder = a.order || pages.indexOf(a) + 1;
+ const bOrder = b.order || pages.indexOf(b) + 1;
+ return aOrder - bOrder;
+ });
+ clone.forEach((item, index) => {
+ // eslint-disable-next-line no-param-reassign
+ item.order = index + 1;
+ });
+ },
updateFieldNameValidation(items) {
items.forEach((item) => {
if (item.inspector) {
@@ -1148,9 +1161,9 @@ export default {
this.updateState();
},
addPage(e) {
- this.showAddPageValidations = true;
const error = this.checkPageName(this.addPageName, true);
if (error) {
+ this.showAddPageValidations = true;
e.preventDefault();
return;
}
@@ -1227,6 +1240,7 @@ export default {
globalObject.ProcessMaker.alert(error.message, "danger");
return;
}
+ this.updatePageOrder(this.config);
this.$store.dispatch("undoRedoModule/pushState", {
config: JSON.stringify(this.config),
currentPage: this.currentPage,
diff --git a/src/stories/DropdownAndPages.stories.js b/src/stories/DropdownAndPages.stories.js
index 5db2901b6..8de866df5 100644
--- a/src/stories/DropdownAndPages.stories.js
+++ b/src/stories/DropdownAndPages.stories.js
@@ -80,7 +80,6 @@ export const OpenPageUsingDropdown = {
"[data-test=page-dropdown] button"
);
let selectorAddPage = canvasElement.querySelector("[data-test=page-Page3]");
- console.log(selectorAddPage);
await selector.click(selector);
await selectorAddPage.click(selectorAddPage);
// Open Page 3 (index=2)
@@ -111,3 +110,37 @@ export const OpenPageUsingDropdown = {
});
}
};
+
+// Open a page using the PageDropdown(index)
+export const ScrollWithMoreThanTenPages = {
+ args: {
+ pages: [
+ { name: "Page1" },
+ { name: "Page2" },
+ { name: "Page3" },
+ { name: "Page4" },
+ { name: "Page5" },
+ { name: "Page6" },
+ { name: "Page7" },
+ { name: "Page8" },
+ { name: "Page9" },
+ { name: "Page10" },
+ { name: "Page11" },
+ { name: "Page12" }
+ ],
+ initialOpenedPages: [0]
+ },
+ play: async ({ canvasElement }) => {
+ const selector = canvasElement.querySelector(
+ "[data-test=page-dropdown] button"
+ );
+ await selector.click(selector);
+ // Test .page-dropdown-menu has scroll (scrollHeight > clientHeight)
+ await waitFor(() => {
+ const dropdownMenu = canvasElement.querySelector(".page-dropdown-menu");
+ expect(dropdownMenu.scrollHeight).toBeGreaterThan(
+ dropdownMenu.clientHeight
+ );
+ });
+ }
+};
diff --git a/src/stories/PageTabs.stories.js b/src/stories/PageTabs.stories.js
index 49c9b2449..15833dd82 100644
--- a/src/stories/PageTabs.stories.js
+++ b/src/stories/PageTabs.stories.js
@@ -336,3 +336,60 @@ export const WithoutAnyPageOpened = {
);
}
};
+
+// User can close tabs
+export const UserCanCloseTabs = {
+ args: {
+ pages: [
+ { name: "Page 1" },
+ { name: "Page 2" },
+ { name: "Page 3" },
+ { name: "Page 4" },
+ { name: "Page 5" }
+ ],
+ initialOpenedPages: [0, 1, 2, 3, 4]
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement);
+
+ // Close Tab #1 = Page 1 (tab index=0)
+ await step("Close Page 1 (tab index=0)", async () => {
+ canvas.getByTestId("close-tab-0").click();
+ await waitFor(
+ () => {
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
+ "Here comes content of Page 2 (#1)"
+ );
+ },
+ { timeout: 1000 }
+ );
+ });
+
+ // Close Tab #1 = Page 2 (tab index=0)
+ await step("Close Page 2 (tab index=0)", async () => {
+ canvas.getByTestId("close-tab-0").click();
+ await waitFor(
+ () => {
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
+ "Here comes content of Page 3 (#2)"
+ );
+ },
+ { timeout: 1000 }
+ );
+ });
+
+ // Close Tab #2 = Page 4 (tab index=1)
+ await step("Close Page 4 (tab index=1)", async () => {
+ canvas.getByTestId("close-tab-1").click();
+ await waitFor(
+ () => {
+ // keep focus in the tab #1
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
+ "Here comes content of Page 3 (#2)"
+ );
+ },
+ { timeout: 1000 }
+ );
+ });
+ }
+};