diff --git a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx index 01db61ee6a..fa230210fa 100644 --- a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx +++ b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx @@ -13,7 +13,7 @@ import debounce from 'lodash/debounce'; import { useRecoilValue } from 'recoil'; import { ISearchBoxStyles } from 'office-ui-fabric-react/lib/SearchBox'; import isEqual from 'lodash/isEqual'; -import { extractSchemaProperties, groupTriggersByPropertyReference } from '@bfc/indexers'; +import { extractSchemaProperties, groupTriggersByPropertyReference, NoGroupingTriggerGroupName } from '@bfc/indexers'; import { dispatcherState, @@ -70,6 +70,19 @@ const tree = css` const SUMMARY_ARROW_SPACE = 28; // the rough pixel size of the dropdown arrow to the left of a Details/Summary element +// -------------------- Helper functions -------------------- // + +// sort trigger groups so that NoGroupingTriggerGroupName is last +const sortTriggerGroups = (x: string, y: string): number => { + if (x === NoGroupingTriggerGroupName && y !== NoGroupingTriggerGroupName) { + return 1; + } else if (y === NoGroupingTriggerGroupName && x !== NoGroupingTriggerGroupName) { + return -1; + } + + return x.localeCompare(y); +}; + // -------------------- ProjectTree -------------------- // export type TreeLink = { @@ -358,10 +371,10 @@ export const ProjectTree: React.FC = ({ }); }; - const renderTriggerGroupHeader = (groupName: string, dialog: DialogInfo, projectId: string) => { + const renderTriggerGroupHeader = (displayName: string, dialog: DialogInfo, projectId: string) => { const link: TreeLink = { dialogName: dialog.id, - displayName: groupName, + displayName, isRoot: false, projectId: projectId, skillId: null, @@ -388,10 +401,16 @@ export const ProjectTree: React.FC = ({ triggers: ITrigger[], startDepth: number ) => { + const groupDisplayName = + groupName === NoGroupingTriggerGroupName ? formatMessage('form-wide operations') : groupName; const key = `${projectId}.${dialog.id}.group-${groupName}`; return ( - +
{renderTriggerList(triggers, dialog, projectId)}
); @@ -405,7 +424,7 @@ export const ProjectTree: React.FC = ({ const triggerGroups = Object.keys(groupedTriggers); - return triggerGroups.map((triggerGroup) => { + return triggerGroups.sort(sortTriggerGroups).map((triggerGroup) => { return renderTriggerGroup(projectId, dialog, triggerGroup, groupedTriggers[triggerGroup], startDepth); }); }; diff --git a/Composer/packages/lib/indexers/src/groupTriggers.ts b/Composer/packages/lib/indexers/src/groupTriggers.ts index 3fcb366a6c..dc69ca327d 100644 --- a/Composer/packages/lib/indexers/src/groupTriggers.ts +++ b/Composer/packages/lib/indexers/src/groupTriggers.ts @@ -5,13 +5,15 @@ import { DialogInfo, ITrigger } from '@bfc/shared'; import { ExpressionParser } from 'adaptive-expressions'; import uniq from 'lodash/uniq'; +export const NoGroupingTriggerGroupName = '(none)'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const getPropertyReferences = (content: any) => { const foundProperties: string[] = []; if (content) { // has $designer: { "propertyGroups": ["", " { foundProperties.push(content.property); } + // had "expectedProperties" : ["", "" if (content.condition) { const expressionParser = new ExpressionParser(); @@ -45,9 +52,24 @@ const getTriggerPropertyReferences = (trigger: ITrigger) => { } } - return uniq(foundProperties); + const result = uniq(foundProperties); + + if (result.length === 0) { + return [NoGroupingTriggerGroupName]; + } + + return result; }; +/** + * Groups triggers by the property name they reference in: + * - $designer: { "propertyGroups": ["", "" + * - "expectedProperties" : ["", "" + * - Any of the trigger's action that reference a property. + * If a trigger does not reference a property, it will be grouped under "(none)" + */ export const groupTriggersByPropertyReference = ( dialog: DialogInfo, options?: { allowMultiParent?: boolean; validProperties?: string[] } @@ -56,7 +78,7 @@ export const groupTriggersByPropertyReference = ( const validProperties = options?.validProperties; const isValidProperty = validProperties - ? (x: string | undefined) => x && validProperties.findIndex((p) => x === p) !== -1 + ? (x: string | undefined) => x && (x === NoGroupingTriggerGroupName || validProperties.includes(x)) : () => true; const addResult = (property: string, trigger: ITrigger) => {