diff --git a/cypress/e2e/11-inline-expression-editor.cy.ts b/cypress/e2e/11-inline-expression-editor.cy.ts
index cbc4736e2b..10bc16fde8 100644
--- a/cypress/e2e/11-inline-expression-editor.cy.ts
+++ b/cypress/e2e/11-inline-expression-editor.cy.ts
@@ -10,7 +10,7 @@ describe('Inline expression editor', () => {
beforeEach(() => {
WorkflowPage.actions.visit();
- WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
+ WorkflowPage.actions.addInitialNodeToCanvas('Manual');
WorkflowPage.actions.addNodeToCanvas('Hacker News');
WorkflowPage.actions.openNode('Hacker News');
WorkflowPage.actions.openInlineExpressionEditor();
@@ -50,6 +50,7 @@ describe('Inline expression editor', () => {
});
it('should resolve array resolvables', () => {
+ WorkflowPage.getters.inlineExpressionEditorInput().clear();
WorkflowPage.getters.inlineExpressionEditorInput().type('{{');
WorkflowPage.getters.inlineExpressionEditorInput().type('[1, 2, 3]');
WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^\[Array: \[1,2,3\]\]$/);
@@ -63,8 +64,9 @@ describe('Inline expression editor', () => {
});
it('should resolve $parameter[]', () => {
+ WorkflowPage.getters.inlineExpressionEditorInput().clear();
WorkflowPage.getters.inlineExpressionEditorInput().type('{{');
WorkflowPage.getters.inlineExpressionEditorInput().type('$parameter["operation"]');
- WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^get$/);
+ WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^getAll$/);
});
});
diff --git a/cypress/e2e/12-canvas.cy.ts b/cypress/e2e/12-canvas.cy.ts
index ac0bbd7bad..13d1bc6392 100644
--- a/cypress/e2e/12-canvas.cy.ts
+++ b/cypress/e2e/12-canvas.cy.ts
@@ -96,6 +96,7 @@ describe('Canvas Actions', () => {
it('should add merge node and test connections', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
for (let i = 0; i < 2; i++) {
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true);
WorkflowPage.getters.nodeViewBackground().click(600 + i * 100, 200, { force: true });
@@ -138,6 +139,7 @@ describe('Canvas Actions', () => {
it('should add nodes and check execution success', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
for (let i = 0; i < 3; i++) {
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true);
}
@@ -235,6 +237,7 @@ describe('Canvas Actions', () => {
it('should move node', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.zoomToFit();
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]);
@@ -316,6 +319,7 @@ describe('Canvas Actions', () => {
it('should select nodes using arrow keys', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.wait(500);
cy.get('body').type('{leftArrow}');
@@ -326,6 +330,7 @@ describe('Canvas Actions', () => {
it('should select nodes using shift and arrow keys', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.wait(500);
cy.get('body').type('{shift}', { release: false }).type('{leftArrow}');
@@ -334,6 +339,7 @@ describe('Canvas Actions', () => {
it('should delete connections by pressing the delete button', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters.nodeConnections().first().realHover();
cy.get('.connection-actions .delete').first().click({ force: true });
@@ -342,6 +348,7 @@ describe('Canvas Actions', () => {
it('should delete a connection by moving it away from endpoint', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.drag(WorkflowPage.getters.getEndpointSelector('input', CODE_NODE_NAME), [0, -100]);
WorkflowPage.getters.nodeConnections().should('have.length', 0);
@@ -349,6 +356,7 @@ describe('Canvas Actions', () => {
it('should disable node by pressing the disable button', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters
.canvasNodes()
@@ -360,6 +368,7 @@ describe('Canvas Actions', () => {
it('should disable node using keyboard shortcut', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters.canvasNodes().last().click();
WorkflowPage.actions.hitDisableNodeShortcut();
@@ -368,6 +377,7 @@ describe('Canvas Actions', () => {
it('should disable multiple nodes', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.get('body').type('{esc}');
cy.get('body').type('{esc}');
@@ -389,6 +399,7 @@ describe('Canvas Actions', () => {
it('should duplicate node', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters
.canvasNodes()
diff --git a/cypress/e2e/13-pinning.cy.ts b/cypress/e2e/13-pinning.cy.ts
index 0b5f7b2683..f21320ca9c 100644
--- a/cypress/e2e/13-pinning.cy.ts
+++ b/cypress/e2e/13-pinning.cy.ts
@@ -12,8 +12,7 @@ describe('Data pinning', () => {
});
it('Should be able to pin node output', () => {
- workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger');
- workflowPage.getters.canvasNodes().first().dblclick();
+ workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true});
ndv.getters.container().should('be.visible');
ndv.getters.pinDataButton().should('not.exist');
ndv.getters.editPinnedDataButton().should('be.visible');
@@ -21,7 +20,9 @@ describe('Data pinning', () => {
ndv.actions.execute();
ndv.getters.outputDataContainer().should('be.visible');
- ndv.getters.outputDataContainer().get('table').should('be.visible');
+ // We hover over the table to get rid of the pinning tooltip which would overlay the table
+ // slightly and cause the test to fail
+ ndv.getters.outputDataContainer().get('table').realHover().should('be.visible');
ndv.getters.outputTableRows().should('have.length', 2);
ndv.getters.outputTableHeaders().should('have.length.at.least', 10);
ndv.getters.outputTableHeaders().first().should('include.text', 'timestamp');
@@ -42,8 +43,7 @@ describe('Data pinning', () => {
});
it('Should be be able to set pinned data', () => {
- workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger');
- workflowPage.getters.canvasNodes().first().dblclick();
+ workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true});
ndv.getters.container().should('be.visible');
ndv.getters.pinDataButton().should('not.exist');
ndv.getters.editPinnedDataButton().should('be.visible');
diff --git a/cypress/e2e/14-data-transformation-expressions.cy.ts b/cypress/e2e/14-data-transformation-expressions.cy.ts
index 6f6b802926..a5186a8af5 100644
--- a/cypress/e2e/14-data-transformation-expressions.cy.ts
+++ b/cypress/e2e/14-data-transformation-expressions.cy.ts
@@ -26,7 +26,8 @@ describe('Data transformation expressions', () => {
ndv.getters.inlineExpressionEditorInput().clear().type(input);
ndv.actions.execute();
- ndv.getters.outputDataContainer().should('be.visible').contains(output);
+ ndv.getters.outputDataContainer().should('be.visible')
+ ndv.getters.outputDataContainer().contains(output);
});
it('$json + n8n string methods', () => {
@@ -40,7 +41,8 @@ describe('Data transformation expressions', () => {
ndv.getters.inlineExpressionEditorInput().clear().type(input);
ndv.actions.execute();
- ndv.getters.outputDataContainer().should('be.visible').contains(output);
+ ndv.getters.outputDataContainer().should('be.visible')
+ ndv.getters.outputDataContainer().contains(output);
});
it('$json + native numeric methods', () => {
@@ -54,7 +56,8 @@ describe('Data transformation expressions', () => {
ndv.getters.inlineExpressionEditorInput().clear().type(input);
ndv.actions.execute();
- ndv.getters.outputDataContainer().should('be.visible').contains(output);
+ ndv.getters.outputDataContainer().should('be.visible')
+ ndv.getters.outputDataContainer().contains(output);
});
it('$json + n8n numeric methods', () => {
@@ -68,7 +71,8 @@ describe('Data transformation expressions', () => {
ndv.getters.inlineExpressionEditorInput().clear().type(input);
ndv.actions.execute();
- ndv.getters.outputDataContainer().should('be.visible').contains(output);
+ ndv.getters.outputDataContainer().should('be.visible')
+ ndv.getters.outputDataContainer().contains(output);
});
it('$json + native array methods', () => {
@@ -82,7 +86,8 @@ describe('Data transformation expressions', () => {
ndv.getters.inlineExpressionEditorInput().clear().type(input);
ndv.actions.execute();
- ndv.getters.outputDataContainer().should('be.visible').contains(output);
+ ndv.getters.outputDataContainer().should('be.visible')
+ ndv.getters.outputDataContainer().contains(output);
});
it('$json + n8n array methods', () => {
diff --git a/cypress/e2e/14-mapping.cy.ts b/cypress/e2e/14-mapping.cy.ts
index ade387e454..846eafb6e5 100644
--- a/cypress/e2e/14-mapping.cy.ts
+++ b/cypress/e2e/14-mapping.cy.ts
@@ -1,8 +1,12 @@
-import { WorkflowPage, NDV, CanvasNode } from '../pages';
+import {
+ MANUAL_TRIGGER_NODE_NAME,
+ MANUAL_TRIGGER_NODE_DISPLAY_NAME,
+ SCHEDULE_TRIGGER_NODE_NAME,
+} from './../constants';
+import { WorkflowPage, NDV } from '../pages';
const workflowPage = new WorkflowPage();
const ndv = new NDV();
-const canvasNode = new CanvasNode();
describe('Data mapping', () => {
beforeEach(() => {
@@ -20,7 +24,7 @@ describe('Data mapping', () => {
cy.fixture('Test_workflow-actions_paste-data.json').then((data) => {
cy.get('body').paste(JSON.stringify(data));
});
- canvasNode.actions.openNode('Set');
+ workflowPage.actions.openNode('Set');
ndv.actions.executePrevious();
ndv.actions.switchInputMode('Table');
ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist');
@@ -42,7 +46,7 @@ describe('Data mapping', () => {
cy.get('body').paste(JSON.stringify(data));
});
- canvasNode.actions.openNode('Set');
+ workflowPage.actions.openNode('Set');
ndv.actions.switchInputMode('Table');
ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist');
@@ -87,7 +91,7 @@ describe('Data mapping', () => {
cy.get('body').paste(JSON.stringify(data));
});
- canvasNode.actions.openNode('Set');
+ workflowPage.actions.openNode('Set');
ndv.actions.switchInputMode('JSON');
ndv.getters.inputDataContainer().should('exist').find('.json-data')
@@ -115,7 +119,7 @@ describe('Data mapping', () => {
cy.get('body').paste(JSON.stringify(data));
});
- canvasNode.actions.openNode('Set');
+ workflowPage.actions.openNode('Set');
ndv.actions.clearParameterInput('value');
cy.get('body').type('{esc}');
@@ -142,22 +146,22 @@ describe('Data mapping', () => {
it('maps expressions from previous nodes', () => {
cy.createFixtureWorkflow('Test_workflow_3.json', `My test workflow`);
- canvasNode.actions.openNode('Set1');
+ workflowPage.actions.openNode('Set1');
- ndv.actions.selectInputNode('Schedule Trigger');
+ ndv.actions.selectInputNode(SCHEDULE_TRIGGER_NODE_NAME);
ndv.getters.inputDataContainer()
.find('span').contains('count')
.realMouseDown();
ndv.actions.mapToParameter('value');
- ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $node["Schedule Trigger"].json.input[0].count }}');
+ ndv.getters.inlineExpressionEditorInput().should('have.text', `{{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input[0].count }}`);
ndv.getters.parameterExpressionPreview('value')
.should('not.exist');
ndv.actions.switchInputMode('Table');
ndv.actions.mapDataFromHeader(1, 'value');
- ndv.getters.inlineExpressionEditorInput().should('have.text', '{{ $node["Schedule Trigger"].json.input[0].count }} {{ $node["Schedule Trigger"].json.input }}');
+ ndv.getters.inlineExpressionEditorInput().should('have.text', `{{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input[0].count }} {{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input }}`);
ndv.getters.parameterExpressionPreview('value')
.should('not.exist');
@@ -175,8 +179,9 @@ describe('Data mapping', () => {
});
it('maps keys to path', () => {
- workflowPage.actions.addInitialNodeToCanvas('Manual Trigger', {keepNdvOpen: true});
-
+ workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
+ workflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
+ workflowPage.actions.openNode(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
ndv.actions.setPinnedData([
{
input: [
@@ -201,7 +206,7 @@ describe('Data mapping', () => {
ndv.actions.close();
workflowPage.actions.addNodeToCanvas('Item Lists');
- canvasNode.actions.openNode('Item Lists');
+ workflowPage.actions.openNode('Item Lists');
ndv.getters.parameterInput('operation')
.click()
diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts
index 0249ab0bbd..2ab6473702 100644
--- a/cypress/e2e/2-credentials.cy.ts
+++ b/cypress/e2e/2-credentials.cy.ts
@@ -253,7 +253,7 @@ describe('Credentials', () => {
it('should render custom node with n8n credential', () => {
workflowPage.actions.visit();
- workflowPage.actions.addNodeToCanvas('Manual Trigger');
+ workflowPage.actions.addNodeToCanvas('Manual');
workflowPage.actions.addNodeToCanvas('E2E Node with native n8n credential', true, true);
workflowPage.getters.nodeCredentialsLabel().click();
cy.contains('Create New Credential').click();
@@ -263,7 +263,7 @@ describe('Credentials', () => {
it('should render custom node with custom credential', () => {
workflowPage.actions.visit();
- workflowPage.actions.addNodeToCanvas('Manual Trigger');
+ workflowPage.actions.addNodeToCanvas('Manual');
workflowPage.actions.addNodeToCanvas('E2E Node with custom credential', true, true);
workflowPage.getters.nodeCredentialsLabel().click();
cy.contains('Create New Credential').click();
diff --git a/cypress/e2e/4-node-creator.cy.ts b/cypress/e2e/4-node-creator.cy.ts
index e70fddc777..4f43684c93 100644
--- a/cypress/e2e/4-node-creator.cy.ts
+++ b/cypress/e2e/4-node-creator.cy.ts
@@ -1,13 +1,16 @@
import { NodeCreator } from '../pages/features/node-creator';
-import { INodeTypeDescription } from 'n8n-workflow';
import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants';
import { randFirstName, randLastName } from '@ngneat/falso';
+import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
+import { NDV } from '../pages/ndv';
const email = DEFAULT_USER_EMAIL;
const password = DEFAULT_USER_PASSWORD;
const firstName = randFirstName();
const lastName = randLastName();
const nodeCreatorFeature = new NodeCreator();
+const WorkflowPage = new WorkflowPageClass();
+const NDVModal = new NDV();
describe('Node Creator', () => {
before(() => {
@@ -27,30 +30,22 @@ describe('Node Creator', () => {
nodeCreatorFeature.getters
.nodeCreator()
- .contains('When should this workflow run?')
+ .contains('Select a trigger')
.should('be.visible');
- nodeCreatorFeature.getters.nodeCreatorTabs().should('not.exist');
- });
-
- it('should see all tabs when opening via plus button', () => {
- nodeCreatorFeature.actions.openNodeCreator();
- nodeCreatorFeature.getters.nodeCreatorTabs().should('exist');
- nodeCreatorFeature.getters.selectedTab().should('have.text', 'Trigger');
});
it('should navigate subcategory', () => {
nodeCreatorFeature.actions.openNodeCreator();
- nodeCreatorFeature.getters.getCreatorItem('On App Event').click();
- nodeCreatorFeature.getters.activeSubcategory().should('have.text', 'On App Event');
+ nodeCreatorFeature.getters.getCreatorItem('On app event').click();
+ nodeCreatorFeature.getters.activeSubcategory().should('have.text', 'On app event');
// Go back
nodeCreatorFeature.getters.activeSubcategory().find('button').click();
- nodeCreatorFeature.getters.activeSubcategory().should('not.exist');
+ nodeCreatorFeature.getters.activeSubcategory().should('not.have.text', 'On app event');
});
it('should search for nodes', () => {
nodeCreatorFeature.actions.openNodeCreator();
- nodeCreatorFeature.getters.selectedTab().should('have.text', 'Trigger');
nodeCreatorFeature.getters.searchBar().find('input').type('manual');
nodeCreatorFeature.getters.creatorItem().should('have.length', 1);
@@ -72,92 +67,63 @@ describe('Node Creator', () => {
nodeCreatorFeature.getters.creatorItem().should('have.length', 0);
nodeCreatorFeature.getters.searchBar().find('input').clear();
- nodeCreatorFeature.getters.getCreatorItem('On App Event').click();
+ nodeCreatorFeature.getters.getCreatorItem('On app event').click();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('edit image');
- nodeCreatorFeature.getters.creatorItem().should('have.length', 0);
- nodeCreatorFeature.getters
- .noResults()
- .should('exist')
- .should('contain.text', 'To see all results, click here');
-
- nodeCreatorFeature.getters.noResults().contains('click here').click();
- nodeCreatorFeature.getters.nodeCreatorTabs().should('exist');
+ nodeCreatorFeature.getters.getCreatorItem('Results in other categories (1)').should('exist');
+ nodeCreatorFeature.getters.creatorItem().should('have.length', 2);
nodeCreatorFeature.getters.getCreatorItem('Edit Image').should('exist');
- nodeCreatorFeature.getters.selectedTab().should('have.text', 'All');
- nodeCreatorFeature.getters.searchBar().find('button').click();
- nodeCreatorFeature.getters.searchBar().find('input').should('be.empty');
+ nodeCreatorFeature.getters.searchBar().find('input').clear().type('edit image123123');
+ nodeCreatorFeature.getters.creatorItem().should('have.length', 0);
});
- it('should add manual trigger node', () => {
+ it('should check correct view panels', () => {
nodeCreatorFeature.getters.canvasAddButton().click();
- nodeCreatorFeature.getters.getCreatorItem('Manually').click();
-
- // TODO: Replace once we have canvas feature utils
- cy.get('span').contains('Back to canvas').click();
+ WorkflowPage.actions.addNodeToCanvas('Manual', false);
nodeCreatorFeature.getters.canvasAddButton().should('not.be.visible');
nodeCreatorFeature.getters.nodeCreator().should('not.exist');
// TODO: Replace once we have canvas feature utils
- cy.get('div').contains('Add first step').should('exist');
+ cy.get('div').contains('Add first step').should('be.hidden');
+ nodeCreatorFeature.actions.openNodeCreator()
+ nodeCreatorFeature.getters
+ .nodeCreator()
+ .contains('What happens next?')
+ .should('be.visible');
+
+ nodeCreatorFeature.getters.getCreatorItem('Add another trigger').click();
+ nodeCreatorFeature.getters.nodeCreator().contains('Select a trigger').should('be.visible');
+ nodeCreatorFeature.getters.activeSubcategory().find('button').should('exist');
+ nodeCreatorFeature.getters.activeSubcategory().find('button').click();
+ nodeCreatorFeature.getters
+ .nodeCreator()
+ .contains('What happens next?')
+ .should('be.visible');
});
- it('check if non-core nodes are rendered', () => {
- cy.wait('@nodesIntercept').then((interception) => {
- const nodes = interception.response?.body as INodeTypeDescription[];
- const categorizedNodes = nodeCreatorFeature.actions.categorizeNodes(nodes);
- nodeCreatorFeature.actions.openNodeCreator();
- nodeCreatorFeature.actions.selectTab('All');
-
- const categories = Object.keys(categorizedNodes);
- categories.forEach((category: string) => {
- // Core Nodes contains subcategories which we'll test separately
- if (category === 'Core Nodes') return;
-
- nodeCreatorFeature.actions.toggleCategory(category);
-
- // Check if all nodes are present
- nodeCreatorFeature.getters.nodeItemName().then(($elements) => {
- const visibleNodes: string[] = [];
- $elements.each((_, element) => {
- visibleNodes.push(element.textContent?.trim() || '');
- });
- const visibleCategoryNodes = (categorizedNodes[category] as INodeTypeDescription[])
- .filter((node) => !node.hidden)
- .map((node) => node.displayName?.trim());
-
- cy.wrap(visibleCategoryNodes).each((categoryNode: string) => {
- expect(visibleNodes).to.include(categoryNode);
- });
- });
-
- nodeCreatorFeature.actions.toggleCategory(category);
- });
- });
- });
+ it('should add node to canvas from actions panel', () => {
+ const editImageNode = 'Edit Image';
+ nodeCreatorFeature.actions.openNodeCreator();
+ nodeCreatorFeature.getters.searchBar().find('input').clear().type(editImageNode);
+ nodeCreatorFeature.getters.getCreatorItem(editImageNode).click();
+ nodeCreatorFeature.getters.activeSubcategory().should('have.text', editImageNode);
+ nodeCreatorFeature.getters.getCreatorItem('Crop Image').click();
+ NDVModal.getters.parameterInput('operation').should('contain.text', 'Crop');
+ })
it('should render and select community node', () => {
cy.intercept('GET', '/types/nodes.json').as('nodesIntercept');
cy.wait('@nodesIntercept').then(() => {
- const customCategory = 'Custom Category';
const customNode = 'E2E Node';
- const customNodeDescription = 'Demonstrate rendering of node';
nodeCreatorFeature.actions.openNodeCreator();
- nodeCreatorFeature.actions.selectTab('All');
+ nodeCreatorFeature.getters.searchBar().find('input').clear().type(customNode);
- nodeCreatorFeature.getters.getCreatorItem(customCategory).should('exist');
-
- nodeCreatorFeature.actions.toggleCategory(customCategory);
nodeCreatorFeature.getters
.getCreatorItem(customNode)
.findChildByTestId('node-creator-item-tooltip')
.should('exist');
- nodeCreatorFeature.getters
- .getCreatorItem(customNode)
- .contains(customNodeDescription)
- .should('exist');
nodeCreatorFeature.actions.selectNode(customNode);
// TODO: Replace once we have canvas feature utils
diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts
index c0e12a6ba2..350ad3a185 100644
--- a/cypress/e2e/5-ndv.cy.ts
+++ b/cypress/e2e/5-ndv.cy.ts
@@ -17,7 +17,7 @@ describe('NDV', () => {
it('should show up when double clicked on a node and close when Back to canvas clicked', () => {
- workflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
+ workflowPage.actions.addInitialNodeToCanvas('Manual');
workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.container().should('be.visible');
ndv.getters.backToCanvas().click();
@@ -67,8 +67,8 @@ describe('NDV', () => {
});
it('should show validation errors only after blur or re-opening of NDV', () => {
- workflowPage.actions.addNodeToCanvas('Manual Trigger');
- workflowPage.actions.addNodeToCanvas('Airtable', true, true);
+ workflowPage.actions.addNodeToCanvas('Manual');
+ workflowPage.actions.addNodeToCanvas('Airtable', true, true, 'Read data from a table');
ndv.getters.container().should('be.visible');
cy.get('.has-issues').should('have.length', 0);
ndv.getters.parameterInput('table').find('input').eq(1).focus().blur();
diff --git a/cypress/e2e/6-code-node.cy.ts b/cypress/e2e/6-code-node.cy.ts
index b833eed003..540e4825a0 100644
--- a/cypress/e2e/6-code-node.cy.ts
+++ b/cypress/e2e/6-code-node.cy.ts
@@ -12,7 +12,7 @@ describe('Code node', () => {
it('should execute the placeholder in all-items mode successfully', () => {
WorkflowPage.actions.visit();
- WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
+ WorkflowPage.actions.addInitialNodeToCanvas('Manual');
WorkflowPage.actions.addNodeToCanvas('Code');
WorkflowPage.actions.openNode('Code');
@@ -23,7 +23,7 @@ describe('Code node', () => {
it('should execute the placeholder in each-item mode successfully', () => {
WorkflowPage.actions.visit();
- WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
+ WorkflowPage.actions.addInitialNodeToCanvas('Manual');
WorkflowPage.actions.addNodeToCanvas('Code');
WorkflowPage.actions.openNode('Code');
ndv.getters.parameterInput('mode').click();
diff --git a/cypress/e2e/8-http-request-node.cy.ts b/cypress/e2e/8-http-request-node.cy.ts
index eb4d93c171..cc62b5182f 100644
--- a/cypress/e2e/8-http-request-node.cy.ts
+++ b/cypress/e2e/8-http-request-node.cy.ts
@@ -14,7 +14,7 @@ describe('HTTP Request node', () => {
cy.visit(workflowsPage.url);
workflowsPage.actions.createWorkflowFromCard();
- workflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
+ workflowPage.actions.addInitialNodeToCanvas('Manual');
workflowPage.actions.addNodeToCanvas('HTTP Request');
workflowPage.actions.openNode('HTTP Request');
ndv.actions.typeIntoParameterInput('url', 'https://catfact.ninja/fact');
diff --git a/cypress/e2e/9-expression-editor-modal.cy.ts b/cypress/e2e/9-expression-editor-modal.cy.ts
index 1a8909872d..957c0505f5 100644
--- a/cypress/e2e/9-expression-editor-modal.cy.ts
+++ b/cypress/e2e/9-expression-editor-modal.cy.ts
@@ -10,13 +10,14 @@ describe('Expression editor modal', () => {
beforeEach(() => {
WorkflowPage.actions.visit();
- WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
+ WorkflowPage.actions.addInitialNodeToCanvas('Manual');
WorkflowPage.actions.addNodeToCanvas('Hacker News');
WorkflowPage.actions.openNode('Hacker News');
WorkflowPage.actions.openExpressionEditorModal();
});
it('should resolve primitive resolvables', () => {
+ WorkflowPage.getters.expressionModalInput().clear();
WorkflowPage.getters.expressionModalInput().type('{{ 1 + 2');
WorkflowPage.getters.expressionModalOutput().contains(/^3$/);
WorkflowPage.getters.expressionModalInput().clear();
@@ -31,6 +32,7 @@ describe('Expression editor modal', () => {
});
it('should resolve object resolvables', () => {
+ WorkflowPage.getters.expressionModalInput().clear();
WorkflowPage.getters
.expressionModalInput()
.type('{{ { a : 1 }', { parseSpecialCharSequences: false });
@@ -45,6 +47,7 @@ describe('Expression editor modal', () => {
});
it('should resolve array resolvables', () => {
+ WorkflowPage.getters.expressionModalInput().clear();
WorkflowPage.getters.expressionModalInput().type('{{ [1, 2, 3]');
WorkflowPage.getters.expressionModalOutput().contains(/^\[Array: \[1,2,3\]\]$/);
@@ -55,7 +58,8 @@ describe('Expression editor modal', () => {
});
it('should resolve $parameter[]', () => {
+ WorkflowPage.getters.expressionModalInput().clear();
WorkflowPage.getters.expressionModalInput().type('{{ $parameter["operation"]');
- WorkflowPage.getters.expressionModalOutput().contains(/^get$/);
+ WorkflowPage.getters.expressionModalOutput().contains(/^getAll$/);
});
});
diff --git a/cypress/pages/canvas-node.ts b/cypress/pages/canvas-node.ts
deleted file mode 100644
index b0d0032320..0000000000
--- a/cypress/pages/canvas-node.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { BasePage } from './base';
-
-export class CanvasNode extends BasePage {
- getters = {
- nodes: () => cy.getByTestId('canvas-node'),
- nodeByName: (nodeName: string) =>
- this.getters.nodes().filter(`:contains("${nodeName}")`),
- };
-
- actions = {
- openNode: (nodeName: string) => {
- this.getters.nodeByName(nodeName).eq(0).dblclick();
- },
- };
-}
diff --git a/cypress/pages/features/node-creator.ts b/cypress/pages/features/node-creator.ts
index 7db317a48e..0899b188d5 100644
--- a/cypress/pages/features/node-creator.ts
+++ b/cypress/pages/features/node-creator.ts
@@ -31,9 +31,6 @@ export class NodeCreator extends BasePage {
selectNode: (displayName: string) => {
this.getters.getCreatorItem(displayName).click();
},
- selectTab: (tab: string) => {
- this.getters.nodeCreatorTabs().contains(tab).click();
- },
toggleCategory: (category: string) => {
this.getters.getCreatorItem(category).click();
},
diff --git a/cypress/pages/index.ts b/cypress/pages/index.ts
index 7b4c1e0931..35ef30d5ec 100644
--- a/cypress/pages/index.ts
+++ b/cypress/pages/index.ts
@@ -9,4 +9,3 @@ export * from './settings-users';
export * from './settings-log-streaming';
export * from './sidebar';
export * from './ndv';
-export * from './canvas-node';
diff --git a/cypress/pages/ndv.ts b/cypress/pages/ndv.ts
index 9cc0285a4f..94b648ccf4 100644
--- a/cypress/pages/ndv.ts
+++ b/cypress/pages/ndv.ts
@@ -19,7 +19,7 @@ export class NDV extends BasePage {
digital: () => cy.getByTestId('ndv-run-data-display-mode'),
pinDataButton: () => cy.getByTestId('ndv-pin-data'),
editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'),
- pinnedDataEditor: () => this.getters.outputPanel().find('.monaco-editor'),
+ pinnedDataEditor: () => this.getters.outputPanel().find('.monaco-editor[role=code]'),
runDataPaneHeader: () => cy.getByTestId('run-data-pane-header'),
savePinnedDataButton: () => this.getters.runDataPaneHeader().find('button').contains('Save'),
outputTableRows: () => this.getters.outputDataContainer().find('table tr'),
@@ -71,10 +71,9 @@ export class NDV extends BasePage {
setPinnedData: (data: object) => {
this.getters.editPinnedDataButton().click();
- const editor = this.getters.pinnedDataEditor();
- editor.click();
- editor.type(`{selectall}{backspace}`);
- editor.type(JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}'));
+ this.getters.pinnedDataEditor().click();
+ this.getters.pinnedDataEditor().type(`{selectall}{backspace}`);
+ this.getters.pinnedDataEditor().type(JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}'));
this.actions.savePinnedData();
},
diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts
index bf150e038a..2094726db8 100644
--- a/cypress/pages/workflow.ts
+++ b/cypress/pages/workflow.ts
@@ -24,7 +24,7 @@ export class WorkflowPage extends BasePage {
canvasPlusButton: () => cy.getByTestId('canvas-plus-button'),
canvasNodes: () => cy.getByTestId('canvas-node'),
canvasNodeByName: (nodeName: string) =>
- this.getters.canvasNodes().filter(`:contains("${nodeName}")`),
+ this.getters.canvasNodes().filter(`:contains(${nodeName})`),
getEndpointSelector: (type: 'input' | 'output' | 'plus', nodeName: string, index = 0) => {
return `[data-endpoint-name='${nodeName}'][data-endpoint-type='${type}'][data-input-index='${index}']`;
},
@@ -124,6 +124,7 @@ export class WorkflowPage extends BasePage {
nodeDisplayName: string,
plusButtonClick = true,
preventNdvClose?: boolean,
+ action?: string,
) => {
if (plusButtonClick) {
this.getters.nodeCreatorPlusButton().click();
@@ -131,11 +132,21 @@ export class WorkflowPage extends BasePage {
this.getters.nodeCreatorSearchBar().type(nodeDisplayName);
this.getters.nodeCreatorSearchBar().type('{enter}');
+ cy.wait(500)
+ cy.get('body').then((body) => {
+ if(body.find('[data-test-id=node-creator]').length > 0) {
+ if(action) {
+ cy.contains(action).click()
+ } else {
+ cy.getByTestId('item-iterator-item').eq(1).click()
+ }
+ }
+ })
if (!preventNdvClose) cy.get('body').type('{esc}');
},
openNode: (nodeTypeName: string) => {
- this.getters.canvasNodeByName(nodeTypeName).dblclick();
+ this.getters.canvasNodeByName(nodeTypeName).first().dblclick();
},
openExpressionEditorModal: () => {
cy.contains('Expression').invoke('show').click();
diff --git a/packages/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue b/packages/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue
index 4e0aef14a9..ebac11ac37 100644
--- a/packages/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue
+++ b/packages/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue
@@ -13,7 +13,7 @@
-
+
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
-import TriggerIcon from './TriggerIcon.vue';
import N8nTooltip from '../N8nTooltip';
export interface Props {
@@ -60,14 +59,14 @@ defineEmits<{
align-items: center;
cursor: pointer;
z-index: 1;
- padding: 11px 8px 11px 0;
+ padding: var(--spacing-xs) var(--spacing-2xs) var(--spacing-xs) 0;
&.hasAction {
user-select: none;
}
}
.creatorNode:hover .panelIcon {
- color: var(--color-text-light);
+ color: var(--action-arrow-color-hover, var(--color-text-light));
}
.panelIcon {
@@ -76,7 +75,7 @@ defineEmits<{
justify-content: flex-end;
align-items: center;
margin-left: var(--spacing-2xs);
- color: var(--color-text-lighter);
+ color: var(--action-arrow-color, var(--color-text-lighter));
cursor: pointer;
background: transparent;
border: none;
@@ -110,11 +109,12 @@ defineEmits<{
font-size: var(--font-size-2xs);
line-height: 1rem;
font-weight: 400;
- color: var(--node-creator-description-colo, var(--color-text-base));
+ color: var(--node-creator-description-colos, var(--color-text-base));
}
.triggerIcon {
- margin-left: var(--spacing-2xs);
+ margin-left: var(--spacing-3xs);
+ color: var(--color-primary);
}
diff --git a/packages/design-system/src/components/N8nNodeCreatorNode/TriggerIcon.vue b/packages/design-system/src/components/N8nNodeCreatorNode/TriggerIcon.vue
deleted file mode 100644
index dda6b227e7..0000000000
--- a/packages/design-system/src/components/N8nNodeCreatorNode/TriggerIcon.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/packages/design-system/src/components/N8nNodeIcon/NodeIcon.vue b/packages/design-system/src/components/N8nNodeIcon/NodeIcon.vue
index 80948018a3..5520a1870d 100644
--- a/packages/design-system/src/components/N8nNodeIcon/NodeIcon.vue
+++ b/packages/design-system/src/components/N8nNodeIcon/NodeIcon.vue
@@ -12,7 +12,7 @@
{{ nodeTypeName }}
-
+
@@ -103,11 +103,11 @@ export default Vue.extend({
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue b/packages/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
index bd73ced378..77415e2f06 100644
--- a/packages/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
+++ b/packages/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
@@ -14,20 +14,17 @@
@mouseup="onMouseUp"
data-test-id="node-creator"
>
-
+
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/TriggerHelperPanel.vue b/packages/editor-ui/src/components/Node/NodeCreator/TriggerHelperPanel.vue
deleted file mode 100644
index 3ce89f63d1..0000000000
--- a/packages/editor-ui/src/components/Node/NodeCreator/TriggerHelperPanel.vue
+++ /dev/null
@@ -1,449 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/TypeSelector.vue b/packages/editor-ui/src/components/Node/NodeCreator/TypeSelector.vue
deleted file mode 100644
index 38a28670df..0000000000
--- a/packages/editor-ui/src/components/Node/NodeCreator/TypeSelector.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/ViewItem.vue b/packages/editor-ui/src/components/Node/NodeCreator/ViewItem.vue
new file mode 100644
index 0000000000..b331301137
--- /dev/null
+++ b/packages/editor-ui/src/components/Node/NodeCreator/ViewItem.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/useMainPanelView.ts b/packages/editor-ui/src/components/Node/NodeCreator/useMainPanelView.ts
new file mode 100644
index 0000000000..05ef090da0
--- /dev/null
+++ b/packages/editor-ui/src/components/Node/NodeCreator/useMainPanelView.ts
@@ -0,0 +1,198 @@
+import { getCurrentInstance, computed } from 'vue';
+import {
+ CORE_NODES_CATEGORY,
+ WEBHOOK_NODE_TYPE,
+ OTHER_TRIGGER_NODES_SUBCATEGORY,
+ EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE,
+ MANUAL_TRIGGER_NODE_TYPE,
+ SCHEDULE_TRIGGER_NODE_TYPE,
+ REGULAR_NODE_FILTER,
+ TRANSFORM_DATA_SUBCATEGORY,
+ FILES_SUBCATEGORY,
+ FLOWS_CONTROL_SUBCATEGORY,
+ HELPERS_SUBCATEGORY,
+ TRIGGER_NODE_FILTER,
+} from '@/constants';
+import { useNodeCreatorStore } from '@/stores/nodeCreator';
+
+export default () => {
+ const instance = getCurrentInstance();
+ const nodeCreatorStore = useNodeCreatorStore();
+
+ const VIEWS = [
+ {
+ value: REGULAR_NODE_FILTER,
+ title: instance?.proxy.$locale.baseText('nodeCreator.triggerHelperPanel.whatHappensNext'),
+ items: [
+ {
+ key: '*',
+ type: 'subcategory',
+ properties: {
+ subcategory: 'App Regular Nodes',
+ icon: 'globe',
+ },
+ },
+ {
+ type: 'subcategory',
+ key: TRANSFORM_DATA_SUBCATEGORY,
+ category: CORE_NODES_CATEGORY,
+ properties: {
+ subcategory: TRANSFORM_DATA_SUBCATEGORY,
+ icon: 'pen',
+ },
+ },
+ {
+ type: 'subcategory',
+ key: HELPERS_SUBCATEGORY,
+ category: CORE_NODES_CATEGORY,
+ properties: {
+ subcategory: HELPERS_SUBCATEGORY,
+ icon: 'toolbox',
+ },
+ },
+ {
+ type: 'subcategory',
+ key: FLOWS_CONTROL_SUBCATEGORY,
+ category: CORE_NODES_CATEGORY,
+ properties: {
+ subcategory: FLOWS_CONTROL_SUBCATEGORY,
+ icon: 'code-branch',
+ },
+ },
+ {
+ type: 'subcategory',
+ key: FILES_SUBCATEGORY,
+ category: CORE_NODES_CATEGORY,
+ properties: {
+ subcategory: FILES_SUBCATEGORY,
+ icon: 'file-alt',
+ },
+ },
+ {
+ key: TRIGGER_NODE_FILTER,
+ type: 'view',
+ properties: {
+ title: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.addAnotherTrigger',
+ ),
+ icon: 'bolt',
+ withTopBorder: true,
+ description: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.addAnotherTriggerDescription',
+ ),
+ },
+ },
+ ],
+ },
+ {
+ value: TRIGGER_NODE_FILTER,
+ title: instance?.proxy.$locale.baseText('nodeCreator.triggerHelperPanel.selectATrigger'),
+ description: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.selectATriggerDescription',
+ ),
+ items: [
+ {
+ key: '*',
+ type: 'subcategory',
+ properties: {
+ subcategory: 'App Trigger Nodes',
+ icon: 'satellite-dish',
+ },
+ },
+ {
+ key: SCHEDULE_TRIGGER_NODE_TYPE,
+ type: 'node',
+ category: [CORE_NODES_CATEGORY],
+ properties: {
+ nodeType: {
+ group: [],
+ name: SCHEDULE_TRIGGER_NODE_TYPE,
+ displayName: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.scheduleTriggerDisplayName',
+ ),
+ description: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.scheduleTriggerDescription',
+ ),
+ icon: 'fa:clock',
+ },
+ },
+ },
+ {
+ key: WEBHOOK_NODE_TYPE,
+ type: 'node',
+ category: [CORE_NODES_CATEGORY],
+ properties: {
+ nodeType: {
+ group: [],
+ name: WEBHOOK_NODE_TYPE,
+ displayName: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.webhookTriggerDisplayName',
+ ),
+ description: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.webhookTriggerDescription',
+ ),
+ iconData: {
+ type: 'file',
+ icon: 'webhook',
+ fileBuffer: '/static/webhook-icon.svg',
+ },
+ },
+ },
+ },
+ {
+ key: MANUAL_TRIGGER_NODE_TYPE,
+ type: 'node',
+ category: [CORE_NODES_CATEGORY],
+ properties: {
+ nodeType: {
+ group: [],
+ name: MANUAL_TRIGGER_NODE_TYPE,
+ displayName: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.manualTriggerDisplayName',
+ ),
+ description: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.manualTriggerDescription',
+ ),
+ icon: 'fa:mouse-pointer',
+ },
+ },
+ },
+ {
+ key: EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE,
+ type: 'node',
+ category: [CORE_NODES_CATEGORY],
+ properties: {
+ nodeType: {
+ group: [],
+ name: EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE,
+ displayName: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.workflowTriggerDisplayName',
+ ),
+ description: instance?.proxy.$locale.baseText(
+ 'nodeCreator.triggerHelperPanel.workflowTriggerDescription',
+ ),
+ icon: 'fa:sign-out-alt',
+ },
+ },
+ },
+ {
+ type: 'subcategory',
+ key: OTHER_TRIGGER_NODES_SUBCATEGORY,
+ category: CORE_NODES_CATEGORY,
+ properties: {
+ subcategory: OTHER_TRIGGER_NODES_SUBCATEGORY,
+ icon: 'folder-open',
+ },
+ },
+ ],
+ },
+ ];
+
+ const activeView = computed(() => {
+ return VIEWS.find((v) => v.value === nodeCreatorStore.selectedView) || VIEWS[0];
+ });
+
+ return {
+ activeView,
+ };
+};
diff --git a/packages/editor-ui/src/components/PersonalizationModal.vue b/packages/editor-ui/src/components/PersonalizationModal.vue
index b0606f5209..c4c4c6f399 100644
--- a/packages/editor-ui/src/components/PersonalizationModal.vue
+++ b/packages/editor-ui/src/components/PersonalizationModal.vue
@@ -1,12 +1,8 @@
-
-
![]()
-
{{ $locale.baseText('personalizationModal.lookOutForThingsMarked') }}
-
-
+
-
@@ -156,7 +141,6 @@ export default mixins(showMessage, workflowHelpers).extend({
name: 'PersonalizationModal',
data() {
return {
- submitted: false,
isSaving: false,
PERSONALIZATION_MODAL_KEY,
otherWorkAreaFieldVisible: false,
@@ -646,12 +630,12 @@ export default mixins(showMessage, workflowHelpers).extend({
}
await this.fetchOnboardingPrompt();
- this.submitted = true;
} catch (e) {
this.$showError(e, 'Error while submitting results');
}
this.$data.isSaving = false;
+ this.closeDialog();
},
async fetchOnboardingPrompt() {
if (
@@ -695,17 +679,4 @@ export default mixins(showMessage, workflowHelpers).extend({
margin-bottom: var(--spacing-m);
}
}
-
-.submittedContainer {
- * {
- margin-bottom: var(--spacing-2xs);
- }
-}
-
-.demoImage {
- border-radius: var(--border-radius-large);
- border: var(--border-base);
- width: 100%;
- height: 140px;
-}
diff --git a/packages/editor-ui/src/components/__tests__/__snapshots__/PersonalizationModal.spec.ts.snap b/packages/editor-ui/src/components/__tests__/__snapshots__/PersonalizationModal.spec.ts.snap
index 02fffa3046..6468a9a5c8 100644
--- a/packages/editor-ui/src/components/__tests__/__snapshots__/PersonalizationModal.spec.ts.snap
+++ b/packages/editor-ui/src/components/__tests__/__snapshots__/PersonalizationModal.spec.ts.snap
@@ -274,7 +274,7 @@ exports[`PersonalizationModal.vue > should render correctly 1`] = `
diff --git a/packages/editor-ui/src/constants.ts b/packages/editor-ui/src/constants.ts
index 4a3f2f17ec..0a11af64c5 100644
--- a/packages/editor-ui/src/constants.ts
+++ b/packages/editor-ui/src/constants.ts
@@ -103,6 +103,7 @@ export const JIRA_TRIGGER_NODE_TYPE = 'n8n-nodes-base.jiraTrigger';
export const MICROSOFT_EXCEL_NODE_TYPE = 'n8n-nodes-base.microsoftExcel';
export const MANUAL_TRIGGER_NODE_TYPE = 'n8n-nodes-base.manualTrigger';
export const MICROSOFT_TEAMS_NODE_TYPE = 'n8n-nodes-base.microsoftTeams';
+export const N8N_NODE_TYPE = 'n8n-nodes-base.n8n';
export const NO_OP_NODE_TYPE = 'n8n-nodes-base.noOp';
export const STICKY_NODE_TYPE = 'n8n-nodes-base.stickyNote';
export const NOTION_TRIGGER_NODE_TYPE = 'n8n-nodes-base.notionTrigger';
@@ -168,6 +169,10 @@ export const UNCATEGORIZED_CATEGORY = 'Miscellaneous';
export const UNCATEGORIZED_SUBCATEGORY = 'Helpers';
export const PERSONALIZED_CATEGORY = 'Suggested Nodes';
export const OTHER_TRIGGER_NODES_SUBCATEGORY = 'Other Trigger Nodes';
+export const TRANSFORM_DATA_SUBCATEGORY = 'Data Transformation';
+export const FILES_SUBCATEGORY = 'Files';
+export const FLOWS_CONTROL_SUBCATEGORY = 'Flow';
+export const HELPERS_SUBCATEGORY = 'Helpers';
export const REQUEST_NODE_FORM_URL = 'https://n8n-community.typeform.com/to/K1fBVTZ3';
diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json
index df4cb84fe5..4b01c2260d 100644
--- a/packages/editor-ui/src/plugins/i18n/locales/en.json
+++ b/packages/editor-ui/src/plugins/i18n/locales/en.json
@@ -677,10 +677,10 @@
"node.discovery.pinData.canvas": "You can pin this output instead of waiting for a test event. Open node to do so.",
"node.discovery.pinData.ndv": "You can pin this output instead of waiting for a test event.",
"nodeBase.clickToAddNodeOrDragToConnect": "Click to add node
or drag to connect",
- "nodeCreator.actionsCategory.operations": "Operations",
+ "nodeCreator.actionsCategory.actions": "Actions",
"nodeCreator.actionsCategory.onNewEvent": "On new {event} event",
"nodeCreator.actionsCategory.onEvent": "On {event}",
- "nodeCreator.actionsCategory.recommended": "Recommended",
+ "nodeCreator.actionsCategory.triggers": "Triggers",
"nodeCreator.actionsCategory.searchActions": "Search {nodeNameTitle} Actions...",
"nodeCreator.actionsList.apiCall": "Didn't find the right event? Make a custom {nodeNameTitle} API call",
"nodeCreator.actionsList.apiCallNoResult": "Nothing found — try making a custom {nodeNameTitle} API call",
@@ -712,26 +712,33 @@
"nodeCreator.noResults.webhook": "Webhook",
"nodeCreator.searchBar.searchNodes": "Search nodes...",
"nodeCreator.subcategoryDescriptions.appTriggerNodes": "Runs the flow when something happens in an app like Telegram, Notion or Airtable",
- "nodeCreator.subcategoryDescriptions.dataTransformation": "Manipulate data fields, run code",
- "nodeCreator.subcategoryDescriptions.files": "Work with CSV, XML, text, images etc.",
- "nodeCreator.subcategoryDescriptions.flow": "Branches, core triggers, merge data",
- "nodeCreator.subcategoryDescriptions.helpers": "HTTP Requests (API calls), date and time, scrape HTML",
- "nodeCreator.subcategoryDescriptions.otherTriggerNodes": "Runs the flow on workflow errors, file changes, and more",
- "nodeCreator.subcategoryNames.appTriggerNodes": "On App Event",
- "nodeCreator.subcategoryNames.dataTransformation": "Data Transformation",
+ "nodeCreator.subcategoryDescriptions.appRegularNodes": "Do something in an app or service like Google Sheets, Telegram or Notion",
+ "nodeCreator.subcategoryDescriptions.dataTransformation": "Manipulate data, run JavaScript code, etc.",
+ "nodeCreator.subcategoryDescriptions.files": "CSV, XLS, XML, text, images, etc.",
+ "nodeCreator.subcategoryDescriptions.flow": "IF, Switch, Wait, Compare and Merge data, etc.",
+ "nodeCreator.subcategoryDescriptions.helpers": "HTTP Requests (API Calls), date and time, scrape HTML, RSS, SSH, etc.",
+ "nodeCreator.subcategoryDescriptions.otherTriggerNodes": "Runs the flow on workflow errors, file changes, etc.",
+ "nodeCreator.subcategoryNames.appTriggerNodes": "On app event",
+ "nodeCreator.subcategoryNames.appRegularNodes": "Action in an app",
+ "nodeCreator.subcategoryNames.dataTransformation": "Data transformation",
"nodeCreator.subcategoryNames.files": "Files",
"nodeCreator.subcategoryNames.flow": "Flow",
"nodeCreator.subcategoryNames.helpers": "Helpers",
"nodeCreator.subcategoryNames.otherTriggerNodes": "Other ways...",
"nodeCreator.subcategoryTitles.otherTriggerNodes": "Other Trigger Nodes",
+ "nodeCreator.triggerHelperPanel.addAnotherTrigger": "Add another trigger",
+ "nodeCreator.triggerHelperPanel.addAnotherTriggerDescription": "Triggers start your workflow. Workflows can have multiple triggers.",
"nodeCreator.triggerHelperPanel.title": "When should this workflow run?",
- "nodeCreator.triggerHelperPanel.scheduleTriggerDisplayName": "On a Schedule",
+ "nodeCreator.triggerHelperPanel.scheduleTriggerDisplayName": "On a schedule",
"nodeCreator.triggerHelperPanel.scheduleTriggerDescription": "Runs the flow every day, hour, or custom interval",
- "nodeCreator.triggerHelperPanel.webhookTriggerDisplayName": "On Webhook Call",
+ "nodeCreator.triggerHelperPanel.webhookTriggerDisplayName": "On webhook call",
"nodeCreator.triggerHelperPanel.webhookTriggerDescription": "Runs the flow when another app sends a webhook",
"nodeCreator.triggerHelperPanel.manualTriggerDisplayName": "Manually",
"nodeCreator.triggerHelperPanel.manualTriggerDescription": "Runs the flow on clicking a button in n8n",
- "nodeCreator.triggerHelperPanel.workflowTriggerDisplayName": "When Called By Another Workflow",
+ "nodeCreator.triggerHelperPanel.whatHappensNext": "What happens next?",
+ "nodeCreator.triggerHelperPanel.selectATrigger": "Select a trigger",
+ "nodeCreator.triggerHelperPanel.selectATriggerDescription": "A trigger is a step that starts your workflow",
+ "nodeCreator.triggerHelperPanel.workflowTriggerDisplayName": "When called by another workflow",
"nodeCreator.triggerHelperPanel.workflowTriggerDescription": "Runs the flow when called by the Execute Workflow node from a different workflow",
"nodeCredentials.createNew": "Create New Credential",
"nodeCredentials.credentialFor": "Credential for {credentialType}",
@@ -929,7 +936,6 @@
"personalizationModal.it": "IT",
"personalizationModal.legal": "Legal",
"personalizationModal.lessThan20People": "Less than 20 people",
- "personalizationModal.lookOutForThingsMarked": "Look out for things marked with a ✨. They are personalized to make n8n more relevant to you.",
"personalizationModal.managedServiceProvider": "Managed service provider",
"personalizationModal.manufacturing": "Manufacturing",
"personalizationModal.marketing": "Marketing",
diff --git a/packages/editor-ui/src/plugins/icons.ts b/packages/editor-ui/src/plugins/icons.ts
index 86f4caf080..3d67c20fc2 100644
--- a/packages/editor-ui/src/plugins/icons.ts
+++ b/packages/editor-ui/src/plugins/icons.ts
@@ -11,6 +11,7 @@ import {
faArrowRight,
faAt,
faBan,
+ faBolt,
faBook,
faBoxOpen,
faBug,
@@ -46,6 +47,7 @@ import {
faExternalLinkAlt,
faExchangeAlt,
faFile,
+ faFileAlt,
faFileArchive,
faFileCode,
faFileDownload,
@@ -112,6 +114,7 @@ import {
faThumbtack,
faTimes,
faTimesCircle,
+ faToolbox,
faTrash,
faUndo,
faUnlink,
@@ -140,6 +143,7 @@ addIcon(faArrowLeft);
addIcon(faArrowRight);
addIcon(faAt);
addIcon(faBan);
+addIcon(faBolt);
addIcon(faBook);
addIcon(faBoxOpen);
addIcon(faBug);
@@ -176,6 +180,7 @@ addIcon(faExpandAlt);
addIcon(faExternalLinkAlt);
addIcon(faExchangeAlt);
addIcon(faFile);
+addIcon(faFileAlt);
addIcon(faFileArchive);
addIcon(faFileCode);
addIcon(faFileDownload);
@@ -243,6 +248,7 @@ addIcon(faThLarge);
addIcon(faThumbtack);
addIcon(faTimes);
addIcon(faTimesCircle);
+addIcon(faToolbox);
addIcon(faTrash);
addIcon(faUndo);
addIcon(faUnlink);
diff --git a/packages/editor-ui/src/stores/nodeCreator.ts b/packages/editor-ui/src/stores/nodeCreator.ts
index f748524733..e25831fdeb 100644
--- a/packages/editor-ui/src/stores/nodeCreator.ts
+++ b/packages/editor-ui/src/stores/nodeCreator.ts
@@ -14,16 +14,14 @@ import {
STORES,
MANUAL_TRIGGER_NODE_TYPE,
CORE_NODES_CATEGORY,
- CALENDLY_TRIGGER_NODE_TYPE,
TRIGGER_NODE_FILTER,
- WEBHOOK_NODE_TYPE,
STICKY_NODE_TYPE,
} from '@/constants';
import { useNodeTypesStore } from '@/stores/nodeTypes';
import { useWorkflowsStore } from './workflows';
import { CUSTOM_API_CALL_KEY, ALL_NODE_FILTER } from '@/constants';
import { INodeCreatorState, INodeFilterType, IUpdateInformation } from '@/Interface';
-import { i18n } from '@/plugins/i18n';
+import { BaseTextKey, i18n } from '@/plugins/i18n';
import { externalHooks } from '@/mixins/externalHooks';
import { Telemetry } from '@/plugins/telemetry';
@@ -42,7 +40,7 @@ const customNodeActionsParsers: {
(categoryItem): INodeActionTypeDescription => ({
...getNodeTypeBase(
nodeTypeDescription,
- i18n.baseText('nodeCreator.actionsCategory.recommended'),
+ i18n.baseText('nodeCreator.actionsCategory.triggers'),
),
actionKey: categoryItem.value as string,
displayName: i18n.baseText('nodeCreator.actionsCategory.onEvent', {
@@ -81,7 +79,9 @@ function getNodeTypeBase(nodeTypeDescription: INodeTypeDescription, category: st
iconUrl: nodeTypeDescription.iconUrl,
icon: nodeTypeDescription.icon,
version: [1],
- defaults: {},
+ defaults: {
+ ...nodeTypeDescription.defaults,
+ },
inputs: [],
outputs: [],
properties: [],
@@ -104,10 +104,7 @@ function operationsCategory(
);
const items = filteredOutItems.map((item: INodePropertyOptions) => ({
- ...getNodeTypeBase(
- nodeTypeDescription,
- i18n.baseText('nodeCreator.actionsCategory.operations'),
- ),
+ ...getNodeTypeBase(nodeTypeDescription, i18n.baseText('nodeCreator.actionsCategory.actions')),
actionKey: item.value as string,
displayName: item.action ?? startCase(item.name),
description: item.description ?? '',
@@ -123,9 +120,7 @@ function operationsCategory(
return items;
}
-function recommendedCategory(
- nodeTypeDescription: INodeTypeDescription,
-): INodeActionTypeDescription[] {
+function triggersCategory(nodeTypeDescription: INodeTypeDescription): INodeActionTypeDescription[] {
const matchingKeys = ['event', 'events', 'trigger on'];
const isTrigger = nodeTypeDescription.displayName?.toLowerCase().includes('trigger');
const matchedProperty = nodeTypeDescription.properties.find((property) =>
@@ -141,7 +136,7 @@ function recommendedCategory(
{
...getNodeTypeBase(
nodeTypeDescription,
- i18n.baseText('nodeCreator.actionsCategory.recommended'),
+ i18n.baseText('nodeCreator.actionsCategory.triggers'),
),
actionKey: PLACEHOLDER_RECOMMENDED_ACTION_KEY,
displayName: i18n.baseText('nodeCreator.actionsCategory.onNewEvent', {
@@ -166,7 +161,7 @@ function recommendedCategory(
filteredOutItems.map((categoryItem: INodePropertyOptions) => ({
...getNodeTypeBase(
nodeTypeDescription,
- i18n.baseText('nodeCreator.actionsCategory.recommended'),
+ i18n.baseText('nodeCreator.actionsCategory.triggers'),
),
actionKey: categoryItem.value as string,
displayName:
@@ -247,19 +242,26 @@ function resourceCategories(
export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {
state: (): INodeCreatorState => ({
itemsFilter: '',
- showTabs: true,
showScrim: false,
- selectedType: ALL_NODE_FILTER,
+ selectedView: TRIGGER_NODE_FILTER,
+ rootViewHistory: [],
}),
actions: {
- setShowTabs(isVisible: boolean) {
- this.showTabs = isVisible;
- },
setShowScrim(isVisible: boolean) {
this.showScrim = isVisible;
},
- setSelectedType(selectedNodeType: INodeFilterType) {
- this.selectedType = selectedNodeType;
+ setSelectedView(selectedNodeType: INodeFilterType) {
+ this.selectedView = selectedNodeType;
+ if (!this.rootViewHistory.includes(selectedNodeType)) {
+ this.rootViewHistory.push(selectedNodeType);
+ }
+ },
+ closeCurrentView() {
+ this.rootViewHistory.pop();
+ this.selectedView = this.rootViewHistory[this.rootViewHistory.length - 1];
+ },
+ resetRootViewHistory() {
+ this.rootViewHistory = [];
},
setFilter(search: string) {
this.itemsFilter = search;
@@ -295,16 +297,11 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {
visibleNodesWithActions(): INodeTypeDescription[] {
const nodes = deepCopy(useNodeTypesStore().visibleNodeTypes);
const nodesWithActions = nodes.map((node) => {
- const isCoreNode = node.codex?.categories?.includes(CORE_NODES_CATEGORY);
- // Core nodes shouldn't support actions
- node.actions = [];
- if (isCoreNode) return node;
-
- node.actions.push(
- ...recommendedCategory(node),
+ node.actions = [
+ ...triggersCategory(node),
...operationsCategory(node),
...resourceCategories(node),
- );
+ ];
return node;
});
@@ -322,20 +319,15 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {
})
.reduce((acc: Record, node: INodeTypeDescription) => {
const clonedNode = deepCopy(node);
- const isCoreNode = node.codex?.categories?.includes(CORE_NODES_CATEGORY);
const actions = node.actions || [];
// Do not merge core nodes
- const normalizedName = isCoreNode
- ? node.name
- : node.name.toLowerCase().replace('trigger', '');
+ const normalizedName = node.name.toLowerCase().replace('trigger', '');
const existingNode = acc[normalizedName];
if (existingNode) existingNode.actions?.push(...actions);
else acc[normalizedName] = clonedNode;
- if (!isCoreNode) {
- acc[normalizedName].displayName = node.displayName.replace('Trigger', '');
- }
+ acc[normalizedName].displayName = node.displayName.replace('Trigger', '');
return acc;
}, {});
@@ -355,7 +347,7 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {
const { workflowTriggerNodes } = useWorkflowsStore();
const isTrigger = useNodeTypesStore().isTriggerNode(nodeType);
const workflowContainsTrigger = workflowTriggerNodes.length > 0;
- const isTriggerPanel = useNodeCreatorStore().selectedType === TRIGGER_NODE_FILTER;
+ const isTriggerPanel = useNodeCreatorStore().selectedView === TRIGGER_NODE_FILTER;
const isStickyNode = nodeType === STICKY_NODE_TYPE;
const nodeTypes =
diff --git a/packages/editor-ui/src/utils/nodeTypesUtils.ts b/packages/editor-ui/src/utils/nodeTypesUtils.ts
index 991aed6e5c..4d74523c9d 100644
--- a/packages/editor-ui/src/utils/nodeTypesUtils.ts
+++ b/packages/editor-ui/src/utils/nodeTypesUtils.ts
@@ -4,7 +4,6 @@ import { useNodeTypesStore } from './../stores/nodeTypes';
import { INodeCredentialDescription } from './../../../workflow/src/Interfaces';
import {
CORE_NODES_CATEGORY,
- RECOMMENDED_CATEGORY,
CUSTOM_NODES_CATEGORY,
SUBCATEGORY_DESCRIPTIONS,
UNCATEGORIZED_CATEGORY,
@@ -72,7 +71,7 @@ const addNodeToCategory = (
}
accu[category][subcategory].nodes.push({
type: nodeType.actionKey ? 'action' : 'node',
- key: `${category}_${nodeType.name}`,
+ key: `${nodeType.name}`,
category,
properties: {
nodeType,
@@ -85,17 +84,12 @@ const addNodeToCategory = (
export const getCategoriesWithNodes = (
nodeTypes: INodeTypeDescription[],
- personalizedNodeTypes: string[],
uncategorizedSubcategory = UNCATEGORIZED_SUBCATEGORY,
): ICategoriesWithNodes => {
const sorted = [...nodeTypes].sort((a: INodeTypeDescription, b: INodeTypeDescription) =>
a.displayName > b.displayName ? 1 : -1,
);
const result = sorted.reduce((accu: ICategoriesWithNodes, nodeType: INodeTypeDescription) => {
- if (personalizedNodeTypes.includes(nodeType.name)) {
- addNodeToCategory(accu, nodeType, PERSONALIZED_CATEGORY, uncategorizedSubcategory);
- }
-
if (!nodeType.codex || !nodeType.codex.categories) {
addNodeToCategory(accu, nodeType, UNCATEGORIZED_CATEGORY, uncategorizedSubcategory);
return accu;
@@ -125,14 +119,12 @@ const getCategories = (categoriesWithNodes: ICategoriesWithNodes): string[] => {
CUSTOM_NODES_CATEGORY,
UNCATEGORIZED_CATEGORY,
PERSONALIZED_CATEGORY,
- RECOMMENDED_CATEGORY,
];
const categories = Object.keys(categoriesWithNodes);
const sorted = categories.filter((category: string) => !excludeFromSort.includes(category));
sorted.sort();
return [
- RECOMMENDED_CATEGORY,
CORE_NODES_CATEGORY,
CUSTOM_NODES_CATEGORY,
PERSONALIZED_CATEGORY,
@@ -155,8 +147,9 @@ export const getCategorizedList = (
const categoryEl: INodeCreateElement = {
type: 'category',
key: category,
- category,
properties: {
+ category,
+ name: category,
expanded: categoryIsExpanded,
},
};
@@ -179,7 +172,6 @@ export const getCategorizedList = (
const subcategoryEl: INodeCreateElement = {
type: 'subcategory',
key: `${category}_${subcategory}`,
- category,
properties: {
subcategory,
description: SUBCATEGORY_DESCRIPTIONS[category][subcategory],
@@ -277,15 +269,15 @@ export const executionDataToJson = (inputData: INodeExecutionData[]): IDataObjec
[],
);
-export const matchesSelectType = (el: INodeCreateElement, selectedType: string) => {
- if (selectedType === REGULAR_NODE_FILTER && el.includedByRegular) {
+export const matchesSelectType = (el: INodeCreateElement, selectedView: string) => {
+ if (selectedView === REGULAR_NODE_FILTER && el.includedByRegular) {
return true;
}
- if (selectedType === TRIGGER_NODE_FILTER && el.includedByTrigger) {
+ if (selectedView === TRIGGER_NODE_FILTER && el.includedByTrigger) {
return true;
}
- return selectedType === ALL_NODE_FILTER;
+ return selectedView === ALL_NODE_FILTER;
};
const matchesAlias = (nodeType: INodeTypeDescription, filter: string): boolean => {
diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue
index f5f854ea12..34fd4062b1 100644
--- a/packages/editor-ui/src/views/NodeView.vue
+++ b/packages/editor-ui/src/views/NodeView.vue
@@ -196,6 +196,8 @@ import {
TRIGGER_NODE_FILTER,
EnterpriseEditionFeature,
POSTHOG_ASSUMPTION_TEST,
+ REGULAR_NODE_FILTER,
+ MANUAL_TRIGGER_NODE_TYPE,
} from '@/constants';
import { copyPaste } from '@/mixins/copyPaste';
import { externalHooks } from '@/mixins/externalHooks';
@@ -752,10 +754,9 @@ export default mixins(
},
showTriggerCreator(source: string) {
if (this.createNodeActive) return;
- this.nodeCreatorStore.setSelectedType(TRIGGER_NODE_FILTER);
+ this.nodeCreatorStore.setSelectedView(TRIGGER_NODE_FILTER);
this.nodeCreatorStore.setShowScrim(true);
this.onToggleNodeCreator({ source, createNodeActive: true });
- this.$nextTick(() => this.nodeCreatorStore.setShowTabs(false));
},
async openExecution(executionId: string) {
this.startLoading();
@@ -1799,6 +1800,7 @@ export default mixins(
options: AddNodeOptions = {},
showDetail = true,
trackHistory = false,
+ isAutoAdd = false,
) {
const nodeTypeData: INodeTypeDescription | null =
this.nodeTypesStore.getNodeType(nodeTypeName);
@@ -1920,6 +1922,7 @@ export default mixins(
this.$externalHooks().run('nodeView.addNodeButton', { nodeTypeName });
const trackProperties: ITelemetryTrackProperties = {
node_type: nodeTypeName,
+ is_auto_add: isAutoAdd,
workflow_id: this.workflowsStore.workflowId,
drag_and_drop: options.dragAndDrop,
};
@@ -2005,6 +2008,7 @@ export default mixins(
options: AddNodeOptions = {},
showDetail = true,
trackHistory = false,
+ isAutoAdd = false,
) {
if (!this.editAllowedCheck()) {
return;
@@ -2016,7 +2020,13 @@ export default mixins(
this.historyStore.startRecordingUndo();
- const newNodeData = await this.injectNode(nodeTypeName, options, showDetail, trackHistory);
+ const newNodeData = await this.injectNode(
+ nodeTypeName,
+ options,
+ showDetail,
+ trackHistory,
+ isAutoAdd,
+ );
if (!newNodeData) {
return;
}
@@ -3674,12 +3684,14 @@ export default mixins(
if (createNodeActive === this.createNodeActive) return;
// Default to the trigger tab in node creator if there's no trigger node yet
- if (!this.containsTrigger) this.nodeCreatorStore.setSelectedType(TRIGGER_NODE_FILTER);
+ this.nodeCreatorStore.setSelectedView(
+ this.containsTrigger ? REGULAR_NODE_FILTER : TRIGGER_NODE_FILTER,
+ );
this.createNodeActive = createNodeActive;
const mode =
- this.nodeCreatorStore.selectedType === TRIGGER_NODE_FILTER ? 'trigger' : 'default';
+ this.nodeCreatorStore.selectedView === TRIGGER_NODE_FILTER ? 'trigger' : 'regular';
this.$externalHooks().run('nodeView.createNodeActiveChanged', {
source,
mode,
@@ -3697,11 +3709,14 @@ export default mixins(
dragAndDrop: boolean,
) {
nodeTypes.forEach(({ nodeTypeName, position }, index) => {
+ const isManualTrigger = nodeTypeName === MANUAL_TRIGGER_NODE_TYPE;
+ const openNDV = !isManualTrigger && (nodeTypes.length === 1 || index > 0);
this.addNode(
nodeTypeName,
{ position, dragAndDrop },
- nodeTypes.length === 1 || index > 0,
+ openNDV,
true,
+ nodeTypes.length > 1 && index < 1,
);
if (index === 0) return;
// If there's more than one node, we want to connect them
@@ -3717,7 +3732,7 @@ export default mixins(
this.connectTwoNodes(previouslyAddedNode.name, 0, lastAddedNode.name, 0),
);
- // Position the added node to the right side of the previsouly added one
+ // Position the added node to the right side of the previously added one
lastAddedNode.position = [
previouslyAddedNode.position[0] + NodeViewUtils.NODE_SIZE * 2,
previouslyAddedNode.position[1],
diff --git a/packages/nodes-base/nodes/EditImage/EditImage.node.ts b/packages/nodes-base/nodes/EditImage/EditImage.node.ts
index 4eb26b73db..efb8409e82 100644
--- a/packages/nodes-base/nodes/EditImage/EditImage.node.ts
+++ b/packages/nodes-base/nodes/EditImage/EditImage.node.ts
@@ -22,56 +22,67 @@ const nodeOperations: INodePropertyOptions[] = [
name: 'Blur',
value: 'blur',
description: 'Adds a blur to the image and so makes it less sharp',
+ action: 'Blur Image',
},
{
name: 'Border',
value: 'border',
description: 'Adds a border to the image',
+ action: 'Border Image',
},
{
name: 'Composite',
value: 'composite',
description: 'Composite image on top of another one',
+ action: 'Composite Image',
},
{
name: 'Create',
value: 'create',
description: 'Create a new image',
+ action: 'Create Image',
},
{
name: 'Crop',
value: 'crop',
description: 'Crops the image',
+ action: 'Crop Image',
},
{
name: 'Draw',
value: 'draw',
description: 'Draw on image',
+ action: 'Draw Image',
},
{
name: 'Rotate',
value: 'rotate',
description: 'Rotate image',
+ action: 'Rotate Image',
},
{
name: 'Resize',
value: 'resize',
description: 'Change the size of image',
+ action: 'Resize Image',
},
{
name: 'Shear',
value: 'shear',
description: 'Shear image along the X or Y axis',
+ action: 'Shear Image',
},
{
name: 'Text',
value: 'text',
description: 'Adds text to image',
+ action: 'Apply Text to Image',
},
{
name: 'Transparent',
value: 'transparent',
description: 'Make a color in image transparent',
+ action: 'Add Transparency to Image',
},
];
diff --git a/packages/nodes-base/nodes/EmailSend/v2/EmailSendV2.node.ts b/packages/nodes-base/nodes/EmailSend/v2/EmailSendV2.node.ts
index f35ce59c2f..d4aeac7401 100644
--- a/packages/nodes-base/nodes/EmailSend/v2/EmailSendV2.node.ts
+++ b/packages/nodes-base/nodes/EmailSend/v2/EmailSendV2.node.ts
@@ -54,6 +54,7 @@ const versionDescription: INodeTypeDescription = {
{
name: 'Send',
value: 'send',
+ action: 'Send an Email',
},
],
},
diff --git a/packages/nodes-base/nodes/N8n/N8n.node.json b/packages/nodes-base/nodes/N8n/N8n.node.json
index 4c2c48d838..34fe502361 100644
--- a/packages/nodes-base/nodes/N8n/N8n.node.json
+++ b/packages/nodes-base/nodes/N8n/N8n.node.json
@@ -17,6 +17,6 @@
},
"alias": ["Workflow", "Execution"],
"subcategories": {
- "Core Nodes": ["Helpers"]
+ "Core Nodes": ["Helpers", "Flow", "Other Trigger Nodes"]
}
}
diff --git a/packages/nodes-base/nodes/N8n/N8n.node.ts b/packages/nodes-base/nodes/N8n/N8n.node.ts
index 0d9c36cff4..91f21bd5c7 100644
--- a/packages/nodes-base/nodes/N8n/N8n.node.ts
+++ b/packages/nodes-base/nodes/N8n/N8n.node.ts
@@ -18,7 +18,7 @@ export class N8n implements INodeType {
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
- description: 'Consume n8n API',
+ description: 'Handle events and perform actions on your n8n instance',
defaults: {
name: 'n8n',
},
diff --git a/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.json b/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.json
index 1be5c3b241..74882ce583 100644
--- a/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.json
+++ b/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.json
@@ -11,6 +11,6 @@
]
},
"subcategories": {
- "Core Nodes": ["Flow", "Other Trigger Nodes"]
+ "Core Nodes": ["Flow", "Other Trigger Nodes", "Helpers"]
}
}
diff --git a/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts b/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts
index 25905de075..b8a4cf115a 100644
--- a/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts
+++ b/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts
@@ -10,7 +10,7 @@ export class N8nTrigger implements INodeType {
icon: 'file:n8nTrigger.svg',
group: ['trigger'],
version: 1,
- description: 'Handle events from your n8n instance',
+ description: 'Handle events and perform actions on your n8n instance',
eventTriggerDescription: '',
mockManualExecution: true,
defaults: {
diff --git a/packages/nodes-base/nodes/SpreadsheetFile/SpreadsheetFile.node.ts b/packages/nodes-base/nodes/SpreadsheetFile/SpreadsheetFile.node.ts
index 8d55b5b12f..0aa177e77b 100644
--- a/packages/nodes-base/nodes/SpreadsheetFile/SpreadsheetFile.node.ts
+++ b/packages/nodes-base/nodes/SpreadsheetFile/SpreadsheetFile.node.ts
@@ -69,7 +69,7 @@ export class SpreadsheetFile implements INodeType {
name: 'Write to File',
value: 'toFile',
description: 'Writes the workflow data to a spreadsheet file',
- action: 'Write the workflow data to a spreadsheet file',
+ action: 'Write data to a spreadsheet file',
},
],
default: 'fromFile',