fix(editor): Keep pending navigation when workflows list query is unchanged (#41125)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
João Almeida 2026-10-16 09:04:20 +00:00 committed by GitHub
parent b36f6c0baf
commit 116a81e06e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 36 additions and 1 deletions

View File

@ -430,6 +430,33 @@ describe('WorkflowsView', () => {
await waitFor(() => expect(router.currentRoute.value.query).toStrictEqual({}));
});
it('should not replace the route when the query does not change', async () => {
await router.replace({ query: { search: 'one' } });
const replaceSpy = vi.spyOn(router, 'replace');
renderComponent({ pinia });
await waitAllPromises();
expect(replaceSpy).not.toHaveBeenCalled();
replaceSpy.mockRestore();
});
it('should not replace the route when the route changes while tags load', async () => {
await router.replace({ query: { tags: 'non-existing-tag' } });
workflowsListStore.fetchWorkflowsPage.mockResolvedValue([]);
const tagStore = mockedStore(useTagsStore);
tagStore.fetchAll.mockImplementation(async () => {
await router.push({ query: { search: 'new' } });
return [];
});
const replaceSpy = vi.spyOn(router, 'replace');
renderComponent({ pinia });
await waitAllPromises();
expect(replaceSpy).not.toHaveBeenCalled();
expect(router.currentRoute.value.query).toStrictEqual({ search: 'new' });
replaceSpy.mockRestore();
});
it('should show archived only hint', async () => {
foldersStore.totalWorkflowCount = 1;
workflowsListStore.fetchWorkflowsPage.mockResolvedValue([]);

View File

@ -96,6 +96,7 @@ import { useI18n } from '@n8n/i18n';
import { getResourcePermissions } from '@n8n/permissions';
import { createEventBus } from '@n8n/utils/event-bus';
import debounce from 'lodash/debounce';
import isEqual from 'lodash/isEqual';
import { type IUser, PROJECT_ROOT } from 'n8n-workflow';
import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
import { type LocationQueryRaw, useRoute, useRouter } from 'vue-router';
@ -1069,6 +1070,7 @@ const saveFiltersOnQueryString = () => {
};
const setFiltersFromQueryString = async () => {
const { fullPath } = route;
const newQuery: LocationQueryRaw = { ...route.query };
const { tags, status, search, homeProject, sort, showArchived } = route.query ?? {};
@ -1139,7 +1141,13 @@ const setFiltersFromQueryString = async () => {
filters.value.showArchived = false;
}
void router.replace({ query: newQuery });
// The route changed while the stores loaded. Do not write this query onto the new route.
if (route.fullPath !== fullPath) return;
// A replace cancels any pending navigation, e.g. a tab click made while this view loads.
if (!isEqual(newQuery, route.query)) {
void router.replace({ query: newQuery });
}
};
// Misc methods