diff --git a/packages/frontend/@n8n/frontend-module-sdk/src/types/component.ts b/packages/frontend/@n8n/frontend-module-sdk/src/types/component.ts index e8e42b07768..3c353ad7061 100644 --- a/packages/frontend/@n8n/frontend-module-sdk/src/types/component.ts +++ b/packages/frontend/@n8n/frontend-module-sdk/src/types/component.ts @@ -33,4 +33,6 @@ export type SlotWorkflowDiffProps = { sourceLabel?: string; targetLabel?: string; showFullscreenButton?: boolean; + canvasIdPrefix?: string; + hideTitle?: boolean; }; diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index 766587e622f..e3ff2cc537c 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -6191,7 +6191,7 @@ "workflowReviews.detail.metadata.otherAuthors": "Other authors", "workflowReviews.detail.metadata.reviewers": "Reviewers", "workflowReviews.detail.metadata.noReviewers": "No reviewers assigned.", - "workflowReviews.detail.metadata.workflow": "Workflow", + "workflowReviews.detail.metadata.workflow": "Workflow | Workflows", "workflowReviews.changes.workflowUnavailable": "The workflow in this review is no longer available.", "workflowReviews.changes.unavailable": "Couldn't load the changes for this review. Refresh to try again.", "workflowReviews.changes.versionUnavailable.body": "The submitted workflow version is no longer available.", @@ -7283,6 +7283,7 @@ "whatsNew.updateAvailable": "You're currently on version {currentVersion}. Update to {latestVersion} to get {count} versions worth of new features, improvements, and fixes. See what changed", "whatsNew.updateAvailable.changelogLink": "in the full changelog", "workflowDiff.changes": "Changes", + "workflowDiff.syncViews": "Sync views", "workflowDiff.nodes": "Nodes", "workflowDiff.connectors": "Connectors", "workflowDiff.compare": "Compare versions", diff --git a/packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.test.ts b/packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.test.ts index 075dad31d1f..e636e6f4331 100644 --- a/packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.test.ts +++ b/packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.test.ts @@ -115,6 +115,15 @@ describe('WorkflowDiffContent', () => { expect(screen.getByTestId('synced-canvas-bottom')).toBeInTheDocument(); }); + it('adds the prefix to the canvas ids', () => { + render(WorkflowDiffContent, { + props: { ...defaultProps, canvasIdPrefix: 'wf-1' }, + }); + + expect(screen.getByTestId('synced-canvas-wf-1-top')).toBeInTheDocument(); + expect(screen.getByTestId('synced-canvas-wf-1-bottom')).toBeInTheDocument(); + }); + it('should display source label', () => { render(WorkflowDiffContent, { props: { ...defaultProps, sourceLabel: 'Remote' }, diff --git a/packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.vue b/packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.vue index b8fdfb4ba42..c166b11ab50 100644 --- a/packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.vue +++ b/packages/frontend/editor-ui/src/features/workflows/workflowDiff/WorkflowDiffContent.vue @@ -8,7 +8,7 @@ import NodeDiff from './NodeDiff.vue'; import HighlightedEdge from './HighlightedEdge.vue'; import Node from '@/features/workflows/canvas/components/elements/nodes/CanvasNode.vue'; import { useI18n } from '@n8n/i18n'; -import { useCssModule } from 'vue'; +import { computed, useCssModule } from 'vue'; import { N8nHeading, N8nText } from '@n8n/design-system'; import { NodeDiffStatus } from 'n8n-workflow'; @@ -29,6 +29,8 @@ const props = defineProps<{ applyLayout?: boolean; nodesDiff: Map; connectionsDiff: Map; + /** Prefix for the Vue Flow store ids. Set it when several diffs can be mounted at once. */ + canvasIdPrefix?: string; }>(); const emit = defineEmits<{ @@ -38,6 +40,14 @@ const emit = defineEmits<{ const i18n = useI18n(); const $style = useCssModule(); +// Vue Flow shares one store for each id across the page. +const sourceCanvasId = computed(() => + props.canvasIdPrefix ? `${props.canvasIdPrefix}-top` : 'top', +); +const targetCanvasId = computed(() => + props.canvasIdPrefix ? `${props.canvasIdPrefix}-bottom` : 'bottom', +); + function getNodeStatusClass(id: string) { const status = props.nodesDiff?.get(id)?.status ?? 'equal'; return $style[status]; @@ -61,7 +71,7 @@ function getEdgeStatusClass(id: string) {