From 288493df0c877bfa73fb4f7753762f2c174fe162 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Fri, 6 Sep 2024 13:37:44 +0300 Subject: [PATCH] fix(editor): Provide correct node output `runData` information in new canvas (no-changelog) (#10691) --- .../editor-ui/src/__tests__/data/canvas.ts | 9 +- .../elements/handles/CanvasHandleRenderer.vue | 17 +- .../CanvasHandleMainOutput.spec.ts | 32 +++ .../render-types/CanvasHandleMainOutput.vue | 43 +++- .../parts/CanvasHandlePlus.spec.ts | 8 + .../render-types/parts/CanvasHandlePlus.vue | 15 +- .../CanvasHandleDiamond.spec.ts.snap | 4 +- .../CanvasHandlePlus.spec.ts.snap | 4 +- .../parts/CanvasNodeStatusIcons.spec.ts | 4 +- .../parts/CanvasNodeStatusIcons.vue | 4 +- .../src/composables/useCanvasMapping.spec.ts | 237 ++++++++++++++++-- .../src/composables/useCanvasMapping.ts | 49 +++- .../src/composables/useCanvasNode.spec.ts | 8 +- .../src/composables/useCanvasNode.ts | 15 +- .../src/composables/useCanvasNodeHandle.ts | 6 +- packages/editor-ui/src/types/canvas.ts | 16 +- 16 files changed, 421 insertions(+), 50 deletions(-) diff --git a/packages/editor-ui/src/__tests__/data/canvas.ts b/packages/editor-ui/src/__tests__/data/canvas.ts index 9fa91e8ebc9dc1..639e1a8e82d3d9 100644 --- a/packages/editor-ui/src/__tests__/data/canvas.ts +++ b/packages/editor-ui/src/__tests__/data/canvas.ts @@ -6,6 +6,7 @@ import type { CanvasNodeEventBusEvents, CanvasNodeHandleInjectionData, CanvasNodeInjectionData, + ExecutionOutputMapData, } from '@/types'; import { CanvasConnectionMode, CanvasNodeRenderType } from '@/types'; import { NodeConnectionType } from 'n8n-workflow'; @@ -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 }, @@ -119,6 +120,8 @@ export function createCanvasHandleProvide({ label = 'Handle', mode = CanvasConnectionMode.Input, type = NodeConnectionType.Main, + index = 0, + runData, isConnected = false, isConnecting = false, isReadOnly = false, @@ -126,6 +129,8 @@ export function createCanvasHandleProvide({ label?: string; mode?: CanvasConnectionMode; type?: NodeConnectionType; + index?: number; + runData?: ExecutionOutputMapData; isConnected?: boolean; isConnecting?: boolean; isReadOnly?: boolean; @@ -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, }; diff --git a/packages/editor-ui/src/components/canvas/elements/handles/CanvasHandleRenderer.vue b/packages/editor-ui/src/components/canvas/elements/handles/CanvasHandleRenderer.vue index 3571cc32f7102d..9b28d855edc111 100644 --- a/packages/editor-ui/src/components/canvas/elements/handles/CanvasHandleRenderer.vue +++ b/packages/editor-ui/src/components/canvas/elements/handles/CanvasHandleRenderer.vue @@ -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'; @@ -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; @@ -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 */ @@ -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, diff --git a/packages/editor-ui/src/components/canvas/elements/handles/render-types/CanvasHandleMainOutput.spec.ts b/packages/editor-ui/src/components/canvas/elements/handles/render-types/CanvasHandleMainOutput.spec.ts index 956f1325829382..6027bda5cd0fc5 100644 --- a/packages/editor-ui/src/components/canvas/elements/handles/render-types/CanvasHandleMainOutput.spec.ts +++ b/packages/editor-ui/src/components/canvas/elements/handles/render-types/CanvasHandleMainOutput.spec.ts @@ -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(); + }); }); diff --git a/packages/editor-ui/src/components/canvas/elements/handles/render-types/CanvasHandleMainOutput.vue b/packages/editor-ui/src/components/canvas/elements/handles/render-types/CanvasHandleMainOutput.vue index c7ed5eed81833b..8ff4d1f074eba9 100644 --- a/packages/editor-ui/src/components/canvas/elements/handles/render-types/CanvasHandleMainOutput.vue +++ b/packages/editor-ui/src/components/canvas/elements/handles/render-types/CanvasHandleMainOutput.vue @@ -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() { @@ -41,7 +54,8 @@ function onClickAdd() {