mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-21 12:51:16 +08:00
fix(editor): Prevent Reka UI from interfering with Element Plus dropdown selections (#23370)
This commit is contained in:
@@ -7,6 +7,8 @@ import {
|
||||
PopoverRoot,
|
||||
type PopoverRootProps,
|
||||
PopoverTrigger,
|
||||
type PointerDownOutsideEvent,
|
||||
type FocusOutsideEvent,
|
||||
} from 'reka-ui';
|
||||
import { watch } from 'vue';
|
||||
import type { CSSProperties } from 'vue';
|
||||
@@ -89,6 +91,21 @@ function handleOpenAutoFocus(e: Event) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles outside interaction events to prevent Reka UI from interfering
|
||||
* with Element Plus dropdown selections. Element Plus teleports dropdowns
|
||||
* to the body, so Reka UI's DismissableLayer detects clicks on them as
|
||||
* "outside" clicks, which would otherwise prevent proper selection.
|
||||
*
|
||||
* TODO: This workaround can be removed once N8nSelect is migrated to Reka UI.
|
||||
*/
|
||||
function handleOutsideInteraction(e: PointerDownOutsideEvent | FocusOutsideEvent) {
|
||||
const target = e.target as HTMLElement | null;
|
||||
if (target?.closest('.el-popper, .el-select-dropdown')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// Watch open state to emit lifecycle events
|
||||
watch(
|
||||
() => props.open,
|
||||
@@ -118,6 +135,8 @@ watch(
|
||||
:style="{ width, zIndex }"
|
||||
:reference="reference"
|
||||
@open-auto-focus="handleOpenAutoFocus"
|
||||
@pointer-down-outside="handleOutsideInteraction"
|
||||
@interact-outside="handleOutsideInteraction"
|
||||
>
|
||||
<N8nScrollArea
|
||||
v-if="enableScrolling"
|
||||
|
||||
@@ -86,4 +86,26 @@ export class ExecutionsPage extends BasePage {
|
||||
await this.page.getByTestId('execution-preview-delete-button').click();
|
||||
await this.page.locator('button.btn--confirm').click();
|
||||
}
|
||||
|
||||
// Filter methods
|
||||
getFilterButton(): Locator {
|
||||
return this.page.getByTestId('executions-filter-button');
|
||||
}
|
||||
|
||||
getFilterForm(): Locator {
|
||||
return this.page.getByTestId('execution-filter-form');
|
||||
}
|
||||
|
||||
getStatusSelect(): Locator {
|
||||
return this.page.getByTestId('executions-filter-status-select');
|
||||
}
|
||||
|
||||
async openFilter(): Promise<void> {
|
||||
await this.getFilterButton().click();
|
||||
}
|
||||
|
||||
async selectStatus(status: string): Promise<void> {
|
||||
await this.getStatusSelect().click();
|
||||
await this.page.locator('.el-select-dropdown__item').filter({ hasText: status }).click();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,53 @@
|
||||
import { test, expect } from '../../../../fixtures/base';
|
||||
import executionOutOfMemoryResponse from '../../../../fixtures/execution-out-of-memory-server-response.json';
|
||||
|
||||
test.describe('Executions Filter', () => {
|
||||
test.beforeEach(async ({ n8n }) => {
|
||||
await n8n.start.fromImportedWorkflow('Test_workflow_4_executions_view.json');
|
||||
});
|
||||
|
||||
test('should keep popover open when selecting from dropdown inside it', async ({ n8n }) => {
|
||||
// Regression test: Element Plus dropdowns are teleported to body, causing
|
||||
// Reka UI's DismissableLayer to detect clicks as "outside" and close the popover.
|
||||
// This test verifies the popover stays open during and after dropdown selection.
|
||||
|
||||
// Create some executions first
|
||||
await n8n.executionsComposer.createExecutions(2);
|
||||
|
||||
// Go to executions tab
|
||||
await n8n.canvas.clickExecutionsTab();
|
||||
await expect(n8n.executions.getExecutionItems().first()).toBeVisible();
|
||||
|
||||
// Open filter popover
|
||||
await n8n.executions.openFilter();
|
||||
const filterForm = n8n.executions.getFilterForm();
|
||||
await expect(filterForm).toBeVisible();
|
||||
|
||||
// Click to open the status dropdown
|
||||
await n8n.executions.getStatusSelect().click();
|
||||
|
||||
// Verify popover is still open while dropdown is open
|
||||
await expect(filterForm).toBeVisible();
|
||||
|
||||
// Set up listener for the filtered executions request
|
||||
const filterRequestPromise = n8n.page.waitForRequest(
|
||||
(request) =>
|
||||
request.url().includes('/rest/executions?filter=') && request.url().includes('success'),
|
||||
);
|
||||
|
||||
// Select an option from the dropdown
|
||||
await n8n.page.locator('.el-select-dropdown__item').filter({ hasText: 'Success' }).click();
|
||||
|
||||
// Verify the filter request was sent to the backend (confirms selection worked)
|
||||
const filterRequest = await filterRequestPromise;
|
||||
expect(filterRequest.url()).toContain('status');
|
||||
expect(filterRequest.url()).toContain('success');
|
||||
|
||||
// KEY ASSERTION: Verify the popover did NOT close after selecting from dropdown
|
||||
await expect(filterForm).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
const ERROR_MESSAGES = {
|
||||
OUT_OF_MEMORY: 'Workflow did not finish, possible out-of-memory issue',
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user