Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -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,
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -358,10 +371,10 @@ export const ProjectTree: React.FC<Props> = ({
});
};

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,
Expand All @@ -388,10 +401,16 @@ export const ProjectTree: React.FC<Props> = ({
triggers: ITrigger[],
startDepth: number
) => {
const groupDisplayName =
groupName === NoGroupingTriggerGroupName ? formatMessage('form-wide operations') : groupName;
const key = `${projectId}.${dialog.id}.group-${groupName}`;

return (
<ExpandableNode key={key} depth={startDepth} summary={renderTriggerGroupHeader(groupName, dialog, projectId)}>
<ExpandableNode
key={key}
depth={startDepth}
summary={renderTriggerGroupHeader(groupDisplayName, dialog, projectId)}
>
<div>{renderTriggerList(triggers, dialog, projectId)}</div>
</ExpandableNode>
);
Expand All @@ -405,7 +424,7 @@ export const ProjectTree: React.FC<Props> = ({

const triggerGroups = Object.keys(groupedTriggers);

return triggerGroups.map((triggerGroup) => {
return triggerGroups.sort(sortTriggerGroups).map((triggerGroup) => {
return renderTriggerGroup(projectId, dialog, triggerGroup, groupedTriggers[triggerGroup], startDepth);
});
};
Expand Down
28 changes: 25 additions & 3 deletions Composer/packages/lib/indexers/src/groupTriggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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": ["<name1>", "<name2", ... ]
if (content.$designer?.propertyGroups && Array.isArray(content.$designer?.propertyGroups === 'array')) {
if (content.$designer?.propertyGroups && Array.isArray(content.$designer?.propertyGroups)) {
foundProperties.push(...content.$designer.propertyGroups);
}

Expand All @@ -20,6 +22,11 @@ const getPropertyReferences = (content: any) => {
foundProperties.push(content.property);
}

// had "expectedProperties" : ["<name1>", "<name2", ... ]
if (content.expectedProperties && Array.isArray(content.expectedProperties)) {
foundProperties.push(...content.expectedProperties);
}

// has condition : "<expresssion referencing properties>"
if (content.condition) {
const expressionParser = new ExpressionParser();
Expand All @@ -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": ["<name1>", "<name2", ... ]
* - "property": "<name>"
* - "expectedProperties" : ["<name1>", "<name2", ... ]
* - condition : "<expresssion referencing properties>"
* - 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[] }
Expand All @@ -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) => {
Expand Down