mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(editor): Fields reset after closing NDV when side panel NDV is also visible (no-changelog) (#18857)
This commit is contained in:
@@ -145,6 +145,7 @@ function onAskAssistantButtonClick() {
|
||||
icon="panel-right"
|
||||
:class="focusPanelActive ? $style.activeButton : ''"
|
||||
:active="focusPanelActive"
|
||||
data-test-id="toggle-focus-panel-button"
|
||||
@click="toggleFocusPanel"
|
||||
/>
|
||||
</KeyboardShortcutTooltip>
|
||||
|
||||
@@ -201,6 +201,7 @@ const dateTimePickerOptions = ref({
|
||||
],
|
||||
});
|
||||
const isFocused = ref(false);
|
||||
const isMapperShown = ref(false);
|
||||
|
||||
const contextNode = expressionLocalResolveCtx?.value?.workflow.getNode(
|
||||
expressionLocalResolveCtx.value.nodeName,
|
||||
@@ -611,11 +612,7 @@ const showDragnDropTip = computed(
|
||||
|
||||
const shouldCaptureForPosthog = computed(() => node.value?.type === AI_TRANSFORM_NODE_TYPE);
|
||||
|
||||
const shouldShowMapper = computed(
|
||||
() =>
|
||||
isFocused.value &&
|
||||
(isModelValueExpression.value || props.forceShowExpression || props.modelValue === ''),
|
||||
);
|
||||
const mapperElRef = computed(() => mapperRef.value?.contentRef);
|
||||
|
||||
function isRemoteParameterOption(option: INodePropertyOptions) {
|
||||
return remoteParameterOptionsKeys.value.includes(option.name);
|
||||
@@ -802,6 +799,10 @@ async function setFocus() {
|
||||
}
|
||||
|
||||
isFocused.value = true;
|
||||
|
||||
if (isModelValueExpression.value || props.forceShowExpression || props.modelValue === '') {
|
||||
isMapperShown.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
emit('focus');
|
||||
@@ -942,17 +943,19 @@ function expressionUpdated(value: string) {
|
||||
}
|
||||
|
||||
function onBlur(event?: FocusEvent | KeyboardEvent) {
|
||||
if (
|
||||
event?.target instanceof HTMLElement &&
|
||||
mapperRef.value?.contentRef &&
|
||||
(event.target === mapperRef.value.contentRef ||
|
||||
mapperRef.value.contentRef.contains(event.target))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit('blur');
|
||||
isFocused.value = false;
|
||||
|
||||
if (
|
||||
!(event?.target instanceof Node && mapperElRef.value?.contains(event.target)) &&
|
||||
!(
|
||||
event instanceof FocusEvent &&
|
||||
event.relatedTarget instanceof Node &&
|
||||
mapperElRef.value?.contains(event.relatedTarget)
|
||||
)
|
||||
) {
|
||||
isMapperShown.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onPaste(event: ClipboardEvent) {
|
||||
@@ -998,6 +1001,12 @@ function onUpdateTextInput(value: string) {
|
||||
onTextInputChange(value);
|
||||
}
|
||||
|
||||
function onClickOutsideMapper() {
|
||||
if (!isFocused.value) {
|
||||
isMapperShown.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function optionSelected(command: string) {
|
||||
const prevValue = props.modelValue;
|
||||
|
||||
@@ -1012,6 +1021,7 @@ async function optionSelected(command: string) {
|
||||
|
||||
case 'removeExpression':
|
||||
isFocused.value = false;
|
||||
isMapperShown.value = false;
|
||||
valueChanged(
|
||||
parseFromExpression(
|
||||
props.modelValue,
|
||||
@@ -1210,7 +1220,7 @@ onUpdated(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
onClickOutside(wrapper, onBlur);
|
||||
onClickOutside(mapperElRef, onClickOutsideMapper);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -1239,7 +1249,7 @@ onClickOutside(wrapper, onBlur);
|
||||
:workflow="expressionLocalResolveCtx?.workflow"
|
||||
:node="node"
|
||||
:input-node-name="expressionLocalResolveCtx?.inputNode?.name"
|
||||
:visible="shouldShowMapper"
|
||||
:visible="isMapperShown"
|
||||
:virtual-ref="wrapper"
|
||||
/>
|
||||
|
||||
|
||||
@@ -360,6 +360,10 @@ export class CanvasPage extends BasePage {
|
||||
return this.page.getByTestId('canvas-wrapper');
|
||||
}
|
||||
|
||||
toggleFocusPanelButton(): Locator {
|
||||
return this.page.getByTestId('toggle-focus-panel-button');
|
||||
}
|
||||
|
||||
// Actions
|
||||
|
||||
async addInitialNodeToCanvas(nodeName: string): Promise<void> {
|
||||
|
||||
@@ -20,7 +20,7 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
}
|
||||
|
||||
getParameterByLabel(labelName: string) {
|
||||
return this.page.locator('.parameter-item').filter({ hasText: labelName });
|
||||
return this.getContainer().locator('.parameter-item').filter({ hasText: labelName });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
import type { TestRequirements } from '../../Types';
|
||||
|
||||
const requirements: TestRequirements = {
|
||||
workflow: {
|
||||
'Test_workflow_1.json': 'Test',
|
||||
},
|
||||
storage: {
|
||||
N8N_EXPERIMENT_OVERRIDES: JSON.stringify({ ndv_in_focus_panel: 'variant' }),
|
||||
},
|
||||
};
|
||||
|
||||
test.describe('SUG-121 Fields reset after closing NDV', () => {
|
||||
test('should preserve changes to parameters after closing NDV when focus panel is open', async ({
|
||||
n8n,
|
||||
setupRequirements,
|
||||
}) => {
|
||||
await setupRequirements(requirements);
|
||||
await n8n.canvas.clickZoomToFitButton();
|
||||
await n8n.canvas.toggleFocusPanelButton().click();
|
||||
await n8n.canvas.canvasPane().click();
|
||||
await n8n.canvas.nodeByName('Code').dblclick();
|
||||
await n8n.ndv.getParameterByLabel('JavaScript').getByRole('textbox').fill('alert(1)');
|
||||
await n8n.ndv.close();
|
||||
await n8n.canvas.nodeByName('Code').dblclick();
|
||||
await expect(n8n.ndv.getParameterByLabel('JavaScript').getByRole('textbox')).toHaveText(
|
||||
'alert(1)',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -10,8 +10,18 @@ export async function setupTestRequirements(
|
||||
context: BrowserContext,
|
||||
requirements: TestRequirements,
|
||||
): Promise<void> {
|
||||
const n8n = new n8nPage(page);
|
||||
// 0. Setup browser storage before creating a new page
|
||||
if (requirements.storage) {
|
||||
await context.addInitScript((storage) => {
|
||||
// Set localStorage items
|
||||
for (const [key, value] of Object.entries(storage)) {
|
||||
window.localStorage.setItem(key, value);
|
||||
}
|
||||
}, requirements.storage);
|
||||
}
|
||||
|
||||
const api = new ApiHelpers(context.request);
|
||||
const n8n = new n8nPage(page, api);
|
||||
|
||||
// 1. Setup frontend settings override
|
||||
if (requirements.config?.settings) {
|
||||
@@ -57,14 +67,4 @@ export async function setupTestRequirements(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Setup browser storage
|
||||
if (requirements.storage) {
|
||||
await context.addInitScript((storage) => {
|
||||
// Set localStorage items
|
||||
for (const [key, value] of Object.entries(storage)) {
|
||||
window.localStorage.setItem(key, value);
|
||||
}
|
||||
}, requirements.storage);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user