Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
4 changes: 4 additions & 0 deletions Composer/cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ Cypress.Commands.add('addEventHandler', handler => {
cy.getByTestId('AddNewTrigger').click();
cy.get(`[data-testid="triggerTypeDropDown"]`).click();
cy.getByText(handler).click();
if (handler === 'Handle a Dialog Event') {
cy.get(`[data-testid="eventTypeDropDown"]`).click();
cy.getByText('consultDialog').click();
}
cy.get('input[data-testid="triggerName"]').type(`__TestTrigger`);
cy.get(`[data-testid="triggerFormSubmit"]`).click();
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,35 @@ import { Dialog, DialogType } from 'office-ui-fabric-react';
import formatMessage from 'format-message';
import { DialogFooter, PrimaryButton, DefaultButton, Stack, TextField, IDropdownOption } from 'office-ui-fabric-react';
import { Dropdown } from 'office-ui-fabric-react/lib/Dropdown';
import { get } from 'lodash';

import { addNewTrigger, getTriggerTypes, TriggerFormData, TriggerFormDataErrors } from '../../utils/dialogUtil';
import {
addNewTrigger,
getTriggerTypes,
TriggerFormData,
TriggerFormDataErrors,
eventTypeKey,
} from '../../utils/dialogUtil';
import { StoreContext } from '../../store';
import { DialogInfo } from '../../store/types';

import { styles, dropdownStyles, name, dialogWindow, description } from './styles';
import { styles, dropdownStyles, name, dialogWindow, constraint } from './styles';

const isValidName = name => {
const nameRegex = /^[a-zA-Z0-9-_.]+$/;
return nameRegex.test(name);
};
const validateForm = (data: TriggerFormData): TriggerFormDataErrors => {
const errors: TriggerFormDataErrors = {};
const { name, $type } = data;
const { name, $type, eventType } = data;
if (!name || !isValidName(name)) {
errors.name = formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.');
}

if ($type === eventTypeKey && !eventType) {
errors.eventType = formatMessage('please select a event type');
}

if (!$type) {
errors.$type = formatMessage('please select a trigger type');
}
Expand All @@ -37,7 +49,8 @@ const initialFormData: TriggerFormData = {
errors: {},
$type: '',
name: '',
description: '',
constraint: '',
eventType: '',
};

const triggerTypeOptions: IDropdownOption[] = getTriggerTypes();
Expand All @@ -46,7 +59,7 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
const { isOpen, onDismiss, onSubmit, dialogId } = props;
const [formData, setFormData] = useState(initialFormData);
const { state } = useContext(StoreContext);
const { dialogs } = state;
const { dialogs, schemas } = state;

const onClickSubmitButton = e => {
e.preventDefault();
Expand All @@ -65,16 +78,26 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
};

const onSelectTriggerType = (e, option) => {
delete formData.eventType;
setFormData({ ...formData, $type: option.key });
};

const onSelectEventType = (e, option) => {
setFormData({ ...formData, eventType: option.key });
};

const updateForm = field => (e, newValue) => {
setFormData({
...formData,
[field]: newValue,
});
};

const eventTypes = get(schemas, `sdk.content.definitions.['${eventTypeKey}'].properties.events.items.enum`, []).map(
t => {
return { key: t, text: t };
}
);
const showEventDropDown = formData.$type === eventTypeKey;
return (
<Dialog
hidden={!isOpen}
Expand All @@ -99,6 +122,18 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
errorMessage={formData.errors.$type}
data-testid={'triggerTypeDropDown'}
/>

{showEventDropDown && (
<Dropdown
placeholder="select a event type"
label="What is the event?"
options={eventTypes}
styles={dropdownStyles}
onChange={onSelectEventType}
errorMessage={formData.errors.eventType}
data-testid={'eventTypeDropDown'}
/>
)}
<TextField
label={formatMessage('What is the name of this trigger?')}
styles={name}
Expand All @@ -107,12 +142,12 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
data-testid={'triggerName'}
/>
<TextField
styles={description}
label={formatMessage('Description')}
styles={constraint}
label={formatMessage('Constraint')}
multiline
resizable={false}
onChange={updateForm('description')}
data-testid={'triggerDescription'}
onChange={updateForm('constraint')}
data-testid={'triggerConstraint'}
/>
</Stack>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,6 @@ export const name = {
subComponentStyles: textFieldlabel,
};

export const description = {
export const constraint = {
subComponentStyles: textFieldlabel,
};
2 changes: 2 additions & 0 deletions Composer/packages/client/src/store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export interface BreadcrumbItem {

export interface BotSchemas {
editor?: any;
sdk?: any;
diagnostics?: any[];
}

export interface State {
Expand Down
17 changes: 14 additions & 3 deletions Composer/packages/client/src/utils/dialogUtil.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { get, set, cloneDeep } from 'lodash';
import { ConceptLabels, seedNewDialog, dialogGroups, DialogGroup } from 'shared-menus';
import { ConceptLabels, seedNewDialog, dialogGroups, DialogGroup, SDKTypes } from 'shared-menus';
import { ExpressionEngine } from 'botbuilder-expression-parser';
import nanoid from 'nanoid/generate';
import { IDropdownOption } from 'office-ui-fabric-react';
Expand All @@ -17,20 +17,24 @@ interface DialogsMap {
export interface TriggerFormData {
errors: TriggerFormDataErrors;
$type: string;
eventType: string;
name: string;
description: string;
constraint: string;
}

export interface TriggerFormDataErrors {
$type?: string;
name?: string;
eventType?: string;
}

export function getDialog(dialogs: DialogInfo[], dialogId: string) {
const dialog = dialogs.find(item => item.id === dialogId);
return cloneDeep(dialog);
}

export const eventTypeKey: string = SDKTypes.OnDialogEvent;

export function getFriendlyName(data) {
if (get(data, '$designer.name')) {
return get(data, '$designer.name');
Expand All @@ -56,9 +60,16 @@ export function getNewDesigner(name: string, description: string) {

export function insert(content, path: string, position: number | undefined, data: TriggerFormData) {
const current = get(content, path, []);
const optionalAttributes: { constraint?: string; events?: string[] } = {};
if (data.constraint) {
optionalAttributes.constraint = data.constraint;
}
if (data.eventType) {
optionalAttributes.events = [data.eventType];
}
const newStep = {
$type: data.$type,
...seedNewDialog(data.$type, { name: data.name, description: data.description }),
...seedNewDialog(data.$type, { name: data.name }, optionalAttributes),
};

const insertAt = typeof position === 'undefined' ? current.length : position;
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/lib/shared-menus/src/appschema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,11 @@ export const dialogGroups: DialogGroupsMap = {
[DialogGroup.EVENTS]: {
label: 'Events',
types: [
SDKTypes.OnDialogEvent,
SDKTypes.OnIntent,
SDKTypes.OnUnknownIntent,
SDKTypes.OnConversationUpdateActivity,
SDKTypes.OnBeginDialog,
SDKTypes.OnDialogEvent,
],
},
[DialogGroup.ADVANCED_EVENTS]: {
Expand Down
13 changes: 11 additions & 2 deletions Composer/packages/lib/shared-menus/src/dialogFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import nanoid from 'nanoid/generate';

interface DesignerAttributes {
name?: string;
description?: string;
}

interface OptionalAttributes {
constraint?: string;
events?: string[];
}

const initialDialogShape = {
Expand All @@ -12,12 +16,17 @@ const initialDialogShape = {
},
};

export const seedNewDialog = ($type: string, designerAttributes: DesignerAttributes): object => {
export const seedNewDialog = (
$type: string,
designerAttributes: DesignerAttributes,
optionalAttributes?: OptionalAttributes
): object => {
return {
$designer: {
id: nanoid('1234567890', 6),
...designerAttributes,
},
...optionalAttributes,
...(initialDialogShape[$type] || {}),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export class DialogIndexer {
trigers.push(trigger);
}
});
return true;
}
return false;
};
Expand Down