Skip to content

Commit

Permalink
Add / sort service menus now show under trigger buttons.
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyanziano committed Nov 19, 2019
1 parent b84c941 commit 09a21b2
Show file tree
Hide file tree
Showing 18 changed files with 317 additions and 62 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [1982](https://github.com/microsoft/BotFramework-Emulator/pull/1982)
- [1983](https://github.com/microsoft/BotFramework-Emulator/pull/1983)
- [1990](https://github.com/microsoft/BotFramework-Emulator/pull/1990)
- [1991](https://github.com/microsoft/BotFramework-Emulator/pull/1991)
- [1992](https://github.com/microsoft/BotFramework-Emulator/pull/1992)
- [1993](https://github.com/microsoft/BotFramework-Emulator/pull/1993)

## Removed
- [main] Removed unused `VersionManager` class in PR [1991](https://github.com/microsoft/BotFramework-Emulator/pull/1991)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,10 @@ describe('connected service actions', () => {

it('should create an openAddServiceContextMenu action', () => {
const payload: any = { resolver: jasmine.any(Function) };
const action = openAddServiceContextMenu(payload, jasmine.any(Function) as any);
const action = openAddServiceContextMenu(payload, jasmine.any(Function) as any, { x: 150, y: 300 });

expect(action.type).toBe(OPEN_ADD_CONNECTED_SERVICE_CONTEXT_MENU);
expect(action.payload).toEqual(payload);
expect(action.payload).toEqual({ ...payload, menuCoords: { x: 150, y: 300 } });
});

it('should create a launchExternalLink action', () => {
Expand All @@ -101,9 +101,9 @@ describe('connected service actions', () => {
});

it('should create an openSortContextMenu action', () => {
const action = openSortContextMenu();
const action = openSortContextMenu({ x: 150, y: 300 });

expect(action.type).toBe(OPEN_CONNECTED_SERVICE_SORT_CONTEXT_MENU);
expect(action.payload).toEqual({ panelId: CONNECTED_SERVICES_PANEL_ID });
expect(action.payload).toEqual({ panelId: CONNECTED_SERVICES_PANEL_ID, menuCoords: { x: 150, y: 300 } });
});
});
19 changes: 16 additions & 3 deletions packages/app/client/src/state/actions/connectedServiceActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import { IConnectedService, ServiceTypes } from 'botframework-config/lib/schema';
import { ComponentClass } from 'react';
import { Action } from 'redux';
import { ContextMenuCoordinates } from '@bfemulator/app-shared';

import { CONNECTED_SERVICES_PANEL_ID } from './explorerActions';

Expand Down Expand Up @@ -80,9 +81,14 @@ export interface ConnectedServicePickerPayload extends ConnectedServicePayload {
}

export interface OpenAddServiceContextMenuPayload extends ConnectedServicePickerPayload {
menuCoords?: ContextMenuCoordinates;
resolver: Function;
}

export interface OpenSortContextMenuPayload extends ConnectedServicePayload {
menuCoords?: ContextMenuCoordinates;
}

export function launchConnectedServicePicker(
payload: ConnectedServicePickerPayload
): ConnectedServiceAction<ConnectedServicePickerPayload> {
Expand Down Expand Up @@ -113,12 +119,14 @@ export function openContextMenuForConnectedService<T>(

export function openAddServiceContextMenu(
payload: ConnectedServicePickerPayload,
resolver: Function
resolver: Function,
menuCoords?: ContextMenuCoordinates
): ConnectedServiceAction<OpenAddServiceContextMenuPayload> {
return {
type: OPEN_ADD_CONNECTED_SERVICE_CONTEXT_MENU,
payload: {
...payload,
menuCoords,
resolver,
},
};
Expand All @@ -131,9 +139,14 @@ export function launchExternalLink(payload: ConnectedServicePayload): ConnectedS
};
}

export function openSortContextMenu(): ConnectedServiceAction<ConnectedServicePayload> {
export function openSortContextMenu(
menuCoords?: ContextMenuCoordinates
): ConnectedServiceAction<OpenSortContextMenuPayload> {
return {
type: OPEN_CONNECTED_SERVICE_SORT_CONTEXT_MENU,
payload: { panelId: CONNECTED_SERVICES_PANEL_ID },
payload: {
panelId: CONNECTED_SERVICES_PANEL_ID,
menuCoords,
},
};
}
47 changes: 44 additions & 3 deletions packages/app/client/src/state/sagas/servicesExplorerSagas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { ServiceCodes, SharedConstants } from '@bfemulator/app-shared';
import { ServiceTypes } from 'botframework-config/lib/schema';
import { applyMiddleware, combineReducers, createStore } from 'redux';
import sagaMiddlewareFactory from 'redux-saga';
import { call } from 'redux-saga/effects';
import { call, put } from 'redux-saga/effects';
import { CommandServiceInstance } from '@bfemulator/sdk-shared';
import { CommandServiceImpl } from '@bfemulator/sdk-shared';

Expand All @@ -58,11 +58,13 @@ import {
launchExternalLink,
openContextMenuForConnectedService,
openServiceDeepLink,
OpenAddServiceContextMenuPayload,
} from '../actions/connectedServiceActions';
import { azureAuth } from '../reducers/azureAuth';
import { bot } from '../reducers/bot';

import { ServicesExplorerSagas, servicesExplorerSagas } from './servicesExplorerSagas';
import { sortExplorerContents } from '../actions/explorerActions';

const sagaMiddleWare = sagaMiddlewareFactory();
const mockStore = createStore(combineReducers({ azureAuth, bot }), {}, applyMiddleware(sagaMiddleWare));
Expand Down Expand Up @@ -492,7 +494,7 @@ describe('The ServiceExplorerSagas', () => {
});

describe(' openAddConnectedServiceContextMenu', () => {
let action: ConnectedServiceAction<ConnectedServicePickerPayload>;
let action: ConnectedServiceAction<OpenAddServiceContextMenuPayload>;
let contextMenuGen;
beforeEach(() => {
const sagaIt = servicesExplorerSagas();
Expand All @@ -508,7 +510,7 @@ describe('The ServiceExplorerSagas', () => {
pickerComponent: ConnectedServicePickerContainer,
};

action = openAddServiceContextMenu(payload);
action = openAddServiceContextMenu(payload, undefined);
let i = 6;
while (i--) {
contextMenuGen = sagaIt.next().value.FORK.args[1];
Expand Down Expand Up @@ -645,4 +647,43 @@ describe('The ServiceExplorerSagas', () => {
expect(window.open).toHaveBeenCalledWith('https://www.qnamaker.ai/Edit/KnowledgeBase?kbId=45432');
});
});

it('should open the sort context menu', () => {
const action: any = {
payload: {
panelId: 'servicesPanel',
menuCoords: {
x: 150,
y: 200,
},
},
};
const gen = ServicesExplorerSagas.openSortContextMenu(action);
gen.next();
// inject result of select() and check next yield
expect(gen.next({ servicesPanel: 'name' }).value).toEqual(
call(
[commandService, commandService.remoteCall],
SharedConstants.Commands.Electron.DisplayContextMenu,
[
{
label: 'Sort by name',
id: 'name',
type: 'checkbox',
checked: true,
},
{
label: 'Sort by type',
id: 'type',
type: 'checkbox',
checked: false,
},
],
{ x: 150, y: 200 }
)
);
// inject result of call() and check next yield
expect(gen.next({ id: 'someId' }).value).toEqual(put(sortExplorerContents(action.payload.panelId, 'someId')));
expect(gen.next().done).toBe(true);
});
});
17 changes: 12 additions & 5 deletions packages/app/client/src/state/sagas/servicesExplorerSagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
OPEN_CONTEXT_MENU_FOR_CONNECTED_SERVICE,
OPEN_SERVICE_DEEP_LINK,
OpenAddServiceContextMenuPayload,
OpenSortContextMenuPayload,
} from '../actions/connectedServiceActions';
import { sortExplorerContents } from '../actions/explorerActions';
import { SortCriteria } from '../reducers/explorer';
Expand Down Expand Up @@ -304,7 +305,7 @@ export class ServicesExplorerSagas {
public static *openAddConnectedServiceContextMenu(
action: ConnectedServiceAction<OpenAddServiceContextMenuPayload>
): IterableIterator<any> {
const { resolver } = action.payload;
const { menuCoords, resolver } = action.payload;
const menuItems = [
{ label: 'Add Language Understanding (LUIS)', id: ServiceTypes.Luis },
{ label: 'Add QnA Maker', id: ServiceTypes.QnA },
Expand All @@ -319,7 +320,8 @@ export class ServicesExplorerSagas {

const response = yield ServicesExplorerSagas.commandService.remoteCall(
SharedConstants.Commands.Electron.DisplayContextMenu,
menuItems
menuItems,
menuCoords
);

const { id: serviceType } = response;
Expand All @@ -333,7 +335,10 @@ export class ServicesExplorerSagas {
resolver && resolver();
}

public static *openSortContextMenu(action: ConnectedServiceAction<ConnectedServicePayload>): IterableIterator<any> {
public static *openSortContextMenu(
action: ConnectedServiceAction<OpenSortContextMenuPayload>
): IterableIterator<any> {
const { menuCoords } = action.payload;
const sortSelectionByPanelId = yield select(getSortSelection);
const currentSort = sortSelectionByPanelId[action.payload.panelId];
const menuItems = [
Expand All @@ -350,9 +355,11 @@ export class ServicesExplorerSagas {
checked: currentSort === 'type',
},
];
const response = yield ServicesExplorerSagas.commandService.remoteCall(
const response = yield call(
[ServicesExplorerSagas.commandService, ServicesExplorerSagas.commandService.remoteCall],
SharedConstants.Commands.Electron.DisplayContextMenu,
menuItems
menuItems,
menuCoords
);
yield response.id ? put(sortExplorerContents(action.payload.panelId, response.id)) : null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// 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, ReactWrapper } from 'enzyme';
import * as React from 'react';

import { ServicePane, ServicePaneProps, ServicePaneState } from './servicePane';

// TODO: Test the rest of the component
describe('<ServicePane />', () => {
let wrapper: ReactWrapper<ServicePaneProps, ServicePaneState, ServicePane<ServicePaneProps, ServicePaneState>>;
let instance: ServicePane<ServicePaneProps, ServicePaneState>;

beforeEach(() => {
wrapper = mount(<ServicePane openContextMenuForService={undefined} window={undefined} />);
instance = wrapper.instance();
});

it('should set the sort icon button ref', () => {
const mockSortButtonRef = {};
(instance as any).setSortIconButtonRef(mockSortButtonRef);

expect((instance as any).sortIconButtonRef).toEqual(mockSortButtonRef);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export abstract class ServicePane<
S extends ServicePaneState = ServicePaneState
> extends Component<T, S> {
protected addIconButtonRef: HTMLButtonElement;
protected sortIconButtonRef: HTMLButtonElement;

protected abstract onLinkClick: (event: SyntheticEvent<HTMLLIElement>) => void; // bound
protected abstract onSortClick: (event: SyntheticEvent<HTMLButtonElement>) => void; // bound
Expand All @@ -77,6 +78,7 @@ export abstract class ServicePane<
onKeyPress={this.onControlKeyPress}
onClick={this.onSortClick}
className={`${styles.sortIconButton} ${styles.serviceIcon}`}
ref={this.setSortIconButtonRef}
>
<svg viewBox="0 0 34.761 26.892" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<g>
Expand Down Expand Up @@ -192,4 +194,8 @@ export abstract class ServicePane<
protected setAddIconButtonRef = (ref: HTMLButtonElement): void => {
this.addIconButtonRef = ref;
};

protected setSortIconButtonRef = (ref: HTMLButtonElement): void => {
this.sortIconButtonRef = ref;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,17 @@ describe('The ServicesExplorer component', () => {
});

it('should dispatch a request to open the connected service picker when the add icon is clicked', async () => {
const mockOnAddIconClick = {
const mockAddIconButtonRef = {
focus: jest.fn(() => {
return null;
}),
getBoundingClientRect: jest.fn(() => ({
left: 150,
bottom: 200,
})),
};

instance.addIconButtonRef = mockOnAddIconClick;
instance.addIconButtonRef = mockAddIconButtonRef;

await instance.onAddIconClick();

Expand All @@ -191,17 +195,26 @@ describe('The ServicesExplorer component', () => {
pickerComponent: ConnectedServicePickerContainer,
progressIndicatorComponent: ProgressIndicatorContainer,
},
jasmine.any(Function) as any
jasmine.any(Function) as any,
{ x: 150, y: 200 }
)
);

expect(mockOnAddIconClick.focus).toHaveBeenCalled();
expect(mockAddIconButtonRef.focus).toHaveBeenCalled();
});

it('should dispatch to the store when a request to open the sort context menu is made', () => {
const instance = node.instance();
const mockSortIconButtonRef = {
getBoundingClientRect: jest.fn(() => ({
left: 150,
bottom: 200,
})),
};

instance.sortIconButtonRef = mockSortIconButtonRef;
instance.onSortClick();
expect(mockDispatch).toHaveBeenCalledWith(openSortContextMenu());
expect(mockDispatch).toHaveBeenCalledWith(openSortContextMenu({ x: 150, y: 200 }));
});

it('should open the service deep link when the enter key is pressed on a focused list item', () => {
Expand Down
Loading

0 comments on commit 09a21b2

Please sign in to comment.