mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-30 19:35:36 +08:00
feat(editor): Tweak command bar labels (no-changelog) (#21228)
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
"generic.any": "Any",
|
||||
"generic.cancel": "Cancel",
|
||||
"generic.open": "Open",
|
||||
"generic.openResource": "Open {resource}",
|
||||
"generic.add": "Add",
|
||||
"generic.close": "Close",
|
||||
"generic.clear": "Clear",
|
||||
|
||||
+16
@@ -222,6 +222,14 @@ describe('useCredentialNavigationCommands', () => {
|
||||
await vi.waitFor(() => {
|
||||
const openCommand = commands.value.find((cmd) => cmd.id === 'open-credential');
|
||||
expect(openCommand?.children).toHaveLength(1);
|
||||
expect(openCommand?.children?.[0].section).toBe('commandBar.credentials.open');
|
||||
expect(openCommand?.children?.[0].title).toEqual(
|
||||
expect.objectContaining({
|
||||
props: expect.objectContaining({
|
||||
title: 'My API Key',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -344,6 +352,14 @@ describe('useCredentialNavigationCommands', () => {
|
||||
await vi.waitFor(() => {
|
||||
const rootCredentials = commands.value.filter((cmd) => cmd.id === 'cred-1');
|
||||
expect(rootCredentials).toHaveLength(1);
|
||||
expect(rootCredentials[0].section).toBe('commandBar.sections.credentials');
|
||||
expect(rootCredentials[0].title).toEqual(
|
||||
expect.objectContaining({
|
||||
props: expect.objectContaining({
|
||||
title: 'generic.openResource',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+15
-6
@@ -79,21 +79,30 @@ export function useCredentialNavigationCommands(options: {
|
||||
return credential.homeProject?.name ?? '';
|
||||
};
|
||||
|
||||
const createCredentialCommand = (credential: ICredentialsResponse): CommandBarItem => {
|
||||
const createCredentialCommand = (
|
||||
credential: ICredentialsResponse,
|
||||
isRoot: boolean,
|
||||
): CommandBarItem => {
|
||||
// Add credential name to keywords since we're using a custom component for the title
|
||||
const keywords = [credential.name];
|
||||
|
||||
const title = isRoot
|
||||
? i18n.baseText('generic.openResource', { interpolate: { resource: credential.name } })
|
||||
: credential.name;
|
||||
const section = isRoot
|
||||
? i18n.baseText('commandBar.sections.credentials')
|
||||
: i18n.baseText('commandBar.credentials.open');
|
||||
|
||||
return {
|
||||
id: credential.id,
|
||||
title: {
|
||||
component: CommandBarItemTitle,
|
||||
props: {
|
||||
title: credential.name,
|
||||
title,
|
||||
suffix: getCredentialProjectSuffix(credential),
|
||||
actionText: i18n.baseText('generic.open'),
|
||||
},
|
||||
},
|
||||
section: i18n.baseText('commandBar.sections.credentials'),
|
||||
section,
|
||||
keywords,
|
||||
icon: {
|
||||
component: CredentialIcon,
|
||||
@@ -108,14 +117,14 @@ export function useCredentialNavigationCommands(options: {
|
||||
};
|
||||
|
||||
const openCredentialCommands = computed<CommandBarItem[]>(() => {
|
||||
return credentialResults.value.map((credential) => createCredentialCommand(credential));
|
||||
return credentialResults.value.map((credential) => createCredentialCommand(credential, false));
|
||||
});
|
||||
|
||||
const rootCredentialItems = computed<CommandBarItem[]>(() => {
|
||||
if (lastQuery.value.length <= 2) {
|
||||
return [];
|
||||
}
|
||||
return credentialResults.value.map((credential) => createCredentialCommand(credential));
|
||||
return credentialResults.value.map((credential) => createCredentialCommand(credential, true));
|
||||
});
|
||||
|
||||
const credentialNavigationCommands = computed<CommandBarItem[]>(() => {
|
||||
|
||||
+8
@@ -299,6 +299,14 @@ describe('useDataTableNavigationCommands', () => {
|
||||
await vi.waitFor(() => {
|
||||
const rootDataTables = commands.value.filter((cmd) => cmd.id === 'dt-1');
|
||||
expect(rootDataTables).toHaveLength(1);
|
||||
expect(rootDataTables[0].section).toBe('commandBar.sections.dataTables');
|
||||
expect(rootDataTables[0].title).toEqual(
|
||||
expect.objectContaining({
|
||||
props: expect.objectContaining({
|
||||
title: 'generic.openResource',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+12
-6
@@ -87,21 +87,27 @@ export function useDataTableNavigationCommands(options: {
|
||||
return dataTable.project?.name ?? '';
|
||||
};
|
||||
|
||||
const createDataTableCommand = (dataTable: DataTable): CommandBarItem => {
|
||||
const createDataTableCommand = (dataTable: DataTable, isRoot: boolean): CommandBarItem => {
|
||||
// Add data table name to keywords since we're using a custom component for the title
|
||||
const keywords = [dataTable.name];
|
||||
|
||||
const title = isRoot
|
||||
? i18n.baseText('generic.openResource', { interpolate: { resource: dataTable.name } })
|
||||
: dataTable.name;
|
||||
const section = isRoot
|
||||
? i18n.baseText('commandBar.sections.dataTables')
|
||||
: i18n.baseText('commandBar.dataTables.open');
|
||||
|
||||
return {
|
||||
id: dataTable.id,
|
||||
title: {
|
||||
component: CommandBarItemTitle,
|
||||
props: {
|
||||
title: dataTable.name,
|
||||
title,
|
||||
suffix: getDataTableProjectSuffix(dataTable),
|
||||
actionText: i18n.baseText('generic.open'),
|
||||
},
|
||||
},
|
||||
section: i18n.baseText('commandBar.sections.dataTables'),
|
||||
section,
|
||||
keywords,
|
||||
handler: () => {
|
||||
void router.push({
|
||||
@@ -116,14 +122,14 @@ export function useDataTableNavigationCommands(options: {
|
||||
};
|
||||
|
||||
const openDataTableCommands = computed<CommandBarItem[]>(() => {
|
||||
return dataTableResults.value.map((dataTable) => createDataTableCommand(dataTable));
|
||||
return dataTableResults.value.map((dataTable) => createDataTableCommand(dataTable, false));
|
||||
});
|
||||
|
||||
const rootDataTableItems = computed<CommandBarItem[]>(() => {
|
||||
if (lastQuery.value.length <= 2) {
|
||||
return [];
|
||||
}
|
||||
return dataTableResults.value.map((dataTable) => createDataTableCommand(dataTable));
|
||||
return dataTableResults.value.map((dataTable) => createDataTableCommand(dataTable, true));
|
||||
});
|
||||
|
||||
const dataTableNavigationCommands = computed<CommandBarItem[]>(() => {
|
||||
|
||||
+2
-9
@@ -244,6 +244,7 @@ describe('useNodeCommands', () => {
|
||||
|
||||
const openCommand = commands.value.find((cmd) => cmd.id === 'open-node');
|
||||
expect(openCommand?.children).toHaveLength(2);
|
||||
expect(openCommand?.children?.[0].title).toEqual('Start');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -297,15 +298,6 @@ describe('useNodeCommands', () => {
|
||||
expect(commands.value.length).toBeLessThanOrEqual(3);
|
||||
}
|
||||
});
|
||||
|
||||
it('should show root add node items when query is longer than 2 characters', () => {
|
||||
const { commands } = useNodeCommands({
|
||||
lastQuery: ref('htt'),
|
||||
activeNodeId: ref(null),
|
||||
});
|
||||
|
||||
expect(commands.value).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('root open node items', () => {
|
||||
@@ -333,6 +325,7 @@ describe('useNodeCommands', () => {
|
||||
|
||||
const rootOpenNodes = commands.value.filter((cmd) => cmd.id === 'node-1');
|
||||
expect(rootOpenNodes).toHaveLength(1);
|
||||
expect(rootOpenNodes[0].title).toEqual('generic.openResource');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+69
-61
@@ -13,6 +13,7 @@ import { useWorkflowsStore } from '@/app/stores/workflows.store';
|
||||
import { getResourcePermissions } from '@n8n/permissions';
|
||||
import NodeIcon from '@/app/components/NodeIcon.vue';
|
||||
import CommandBarItemTitle from '@/features/shared/commandBar/components/CommandBarItemTitle.vue';
|
||||
import type { INodeUi, SimplifiedNodeType } from '@/Interface';
|
||||
|
||||
const ITEM_ID = {
|
||||
ADD_NODE: 'add-node',
|
||||
@@ -45,81 +46,88 @@ export function useNodeCommands(options: {
|
||||
(workflowPermissions.value[permission] === true && !isReadOnly.value && !isArchived.value) ||
|
||||
workflowsStore.isNewWorkflow;
|
||||
|
||||
const mergedNodes = computed(() => {
|
||||
const httpOnlyCredentials = credentialsStore.httpOnlyCredentialTypes;
|
||||
const nodeTypes = nodeTypesStore.visibleNodeTypes;
|
||||
return generateMergedNodesAndActions(nodeTypes, httpOnlyCredentials).mergedNodes;
|
||||
});
|
||||
|
||||
const buildAddNodeCommand = (node: SimplifiedNodeType, isRoot: boolean): CommandBarItem => {
|
||||
const { name, displayName } = node;
|
||||
|
||||
const title = isRoot ? `${i18n.baseText('generic.add')} ${displayName}` : displayName;
|
||||
const section = isRoot
|
||||
? i18n.baseText('commandBar.sections.nodes')
|
||||
: i18n.baseText('commandBar.nodes.addNode');
|
||||
|
||||
return {
|
||||
id: name,
|
||||
title,
|
||||
section,
|
||||
keywords: [displayName],
|
||||
icon: {
|
||||
component: NodeIcon as Component,
|
||||
props: {
|
||||
nodeType: node,
|
||||
size: 16,
|
||||
},
|
||||
},
|
||||
handler: async () => {
|
||||
const nodes = await addNodes([{ type: name }]);
|
||||
if (nodes && nodes.length > 0) {
|
||||
canvasEventBus.emit('nodes:select', { ids: [nodes[0].id] });
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const addNodeCommands = computed<CommandBarItem[]>(() => {
|
||||
if (!hasPermission('update')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const httpOnlyCredentials = credentialsStore.httpOnlyCredentialTypes;
|
||||
const nodeTypes = nodeTypesStore.visibleNodeTypes;
|
||||
const { mergedNodes } = generateMergedNodesAndActions(nodeTypes, httpOnlyCredentials);
|
||||
return mergedNodes.map((node) => {
|
||||
const { name, displayName } = node;
|
||||
|
||||
return {
|
||||
id: name,
|
||||
title: {
|
||||
component: CommandBarItemTitle,
|
||||
props: {
|
||||
title: displayName,
|
||||
actionText: i18n.baseText('generic.add'),
|
||||
},
|
||||
},
|
||||
keywords: [displayName],
|
||||
icon: {
|
||||
component: NodeIcon as Component,
|
||||
props: {
|
||||
nodeType: node,
|
||||
size: 16,
|
||||
},
|
||||
},
|
||||
handler: async () => {
|
||||
const nodes = await addNodes([{ type: name }]);
|
||||
if (nodes && nodes.length > 0) {
|
||||
canvasEventBus.emit('nodes:select', { ids: [nodes[0].id] });
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
return mergedNodes.value.map((node) => buildAddNodeCommand(node, false));
|
||||
});
|
||||
|
||||
const rootAddNodeCommandItems = computed<CommandBarItem[]>(() => {
|
||||
if (lastQuery.value.length <= 2) {
|
||||
if (lastQuery.value.length <= 2 || !hasPermission('update')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return addNodeCommands.value;
|
||||
return mergedNodes.value.map((node) => buildAddNodeCommand(node, true));
|
||||
});
|
||||
|
||||
const openNodeCommands = computed<CommandBarItem[]>(() => {
|
||||
return editableWorkflow.value.nodes.map((node) => {
|
||||
const { id, name, type } = node;
|
||||
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
||||
const buildOpenNodeCommand = (node: INodeUi, isRoot: boolean): CommandBarItem => {
|
||||
const { id, name, type } = node;
|
||||
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
||||
const title = isRoot
|
||||
? i18n.baseText('generic.openResource', { interpolate: { resource: name } })
|
||||
: name;
|
||||
const section = isRoot
|
||||
? i18n.baseText('commandBar.sections.nodes')
|
||||
: i18n.baseText('commandBar.nodes.openNode');
|
||||
|
||||
return {
|
||||
id,
|
||||
title: {
|
||||
component: CommandBarItemTitle,
|
||||
props: {
|
||||
title: name,
|
||||
actionText: i18n.baseText('generic.open'),
|
||||
},
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
section,
|
||||
keywords: [name, type],
|
||||
icon: {
|
||||
component: NodeIcon,
|
||||
props: {
|
||||
nodeType,
|
||||
size: 16,
|
||||
},
|
||||
section: i18n.baseText('commandBar.sections.nodes'),
|
||||
keywords: [name, type],
|
||||
icon: {
|
||||
component: NodeIcon,
|
||||
props: {
|
||||
nodeType,
|
||||
size: 16,
|
||||
},
|
||||
},
|
||||
handler: () => {
|
||||
setNodeActive(id, 'command_bar');
|
||||
},
|
||||
placeholder: i18n.baseText('commandBar.nodes.searchPlaceholder'),
|
||||
};
|
||||
});
|
||||
},
|
||||
handler: () => {
|
||||
setNodeActive(id, 'command_bar');
|
||||
},
|
||||
placeholder: i18n.baseText('commandBar.nodes.searchPlaceholder'),
|
||||
};
|
||||
};
|
||||
|
||||
const openNodeCommands = computed<CommandBarItem[]>(() => {
|
||||
return editableWorkflow.value.nodes.map((node) => buildOpenNodeCommand(node, false));
|
||||
});
|
||||
|
||||
const rootOpenNodeCommandItems = computed<CommandBarItem[]>(() => {
|
||||
@@ -127,7 +135,7 @@ export function useNodeCommands(options: {
|
||||
return [];
|
||||
}
|
||||
|
||||
return openNodeCommands.value;
|
||||
return editableWorkflow.value.nodes.map((node) => buildOpenNodeCommand(node, true));
|
||||
});
|
||||
|
||||
const nodeCommands = computed<CommandBarItem[]>(() => {
|
||||
|
||||
+4
-18
@@ -291,6 +291,7 @@ describe('useProjectNavigationCommands', () => {
|
||||
|
||||
const rootProjects = commands.value.filter((cmd) => cmd.id === 'project-1');
|
||||
expect(rootProjects).toHaveLength(1);
|
||||
expect(rootProjects[0].title).toEqual('generic.openResource');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -341,12 +342,7 @@ describe('useProjectNavigationCommands', () => {
|
||||
const openCommand = commands.value.find((cmd) => cmd.id === 'open-project');
|
||||
const personalProject = openCommand?.children?.[0];
|
||||
expect(personalProject).toBeDefined();
|
||||
expect(personalProject?.title).toEqual({
|
||||
component: expect.any(Object),
|
||||
props: expect.objectContaining({
|
||||
title: 'projects.menu.personal',
|
||||
}),
|
||||
});
|
||||
expect(personalProject?.title).toEqual('projects.menu.personal');
|
||||
});
|
||||
|
||||
it('should show project name for team project', () => {
|
||||
@@ -365,12 +361,7 @@ describe('useProjectNavigationCommands', () => {
|
||||
const openCommand = commands.value.find((cmd) => cmd.id === 'open-project');
|
||||
const teamProject = openCommand?.children?.[0];
|
||||
expect(teamProject).toBeDefined();
|
||||
expect(teamProject?.title).toEqual({
|
||||
component: expect.any(Object),
|
||||
props: expect.objectContaining({
|
||||
title: 'Marketing Team',
|
||||
}),
|
||||
});
|
||||
expect(teamProject?.title).toEqual('Marketing Team');
|
||||
});
|
||||
|
||||
it('should show unnamed text for team project without name', () => {
|
||||
@@ -389,12 +380,7 @@ describe('useProjectNavigationCommands', () => {
|
||||
const openCommand = commands.value.find((cmd) => cmd.id === 'open-project');
|
||||
const unnamedProject = openCommand?.children?.[0];
|
||||
expect(unnamedProject).toBeDefined();
|
||||
expect(unnamedProject?.title).toEqual({
|
||||
component: expect.any(Object),
|
||||
props: expect.objectContaining({
|
||||
title: 'commandBar.projects.unnamed',
|
||||
}),
|
||||
});
|
||||
expect(unnamedProject?.title).toEqual('commandBar.projects.unnamed');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+14
-13
@@ -7,7 +7,6 @@ import type { ProjectListItem } from '@/features/collaboration/projects/projects
|
||||
import { useProjectsStore } from '@/features/collaboration/projects/projects.store';
|
||||
import type { CommandBarItem } from '../types';
|
||||
import { useGlobalEntityCreation } from '@/app/composables/useGlobalEntityCreation';
|
||||
import CommandBarItemTitle from '@/features/shared/commandBar/components/CommandBarItemTitle.vue';
|
||||
|
||||
const ITEM_ID = {
|
||||
CREATE_PROJECT: 'create-project',
|
||||
@@ -40,24 +39,26 @@ export function useProjectNavigationCommands(options: {
|
||||
);
|
||||
});
|
||||
|
||||
const createProjectCommand = (project: ProjectListItem): CommandBarItem => {
|
||||
const title =
|
||||
const createProjectCommand = (project: ProjectListItem, isRoot: boolean): CommandBarItem => {
|
||||
let title =
|
||||
project.type === 'personal'
|
||||
? i18n.baseText('projects.menu.personal')
|
||||
: project.name
|
||||
? project.name
|
||||
: i18n.baseText('commandBar.projects.unnamed');
|
||||
|
||||
if (isRoot) {
|
||||
title = i18n.baseText('generic.openResource', { interpolate: { resource: title } });
|
||||
}
|
||||
|
||||
const section = isRoot
|
||||
? i18n.baseText('commandBar.sections.projects')
|
||||
: i18n.baseText('commandBar.projects.open');
|
||||
|
||||
return {
|
||||
id: project.id,
|
||||
title: {
|
||||
component: CommandBarItemTitle,
|
||||
props: {
|
||||
title,
|
||||
actionText: i18n.baseText('generic.open'),
|
||||
},
|
||||
},
|
||||
section: i18n.baseText('commandBar.sections.projects'),
|
||||
title,
|
||||
section,
|
||||
keywords: [title],
|
||||
handler: () => {
|
||||
void router.push({
|
||||
@@ -71,13 +72,13 @@ export function useProjectNavigationCommands(options: {
|
||||
const openProjectCommands = computed<CommandBarItem[]>(() => {
|
||||
const isInProjectParent = activeNodeId.value === ITEM_ID.OPEN_PROJECT;
|
||||
if (!isInProjectParent) return [];
|
||||
return filteredProjects.value.map((project) => createProjectCommand(project));
|
||||
return filteredProjects.value.map((project) => createProjectCommand(project, false));
|
||||
});
|
||||
|
||||
const rootProjectItems = computed<CommandBarItem[]>(() => {
|
||||
const isRootWithQuery = activeNodeId.value === null && lastQuery.value.trim().length > 2;
|
||||
if (!isRootWithQuery) return [];
|
||||
return filteredProjects.value.map((project) => createProjectCommand(project));
|
||||
return filteredProjects.value.map((project) => createProjectCommand(project, true));
|
||||
});
|
||||
|
||||
const projectNavigationCommands = computed<CommandBarItem[]>(() => {
|
||||
|
||||
+4
-4
@@ -308,7 +308,7 @@ describe('useRecentResources', () => {
|
||||
expect(nodeItems).toHaveLength(2);
|
||||
// node-2 is most recent since it was tracked last
|
||||
expect(nodeItems[0].id).toBe('recent-node-workflow-1-node-2');
|
||||
expect(nodeItems[0].title).toBe('Test Node 2');
|
||||
expect(nodeItems[0].title).toBe('generic.openResource');
|
||||
expect(nodeItems[1].id).toBe('recent-node-workflow-1-node-1');
|
||||
expect(nodeItems[0].section).toBe('commandBar.sections.recent');
|
||||
});
|
||||
@@ -374,9 +374,9 @@ describe('useRecentResources', () => {
|
||||
|
||||
expect(workflowItems).toHaveLength(2);
|
||||
expect(workflowItems[0].id).toBe('recent-workflow-workflow-2');
|
||||
expect(workflowItems[0].title).toBe('Workflow 2');
|
||||
expect(workflowItems[0].title).toBe('generic.openResource');
|
||||
expect(workflowItems[1].id).toBe('recent-workflow-workflow-1');
|
||||
expect(workflowItems[1].title).toBe('Workflow 1');
|
||||
expect(workflowItems[1].title).toBe('generic.openResource');
|
||||
});
|
||||
|
||||
it('should limit workflow items to MAX_RECENT_WORKFLOWS_TO_DISPLAY (3)', () => {
|
||||
@@ -437,7 +437,7 @@ describe('useRecentResources', () => {
|
||||
const items = commands.value;
|
||||
const workflowItem = items.find((item) => item.id === 'recent-workflow-workflow-unnamed');
|
||||
|
||||
expect(workflowItem?.title).toBe('commandBar.workflows.unnamed');
|
||||
expect(workflowItem?.title).toBe('generic.openResource');
|
||||
});
|
||||
|
||||
it('should navigate using window.location.href when workflow item handler is executed', () => {
|
||||
|
||||
+8
-2
@@ -109,7 +109,9 @@ export function useRecentResources() {
|
||||
|
||||
items.push({
|
||||
id: `recent-node-${currentWorkflowId}-${recentNode.nodeId}`,
|
||||
title: node.name,
|
||||
title: i18n.baseText('generic.openResource', {
|
||||
interpolate: { resource: node.name },
|
||||
}),
|
||||
section: i18n.baseText('commandBar.sections.recent'),
|
||||
icon: {
|
||||
component: NodeIcon as Component,
|
||||
@@ -145,7 +147,11 @@ export function useRecentResources() {
|
||||
|
||||
items.push({
|
||||
id: `recent-workflow-${recentWorkflow.id}`,
|
||||
title: workflow.name || i18n.baseText('commandBar.workflows.unnamed'),
|
||||
title: i18n.baseText('generic.openResource', {
|
||||
interpolate: {
|
||||
resource: workflow.name || i18n.baseText('commandBar.workflows.unnamed'),
|
||||
},
|
||||
}),
|
||||
section: i18n.baseText('commandBar.sections.recent'),
|
||||
icon: {
|
||||
component: N8nIcon,
|
||||
|
||||
+39
@@ -304,4 +304,43 @@ describe('useWorkflowNavigationCommands', () => {
|
||||
// Icon present when matched by node type
|
||||
expect(item.icon).toBeDefined();
|
||||
});
|
||||
|
||||
it('root workflow items have correct title and section', async () => {
|
||||
const api = useWorkflowNavigationCommands({
|
||||
lastQuery: ref('Alpha'),
|
||||
activeNodeId: ref(null),
|
||||
currentProjectName: ref('My Project'),
|
||||
});
|
||||
(api.handlers?.onCommandBarChange as (q: string) => void)('Alpha');
|
||||
await waitFor(() => {
|
||||
expect(api.commands.value.length).toBeGreaterThan(2);
|
||||
});
|
||||
const alphaWf = api.commands.value.find((c) => c.id === 'w1');
|
||||
expect((alphaWf?.title as unknown as { props?: { title?: string } }).props?.title).toBe(
|
||||
'generic.openResource',
|
||||
);
|
||||
expect(alphaWf?.section).toBe('commandBar.sections.workflows');
|
||||
});
|
||||
|
||||
it('open workflow children have correct title and section', async () => {
|
||||
const api = useWorkflowNavigationCommands({
|
||||
lastQuery: ref(''),
|
||||
activeNodeId: ref(null),
|
||||
currentProjectName: ref('My Project'),
|
||||
});
|
||||
api.handlers?.onCommandBarNavigateTo?.('open-workflow');
|
||||
await waitFor(() => {
|
||||
const open = api.commands.value.find((c) => c.id === 'open-workflow');
|
||||
expect(open?.children?.length).toBeGreaterThan(0);
|
||||
});
|
||||
const children = api.commands.value.find((c) => c.id === 'open-workflow')?.children;
|
||||
if (!children) {
|
||||
throw new Error('Open workflow command not found');
|
||||
}
|
||||
expect(children).toHaveLength(2);
|
||||
expect((children[0].title as unknown as { props?: { title?: string } }).props?.title).toBe(
|
||||
'Alpha',
|
||||
);
|
||||
expect(children[0].section).toBe('commandBar.workflows.open');
|
||||
});
|
||||
});
|
||||
|
||||
+15
-6
@@ -219,7 +219,7 @@ export function useWorkflowNavigationCommands(options: {
|
||||
return parts.join(' / ');
|
||||
};
|
||||
|
||||
const createWorkflowCommand = (workflow: IWorkflowDb): CommandBarItem => {
|
||||
const createWorkflowCommand = (workflow: IWorkflowDb, isRoot: boolean): CommandBarItem => {
|
||||
let keywords = workflowKeywords.value.get(workflow.id) ?? [];
|
||||
const matchedNodeType = workflowMatchedNodeTypes.value.get(workflow.id);
|
||||
|
||||
@@ -251,18 +251,27 @@ export function useWorkflowNavigationCommands(options: {
|
||||
|
||||
const suffix = getWorkflowProjectSuffix(workflow);
|
||||
|
||||
const name = workflow.name || i18n.baseText('commandBar.workflows.unnamed');
|
||||
const title = isRoot
|
||||
? i18n.baseText('generic.openResource', {
|
||||
interpolate: { resource: name },
|
||||
})
|
||||
: name;
|
||||
const section = isRoot
|
||||
? i18n.baseText('commandBar.sections.workflows')
|
||||
: i18n.baseText('commandBar.workflows.open');
|
||||
|
||||
return {
|
||||
id: workflow.id,
|
||||
title: {
|
||||
component: CommandBarItemTitle,
|
||||
props: {
|
||||
title: workflow.name || i18n.baseText('commandBar.workflows.unnamed'),
|
||||
title,
|
||||
suffix,
|
||||
...(suffix ? { suffixIcon: getProjectIcon(workflow) } : {}),
|
||||
actionText: i18n.baseText('generic.open'),
|
||||
},
|
||||
},
|
||||
section: i18n.baseText('commandBar.sections.workflows'),
|
||||
section,
|
||||
...(keywords.length > 0 ? { keywords } : {}),
|
||||
...(icon ? { icon } : {}),
|
||||
handler: () => {
|
||||
@@ -276,14 +285,14 @@ export function useWorkflowNavigationCommands(options: {
|
||||
};
|
||||
|
||||
const openWorkflowCommands = computed<CommandBarItem[]>(() => {
|
||||
return workflowResults.value.map((workflow) => createWorkflowCommand(workflow));
|
||||
return workflowResults.value.map((workflow) => createWorkflowCommand(workflow, false));
|
||||
});
|
||||
|
||||
const rootWorkflowItems = computed<CommandBarItem[]>(() => {
|
||||
if (lastQuery.value.length <= 2) {
|
||||
return [];
|
||||
}
|
||||
return workflowResults.value.map((workflow) => createWorkflowCommand(workflow));
|
||||
return workflowResults.value.map((workflow) => createWorkflowCommand(workflow, true));
|
||||
});
|
||||
|
||||
const workflowNavigationCommands = computed<CommandBarItem[]>(() => {
|
||||
|
||||
Reference in New Issue
Block a user