Skip to content

Commit

Permalink
Merge branch 'benbrown/deletebutton' of https://github.com/microsoft/…
Browse files Browse the repository at this point in the history
…BotFramework-Composer into benbrown/deletebutton
  • Loading branch information
benbrown committed Mar 18, 2021
2 parents 4a9570d + 3958c7e commit 3733eb8
Show file tree
Hide file tree
Showing 102 changed files with 7,495 additions and 2,298 deletions.
38 changes: 21 additions & 17 deletions Composer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,27 @@
"engines": {
"node": ">=12"
},
"workspaces": [
"packages/adaptive-flow",
"packages/adaptive-form",
"packages/client",
"packages/electron-server",
"packages/extension",
"packages/extension-client",
"packages/form-dialogs",
"packages/intellisense",
"packages/lib/*",
"packages/server",
"packages/test-utils",
"packages/tools/built-in-functions",
"packages/tools/language-servers/*",
"packages/types",
"packages/ui-plugins/*"
],
"workspaces": {
"packages": [
"packages/adaptive-flow",
"packages/adaptive-form",
"packages/client",
"packages/electron-server",
"packages/extension",
"packages/extension-client",
"packages/form-dialogs",
"packages/intellisense",
"packages/lib/*",
"packages/server",
"packages/server-workers",
"packages/test-utils",
"packages/tools/built-in-functions",
"packages/tools/language-servers/*",
"packages/types",
"packages/ui-plugins/*"
],
"nohoist": ["**/server-workers/**"]
},
"scripts": {
"build": "node scripts/begin.js && yarn build:prod && yarn l10n",
"build:prod": "yarn build:dev && yarn build:client && yarn build:server && yarn build:electron",
Expand Down
6 changes: 3 additions & 3 deletions Composer/packages/adaptive-form/src/components/FormTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ const FormTitle: React.FC<FormTitleProps> = (props) => {
const uiSubtitle = typeof uiOptions?.subtitle === 'function' ? uiOptions.subtitle(formData) : uiOptions.subtitle;
const initialValue = useMemo(() => {
const designerName = formData.$designer?.name;

return designerName ?? uiLabel ?? schema.title;
}, [formData.$designer?.name, uiLabel, schema.title]);
const id = formData.id;
return designerName ?? id ?? uiLabel ?? schema.title;
}, [formData.$designer?.name, uiLabel, schema.title, formData.id]);

const getHelpLinkLabel = (): string => {
return (uiLabel || schema.title || '').toLowerCase();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('ExternalAdapterSettings', () => {
initRecoilState
);

const link = getByText('Package Settings');
const link = getByText('the package manager');

expect(link.attributes.getNamedItem('href')?.value).toEqual('plugin/package-manager/package-manager');
});
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"re-resizable": "^6.9.0",
"react": "16.13.1",
"react-app-polyfill": "^0.2.1",
"react-dev-utils": "^7.0.3",
"react-dev-utils": "9.1.0",
"react-dom": "16.13.1",
"react-frame-component": "^4.0.2",
"react-markdown": "^5.0.3",
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/client/scripts/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ checkBrowsers(paths.appPath, isInteractive)
const appName = require(paths.appPackageJson).name;
const urls = prepareUrls(protocol, HOST, port);
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
const compiler = createCompiler({ webpack, config, appName, urls, useYarn });
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@ const calloutLink = css`
margin-bottom: 24px;
`;

const descriptionLabel = css`
font-weight: bold;
text-transform: capitalize;
`;

const descriptionText = css`
margin-left: 15px;
margin-bottom: 10px;
`;

const descriptionLongText = css`
overflow: auto;
font-size: small;
`;
const descriptionShow = css``;
const descriptionHide = css`
display: none;
`;

// -------------------- ErrorCallout -------------------- //

export interface IErrorCalloutProps {
Expand All @@ -47,8 +66,83 @@ export interface IErrorCalloutProps {
};
}

interface IErrorMessage {
key: string;
value: any;
isPre: boolean;
visible: boolean;
order: number;
}

interface IField {
visible?: boolean;
name: string;
index?: number;
}

const fieldsWhiteList = new Map<string, IField>();
fieldsWhiteList.set('message', { visible: true, name: 'Message', index: 1 });
fieldsWhiteList.set('stack', { visible: true, name: 'Stack Trace', index: 3 });
fieldsWhiteList.set('stdout', { visible: true, name: 'Output', index: 4 });
fieldsWhiteList.set('cmd', { visible: true, name: 'Command', index: 2 });
fieldsWhiteList.set('killed', { visible: false, name: 'killed' });
fieldsWhiteList.set('code', { visible: false, name: 'code' });
fieldsWhiteList.set('signal', { visible: false, name: 'signal' });
fieldsWhiteList.set('stderr', { visible: false, name: 'stderr' });

export const ErrorCallout: React.FC<IErrorCalloutProps> = (props) => {
const { onDismiss, onTry, target, visible, error } = props;

const convertToJson = function (item): any {
if (typeof item === 'object') {
return item;
}

try {
return JSON.parse(item);
} catch (e) {
return null;
}
};
const parseObject = function (map): IErrorMessage[] {
return Object.keys(map)
.map((k) => {
const field: IField = fieldsWhiteList.get(k) || { visible: true, name: k };

return {
key: field.name,
value: map[k],
order: field.index || 1000,
isPre: map[k] != null && typeof map[k] == 'string' && map[k].length >= 75 && map[k].indexOf('\n') != -1,
visible:
field.visible ||
(map[k] != null && ((typeof map[k] == 'string' && map[k].trim() != '') || typeof map[k] != 'string')),
};
})
.sort((left, right) => {
return left.order - right.order;
});
};
const renderRow = function (obj: IErrorMessage) {
return (
<div css={obj.visible ? descriptionShow : descriptionHide}>
<div css={descriptionLabel}>{obj.key}:</div>
<div css={descriptionText}>{obj.isPre ? <pre css={descriptionLongText}>{obj.value}</pre> : obj.value}</div>
</div>
);
};

const buildErrorMessage = (error) => {
const jsonObj = convertToJson(error.message);
if (jsonObj === null) {
return error.message + ' ';
} else {
const parsed = parseObject(jsonObj);

return <div>{parsed.map(renderRow)}</div>;
}
};

return (
<Callout
setInitialFocus
Expand All @@ -64,7 +158,7 @@ export const ErrorCallout: React.FC<IErrorCalloutProps> = (props) => {
{error.title}
</p>
<p css={calloutDescription} id="callout-description-id">
{error.message + ' '}
{buildErrorMessage(error)}
{error.linkAfterMessage != null && (
<Link href={error.linkAfterMessage.url} target={'_blank'}>
{error.linkAfterMessage.text}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import querystring from 'query-string';
import axios from 'axios';

import { DialogCreationCopy, EmptyBotTemplateId } from '../../constants';
import { creationFlowTypeState } from '../../recoilModel';
import { creationFlowTypeState, featureFlagsState } from '../../recoilModel';
import TelemetryClient from '../../telemetry/TelemetryClient';
import { getAliasFromPayload } from '../../utils/electronUtil';

Expand Down Expand Up @@ -129,6 +129,7 @@ export function CreateOptions(props: CreateOptionsProps) {
const [currentTemplate, setCurrentTemplate] = useState('');
const [emptyBotKey, setEmptyBotKey] = useState('');
const creationFlowType = useRecoilValue(creationFlowTypeState);
const featureFlags = useRecoilValue(featureFlagsState);

const selection = useMemo(() => {
return new Selection({
Expand Down Expand Up @@ -235,6 +236,12 @@ export function CreateOptions(props: CreateOptionsProps) {
return null;
};

useEffect(() => {
if (featureFlags.NEW_CREATION_FLOW?.enabled) {
navigate(`/v2/projects/create${props?.location?.search}`);
}
});

useEffect(() => {
if (templates.length > 1) {
const emptyBotTemplate = find(templates, ['id', EmptyBotTemplateId]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import DefineConversation from './DefineConversation';

type CreationFlowProps = RouteComponentProps<{}>;

const CreationFlow: React.FC<CreationFlowProps> = () => {
const CreationFlow: React.FC<CreationFlowProps> = (props: CreationFlowProps) => {
const {
fetchTemplates,
fetchTemplatesV2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ import {
import { BotTemplate, QnABotTemplateId } from '@bfc/shared';
import { DialogWrapper, DialogTypes, LoadingSpinner } from '@bfc/ui-shared';
import { NeutralColors } from '@uifabric/fluent-theme';
import { RouteComponentProps } from '@reach/router';
import { navigate, RouteComponentProps } from '@reach/router';
import { IPivotItemProps, Pivot, PivotItem } from 'office-ui-fabric-react/lib/Pivot';
import { Link } from 'office-ui-fabric-react/lib/Link';
import { FontIcon } from 'office-ui-fabric-react/lib/Icon';
import { csharpFeedKey } from '@botframework-composer/types';
import { useRecoilState, useRecoilValue } from 'recoil';
import axios from 'axios';
import querystring from 'query-string';

import msftIcon from '../../../images/msftIcon.svg';
import { DialogCreationCopy, EmptyBotTemplateId, feedDictionary } from '../../../constants';
import { fetchReadMePendingState, selectedTemplateReadMeState } from '../../../recoilModel';
import { DialogCreationCopy, feedDictionary } from '../../../constants';
import { creationFlowTypeState, fetchReadMePendingState, selectedTemplateReadMeState } from '../../../recoilModel';
import TelemetryClient from '../../../telemetry/TelemetryClient';
import { getAliasFromPayload } from '../../../utils/electronUtil';

import { TemplateDetailView } from './TemplateDetailView';

Expand Down Expand Up @@ -118,20 +121,22 @@ const defaultTemplateId = '@microsoft/generator-microsoft-bot-empty';
type CreateOptionsProps = {
templates: BotTemplate[];
onDismiss: () => void;
onNext: (data: string) => void;
onNext: (templateName: string, urlData?: string) => void;
fetchTemplates: (feedUrls?: string[]) => Promise<void>;
fetchReadMe: (moduleName: string) => {};
} & RouteComponentProps<{}>;

export function CreateOptionsV2(props: CreateOptionsProps) {
const [option] = useState(optionKeys.createFromTemplate);
const [disabled] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const { templates, onDismiss, onNext } = props;
const [currentTemplateId, setCurrentTemplateId] = useState(defaultTemplateId);
const [emptyBotKey, setEmptyBotKey] = useState('');
const [selectedFeed, setSelectedFeed] = useState<{ props: IPivotItemProps }>({ props: { itemKey: csharpFeedKey } });
const [readMe] = useRecoilState(selectedTemplateReadMeState);
const fetchReadMePending = useRecoilValue(fetchReadMePendingState);
const creationFlowType = useRecoilValue(creationFlowTypeState);

const selectedTemplate = useMemo(() => {
return new Selection({
Expand All @@ -154,13 +159,13 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
routeToTemplate = QnABotTemplateId;
}

if (props.location && props.location.search) {
routeToTemplate += props.location.search;
}

TelemetryClient.track('CreateNewBotProjectNextButton', { template: routeToTemplate });

onNext(routeToTemplate);
if (props.location && props.location.search) {
onNext(routeToTemplate, props.location.search);
} else {
onNext(routeToTemplate);
}
};

const tableColumns = [
Expand Down Expand Up @@ -205,14 +210,39 @@ export function CreateOptionsV2(props: CreateOptionsProps) {

useEffect(() => {
if (templates.length > 1) {
const emptyBotTemplate = find(templates, ['id', EmptyBotTemplateId]);
const emptyBotTemplate = find(templates, ['id', defaultTemplateId]);
if (emptyBotTemplate) {
setCurrentTemplateId(emptyBotTemplate.id);
setEmptyBotKey(emptyBotTemplate.id);
}
}
}, [templates]);

useEffect(() => {
// open bot directly if alias exist.
if (props.location?.search) {
const decoded = decodeURIComponent(props.location.search);
const { source, payload } = querystring.parse(decoded);
if (typeof source === 'string' && typeof payload === 'string') {
const alias = getAliasFromPayload(source, payload);
// check to see if Composer currently has a bot project corresponding to the alias
axios
.get<any>(`/api/projects/alias/${alias}`)
.then((aliasRes) => {
if (aliasRes.status === 200) {
navigate(`/bot/${aliasRes.data.id}`);
return;
}
})
.catch((e) => {
setIsOpen(true);
});
return;
}
}
setIsOpen(true);
}, [props.location?.search]);

useEffect(() => {
if (selectedFeed?.props?.itemKey) {
props.fetchTemplates([feedDictionary[selectedFeed.props.itemKey]]);
Expand All @@ -225,14 +255,12 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
}
}, [currentTemplateId, props.fetchReadMe]);

const dialogWrapperProps =
creationFlowType === 'Skill' ? DialogCreationCopy.CREATE_NEW_SKILLBOT : DialogCreationCopy.CREATE_NEW_BOT_V2;

return (
<Fragment>
<DialogWrapper
isOpen
{...DialogCreationCopy.CREATE_NEW_BOT_V2}
dialogType={DialogTypes.CreateFlow}
onDismiss={onDismiss}
>
<DialogWrapper isOpen={isOpen} {...dialogWrapperProps} dialogType={DialogTypes.CreateFlow} onDismiss={onDismiss}>
<Pivot
defaultSelectedKey={csharpFeedKey}
onLinkClick={(item) => {
Expand Down Expand Up @@ -268,12 +296,7 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
</div>
</div>
<DialogFooter>
<Link
underline
href={templateRequestUrl}
styles={{ root: { fontSize: '12px', float: 'left' } }}
target="_blank"
>
<Link href={templateRequestUrl} styles={{ root: { fontSize: '12px', float: 'left' } }} target="_blank">
<FontIcon iconName="ChatInviteFriend" style={{ marginRight: '5px' }} />
{formatMessage('Need another template? Send us a request')}
</Link>
Expand Down
Loading

0 comments on commit 3733eb8

Please sign in to comment.