From 0158e1a70825b3270e1a461f7ea9157f71aecf42 Mon Sep 17 00:00:00 2001 From: Chris Beer <cabeer@stanford.edu> Date: Thu, 14 Mar 2019 14:38:44 -0700 Subject: [PATCH] Pan the elastic workspace to the focused window when selected in the window list --- __tests__/src/actions/window.test.js | 49 +++++++++++++++++++++ __tests__/src/components/WindowList.test.js | 2 +- __tests__/src/reducers/workspace.test.js | 11 +++++ src/components/WindowList.js | 2 +- src/state/actions/window.js | 13 +++++- src/state/reducers/workspace.js | 9 +++- 6 files changed, 81 insertions(+), 5 deletions(-) diff --git a/__tests__/src/actions/window.test.js b/__tests__/src/actions/window.test.js index 89889961e..0eeb46dbe 100644 --- a/__tests__/src/actions/window.test.js +++ b/__tests__/src/actions/window.test.js @@ -2,6 +2,55 @@ import * as actions from '../../../src/state/actions'; import ActionTypes from '../../../src/state/actions/action-types'; describe('window actions', () => { + describe('focusWindow', () => { + it('should return correct action object with pan=true', () => { + const expectedAction = { + type: ActionTypes.FOCUS_WINDOW, + windowId: 'window', + position: { x: -150, y: -188 }, + }; + + const mockState = { + windows: { + window: { x: 50, y: 12 }, + }, + companionWindows: {}, + }; + + const mockDispatch = jest.fn(() => ({})); + const mockGetState = jest.fn(() => mockState); + const thunk = actions.focusWindow('window', true); + + thunk(mockDispatch, mockGetState); + + const action = mockDispatch.mock.calls[0][0]; + expect(action).toEqual(expectedAction); + }); + it('should return correct action object with pan=false', () => { + const expectedAction = { + type: ActionTypes.FOCUS_WINDOW, + windowId: 'window', + position: {}, + }; + + const mockState = { + windows: { + window: { x: 50, y: 12 }, + }, + companionWindows: {}, + }; + + const mockDispatch = jest.fn(() => ({})); + const mockGetState = jest.fn(() => mockState); + const thunk = actions.focusWindow('window'); + + thunk(mockDispatch, mockGetState); + + const action = mockDispatch.mock.calls[0][0]; + expect(action).toEqual(expectedAction); + }); + }); + describe('addWindow', () => { it('should create a new window with merged defaults', () => { const options = { diff --git a/__tests__/src/components/WindowList.test.js b/__tests__/src/components/WindowList.test.js index ab552b1f3..f41fe0e60 100644 --- a/__tests__/src/components/WindowList.test.js +++ b/__tests__/src/components/WindowList.test.js @@ -56,7 +56,7 @@ describe('WindowList', () => { ).toBe(true); wrapper.find('WithStyles(MenuItem)').simulate('click', {}); expect(handleClose).toBeCalled(); - expect(focusWindow).toBeCalledWith('xyz'); + expect(focusWindow).toBeCalledWith('xyz', true); }); }); diff --git a/__tests__/src/reducers/workspace.test.js b/__tests__/src/reducers/workspace.test.js index 9873e434d..e4d40d594 100644 --- a/__tests__/src/reducers/workspace.test.js +++ b/__tests__/src/reducers/workspace.test.js @@ -2,12 +2,23 @@ import { workspaceReducer } from '../../../src/state/reducers/workspace'; import ActionTypes from '../../../src/state/actions/action-types'; describe('workspace reducer', () => { + it('should handle FOCUS_WINDOW without position coordinates', () => { + expect(workspaceReducer([], { + type: ActionTypes.FOCUS_WINDOW, + windowId: 'abc123', + })).toEqual({ + focusedWindowId: 'abc123', + viewportPosition: {}, + }); + }); it('should handle FOCUS_WINDOW', () => { expect(workspaceReducer([], { type: ActionTypes.FOCUS_WINDOW, windowId: 'abc123', + position: { x: 10, y: 50 }, })).toEqual({ focusedWindowId: 'abc123', + viewportPosition: { x: 10, y: 50 }, }); }); it('should handle SET_WORKSPACE_FULLSCREEN', () => { diff --git a/src/components/WindowList.js b/src/components/WindowList.js index d82c67f27..193e3e359 100644 --- a/src/components/WindowList.js +++ b/src/components/WindowList.js @@ -48,7 +48,7 @@ export class WindowList extends Component { <MenuItem key={window.id} selected={i === 0} - onClick={(e) => { focusWindow(window.id); handleClose(e); }} + onClick={(e) => { focusWindow(window.id, true); handleClose(e); }} > <Typography variant="body1"> { diff --git a/src/state/actions/window.js b/src/state/actions/window.js index 2aaf858d9..9a58008f5 100644 --- a/src/state/actions/window.js +++ b/src/state/actions/window.js @@ -7,8 +7,17 @@ import ActionTypes from './action-types'; * @param {String} windowId * @memberof ActionCreators */ -export function focusWindow(windowId) { - return { type: ActionTypes.FOCUS_WINDOW, windowId }; +export function focusWindow(windowId, pan = false) { + return (dispatch, getState) => { + const { windows } = getState(); + const { x, y } = windows[windowId]; + + dispatch({ + type: ActionTypes.FOCUS_WINDOW, + windowId, + position: pan ? { x: x - 200, y: y - 200 } : {}, + }); + }; } /** diff --git a/src/state/reducers/workspace.js b/src/state/reducers/workspace.js index 0db841dc2..d9606abd8 100644 --- a/src/state/reducers/workspace.js +++ b/src/state/reducers/workspace.js @@ -17,7 +17,14 @@ export const workspaceReducer = ( ) => { switch (action.type) { case ActionTypes.FOCUS_WINDOW: - return { ...state, focusedWindowId: action.windowId }; + return { + ...state, + focusedWindowId: action.windowId, + viewportPosition: { + ...state.viewportPosition, + ...action.position, + }, + }; case ActionTypes.SET_WORKSPACE_FULLSCREEN: return { ...state, isFullscreenEnabled: action.isFullscreenEnabled }; case ActionTypes.TOGGLE_ZOOM_CONTROLS: -- GitLab