feat(editor): Unify regular and trigger node creator panels (#5315)

* WIP: Merge TriggerHelperPanel with MainPanel

* WIP: Implement switching between views

* Remove logging

* WIP: Rework search

* Fix category toggling and search results display

* Fix node item description

* Sort actions based on the root view

* Adjust personalisation modal, make trigger canvas node round

* Linting fixes

* Fix filtering of API options

* Fix types and no result state

* Cleanup

* Linting fixes

* Adjust mode prop for node creator tracking

* Fix merging of core nodes and filtering of single placeholder actions

* Lint fixes

* Implement actions override, fix node creator view item spacing and increase click radius of trigger node icon

* Fix keyboard view navigation

* WIP: E2E Tests

* Address product review

* Minor fixes & cleanup

* Fix tests

* Some more test fixes

* Add specs to check actions and panels

* Update personalisation survey snapshot
This commit is contained in:
OlegIvaniv
2023-02-17 15:08:26 +01:00
committed by GitHub
parent 561882f599
commit 9a1e7b52f7
49 changed files with 1187 additions and 1339 deletions

View File

@@ -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<string, INodeTypeDescription>, 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 =