diff --git a/Composer/packages/client/src/pages/notifications/index.tsx b/Composer/packages/client/src/pages/notifications/index.tsx index 416a3cd03e..6cb913dbd3 100644 --- a/Composer/packages/client/src/pages/notifications/index.tsx +++ b/Composer/packages/client/src/pages/notifications/index.tsx @@ -13,6 +13,7 @@ import { NotificationHeader } from './NotificationHeader'; import { root } from './styles'; import { INotification } from './types'; import { navigateTo } from './../../utils'; +import { convertDialogDiagnosticToUrl } from './../../utils/navigation'; const navigations = { lg: (item: INotification) => { @@ -24,25 +25,7 @@ const navigations = { dialog: (item: INotification) => { //path is like main.trigers[0].actions[0] //uri = id?selected=triggers[0]&focused=triggers[0].actions[0] - const path = item.diagnostic.path; - let uri = `/dialogs/${item.id}`; - if (path) { - const matchTriggers = /triggers\[(\d+)\]/g.exec(path); - const actionPatt = /actions\[(\d+)\]/g; - let temp: RegExpExecArray | null = null; - let matchActions: RegExpExecArray | null = null; - while ((temp = actionPatt.exec(path)) !== null) { - matchActions = temp; - } - const trigger = matchTriggers ? `triggers[${+matchTriggers[1]}]` : ''; - const action = matchActions ? `actions[${+matchActions[1]}]` : ''; - if (trigger) { - uri += `?selected=${trigger}`; - if (action) { - uri += `&focused=${trigger}.${action}`; - } - } - } + const uri = convertDialogDiagnosticToUrl(item.diagnostic); navigateTo(uri); }, }; diff --git a/Composer/packages/client/src/utils/dialogUtil.ts b/Composer/packages/client/src/utils/dialogUtil.ts index d82214f6ec..a9044aed67 100644 --- a/Composer/packages/client/src/utils/dialogUtil.ts +++ b/Composer/packages/client/src/utils/dialogUtil.ts @@ -292,7 +292,7 @@ export function getSelected(focused: string): string { export function replaceDialogDiagnosticLabel(path?: string): string { if (!path) return ''; - let list = path.split(': '); + let list = path.split('#'); list = list.map(item => { return ConceptLabels[item]?.title || item; }); diff --git a/Composer/packages/client/src/utils/navigation.ts b/Composer/packages/client/src/utils/navigation.ts index 412496422e..d2caa78152 100644 --- a/Composer/packages/client/src/utils/navigation.ts +++ b/Composer/packages/client/src/utils/navigation.ts @@ -3,6 +3,8 @@ import cloneDeep from 'lodash/cloneDeep'; import { navigate, NavigateOptions } from '@reach/router'; +import { Diagnostic } from '@bfc/indexers'; +import { parsePathToFocused, parsePathToSelected, parseTypeToFragment } from '@bfc/shared'; import { BreadcrumbItem, DesignPageLocation } from '../store/types'; @@ -76,6 +78,36 @@ interface NavigationState { breadcrumb: BreadcrumbItem[]; } +export function convertDialogDiagnosticToUrl(diagnostic: Diagnostic): 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}`; + if (!path) return uri; + + const items = path.split('#'); + const sub = items[0]; + const type = items[1]; + const property = items[2]; + + const selected = parsePathToSelected(sub); + + if (!selected) return uri; + uri += `?selected=${selected}`; + + const focused = parsePathToFocused(sub); + if (!focused) return uri; + uri += `&focused=${focused}`; + + const fragment = parseTypeToFragment(type, property); + if (!fragment) return uri; + uri += `#${fragment}`; + + return uri; +} + export function navigateTo(to: string, navigateOpts: NavigateOptions = {}) { const mapNavPath = resolveToBasePath(BASEPATH, to); navigate(mapNavPath, navigateOpts); diff --git a/Composer/packages/lib/indexers/src/dialogUtils/dialogChecker.ts b/Composer/packages/lib/indexers/src/dialogUtils/dialogChecker.ts index d610553c83..82e0acd849 100644 --- a/Composer/packages/lib/indexers/src/dialogUtils/dialogChecker.ts +++ b/Composer/packages/lib/indexers/src/dialogUtils/dialogChecker.ts @@ -31,7 +31,7 @@ export const IsExpression: CheckerFunc = ( } if (message) { const diagnostic = new Diagnostic(message, ''); - diagnostic.path = `${path}: ${value.$type}: ${property}`; + diagnostic.path = `${path}#${value.$type}#${property}`; result.push(diagnostic); } return result; diff --git a/Composer/packages/lib/shared/__tests__/convertUtils/parsePathToFocused.test.ts b/Composer/packages/lib/shared/__tests__/convertUtils/parsePathToFocused.test.ts new file mode 100644 index 0000000000..9a58bf4bde --- /dev/null +++ b/Composer/packages/lib/shared/__tests__/convertUtils/parsePathToFocused.test.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { parsePathToFocused } from '../../src'; + +describe('parsePathToFocused', () => { + it('should return focusedPath', () => { + expect(parsePathToFocused('')).toBe(''); + expect(parsePathToFocused('main.trigers[0].actions[0]')).toBe('trigers[0].actions[0]'); + expect(parsePathToFocused('main.trigers[0].actions[0].actions[1]')).toBe('trigers[0].actions[0].actions[1]'); + expect(parsePathToFocused('main.trigers[0].actions[0].elseActions[1]')).toBe( + 'trigers[0].actions[0].elseActions[1]' + ); + }); +}); diff --git a/Composer/packages/lib/shared/__tests__/convertUtils/parsePathToSelected.test.ts b/Composer/packages/lib/shared/__tests__/convertUtils/parsePathToSelected.test.ts new file mode 100644 index 0000000000..83b78315fa --- /dev/null +++ b/Composer/packages/lib/shared/__tests__/convertUtils/parsePathToSelected.test.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { parsePathToSelected } from '../../src'; + +describe('parsePathToSelected', () => { + it('should return selected path', () => { + expect(parsePathToSelected('')).toBe(''); + expect(parsePathToSelected('main.triggers[0].actions[0]')).toBe('triggers[0]'); + expect(parsePathToSelected('main')).toBe(''); + expect(parsePathToSelected('main.triggers[0]')).toBe('triggers[0]'); + }); +}); diff --git a/Composer/packages/lib/shared/__tests__/convertUtils/parseTypeToFragment.test.ts b/Composer/packages/lib/shared/__tests__/convertUtils/parseTypeToFragment.test.ts new file mode 100644 index 0000000000..b7715cc5ed --- /dev/null +++ b/Composer/packages/lib/shared/__tests__/convertUtils/parseTypeToFragment.test.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { parseTypeToFragment, PromptTab } from './../../src'; + +describe('parseTypeToFragment', () => { + it('should return corrent tab name', () => { + expect(parseTypeToFragment('Microsoft.TextInput', 'unrecognizedPrompt')).toBe(PromptTab.OTHER); + expect(parseTypeToFragment('Microsoft.TextInput', 'property')).toBe(PromptTab.USER_INPUT); + expect(parseTypeToFragment('Microsoft.TextInput', 'prompt')).toBe(PromptTab.BOT_ASKS); + expect(parseTypeToFragment('Microsoft.NumberInput', 'property')).toBe(PromptTab.USER_INPUT); + expect(parseTypeToFragment('Microsoft.LuisRecognizer', 'property')).toBe(''); + expect(parseTypeToFragment('test', 'items')).toBe(''); + }); +}); diff --git a/Composer/packages/lib/shared/src/convertUtils/index.ts b/Composer/packages/lib/shared/src/convertUtils/index.ts new file mode 100644 index 0000000000..a9c34ecbef --- /dev/null +++ b/Composer/packages/lib/shared/src/convertUtils/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export * from './parsePathToFocused'; +export * from './parsePathToSelected'; +export * from './parseTypeToFragment'; diff --git a/Composer/packages/lib/shared/src/convertUtils/parsePathToFocused.ts b/Composer/packages/lib/shared/src/convertUtils/parsePathToFocused.ts new file mode 100644 index 0000000000..d7997c1350 --- /dev/null +++ b/Composer/packages/lib/shared/src/convertUtils/parsePathToFocused.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export function parsePathToFocused(path: string): string { + //path is like main.trigers[0].actions[0] + + const list = path.split('.'); + + if (list.length > 2) { + list.shift(); + return list.join('.'); + } + return ''; +} diff --git a/Composer/packages/lib/shared/src/convertUtils/parsePathToSelected.ts b/Composer/packages/lib/shared/src/convertUtils/parsePathToSelected.ts new file mode 100644 index 0000000000..ec8786aee5 --- /dev/null +++ b/Composer/packages/lib/shared/src/convertUtils/parsePathToSelected.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export function parsePathToSelected(path: string): string { + //path is like main.trigers[0].actions[0] + + const triggerPattern = /triggers\[(\d+)\]/g; + const matchTriggers = triggerPattern.exec(path); + + const trigger = matchTriggers ? `triggers[${+matchTriggers[1]}]` : ''; + + return trigger; +} diff --git a/Composer/packages/lib/shared/src/convertUtils/parseTypeToFragment.ts b/Composer/packages/lib/shared/src/convertUtils/parseTypeToFragment.ts new file mode 100644 index 0000000000..023c247c89 --- /dev/null +++ b/Composer/packages/lib/shared/src/convertUtils/parseTypeToFragment.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { dialogGroups, DialogGroup } from '../viewUtils'; +import { PromptTab } from '../promptTabs'; + +export function parseTypeToFragment(type: string, property: string): string { + const inputTypes = dialogGroups[DialogGroup.INPUT].types; + const index = inputTypes.findIndex(t => t === type); + if (index >= 0) { + if (property === 'property') return PromptTab.USER_INPUT; + if (property === 'prompt') return PromptTab.BOT_ASKS; + return PromptTab.OTHER; + } + return ''; +} diff --git a/Composer/packages/lib/shared/src/index.ts b/Composer/packages/lib/shared/src/index.ts index 2f496d4a9b..07326040cc 100644 --- a/Composer/packages/lib/shared/src/index.ts +++ b/Composer/packages/lib/shared/src/index.ts @@ -12,4 +12,5 @@ export * from './promptTabs'; export * from './appschema'; export * from './types'; export * from './constant'; +export * from './convertUtils'; export { LgMetaData, LgTemplateRef } from '@bfc/indexers';