diff --git a/Composer/packages/adaptive-flow/__tests__/adaptive-flow-renderer/adaptive/AdaptiveDialog.test.tsx b/Composer/packages/adaptive-flow/__tests__/adaptive-flow-renderer/adaptive/AdaptiveDialog.test.tsx index 7e86d9de73..c588d5329e 100644 --- a/Composer/packages/adaptive-flow/__tests__/adaptive-flow-renderer/adaptive/AdaptiveDialog.test.tsx +++ b/Composer/packages/adaptive-flow/__tests__/adaptive-flow-renderer/adaptive/AdaptiveDialog.test.tsx @@ -37,7 +37,7 @@ describe('', () => { activeTrigger="triggers[0]" dialogData={dialog} dialogId="test" - schema={uischema} + uischema={uischema} widgets={widgets} onEvent={() => null} /> diff --git a/Composer/packages/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx b/Composer/packages/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx index 50dad3bc4c..70885e352a 100644 --- a/Composer/packages/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx +++ b/Composer/packages/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx @@ -7,7 +7,7 @@ import createCache from '@emotion/cache'; import React, { useRef, useMemo, useEffect } from 'react'; import isEqual from 'lodash/isEqual'; import formatMessage from 'format-message'; -import { DialogFactory } from '@bfc/shared'; +import { DialogFactory, SchemaDefinitions } from '@bfc/shared'; import { useShellApi, JSONSchema7, FlowUISchema, FlowWidget } from '@bfc/extension-client'; import { MarqueeSelection } from 'office-ui-fabric-react/lib/MarqueeSelection'; @@ -171,7 +171,8 @@ const VisualDesigner: React.FC = ({ onFocus, onBlur, schema NodeWrapper: VisualEditorNodeWrapper, ElementWrapper: VisualEditorElementWrapper, }} - schema={{ ...schemaFromPlugins, ...customFlowSchema }} + sdkschema={schema?.definitions as SchemaDefinitions} + uischema={{ ...schemaFromPlugins, ...customFlowSchema }} widgets={widgetsFromPlugins} onEvent={(eventName, eventData) => { divRef.current?.focus({ preventScroll: true }); diff --git a/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/adaptive/AdaptiveDialog.tsx b/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/adaptive/AdaptiveDialog.tsx index c95b098491..d30b1015d9 100644 --- a/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/adaptive/AdaptiveDialog.tsx +++ b/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/adaptive/AdaptiveDialog.tsx @@ -5,7 +5,7 @@ import { jsx } from '@emotion/core'; import { FC, Fragment } from 'react'; import get from 'lodash/get'; -import { FlowEditorWidgetMap, FlowUISchema } from '@bfc/extension-client'; +import { FlowEditorWidgetMap, FlowUISchema, SchemaDefinitions } from '@bfc/extension-client'; import { EditorEventHandler } from '../constants/NodeEventTypes'; import { RendererContext, DefaultRenderers, RendererContextData } from '../contexts/RendererContext'; @@ -30,10 +30,14 @@ export interface AdaptiveDialogProps { onEvent: EditorEventHandler; /** UI schema to define how to render a sdk $kind */ - schema: FlowUISchema; + uischema: FlowUISchema; /** All available widgets to render a node */ widgets: FlowEditorWidgetMap; + + /** SDK schema to define the data model of a sdk $kind */ + sdkschema?: SchemaDefinitions; + renderers?: Partial; } @@ -42,7 +46,8 @@ export const AdaptiveDialog: FC = ({ dialogData, activeTrigger, onEvent, - schema = builtinSchema, + sdkschema, + uischema = builtinSchema, widgets = builtinWidgets, renderers = {}, }): JSX.Element => { @@ -55,7 +60,8 @@ export const AdaptiveDialog: FC = ({ ({ widgets: {}, schemaProvider: new WidgetSchemaProvider({ default: { widget: () => null } }), + sdkschema: {}, }); diff --git a/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/utils/visual/widgetRenderer.tsx b/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/utils/visual/widgetRenderer.tsx index 4734ab1d65..78d9dcd3fb 100644 --- a/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/utils/visual/widgetRenderer.tsx +++ b/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/utils/visual/widgetRenderer.tsx @@ -2,22 +2,12 @@ // Licensed under the MIT License. import React from 'react'; -import { BaseSchema } from '@bfc/shared'; -import { FlowEditorWidgetMap, FlowWidget, FlowWidgetProp, WidgetEventHandler } from '@bfc/extension-client'; +import { FlowEditorWidgetMap, FlowWidget, FlowWidgetProp, WidgetContainerProps } from '@bfc/extension-client'; import { Boundary } from '../../models/Boundary'; import { evaluateWidgetProp, ActionContextKey } from '../expression/widgetExpressionEvaluator'; -export interface UIWidgetContext { - /** The uniq id of current schema data. Usually a json path. */ - id: string; - - /** Declarative json with a $kind field. */ - data: BaseSchema; - - /** Handle UI events */ - onEvent: WidgetEventHandler; - +export interface UIWidgetContext extends WidgetContainerProps { /** Report widget boundary */ onResize: (boundary: Boundary) => void; } diff --git a/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/widgets/ActionHeader/ActionHeader.tsx b/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/widgets/ActionHeader/ActionHeader.tsx index 1fdeda32e0..875defdb0a 100644 --- a/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/widgets/ActionHeader/ActionHeader.tsx +++ b/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/widgets/ActionHeader/ActionHeader.tsx @@ -35,6 +35,7 @@ export interface ActionHeaderProps extends WidgetContainerProps { export const ActionHeader: WidgetComponent = ({ id, data, + adaptiveSchema, onEvent, title = '', disableSDKTitle, @@ -48,7 +49,7 @@ export const ActionHeader: WidgetComponent = ({ const textCSS = disabled ? DisabledHeaderTextCSS : HeaderTextCSS(colors.color); const iconColor = disabled ? DisabledIconColor : colors.icon; - const headerContent = disableSDKTitle ? title : generateSDKTitle(data, title); + const headerContent = disableSDKTitle ? title : generateSDKTitle(adaptiveSchema, data, title); const { NodeMenu } = useContext(RendererContext); const menuNode = diff --git a/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/widgets/AdaptiveAction.tsx b/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/widgets/AdaptiveAction.tsx index 4d95e75566..29ecf057a9 100644 --- a/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/widgets/AdaptiveAction.tsx +++ b/Composer/packages/adaptive-flow/src/adaptive-flow-renderer/widgets/AdaptiveAction.tsx @@ -13,13 +13,14 @@ import { RendererContext } from '../contexts/RendererContext'; import { ElementMeasurer } from '../components/ElementMeasurer'; export const StepRenderer: FC = ({ id, data, onEvent, onResize }): JSX.Element => { - const { widgets, schemaProvider } = useContext(SchemaContext); + const { widgets, schemaProvider, sdkschema } = useContext(SchemaContext); const { NodeWrapper } = useContext(RendererContext); const $kind = get(data, '$kind', ''); const widgetSchema = schemaProvider.get($kind); + const adaptiveSchema = get(sdkschema, $kind, {}); - const content = renderUIWidget(widgetSchema, widgets, { id, data, onEvent, onResize }); + const content = renderUIWidget(widgetSchema, widgets, { id, data, adaptiveSchema, onEvent, onResize }); if (widgetSchema.nowrap) { return content; } diff --git a/Composer/packages/client/src/pages/design/VisualEditor.tsx b/Composer/packages/client/src/pages/design/VisualEditor.tsx index 708cf47a29..86a7424a73 100644 --- a/Composer/packages/client/src/pages/design/VisualEditor.tsx +++ b/Composer/packages/client/src/pages/design/VisualEditor.tsx @@ -3,13 +3,14 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; -import React, { useCallback, useState, useEffect } from 'react'; +import React, { useCallback, useState, useEffect, useMemo } from 'react'; import formatMessage from 'format-message'; import { ActionButton } from 'office-ui-fabric-react/lib/Button'; import get from 'lodash/get'; import VisualDesigner from '@bfc/adaptive-flow'; import { useRecoilValue } from 'recoil'; -import { useShellApi } from '@bfc/extension-client'; +import { useFormConfig, useShellApi } from '@bfc/extension-client'; +import cloneDeep from 'lodash/cloneDeep'; import grayComposerIcon from '../../images/grayComposerIcon.svg'; import { @@ -72,6 +73,21 @@ const VisualEditor: React.FC = (props) => { const addRef = useCallback((visualEditor) => onboardingAddCoachMarkRef({ visualEditor }), []); + const formConfig = useFormConfig(); + const overridedSDKSchema = useMemo(() => { + const sdkSchema = cloneDeep(schemas.sdk?.content ?? {}); + const sdkDefinitions = sdkSchema.definitions; + + // Override the sdk.schema 'title' field with form ui option 'label' field + // to make sure the title is consistent with Form Editor. + Object.entries(formConfig).forEach(([$kind, formOptions]) => { + if (formOptions && sdkDefinitions[$kind]) { + sdkDefinitions[$kind].title = formOptions?.label; + } + }); + return sdkSchema; + }, [formConfig, schemas]); + useEffect(() => { const dialog = dialogs.find((d) => d.id === dialogId); const visible = get(dialog, 'triggers', []).length === 0; @@ -88,7 +104,7 @@ const VisualEditor: React.FC = (props) => { > diff --git a/Composer/packages/extension-client/src/types/flowSchema.ts b/Composer/packages/extension-client/src/types/flowSchema.ts index 172bb57965..b10223e05e 100644 --- a/Composer/packages/extension-client/src/types/flowSchema.ts +++ b/Composer/packages/extension-client/src/types/flowSchema.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { FC, ComponentClass } from 'react'; -import { BaseSchema, SDKKinds } from '@botframework-composer/types'; +import { BaseSchema, JSONSchema7, SDKKinds } from '@botframework-composer/types'; export type FlowEditorWidgetMap = { [widgetName: string]: WidgetComponent }; export enum FlowSchemaBuiltinKeys { @@ -25,9 +25,18 @@ export type WidgetComponent = FC | ComponentC export type WidgetEventHandler = (eventName: string, eventData?: any) => void; export interface WidgetContainerProps { + /** The uniq id of current schema data. Usually a json path. */ id: string; + + /** Declarative json with a $kind field. */ data: BaseSchema; + + /** Declarative json's schema from bot-builder sdk. */ + adaptiveSchema: JSONSchema7; + + /** UI events handler */ onEvent: WidgetEventHandler; + [propKey: string]: any; } diff --git a/Composer/packages/lib/shared/src/viewUtils.ts b/Composer/packages/lib/shared/src/viewUtils.ts index abb556db04..7dd0e83d39 100644 --- a/Composer/packages/lib/shared/src/viewUtils.ts +++ b/Composer/packages/lib/shared/src/viewUtils.ts @@ -3,9 +3,8 @@ import get from 'lodash/get'; import formatMessage from 'format-message'; -import { SDKKinds } from '@botframework-composer/types'; +import { JSONSchema7, SDKKinds } from '@botframework-composer/types'; -import { conceptLabels as conceptLabelsFn } from './labelMap'; import { PromptTab, PromptTabTitles } from './promptTabs'; export const PROMPT_TYPES = [ @@ -188,13 +187,13 @@ const truncateSDKType = ($kind) => (typeof $kind === 'string' ? $kind.replace('M * Title priority: $designer.name > title from sdk schema > customize title > $kind suffix * @param customizedTitile customized title */ -export function generateSDKTitle(data, customizedTitle?: string, tab?: PromptTab) { +export function generateSDKTitle(sdkschema: JSONSchema7, data, customizedTitle?: string, tab?: PromptTab) { const $kind = get(data, '$kind'); - const titleFromShared = get(conceptLabelsFn(), [$kind, 'title']); + const titleFromSDKSchema = get(sdkschema, 'title'); const titleFrom$designer = get(data, '$designer.name'); const titleFrom$kind = truncateSDKType($kind); - const title = titleFromShared || titleFrom$designer || customizedTitle || titleFrom$kind; + const title = titleFrom$designer || titleFromSDKSchema || customizedTitle || titleFrom$kind; if (tab) { return `${PromptTabTitles} (${title})`; }