Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(editor): Provide correct node output runData information in new canvas (no-changelog) #10691

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/editor-ui/src/__tests__/data/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
CanvasNodeEventBusEvents,
CanvasNodeHandleInjectionData,
CanvasNodeInjectionData,
ExecutionOutputMapData,
} from '@/types';
import { CanvasConnectionMode, CanvasNodeRenderType } from '@/types';
import { NodeConnectionType } from 'n8n-workflow';
Expand All @@ -25,7 +26,7 @@ export function createCanvasNodeData({
execution = { running: false },
issues = { items: [], visible: false },
pinnedData = { count: 0, visible: false },
runData = { count: 0, visible: false },
runData = { outputMap: {}, iterations: 0, visible: false },
render = {
type: CanvasNodeRenderType.Default,
options: { configurable: false, configuration: false, trigger: false },
Expand Down Expand Up @@ -119,13 +120,17 @@ export function createCanvasHandleProvide({
label = 'Handle',
mode = CanvasConnectionMode.Input,
type = NodeConnectionType.Main,
index = 0,
runData,
isConnected = false,
isConnecting = false,
isReadOnly = false,
}: {
label?: string;
mode?: CanvasConnectionMode;
type?: NodeConnectionType;
index?: number;
runData?: ExecutionOutputMapData;
isConnected?: boolean;
isConnecting?: boolean;
isReadOnly?: boolean;
Expand All @@ -135,8 +140,10 @@ export function createCanvasHandleProvide({
label: ref(label),
mode: ref(mode),
type: ref(type),
index: ref(index),
isConnected: ref(isConnected),
isConnecting: ref(isConnecting),
runData: ref(runData),
isReadOnly: ref(isReadOnly),
} satisfies CanvasNodeHandleInjectionData,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { computed, h, provide, toRef, useCssModule } from 'vue';
import type { CanvasConnectionPort, CanvasElementPortWithRenderData } from '@/types';
import { CanvasConnectionMode } from '@/types';

import type { ValidConnectionFunc } from '@vue-flow/core';
import { Handle } from '@vue-flow/core';
import { NodeConnectionType } from 'n8n-workflow';
Expand All @@ -13,6 +12,7 @@ import CanvasHandleNonMainInput from '@/components/canvas/elements/handles/rende
import CanvasHandleNonMainOutput from '@/components/canvas/elements/handles/render-types/CanvasHandleNonMainOutput.vue';
import { CanvasNodeHandleKey } from '@/constants';
import { createCanvasConnectionHandleString } from '@/utils/canvasUtilsV2';
import { useCanvasNode } from '@/composables/useCanvasNode';

const props = defineProps<{
mode: CanvasConnectionMode;
Expand Down Expand Up @@ -59,6 +59,18 @@ const isConnectableEnd = computed(() => {

const handleClasses = computed(() => [style.handle, style[props.type], style[props.mode]]);

/**
* Run data
*/

const { runDataOutputMap } = useCanvasNode();

const runData = computed(() =>
props.mode === CanvasConnectionMode.Output
? runDataOutputMap.value[props.type]?.[props.index]
: undefined,
);

/**
* Render additional elements
*/
Expand Down Expand Up @@ -103,11 +115,14 @@ const isConnecting = toRef(props, 'isConnecting');
const isReadOnly = toRef(props, 'isReadOnly');
const mode = toRef(props, 'mode');
const type = toRef(props, 'type');
const index = toRef(props, 'index');

provide(CanvasNodeHandleKey, {
label,
mode,
type,
index,
runData,
isConnected,
isConnecting,
isReadOnly,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,36 @@ describe('CanvasHandleMainOutput', () => {

expect(queryByTestId('canvas-handle-plus')).not.toBeInTheDocument();
});

it('should render run data label', async () => {
const runData = {
total: 1,
iterations: 1,
};
const { getByText } = renderComponent({
global: {
provide: {
...createCanvasHandleProvide({ label: '', runData }),
},
},
});
expect(getByText('1 item')).toBeInTheDocument();
});

it('should not render run data label if output label is available', async () => {
const runData = {
total: 1,
iterations: 1,
};
const { getByText } = renderComponent({
global: {
provide: {
...createCanvasHandleProvide({ label: 'Output', runData }),
},
},
});

expect(() => getByText('1 item')).toThrow();
expect(getByText('Output')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,41 @@ import { useCanvasNodeHandle } from '@/composables/useCanvasNodeHandle';
import { useCanvasNode } from '@/composables/useCanvasNode';
import { computed, ref } from 'vue';
import type { CanvasNodeDefaultRender } from '@/types';
import { useI18n } from '@/composables/useI18n';

const emit = defineEmits<{
add: [];
}>();

const i18n = useI18n();
const { render } = useCanvasNode();
const { label, isConnected, isConnecting, isReadOnly } = useCanvasNodeHandle();
const { label, isConnected, isConnecting, isReadOnly, runData } = useCanvasNodeHandle();

const handleClasses = 'source';
const isHovered = ref(false);

const renderOptions = computed(() => render.value.options as CanvasNodeDefaultRender['options']);

const runDataLabel = computed(() =>
runData.value
? i18n.baseText('ndv.output.items', {
adjustToNumber: runData.value.total,
interpolate: { count: String(runData.value.total) },
})
: '',
);

const isHandlePlusVisible = computed(() => !isConnecting.value || isHovered.value);

const plusState = computed(() => (runData.value ? 'success' : 'default'));

const plusLineSize = computed(
() =>
({
small: 46,
medium: 66,
large: 80,
})[renderOptions.value.outputs?.labelSize ?? 'small'],
})[renderOptions.value.outputs?.labelSize ?? runData.value ? 'large' : 'small'],
);

function onMouseEnter() {
Expand All @@ -41,7 +54,8 @@ function onClickAdd() {
</script>
<template>
<div :class="['canvas-node-handle-main-output', $style.handle]">
<div :class="[$style.label]">{{ label }}</div>
<div v-if="label" :class="[$style.label, $style.outputLabel]">{{ label }}</div>
<div v-else-if="runData" :class="[$style.label, $style.runDataLabel]">{{ runDataLabel }}</div>
<CanvasHandleDot :handle-classes="handleClasses" />
<Transition name="canvas-node-handle-main-output">
<CanvasHandlePlus
Expand All @@ -50,6 +64,7 @@ function onClickAdd() {
data-test-id="canvas-handle-plus"
:line-size="plusLineSize"
:handle-classes="handleClasses"
:state="plusState"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave"
@click:plus="onClickAdd"
Expand All @@ -68,18 +83,30 @@ function onClickAdd() {

.label {
position: absolute;
top: 50%;
left: var(--spacing-m);
transform: translate(0, -50%);
font-size: var(--font-size-2xs);
color: var(--color-foreground-xdark);
background: var(--color-canvas-label-background);
z-index: 1;
max-width: calc(100% - var(--spacing-m) - 24px);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}

.outputLabel {
top: 50%;
left: var(--spacing-m);
transform: translate(0, -50%);
font-size: var(--font-size-2xs);
color: var(--color-foreground-xdark);
}

.runDataLabel {
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
font-size: var(--font-size-xs);
color: var(--color-success);
}
</style>

<style lang="scss">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ describe('CanvasHandlePlus', () => {
});
});

it('should apply correct classes based on state', () => {
const { container } = renderComponent({
props: { state: 'success' },
});

expect(container.firstChild).toHaveClass('success');
});

it('should render SVG elements correctly', () => {
const { container } = renderComponent();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ const props = withDefaults(
handleClasses?: string;
plusSize?: number;
lineSize?: number;
state?: 'success' | 'default';
}>(),
{
position: 'right',
handleClasses: undefined,
plusSize: 24,
lineSize: 46,
state: 'default',
},
);

Expand All @@ -22,7 +24,12 @@ const emit = defineEmits<{

const style = useCssModule();

const classes = computed(() => [style.wrapper, style[props.position], props.handleClasses]);
const classes = computed(() => [
style.wrapper,
style[props.position],
style[props.state],
props.handleClasses,
]);

const viewBox = computed(() => {
switch (props.position) {
Expand Down Expand Up @@ -91,7 +98,7 @@ function onClick(event: MouseEvent) {
<template>
<svg :class="classes" :viewBox="`0 0 ${viewBox.width} ${viewBox.height}`" :style="styles">
<line
:class="handleClasses"
:class="[handleClasses, $style.line]"
:x1="linePosition[0][0]"
:y1="linePosition[0][1]"
:x2="linePosition[1][0]"
Expand Down Expand Up @@ -127,6 +134,10 @@ function onClick(event: MouseEvent) {
<style lang="scss" module>
.wrapper {
position: relative;

&.success .line {
stroke: var(--color-success);
}
}

.plus {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`CanvasHandleDiamond > should render with default props 1`] = `
"<svg class="wrapper right" viewBox="0 0 70 24" style="width: 70px; height: 24px;">
<line class="" x1="0" y1="12" x2="47" y2="12" stroke="var(--color-foreground-xdark)" stroke-width="2"></line>
"<svg class="wrapper right default" viewBox="0 0 70 24" style="width: 70px; height: 24px;">
<line class="line" x1="0" y1="12" x2="47" y2="12" stroke="var(--color-foreground-xdark)" stroke-width="2"></line>
<g class="plus clickable" transform="translate(46, 0)">
<rect class="clickable" x="2" y="2" width="20" height="20" stroke="var(--color-foreground-xdark)" stroke-width="2" rx="4" fill="#ffffff"></rect>
<path class="clickable" fill="var(--color-foreground-xdark)" d="m16.40655,10.89837l-3.30491,0l0,-3.30491c0,-0.40555 -0.32889,-0.73443 -0.73443,-0.73443l-0.73443,0c-0.40554,0 -0.73442,0.32888 -0.73442,0.73443l0,3.30491l-3.30491,0c-0.40555,0 -0.73443,0.32888 -0.73443,0.73442l0,0.73443c0,0.40554 0.32888,0.73443 0.73443,0.73443l3.30491,0l0,3.30491c0,0.40554 0.32888,0.73442 0.73442,0.73442l0.73443,0c0.40554,0 0.73443,-0.32888 0.73443,-0.73442l0,-3.30491l3.30491,0c0.40554,0 0.73442,-0.32889 0.73442,-0.73443l0,-0.73443c0,-0.40554 -0.32888,-0.73442 -0.73442,-0.73442z"></path>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`CanvasHandlePlus > should render with default props 1`] = `
"<svg class="wrapper right" viewBox="0 0 70 24" style="width: 70px; height: 24px;">
<line class="" x1="0" y1="12" x2="47" y2="12" stroke="var(--color-foreground-xdark)" stroke-width="2"></line>
"<svg class="wrapper right default" viewBox="0 0 70 24" style="width: 70px; height: 24px;">
<line class="line" x1="0" y1="12" x2="47" y2="12" stroke="var(--color-foreground-xdark)" stroke-width="2"></line>
<g class="plus clickable" transform="translate(46, 0)">
<rect class="clickable" x="2" y="2" width="20" height="20" stroke="var(--color-foreground-xdark)" stroke-width="2" rx="4" fill="#ffffff"></rect>
<path class="clickable" fill="var(--color-foreground-xdark)" d="m16.40655,10.89837l-3.30491,0l0,-3.30491c0,-0.40555 -0.32889,-0.73443 -0.73443,-0.73443l-0.73443,0c-0.40554,0 -0.73442,0.32888 -0.73442,0.73443l0,3.30491l-3.30491,0c-0.40555,0 -0.73443,0.32888 -0.73443,0.73442l0,0.73443c0,0.40554 0.32888,0.73443 0.73443,0.73443l3.30491,0l0,3.30491c0,0.40554 0.32888,0.73442 0.73442,0.73442l0.73443,0c0.40554,0 0.73443,-0.32888 0.73443,-0.73442l0,-3.30491l3.30491,0c0.40554,0 0.73442,-0.32889 0.73442,-0.73443l0,-0.73443c0,-0.40554 -0.32888,-0.73442 -0.73442,-0.73442z"></path>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ describe('CanvasNodeStatusIcons', () => {
it('should render correctly for a node that ran successfully', () => {
const { getByTestId } = renderComponent({
global: {
provide: createCanvasNodeProvide({ data: { runData: { count: 15, visible: true } } }),
provide: createCanvasNodeProvide({
data: { runData: { outputMap: {}, iterations: 15, visible: true } },
}),
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const {
executionWaiting,
executionRunning,
hasRunData,
runDataCount,
runDataIterations,
} = useCanvasNode();

const hideNodeIssues = computed(() => false); // @TODO Implement this
Expand Down Expand Up @@ -67,7 +67,7 @@ const hideNodeIssues = computed(() => false); // @TODO Implement this
:class="[$style.status, $style.runData]"
>
<FontAwesomeIcon icon="check" />
<span v-if="runDataCount > 1" :class="$style.count"> {{ runDataCount }}</span>
<span v-if="runDataIterations > 1" :class="$style.count"> {{ runDataIterations }}</span>
</div>
</template>

Expand Down
Loading
Loading