{accessToken && !accessToken.startsWith('invalid') ? (
Sign out
) : (
- Sign in with your Azure account.
+
+ Sign in with your Azure account.
+
)}
);
@@ -136,8 +145,34 @@ export class WelcomePage extends React.Component {
this.props.switchToBot(bot.path);
};
- private onOpenBotClick = () => {
- this.props.showOpenBotDialog();
+ private onOpenBotClick = async () => {
+ await this.props.showOpenBotDialog();
+
+ this.openBotButtonRef && this.openBotButtonRef.focus();
+ };
+
+ private onNewBotClick = async () => {
+ await this.props.onNewBotClick();
+
+ this.newBotButtonRef && this.newBotButtonRef.focus();
+ };
+
+ private signInToAzure = async () => {
+ await this.props.signInWithAzure();
+
+ this.signIntoAzureButtonRef && this.signIntoAzureButtonRef.focus();
+ };
+
+ private setNewBotButtonRef = (ref: HTMLButtonElement): void => {
+ this.newBotButtonRef = ref;
+ };
+
+ private setOpenBotButtonRef = (ref: HTMLButtonElement): void => {
+ this.openBotButtonRef = ref;
+ };
+
+ private setSignInToAzureButtonRef = (ref: HTMLButtonElement): void => {
+ this.signIntoAzureButtonRef = ref;
};
private onWorkingLocallyLinkClick = this.createAnchorClickHandler(
diff --git a/packages/app/client/src/ui/editor/welcomePage/welcomePageContainer.ts b/packages/app/client/src/ui/editor/welcomePage/welcomePageContainer.ts
index 6b6843878..206366881 100644
--- a/packages/app/client/src/ui/editor/welcomePage/welcomePageContainer.ts
+++ b/packages/app/client/src/ui/editor/welcomePage/welcomePageContainer.ts
@@ -54,9 +54,11 @@ function mapDispatchToProps(dispatch: (action: Action) => void): WelcomePageProp
onAnchorClick: (url: string) => {
dispatch(executeCommand(true, SharedConstants.Commands.Electron.OpenExternal, null, url));
},
- onNewBotClick: () => dispatch(executeCommand(false, Commands.UI.ShowBotCreationDialog)),
- showOpenBotDialog: () => dispatch(executeCommand(false, SharedConstants.Commands.UI.ShowOpenBotDialog)),
- signInWithAzure: () => dispatch(executeCommand(false, Commands.UI.SignInToAzure)),
+ onNewBotClick: () =>
+ new Promise(resolve => dispatch(executeCommand(false, Commands.UI.ShowBotCreationDialog, resolve))),
+ showOpenBotDialog: () =>
+ new Promise(resolve => dispatch(executeCommand(false, SharedConstants.Commands.UI.ShowOpenBotDialog, resolve))),
+ signInWithAzure: () => new Promise(resolve => dispatch(executeCommand(false, Commands.UI.SignInToAzure, resolve))),
signOutWithAzure: () => {
dispatch(executeCommand(true, Commands.Azure.SignUserOutOfAzure));
dispatch(executeCommand(false, Commands.UI.InvalidateAzureArmToken));
diff --git a/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBar.spec.tsx b/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBar.spec.tsx
new file mode 100644
index 000000000..35a4d9a6c
--- /dev/null
+++ b/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBar.spec.tsx
@@ -0,0 +1,96 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license.
+//
+// Microsoft Bot Framework: http://botframework.com
+//
+// Bot Framework Emulator Github:
+// https://github.com/Microsoft/BotFramwork-Emulator
+//
+// Copyright (c) Microsoft Corporation
+// All rights reserved.
+//
+// MIT License:
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+import { mount } from 'enzyme';
+import * as React from 'react';
+import { Provider } from 'react-redux';
+import { combineReducers, createStore } from 'redux';
+
+import { bot } from '../../../../state/reducers/bot';
+import { chat } from '../../../../state/reducers/chat';
+
+import BotExplorerBar from './botExplorerBar';
+import { BotExplorerBarContainer } from './botExplorerBarContainer';
+
+jest.mock('../../../dialogs', () => ({}));
+
+jest.mock('electron', () => ({
+ ipcMain: new Proxy(
+ {},
+ {
+ get(): any {
+ return () => ({});
+ },
+ has() {
+ return true;
+ },
+ }
+ ),
+ ipcRenderer: new Proxy(
+ {},
+ {
+ get(): any {
+ return () => ({});
+ },
+ has() {
+ return true;
+ },
+ }
+ ),
+}));
+
+const mockStore = createStore(combineReducers({ bot, chat }));
+
+describe('The BotExplorerBotContainer component', () => {
+ let parent;
+ let node;
+ beforeEach(() => {
+ parent = mount(
+
+
+
+ );
+ node = parent.find(BotExplorerBar);
+ });
+
+ it('should render deeply', () => {
+ expect(parent.find(BotExplorerBarContainer)).not.toBe(null);
+ expect(parent.find(BotExplorerBar)).not.toBe(null);
+ });
+
+ it('should set a button ref', () => {
+ const mockButtonRef: any = {};
+ node.instance().setOpenBotSettingsRef(mockButtonRef);
+
+ expect(node.instance().openBotSettingsButtonRef).toBe(mockButtonRef);
+ });
+});
diff --git a/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBar.tsx b/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBar.tsx
index 4ecde03ef..848051136 100644
--- a/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBar.tsx
+++ b/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBar.tsx
@@ -57,6 +57,8 @@ export default class BotExplorerBar extends React.Component;
@@ -68,7 +70,8 @@ export default class BotExplorerBar extends React.Component
@@ -79,4 +82,13 @@ export default class BotExplorerBar extends React.Component
);
}
+
+ private openBotSettingsClick = async () => {
+ await this.props.openBotSettings();
+ this.openBotSettingsButtonRef.focus();
+ };
+
+ private setOpenBotSettingsRef = (ref: HTMLButtonElement): void => {
+ this.openBotSettingsButtonRef = ref;
+ };
}
diff --git a/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBarContainer.ts b/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBarContainer.ts
index c41b51f4b..81949dbdf 100644
--- a/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBarContainer.ts
+++ b/packages/app/client/src/ui/shell/explorer/botExplorerBar/botExplorerBarContainer.ts
@@ -40,11 +40,7 @@ import BotExplorerBar from './botExplorerBar';
const mapStateToProps = (state: RootState) => {
return {
- openBotSettings: () => {
- DialogService.showDialog(BotSettingsEditorContainer, {
- bot: state.bot.activeBot,
- }).catch();
- },
+ openBotSettings: () => DialogService.showDialog(BotSettingsEditorContainer, { bot: state.bot.activeBot }).catch(),
};
};
export const BotExplorerBarContainer = connect(mapStateToProps)(BotExplorerBar);
diff --git a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.spec.tsx b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.spec.tsx
index 641aaa795..2a52eeaa3 100644
--- a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.spec.tsx
+++ b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.spec.tsx
@@ -35,26 +35,24 @@ import { newNotification, SharedConstants } from '@bfemulator/app-shared';
import { mount } from 'enzyme';
import * as React from 'react';
import { Provider } from 'react-redux';
-import { combineReducers, createStore } from 'redux';
+import { applyMiddleware, combineReducers, createStore } from 'redux';
+import sagaMiddlewareFactory from 'redux-saga';
+import { ActiveBotHelper } from '../../../helpers/activeBotHelper';
import { beginAdd } from '../../../../state/actions/notificationActions';
import { bot } from '../../../../state/reducers/bot';
import { chat } from '../../../../state/reducers/chat';
-import { ActiveBotHelper } from '../../../helpers/activeBotHelper';
-import { executeCommand } from '../../../../state/actions/commandActions';
+import { commandSagas } from '../../../../state/sagas/commandSagas';
+import {
+ executeCommand,
+ EXECUTE_COMMAND,
+ CommandAction,
+ CommandActionPayload,
+} from '../../../../state/actions/commandActions';
import { BotNotOpenExplorer } from './botNotOpenExplorer';
import { BotNotOpenExplorerContainer } from './botNotOpenExplorerContainer';
-const mockStore = createStore(combineReducers({ bot, chat }), {});
-
-jest.mock('../../../dialogs', () => ({
- DialogService: {
- showDialog: () => Promise.resolve(true),
- hideDialog: () => Promise.resolve(false),
- },
-}));
-
jest.mock('electron', () => ({
ipcMain: new Proxy(
{},
@@ -80,6 +78,10 @@ jest.mock('electron', () => ({
),
}));
+const sagaMiddleware = sagaMiddlewareFactory();
+const mockStore = createStore(combineReducers({ bot, chat }), {}, applyMiddleware(sagaMiddleware));
+sagaMiddleware.run(commandSagas);
+
jest.mock('../../../../state/store', () => ({
get store() {
return mockStore;
@@ -93,7 +95,19 @@ describe('The EndpointExplorer component should', () => {
let instance;
beforeEach(() => {
- mockDispatch = jest.spyOn(mockStore, 'dispatch');
+ mockDispatch = jest
+ .spyOn(mockStore, 'dispatch')
+ .mockImplementation((action: CommandAction) => {
+ if (
+ action.type === EXECUTE_COMMAND &&
+ action.payload.commandName === SharedConstants.Commands.UI.ShowBotCreationDialog
+ ) {
+ action.payload.resolver();
+ }
+
+ return action;
+ });
+
parent = mount(
@@ -104,10 +118,18 @@ describe('The EndpointExplorer component should', () => {
});
it('should make the appropriate calls when onCreateNewBotClick in called', async () => {
+ const mockCreateNewBotButton = {
+ focus: jest.fn(() => {
+ return null;
+ }),
+ };
+ instance.createNewBotButtonRef = mockCreateNewBotButton;
+
await instance.onCreateNewBotClick();
- expect(mockDispatch).toHaveBeenLastCalledWith(
- executeCommand(false, SharedConstants.Commands.UI.ShowBotCreationDialog)
+ expect(mockDispatch).toHaveBeenCalledWith(
+ executeCommand(false, SharedConstants.Commands.UI.ShowBotCreationDialog, jasmine.any(Function))
);
+ expect(mockCreateNewBotButton.focus).toHaveBeenCalledTimes(1);
});
it('should make the appropriate calls when onOpenBotFileClick in called', async () => {
diff --git a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.tsx b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.tsx
index 8651abbc0..0690a5446 100644
--- a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.tsx
+++ b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.tsx
@@ -43,6 +43,8 @@ export interface BotNotOpenExplorerProps {
}
export class BotNotOpenExplorer extends React.Component {
+ private createNewBotButtonRef: HTMLButtonElement;
+
public render() {
const label = 'Services Not Available';
return (
@@ -56,7 +58,11 @@ export class BotNotOpenExplorer extends React.Component
{` or `}
-
+
create a new bot configuration
.
@@ -69,10 +75,16 @@ export class BotNotOpenExplorer extends React.Component {
- this.props.showCreateNewBotDialog();
+ await this.props.showCreateNewBotDialog();
+
+ this.createNewBotButtonRef.focus();
};
private onOpenBotFileClick = async () => {
await this.props.openBotFile();
};
+
+ private setCreateNewBotButtonRef = (ref: HTMLButtonElement): void => {
+ this.createNewBotButtonRef = ref;
+ };
}
diff --git a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorerContainer.ts b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorerContainer.ts
index 1355fa9c8..4ff7abcdb 100644
--- a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorerContainer.ts
+++ b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorerContainer.ts
@@ -54,7 +54,10 @@ const mapDispatchToProps = (dispatch: (action: Action) => void): BotNotOpenExplo
dispatch(beginAdd(newNotification(`An Error occurred on the Bot Not Open Explorer: ${e}`)));
}
},
- showCreateNewBotDialog: () => dispatch(executeCommand(false, SharedConstants.Commands.UI.ShowBotCreationDialog)),
+ showCreateNewBotDialog: () =>
+ new Promise(resolve => {
+ dispatch(executeCommand(false, SharedConstants.Commands.UI.ShowBotCreationDialog, resolve));
+ }),
});
export const BotNotOpenExplorerContainer = connect(
diff --git a/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorer.spec.tsx b/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorer.spec.tsx
index d2f9b755c..25672dc56 100644
--- a/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorer.spec.tsx
+++ b/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorer.spec.tsx
@@ -30,6 +30,7 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+
import { mount } from 'enzyme';
import * as React from 'react';
import { Provider } from 'react-redux';
@@ -93,7 +94,7 @@ const mockBot = {
},
],
};
-describe('The EndpointExplorer component should', () => {
+describe('The EndpointExplorer component', () => {
let parent;
let node;
let mockDispatch;
@@ -140,4 +141,11 @@ describe('The EndpointExplorer component should', () => {
expect(mockDispatch).toHaveBeenCalledWith(openEndpointInEmulator(mockBot.services[0] as any, true));
});
+
+ it('should set a button ref', () => {
+ const mockButtonRef: any = {};
+ node.instance().setAddIconButtonRef(mockButtonRef);
+
+ expect(node.instance().addIconButtonRef).toBe(mockButtonRef);
+ });
});
diff --git a/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorer.tsx b/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorer.tsx
index 0de8bfe20..fa1d9bc1c 100644
--- a/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorer.tsx
+++ b/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorer.tsx
@@ -43,7 +43,7 @@ import * as styles from './endpointExplorer.scss';
export interface EndpointProps extends ServicePaneProps {
endpointServices?: IEndpointService[];
- launchEndpointEditor: (endpointEditor: ComponentClass) => void;
+ launchEndpointEditor: (endpointEditor: ComponentClass) => Promise;
openEndpointInEmulator: (endpointService: IEndpointService) => void;
}
@@ -109,11 +109,16 @@ export class EndpointExplorer extends ServicePane {
this.props.openContextMenuForService(new EndpointService(endpointService), EndpointEditorContainer);
}
- protected onAddIconClick = (_event: SyntheticEvent): void => {
- this.props.launchEndpointEditor(EndpointEditorContainer);
+ protected onAddIconClick = async (_event: SyntheticEvent): Promise => {
+ await this.props.launchEndpointEditor(EndpointEditorContainer);
+ this.addIconButtonRef && this.addIconButtonRef.focus();
};
protected onSortClick = (_event: SyntheticEvent): void => {
// TODO - Implement this.
};
+
+ protected setAddIconButtonRef = (ref: HTMLButtonElement): void => {
+ this.addIconButtonRef = ref;
+ };
}
diff --git a/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorerContainer.ts b/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorerContainer.ts
index dc96ca97c..a3d6059a9 100644
--- a/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorerContainer.ts
+++ b/packages/app/client/src/ui/shell/explorer/endpointExplorer/endpointExplorerContainer.ts
@@ -57,7 +57,9 @@ const mapStateToProps = (state: RootState, ...ownProps: any[]) => {
const mapDispatchToProps = dispatch => {
return {
launchEndpointEditor: (endpointEditor: ComponentClass, endpointService: IEndpointService) =>
- dispatch(launchEndpointEditor(endpointEditor, endpointService)),
+ new Promise(resolve => {
+ dispatch(launchEndpointEditor(endpointEditor, endpointService, resolve));
+ }),
openEndpointInEmulator: (endpointService: IEndpointService) =>
dispatch(openEndpointInEmulator(endpointService, true)),
openContextMenuForService: (endpointService: IEndpointService, endpointEditor: ComponentClass) =>
diff --git a/packages/app/client/src/ui/shell/explorer/servicePane/servicePane.tsx b/packages/app/client/src/ui/shell/explorer/servicePane/servicePane.tsx
index 501c51c30..b201a81f7 100644
--- a/packages/app/client/src/ui/shell/explorer/servicePane/servicePane.tsx
+++ b/packages/app/client/src/ui/shell/explorer/servicePane/servicePane.tsx
@@ -56,6 +56,8 @@ export abstract class ServicePane<
T extends ServicePaneProps,
S extends ServicePaneState = ServicePaneState
> extends Component {
+ protected addIconButtonRef: HTMLButtonElement;
+
protected abstract onLinkClick: (event: SyntheticEvent) => void; // bound
protected abstract onSortClick: (event: SyntheticEvent) => void; // bound
protected onAddIconClick: (event: SyntheticEvent) => void; // bound
@@ -88,6 +90,7 @@ export abstract class ServicePane<
onKeyPress={this.onControlKeyPress}
onClick={this.onAddIconClick}
className={`${styles.addIconButton} ${styles.serviceIcon}`}
+ ref={this.setAddIconButtonRef}
>
@@ -264,7 +264,7 @@ export class ConnectedServiceEditor extends Component
{`You can find your knowledge base ID and subscription key in the `}
-
+
Azure Portal.
@@ -277,7 +277,7 @@ export class ConnectedServiceEditor extends Component
{`You can find the information below in the `}
-
+
Azure Portal.
@@ -320,7 +320,7 @@ export class ConnectedServiceEditor extends Component
@@ -333,7 +333,7 @@ export class ConnectedServiceEditor extends Component
@@ -346,7 +346,7 @@ export class ConnectedServiceEditor extends Component
@@ -359,7 +359,7 @@ export class ConnectedServiceEditor extends Component
@@ -373,7 +373,7 @@ export class ConnectedServiceEditor extends Component
@@ -386,7 +386,7 @@ export class ConnectedServiceEditor extends Component
diff --git a/packages/app/client/src/ui/shell/explorer/servicesExplorer/connectedServiceEditor/kvPair.tsx b/packages/app/client/src/ui/shell/explorer/servicesExplorer/connectedServiceEditor/kvPair.tsx
index 7ae037fad..402eee720 100644
--- a/packages/app/client/src/ui/shell/explorer/servicesExplorer/connectedServiceEditor/kvPair.tsx
+++ b/packages/app/client/src/ui/shell/explorer/servicesExplorer/connectedServiceEditor/kvPair.tsx
@@ -31,7 +31,7 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-import { TextField } from '@bfemulator/ui-react';
+import { LinkButton, TextField } from '@bfemulator/ui-react';
import * as React from 'react';
import { ChangeEvent, Component, ReactNode } from 'react';
@@ -71,17 +71,21 @@ export class KvPair extends Component {