diff --git a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.scss b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.scss index 86307bbeb..7e77e9317 100644 --- a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.scss +++ b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.scss @@ -34,8 +34,8 @@ .browse-button { overflow: hidden; position: absolute; - right: 0; - bottom: 12px; + right: 4px; + bottom: 11.5px; } .input-container { diff --git a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.spec.tsx b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.spec.tsx index 29a3b66a5..36173623b 100644 --- a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.spec.tsx +++ b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.spec.tsx @@ -31,13 +31,12 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -import { newNotification, SharedConstants } from '@bfemulator/app-shared'; +import { 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 * as BotActions from '../../../data/action/botActions'; -import { beginAdd } from '../../../data/action/notificationActions'; import { bot } from '../../../data/reducer/bot'; import { CommandServiceImpl } from '../../../platform/commands/commandServiceImpl'; import { ActiveBotHelper } from '../../helpers/activeBotHelper'; @@ -72,13 +71,11 @@ const bots = [ ]; describe('The OpenBotDialog', () => { - let mockDispatch; let node; let parent; let instance; beforeEach(() => { mockStore.dispatch(BotActions.load(bots)); - mockDispatch = jest.spyOn(mockStore, 'dispatch'); parent = mount( ); @@ -92,47 +89,6 @@ describe('The OpenBotDialog', () => { expect(spy).toHaveBeenCalled(); }); - it('should orchestrate the appropriate sequences when a recent bot is clicked', async () => { - const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call').mockResolvedValue(true); - const dialogSpy = jest.spyOn(DialogService, 'hideDialog'); - await instance.onBotSelected(bots[0]); - const { Switch } = SharedConstants.Commands.Bot; - expect(commandServiceSpy).toHaveBeenCalledWith(Switch, '/some/path'); - expect(dialogSpy).toHaveBeenCalled(); - }); - - it('should send a notification when the bot fails to open', async () => { - await instance.onBotSelected(null); - const message = `An Error occurred on the Open Bot Dialog: TypeError: Cannot read property 'path' of null`; - const notification = newNotification(message); - const action = beginAdd(notification); - notification.timestamp = jasmine.any(Number) as any; - notification.id = jasmine.any(String) as any; - expect(mockDispatch).toHaveBeenLastCalledWith(action); - }); - - it('should make the appropriate calls when onCreateNewBotClick in called', async () => { - const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call').mockResolvedValue(true); - const dialogSpy = jest.spyOn(DialogService, 'hideDialog'); - - await instance.onCreateNewBotClick(); - expect(commandServiceSpy).toHaveBeenLastCalledWith(SharedConstants.Commands.UI.ShowBotCreationDialog); - expect(dialogSpy).toHaveBeenCalled(); - }); - - it('should send a notification when onCreateNewBotClick fails', async () => { - const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call').mockRejectedValue('oh noes!'); - await instance.onCreateNewBotClick(); - const message = `An Error occurred on the Open Bot Dialog: oh noes!`; - const notification = newNotification(message); - const action = beginAdd(notification); - notification.timestamp = jasmine.any(Number) as any; - notification.id = jasmine.any(String) as any; - expect(mockDispatch).toHaveBeenLastCalledWith(action); - - expect(commandServiceSpy).toHaveBeenLastCalledWith(SharedConstants.Commands.UI.ShowBotCreationDialog); - }); - it('should properly set the state when the input changes', () => { instance.onInputChange({ target: { @@ -174,7 +130,7 @@ describe('The OpenBotDialog', () => { } as any); const botHelperSpy = jest.spyOn(ActiveBotHelper, 'confirmAndOpenBotFromFile').mockResolvedValue(true); - await instance.onOpenClick(); + await instance.onSubmit(); expect(botHelperSpy).toHaveBeenCalledWith('some/path/to/myBot.bot'); }); @@ -188,7 +144,7 @@ describe('The OpenBotDialog', () => { } as any); const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call'); - await instance.onOpenClick(); + await instance.onSubmit(); expect(commandServiceSpy).toHaveBeenCalledWith(SharedConstants.Commands.Emulator.NewLiveChat, { endpoint: 'http://localhost:6500/api/messages' }); diff --git a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx index e4a7ff11e..8e7044ab8 100644 --- a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx +++ b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialog.tsx @@ -31,16 +31,15 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -import { BotInfo } from '@bfemulator/app-shared'; +// import { BotInfo } from '@bfemulator/app-shared'; import { DefaultButton, Dialog, DialogFooter, PrimaryButton, TextField } from '@bfemulator/ui-react'; import * as React from 'react'; import { ChangeEvent, FocusEvent, ReactNode } from 'react'; -import { RecentBotsListContainer } from '../../editor/recentBotsList/recentBotsListContainer'; +// import { RecentBotsListContainer } from '../../editor/recentBotsList/recentBotsListContainer'; import * as styles from '../dialogStyles.scss'; import * as openBotStyles from './openBotDialog.scss'; export interface OpenBotDialogProps { - showCreateNewBotDialog?: () => Promise; onDialogCancel?: () => void; openBot?: (urlOrPath: string) => Promise; sendNotification?: (error: Error) => void; @@ -71,64 +70,43 @@ export class OpenBotDialog extends React.Component - - { `You can open a bot using just a URL, choose from a list of recently opened bots or ` } - create a new bot configuration - - + title="Open a bot"> + + - - Browse - - + + Browse + + - - - - - - + + + + + + ); } - private onBotSelected = async (bot: BotInfo) => { - try { - await this.props.switchToBot(bot.path); - this.props.onDialogCancel(); - } catch (e) { - this.props.sendNotification(e); - } - } - - private onCreateNewBotClick = async () => { - try { - await this.props.showCreateNewBotDialog(); - this.props.onDialogCancel(); - } catch (e) { - this.props.sendNotification(e); - } - } - private onFocus = (event: FocusEvent) => { const input = event.target as HTMLInputElement; input.setSelectionRange(0, (input.value || '').length); @@ -140,7 +118,7 @@ export class OpenBotDialog extends React.Component { + private onSubmit = async () => { try { await this.props.openBot(this.state.botUrl); } catch (e) { diff --git a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialogContainer.ts b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialogContainer.ts index 262dd899d..44d890ccd 100644 --- a/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialogContainer.ts +++ b/packages/app/client/src/ui/dialogs/openBotDialog/openBotDialogContainer.ts @@ -32,25 +32,17 @@ // import { newNotification, SharedConstants } from '@bfemulator/app-shared'; -import { Action } from 'redux'; import { IEndpointService } from 'botframework-config/lib/schema'; import { connect } from 'react-redux'; +import { Action } from 'redux'; import { beginAdd } from '../../../data/action/notificationActions'; -import { RootState } from '../../../data/store'; import { CommandServiceImpl } from '../../../platform/commands/commandServiceImpl'; import { ActiveBotHelper } from '../../helpers/activeBotHelper'; import { DialogService } from '../service'; import { OpenBotDialog, OpenBotDialogProps } from './openBotDialog'; -const mapStateToProps = (state: RootState, ownProps: { [propName: string]: any }): OpenBotDialogProps => { - return { - showCreateNewBotDialog: () => CommandServiceImpl.call(SharedConstants.Commands.UI.ShowBotCreationDialog), - ...ownProps - }; -}; - const mapDispatchToProps = (dispatch: (action: Action) => void): OpenBotDialogProps => { - const { Commands: { Emulator: { NewLiveChat }, Bot: { Switch } } } = SharedConstants; + const { Commands: { Emulator: { NewLiveChat } } } = SharedConstants; return { onDialogCancel: () => DialogService.hideDialog(), openBot: (urlOrPath: string) => { @@ -62,11 +54,10 @@ const mapDispatchToProps = (dispatch: (action: Action) => void): OpenBotDialogPr }, sendNotification: (error: Error) => dispatch(beginAdd(newNotification(`An Error occurred on the Open Bot Dialog: ${ error }`))), - switchToBot: (path: string) => CommandServiceImpl.call(Switch, path) }; }; export const OpenBotDialogContainer = connect( - mapStateToProps, + null, mapDispatchToProps )(OpenBotDialog); diff --git a/packages/app/client/src/ui/helpers/activeBotHelper.ts b/packages/app/client/src/ui/helpers/activeBotHelper.ts index 3026299c6..3992eed1d 100644 --- a/packages/app/client/src/ui/helpers/activeBotHelper.ts +++ b/packages/app/client/src/ui/helpers/activeBotHelper.ts @@ -97,7 +97,7 @@ export const ActiveBotHelper = new class { CommandServiceImpl.remoteCall(SharedConstants.Commands.Electron.SetTitleBar, getBotDisplayName(bot)) ]); } catch (e) { - const errMsg = `Error while setting active bot: ${e}`; + const errMsg = `Error while setting active bot: ${ e }`; const notification = newNotification(errMsg); store.dispatch(beginAdd(notification)); throw new Error(errMsg); @@ -112,7 +112,7 @@ export const ActiveBotHelper = new class { CommandServiceImpl.remoteCall(SharedConstants.Commands.Electron.SetTitleBar, ''); }) .catch(err => { - const errMsg = `Error while closing active bot: ${err}`; + const errMsg = `Error while closing active bot: ${ err }`; const notification = newNotification(errMsg); store.dispatch(beginAdd(notification)); throw new Error(errMsg); @@ -159,7 +159,7 @@ export const ActiveBotHelper = new class { store.dispatch(NavBarActions.select(Constants.NAVBAR_BOT_EXPLORER)); store.dispatch(ExplorerActions.showExplorer(true)); } catch (err) { - const errMsg = `Error during bot create: ${err}`; + const errMsg = `Error during bot create: ${ err }`; const notification = newNotification(errMsg); store.dispatch(beginAdd(notification)); throw new Error(errMsg); @@ -194,34 +194,21 @@ export const ActiveBotHelper = new class { await CommandServiceImpl.call(SharedConstants.Commands.Bot.Switch, activeBot); return; } + const result = this.confirmSwitchBot(); - try { - const result = this.confirmSwitchBot(); - - if (result) { - try { - store.dispatch(EditorActions.closeNonGlobalTabs()); - const bot = await CommandServiceImpl.remoteCall(SharedConstants.Commands.Bot.Open, filename) - // Secret prompt dialog was closed - .catch(_err => null); - if (!bot) { - return; - } - await CommandServiceImpl.remoteCall(SharedConstants.Commands.Bot.SetActive, bot); - await CommandServiceImpl.call(SharedConstants.Commands.Bot.Load, bot); - } catch (err) { - console.error('Error while trying to open bot from file: ', err); - throw new Error(`[confirmAndOpenBotFromFile] Error while trying to open bot from file: ${err}`); - } + if (result) { + store.dispatch(EditorActions.closeNonGlobalTabs()); + const bot = await CommandServiceImpl.remoteCall(SharedConstants.Commands.Bot.Open, filename); + if (!bot) { + return; } - } catch (err) { - console.error('Error while calling confirmSwitchBot: ', err); - throw new Error(`[confirmAndOpenBotFromFile] Error while calling confirmSwitchBot: ${err}`); + await CommandServiceImpl.remoteCall(SharedConstants.Commands.Bot.SetActive, bot); + await CommandServiceImpl.call(SharedConstants.Commands.Bot.Load, bot); } } } catch (err) { console.error('Error while calling browseForBotFile: ', err); - throw new Error(`[confirmAndOpenBotFromFile] Error while calling browseForBotFile: ${err}`); + throw new Error(`[confirmAndOpenBotFromFile] Error while calling browseForBotFile: ${ err }`); } } @@ -241,13 +228,13 @@ export const ActiveBotHelper = new class { await CommandServiceImpl.call( SharedConstants.Commands.Emulator.NewLiveChat, currentActiveBot.services[0]); } catch (e) { - throw new Error(`[confirmAndSwitchBots] Error while trying to open bot at ${botPath}: ${e}`); + throw new Error(`[confirmAndSwitchBots] Error while trying to open bot at ${ botPath }: ${ e }`); } return; } // TODO: We need to think about merging this with confirmAndCreateBot - console.log(`Switching to bot ${botPath}`); + console.log(`Switching to bot ${ botPath }`); try { // prompt the user to confirm the switch @@ -261,7 +248,7 @@ export const ActiveBotHelper = new class { try { newActiveBot = await CommandServiceImpl.remoteCall(SharedConstants.Commands.Bot.Open, bot); } catch (e) { - throw new Error(`[confirmAndSwitchBots] Error while trying to open bot at ${botPath}: ${e}`); + throw new Error(`[confirmAndSwitchBots] Error while trying to open bot at ${ botPath }: ${ e }`); } } else { newActiveBot = bot; @@ -300,7 +287,7 @@ export const ActiveBotHelper = new class { store.dispatch(ExplorerActions.showExplorer(true)); } } catch (e) { - const errMsg = `Error while trying to switch to bot: ${botPath}`; + const errMsg = `Error while trying to switch to bot: ${ botPath }`; const notification = newNotification(errMsg); store.dispatch(beginAdd(notification)); throw new Error(errMsg); @@ -324,7 +311,7 @@ export const ActiveBotHelper = new class { } }) .catch(err => { - const errMsg = `Error while closing active bot: ${err}`; + const errMsg = `Error while closing active bot: ${ err }`; const notification = newNotification(errMsg); store.dispatch(beginAdd(notification)); throw new Error(errMsg); diff --git a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.scss b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.scss index 6c5cae405..e1e62dcfa 100644 --- a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.scss +++ b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.scss @@ -40,20 +40,17 @@ list-style-type: none; margin: 0; padding: 0; +} - & .explorer-empty-state { - padding: 16px; - overflow: hidden; - } - - & .open-bot { - margin-top: 13px; - margin-bottom: 26px; - width: 180px; - height: 26px; - } +.explorer-empty-state { + padding: 16px; + margin-top: 13px; + overflow: hidden; +} - & .empty-state-text { - margin-top: 13px; - } +.open-bot { + margin-top: 13px; + margin-bottom: 26px; + width: 180px; + height: 26px; } diff --git a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.scss.d.ts b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.scss.d.ts index 179d6c738..6f08db8ce 100644 --- a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.scss.d.ts +++ b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.scss.d.ts @@ -2,4 +2,3 @@ export const botNotOpenExplorer: string; export const explorerEmptyState: string; export const openBot: string; -export const emptyStateText: string; 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 148ea50d9..978775b0e 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 @@ -40,6 +40,7 @@ import { beginAdd } from '../../../../data/action/notificationActions'; import { bot } from '../../../../data/reducer/bot'; import { chat } from '../../../../data/reducer/chat'; import { CommandServiceImpl } from '../../../../platform/commands/commandServiceImpl'; +import { ActiveBotHelper } from '../../../helpers/activeBotHelper'; import { BotNotOpenExplorer } from './botNotOpenExplorer'; import { BotNotOpenExplorerContainer } from './botNotOpenExplorerContainer'; @@ -63,27 +64,44 @@ describe('The EndpointExplorer component should', () => { let parent; let node; let mockDispatch; - + let instance; beforeEach(() => { mockDispatch = jest.spyOn(mockStore, 'dispatch'); parent = mount( ); node = parent.find(BotNotOpenExplorer); + instance = node.instance(); }); - it('should show the OpenBotDialog when onOpenBotClick is called', async () => { - const spy = jest.spyOn(CommandServiceImpl, 'call').mockResolvedValue(true); - const instance = node.instance(); - await instance.onOpenBotClick(); - - expect(spy).toHaveBeenCalledWith(SharedConstants.Commands.UI.ShowOpenBotDialog); + it('should make the appropriate calls when onCreateNewBotClick in called', async () => { + const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call').mockResolvedValue(true); + await instance.onCreateNewBotClick(); + expect(commandServiceSpy).toHaveBeenLastCalledWith(SharedConstants.Commands.UI.ShowBotCreationDialog); }); - it('should send a notification if the call to onOpenBotClick fails', async () => { + it('should send a notification when onCreateNewBotClick fails', async () => { const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'call').mockRejectedValue('oh noes!'); - const instance = node.instance(); - await instance.onOpenBotClick(); + await instance.onCreateNewBotClick(); + const message = `An Error occurred on the Bot Not Open Explorer: oh noes!`; + const notification = newNotification(message); + const action = beginAdd(notification); + notification.timestamp = jasmine.any(Number) as any; + notification.id = jasmine.any(String) as any; + expect(mockDispatch).toHaveBeenLastCalledWith(action); + + expect(commandServiceSpy).toHaveBeenLastCalledWith(SharedConstants.Commands.UI.ShowBotCreationDialog); + }); + + it('should make the appropriate calls when onOpenBotFileClick in called', async () => { + const spy = jest.spyOn(ActiveBotHelper, 'confirmAndOpenBotFromFile').mockResolvedValue(true); + await instance.onOpenBotFileClick(); + expect(spy).toHaveBeenCalled(); + }); + + it('should send a notification when onOpenBotFileClick fails', async () => { + const spy = jest.spyOn(ActiveBotHelper, 'confirmAndOpenBotFromFile').mockRejectedValue('oh noes!'); + await instance.onOpenBotFileClick(); const message = `An Error occurred on the Bot Not Open Explorer: oh noes!`; const notification = newNotification(message); const action = beginAdd(notification); @@ -91,6 +109,6 @@ describe('The EndpointExplorer component should', () => { notification.id = jasmine.any(String) as any; expect(mockDispatch).toHaveBeenLastCalledWith(action); - expect(commandServiceSpy).toHaveBeenLastCalledWith(SharedConstants.Commands.UI.ShowOpenBotDialog); + expect(spy).toHaveBeenCalled(); }); }); 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 28476cc90..4a448f6f3 100644 --- a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.tsx +++ b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorer.tsx @@ -31,22 +31,21 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -import { ExpandCollapse, ExpandCollapseContent, PrimaryButton } from '@bfemulator/ui-react'; +import { ExpandCollapse, ExpandCollapseContent } from '@bfemulator/ui-react'; import * as React from 'react'; import * as styles from './botNotOpenExplorer.scss'; export interface BotNotOpenExplorerProps { hasChat?: boolean; - showOpenBotDialog: () => Promise; + openBotFile?: () => Promise; + showCreateNewBotDialog?: () => Promise; sendNotification: (error: Error) => void; } export class BotNotOpenExplorer extends React.Component { public render() { - const { hasChat } = this.props; - const label = hasChat ? 'Services Not Available' : 'No Bot Opened'; - const message = hasChat ? 'You are in Direct Connect mode.' : 'You have not yet opened a bot.'; + const label = 'Services Not Available'; return ( @@ -57,8 +56,10 @@ export class BotNotOpenExplorer extends React.Component - { message } - + { `To connect the Emulator services, ` } + open a .bot file + { ` or ` } + create a new bot configuration. @@ -67,9 +68,17 @@ export class BotNotOpenExplorer extends React.Component { + private onCreateNewBotClick = async () => { try { - await this.props.showOpenBotDialog(); + await this.props.showCreateNewBotDialog(); + } catch (e) { + this.props.sendNotification(e); + } + } + + private onOpenBotFileClick = async () => { + try { + await this.props.openBotFile(); } catch (e) { this.props.sendNotification(e); } 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 40cab12cc..3db5bc1b1 100644 --- a/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorerContainer.ts +++ b/packages/app/client/src/ui/shell/explorer/botNotOpenExplorer/botNotOpenExplorerContainer.ts @@ -37,13 +37,15 @@ import { Action } from 'redux'; import { beginAdd } from '../../../../data/action/notificationActions'; import { RootState } from '../../../../data/store'; import { CommandServiceImpl } from '../../../../platform/commands/commandServiceImpl'; +import { ActiveBotHelper } from '../../../helpers/activeBotHelper'; import { BotNotOpenExplorer as BotNotOpenExplorerComp, BotNotOpenExplorerProps } from './botNotOpenExplorer'; const mapStateToProps = (state: RootState): any => ({ - hasChat: !!Object.keys(state.chat.chats).length + hasChat: !!Object.keys(state.chat.chats).length, + showCreateNewBotDialog: () => CommandServiceImpl.call(SharedConstants.Commands.UI.ShowBotCreationDialog), }); const mapDispatchToProps = (dispatch: (action: Action) => void): BotNotOpenExplorerProps => ({ - showOpenBotDialog: (): Promise => CommandServiceImpl.call(SharedConstants.Commands.UI.ShowOpenBotDialog), + openBotFile: () => ActiveBotHelper.confirmAndOpenBotFromFile(), sendNotification: (error: Error) => dispatch(beginAdd(newNotification(`An Error occurred on the Bot Not Open Explorer: ${ error }`))), });
- { `You can open a bot using just a URL, choose from a list of recently opened bots or ` } - create a new bot configuration -