mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
fix(editor): Data in input/output panel incorrectly mapped (#14878)
This commit is contained in:
41
cypress/composables/logs.ts
Normal file
41
cypress/composables/logs.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Accessors
|
||||
*/
|
||||
|
||||
export function getLogEntryAtRow(rowIndex: number) {
|
||||
return cy.getByTestId('logs-overview-body').find('[role=treeitem]').eq(rowIndex);
|
||||
}
|
||||
|
||||
export function getInputTableRows() {
|
||||
return cy.getByTestId('log-details-input').find('table tr');
|
||||
}
|
||||
|
||||
export function getInputTbodyCell(row: number, col: number) {
|
||||
return cy.getByTestId('log-details-input').find('table tr').eq(row).find('td').eq(col);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export function openLogsPanel() {
|
||||
cy.getByTestId('logs-overview-header').click();
|
||||
}
|
||||
|
||||
export function clickLogEntryAtRow(rowIndex: number) {
|
||||
getLogEntryAtRow(rowIndex).click();
|
||||
}
|
||||
|
||||
export function toggleInputPanel() {
|
||||
cy.getByTestId('log-details-header').contains('Input').click();
|
||||
}
|
||||
|
||||
export function clickOpenNdvAtRow(rowIndex: number) {
|
||||
getLogEntryAtRow(rowIndex).realHover();
|
||||
getLogEntryAtRow(rowIndex).find('[aria-label="Open..."]').click();
|
||||
}
|
||||
|
||||
export function setInputDisplayMode(mode: 'table') {
|
||||
cy.getByTestId('log-details-input').realHover();
|
||||
cy.getByTestId('log-details-input').findChildByTestId(`radio-button-${mode}`).click();
|
||||
}
|
||||
@@ -32,6 +32,10 @@ export function getInputPanel() {
|
||||
return cy.getByTestId('ndv-input-panel');
|
||||
}
|
||||
|
||||
export function getInputSelect() {
|
||||
return cy.getByTestId('ndv-input-select').find('input');
|
||||
}
|
||||
|
||||
export function getMainPanel() {
|
||||
return cy.getByTestId('node-parameters');
|
||||
}
|
||||
@@ -53,11 +57,19 @@ export function getResourceLocatorInput(paramName: string) {
|
||||
}
|
||||
|
||||
export function getInputPanelDataContainer() {
|
||||
return getInputPanel().getByTestId('ndv-data-container');
|
||||
return getInputPanel().findChildByTestId('ndv-data-container');
|
||||
}
|
||||
|
||||
export function getInputTableRows() {
|
||||
return getInputPanelDataContainer().find('table tr');
|
||||
}
|
||||
|
||||
export function getInputTbodyCell(row: number, col: number) {
|
||||
return getInputTableRows().eq(row).find('td').eq(col);
|
||||
}
|
||||
|
||||
export function getOutputPanelDataContainer() {
|
||||
return getOutputPanel().getByTestId('ndv-data-container');
|
||||
return getOutputPanel().findChildByTestId('ndv-data-container');
|
||||
}
|
||||
|
||||
export function getOutputTableRows() {
|
||||
@@ -278,7 +290,7 @@ export function assertInlineExpressionValid() {
|
||||
}
|
||||
|
||||
export function hoverInputItemByText(text: string) {
|
||||
return getInputPanelDataContainer().contains(text).trigger('mouseover', { force: true });
|
||||
return getInputPanelDataContainer().contains(text).realHover();
|
||||
}
|
||||
|
||||
export function verifyInputHoverState(expectedText: string) {
|
||||
@@ -296,5 +308,5 @@ export function verifyOutputHoverState(expectedText: string) {
|
||||
}
|
||||
|
||||
export function resetHoverState() {
|
||||
getBackToCanvasButton().trigger('mouseover');
|
||||
getBackToCanvasButton().realHover();
|
||||
}
|
||||
|
||||
@@ -25,10 +25,12 @@ export type EndpointType =
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export function executeWorkflowAndWait() {
|
||||
export function executeWorkflowAndWait(waitForSuccessBannerToDisappear = true) {
|
||||
cy.get('[data-test-id="execute-workflow-button"]').click();
|
||||
cy.contains('Workflow executed successfully', { timeout: 4000 }).should('be.visible');
|
||||
cy.contains('Workflow executed successfully', { timeout: 10000 }).should('not.exist');
|
||||
if (waitForSuccessBannerToDisappear) {
|
||||
cy.contains('Workflow executed successfully', { timeout: 10000 }).should('not.exist');
|
||||
}
|
||||
}
|
||||
|
||||
export function getCanvas() {
|
||||
|
||||
@@ -87,23 +87,18 @@ describe('NDV', () => {
|
||||
ndv.actions.selectInputNode('Set1');
|
||||
ndvComposables.verifyInputHoverState('1000');
|
||||
|
||||
ndv.actions.dragMainPanelToRight();
|
||||
|
||||
ndvComposables.resetHoverState();
|
||||
ndvComposables.hoverInputItemByText('1000');
|
||||
ndvComposables.verifyOutputHoverState('1000');
|
||||
// BUG(ADO-3469): Expression preview is not updated when input node is changed it uses the old value
|
||||
// ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
|
||||
|
||||
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
|
||||
|
||||
ndv.actions.selectInputNode('Sort');
|
||||
ndv.actions.dragMainPanelToLeft();
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
||||
|
||||
ndvComposables.resetHoverState();
|
||||
ndvComposables.verifyInputHoverState('1111');
|
||||
|
||||
ndv.actions.dragMainPanelToRight();
|
||||
|
||||
ndvComposables.resetHoverState();
|
||||
ndvComposables.hoverInputItemByText('1111');
|
||||
ndvComposables.verifyOutputHoverState('1111');
|
||||
|
||||
ndv.getters.parameterExpressionPreview('value').should('include.text', '1111');
|
||||
|
||||
@@ -1,4 +1,57 @@
|
||||
import * as logs from '../composables/logs';
|
||||
import * as ndv from '../composables/ndv';
|
||||
import * as workflow from '../composables/workflow';
|
||||
import Workflow from '../fixtures/Workflow_if.json';
|
||||
|
||||
describe('Logs', () => {
|
||||
// TODO: the test can be written without AI nodes once https://linear.app/n8n/issue/SUG-39 is implemented
|
||||
it('should open NDV with the run index that corresponds to clicked log entry');
|
||||
beforeEach(() => {
|
||||
cy.overrideSettings({ logsView: { enabled: true } });
|
||||
});
|
||||
|
||||
it('should show input and output data of correct run index and branch', () => {
|
||||
workflow.navigateToNewWorkflowPage();
|
||||
workflow.pasteWorkflow(Workflow);
|
||||
workflow.clickZoomToFit();
|
||||
logs.openLogsPanel();
|
||||
workflow.executeWorkflowAndWait(false);
|
||||
|
||||
logs.clickLogEntryAtRow(2); // Run #1 of 'Edit Fields' node; input is 'Code' node
|
||||
logs.toggleInputPanel();
|
||||
logs.setInputDisplayMode('table');
|
||||
logs.getInputTableRows().should('have.length', 11);
|
||||
logs.getInputTbodyCell(1, 0).should('contain.text', '0');
|
||||
logs.getInputTbodyCell(10, 0).should('contain.text', '9');
|
||||
logs.clickOpenNdvAtRow(2);
|
||||
ndv.getInputSelect().should('have.value', 'Code ');
|
||||
ndv.getInputTableRows().should('have.length', 11);
|
||||
ndv.getInputTbodyCell(1, 0).should('contain.text', '0');
|
||||
ndv.getInputTbodyCell(10, 0).should('contain.text', '9');
|
||||
ndv.getOutputRunSelectorInput().should('have.value', '1 of 3 (10 items)');
|
||||
|
||||
ndv.clickGetBackToCanvas();
|
||||
|
||||
logs.clickLogEntryAtRow(4); // Run #2 of 'Edit Fields' node; input is false branch of 'If' node
|
||||
logs.getInputTableRows().should('have.length', 6);
|
||||
logs.getInputTbodyCell(1, 0).should('contain.text', '5');
|
||||
logs.getInputTbodyCell(5, 0).should('contain.text', '9');
|
||||
logs.clickOpenNdvAtRow(4);
|
||||
ndv.getInputSelect().should('have.value', 'If ');
|
||||
ndv.getInputTableRows().should('have.length', 6);
|
||||
ndv.getInputTbodyCell(1, 0).should('contain.text', '5');
|
||||
ndv.getInputTbodyCell(5, 0).should('contain.text', '9');
|
||||
ndv.getOutputRunSelectorInput().should('have.value', '2 of 3 (5 items)');
|
||||
|
||||
ndv.clickGetBackToCanvas();
|
||||
|
||||
logs.clickLogEntryAtRow(5); // Run #3 of 'Edit Fields' node; input is true branch of 'If' node
|
||||
logs.getInputTableRows().should('have.length', 6);
|
||||
logs.getInputTbodyCell(1, 0).should('contain.text', '0');
|
||||
logs.getInputTbodyCell(5, 0).should('contain.text', '4');
|
||||
logs.clickOpenNdvAtRow(5);
|
||||
ndv.getInputSelect().should('have.value', 'If ');
|
||||
ndv.getInputTableRows().should('have.length', 6);
|
||||
ndv.getInputTbodyCell(1, 0).should('contain.text', '0');
|
||||
ndv.getInputTbodyCell(5, 0).should('contain.text', '4');
|
||||
ndv.getOutputRunSelectorInput().should('have.value', '3 of 3 (5 items)');
|
||||
});
|
||||
});
|
||||
|
||||
127
cypress/fixtures/Workflow_if.json
Normal file
127
cypress/fixtures/Workflow_if.json
Normal file
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [{}]
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.2,
|
||||
"position": [-900, 60],
|
||||
"id": "e6b8fc7c-442e-4283-a0cd-604dc7c9e816",
|
||||
"name": "Schedule Trigger"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict",
|
||||
"version": 2
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "553f50d9-5023-433f-8f62-eebc9c9e2269",
|
||||
"leftValue": "={{ $json.data }}",
|
||||
"rightValue": 5,
|
||||
"operator": {
|
||||
"type": "number",
|
||||
"operation": "lt"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.2,
|
||||
"position": [-460, 135],
|
||||
"id": "f5c96b5b-9e22-4348-a258-fdb0417f5ff5",
|
||||
"name": "If"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "71475f04-571e-4e99-bdf8-adff367533fb",
|
||||
"name": "data",
|
||||
"value": "={{ $json.data }}",
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [-240, 60],
|
||||
"id": "2a6fc40d-5d8c-4c35-bf53-ee910267619f",
|
||||
"name": "Edit Fields"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "return Array.from({length:10}).map((_,i)=>({data:i}))"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [-680, 60],
|
||||
"id": "12ae07e7-be34-43b6-806b-4c24be169ee6",
|
||||
"name": "Code"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Schedule Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"If": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"meta": {
|
||||
"instanceId": "db1f26b45a71ad9a8df79dde8d35bf1be13616c3b23eb55be8ecf642dd31500c"
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,15 @@ interface RadioButtonProps {
|
||||
value: string;
|
||||
active?: boolean;
|
||||
disabled?: boolean;
|
||||
size?: 'small' | 'medium';
|
||||
noPadding?: boolean;
|
||||
size?: 'small' | 'small-medium' | 'medium';
|
||||
square?: boolean;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<RadioButtonProps>(), {
|
||||
active: false,
|
||||
disabled: false,
|
||||
size: 'medium',
|
||||
noPadding: false,
|
||||
square: false,
|
||||
});
|
||||
|
||||
defineSlots<{ default?: {} }>();
|
||||
@@ -26,7 +26,7 @@ defineSlots<{ default?: {} }>();
|
||||
'n8n-radio-button': true,
|
||||
[$style.container]: true,
|
||||
[$style.hoverable]: !disabled,
|
||||
[$style.noPadding]: noPadding,
|
||||
[$style.square]: square,
|
||||
}"
|
||||
:aria-checked="active"
|
||||
>
|
||||
@@ -76,8 +76,10 @@ defineSlots<{ default?: {} }>();
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
.noPadding & {
|
||||
padding-inline: 0;
|
||||
.square & {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,12 +91,33 @@ defineSlots<{ default?: {} }>();
|
||||
height: 26px;
|
||||
font-size: var(--font-size-2xs);
|
||||
padding: 0 var(--spacing-xs);
|
||||
|
||||
.square & {
|
||||
width: 26px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.small-medium {
|
||||
height: 22px;
|
||||
font-size: var(--font-size-3xs);
|
||||
padding: 0 var(--spacing-2xs);
|
||||
|
||||
.square & {
|
||||
width: 22px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: var(--font-size-3xs);
|
||||
height: 15px;
|
||||
padding: 0 var(--spacing-4xs);
|
||||
|
||||
.square & {
|
||||
width: 15px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
|
||||
@@ -11,14 +11,16 @@ interface RadioButtonsProps {
|
||||
modelValue?: Value;
|
||||
options?: RadioOption[];
|
||||
/** @default medium */
|
||||
size?: 'small' | 'medium';
|
||||
size?: 'small' | 'small-medium' | 'medium';
|
||||
disabled?: boolean;
|
||||
squareButtons?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<RadioButtonsProps>(), {
|
||||
active: false,
|
||||
disabled: false,
|
||||
size: 'medium',
|
||||
squareButtons: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -50,7 +52,7 @@ const onClick = (
|
||||
:active="modelValue === option.value"
|
||||
:size="size"
|
||||
:disabled="disabled || option.disabled"
|
||||
:no-padding="!!slots.option"
|
||||
:square="squareButtons"
|
||||
@click.prevent.stop="onClick(option, $event)"
|
||||
>
|
||||
<slot name="option" v-bind="option" />
|
||||
|
||||
@@ -17,10 +17,12 @@ interface TabOptions {
|
||||
interface TabsProps {
|
||||
modelValue?: Value;
|
||||
options?: TabOptions[];
|
||||
size?: 'small' | 'medium';
|
||||
}
|
||||
|
||||
withDefaults(defineProps<TabsProps>(), {
|
||||
options: () => [],
|
||||
size: 'medium',
|
||||
});
|
||||
|
||||
const scrollPosition = ref(0);
|
||||
@@ -74,7 +76,7 @@ const scrollRight = () => scroll(50);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="['n8n-tabs', $style.container]">
|
||||
<div :class="['n8n-tabs', $style.container, size === 'small' ? $style.small : '']">
|
||||
<div v-if="scrollPosition > 0" :class="$style.back" @click="scrollLeft">
|
||||
<N8nIcon icon="chevron-left" size="small" />
|
||||
</div>
|
||||
@@ -172,6 +174,10 @@ const scrollRight = () => scroll(50);
|
||||
span + span {
|
||||
margin-left: var(--spacing-4xs);
|
||||
}
|
||||
|
||||
.small & {
|
||||
font-size: var(--font-size-2xs);
|
||||
}
|
||||
}
|
||||
|
||||
.activeTab {
|
||||
|
||||
@@ -34,6 +34,7 @@ describe('LogDetailsPanel', () => {
|
||||
executionStatus: 'success',
|
||||
executionTime: 10,
|
||||
data: { main: [[{ json: { response: 'Hello!' } }]] },
|
||||
source: [{ previousNode: 'Chat Trigger' }],
|
||||
});
|
||||
|
||||
function render(props: Partial<InstanceType<typeof LogDetailsPanel>['$props']>) {
|
||||
@@ -116,7 +117,7 @@ describe('LogDetailsPanel', () => {
|
||||
it('should toggle input and output panel when the button is clicked', async () => {
|
||||
const rendered = render({
|
||||
isOpen: true,
|
||||
logEntry: createTestLogEntry({ node: aiNode, runIndex: 0 }),
|
||||
logEntry: createTestLogEntry({ node: aiNode, runIndex: 0, runData: aiNodeRunData }),
|
||||
});
|
||||
|
||||
const header = within(rendered.getByTestId('log-details-header'));
|
||||
@@ -140,7 +141,7 @@ describe('LogDetailsPanel', () => {
|
||||
|
||||
const rendered = render({
|
||||
isOpen: true,
|
||||
logEntry: createTestLogEntry({ node: aiNode, runIndex: 0 }),
|
||||
logEntry: createTestLogEntry({ node: aiNode, runIndex: 0, runData: aiNodeRunData }),
|
||||
});
|
||||
|
||||
await fireEvent.mouseDown(rendered.getByTestId('resize-handle'));
|
||||
@@ -159,7 +160,7 @@ describe('LogDetailsPanel', () => {
|
||||
|
||||
const rendered = render({
|
||||
isOpen: true,
|
||||
logEntry: createTestLogEntry({ node: aiNode, runIndex: 0 }),
|
||||
logEntry: createTestLogEntry({ node: aiNode, runIndex: 0, runData: aiNodeRunData }),
|
||||
});
|
||||
|
||||
await fireEvent.mouseDown(rendered.getByTestId('resize-handle'));
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
type LogEntry,
|
||||
} from '@/components/RunDataAi/utils';
|
||||
import { useVirtualList } from '@vueuse/core';
|
||||
import { ndvEventBus } from '@/event-bus';
|
||||
|
||||
const { isOpen, isReadOnly, selected, isCompact, execution, latestNodeInfo, scrollToSelection } =
|
||||
defineProps<{
|
||||
@@ -88,7 +89,14 @@ function handleToggleExpanded(treeNode: LogEntry) {
|
||||
async function handleOpenNdv(treeNode: LogEntry) {
|
||||
ndvStore.setActiveNodeName(treeNode.node.name);
|
||||
|
||||
await nextTick(() => ndvStore.setOutputRunIndex(treeNode.runIndex));
|
||||
await nextTick(() => {
|
||||
const source = treeNode.runData.source[0];
|
||||
const inputBranch = source?.previousNodeOutput ?? 0;
|
||||
|
||||
ndvEventBus.emit('updateInputNodeName', source?.previousNode);
|
||||
ndvEventBus.emit('setInputBranchIndex', inputBranch);
|
||||
ndvStore.setOutputRunIndex(treeNode.runIndex);
|
||||
});
|
||||
}
|
||||
|
||||
async function handleTriggerPartialExecution(treeNode: LogEntry) {
|
||||
@@ -190,7 +198,7 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
<N8nRadioButtons
|
||||
size="small"
|
||||
size="small-medium"
|
||||
:class="$style.switchViewButtons"
|
||||
:model-value="selected ? 'details' : 'overview'"
|
||||
:options="switchViewOptions"
|
||||
@@ -257,7 +265,7 @@ watch(
|
||||
z-index: 10; /* higher than log entry rows background */
|
||||
right: 0;
|
||||
top: 0;
|
||||
margin: var(--spacing-2xs);
|
||||
margin: var(--spacing-4xs) var(--spacing-2xs);
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s $ease-out-expo;
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useI18n } from '@/composables/useI18n';
|
||||
import { type IExecutionResponse, type NodePanelType } from '@/Interface';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { N8nLink, N8nText } from '@n8n/design-system';
|
||||
import { uniq } from 'lodash-es';
|
||||
import { type Workflow } from 'n8n-workflow';
|
||||
import { computed } from 'vue';
|
||||
import { I18nT } from 'vue-i18n';
|
||||
@@ -20,17 +19,27 @@ const { title, logEntry, paneType, workflow, execution } = defineProps<{
|
||||
|
||||
const locale = useI18n();
|
||||
const ndvStore = useNDVStore();
|
||||
const parentNodeNames = computed(() =>
|
||||
uniq(workflow.getParentNodesByDepth(logEntry.node.name, 1)).map((c) => c.name),
|
||||
);
|
||||
const node = computed(() => {
|
||||
const isMultipleInput = computed(() => paneType === 'input' && logEntry.runData.source.length > 1);
|
||||
const runDataProps = computed<
|
||||
Pick<InstanceType<typeof RunData>['$props'], 'node' | 'runIndex' | 'overrideOutputs'> | undefined
|
||||
>(() => {
|
||||
if (logEntry.depth > 0 || paneType === 'output') {
|
||||
return logEntry.node;
|
||||
return { node: logEntry.node, runIndex: logEntry.runIndex };
|
||||
}
|
||||
|
||||
return parentNodeNames.value.length > 0 ? workflow.getNode(parentNodeNames.value[0]) : undefined;
|
||||
const source = logEntry.runData.source[0];
|
||||
const node = source && workflow.getNode(source.previousNode);
|
||||
|
||||
if (!source || !node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
node,
|
||||
runIndex: source.previousNodeRun ?? 0,
|
||||
overrideOutputs: [source.previousNodeOutput ?? 0],
|
||||
};
|
||||
});
|
||||
const isMultipleInput = computed(() => paneType === 'input' && parentNodeNames.value.length > 1);
|
||||
|
||||
function handleClickOpenNdv() {
|
||||
ndvStore.setActiveNodeName(logEntry.node.name);
|
||||
@@ -39,11 +48,10 @@ function handleClickOpenNdv() {
|
||||
|
||||
<template>
|
||||
<RunData
|
||||
v-if="node"
|
||||
:node="node"
|
||||
v-if="runDataProps"
|
||||
v-bind="runDataProps"
|
||||
:workflow="workflow"
|
||||
:workflow-execution="execution"
|
||||
:run-index="logEntry.runIndex"
|
||||
:too-much-data-title="locale.baseText('ndv.output.tooMuchData.title')"
|
||||
:no-data-in-branch-message="locale.baseText('ndv.output.noOutputDataInBranch')"
|
||||
:executing-message="locale.baseText('ndv.output.executing')"
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from '@/constants';
|
||||
import { useWorkflowActivate } from '@/composables/useWorkflowActivate';
|
||||
import type { DataPinningDiscoveryEvent } from '@/event-bus';
|
||||
import { dataPinningEventBus } from '@/event-bus';
|
||||
import { dataPinningEventBus, ndvEventBus } from '@/event-bus';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
@@ -80,8 +80,8 @@ const settingsEventBus = createEventBus();
|
||||
const redrawRequired = ref(false);
|
||||
const runInputIndex = ref(-1);
|
||||
const runOutputIndex = computed(() => ndvStore.output.run ?? -1);
|
||||
const isLinkingEnabled = ref(true);
|
||||
const selectedInput = ref<string | undefined>();
|
||||
const isLinkingEnabled = ref(true);
|
||||
const triggerWaitingWarningEnabled = ref(false);
|
||||
const isDragging = ref(false);
|
||||
const mainPanelPosition = ref(0);
|
||||
@@ -615,6 +615,10 @@ const unregisterKeyboardListener = () => {
|
||||
document.removeEventListener('keydown', onKeyDown, true);
|
||||
};
|
||||
|
||||
const setSelectedInput = (value: string | undefined) => {
|
||||
selectedInput.value = value;
|
||||
};
|
||||
|
||||
//watchers
|
||||
|
||||
watch(
|
||||
@@ -702,10 +706,12 @@ watch(inputRun, (inputRun) => {
|
||||
|
||||
onMounted(() => {
|
||||
dataPinningEventBus.on('data-pinning-discovery', setIsTooltipVisible);
|
||||
ndvEventBus.on('updateInputNodeName', setSelectedInput);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
dataPinningEventBus.off('data-pinning-discovery', setIsTooltipVisible);
|
||||
ndvEventBus.off('updateInputNodeName', setSelectedInput);
|
||||
unregisterKeyboardListener();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -60,7 +60,7 @@ import type { PinDataSource, UnpinDataSource } from '@/composables/usePinnedData
|
||||
import { usePinnedData } from '@/composables/usePinnedData';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { dataPinningEventBus } from '@/event-bus';
|
||||
import { dataPinningEventBus, ndvEventBus } from '@/event-bus';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useRootStore } from '@/stores/root.store';
|
||||
@@ -617,6 +617,12 @@ const itemsCountProps = computed<InstanceType<typeof RunDataItemCount>['$props']
|
||||
subExecutionsCount: activeTaskMetadata.value?.subExecutionsCount,
|
||||
}));
|
||||
|
||||
function setInputBranchIndex(value: number) {
|
||||
if (props.paneType === 'input') {
|
||||
outputIndex.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
watch(node, (newNode, prevNode) => {
|
||||
if (newNode?.id === prevNode?.id) return;
|
||||
init();
|
||||
@@ -674,6 +680,8 @@ watch(search, (newSearch) => {
|
||||
onMounted(() => {
|
||||
init();
|
||||
|
||||
ndvEventBus.on('setInputBranchIndex', setInputBranchIndex);
|
||||
|
||||
if (!isPaneTypeInput.value) {
|
||||
showPinDataDiscoveryTooltip(jsonData.value);
|
||||
}
|
||||
@@ -710,6 +718,7 @@ onMounted(() => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
hidePinDataDiscoveryTooltip();
|
||||
ndvEventBus.off('setInputBranchIndex', setInputBranchIndex);
|
||||
});
|
||||
|
||||
function getResolvedNodeOutputs() {
|
||||
@@ -1551,6 +1560,7 @@ defineExpose({ enterEditMode });
|
||||
|
||||
<div :class="$style.tabs">
|
||||
<N8nTabs
|
||||
size="small"
|
||||
:model-value="currentOutputIndex"
|
||||
:options="branches"
|
||||
@update:model-value="onBranchChange"
|
||||
@@ -2049,6 +2059,13 @@ defineExpose({ enterEditMode });
|
||||
padding-left: var(--spacing-s);
|
||||
padding-right: var(--spacing-s);
|
||||
padding-bottom: var(--spacing-s);
|
||||
|
||||
.compact & {
|
||||
padding-left: var(--spacing-2xs);
|
||||
padding-right: var(--spacing-2xs);
|
||||
padding-bottom: var(--spacing-2xs);
|
||||
font-size: var(--font-size-2xs);
|
||||
}
|
||||
}
|
||||
|
||||
.tabs {
|
||||
|
||||
@@ -39,32 +39,17 @@ const options = computed(() => {
|
||||
:model-value="value"
|
||||
:options="options"
|
||||
data-test-id="ndv-run-data-display-mode"
|
||||
:size="compact ? 'small' : 'medium'"
|
||||
:size="compact ? 'small-medium' : 'medium'"
|
||||
:square-buttons="compact"
|
||||
@update:model-value="(selected) => emit('change', selected)"
|
||||
>
|
||||
<template v-if="compact" #option="option">
|
||||
<N8nIcon v-if="option.value === 'table'" icon="table" size="small" :class="$style.icon" />
|
||||
<N8nIcon v-else-if="option.value === 'json'" icon="json" size="small" :class="$style.icon" />
|
||||
<N8nIcon
|
||||
v-else-if="option.value === 'binary'"
|
||||
icon="binary"
|
||||
size="small"
|
||||
:class="$style.icon"
|
||||
/>
|
||||
<N8nIcon
|
||||
v-else-if="option.value === 'schema'"
|
||||
icon="schema"
|
||||
size="small"
|
||||
:class="$style.icon"
|
||||
/>
|
||||
<N8nIcon v-else-if="option.value === 'html'" icon="html" size="small" :class="$style.icon" />
|
||||
<N8nIcon v-if="option.value === 'table'" icon="table" size="small" />
|
||||
<N8nIcon v-else-if="option.value === 'json'" icon="json" size="small" />
|
||||
<N8nIcon v-else-if="option.value === 'binary'" icon="binary" size="small" />
|
||||
<N8nIcon v-else-if="option.value === 'schema'" icon="schema" size="small" />
|
||||
<N8nIcon v-else-if="option.value === 'html'" icon="html" size="small" />
|
||||
<span v-else>{{ option.label }}</span>
|
||||
</template>
|
||||
</N8nRadioButtons>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.icon {
|
||||
padding-inline: var(--spacing-4xs);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,6 +16,10 @@ export interface NdvEventBusEvents {
|
||||
setPositionByName: Position;
|
||||
|
||||
updateParameterValue: IUpdateInformation;
|
||||
|
||||
updateInputNodeName: string | undefined;
|
||||
|
||||
setInputBranchIndex: number;
|
||||
}
|
||||
|
||||
export const ndvEventBus = createEventBus<NdvEventBusEvents>();
|
||||
|
||||
Reference in New Issue
Block a user