This repository has been archived by the owner on Dec 7, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 838
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP - Started on keyboard manager * Added keyboard binding component * Updated existing unit tests * Added unit tests for keyboard registration manager * Added unit tests for keyboard manager component * Added unit tests for keyboard binding component * Added unit tests for previous/next asset in editor page * Updated unit tests for toolbar item * Removed direct reference to keyboard manager context - no longer needed
- Loading branch information
Showing
17 changed files
with
598 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
src/react/components/common/keyboardBinding/keyboardBinding.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import React from "react"; | ||
import { mount, ReactWrapper } from "enzyme"; | ||
import { KeyboardManager } from "../keyboardManager/keyboardManager"; | ||
import { KeyboardBinding, IKeyboardBindingProps } from "./keyboardBinding"; | ||
|
||
jest.mock("../keyboardManager/keyboardRegistrationManager"); | ||
import { KeyboardRegistrationManager } from "../keyboardManager/keyboardRegistrationManager"; | ||
|
||
describe("Keyboard Binding Component", () => { | ||
let wrapper: ReactWrapper = null; | ||
const onKeyDownHandler = jest.fn(); | ||
const deregisterFunc = jest.fn(); | ||
|
||
const defaultProps: IKeyboardBindingProps = { | ||
accelerator: "Ctrl+1", | ||
onKeyDown: onKeyDownHandler, | ||
}; | ||
|
||
const registrationMock = KeyboardRegistrationManager as jest.Mocked<typeof KeyboardRegistrationManager>; | ||
registrationMock.prototype.addHandler = jest.fn(() => deregisterFunc); | ||
|
||
function createComponent(props?: IKeyboardBindingProps): ReactWrapper { | ||
props = props || defaultProps; | ||
|
||
return mount( | ||
<KeyboardManager> | ||
<KeyboardBinding {...props} /> | ||
</KeyboardManager>, | ||
); | ||
} | ||
|
||
beforeEach(() => { | ||
wrapper = createComponent(); | ||
}); | ||
|
||
it("is defined", () => { | ||
expect(wrapper).not.toBeNull(); | ||
}); | ||
|
||
it("does not render anything", () => { | ||
expect(wrapper.find(KeyboardBinding).html()).toBeNull(); | ||
}); | ||
|
||
it("registered the key code and event handler", () => { | ||
expect(registrationMock.prototype.addHandler).toBeCalledWith(defaultProps.accelerator, defaultProps.onKeyDown); | ||
}); | ||
|
||
it("deregisters the event handler", () => { | ||
wrapper.unmount(); | ||
expect(deregisterFunc).toBeCalled(); | ||
}); | ||
}); |
27 changes: 27 additions & 0 deletions
27
src/react/components/common/keyboardBinding/keyboardBinding.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { KeyboardContext, IKeyboardContext } from "../keyboardManager/keyboardManager"; | ||
import React from "react"; | ||
|
||
export interface IKeyboardBindingProps { | ||
accelerator: string; | ||
onKeyDown: (evt?: KeyboardEvent) => void; | ||
} | ||
|
||
export class KeyboardBinding extends React.Component<IKeyboardBindingProps> { | ||
public static contextType = KeyboardContext; | ||
public context!: IKeyboardContext; | ||
private deregisterBinding: () => void; | ||
|
||
public componentDidMount() { | ||
this.deregisterBinding = this.context.keyboard.addHandler(this.props.accelerator, this.props.onKeyDown); | ||
} | ||
|
||
public componentWillUnmount() { | ||
if (this.deregisterBinding) { | ||
this.deregisterBinding(); | ||
} | ||
} | ||
|
||
public render() { | ||
return null; | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
src/react/components/common/keyboardManager/keyboardManager.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import React from "react"; | ||
import { mount, ReactWrapper } from "enzyme"; | ||
import { KeyboardManager, IKeyboardContext } from "./keyboardManager"; | ||
|
||
jest.mock("./keyboardRegistrationManager"); | ||
import { KeyboardRegistrationManager } from "./keyboardRegistrationManager"; | ||
|
||
describe("Keyboard Manager Component", () => { | ||
let wrapper: ReactWrapper<{}, IKeyboardContext> = null; | ||
let addEventListenerSpy: jest.SpyInstance = null; | ||
let removeEventListenerSpy: jest.SpyInstance = null; | ||
|
||
function createComponent(): ReactWrapper<{}, IKeyboardContext> { | ||
return mount( | ||
<KeyboardManager> | ||
<div className="child">Hello - I am a child component</div> | ||
</KeyboardManager>, | ||
); | ||
} | ||
|
||
beforeEach(() => { | ||
addEventListenerSpy = jest.spyOn(window, "addEventListener"); | ||
removeEventListenerSpy = jest.spyOn(window, "removeEventListener"); | ||
wrapper = createComponent(); | ||
}); | ||
|
||
it("is defined", () => { | ||
expect(wrapper).not.toBeNull(); | ||
}); | ||
|
||
it("initial state is defined", () => { | ||
expect(wrapper.state().keyboard).toBeInstanceOf(KeyboardRegistrationManager); | ||
}); | ||
|
||
it("registers event handlers for keydown events", () => { | ||
expect(addEventListenerSpy).toBeCalled(); | ||
expect(removeEventListenerSpy).toBeCalled(); | ||
}); | ||
|
||
it("renders all child components", () => { | ||
expect(wrapper.find(".child").exists()).toBe(true); | ||
}); | ||
|
||
it("listens for Ctrl+ keydown events and invokes handlers", () => { | ||
const keyboardEvent = new KeyboardEvent("keydown", { | ||
ctrlKey: true, | ||
key: "1", | ||
}); | ||
|
||
window.dispatchEvent(keyboardEvent); | ||
|
||
const registrationManagerMock = KeyboardRegistrationManager as jest.Mocked<typeof KeyboardRegistrationManager>; | ||
expect(registrationManagerMock.prototype.invokeHandlers).toBeCalledWith("Ctrl+1", keyboardEvent); | ||
}); | ||
|
||
it("listens for Alt+ keydown events and invokes handlers", () => { | ||
const keyboardEvent = new KeyboardEvent("keydown", { | ||
ctrlKey: false, | ||
altKey: true, | ||
key: "1", | ||
}); | ||
|
||
window.dispatchEvent(keyboardEvent); | ||
|
||
const registrationManagerMock = KeyboardRegistrationManager as jest.Mocked<typeof KeyboardRegistrationManager>; | ||
expect(registrationManagerMock.prototype.invokeHandlers).toBeCalledWith("Alt+1", keyboardEvent); | ||
}); | ||
|
||
it("listens for keydown events and invokes handlers", () => { | ||
const keyboardEvent = new KeyboardEvent("keydown", { | ||
ctrlKey: false, | ||
altKey: false, | ||
key: "F1", | ||
}); | ||
|
||
window.dispatchEvent(keyboardEvent); | ||
|
||
const registrationManagerMock = KeyboardRegistrationManager as jest.Mocked<typeof KeyboardRegistrationManager>; | ||
expect(registrationManagerMock.prototype.invokeHandlers).toBeCalledWith("F1", keyboardEvent); | ||
}); | ||
}); |
49 changes: 49 additions & 0 deletions
49
src/react/components/common/keyboardManager/keyboardManager.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from "react"; | ||
import { KeyboardRegistrationManager } from "./keyboardRegistrationManager"; | ||
|
||
export const KeyboardContext = React.createContext<IKeyboardContext>(null); | ||
|
||
export interface IKeyboardContext { | ||
keyboard: KeyboardRegistrationManager; | ||
} | ||
|
||
export class KeyboardManager extends React.Component<any, IKeyboardContext> { | ||
public static contextType = KeyboardContext; | ||
|
||
public state: IKeyboardContext = { | ||
keyboard: new KeyboardRegistrationManager(), | ||
}; | ||
|
||
public componentDidMount() { | ||
window.addEventListener("keydown", this.onKeyDown); | ||
} | ||
|
||
public componentWillUnmount() { | ||
window.removeEventListener("keydown", this.onKeyDown); | ||
} | ||
|
||
public render() { | ||
return ( | ||
<KeyboardContext.Provider value={this.state}> | ||
{this.props.children} | ||
</KeyboardContext.Provider> | ||
); | ||
} | ||
|
||
private onKeyDown = (evt: KeyboardEvent) => { | ||
if (evt.key === "Ctrl" || evt.key === "Control" || evt.key === "Alt") { | ||
return; | ||
} | ||
|
||
const keyParts = []; | ||
if (evt.ctrlKey) { | ||
keyParts.push("Ctrl+"); | ||
} | ||
if (evt.altKey) { | ||
keyParts.push("Alt+"); | ||
} | ||
keyParts.push(evt.key); | ||
|
||
this.state.keyboard.invokeHandlers(keyParts.join(""), evt); | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
src/react/components/common/keyboardManager/keyboardRegistrationManager.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { KeyboardRegistrationManager } from "./keyboardRegistrationManager"; | ||
|
||
describe("Keyboard Registration Manager", () => { | ||
let keyboardManager: KeyboardRegistrationManager = null; | ||
|
||
beforeEach(() => { | ||
keyboardManager = new KeyboardRegistrationManager(); | ||
}); | ||
|
||
it("is defined", () => { | ||
expect(KeyboardRegistrationManager).toBeDefined(); | ||
expect(keyboardManager).not.toBeNull(); | ||
}); | ||
|
||
it("can add keybard event handlers", () => { | ||
const keyCode1 = "Ctrl+1"; | ||
const handler1 = (evt: KeyboardEvent) => null; | ||
const keyCode2 = "Ctrl+S"; | ||
const handler2 = (evt: KeyboardEvent) => null; | ||
|
||
keyboardManager.addHandler(keyCode1, handler1); | ||
keyboardManager.addHandler(keyCode2, handler2); | ||
|
||
const handlers1 = keyboardManager.getHandlers(keyCode1); | ||
const handlers2 = keyboardManager.getHandlers(keyCode2); | ||
|
||
expect(handlers1.length).toEqual(1); | ||
expect(handlers2.length).toEqual(1); | ||
|
||
expect(handlers1[0]).toBe(handler1); | ||
expect(handlers2[0]).toBe(handler2); | ||
}); | ||
|
||
it("can register multiple handlers for same key code", () => { | ||
const keyCode = "Ctrl+H"; | ||
const handler1 = (evt: KeyboardEvent) => null; | ||
const handler2 = (evt: KeyboardEvent) => null; | ||
|
||
keyboardManager.addHandler(keyCode, handler1); | ||
keyboardManager.addHandler(keyCode, handler2); | ||
|
||
const handlers = keyboardManager.getHandlers(keyCode); | ||
expect(handlers.length).toEqual(2); | ||
}); | ||
|
||
it("list of handlers cannot be mutated outside of API", () => { | ||
const keyCode = "Ctrl+K"; | ||
const handler1 = (evt: KeyboardEvent) => null; | ||
|
||
keyboardManager.addHandler(keyCode, handler1); | ||
const handlers = keyboardManager.getHandlers(keyCode); | ||
const handlerCount = handlers.length; | ||
|
||
// Attempt to add more handlers | ||
handlers.push(handler1, handler1, handler1); | ||
|
||
const newHandlers = keyboardManager.getHandlers(keyCode); | ||
expect(newHandlers.length).toEqual(handlerCount); | ||
}); | ||
|
||
it("can remove keyboard event handlers", () => { | ||
const keyCode1 = "Ctrl+1"; | ||
const handler1 = (evt: KeyboardEvent) => null; | ||
|
||
// Register keyboard handler | ||
const deregister = keyboardManager.addHandler(keyCode1, handler1); | ||
|
||
// Get registered handlers | ||
let handlers1 = keyboardManager.getHandlers(keyCode1); | ||
expect(handlers1.length).toEqual(1); | ||
|
||
// Invode deregister functions | ||
deregister(); | ||
|
||
// Get registered handlers after deregistered | ||
handlers1 = keyboardManager.getHandlers(keyCode1); | ||
expect(handlers1.length).toEqual(0); | ||
}); | ||
|
||
it("get handlers for unregistered key code returns emtpy array", () => { | ||
const handlers = keyboardManager.getHandlers("Alt+1"); | ||
expect(handlers.length).toEqual(0); | ||
}); | ||
|
||
it("invokes registered keyboard handlers", () => { | ||
const keyCode = "Ctrl+1"; | ||
const handler1 = jest.fn(); | ||
const handler2 = jest.fn(); | ||
|
||
keyboardManager.addHandler(keyCode, handler1); | ||
keyboardManager.addHandler(keyCode, handler2); | ||
|
||
const keyboardEvent = new KeyboardEvent("keydown", { | ||
ctrlKey: true, | ||
code: "1", | ||
}); | ||
|
||
keyboardManager.invokeHandlers(keyCode, keyboardEvent); | ||
|
||
expect(handler1).toBeCalledWith(keyboardEvent); | ||
expect(handler2).toBeCalledWith(keyboardEvent); | ||
}); | ||
}); |
Oops, something went wrong.