From 7adc20d9ae0715b4f85b95fab45172dd1d9734cd Mon Sep 17 00:00:00 2001 From: TJ Durnford Date: Wed, 24 Mar 2021 15:52:59 -0600 Subject: [PATCH 1/3] feat: Add begin dialog options form (#6508) * feat: Add begin dialog options form * small change * add tests Co-authored-by: Chris Whitten --- .../packages/server/src/locales/en-US.json | 21 +++ .../select-dialog/src/DialogOptionsField.tsx | 156 ++++++++++++++++++ .../src/__tests__/DialogOptions.test.tsx | 111 +++++++++++++ .../ui-plugins/select-dialog/src/index.ts | 16 ++ 4 files changed, 304 insertions(+) create mode 100644 Composer/packages/ui-plugins/select-dialog/src/DialogOptionsField.tsx create mode 100644 Composer/packages/ui-plugins/select-dialog/src/__tests__/DialogOptions.test.tsx diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 37b101e51f..bd250e2ee8 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -593,6 +593,9 @@ "close_d634289d": { "message": "Close" }, + "code_editor_1438efc": { + "message": "code editor" + }, "cognitive_service_region_87c668be": { "message": "Cognitive Service Region" }, @@ -698,6 +701,9 @@ "connect_to_a_skill_53c9dff0": { "message": "Connect to a skill" }, + "connect_to_an_existing_bot_7f42990b": { + "message": "Connect to an existing bot" + }, "connect_to_qna_knowledgebase_4b324132": { "message": "Connect to QnA Knowledgebase" }, @@ -776,6 +782,9 @@ "create_a_name_for_the_project_which_will_be_used_t_57e9b690": { "message": "Create a name for the project which will be used to name the application: (projectname-environment-LUfilename)" }, + "create_a_new_bot_51ce70d3": { + "message": "Create a new bot" + }, "create_a_new_dialog_21d84b82": { "message": "Create a new dialog" }, @@ -1079,6 +1088,9 @@ "display_text_used_by_the_channel_to_render_visuall_4e4ab704": { "message": "Display text used by the channel to render visually." }, + "do_you_want_to_create_a_new_bot_or_connect_your_az_f5c9dee": { + "message": "Do you want to create a new bot, or connect your Azure Bot resource to an existing bot?" + }, "do_you_want_to_proceed_cd35aa38": { "message": "Do you want to proceed?" }, @@ -1484,6 +1496,9 @@ "for_properties_of_type_list_or_enum_your_bot_accep_9e7649c6": { "message": "For properties of type list (or enum), your bot accepts only the values you define. After your dialog is generated, you can provide synonyms for each value." }, + "form_b674666c": { + "message": "form" + }, "form_dialog_error_ba7c37fe": { "message": "Form dialog error" }, @@ -2327,6 +2342,9 @@ "one_of_the_variations_added_below_will_be_selected_bee3c3f1": { "message": "One of the variations added below will be selected at random by the LG library." }, + "one_or_more_options_that_are_passed_to_the_dialog__cbcf5d72": { + "message": "One or more options that are passed to the dialog that is called." + }, "open_an_existing_skill_fbd87273": { "message": "Open an existing skill" }, @@ -2348,6 +2366,9 @@ "open_web_chat_7a24d4f8": { "message": "Open web chat" }, + "open_your_azure_bot_resource_9165e3d1": { + "message": "Open your Azure Bot resource" + }, "optional_221bcc9d": { "message": "Optional" }, diff --git a/Composer/packages/ui-plugins/select-dialog/src/DialogOptionsField.tsx b/Composer/packages/ui-plugins/select-dialog/src/DialogOptionsField.tsx new file mode 100644 index 0000000000..e5fd0575c9 --- /dev/null +++ b/Composer/packages/ui-plugins/select-dialog/src/DialogOptionsField.tsx @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import styled from '@emotion/styled'; +import { FieldProps, JSONSchema7, useShellApi } from '@bfc/extension-client'; +import { FieldLabel, JsonField, SchemaField, IntellisenseTextField, WithTypeIcons } from '@bfc/adaptive-form'; +import Stack from 'office-ui-fabric-react/lib/components/Stack/Stack'; +import { FluentTheme, NeutralColors } from '@uifabric/fluent-theme'; +import formatMessage from 'format-message'; +import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; + +const IntellisenseTextFieldWithIcon = WithTypeIcons(IntellisenseTextField); + +const BorderedStack = styled(Stack)(({ border }: { border: boolean }) => + border + ? { + borderBottom: `1px solid ${NeutralColors.gray30}`, + } + : {} +); + +const styles = { + dropdown: { + root: { + ':hover .ms-Dropdown-title, :active .ms-Dropdown-title, :hover .ms-Dropdown-caretDown, :active .ms-Dropdown-caretDown': { + color: FluentTheme.palette.themeDarker, + }, + ':focus-within .ms-Dropdown-title, :focus-within .ms-Dropdown-caretDown': { + color: FluentTheme.palette.accent, + }, + }, + caretDown: { fontSize: FluentTheme.fonts.xSmall.fontSize, color: FluentTheme.palette.accent }, + dropdownOptionText: { fontSize: FluentTheme.fonts.small.fontSize }, + title: { + border: 'none', + fontSize: FluentTheme.fonts.small.fontSize, + color: FluentTheme.palette.accent, + }, + }, +}; + +const dropdownCalloutProps = { styles: { root: { minWidth: 140 } } }; + +const getInitialSelectedKey = (value?: string | Record, schema?: JSONSchema7): string => { + if (typeof value !== 'string' && schema) { + return 'form'; + } else if (typeof value !== 'string' && !schema) { + return 'code'; + } else { + return 'expression'; + } +}; + +const DialogOptionsField: React.FC = ({ + description, + uiOptions, + label, + required, + id, + value = {}, + onChange, +}) => { + const { dialog, options } = value; + const { dialogSchemas } = useShellApi(); + const { content: schema }: { content?: JSONSchema7 } = React.useMemo( + () => dialogSchemas.find(({ id }) => id === dialog) || {}, + [dialog, dialogSchemas] + ); + + const [selectedKey, setSelectedKey] = React.useState(getInitialSelectedKey(options, schema)); + + React.useLayoutEffect(() => { + setSelectedKey(getInitialSelectedKey(options, schema)); + }, [dialog]); + + const change = React.useCallback( + (newOptions?: string | Record) => { + onChange({ ...value, options: newOptions }); + }, + [value, onChange] + ); + + const onDropdownChange = React.useCallback( + (_: React.FormEvent, option?: IDropdownOption) => { + if (option) { + setSelectedKey(option.key as string); + if (option.key === 'expression') { + change(); + } + } + }, + [change] + ); + + const typeOptions = React.useMemo(() => { + return [ + { + key: 'form', + text: formatMessage('form'), + disabled: !schema || !Object.keys(schema).length, + }, + { + key: 'code', + text: formatMessage('code editor'), + }, + { + key: 'expression', + text: 'expression', + }, + ]; + }, [schema]); + + let Field = IntellisenseTextFieldWithIcon; + if (selectedKey === 'form') { + Field = SchemaField; + } else if (selectedKey === 'code') { + Field = JsonField; + } + + return ( + + + + + + + + ); +}; + +export { DialogOptionsField }; diff --git a/Composer/packages/ui-plugins/select-dialog/src/__tests__/DialogOptions.test.tsx b/Composer/packages/ui-plugins/select-dialog/src/__tests__/DialogOptions.test.tsx new file mode 100644 index 0000000000..9c213347d4 --- /dev/null +++ b/Composer/packages/ui-plugins/select-dialog/src/__tests__/DialogOptions.test.tsx @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// @ts-nocheck + +import React from 'react'; +import { fireEvent, render } from '@botframework-composer/test-utils'; +import { EditorExtension } from '@bfc/extension-client'; + +import { DialogOptionsField } from '../DialogOptionsField'; + +jest.mock('@bfc/adaptive-form', () => { + const AdaptiveForm = jest.requireActual('@bfc/adaptive-form'); + + return { + ...AdaptiveForm, + JsonField: () =>
Json Field
, + SchemaField: () =>
Options Form
, + IntellisenseTextField: () =>
Intellisense Text Field
, + }; +}); + +jest.mock('office-ui-fabric-react/lib/Dropdown', () => { + const Dropdown = jest.requireActual('office-ui-fabric-react/lib/Dropdown'); + + return { + ...Dropdown, + Dropdown: ({ onChange }) => ( + + ), + }; +}); + +const renderDialogOptionsField = ({ value } = {}) => { + const props = { + description: 'Options passed to the dialog.', + id: 'dialog.options', + label: 'Dialog options', + value, + }; + + const shell = {}; + + const shellData = { + dialogs: [ + { id: 'dialog1', displayName: 'dialog1' }, + { id: 'dialog2', displayName: 'dialog2' }, + { id: 'dialog3', displayName: 'dialog3' }, + ], + dialogSchemas: [ + { + id: 'dialog1', + content: { + type: 'object', + properties: { + foo: { + type: 'string', + }, + bar: { + type: 'number', + }, + }, + }, + }, + ], + }; + return render( + + + + ); +}; + +describe('DialogOptionsField', () => { + it('should render label', async () => { + const { findByText } = renderDialogOptionsField(); + await findByText('Dialog options'); + }); + it('should render the options form if the dialog schema is defined and options is not a string', async () => { + const { findByText } = renderDialogOptionsField({ value: { dialog: 'dialog1', options: {} } }); + await findByText('Options Form'); + }); + it('should render the JsonField if the dialog schema is undefined and options is not a string', async () => { + const { findByText } = renderDialogOptionsField({ value: { dialog: 'dialog2', options: {} } }); + await findByText('Json Field'); + }); + it('should render the IntellisenseTextField if options is a string', async () => { + const { findByText } = renderDialogOptionsField({ value: { dialog: 'dialog2', options: '=user.data' } }); + await findByText('Intellisense Text Field'); + }); + it('should be able to switch between fields', async () => { + const { findByText } = renderDialogOptionsField({ + value: { dialog: 'dialog1', options: {} }, + }); + + // Should initially render Options Form + await findByText('Options Form'); + + // Switch to Json field + const button = await findByText('Switch to Json Field'); + fireEvent.click(button); + + await findByText('Json Field'); + }); +}); diff --git a/Composer/packages/ui-plugins/select-dialog/src/index.ts b/Composer/packages/ui-plugins/select-dialog/src/index.ts index ad19ba9ed8..82842c016f 100644 --- a/Composer/packages/ui-plugins/select-dialog/src/index.ts +++ b/Composer/packages/ui-plugins/select-dialog/src/index.ts @@ -3,26 +3,42 @@ import { PluginConfig } from '@bfc/extension-client'; import { SDKKinds } from '@bfc/shared'; +import formatMessage from 'format-message'; import { SelectDialog } from './SelectDialog'; +import { DialogOptionsField } from './DialogOptionsField'; const config: PluginConfig = { uiSchema: { [SDKKinds.BeginDialog]: { form: { + hidden: ['options'], properties: { dialog: { field: SelectDialog, }, + dialogOptions: { + additionalField: true, + field: DialogOptionsField, + label: () => formatMessage('Options'), + description: () => formatMessage('One or more options that are passed to the dialog that is called.'), + }, }, }, }, [SDKKinds.ReplaceDialog]: { form: { + hidden: ['options'], properties: { dialog: { field: SelectDialog, }, + dialogOptions: { + additionalField: true, + field: DialogOptionsField, + label: () => formatMessage('Options'), + description: () => formatMessage('One or more options that are passed to the dialog that is called.'), + }, }, }, }, From bf290b8b861a1e09edbc0b7ff6227f132477e407 Mon Sep 17 00:00:00 2001 From: Zhixiang Zhan Date: Thu, 25 Mar 2021 16:40:53 +0800 Subject: [PATCH 2/3] fix: navigation of imports in project tree (#6516) * navigation of imports in project tree * clean up Co-authored-by: Dong Lei --- .../components/ProjectTree/ProjectTree.tsx | 63 ++++++++++++++++++- .../src/pages/language-generation/LGPage.tsx | 33 +++++++--- .../pages/language-generation/code-editor.tsx | 7 +-- .../pages/language-generation/table-view.tsx | 8 +-- .../pages/language-understanding/LUPage.tsx | 33 +++++++--- .../language-understanding/code-editor.tsx | 8 +-- .../language-understanding/table-view.tsx | 11 ++-- .../packages/client/src/utils/navigation.ts | 4 +- 8 files changed, 126 insertions(+), 41 deletions(-) diff --git a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx index 2a706a50fa..1e26365b92 100644 --- a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx +++ b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx @@ -26,7 +26,7 @@ import { triggerNotSupported } from '../../utils/dialogValidator'; import { useFeatureFlag } from '../../utils/hooks'; import { LoadingSpinner } from '../LoadingSpinner'; import TelemetryClient from '../../telemetry/TelemetryClient'; -import { dialog } from '../ErrorPopup/styles'; +import { getBaseName } from '../../utils/fileUtil'; import { TreeItem } from './treeItem'; import { ExpandableNode } from './ExpandableNode'; @@ -500,6 +500,35 @@ export const ProjectTree: React.FC = ({ : renderTriggerList(dialog.triggers, dialog, projectId, dialogLink, 1); }; + // flatten lg imports url is same to dialog, to match correct link need render it as dialog + const renderLgImportAsDialog = (item: LanguageFileImport, projectId: string): React.ReactNode => { + const link: TreeLink = { + projectId: rootProjectId, + skillId: projectId === rootProjectId ? undefined : projectId, + dialogId: getBaseName(item.id), + displayName: item.displayName ?? item.id, + diagnostics: [], + isRoot: false, + isRemote: false, + }; + + return ( + + ); + }; + const renderLgImport = (item: LanguageFileImport, projectId: string, dialogId: string): React.ReactNode => { const link: TreeLink = { projectId: rootProjectId, @@ -537,6 +566,34 @@ export const ProjectTree: React.FC = ({ }); }; + const renderLuImportAsDialog = (item: LanguageFileImport, projectId: string): React.ReactNode => { + const link: TreeLink = { + projectId: rootProjectId, + skillId: projectId === rootProjectId ? undefined : projectId, + dialogId: getBaseName(item.id), + displayName: item.displayName ?? item.id, + diagnostics: [], + isRoot: false, + isRemote: false, + }; + + return ( + + ); + }; + const renderLuImport = (item: LanguageFileImport, projectId: string, dialogId: string): React.ReactNode => { const link: TreeLink = { projectId: rootProjectId, @@ -588,10 +645,10 @@ export const ProjectTree: React.FC = ({ const commonLink = options.showCommonLinks ? [renderCommonDialogHeader(projectId, 1)] : []; const importedLgLinks = options.showLgImports - ? lgImportsList.map((file) => renderLgImport(file, projectId, dialog.id)) + ? lgImportsList.map((file) => renderLgImportAsDialog(file, projectId)) : []; const importedLuLinks = options.showLuImports - ? luImportsList.map((file) => renderLuImport(file, projectId, dialog.id)) + ? luImportsList.map((file) => renderLuImportAsDialog(file, projectId)) : []; return [ diff --git a/Composer/packages/client/src/pages/language-generation/LGPage.tsx b/Composer/packages/client/src/pages/language-generation/LGPage.tsx index 94e4a06870..d83b975832 100644 --- a/Composer/packages/client/src/pages/language-generation/LGPage.tsx +++ b/Composer/packages/client/src/pages/language-generation/LGPage.tsx @@ -12,7 +12,7 @@ import { useRecoilValue } from 'recoil'; import { LoadingSpinner } from '../../components/LoadingSpinner'; import { navigateTo } from '../../utils/navigation'; import { Page } from '../../components/Page'; -import { dialogIdsState } from '../../recoilModel'; +import { lgFilesSelectorFamily, localeState } from '../../recoilModel'; import TelemetryClient from '../../telemetry/TelemetryClient'; import TableView from './table-view'; @@ -25,19 +25,24 @@ const LGPage: React.FC> = (props) => { const { dialogId = '', projectId = '', skillId, lgFileId = '' } = props; - const dialogs = useRecoilValue(dialogIdsState(skillId ?? projectId)); + const actualProjectId = skillId ?? projectId; + const locale = useRecoilValue(localeState(actualProjectId)); + const lgFiles = useRecoilValue(lgFilesSelectorFamily(skillId ?? projectId)); const path = props.location?.pathname ?? ''; const edit = /\/edit(\/)?$/.test(path); const baseURL = skillId == null ? `/bot/${projectId}/` : `/bot/${projectId}/skill/${skillId}/`; + const activeFile = lgFileId + ? lgFiles.find(({ id }) => id === lgFileId || id === `${lgFileId}.${locale}`) + : lgFiles.find(({ id }) => id === dialogId || id === `${dialogId}.${locale}`); + useEffect(() => { - const activeDialog = dialogs.find((id) => id === dialogId); - if (!activeDialog && dialogs.length && dialogId !== 'common' && !lgFileId) { + if (!activeFile && lgFiles.length) { navigateTo(`${baseURL}language-generation/common`); } - }, [dialogId, dialogs, projectId, lgFileId]); + }, [dialogId, lgFiles, projectId, lgFileId]); const onToggleEditMode = useCallback( (_e) => { @@ -77,8 +82,22 @@ const LGPage: React.FC }> - - + + diff --git a/Composer/packages/client/src/pages/language-generation/code-editor.tsx b/Composer/packages/client/src/pages/language-generation/code-editor.tsx index dbfd6709ed..f226009d4e 100644 --- a/Composer/packages/client/src/pages/language-generation/code-editor.tsx +++ b/Composer/packages/client/src/pages/language-generation/code-editor.tsx @@ -29,10 +29,11 @@ interface CodeEditorProps extends RouteComponentProps<{}> { projectId: string; skillId?: string; lgFileId?: string; + file?: LgFile; } const CodeEditor: React.FC = (props) => { - const { dialogId, projectId, skillId, lgFileId } = props; + const { dialogId, projectId, skillId, lgFileId, file } = props; const actualProjectId = skillId ?? projectId; const userSettings = useRecoilValue(userSettingsState); @@ -49,10 +50,6 @@ const CodeEditor: React.FC = (props) => { setLocale, } = useRecoilValue(dispatcherState); - const file: LgFile | undefined = lgFileId - ? lgFiles.find(({ id }) => id === lgFileId) - : lgFiles.find(({ id }) => id === dialogId || id === `${dialogId}.${locale}`); - const defaultLangFile = lgFileId ? lgFiles.find(({ id }) => id === lgFileId) : lgFiles.find(({ id }) => id === `${dialogId}.${defaultLanguage}`); diff --git a/Composer/packages/client/src/pages/language-generation/table-view.tsx b/Composer/packages/client/src/pages/language-generation/table-view.tsx index 9ed2b4e732..2b98abeae3 100644 --- a/Composer/packages/client/src/pages/language-generation/table-view.tsx +++ b/Composer/packages/client/src/pages/language-generation/table-view.tsx @@ -18,6 +18,7 @@ import { RouteComponentProps } from '@reach/router'; import { LgTemplate } from '@bfc/shared'; import { useRecoilValue } from 'recoil'; import { lgUtil } from '@bfc/indexers'; +import { LgFile } from '@botframework-composer/types/src'; import { EditableField } from '../../components/EditableField'; import { navigateTo } from '../../utils/navigation'; @@ -32,10 +33,11 @@ interface TableViewProps extends RouteComponentProps<{ dialogId: string; skillId skillId?: string; dialogId?: string; lgFileId?: string; + file?: LgFile; } const TableView: React.FC = (props) => { - const { dialogId, projectId, skillId, lgFileId } = props; + const { dialogId, projectId, skillId, lgFileId, file } = props; const actualProjectId = skillId ?? projectId; @@ -49,10 +51,6 @@ const TableView: React.FC = (props) => { const { languages, defaultLanguage } = settings; - const file = lgFileId - ? lgFiles.find(({ id }) => id === lgFileId) - : lgFiles.find(({ id }) => id === dialogId || id === `${dialogId}.${locale}`); - const defaultLangFile = lgFileId ? lgFiles.find(({ id }) => id === lgFileId) : lgFiles.find(({ id }) => id === `${dialogId}.${defaultLanguage}`); diff --git a/Composer/packages/client/src/pages/language-understanding/LUPage.tsx b/Composer/packages/client/src/pages/language-understanding/LUPage.tsx index 5aa4f25dd2..64a2d3df50 100644 --- a/Composer/packages/client/src/pages/language-understanding/LUPage.tsx +++ b/Composer/packages/client/src/pages/language-understanding/LUPage.tsx @@ -11,7 +11,7 @@ import { useRecoilValue } from 'recoil'; import { navigateTo, buildURL } from '../../utils/navigation'; import { LoadingSpinner } from '../../components/LoadingSpinner'; import { Page } from '../../components/Page'; -import { dialogIdsState } from '../../recoilModel'; +import { localeState, luFilesState } from '../../recoilModel'; import TableView from './table-view'; @@ -24,18 +24,23 @@ const LUPage: React.FC> = (props) => { const { dialogId = '', projectId = '', skillId, luFileId = '' } = props; - const dialogs = useRecoilValue(dialogIdsState(skillId ?? projectId)); + const actualProjectId = skillId ?? projectId; + const locale = useRecoilValue(localeState(actualProjectId)); + const luFiles = useRecoilValue(luFilesState(actualProjectId)); const path = props.location?.pathname ?? ''; const edit = /\/edit(\/)?$/.test(path); const isRoot = dialogId === 'all'; + const activeFile = luFileId + ? luFiles.find(({ id }) => id === luFileId || id === `${luFileId}.${locale}`) + : luFiles.find(({ id }) => id === dialogId || id === `${dialogId}.${locale}`); + useEffect(() => { - const activeDialog = dialogs.find((id) => id === dialogId); - if (!activeDialog && dialogId !== 'all' && dialogs.length && !luFileId) { + if (!activeFile && luFiles.length) { navigateTo(buildURL('language-understanding', { projectId, skillId })); } - }, [dialogId, dialogs, projectId, luFileId]); + }, [dialogId, luFiles, projectId, luFileId]); const onToggleEditMode = useCallback(() => { let url = buildURL('language-understanding', { projectId, skillId, dialogId }); @@ -73,8 +78,22 @@ const LUPage: React.FC }> - - + + diff --git a/Composer/packages/client/src/pages/language-understanding/code-editor.tsx b/Composer/packages/client/src/pages/language-understanding/code-editor.tsx index a056e8c01e..3f0cbd86c7 100644 --- a/Composer/packages/client/src/pages/language-understanding/code-editor.tsx +++ b/Composer/packages/client/src/pages/language-understanding/code-editor.tsx @@ -11,6 +11,7 @@ import { RouteComponentProps } from '@reach/router'; import querystring from 'query-string'; import { CodeEditorSettings } from '@bfc/shared'; import { useRecoilValue } from 'recoil'; +import { LuFile } from '@bfc/shared'; import { luFilesState, localeState, settingsState } from '../../recoilModel/atoms'; import { userSettingsState, dispatcherState } from '../../recoilModel'; @@ -24,6 +25,7 @@ interface CodeEditorProps extends RouteComponentProps<{}> { projectId: string; skillId?: string; luFileId?: string; + file?: LuFile; } const CodeEditor: React.FC = (props) => { @@ -34,7 +36,7 @@ const CodeEditor: React.FC = (props) => { updateUserSettings, setLocale, } = useRecoilValue(dispatcherState); - const { dialogId, projectId, skillId, luFileId } = props; + const { dialogId, projectId, skillId, luFileId, file } = props; const actualProjectId = skillId ?? projectId; const luFiles = useRecoilValue(luFilesState(actualProjectId)); @@ -43,10 +45,6 @@ const CodeEditor: React.FC = (props) => { const { languages, defaultLanguage } = settings; - const file = luFileId - ? luFiles.find(({ id }) => id === luFileId) - : luFiles.find(({ id }) => id === dialogId || id === `${dialogId}.${locale}`); - const defaultLangFile = luFileId ? luFiles.find(({ id }) => id === luFileId) : luFiles.find(({ id }) => id === `${dialogId}.${defaultLanguage}`); 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 95a222d373..e79ebec04a 100644 --- a/Composer/packages/client/src/pages/language-understanding/table-view.tsx +++ b/Composer/packages/client/src/pages/language-understanding/table-view.tsx @@ -32,6 +32,7 @@ interface TableViewProps extends RouteComponentProps<{ dialogId: string; skillId skillId?: string; dialogId?: string; luFileId?: string; + file?: LuFile; } interface Intent { @@ -44,7 +45,7 @@ interface Intent { } const TableView: React.FC = (props) => { - const { dialogId, projectId, skillId, luFileId } = props; + const { dialogId, projectId, skillId, luFileId, file } = props; const actualProjectId = skillId ?? projectId; const baseURL = skillId == null ? `/bot/${projectId}/` : `/bot/${projectId}/skill/${skillId}/`; @@ -60,10 +61,6 @@ const TableView: React.FC = (props) => { const activeDialog = dialogs.find(({ id }) => id === dialogId); - const file = luFileId - ? luFiles.find(({ id }) => id === luFileId) - : luFiles.find(({ id }) => id === dialogId || id === `${dialogId}.${locale}`); - const defaultLangFile = luFileId ? luFiles.find(({ id }) => id === luFileId) : luFiles.find(({ id }) => id === `${dialogId}.${defaultLanguage}`); @@ -107,7 +104,7 @@ const TableView: React.FC = (props) => { return result.concat(items); }, []); - if (luFileId && file) { + if (file) { const luIntents: Intent[] = []; get(file, 'intents', []).forEach(({ Name: name, Body: phrases }) => { const state = getIntentState(file); @@ -127,7 +124,7 @@ const TableView: React.FC = (props) => { } else { setIntents(allIntents); } - }, [luFiles, activeDialog, actualProjectId, luFileId]); + }, [luFiles, activeDialog, actualProjectId, luFileId, file]); const handleIntentUpdate = useCallback( (fileId: string, intentName: string, intent: LuIntentSection) => { diff --git a/Composer/packages/client/src/utils/navigation.ts b/Composer/packages/client/src/utils/navigation.ts index a2da1037df..764938c358 100644 --- a/Composer/packages/client/src/utils/navigation.ts +++ b/Composer/packages/client/src/utils/navigation.ts @@ -121,11 +121,11 @@ export function buildURL(pageMode: PageMode, link: Partial) { const baseURL = skillId == null ? `/bot/${projectId}/` : `/bot/${projectId}/skill/${skillId}/`; if (pageMode === 'language-generation' && lgFileId) { - return `${baseURL}${pageMode}/${dialogId ?? 'all'}/item/${lgFileId}`; + return dialogId ? `${baseURL}${pageMode}/${dialogId}/item/${lgFileId}` : `${baseURL}${pageMode}/${lgFileId}`; } if (pageMode === 'language-understanding' && luFileId) { - return `${baseURL}${pageMode}/${dialogId ?? 'all'}/item/${luFileId}`; + return dialogId ? `${baseURL}${pageMode}/${dialogId}/item/${luFileId}` : `${baseURL}${pageMode}/${luFileId}`; } return `${baseURL}${pageMode}/${dialogId ?? 'all'}`; From c68205a94169b6693423771c71c5dbba3a619d80 Mon Sep 17 00:00:00 2001 From: leileizhang Date: Thu, 25 Mar 2021 21:10:38 +0800 Subject: [PATCH 3/3] fix: docker build failed (#6551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix:docker build failed * update the ci file * add some comments --- .github/workflows/docker.yml | 1 + Dockerfile | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a58188d734..4986cc90f2 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -31,6 +31,7 @@ jobs: context: . file: Dockerfile tags: botframework-composer + target: build # CI will stop at the build stage - name: Health check run: | diff --git a/Dockerfile b/Dockerfile index a113ef3241..625494d6a3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,12 @@ ENV NODE_ENV "production" ENV COMPOSER_BUILTIN_EXTENSIONS_DIR "/src/extensions" RUN yarn build:prod $YARN_ARGS +# CI only +ENV COMPOSER_REMOTE_EXTENSIONS_DIR "/src/remote-extensions" +ENV COMPOSER_REMOTE_EXTENSION_DATA_DIR "/src/extension-data" +ENV COMPOSER_EXTENSION_MANIFEST "/src/extensions.json" +CMD ["yarn","start:server"] + FROM base as composerbasic ARG YARN_ARGS