mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Prevent pagination setting from being overwritten in URL (#13266)
This commit is contained in:
parent
8e15ebf833
commit
d1e65a1cd5
|
@ -97,4 +97,45 @@ describe('Workflows', () => {
|
|||
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should preserve filters and pagination in URL', () => {
|
||||
// Add a search query
|
||||
WorkflowsPage.getters.searchBar().type('My');
|
||||
// Add a tag filter
|
||||
WorkflowsPage.getters.workflowFilterButton().click();
|
||||
WorkflowsPage.getters.workflowTagsDropdown().click();
|
||||
WorkflowsPage.getters.workflowTagItem('other-tag-1').click();
|
||||
WorkflowsPage.getters.workflowsListContainer().click();
|
||||
// Update sort order
|
||||
WorkflowsPage.getters.workflowSortDropdown().click();
|
||||
WorkflowsPage.getters.workflowSortItem('Sort by last created').click({ force: true });
|
||||
// Update page size
|
||||
WorkflowsPage.getters.workflowListPageSizeDropdown().click();
|
||||
WorkflowsPage.getters.workflowListPageSizeItem('25').click();
|
||||
|
||||
// URL should contain all applied filters and pagination
|
||||
cy.url().should('include', 'search=My');
|
||||
// Cannot really know tag id, so just check if it contains 'tags='
|
||||
cy.url().should('include', 'tags=');
|
||||
cy.url().should('include', 'sort=lastCreated');
|
||||
cy.url().should('include', 'pageSize=25');
|
||||
|
||||
// Reload the page
|
||||
cy.reload();
|
||||
// Check if filters and pagination are preserved
|
||||
WorkflowsPage.getters.searchBar().should('have.value', 'My');
|
||||
WorkflowsPage.getters.workflowFilterButton().click();
|
||||
WorkflowsPage.getters.workflowTagsDropdown().should('contain.text', 'other-tag-1');
|
||||
WorkflowsPage.getters
|
||||
.workflowSortItem('Sort by last created')
|
||||
.should('have.attr', 'aria-selected', 'true');
|
||||
WorkflowsPage.getters
|
||||
.workflowListPageSizeItem('25', false)
|
||||
.should('have.attr', 'aria-selected', 'true');
|
||||
// Aso, check if the URL is preserved
|
||||
cy.url().should('include', 'search=My');
|
||||
cy.url().should('include', 'tags=');
|
||||
cy.url().should('include', 'sort=lastCreated');
|
||||
cy.url().should('include', 'pageSize=25');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -47,6 +47,18 @@ export class WorkflowsPage extends BasePage {
|
|||
workflowOwnershipDropdown: () => cy.getByTestId('user-select-trigger'),
|
||||
workflowOwner: (email: string) => cy.getByTestId('user-email').contains(email),
|
||||
workflowResetFilters: () => cy.getByTestId('workflows-filter-reset'),
|
||||
workflowSortDropdown: () => cy.getByTestId('resources-list-sort'),
|
||||
workflowSortItem: (sort: string) =>
|
||||
cy.getByTestId('resources-list-sort-item').contains(sort).parent(),
|
||||
workflowPagination: () => cy.getByTestId('resources-list-pagination'),
|
||||
workflowListPageSizeDropdown: () => this.getters.workflowPagination().find('.select-trigger'),
|
||||
workflowListPageSizeItem: (pageSize: string, visible: boolean = true) => {
|
||||
if (visible) {
|
||||
return cy.get('[role=option]').filter(':visible').contains(`${pageSize}/page`);
|
||||
}
|
||||
return cy.get('[role=option]').contains(`${pageSize}/page`).parent();
|
||||
},
|
||||
workflowsListContainer: () => cy.getByTestId('resources-list-wrapper'),
|
||||
// Not yet implemented
|
||||
// myWorkflows: () => cy.getByTestId('my-workflows'),
|
||||
// allWorkflows: () => cy.getByTestId('all-workflows'),
|
||||
|
|
|
@ -557,6 +557,7 @@ const loadPaginationFromQueryString = async () => {
|
|||
<div
|
||||
v-else-if="filteredAndSortedResources.length > 0"
|
||||
ref="listWrapperRef"
|
||||
data-test-id="resources-list-wrapper"
|
||||
:class="$style.listWrapper"
|
||||
>
|
||||
<!-- FULL SCROLLING LIST (Shows all resources, filtering and sorting is done in this component) -->
|
||||
|
@ -588,6 +589,7 @@ const loadPaginationFromQueryString = async () => {
|
|||
:total="totalItems"
|
||||
:page-sizes="availablePageSizeOptions"
|
||||
layout="total, prev, pager, next, sizes"
|
||||
data-test-id="resources-list-pagination"
|
||||
@update:current-page="setCurrentPage"
|
||||
@size-change="setRowsPerPage"
|
||||
></el-pagination>
|
||||
|
|
|
@ -185,12 +185,11 @@ const onFiltersUpdated = async () => {
|
|||
};
|
||||
|
||||
const onSearchUpdated = async (search: string) => {
|
||||
if (search) {
|
||||
currentPage.value = 1;
|
||||
saveFiltersOnQueryString();
|
||||
if (search) {
|
||||
await callDebounced(fetchWorkflows, { debounceTime: 500, trailing: true });
|
||||
} else {
|
||||
currentPage.value = 1;
|
||||
// No need to debounce when clearing search
|
||||
await fetchWorkflows();
|
||||
}
|
||||
|
@ -306,46 +305,67 @@ function isValidProjectId(projectId: string) {
|
|||
}
|
||||
|
||||
const setFiltersFromQueryString = async () => {
|
||||
const newQuery: LocationQueryRaw = { ...route.query };
|
||||
const { tags, status, search, homeProject, sort } = route.query ?? {};
|
||||
const newQuery: LocationQueryRaw = {};
|
||||
|
||||
if (homeProject && typeof homeProject === 'string') {
|
||||
// Helper to check if string value is not empty
|
||||
const isValidString = (value: unknown): value is string =>
|
||||
typeof value === 'string' && value.trim().length > 0;
|
||||
|
||||
// Handle home project
|
||||
if (isValidString(homeProject)) {
|
||||
await projectsStore.getAvailableProjects();
|
||||
if (isValidProjectId(homeProject)) {
|
||||
newQuery.homeProject = homeProject;
|
||||
filters.value.homeProject = homeProject;
|
||||
} else {
|
||||
delete newQuery.homeProject;
|
||||
}
|
||||
} else {
|
||||
delete newQuery.homeProject;
|
||||
}
|
||||
|
||||
if (search && typeof search === 'string') {
|
||||
// Handle search
|
||||
if (isValidString(search)) {
|
||||
newQuery.search = search;
|
||||
filters.value.search = search;
|
||||
} else {
|
||||
delete newQuery.search;
|
||||
}
|
||||
|
||||
if (tags && typeof tags === 'string') {
|
||||
// Handle tags
|
||||
if (isValidString(tags)) {
|
||||
await tagsStore.fetchAll();
|
||||
const currentTags = tagsStore.allTags.map((tag) => tag.id);
|
||||
const validTags = tags.split(',').filter((tag) => currentTags.includes(tag));
|
||||
const validTags = tags
|
||||
.split(',')
|
||||
.filter((tag) => tagsStore.allTags.map((t) => t.id).includes(tag));
|
||||
|
||||
if (validTags.length) {
|
||||
newQuery.tags = validTags.join(',');
|
||||
filters.value.tags = validTags;
|
||||
} else {
|
||||
delete newQuery.tags;
|
||||
}
|
||||
} else {
|
||||
delete newQuery.tags;
|
||||
}
|
||||
|
||||
if (
|
||||
status &&
|
||||
typeof status === 'string' &&
|
||||
[StatusFilter.ACTIVE.toString(), StatusFilter.DEACTIVATED.toString()].includes(status)
|
||||
) {
|
||||
newQuery.status = status; // Keep as string in URL
|
||||
filters.value.status = status === 'true'; // Convert to boolean for filters
|
||||
// Handle status
|
||||
const validStatusValues = [StatusFilter.ACTIVE.toString(), StatusFilter.DEACTIVATED.toString()];
|
||||
if (isValidString(status) && validStatusValues.includes(status)) {
|
||||
newQuery.status = status;
|
||||
filters.value.status = status === 'true';
|
||||
} else {
|
||||
delete newQuery.status;
|
||||
}
|
||||
|
||||
if (sort && typeof sort === 'string') {
|
||||
// Handle sort
|
||||
if (isValidString(sort)) {
|
||||
const newSort = WORKFLOWS_SORT_MAP[sort as keyof typeof WORKFLOWS_SORT_MAP] ?? 'updatedAt:desc';
|
||||
newQuery.sort = sort;
|
||||
currentSort.value = newSort;
|
||||
} else {
|
||||
delete newQuery.sort;
|
||||
}
|
||||
|
||||
void router.replace({ query: newQuery });
|
||||
|
|
Loading…
Reference in a new issue