diff --git a/Composer/packages/client/__tests__/components/design.test.js b/Composer/packages/client/__tests__/components/design.test.js
index ead441d63f..dbd3ce46fa 100644
--- a/Composer/packages/client/__tests__/components/design.test.js
+++ b/Composer/packages/client/__tests__/components/design.test.js
@@ -10,6 +10,13 @@ import { dialogs } from '../constants.json';
import { TriggerCreationModal } from './../../src/components/ProjectTree/TriggerCreationModal';
import { ProjectTree } from './../../src/components/ProjectTree';
import { CreateDialogModal } from './../../src/pages/design/createDialogModal';
+
+jest.mock('@bfc/code-editor', () => {
+ return {
+ LuEditor: () =>
,
+ };
+});
+
describe('', () => {
it('should render the ProjectTree', async () => {
const dialogId = 'Main';
diff --git a/Composer/packages/client/src/ShellApi.ts b/Composer/packages/client/src/ShellApi.ts
index ddbacb8775..ed3dd20dea 100644
--- a/Composer/packages/client/src/ShellApi.ts
+++ b/Composer/packages/client/src/ShellApi.ts
@@ -217,9 +217,9 @@ export const ShellApi: React.FC = () => {
if (!file) throw new Error(`lu file ${id} not found`);
if (!intentName) throw new Error(`intentName is missing or empty`);
- const newLuContent = luUtil.updateIntent(file.content, intentName, intent);
+ const content = luUtil.updateIntent(file.content, intentName, intent);
- return await updateLuFile({ id, newLuContent });
+ return await updateLuFile({ id, content });
}
async function addLuIntentHandler({ id, intent }, event) {
@@ -227,9 +227,9 @@ export const ShellApi: React.FC = () => {
const file = luFiles.find(file => file.id === id);
if (!file) throw new Error(`lu file ${id} not found`);
- const newLuContent = luUtil.addIntent(file.content, intent);
+ const content = luUtil.addIntent(file.content, intent);
- return await updateLuFile({ id, newLuContent });
+ return await updateLuFile({ id, content });
}
async function removeLuIntentHandler({ id, intentName }, event) {
@@ -238,9 +238,9 @@ export const ShellApi: React.FC = () => {
if (!file) throw new Error(`lu file ${id} not found`);
if (!intentName) throw new Error(`intentName is missing or empty`);
- const newLuContent = luUtil.removeIntent(file.content, intentName);
+ const content = luUtil.removeIntent(file.content, intentName);
- return await updateLuFile({ id, newLuContent });
+ return await updateLuFile({ id, content });
}
async function fileHandler(fileTargetType, fileChangeType, { id, content }, event) {
diff --git a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx
index ade8dd9461..472df83534 100644
--- a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx
+++ b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx
@@ -7,11 +7,14 @@ import React, { useState, useContext } from 'react';
import formatMessage from 'format-message';
import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
+import { Label } from 'office-ui-fabric-react/lib/Label';
import { Stack } from 'office-ui-fabric-react/lib/Stack';
import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { Dropdown } from 'office-ui-fabric-react/lib/Dropdown';
+import { TextField } from 'office-ui-fabric-react/lib/TextField';
+import { DialogInfo, luIndexer, combineMessage } from '@bfc/indexers';
import get from 'lodash/get';
-import { DialogInfo } from '@bfc/indexers';
+import { LuEditor } from '@bfc/code-editor';
import {
addNewTrigger,
@@ -25,15 +28,16 @@ import {
getEventTypes,
getActivityTypes,
getMessageTypes,
- regexRecognizerKey,
} from '../../utils/dialogUtil';
+import { addIntent } from '../../utils/luUtil';
import { StoreContext } from '../../store';
-import { styles, dropdownStyles, dialogWindow } from './styles';
+import { styles, dropdownStyles, dialogWindow, intent } from './styles';
+const nameRegex = /^[a-zA-Z0-9-_.]+$/;
const validateForm = (data: TriggerFormData): TriggerFormDataErrors => {
const errors: TriggerFormDataErrors = {};
- const { $type, specifiedType } = data;
+ const { $type, specifiedType, intent, triggerPhrases } = data;
if ($type === eventTypeKey && !specifiedType) {
errors.specifiedType = formatMessage('Please select a event type');
@@ -46,21 +50,40 @@ const validateForm = (data: TriggerFormData): TriggerFormDataErrors => {
if (!$type) {
errors.$type = formatMessage('Please select a trigger type');
}
+
+ if (!intent || !nameRegex.test(intent)) {
+ errors.intent = formatMessage(
+ 'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _'
+ );
+ }
+
+ if (!triggerPhrases) {
+ errors.triggerPhrases = formatMessage('Please input trigger phrases');
+ }
+ if (data.errors.triggerPhrases) {
+ errors.triggerPhrases = data.errors.triggerPhrases;
+ }
return errors;
};
+interface LuFilePayload {
+ id: string;
+ content: string;
+}
+
interface TriggerCreationModalProps {
dialogId: string;
isOpen: boolean;
onDismiss: () => void;
- onSubmit: (dialog: DialogInfo) => void;
+ onSubmit: (dialog: DialogInfo, luFilePayload: LuFilePayload) => void;
}
const initialFormData: TriggerFormData = {
errors: {},
$type: intentTypeKey,
- intent: '',
specifiedType: '',
+ intent: '',
+ triggerPhrases: '',
};
const triggerTypeOptions: IDropdownOption[] = getTriggerTypes();
@@ -71,7 +94,7 @@ export const TriggerCreationModal: React.FC = props =
const { state } = useContext(StoreContext);
const { dialogs, luFiles } = state;
const luFile = luFiles.find(lu => lu.id === dialogId);
- const dialogFile = dialogs.find(dialog => dialog.id === dialogId);
+
const onClickSubmitButton = e => {
e.preventDefault();
const errors = validateForm(formData);
@@ -83,8 +106,15 @@ export const TriggerCreationModal: React.FC = props =
});
return;
}
+
+ const content = get(luFile, 'content', '');
+ const newContent = addIntent(content, { Name: formData.intent, Body: formData.triggerPhrases });
+ const updateLuFile = {
+ id: dialogId,
+ content: newContent,
+ };
const newDialog = addNewTrigger(dialogs, dialogId, formData);
- onSubmit(newDialog);
+ onSubmit(newDialog, updateLuFile);
onDismiss();
};
@@ -92,29 +122,27 @@ export const TriggerCreationModal: React.FC = props =
setFormData({ ...initialFormData, $type: option.key });
};
- const onSelectIntent = (e, option) => {
- setFormData({ ...formData, intent: option.key });
- };
-
const onSelectSpecifiedTypeType = (e, option) => {
setFormData({ ...formData, specifiedType: option.key });
};
+ const onNameChange = (e, name) => {
+ setFormData({ ...formData, intent: name });
+ };
+
+ const onTriggerPhrasesChange = (body: string) => {
+ const errors = formData.errors;
+ const content = '#' + formData.intent + '\n' + body;
+ const { diagnostics } = luIndexer.parse(content);
+ errors.triggerPhrases = combineMessage(diagnostics);
+ setFormData({ ...formData, triggerPhrases: body, errors });
+ };
+
const eventTypes: IDropdownOption[] = getEventTypes();
const activityTypes: IDropdownOption[] = getActivityTypes();
const messageTypes: IDropdownOption[] = getMessageTypes();
- const isRegEx = get(dialogFile, 'content.recognizer.$type', '') === regexRecognizerKey;
-
- const regexIntents = get(dialogFile, 'content.recognizer.intents', []);
- const luisIntents = get(luFile, 'intents', []);
- const intents = isRegEx ? regexIntents : luisIntents;
-
- const intentOptions = intents.map(t => {
- return { key: t.name || t.Name || t.intent, text: t.name || t.Name || t.intent };
- });
-
- const showIntentDropDown = formData.$type === intentTypeKey;
+ const showIntentFields = formData.$type === intentTypeKey;
const showEventDropDown = formData.$type === eventTypeKey;
const showActivityDropDown = formData.$type === activityTypeKey;
const showMessageDropDown = formData.$type === messageTypeKey;
@@ -178,15 +206,38 @@ export const TriggerCreationModal: React.FC = props =
data-testid={'messageTypeDropDown'}
/>
)}
- {showIntentDropDown && (
-
+ )}
+ {showIntentFields && }
+ {showIntentFields && (
+
)}
diff --git a/Composer/packages/client/src/components/ProjectTree/styles.ts b/Composer/packages/client/src/components/ProjectTree/styles.ts
index afb5647bd4..0d53763b9a 100644
--- a/Composer/packages/client/src/components/ProjectTree/styles.ts
+++ b/Composer/packages/client/src/components/ProjectTree/styles.ts
@@ -137,7 +137,7 @@ export const dropdownStyles = {
fontWeight: FontWeights.semibold,
},
dropdown: {
- width: '300px',
+ width: '400px',
},
root: {
paddingBottom: '20px',
@@ -148,7 +148,7 @@ export const dialogWindow = css`
display: flex;
flex-direction: column;
width: 400px;
- height: 250px;
+ min-height: 300px;
`;
export const textFieldlabel = {
@@ -162,11 +162,17 @@ export const textFieldlabel = {
};
export const intent = {
- fieldGroup: {
- width: 200,
+ root: {
+ width: '400px',
+ paddingBottom: '20px',
},
+};
+
+export const triggerPhrases = {
root: {
- height: '90px',
+ width: '400px',
+ },
+ fieldGroup: {
+ height: 80,
},
- subComponentStyles: textFieldlabel,
};
diff --git a/Composer/packages/client/src/extension-container/ExtensionContainer.tsx b/Composer/packages/client/src/extension-container/ExtensionContainer.tsx
index bb37bc25b1..9ad680cfd9 100644
--- a/Composer/packages/client/src/extension-container/ExtensionContainer.tsx
+++ b/Composer/packages/client/src/extension-container/ExtensionContainer.tsx
@@ -98,6 +98,18 @@ const shellApi: ShellApi = {
});
},
+ addLuIntent: (id, intent) => {
+ return apiClient.apiCall('addLuIntent', { id, intent });
+ },
+
+ updateLuIntent: (id, intentName, intent) => {
+ return apiClient.apiCall('updateLuIntent', { id, intentName, intent });
+ },
+
+ removeLuIntent: (id, intentName) => {
+ return apiClient.apiCall('removeLuIntent', { id, intentName });
+ },
+
createDialog: () => {
return apiClient.apiCall('createDialog');
},
diff --git a/Composer/packages/client/src/pages/design/index.tsx b/Composer/packages/client/src/pages/design/index.tsx
index 778be7846f..25655f874f 100644
--- a/Composer/packages/client/src/pages/design/index.tsx
+++ b/Composer/packages/client/src/pages/design/index.tsx
@@ -171,14 +171,19 @@ function DesignPage(props) {
setTriggerModalVisibility(true);
};
- const onTriggerCreationSubmit = dialog => {
- const payload = {
+ const onTriggerCreationSubmit = (dialog, luFile) => {
+ const dialogPayload = {
id: dialog.id,
content: dialog.content,
};
+ const luFilePayload = {
+ id: luFile.id,
+ content: luFile.content,
+ };
const index = get(dialog, 'content.triggers', []).length - 1;
actions.selectTo(`triggers[${index}]`);
- actions.updateDialog(payload);
+ actions.updateLuFile(luFilePayload);
+ actions.updateDialog(dialogPayload);
};
function handleSelect(id, selected = '') {
diff --git a/Composer/packages/client/src/pages/design/styles.js b/Composer/packages/client/src/pages/design/styles.js
index 5b267a00eb..a155b225fd 100644
--- a/Composer/packages/client/src/pages/design/styles.js
+++ b/Composer/packages/client/src/pages/design/styles.js
@@ -113,9 +113,9 @@ export const middleTriggerContainer = css`
display: flex;
justify-content: center;
align-items: center;
- background: #e5e5e5;
+ background: #f6f6f6;
width: 100%;
- margin-top: 48px;
+ margin-top: 55px;
height: calc(100% - 48px);
position: absolute;
`;
diff --git a/Composer/packages/client/src/pages/language-understanding/styles.js b/Composer/packages/client/src/pages/language-understanding/styles.ts
similarity index 100%
rename from Composer/packages/client/src/pages/language-understanding/styles.js
rename to Composer/packages/client/src/pages/language-understanding/styles.ts
diff --git a/Composer/packages/client/src/pages/language-understanding/table-view.tsx b/Composer/packages/client/src/pages/language-understanding/table-view.tsx
index b04afef158..2edbf09e13 100644
--- a/Composer/packages/client/src/pages/language-understanding/table-view.tsx
+++ b/Composer/packages/client/src/pages/language-understanding/table-view.tsx
@@ -79,7 +79,7 @@ const TableView: React.FC = props => {
name,
phrases,
fileId: luFile.id,
- used: luDialog ? luDialog.luIntents.includes(name) : false, // used by it's dialog or not
+ used: !!luDialog && luDialog.referredLuIntents.some(lu => lu.name === name), // used by it's dialog or not
state,
});
});
diff --git a/Composer/packages/client/src/pages/notifications/index.tsx b/Composer/packages/client/src/pages/notifications/index.tsx
index cccac44dc3..093238013a 100644
--- a/Composer/packages/client/src/pages/notifications/index.tsx
+++ b/Composer/packages/client/src/pages/notifications/index.tsx
@@ -3,49 +3,41 @@
/** @jsx jsx */
import { jsx } from '@emotion/core';
-import { useState, useContext } from 'react';
+import { useState } from 'react';
import { RouteComponentProps } from '@reach/router';
-import { StoreContext } from '../../store';
-
import { ToolBar } from './../../components/ToolBar/index';
import useNotifications from './useNotifications';
import { NotificationList } from './NotificationList';
import { NotificationHeader } from './NotificationHeader';
import { root } from './styles';
-import { INotification } from './types';
+import { INotification, NotificationType } from './types';
import { navigateTo } from './../../utils';
-import { convertDialogDiagnosticToUrl, toUrlUtil } from './../../utils/navigation';
+import { convertPathToUrl, toUrlUtil } from './../../utils/navigation';
const Notifications: React.FC = () => {
const [filter, setFilter] = useState('');
- const { state } = useContext(StoreContext);
- const { dialogs } = state;
const notifications = useNotifications(filter);
const navigations = {
- lg: (item: INotification) => {
+ [NotificationType.LG]: (item: INotification) => {
let url = `/language-generation/${item.id}/edit#L=${item.diagnostic.range?.start.line || 0}`;
- const dividerIndex = item.id.indexOf('#');
//the format of item.id is lgFile#inlineTemplateId
- if (dividerIndex > -1) {
- const templateId = item.id.substring(dividerIndex + 1);
- const lgFile = item.id.substring(0, dividerIndex);
- const dialog = dialogs.find(d => d.lgFile === lgFile);
- const lgTemplate = dialog ? dialog.lgTemplates.find(lg => lg.name === templateId) : null;
- const path = lgTemplate ? lgTemplate.path : '';
- if (path && dialog) {
- url = toUrlUtil(dialog.id, path);
- }
+ if (item.dialogPath) {
+ url = toUrlUtil(item.dialogPath);
}
navigateTo(url);
},
- lu: (item: INotification) => {
- navigateTo(`/dialogs/${item.id}`);
+ [NotificationType.LU]: (item: INotification) => {
+ let uri = `/language-understanding/${item.id}`;
+ if (item.dialogPath) {
+ uri = convertPathToUrl(item.id, item.dialogPath);
+ }
+ navigateTo(uri);
},
- dialog: (item: INotification) => {
+ [NotificationType.DIALOG]: (item: INotification) => {
//path is like main.trigers[0].actions[0]
//uri = id?selected=triggers[0]&focused=triggers[0].actions[0]
- const uri = convertDialogDiagnosticToUrl(item.diagnostic);
+ const uri = convertPathToUrl(item.id, item.dialogPath);
navigateTo(uri);
},
};
diff --git a/Composer/packages/client/src/pages/notifications/types.ts b/Composer/packages/client/src/pages/notifications/types.ts
index bce5eef0a0..707e3823da 100644
--- a/Composer/packages/client/src/pages/notifications/types.ts
+++ b/Composer/packages/client/src/pages/notifications/types.ts
@@ -1,13 +1,84 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+import { Diagnostic, createSingleMessage, DialogInfo, LuFile, isDiagnosticWithInRange } from '@bfc/indexers';
+
+import { replaceDialogDiagnosticLabel } from '../../utils';
+export const DiagnosticSeverity = ['Error', 'Warning']; //'Information', 'Hint'
+
+export enum NotificationType {
+ DIALOG,
+ LG,
+ LU,
+ GENERAL,
+}
export interface INotification {
id: string;
severity: string;
- type: string;
+ type: NotificationType;
location: string;
message: string;
diagnostic: any;
+ dialogPath?: string; //the data path in dialog
}
-export const DiagnosticSeverity = ['Error', 'Warning']; //'Information', 'Hint'
+export abstract class Notification implements INotification {
+ id: string;
+ severity: string;
+ type = NotificationType.GENERAL;
+ location: string;
+ message = '';
+ diagnostic: Diagnostic;
+ dialogPath?: string;
+ constructor(id: string, location: string, diagnostic: Diagnostic) {
+ this.id = id;
+ this.severity = DiagnosticSeverity[diagnostic.severity] || '';
+ this.diagnostic = diagnostic;
+ this.location = location;
+ }
+}
+
+export class DialogNotification extends Notification {
+ type = NotificationType.DIALOG;
+ constructor(id: string, location: string, diagnostic: Diagnostic) {
+ super(id, location, diagnostic);
+ this.message = `In ${replaceDialogDiagnosticLabel(diagnostic.path)} ${diagnostic.message}`;
+ this.dialogPath = diagnostic.path;
+ }
+}
+
+export class LgNotification extends Notification {
+ type = NotificationType.LG;
+ constructor(id: string, lgTemplateName: string, location: string, diagnostic: Diagnostic, dialogs: DialogInfo[]) {
+ super(id, location, diagnostic);
+ this.message = createSingleMessage(diagnostic);
+ this.dialogPath = this.findDialogPath(dialogs, id, lgTemplateName);
+ }
+ private findDialogPath(dialogs: DialogInfo[], id: string, lgTemplateName: string) {
+ if (lgTemplateName) {
+ const dialog = dialogs.find(d => d.lgFile === id);
+ const lgTemplate = dialog ? dialog.lgTemplates.find(lg => lg.name === lgTemplateName) : null;
+ const path = lgTemplate ? lgTemplate.path : '';
+ return path;
+ }
+ }
+}
+
+export class LuNotification extends Notification {
+ type = NotificationType.LU;
+ constructor(id: string, location: string, diagnostic: Diagnostic, luFile: LuFile, dialogs: DialogInfo[]) {
+ super(id, location, diagnostic);
+ this.dialogPath = this.findDialogPath(luFile, dialogs, diagnostic);
+ this.message = createSingleMessage(diagnostic);
+ }
+
+ private findDialogPath(luFile: LuFile, dialogs: DialogInfo[], d: Diagnostic) {
+ const intentName = luFile.intents.find(intent => {
+ const { range } = intent;
+ if (!range) return false;
+ return isDiagnosticWithInRange(d, range);
+ })?.Name;
+
+ return dialogs.find(dialog => dialog.id === luFile.id)?.referredLuIntents.find(lu => lu.name === intentName)?.path;
+ }
+}
diff --git a/Composer/packages/client/src/pages/notifications/useNotifications.tsx b/Composer/packages/client/src/pages/notifications/useNotifications.tsx
index 8865060dc7..91441951de 100644
--- a/Composer/packages/client/src/pages/notifications/useNotifications.tsx
+++ b/Composer/packages/client/src/pages/notifications/useNotifications.tsx
@@ -2,44 +2,28 @@
// Licensed under the MIT License.
import { useContext, useMemo } from 'react';
-import { createSingleMessage } from '@bfc/indexers';
import get from 'lodash/get';
import { LgNamePattern } from '@bfc/shared';
import { StoreContext } from '../../store';
-import { replaceDialogDiagnosticLabel } from '../../utils';
-import { INotification, DiagnosticSeverity } from './types';
+import { Notification, DialogNotification, LuNotification, LgNotification } from './types';
import { getReferredFiles } from './../../utils/luUtil';
export default function useNotifications(filter?: string) {
const { state } = useContext(StoreContext);
const { dialogs, luFiles, lgFiles } = state;
const memoized = useMemo(() => {
- const notifactions: INotification[] = [];
+ const notifactions: Notification[] = [];
dialogs.forEach(dialog => {
dialog.diagnostics.map(diagnostic => {
const location = `${dialog.id}.dialog`;
- notifactions.push({
- type: 'dialog',
- location,
- message: `In ${replaceDialogDiagnosticLabel(diagnostic.path)} ${diagnostic.message}`,
- severity: DiagnosticSeverity[diagnostic.severity] || '',
- diagnostic,
- id: dialog.id,
- });
+ notifactions.push(new DialogNotification(dialog.id, location, diagnostic));
});
});
getReferredFiles(luFiles, dialogs).forEach(lufile => {
lufile.diagnostics.map(diagnostic => {
const location = `${lufile.id}.lu`;
- notifactions.push({
- type: 'lu',
- location,
- message: createSingleMessage(diagnostic),
- severity: 'Error',
- diagnostic,
- id: lufile.id,
- });
+ notifactions.push(new LuNotification(lufile.id, location, diagnostic, lufile, dialogs));
});
});
lgFiles.forEach(lgFile => {
@@ -53,26 +37,20 @@ export default function useNotifications(filter?: string) {
get(diagnostic, 'range.start.line') >= get(t, 'range.startLineNumber') &&
get(diagnostic, 'range.end.line') <= get(t, 'range.endLineNumber')
);
- let id = lgFile.id;
+ const id = lgFile.id;
const location = `${lgFile.id}.lg`;
+ let lgTemplateName = '';
if (mappedTemplate && mappedTemplate.name.match(LgNamePattern)) {
//should navigate to design page
- id = `${lgFile.id}#${mappedTemplate.name}`;
+ lgTemplateName = mappedTemplate.name;
}
- notifactions.push({
- type: 'lg',
- severity: DiagnosticSeverity[diagnostic.severity] || '',
- location,
- message: createSingleMessage(diagnostic),
- diagnostic,
- id,
- });
+ notifactions.push(new LgNotification(id, lgTemplateName, location, diagnostic, dialogs));
});
});
return notifactions;
}, [dialogs, luFiles, lgFiles]);
- const notifications: INotification[] = !filter ? memoized : memoized.filter(x => x.severity === filter);
+ const notifications: Notification[] = filter ? memoized.filter(x => x.severity === filter) : memoized;
return notifications;
}
diff --git a/Composer/packages/client/src/utils/dialogUtil.ts b/Composer/packages/client/src/utils/dialogUtil.ts
index e388decfb0..e54934034c 100644
--- a/Composer/packages/client/src/utils/dialogUtil.ts
+++ b/Composer/packages/client/src/utils/dialogUtil.ts
@@ -21,14 +21,16 @@ interface DialogsMap {
export interface TriggerFormData {
errors: TriggerFormDataErrors;
$type: string;
- intent: string;
specifiedType: string;
+ intent: string;
+ triggerPhrases: string;
}
export interface TriggerFormDataErrors {
$type?: string;
intent?: string;
specifiedType?: string;
+ triggerPhrases?: string;
}
export function getDialog(dialogs: DialogInfo[], dialogId: string) {
@@ -67,7 +69,6 @@ export function insert(content, path: string, position: number | undefined, data
if (data.intent) {
optionalAttributes.intent = data.intent;
}
-
const newStep = {
$type: data.$type,
...seedNewDialog(data.$type, {}, optionalAttributes),
diff --git a/Composer/packages/client/src/utils/navigation.ts b/Composer/packages/client/src/utils/navigation.ts
index 415f6285a1..117734326b 100644
--- a/Composer/packages/client/src/utils/navigation.ts
+++ b/Composer/packages/client/src/utils/navigation.ts
@@ -3,7 +3,6 @@
import cloneDeep from 'lodash/cloneDeep';
import { navigate, NavigateOptions } from '@reach/router';
-import { Diagnostic } from '@bfc/indexers';
import { BreadcrumbItem, DesignPageLocation } from '../store/types';
@@ -77,13 +76,11 @@ interface NavigationState {
breadcrumb: BreadcrumbItem[];
}
-export function convertDialogDiagnosticToUrl(diagnostic: Diagnostic): string {
+export function convertPathToUrl(id: string, path?: string): string {
//path is like main.trigers[0].actions[0]
//uri = id?selected=triggers[0]&focused=triggers[0].actions[0]
- const { path, source } = diagnostic;
- if (!source) return '';
- let uri = `/dialogs/${source}`;
+ let uri = `/dialogs/${id}`;
if (!path) return uri;
const items = path.split('#');
@@ -107,8 +104,10 @@ export function convertDialogDiagnosticToUrl(diagnostic: Diagnostic): string {
return uri;
}
-export function toUrlUtil(dialogId: string, path: string): string {
+export function toUrlUtil(path: string): string {
const tokens = path.split('#');
+ const firstDotIndex = tokens[0].indexOf('.');
+ const dialogId = tokens[0].substring(0, firstDotIndex);
const focusedPath = parsePathToFocused(tokens[0]);
const selectedPath = parsePathToSelected(tokens[0]);
const type = tokens[1];
diff --git a/Composer/packages/extensions/obiformeditor/demo/src/index.tsx b/Composer/packages/extensions/obiformeditor/demo/src/index.tsx
index 8897c9d207..a399e293d7 100644
--- a/Composer/packages/extensions/obiformeditor/demo/src/index.tsx
+++ b/Composer/packages/extensions/obiformeditor/demo/src/index.tsx
@@ -173,6 +173,9 @@ const mockShellApi = [
'getLgTemplates',
'createLgTemplate',
'updateLgTemplate',
+ 'addLuIntent',
+ 'updateLuIntent',
+ 'removeLuIntent',
'validateExpression',
'onFocusSteps',
'onFocusEvent',
diff --git a/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/InlineLuEditor.tsx b/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/InlineLuEditor.tsx
index 65f989aabf..2652028a8e 100644
--- a/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/InlineLuEditor.tsx
+++ b/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/InlineLuEditor.tsx
@@ -20,8 +20,12 @@ const InlineLuEditor: React.FC = props => {
setLocalContent(value);
onSave(value);
};
-
- return ;
+ const luOption = {
+ fileId: file.id,
+ };
+ return (
+
+ );
};
export default InlineLuEditor;
diff --git a/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/index.tsx b/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/index.tsx
index a4ba6f1048..5a490cfda5 100644
--- a/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/index.tsx
+++ b/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/index.tsx
@@ -1,28 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-import React, { useState, ReactElement, Suspense, useEffect } from 'react';
+import React, { useState, ReactElement } from 'react';
import formatMessage from 'format-message';
import { FieldProps } from '@bfcomposer/react-jsonschema-form';
import { Dropdown, ResponsiveMode, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner';
import { MicrosoftIRecognizer } from '@bfc/shared';
-import { LuFile, combineMessage } from '@bfc/indexers';
+import { LuFile } from '@bfc/indexers';
import { BaseField } from '../BaseField';
-import { LoadingSpinner } from '../../../LoadingSpinner';
import ToggleEditor from './ToggleEditor';
import RegexEditor from './RegexEditor';
import './styles.css';
-const InlineLuEditor = React.lazy(() => import('./InlineLuEditor'));
-
export const RecognizerField: React.FC> = props => {
const { formData } = props;
const [loading, setLoading] = useState(false);
- const [errorMsg, setErrorMsg] = useState('');
const {
formContext: { luFiles, shellApi, currentDialog },
@@ -32,19 +28,6 @@ export const RecognizerField: React.FC> = props
const isRegex = typeof formData === 'object' && formData.$type === 'Microsoft.RegexRecognizer';
const currentDialogId = currentDialog.id;
const selectedFile: LuFile | void = luFiles.find(f => f.id === currentDialogId);
- const isLuFileSelected = Boolean(
- selectedFile && typeof props.formData === 'string' && props.formData.startsWith(selectedFile.id)
- );
-
- //make the inline editor show error message
- useEffect(() => {
- if (selectedFile && selectedFile.diagnostics.length > 0) {
- const msg = combineMessage(selectedFile.diagnostics);
- setErrorMsg(msg);
- } else {
- setErrorMsg('');
- }
- }, [selectedFile]);
const handleChange = (_, option?: IDropdownOption): void => {
if (option) {
@@ -144,23 +127,8 @@ export const RecognizerField: React.FC> = props
responsiveMode={ResponsiveMode.large}
onRenderTitle={onRenderTitle}
/>
-
+
{() => {
- if (selectedFile && isLuFileSelected) {
- const updateLuFile = (newValue?: string): void => {
- shellApi.updateLuFile({ id: selectedFile.id, content: newValue }).catch(setErrorMsg);
- };
-
- return (
- }>
-
-
- );
- }
if (isRegex) {
return ;
}
diff --git a/Composer/packages/extensions/obiformeditor/src/Form/widgets/IntentWidget.tsx b/Composer/packages/extensions/obiformeditor/src/Form/widgets/IntentWidget.tsx
index dbe9bef687..bc3579bb6d 100644
--- a/Composer/packages/extensions/obiformeditor/src/Form/widgets/IntentWidget.tsx
+++ b/Composer/packages/extensions/obiformeditor/src/Form/widgets/IntentWidget.tsx
@@ -5,11 +5,12 @@ import React from 'react';
import { Dropdown, ResponsiveMode, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import formatMessage from 'format-message';
import { RegexRecognizer } from '@bfc/shared';
-import { LuFile, DialogInfo } from '@bfc/indexers';
+import { DialogInfo } from '@bfc/indexers';
-import { BFDWidgetProps, FormContext } from '../types';
+import { BFDWidgetProps } from '../types';
import { WidgetLabel } from './WidgetLabel';
+import { LuEditorWidget } from './LuEditorWidget';
const EMPTY_OPTION = { key: '', text: '' };
@@ -48,42 +49,26 @@ function regexIntentOptions(currentDialog: DialogInfo): IDropdownOption[] {
return options;
}
-function luIntentOptions(formContext: FormContext): IDropdownOption[] {
- const luFile: LuFile | void = formContext.luFiles.find(f => f.id === formContext.currentDialog.id);
- let options: IDropdownOption[] = [EMPTY_OPTION];
-
- if (luFile) {
- const intents: { name: string }[] = luFile.intents.map(({ Name: name }) => {
- return {
- name,
- };
- });
-
- options = options.concat(
- intents.map(i => ({
- key: i.name,
- text: i.name,
- }))
- );
- }
-
- return options;
-}
-
export const IntentWidget: React.FC = props => {
const { disabled, onChange, id, onFocus, onBlur, value, formContext, placeholder, label, schema } = props;
const { description } = schema;
+ const { currentDialog } = formContext;
let options: IDropdownOption[] = [];
+ let widgetLabel = label;
+ let isLuisSelected = false;
- switch (recognizerType(formContext.currentDialog)) {
+ switch (recognizerType(currentDialog)) {
case RecognizerType.regex:
- options = regexIntentOptions(formContext.currentDialog);
+ options = regexIntentOptions(currentDialog);
+ isLuisSelected = false;
break;
case RecognizerType.luis:
- options = luIntentOptions(formContext);
+ widgetLabel = `Trigger phrases(intent name: #${value || ''})`;
+ isLuisSelected = true;
break;
default:
options = [EMPTY_OPTION];
+ isLuisSelected = false;
break;
}
@@ -95,18 +80,21 @@ export const IntentWidget: React.FC = props => {
return (
<>
-
- onBlur && onBlur(id, value)}
- onChange={handleChange}
- onFocus={() => onFocus && onFocus(id, value)}
- options={options}
- selectedKey={value || null}
- responsiveMode={ResponsiveMode.large}
- disabled={disabled || options.length === 1}
- placeholder={options.length > 1 ? placeholder : formatMessage('No intents configured for this dialog')}
- />
+
+ {!isLuisSelected && (
+ onBlur && onBlur(id, value)}
+ onChange={handleChange}
+ onFocus={() => onFocus && onFocus(id, value)}
+ options={options}
+ selectedKey={value || null}
+ responsiveMode={ResponsiveMode.large}
+ disabled={disabled || options.length === 1}
+ placeholder={options.length > 1 ? placeholder : formatMessage('No intents configured for this dialog')}
+ />
+ )}
+ {isLuisSelected && }
>
);
};
diff --git a/Composer/packages/extensions/obiformeditor/src/Form/widgets/LuEditorWidget.tsx b/Composer/packages/extensions/obiformeditor/src/Form/widgets/LuEditorWidget.tsx
new file mode 100644
index 0000000000..0d05aac956
--- /dev/null
+++ b/Composer/packages/extensions/obiformeditor/src/Form/widgets/LuEditorWidget.tsx
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+import React from 'react';
+import { LuEditor } from '@bfc/code-editor';
+import debounce from 'lodash/debounce';
+import { LuIntentSection } from '@bfc/shared';
+import { LuFile, filterSectionDiagnostics } from '@bfc/indexers';
+
+import { FormContext } from '../types';
+
+interface LuEditorWidgetProps {
+ formContext: FormContext;
+ name: string;
+ height?: number | string;
+ onChange: (template?: string) => void;
+}
+
+export class LuEditorWidget extends React.Component {
+ constructor(props) {
+ super(props);
+ this.debounceUpdate = debounce(this.updateLuIntent, 500);
+ this.name = this.props.name;
+ this.formContext = this.props.formContext;
+ this.luFileId = this.formContext.currentDialog.id;
+ this.luFile = this.formContext.luFiles.find(f => f.id === this.luFileId);
+ this.luIntent = (this.luFile && this.luFile.intents.find(intent => intent.Name === this.name)) || {
+ Name: this.name,
+ Body: '',
+ };
+ }
+
+ formContext: FormContext;
+ name: string;
+ luFileId: string;
+ luFile: LuFile | null;
+ luIntent: LuIntentSection;
+ state = { localValue: '' };
+ debounceUpdate;
+ updateLuIntent = (body: string) => {
+ this.formContext.shellApi.updateLuIntent(this.luFileId, this.name, { Name: this.name, Body: body }).catch(() => {});
+ };
+
+ static getDerivedStateFromProps(nextProps, prevState) {
+ const name = nextProps.name;
+ const formContext = nextProps.formContext;
+ const luFileId = formContext.currentDialog.id;
+ const luFile = formContext.luFiles.find(f => f.id === luFileId);
+ const luIntent = (luFile && luFile.intents.find(intent => intent.Name === name)) || {
+ Name: name,
+ Body: '',
+ };
+ if (!prevState.localValue) {
+ return {
+ localValue: luIntent.Body,
+ };
+ }
+ return null;
+ }
+
+ onChange = (body: string) => {
+ this.setState({
+ localValue: body,
+ });
+ if (this.luFileId) {
+ if (body) {
+ this.updateLuIntent(body);
+ } else {
+ this.formContext.shellApi.removeLuIntent(this.luFileId, this.name);
+ }
+ }
+ };
+ render() {
+ const diagnostic = this.luFile && filterSectionDiagnostics(this.luFile.diagnostics, this.luIntent)[0];
+
+ const errorMsg = diagnostic
+ ? diagnostic.message.split('error message: ')[diagnostic.message.split('error message: ').length - 1]
+ : '';
+ const luOption = {
+ fileId: this.luFileId,
+ sectionId: this.luIntent?.Name,
+ };
+ const height = this.props.height || 250;
+
+ return (
+
+ );
+ }
+}
diff --git a/Composer/packages/lib/indexers/src/dialogIndexer.ts b/Composer/packages/lib/indexers/src/dialogIndexer.ts
index 0940379b96..eb36ee6ae6 100644
--- a/Composer/packages/lib/indexers/src/dialogIndexer.ts
+++ b/Composer/packages/lib/indexers/src/dialogIndexer.ts
@@ -5,8 +5,9 @@ import has from 'lodash/has';
import uniq from 'lodash/uniq';
import { extractLgTemplateRefs } from '@bfc/shared';
+import { createPath } from './dialogUtils/dialogChecker';
import { checkerFuncs } from './dialogUtils/dialogChecker';
-import { ITrigger, DialogInfo, FileInfo, LgTemplateJsonPath } from './type';
+import { ITrigger, DialogInfo, FileInfo, LgTemplateJsonPath, ReferredLuIntents } from './type';
import { JsonWalk, VisitorFunc } from './utils/jsonWalk';
import { getBaseName } from './utils/help';
import { Diagnostic } from './diagnostic';
@@ -70,8 +71,8 @@ function ExtractLgTemplates(id, dialog): LgTemplateJsonPath[] {
}
// find out all lu intents given dialog
-function ExtractLuIntents(dialog): string[] {
- const intents: string[] = [];
+function ExtractLuIntents(dialog, id: string): ReferredLuIntents[] {
+ const intents: ReferredLuIntents[] = [];
/** *
* @param path , jsonPath string
* @param value , current node value *
@@ -81,11 +82,14 @@ function ExtractLuIntents(dialog): string[] {
// it's a valid schema dialog node.
if (has(value, '$type') && value.$type === 'Microsoft.OnIntent') {
const intentName = value.intent;
- intents.push(intentName);
+ intents.push({
+ name: intentName,
+ path: createPath(path, value.$type),
+ });
}
return false;
};
- JsonWalk('$', dialog, visitor);
+ JsonWalk(id, dialog, visitor);
return uniq(intents);
}
@@ -195,8 +199,8 @@ function parse(id: string, content: any, schema: any) {
diagnostics: validate(id, content, schema),
referredDialogs: ExtractReferredDialogs(content),
lgTemplates: ExtractLgTemplates(id, content),
- luIntents: ExtractLuIntents(content),
userDefinedVariables: ExtractMemoryPaths(content),
+ referredLuIntents: ExtractLuIntents(content, id),
luFile: getBaseName(luFile, '.lu'),
lgFile: getBaseName(lgFile, '.lg'),
triggers: ExtractTriggers(content),
diff --git a/Composer/packages/lib/indexers/src/luIndexer.ts b/Composer/packages/lib/indexers/src/luIndexer.ts
index 49a579bcc0..164ea2d89c 100644
--- a/Composer/packages/lib/indexers/src/luIndexer.ts
+++ b/Composer/packages/lib/indexers/src/luIndexer.ts
@@ -3,8 +3,9 @@
import { sectionHandler } from '@bfcomposer/bf-lu/lib/parser';
import get from 'lodash/get';
+import { LuIntentSection } from '@bfc/shared';
-import { FileInfo, LuFile, LuParsed, LuSectionTypes, LuIntentSection } from './type';
+import { FileInfo, LuFile, LuParsed, LuSectionTypes } from './type';
import { getBaseName } from './utils/help';
import { Diagnostic, Position, Range, DiagnosticSeverity } from './diagnostic';
import { FileExtensions } from './utils/fileExtensions';
diff --git a/Composer/packages/lib/indexers/src/type.ts b/Composer/packages/lib/indexers/src/type.ts
index 5e9f3d62ce..5b47641a57 100644
--- a/Composer/packages/lib/indexers/src/type.ts
+++ b/Composer/packages/lib/indexers/src/type.ts
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+import { LuIntentSection } from '@bfc/shared';
+
import { Diagnostic } from './diagnostic';
export interface FileInfo {
@@ -17,6 +19,11 @@ export interface ITrigger {
isIntent: boolean;
}
+export interface ReferredLuIntents {
+ name: string;
+ path: string;
+}
+
export interface DialogInfo {
content: any;
diagnostics: Diagnostic[];
@@ -26,7 +33,7 @@ export interface DialogInfo {
lgFile: string;
lgTemplates: LgTemplateJsonPath[];
luFile: string;
- luIntents: string[];
+ referredLuIntents: ReferredLuIntents[];
referredDialogs: string[];
relativePath: string;
userDefinedVariables: string[];
@@ -62,14 +69,6 @@ export interface LuEntity {
Name: string;
}
-export interface LuIntentSection {
- Name: string;
- Body: string;
- Entities?: LuEntity[];
- Children?: LuIntentSection[];
- range?: CodeRange;
-}
-
export interface LuFile {
id: string;
relativePath: string;
diff --git a/Composer/packages/lib/indexers/src/utils/diagnosticUtil.ts b/Composer/packages/lib/indexers/src/utils/diagnosticUtil.ts
index 41695327c2..92c16061f3 100644
--- a/Composer/packages/lib/indexers/src/utils/diagnosticUtil.ts
+++ b/Composer/packages/lib/indexers/src/utils/diagnosticUtil.ts
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+import { LuIntentSection } from '@bfc/shared';
+
import { Diagnostic, DiagnosticSeverity, Range, Position } from '../diagnostic';
import { CodeRange } from '../type';
@@ -28,10 +30,15 @@ export function offsetRange(range: Range, offset: number): Range {
);
}
+export function isDiagnosticWithInRange(diagnostic: Diagnostic, range: CodeRange): boolean {
+ if (!diagnostic.range) return false;
+ return diagnostic.range.start.line >= range.startLineNumber && diagnostic.range.end.line <= range.endLineNumber;
+}
+
export function filterTemplateDiagnostics(diagnostics: Diagnostic[], { range }: { range?: CodeRange }): Diagnostic[] {
if (!range) return diagnostics;
const filteredDiags = diagnostics.filter(d => {
- return d.range && d.range.start.line >= range.startLineNumber && d.range.end.line <= range.endLineNumber;
+ return d.range && isDiagnosticWithInRange(d, range);
});
const offset = range.startLineNumber;
return filteredDiags.map(d => {
@@ -46,6 +53,22 @@ export function filterTemplateDiagnostics(diagnostics: Diagnostic[], { range }:
});
}
+export function filterSectionDiagnostics(diagnostics: Diagnostic[], section: LuIntentSection): Diagnostic[] {
+ const { range } = section;
+ if (!range) return diagnostics;
+ const filteredDiags = diagnostics.filter(d => {
+ return isDiagnosticWithInRange(d, range);
+ });
+ const offset = range.startLineNumber;
+ return filteredDiags.map(d => {
+ const { range } = d;
+ if (range) {
+ d.range = offsetRange(range, offset);
+ }
+ return d;
+ });
+}
+
export function findErrors(diagnostics: Diagnostic[]): Diagnostic[] {
return diagnostics.filter(d => d.severity === DiagnosticSeverity.Error);
}
diff --git a/Composer/packages/lib/indexers/src/utils/luUtil.ts b/Composer/packages/lib/indexers/src/utils/luUtil.ts
index ef0867686d..0baac955ff 100644
--- a/Composer/packages/lib/indexers/src/utils/luUtil.ts
+++ b/Composer/packages/lib/indexers/src/utils/luUtil.ts
@@ -9,8 +9,9 @@
import { sectionHandler } from '@bfcomposer/bf-lu/lib/parser';
import isEmpty from 'lodash/isEmpty';
+import { LuIntentSection } from '@bfc/shared';
-import { LuIntentSection, LuSectionTypes } from '../type';
+import { LuSectionTypes } from '../type';
import { luIndexer } from '../luIndexer';
import { Diagnostic } from '../diagnostic';
const { parse } = luIndexer;
diff --git a/Composer/packages/lib/shared/src/types/shell.ts b/Composer/packages/lib/shared/src/types/shell.ts
index 0077441749..521d3600a1 100644
--- a/Composer/packages/lib/shared/src/types/shell.ts
+++ b/Composer/packages/lib/shared/src/types/shell.ts
@@ -3,6 +3,23 @@
import { LGTemplate as LgTemplate } from 'botbuilder-lg';
+export interface LuIntentSection {
+ Name: string;
+ Body: string;
+ Entities?: LuEntity[];
+ Children?: LuIntentSection[];
+ range?: CodeRange;
+}
+
+export interface CodeRange {
+ startLineNumber: number;
+ endLineNumber: number;
+}
+
+export interface LuEntity {
+ Name: string;
+}
+
export interface EditorSchema {
content?: {
fieldTemplateOverrides?: any;
@@ -53,6 +70,9 @@ export interface ShellApi {
updateLgTemplate: (id: string, templateName: string, templateStr: string) => Promise;
removeLgTemplate: (id: string, templateName: string) => Promise;
removeLgTemplates: (id: string, templateNames: string[]) => Promise;
+ addLuIntent: (id: string, intent: LuIntentSection | null) => Promise;
+ updateLuIntent: (id: string, intentName: string, intent: LuIntentSection | null) => Promise;
+ removeLuIntent: (id: string, intentName: string) => Promise;
createDialog: () => Promise;
validateExpression: (expression?: string) => Promise;
// TODO: fix these types
diff --git a/Composer/yarn.lock b/Composer/yarn.lock
index 160659b32a..be76d64dd2 100644
--- a/Composer/yarn.lock
+++ b/Composer/yarn.lock
@@ -1972,6 +1972,13 @@
dependencies:
regenerator-runtime "^0.13.2"
+"@babel/runtime@^7.2.0", "@babel/runtime@^7.8.4":
+ version "7.8.4"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
+ integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
+ dependencies:
+ regenerator-runtime "^0.13.2"
+
"@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.2.tgz#f5ab6897320f16decd855eed70b705908a313fe8"
@@ -2014,13 +2021,6 @@
dependencies:
regenerator-runtime "^0.13.2"
-"@babel/runtime@^7.8.4":
- version "7.8.4"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
- integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
- dependencies:
- regenerator-runtime "^0.13.2"
-
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.2.2", "@babel/template@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.0.tgz#12474e9c077bae585c5d835a95c0b0b790c25c8b"
@@ -8554,7 +8554,7 @@ findup-sync@3.0.0:
findup-sync@^2.0.0:
version "2.0.0"
- resolved "https://registry.npm.taobao.org/findup-sync/download/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc"
+ resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc"
integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=
dependencies:
detect-file "^1.0.0"
@@ -8869,6 +8869,11 @@ get-caller-file@^2.0.1:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+get-node-dimensions@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz#fb7b4bb57060fb4247dd51c9d690dfbec56b0823"
+ integrity sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ==
+
get-own-enumerable-property-symbols@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203"
@@ -9561,7 +9566,7 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
-https-proxy-agent@^2.2.1:
+https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.3:
version "2.2.4"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha1-TuenN6vZJniik9mzShr00NCMeHs=
@@ -14474,6 +14479,16 @@ react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+react-measure@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/react-measure/-/react-measure-2.3.0.tgz#75835d39abec9ae13517f35a819c160997a7a44e"
+ integrity sha512-dwAvmiOeblj5Dvpnk8Jm7Q8B4THF/f1l1HtKVi0XDecsG6LXwGvzV5R1H32kq3TW6RW64OAf5aoQxpIgLa4z8A==
+ dependencies:
+ "@babel/runtime" "^7.2.0"
+ get-node-dimensions "^1.2.1"
+ prop-types "^15.6.2"
+ resize-observer-polyfill "^1.5.0"
+
react-testing-library@^6.0.1:
version "6.0.2"
resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-6.0.2.tgz#afd7ddaa174e21cf672605e4e4f6f8156c4c9ef9"
@@ -14622,8 +14637,8 @@ realpath-native@^1.1.0:
reconnecting-websocket@^3.2.2:
version "3.2.2"
- resolved "https://registry.npm.taobao.org/reconnecting-websocket/download/reconnecting-websocket-3.2.2.tgz#8097514e926e9855e03c39e76efa2e3d1f371bee"
- integrity sha1-gJdRTpJumFXgPDnnbvouPR83G+4=
+ resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-3.2.2.tgz#8097514e926e9855e03c39e76efa2e3d1f371bee"
+ integrity sha512-SWSfoXiaHVOqXuPWFgGWeUxKnb5HIY7I/Fh5C/hy4wUOgeOh7YIMXEiv5/eHBlNs4tNzCrO5YDR9AH62NWle0Q==
recursive-readdir@2.2.2:
version "2.2.2"
@@ -14834,13 +14849,13 @@ replace-ext@1.0.0:
integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=
request-light@^0.2.2:
- version "0.2.4"
- resolved "https://registry.npm.taobao.org/request-light/download/request-light-0.2.4.tgz#3cea29c126682e6bcadf7915353322eeba01a755"
- integrity sha1-POopwSZoLmvK33kVNTMi7roBp1U=
+ version "0.2.5"
+ resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.5.tgz#38a3da7b2e56f7af8cbba57e8a94930ee2380746"
+ integrity sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw==
dependencies:
http-proxy-agent "^2.1.0"
- https-proxy-agent "^2.2.1"
- vscode-nls "^4.0.0"
+ https-proxy-agent "^2.2.3"
+ vscode-nls "^4.1.1"
request-progress@3.0.0:
version "3.0.0"
@@ -14926,6 +14941,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
+resize-observer-polyfill@^1.5.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
+ integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
+
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
@@ -17102,7 +17122,7 @@ vscode-languageserver@^5.3.0-next:
vscode-languageserver-protocol "^3.15.0-next.8"
vscode-textbuffer "^1.0.0"
-vscode-nls@^4.0.0, vscode-nls@^4.1.1:
+vscode-nls@^4.1.1:
version "4.1.1"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
integrity sha1-+ZFrZOSUeyAyLe+x5naklYYfEzw=