Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export interface DesignerOptionsState {
enableNestedAgentLoops?: boolean; // allow agent loops to be added inside regular loops (requires bundle version >= 1.115.0)
disableMcpClientTools?: boolean; // hide MCP client tools from browse panel
disableNativeMcpClientTools?: boolean; // hide native (built-in) MCP client tools tab from browse panel
hiddenBrowseCategories?: string[]; // hide specific categories from browse panel by category key (e.g., ['aiAgent', 'humanInTheLoop', 'favorites'])
};
nodeSelectAdditionalCallback?: (nodeId: string) => any;
panelTabHideKeys?: PANEL_TAB_NAMES[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export const useDisableNativeMcpClientTools = () => {
return useSelector((state: RootState) => state.designerOptions.hostOptions?.disableNativeMcpClientTools ?? false);
};

export const useHiddenBrowseCategories = () => {
return useSelector((state: RootState) => state.designerOptions.hostOptions?.hiddenBrowseCategories ?? []);
};
Comment thread
SakoPak marked this conversation as resolved.

export const useAreDesignerOptionsInitialized = () => {
return useSelector((state: RootState) => state.designerOptions?.designerOptionsInitialized ?? false);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,178 @@ describe('browse helper', () => {
expect(humanInTheLoop?.connectorFilters?.name).toContain('teams');
});
});

describe('hiddenBrowseCategories', () => {
describe('action categories', () => {
test('should hide aiAgent when included in hiddenCategories', () => {
const categories = getActionCategories(true, false, false, ['aiAgent']);
const aiAgent = categories.find((c) => c.key === 'aiAgent');
expect(aiAgent?.visible).toBe(false);
});

test('should hide humanInTheLoop when included in hiddenCategories', () => {
const categories = getActionCategories(false, false, false, ['humanInTheLoop']);
const humanInTheLoop = categories.find((c) => c.key === 'humanInTheLoop');
expect(humanInTheLoop?.visible).toBe(false);
});

test('should hide multiple categories', () => {
const categories = getActionCategories(true, false, false, ['aiAgent', 'humanInTheLoop', 'favorites']);
expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
expect(categories.find((c) => c.key === 'humanInTheLoop')?.visible).toBe(false);
expect(categories.find((c) => c.key === 'favorites')?.visible).toBe(false);
});

test('should not affect non-hidden categories', () => {
const categories = getActionCategories(true, false, false, ['aiAgent']);
const actionInApp = categories.find((c) => c.key === 'actionInApp');
expect(actionInApp?.visible).toBeUndefined(); // undefined = visible by default
});

test('should work with empty array', () => {
const categories = getActionCategories(true, false, false, []);
expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
});

test('should work with undefined', () => {
const categories = getActionCategories(true, false, false, undefined);
expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
});

test('should respect allowAgents flag even when not in hiddenCategories', () => {
const categoriesWithAgents = getActionCategories(true, false, false, []);
const categoriesWithoutAgents = getActionCategories(false, false, false, []);

expect(categoriesWithAgents.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
expect(categoriesWithoutAgents.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
});

test('should hide aiAgent via hiddenCategories even when allowAgents is true', () => {
const categories = getActionCategories(true, false, false, ['aiAgent']);
expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
});
});

describe('trigger categories', () => {
test('should hide manual trigger when included in hiddenCategories', () => {
const categories = getTriggerCategories(['manual']);
expect(categories.find((c) => c.key === 'manual')?.visible).toBe(false);
});

test('should hide schedule trigger when included in hiddenCategories', () => {
const categories = getTriggerCategories(['schedule']);
expect(categories.find((c) => c.key === 'schedule')?.visible).toBe(false);
});

test('should hide multiple trigger categories', () => {
const categories = getTriggerCategories(['manual', 'schedule', 'appEvent']);
expect(categories.find((c) => c.key === 'manual')?.visible).toBe(false);
expect(categories.find((c) => c.key === 'schedule')?.visible).toBe(false);
expect(categories.find((c) => c.key === 'appEvent')?.visible).toBe(false);
});

test('should not affect non-hidden trigger categories', () => {
const categories = getTriggerCategories(['manual']);
const schedule = categories.find((c) => c.key === 'schedule');
expect(schedule?.visible).toBeUndefined(); // undefined = visible by default
});

test('should work with empty array for triggers', () => {
const categories = getTriggerCategories([]);
expect(categories.find((c) => c.key === 'manual')?.visible).toBeUndefined();
});

test('should work with undefined for triggers', () => {
const categories = getTriggerCategories(undefined);
expect(categories.find((c) => c.key === 'manual')?.visible).toBeUndefined();
});
});
});

describe('hiddenBrowseCategories', () => {
describe('action categories', () => {
test('should hide aiAgent when included in hiddenCategories', () => {
const categories = getActionCategories(true, false, false, ['aiAgent']);
const aiAgent = categories.find((c) => c.key === 'aiAgent');
expect(aiAgent?.visible).toBe(false);
});

test('should hide humanInTheLoop when included in hiddenCategories', () => {
const categories = getActionCategories(false, false, false, ['humanInTheLoop']);
const humanInTheLoop = categories.find((c) => c.key === 'humanInTheLoop');
expect(humanInTheLoop?.visible).toBe(false);
});

test('should hide multiple categories', () => {
const categories = getActionCategories(true, false, false, ['aiAgent', 'humanInTheLoop', 'favorites']);
expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
expect(categories.find((c) => c.key === 'humanInTheLoop')?.visible).toBe(false);
expect(categories.find((c) => c.key === 'favorites')?.visible).toBe(false);
});

test('should not affect non-hidden categories', () => {
const categories = getActionCategories(true, false, false, ['aiAgent']);
const actionInApp = categories.find((c) => c.key === 'actionInApp');
expect(actionInApp?.visible).toBeUndefined(); // undefined = visible by default
});

test('should work with empty array', () => {
const categories = getActionCategories(true, false, false, []);
expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
});

test('should work with undefined', () => {
const categories = getActionCategories(true, false, false, undefined);
expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
});

test('should respect allowAgents flag even when not in hiddenCategories', () => {
const categoriesWithAgents = getActionCategories(true, false, false, []);
const categoriesWithoutAgents = getActionCategories(false, false, false, []);

expect(categoriesWithAgents.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
expect(categoriesWithoutAgents.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
});

test('should hide aiAgent via hiddenCategories even when allowAgents is true', () => {
const categories = getActionCategories(true, false, false, ['aiAgent']);
expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
});
});
Comment thread
SakoPak marked this conversation as resolved.

describe('trigger categories', () => {
test('should hide manual trigger when included in hiddenCategories', () => {
const categories = getTriggerCategories(['manual']);
expect(categories.find((c) => c.key === 'manual')?.visible).toBe(false);
});

test('should hide schedule trigger when included in hiddenCategories', () => {
const categories = getTriggerCategories(['schedule']);
expect(categories.find((c) => c.key === 'schedule')?.visible).toBe(false);
});

test('should hide multiple trigger categories', () => {
const categories = getTriggerCategories(['manual', 'schedule', 'appEvent']);
expect(categories.find((c) => c.key === 'manual')?.visible).toBe(false);
expect(categories.find((c) => c.key === 'schedule')?.visible).toBe(false);
expect(categories.find((c) => c.key === 'appEvent')?.visible).toBe(false);
});

test('should not affect non-hidden trigger categories', () => {
const categories = getTriggerCategories(['manual']);
const schedule = categories.find((c) => c.key === 'schedule');
expect(schedule?.visible).toBeUndefined(); // undefined = visible by default
});

test('should work with empty array for triggers', () => {
const categories = getTriggerCategories([]);
expect(categories.find((c) => c.key === 'manual')?.visible).toBeUndefined();
});

test('should work with undefined for triggers', () => {
const categories = getTriggerCategories(undefined);
expect(categories.find((c) => c.key === 'manual')?.visible).toBeUndefined();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { AppDispatch } from '../../../../core';
import { equals, type DiscoveryOperation, type DiscoveryResultTypes } from '@microsoft/logic-apps-shared';
import { getNodeId } from '../helpers';
import { getTriggerCategories, getActionCategories, BrowseCategoryType } from './helper';
import { useDisableMcpClientTools } from '../../../../core/state/designerOptions/designerOptionsSelectors';
import { useDisableMcpClientTools, useHiddenBrowseCategories } from '../../../../core/state/designerOptions/designerOptionsSelectors';

interface BrowseViewProps {
isTrigger?: boolean;
Expand All @@ -35,8 +35,11 @@ export const BrowseView = ({ isTrigger = false, onOperationClick }: BrowseViewPr
const allowAgents = equals(relationshipIds.graphId, 'root');
const isAddingAgentTool = useIsAddingAgentTool();
const disableMcpClientTools = useDisableMcpClientTools();
const hiddenCategories = useHiddenBrowseCategories();

const categories = isTrigger ? getTriggerCategories() : getActionCategories(allowAgents, isAddingAgentTool, disableMcpClientTools);
const categories = isTrigger
? getTriggerCategories(hiddenCategories)
: getActionCategories(allowAgents, isAddingAgentTool, disableMcpClientTools, hiddenCategories);

const addTriggerOperation = useCallback(
(operation: DiscoveryOperation<DiscoveryResultTypes>) => {
Expand Down
28 changes: 24 additions & 4 deletions libs/designer-v2/src/lib/ui/panel/recommendation/browse/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,15 @@ export interface BrowseCategoryConfig {
connectorFilters?: ConnectorFilterTypes;
}

export const getTriggerCategories = (): BrowseCategoryConfig[] => {
/**
* Get trigger categories for the browse panel.
* @param hiddenCategories - Array of category keys to hide. Available keys: 'manual', 'schedule', 'appEvent', 'azure', 'workflowExecution', 'chatMessage', 'evaluation', 'otherWays'
* @returns Array of trigger category configurations
*/
export const getTriggerCategories = (hiddenCategories?: string[]): BrowseCategoryConfig[] => {
const intl = getIntl();

return [
const categories = [
{
key: 'manual',
text: intl.formatMessage({
Expand Down Expand Up @@ -183,16 +188,28 @@ export const getTriggerCategories = (): BrowseCategoryConfig[] => {
type: BrowseCategoryType.BROWSE,
},
];

// Apply hidden categories filter
return categories.map((category) => (hiddenCategories?.includes(category.key) ? { ...category, visible: false } : category));
};

/**
* Get action categories for the browse panel.
* @param allowAgents - Whether to show AI agent category (based on graph root)
* @param isAddingAgentTool - Whether currently adding an agent tool
* @param disableMcpClientTools - Whether to disable MCP client tools category
* @param hiddenCategories - Array of category keys to hide. Available keys: 'favorites', 'mcpServers', 'aiAgent', 'actionInApp', 'dataTransformation', 'simpleOperations', 'humanInTheLoop'
* @returns Array of action category configurations
*/
export const getActionCategories = (
allowAgents?: boolean,
isAddingAgentTool?: boolean,
disableMcpClientTools?: boolean
disableMcpClientTools?: boolean,
hiddenCategories?: string[]
): BrowseCategoryConfig[] => {
const intl = getIntl();

return [
const categories = [
{
key: 'favorites',
text: intl.formatMessage({
Expand Down Expand Up @@ -337,4 +354,7 @@ export const getActionCategories = (
},
},
];

// Apply hidden categories filter
return categories.map((category) => (hiddenCategories?.includes(category.key) ? { ...category, visible: false } : category));
};
Loading