From 6a400732ae2c903750ec016faa3db8706d1ecdfb Mon Sep 17 00:00:00 2001 From: Suguru Inoue Date: Fri, 29 Aug 2025 16:42:59 +0200 Subject: [PATCH] fix(editor): Fix condition for opening the rename node prompt (no-changelog) (#18962) --- .../InlineExpressionEditorOutput.vue | 2 +- .../ExperimentalEmbeddedNdvMapper.vue | 2 +- .../src/composables/useKeybindings.ts | 14 ++----- .../editor-ui/src/utils/canvasUtils.test.ts | 42 +++++++++++++++++++ .../editor-ui/src/utils/canvasUtils.ts | 8 ++++ .../frontend/editor-ui/src/views/NodeView.vue | 7 +++- 6 files changed, 61 insertions(+), 14 deletions(-) diff --git a/packages/frontend/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue b/packages/frontend/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue index 9fd03f997a..6d6a501619 100644 --- a/packages/frontend/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue +++ b/packages/frontend/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue @@ -55,7 +55,7 @@ defineExpose({ :virtual-triggering="virtualRef !== undefined" :virtual-ref="virtualRef" :width="virtualRefSize.width.value" - :popper-class="$style.popper" + :popper-class="`${$style.popper} ignore-key-press-canvas`" :popper-options="{ modifiers: [ { name: 'flip', enabled: false }, diff --git a/packages/frontend/editor-ui/src/components/canvas/experimental/components/ExperimentalEmbeddedNdvMapper.vue b/packages/frontend/editor-ui/src/components/canvas/experimental/components/ExperimentalEmbeddedNdvMapper.vue index 918eb280dd..c3b29bc279 100644 --- a/packages/frontend/editor-ui/src/components/canvas/experimental/components/ExperimentalEmbeddedNdvMapper.vue +++ b/packages/frontend/editor-ui/src/components/canvas/experimental/components/ExperimentalEmbeddedNdvMapper.vue @@ -38,7 +38,7 @@ defineExpose({ :visible="isVisible" placement="left" :show-arrow="false" - :popper-class="$style.component" + :popper-class="`${$style.component} ignore-key-press-canvas`" :width="360" :offset="8" append-to="body" diff --git a/packages/frontend/editor-ui/src/composables/useKeybindings.ts b/packages/frontend/editor-ui/src/composables/useKeybindings.ts index 57a3a4c2d6..373b2a2110 100644 --- a/packages/frontend/editor-ui/src/composables/useKeybindings.ts +++ b/packages/frontend/editor-ui/src/composables/useKeybindings.ts @@ -1,4 +1,5 @@ import { PopOutWindowKey } from '@/constants'; +import { shouldIgnoreCanvasShortcut } from '@/utils/canvasUtils'; import { useDeviceSupport } from '@n8n/composables/useDeviceSupport'; import { useActiveElement, useEventListener } from '@vueuse/core'; import type { MaybeRefOrGetter } from 'vue'; @@ -36,16 +37,9 @@ export const useKeybindings = ( const isDisabled = computed(() => toValue(options?.disabled)); - const ignoreKeyPresses = computed(() => { - if (!activeElement.value) return false; - - const active = activeElement.value; - const isInput = ['INPUT', 'TEXTAREA'].includes(active.tagName); - const isContentEditable = active.closest('[contenteditable]') !== null; - const isIgnoreClass = active.closest('.ignore-key-press-canvas') !== null; - - return isInput || isContentEditable || isIgnoreClass; - }); + const ignoreKeyPresses = computed( + () => activeElement.value && shouldIgnoreCanvasShortcut(activeElement.value), + ); const normalizedKeymap = computed(() => Object.fromEntries( diff --git a/packages/frontend/editor-ui/src/utils/canvasUtils.test.ts b/packages/frontend/editor-ui/src/utils/canvasUtils.test.ts index 77d4d6001d..241bd9f462 100644 --- a/packages/frontend/editor-ui/src/utils/canvasUtils.test.ts +++ b/packages/frontend/editor-ui/src/utils/canvasUtils.test.ts @@ -7,6 +7,7 @@ import { mapLegacyConnectionsToCanvasConnections, mapLegacyEndpointsToCanvasConnectionPort, parseCanvasConnectionHandleString, + shouldIgnoreCanvasShortcut, } from '@/utils/canvasUtils'; import type { IConnection, IConnections, INodeTypeDescription } from 'n8n-workflow'; import { NodeConnectionTypes } from 'n8n-workflow'; @@ -1051,3 +1052,44 @@ describe('insertSpacersBetweenEndpoints', () => { expect(result).toEqual([{ index: 0, required: true }, null, null, null]); }); }); + +describe(shouldIgnoreCanvasShortcut, () => { + it('should return false if given element is a div element', () => { + expect(shouldIgnoreCanvasShortcut(document.createElement('div'))).toEqual(false); + }); + + it('should return true if given element is an input element', () => { + expect(shouldIgnoreCanvasShortcut(document.createElement('input'))).toEqual(true); + }); + + it('should return true if given element is a textarea element', () => { + expect(shouldIgnoreCanvasShortcut(document.createElement('textarea'))).toEqual(true); + }); + + it('should return true if given element is an element with contenteditable attribute', () => { + const div = document.createElement('div'); + + div.setAttribute('contenteditable', 'true'); + + expect(shouldIgnoreCanvasShortcut(div)).toEqual(true); + }); + + it('should return true if given element is a child of an element with contenteditable attribute', () => { + const parent = document.createElement('div'); + const child = document.createElement('div'); + + parent.appendChild(child); + + parent.setAttribute('contenteditable', 'true'); + + expect(shouldIgnoreCanvasShortcut(child)).toEqual(true); + }); + + it('should return true if given element is has class "ignore-key-press-canvas"', () => { + const div = document.createElement('div'); + + div.classList.add('ignore-key-press-canvas'); + + expect(shouldIgnoreCanvasShortcut(div)).toEqual(true); + }); +}); diff --git a/packages/frontend/editor-ui/src/utils/canvasUtils.ts b/packages/frontend/editor-ui/src/utils/canvasUtils.ts index ee8016cf28..1549a54ada 100644 --- a/packages/frontend/editor-ui/src/utils/canvasUtils.ts +++ b/packages/frontend/editor-ui/src/utils/canvasUtils.ts @@ -263,3 +263,11 @@ export function insertSpacersBetweenEndpoints(endpoints: T[], requiredEndpoin return endpointsWithSpacers; } + +export function shouldIgnoreCanvasShortcut(el: Element): boolean { + return ( + ['INPUT', 'TEXTAREA'].includes(el.tagName) || + el.closest('[contenteditable]') !== null || + el.closest('.ignore-key-press-canvas') !== null + ); +} diff --git a/packages/frontend/editor-ui/src/views/NodeView.vue b/packages/frontend/editor-ui/src/views/NodeView.vue index b55b2f82d9..d18fb3543d 100644 --- a/packages/frontend/editor-ui/src/views/NodeView.vue +++ b/packages/frontend/editor-ui/src/views/NodeView.vue @@ -122,7 +122,10 @@ import { useClipboard } from '@/composables/useClipboard'; import { useBeforeUnload } from '@/composables/useBeforeUnload'; import { getResourcePermissions } from '@n8n/permissions'; import NodeViewUnfinishedWorkflowMessage from '@/components/NodeViewUnfinishedWorkflowMessage.vue'; -import { createCanvasConnectionHandleString } from '@/utils/canvasUtils'; +import { + createCanvasConnectionHandleString, + shouldIgnoreCanvasShortcut, +} from '@/utils/canvasUtils'; import { isValidNodeConnectionType } from '@/utils/typeGuards'; import { getSampleWorkflowByTemplateId } from '@/utils/templates/workflowSamples'; import type { CanvasLayoutEvent } from '@/composables/useCanvasLayout'; @@ -917,7 +920,7 @@ async function onOpenRenameNodeModal(id: string) { const activeElement = document.activeElement; - if (activeElement && activeElement.tagName === 'INPUT') { + if (activeElement && shouldIgnoreCanvasShortcut(activeElement)) { // If an input is focused, do not open the rename modal return; }