Skip to content

Commit

Permalink
feat: Data transformation nodes and actions in Nodes Panel (#7760)
Browse files Browse the repository at this point in the history
- Split Items List node into separate nodes per action
- Review node descriptions
- New icons
- New sections in subcategories

---------

Co-authored-by: Giulio Andreini <andreini@netseven.it>
Co-authored-by: Deborah <deborah@starfallprojects.co.uk>
Co-authored-by: Michael Kret <michael.k@radency.com>
  • Loading branch information
4 people authored Dec 8, 2023
1 parent 90824b5 commit 675ec21
Show file tree
Hide file tree
Showing 78 changed files with 4,353 additions and 74 deletions.
2 changes: 1 addition & 1 deletion cypress/e2e/11-inline-expression-editor.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ describe('Inline expression editor', () => {
WorkflowPage.getters.inlineExpressionEditorInput().type('{{');
// Resolving $parameter is slow, especially on CI runner
WorkflowPage.getters.inlineExpressionEditorInput().type('$parameter["operation"]');
WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^get$/);
WorkflowPage.getters.inlineExpressionEditorOutput().should('have.text', 'getAll');
});
});
7 changes: 2 additions & 5 deletions cypress/e2e/14-mapping.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,8 @@ describe('Data mapping', () => {

ndv.actions.close();

workflowPage.actions.addNodeToCanvas('Item Lists');
workflowPage.actions.openNode('Item Lists');

ndv.getters.parameterInput('operation').click();
getVisibleSelect().find('li').contains('Sort').click();
workflowPage.actions.addNodeToCanvas('Sort');
workflowPage.actions.openNode('Sort');

ndv.getters.nodeParameters().find('button').contains('Add Field To Sort By').click();

Expand Down
4 changes: 2 additions & 2 deletions cypress/e2e/24-ndv-paired-item.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('NDV', () => {

workflowPage.actions.executeWorkflow();

workflowPage.actions.openNode('Item Lists');
workflowPage.actions.openNode('Sort');

ndv.getters.inputPanel().contains('6 items').should('exist');
ndv.getters.outputPanel().contains('6 items').should('exist');
Expand Down Expand Up @@ -92,7 +92,7 @@ describe('NDV', () => {
ndv.getters.outputHoveringItem().should('have.text', '1000');
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');

ndv.actions.selectInputNode('Item Lists');
ndv.actions.selectInputNode('Sort');
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
ndv.getters.backToCanvas().realHover(); // reset to default hover

Expand Down
8 changes: 4 additions & 4 deletions cypress/e2e/26-resource-locator.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('Resource Locator', () => {

it('should render both RLC components in google sheets', () => {
workflowPage.actions.addInitialNodeToCanvas('Manual');
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet');
ndv.getters.resourceLocator('documentId').should('be.visible');
ndv.getters.resourceLocator('sheetName').should('be.visible');
ndv.getters
Expand All @@ -31,15 +31,15 @@ describe('Resource Locator', () => {

it('should show appropriate error when credentials are not set', () => {
workflowPage.actions.addInitialNodeToCanvas('Manual');
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet');
ndv.getters.resourceLocator('documentId').should('be.visible');
ndv.getters.resourceLocatorInput('documentId').click();
ndv.getters.resourceLocatorErrorMessage().should('contain', NO_CREDENTIALS_MESSAGE);
});

it('should show appropriate error when credentials are not valid', () => {
workflowPage.actions.addInitialNodeToCanvas('Manual');
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet');
workflowPage.getters.nodeCredentialsSelect().click();
// Add oAuth credentials
getVisibleSelect().find('li').last().click();
Expand All @@ -54,7 +54,7 @@ describe('Resource Locator', () => {

it('should reset resource locator when dependent field is changed', () => {
workflowPage.actions.addInitialNodeToCanvas('Manual');
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet');
ndv.actions.setRLCValue('documentId', '123');
ndv.actions.setRLCValue('sheetName', '123');
ndv.actions.setRLCValue('documentId', '321');
Expand Down
4 changes: 2 additions & 2 deletions cypress/e2e/4-node-creator.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ describe('Node Creator', () => {
NDVModal.actions.close();
WorkflowPage.getters.canvasNodes().should('have.length', 2);
WorkflowPage.actions.zoomToFit();
WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Item Lists', 'Summarize');
WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Summarize');
WorkflowPage.getters.canvasNodes().should('have.length', 3);
});
});
Expand Down Expand Up @@ -410,7 +410,7 @@ describe('Node Creator', () => {

nodeCreatorFeature.getters.searchBar().find('input').clear().type('js');
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Code');
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Item Lists');
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Edit Fields (Set)');

nodeCreatorFeature.getters.searchBar().find('input').clear().type('fi');
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Filter');
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/9-expression-editor-modal.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ 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().should('have.text', 'getAll');
});
});
13 changes: 6 additions & 7 deletions cypress/fixtures/Test_workflow_5.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
},
{
"parameters": {
"operation": "sort",
"sortFieldsUi": {
"sortField": [
{
Expand All @@ -61,9 +60,9 @@
"options": {}
},
"id": "555a150c-d735-4331-b628-c1f1cfed2da1",
"name": "Item Lists",
"type": "n8n-nodes-base.itemLists",
"typeVersion": 2,
"name": "Sort",
"type": "n8n-nodes-base.sort",
"typeVersion": 1,
"position": [
-280,
580
Expand Down Expand Up @@ -182,7 +181,7 @@
"main": [
[
{
"node": "Item Lists",
"node": "Sort",
"type": "main",
"index": 0
}
Expand Down Expand Up @@ -216,7 +215,7 @@
]
]
},
"Item Lists": {
"Sort": {
"main": [
[
{
Expand Down Expand Up @@ -289,4 +288,4 @@
]
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ function subcategoriesMapper(item: INodeCreateElement) {
}
function baseSubcategoriesFilter(item: INodeCreateElement): boolean {
if (item.type === 'section') return item.children.every(baseSubcategoriesFilter);
if (item.type === 'section') return true;
if (item.type !== 'node') return false;
const hasTriggerGroup = item.properties.group.includes('trigger');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { SectionCreateElement } from '@/Interface';
import { groupItemsInSections } from '../utils';
import { mockNodeCreateElement } from './utils';
import { groupItemsInSections, sortNodeCreateElements } from '../utils';
import { mockActionCreateElement, mockNodeCreateElement, mockSectionCreateElement } from './utils';

describe('NodeCreator - utils', () => {
describe('groupItemsInSections', () => {
Expand Down Expand Up @@ -46,4 +46,20 @@ describe('NodeCreator - utils', () => {
expect(result).toEqual([node1, node2, node3]);
});
});

describe('sortNodeCreateElements', () => {
it('should sort nodes alphabetically by displayName', () => {
const node1 = mockNodeCreateElement({ key: 'newNode' }, { displayName: 'xyz' });
const node2 = mockNodeCreateElement({ key: 'popularNode' }, { displayName: 'abc' });
const node3 = mockNodeCreateElement({ key: 'otherNode' }, { displayName: 'ABC' });
expect(sortNodeCreateElements([node1, node2, node3])).toEqual([node2, node3, node1]);
});

it('should not change order for other types (sections, actions)', () => {
const node1 = mockSectionCreateElement();
const node2 = mockActionCreateElement();
const node3 = mockSectionCreateElement();
expect(sortNodeCreateElements([node1, node2, node3])).toEqual([node1, node2, node3]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export const useViewStacks = defineStore('nodeCreatorViewStacks', () => {

// Sort only if non-root view
if (!stack.items) {
sortNodeCreateElements(stackItems);
stackItems = sortNodeCreateElements(stackItems);
}

updateCurrentViewStack({ baselineItems: stackItems });
Expand Down
8 changes: 4 additions & 4 deletions packages/editor-ui/src/components/Node/NodeCreator/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function subcategorizeItems(items: SimplifiedNodeType[]) {

export function sortNodeCreateElements(nodes: INodeCreateElement[]) {
return nodes.sort((a, b) => {
if (a.type !== 'node' || b.type !== 'node') return -1;
if (a.type !== 'node' || b.type !== 'node') return 0;
const displayNameA = a.properties?.displayName?.toLowerCase() || a.key;
const displayNameB = b.properties?.displayName?.toLowerCase() || b.key;

Expand Down Expand Up @@ -101,16 +101,16 @@ export function groupItemsInSections(
type: 'section',
key: section.key,
title: section.title,
children: itemsBySection[section.key],
children: sortNodeCreateElements(itemsBySection[section.key] ?? []),
}),
)
.concat({
type: 'section',
key: 'other',
title: i18n.baseText('nodeCreator.sectionNames.other'),
children: itemsBySection.other,
children: sortNodeCreateElements(itemsBySection.other ?? []),
})
.filter((section) => section.children);
.filter((section) => section.children.length > 0);

if (result.length <= 1) {
return items;
Expand Down
76 changes: 65 additions & 11 deletions packages/editor-ui/src/components/Node/NodeCreator/viewsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
TRANSFORM_DATA_SUBCATEGORY,
FILES_SUBCATEGORY,
FLOWS_CONTROL_SUBCATEGORY,
HELPERS_SUBCATEGORY,
TRIGGER_NODE_CREATOR_VIEW,
EMAIL_IMAP_NODE_TYPE,
DEFAULT_SUBCATEGORY,
Expand All @@ -29,6 +28,26 @@ import {
AI_CATEGORY_EMBEDDING,
AI_OTHERS_NODE_CREATOR_VIEW,
AI_UNCATEGORIZED_CATEGORY,
SET_NODE_TYPE,
CODE_NODE_TYPE,
DATETIME_NODE_TYPE,
FILTER_NODE_TYPE,
REMOVE_DUPLICATES_NODE_TYPE,
SPLIT_OUT_NODE_TYPE,
LIMIT_NODE_TYPE,
SUMMARIZE_NODE_TYPE,
AGGREGATE_NODE_TYPE,
MERGE_NODE_TYPE,
HTML_NODE_TYPE,
MARKDOWN_NODE_TYPE,
XML_NODE_TYPE,
CRYPTO_NODE_TYPE,
IF_NODE_TYPE,
SPLIT_IN_BATCHES_NODE_TYPE,
HTTP_REQUEST_NODE_TYPE,
HELPERS_SUBCATEGORY,
RSS_READ_NODE_TYPE,
EMAIL_SEND_NODE_TYPE,
} from '@/constants';
import { useI18n } from '@/composables/useI18n';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
Expand Down Expand Up @@ -340,6 +359,7 @@ export function RegularView(nodes: SimplifiedNodeType[]) {
properties: {
title: 'App Regular Nodes',
icon: 'globe',
forceIncludeNodes: [RSS_READ_NODE_TYPE, EMAIL_SEND_NODE_TYPE],
},
},
{
Expand All @@ -353,27 +373,45 @@ export function RegularView(nodes: SimplifiedNodeType[]) {
{
key: 'popular',
title: i18n.baseText('nodeCreator.sectionNames.popular'),
items: [],
items: [SET_NODE_TYPE, CODE_NODE_TYPE, DATETIME_NODE_TYPE],
},
{
key: 'addOrRemove',
title: i18n.baseText('nodeCreator.sectionNames.transform.addOrRemove'),
items: [
FILTER_NODE_TYPE,
REMOVE_DUPLICATES_NODE_TYPE,
SPLIT_OUT_NODE_TYPE,
LIMIT_NODE_TYPE,
],
},
{
key: 'combine',
title: i18n.baseText('nodeCreator.sectionNames.transform.combine'),
items: [SUMMARIZE_NODE_TYPE, AGGREGATE_NODE_TYPE, MERGE_NODE_TYPE],
},
{
key: 'convert',
title: i18n.baseText('nodeCreator.sectionNames.transform.convert'),
items: [HTML_NODE_TYPE, MARKDOWN_NODE_TYPE, XML_NODE_TYPE, CRYPTO_NODE_TYPE],
},
],
},
},
{
type: 'subcategory',
key: HELPERS_SUBCATEGORY,
category: CORE_NODES_CATEGORY,
properties: {
title: HELPERS_SUBCATEGORY,
icon: 'toolbox',
},
},
{
type: 'subcategory',
key: FLOWS_CONTROL_SUBCATEGORY,
category: CORE_NODES_CATEGORY,
properties: {
title: FLOWS_CONTROL_SUBCATEGORY,
icon: 'code-branch',
sections: [
{
key: 'popular',
title: i18n.baseText('nodeCreator.sectionNames.popular'),
items: [FILTER_NODE_TYPE, IF_NODE_TYPE, SPLIT_IN_BATCHES_NODE_TYPE, MERGE_NODE_TYPE],
},
],
},
},
{
Expand All @@ -385,6 +423,22 @@ export function RegularView(nodes: SimplifiedNodeType[]) {
icon: 'file-alt',
},
},
{
type: 'subcategory',
key: HELPERS_SUBCATEGORY,
category: CORE_NODES_CATEGORY,
properties: {
title: HELPERS_SUBCATEGORY,
icon: 'toolbox',
sections: [
{
key: 'popular',
title: i18n.baseText('nodeCreator.sectionNames.popular'),
items: [HTTP_REQUEST_NODE_TYPE, WEBHOOK_NODE_TYPE, CODE_NODE_TYPE],
},
],
},
},
],
};

Expand Down
11 changes: 11 additions & 0 deletions packages/editor-ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,17 @@ export const XERO_NODE_TYPE = 'n8n-nodes-base.xero';
export const ZENDESK_NODE_TYPE = 'n8n-nodes-base.zendesk';
export const ZENDESK_TRIGGER_NODE_TYPE = 'n8n-nodes-base.zendeskTrigger';
export const DISCORD_NODE_TYPE = 'n8n-nodes-base.discord';
export const DATETIME_NODE_TYPE = 'n8n-nodes-base.dateTime';
export const REMOVE_DUPLICATES_NODE_TYPE = 'n8n-nodes-base.removeDuplicates';
export const SPLIT_OUT_NODE_TYPE = 'n8n-nodes-base.splitOut';
export const LIMIT_NODE_TYPE = 'n8n-nodes-base.limit';
export const SUMMARIZE_NODE_TYPE = 'n8n-nodes-base.summarize';
export const AGGREGATE_NODE_TYPE = 'n8n-nodes-base.aggregate';
export const MERGE_NODE_TYPE = 'n8n-nodes-base.merge';
export const MARKDOWN_NODE_TYPE = 'n8n-nodes-base.markdown';
export const XML_NODE_TYPE = 'n8n-nodes-base.xml';
export const CRYPTO_NODE_TYPE = 'n8n-nodes-base.crypto';
export const RSS_READ_NODE_TYPE = 'n8n-nodes-base.rssFeedRead';

export const CREDENTIAL_ONLY_NODE_PREFIX = 'n8n-creds-base';
export const CREDENTIAL_ONLY_HTTP_NODE_VERSION = 4.1;
Expand Down
5 changes: 4 additions & 1 deletion packages/editor-ui/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@
"nodeCreator.subcategoryNames.dataTransformation": "Data transformation",
"nodeCreator.subcategoryNames.files": "Files",
"nodeCreator.subcategoryNames.flow": "Flow",
"nodeCreator.subcategoryNames.helpers": "Helpers",
"nodeCreator.subcategoryNames.helpers": "Advanced",
"nodeCreator.subcategoryNames.otherTriggerNodes": "Other ways...",
"nodeCreator.subcategoryNames.agents": "Agents",
"nodeCreator.subcategoryNames.chains": "Chains",
Expand All @@ -900,6 +900,9 @@
"nodeCreator.subcategoryNames.miscellaneous": "Miscellaneous",
"nodeCreator.sectionNames.popular": "Popular",
"nodeCreator.sectionNames.other": "Other",
"nodeCreator.sectionNames.transform.combine": "Combine items",
"nodeCreator.sectionNames.transform.addOrRemove": "Add or remove items",
"nodeCreator.sectionNames.transform.convert": "Convert data",
"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?",
Expand Down
2 changes: 1 addition & 1 deletion packages/nodes-base/nodes/Code/Code.node.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
},
"alias": ["cpde", "Javascript", "JS", "Python", "Script", "Custom Code", "Function"],
"subcategories": {
"Core Nodes": ["Data Transformation"]
"Core Nodes": ["Helpers", "Data Transformation"]
}
}
2 changes: 1 addition & 1 deletion packages/nodes-base/nodes/Crypto/Crypto.node.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
},
"alias": ["Encrypt", "SHA", "Hash"],
"subcategories": {
"Core Nodes": ["Helpers"]
"Core Nodes": ["Data Transformation"]
}
}
Loading

0 comments on commit 675ec21

Please # to comment.