fix(editor): Fixes the issue that Switch Node can not be created (#7516)

Fixes the issue that currently no Switch-Node can be created by for
example pressing + on the parent node or via tab when another node is
already selected.

Github issue / Community forum post (link here to close automatically):

---------

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
Jan Oberhauser 2023-10-26 09:46:26 +02:00 committed by GitHub
parent 742c8a8534
commit df89685e15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 15 deletions

View file

@ -30,6 +30,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
it('should add switch node and test connections', () => {
const desiredOutputs = 4;
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true);
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true, true);
for (let i = 0; i < desiredOutputs; i++) {
@ -43,9 +44,14 @@ describe('Canvas Node Manipulation and Navigation', () => {
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, false);
WorkflowPage.actions.zoomToFit();
}
WorkflowPage.getters.nodeViewBackground().click({ force: true });
WorkflowPage.getters.canvasNodePlusEndpointByName(`${EDIT_FIELDS_SET_NODE_NAME}3`).click();
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, false);
WorkflowPage.actions.saveWorkflowOnButtonClick();
cy.reload();
cy.waitForLoad();
// Make sure outputless switch was connected correctly
cy.get(`[data-target-node="${SWITCH_NODE_NAME}1"][data-source-node="${EDIT_FIELDS_SET_NODE_NAME}3"]`).should('be.visible');
// Make sure all connections are there after reload
for (let i = 0; i < desiredOutputs; i++) {
const setName = `${EDIT_FIELDS_SET_NODE_NAME}${i > 0 ? i : ''}`;

View file

@ -265,6 +265,7 @@ import type {
IWorkflowBase,
Workflow,
ConnectionTypes,
INodeOutputConfiguration,
} from 'n8n-workflow';
import {
deepCopy,
@ -1970,29 +1971,47 @@ export default defineComponent({
this.canvasStore.newNodeInsertPosition = null;
} else {
let yOffset = 0;
const workflow = this.getCurrentWorkflow();
if (lastSelectedConnection) {
const sourceNodeType = this.nodeTypesStore.getNodeType(
lastSelectedNode.type,
lastSelectedNode.typeVersion,
);
const offsets = [
[-100, 100],
[-140, 0, 140],
[-240, -100, 100, 240],
];
if (sourceNodeType && sourceNodeType.outputs.length > 1) {
const offset = offsets[sourceNodeType.outputs.length - 2];
const sourceOutputIndex = lastSelectedConnection.__meta
? lastSelectedConnection.__meta.sourceOutputIndex
: 0;
yOffset = offset[sourceOutputIndex];
if (sourceNodeType) {
const offsets = [
[-100, 100],
[-140, 0, 140],
[-240, -100, 100, 240],
];
const sourceNodeOutputs = NodeHelpers.getNodeOutputs(
workflow,
lastSelectedNode!,
sourceNodeType,
);
const sourceNodeOutputTypes = NodeHelpers.getConnectionTypes(sourceNodeOutputs);
const sourceNodeOutputMainOutputs = sourceNodeOutputTypes.filter(
(output) => output === NodeConnectionType.Main,
);
if (sourceNodeOutputMainOutputs.length > 1) {
const offset = offsets[sourceNodeOutputMainOutputs.length - 2];
const sourceOutputIndex = lastSelectedConnection.__meta
? lastSelectedConnection.__meta.sourceOutputIndex
: 0;
yOffset = offset[sourceOutputIndex];
}
}
}
const workflow = this.getCurrentWorkflow();
const workflowNode = workflow.getNode(newNodeData.name);
const outputs = NodeHelpers.getNodeOutputs(workflow, workflowNode!, nodeTypeData);
let outputs: Array<ConnectionTypes | INodeOutputConfiguration> = [];
try {
// It fails when the outputs are an expression. As those node have
// normally no outputs by default and the only reason we need the
// outputs here is to calculate the position it is fine to assume
// that they have no outputs and are so treated as a regular node
// with only "main" outputs.
outputs = NodeHelpers.getNodeOutputs(workflow, newNodeData!, nodeTypeData);
} catch (e) {}
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
const lastSelectedNodeType = this.nodeTypesStore.getNodeType(
lastSelectedNode.type,
@ -2000,7 +2019,10 @@ export default defineComponent({
);
// If node has only scoped outputs, position it below the last selected node
if (outputTypes.every((outputName) => outputName !== NodeConnectionType.Main)) {
if (
outputTypes.length > 0 &&
outputTypes.every((outputName) => outputName !== NodeConnectionType.Main)
) {
const lastSelectedNodeWorkflow = workflow.getNode(lastSelectedNode.name);
const lastSelectedInputs = NodeHelpers.getNodeInputs(
workflow,
@ -2025,6 +2047,7 @@ export default defineComponent({
[100, 0],
);
} else {
// Has only main outputs or no outputs at all
const inputs = NodeHelpers.getNodeInputs(
workflow,
lastSelectedNode,