From ded2e71d41e31ed7a1347f57c0cdddd2bd535d26 Mon Sep 17 00:00:00 2001 From: Suguru Inoue Date: Tue, 15 Jul 2025 12:10:53 +0200 Subject: [PATCH] feat(editor): Show input panel for mapping in embedded NDV (no-changelog) (#17227) --- .../src/components/InputPanel.test.ts | 11 +- .../editor-ui/src/components/InputPanel.vue | 32 +-- .../src/components/NodeDetailsView.vue | 3 + .../src/components/NodeDetailsViewV2.vue | 3 + .../editor-ui/src/components/NodeSettings.vue | 2 +- .../editor-ui/src/components/RunData.vue | 3 + .../__snapshots__/InputPanel.test.ts.snap | 4 +- .../ExperimentalEmbeddedNodeDetails.vue | 206 +++++++++++++----- 8 files changed, 185 insertions(+), 79 deletions(-) diff --git a/packages/frontend/editor-ui/src/components/InputPanel.test.ts b/packages/frontend/editor-ui/src/components/InputPanel.test.ts index 6d75bccb55..a409192952 100644 --- a/packages/frontend/editor-ui/src/components/InputPanel.test.ts +++ b/packages/frontend/editor-ui/src/components/InputPanel.test.ts @@ -1,7 +1,6 @@ import { createTestNode, createTestWorkflow, createTestWorkflowObject } from '@/__tests__/mocks'; import { createComponentRenderer } from '@/__tests__/render'; import InputPanel, { type Props } from '@/components/InputPanel.vue'; -import { STORES } from '@n8n/stores'; import { useWorkflowsStore } from '@/stores/workflows.store'; import { createTestingPinia } from '@pinia/testing'; import { waitFor } from '@testing-library/vue'; @@ -50,7 +49,6 @@ const render = (props: Partial = {}, pinData?: INodeExecutionData[], runD const pinia = createTestingPinia({ stubActions: false, - initialState: { [STORES.NDV]: { activeNodeName: props.currentNodeName ?? nodes[1].name } }, }); setActivePinia(pinia); @@ -97,9 +95,12 @@ const render = (props: Partial = {}, pinData?: INodeExecutionData[], runD props: { pushRef: 'pushRef', runIndex: 0, - currentNodeName: nodes[1].name, + currentNodeName: nodes[0].name, + activeNodeName: nodes[1].name, workflow: workflowObject, displayMode: 'schema', + focusedMappableInput: '', + isMappingOnboarded: false, }, global: { stubs: { @@ -118,14 +119,14 @@ describe('InputPanel', () => { }); it("opens mapping tab by default if the node hasn't run yet", async () => { - const { findByTestId } = render({ currentNodeName: 'Tool' }); + const { findByTestId } = render({ activeNodeName: 'Tool' }); expect((await findByTestId('radio-button-mapping')).parentNode).toBeChecked(); expect((await findByTestId('radio-button-debugging')).parentNode).not.toBeChecked(); }); it('opens debugging tab by default if the node has already run', async () => { - const { findByTestId } = render({ currentNodeName: 'Tool' }, undefined, { + const { findByTestId } = render({ activeNodeName: 'Tool' }, undefined, { Tool: [ { startTime: 0, diff --git a/packages/frontend/editor-ui/src/components/InputPanel.vue b/packages/frontend/editor-ui/src/components/InputPanel.vue index cab319c30c..519d758bdb 100644 --- a/packages/frontend/editor-ui/src/components/InputPanel.vue +++ b/packages/frontend/editor-ui/src/components/InputPanel.vue @@ -10,7 +10,6 @@ import { } from '@/constants'; import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { useWorkflowsStore } from '@/stores/workflows.store'; -import { useNDVStore } from '@/stores/ndv.store'; import { waitingNodeTooltip } from '@/utils/executionUtils'; import uniqBy from 'lodash/uniqBy'; import { N8nIcon, N8nRadioButtons, N8nText, N8nTooltip } from '@n8n/design-system'; @@ -22,7 +21,6 @@ import { NodeConnectionTypes, NodeHelpers, } from 'n8n-workflow'; -import { storeToRefs } from 'pinia'; import { computed, ref, watch } from 'vue'; import InputNodeSelect from './InputNodeSelect.vue'; import NodeExecuteButton from './NodeExecuteButton.vue'; @@ -38,6 +36,7 @@ export type Props = { runIndex: number; workflow: Workflow; pushRef: string; + activeNodeName: string; currentNodeName?: string; canLinkRuns?: boolean; linkedRuns?: boolean; @@ -45,6 +44,10 @@ export type Props = { isProductionExecutionPreview?: boolean; isPaneActive?: boolean; displayMode: IRunDataDisplayMode; + compact?: boolean; + disableDisplayModeSelection?: boolean; + focusedMappableInput: string; + isMappingOnboarded: boolean; }; const props = withDefaults(defineProps(), { @@ -91,15 +94,10 @@ const inputModes = [ ]; const nodeTypesStore = useNodeTypesStore(); -const ndvStore = useNDVStore(); const workflowsStore = useWorkflowsStore(); const posthogStore = usePostHog(); -const { - activeNode, - focusedMappableInput, - isMappingOnboarded: isUserOnboarded, -} = storeToRefs(ndvStore); +const activeNode = computed(() => workflowsStore.getNodeByName(props.activeNodeName)); const rootNode = computed(() => { if (!activeNode.value) return null; @@ -128,7 +126,7 @@ const showDraggableHint = computed(() => { return false; } - return !!focusedMappableInput.value && !isUserOnboarded.value; + return !!props.focusedMappableInput && !props.isMappingOnboarded; }); const isActiveNodeConfig = computed(() => { @@ -396,6 +394,8 @@ function handleChangeCollapsingColumn(columnName: string | null) { data-test-id="ndv-input-panel" :disable-ai-content="true" :collapsing-table-column-name="collapsingColumnName" + :compact="compact" + :disable-display-mode-selection="disableDisplayModeSelection" @activate-pane="activatePane" @item-hover="onItemHover" @link-run="onLinkRun" @@ -408,9 +408,14 @@ function handleChangeCollapsingColumn(columnName: string | null) { >