-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(keyboard-key): create package (#4136)
* feat(keyboard-key): component init * chore(tools): plopfile syntax fix * chore(tools): plopfile syntax fix * chore(tools): plopfile syntax fix * feat(keyboard-key): styled w/ hook unit tests * feat(keyboard-key): added variants and styles * chore(plop): update tsx dependency * chore(keyboard-key): internal exports in core * chore(keyboard-key): typedocs & build * feat(keyboard-key): change to infline-flex style * chore(keyboard-key): added stroy * chore(keyboard-key): linting * fix(keyboard-key): command logic * feat(design-tokens): added new box shadows to support keyboard-keys * chore(ci-cd): added chagesets * fix(keyboard-key): boxShadow stylings * fix(keyboard-key): remove null component wrapper * fix(keyboard-key): aria-hidden * chore(keyboard-key): refactor * chore(keyboard-key): code cleanup * chore(keyboard-key): code cleanup * chore(keyboard-key): typedocs * chore(keyboard-key): fix tests * fix(keyboard-key): aria and diableBrowserShortcuts * fix(keyboard-key): props fix * chore(keyboard-key): playgorund storybook * chore(keyboard-key): formatting fix * chore(keyboard-key): stories update * chore(keyboard-key): formatting fix * chore(keyboard-key): typo * Update .changeset/sweet-mugs-admire.md Co-authored-by: Nora Krantz <[email protected]> * Update .changeset/shaggy-sheep-confess.md Co-authored-by: Nora Krantz <[email protected]> * chore(keyboard-key): address PR comments * fix(keyboard-key): no exported member * fix(keyboard-key): typedocs * fix(keyboard-key): typedocs * chore(keyboard-key): update prop name to eventKey * chore(keyboard-key): typedoc changes --------- Co-authored-by: Nora Krantz <[email protected]> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
- Loading branch information
1 parent
65d6054
commit a4404ea
Showing
31 changed files
with
4,331 additions
and
6 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@twilio-paste/codemods": minor | ||
--- | ||
|
||
[KeyboardKey] added a new component to display visual indicators for keyboard shortcuts available to users |
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,6 @@ | ||
--- | ||
"@twilio-paste/core": minor | ||
"@twilio-paste/design-tokens": minor | ||
--- | ||
|
||
[Design Tokens] added new design tokens shadowBorderBottomWeak and shadowBorderBottomInverseWeaker to support new feature, KeybaordKey. |
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,6 @@ | ||
--- | ||
"@twilio-paste/keyboard-key": major | ||
"@twilio-paste/core": minor | ||
--- | ||
|
||
[KeyboardKey] added a new component to display visual indicators for keyboard shortcuts available to users |
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
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
Empty file.
315 changes: 315 additions & 0 deletions
315
packages/paste-core/components/keyboard-key/__tests__/hooks.spec.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,315 @@ | ||
import { act, fireEvent, render, renderHook, screen, waitFor } from "@testing-library/react"; | ||
import * as React from "react"; | ||
|
||
import { useKeyCombination, useKeyCombinations } from "../src"; | ||
import { Default } from "../stories/index.stories"; | ||
|
||
describe("Hooks", () => { | ||
it("should handle pressed styling", async () => { | ||
const { getAllByText } = render(<Default />); | ||
|
||
const controlKey = getAllByText("Control")[0]; | ||
const bKey = getAllByText("B")[0]; | ||
expect(controlKey).toBeDefined(); | ||
expect(controlKey).toHaveStyleRule("background-color", "rgb(249, 249, 250)"); | ||
expect(bKey).toHaveStyleRule("background-color", "rgb(249, 249, 250)"); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(controlKey, { key: "Control" }); | ||
}); | ||
|
||
expect(controlKey).toHaveStyleRule("background-color", "rgb(225, 227, 234)"); | ||
expect(bKey).toHaveStyleRule("background-color", "rgb(249, 249, 250)"); | ||
|
||
await act(async () => { | ||
fireEvent.keyUp(controlKey, { key: "Control" }); | ||
}); | ||
|
||
expect(controlKey).toHaveStyleRule("background-color", "rgb(249, 249, 250)"); | ||
expect(bKey).toHaveStyleRule("background-color", "rgb(249, 249, 250)"); | ||
}); | ||
|
||
describe("useKeyCombination", () => { | ||
it("should update activeKeys on keydown and keyup", async () => { | ||
const { result } = renderHook(() => useKeyCombination({ keys: ["Control", "b"], onCombinationPress: jest.fn() })); | ||
|
||
expect(result.current?.activeKeys).toEqual([]); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "Control" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "d" }); | ||
fireEvent.keyDown(window, { key: "v" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control", "d", "v"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyUp(window, { key: "Control" }); | ||
fireEvent.keyUp(window, { key: "d" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["v"]); | ||
}); | ||
}); | ||
|
||
it("should call onCombinationPress when keys match", async () => { | ||
const onCombinationPress = jest.fn(); | ||
const { result } = renderHook(() => useKeyCombination({ keys: ["Control", "b"], onCombinationPress }), {}); | ||
|
||
expect(result.current?.activeKeys).toEqual([]); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "Control" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "b" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "b"])); | ||
expect(onCombinationPress).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
it("should not call onCombinationPress when keys do not match", async () => { | ||
const onCombinationPress = jest.fn(); | ||
const { result } = renderHook(() => useKeyCombination({ keys: ["Control", "b"], onCombinationPress }), {}); | ||
|
||
expect(result.current?.activeKeys).toEqual([]); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "Control" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "d" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control", "d"]); | ||
expect(onCombinationPress).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
it("should not call onCombinationPress when keys are present but more are pressed", async () => { | ||
const onCombinationPress = jest.fn(); | ||
const { result } = renderHook(() => useKeyCombination({ keys: ["Control", "b"], onCombinationPress }), {}); | ||
|
||
expect(result.current?.activeKeys).toEqual([]); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "Control" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "v" }); | ||
fireEvent.keyDown(window, { key: "b" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "v", "b"])); | ||
expect(onCombinationPress).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
it("should not call onCombinationPress when disabled", async () => { | ||
const onCombinationPress = jest.fn(); | ||
const { result } = renderHook(() => | ||
useKeyCombination({ keys: ["Control", "b"], onCombinationPress, disabled: true }), | ||
); | ||
|
||
expect(result.current?.activeKeys).toEqual([]); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "Control" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "b" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "b"])); | ||
expect(onCombinationPress).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("useKeyCombinations", () => { | ||
it("should update activeKeys on keydown and keyup", async () => { | ||
const { result } = renderHook(() => | ||
useKeyCombinations({ | ||
combinations: [ | ||
{ keys: ["Control", "b"], onCombinationPress: jest.fn() }, | ||
{ keys: ["Control", "c"], onCombinationPress: jest.fn() }, | ||
], | ||
}), | ||
); | ||
|
||
expect(result.current?.activeKeys).toEqual([]); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "Control" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "d" }); | ||
fireEvent.keyDown(window, { key: "v" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control", "d", "v"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyUp(window, { key: "Control" }); | ||
fireEvent.keyUp(window, { key: "d" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["v"]); | ||
}); | ||
}); | ||
|
||
it("should call onCombinationPress when keys match", async () => { | ||
const onCombinationPress1 = jest.fn(); | ||
const onCombinationPress2 = jest.fn(); | ||
|
||
const { result } = renderHook(() => | ||
useKeyCombinations({ | ||
combinations: [ | ||
{ keys: ["Control", "b"], onCombinationPress: onCombinationPress1 }, | ||
{ keys: ["Control", "c"], onCombinationPress: onCombinationPress2 }, | ||
], | ||
}), | ||
); | ||
|
||
expect(result.current?.activeKeys).toEqual([]); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "Control" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "b" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "b"])); | ||
expect(onCombinationPress1).toHaveBeenCalled(); | ||
expect(onCombinationPress2).not.toHaveBeenCalled(); | ||
}); | ||
|
||
await act(async () => { | ||
onCombinationPress1.mockClear(); | ||
fireEvent.keyDown(window, { key: "c" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "b", "c"])); | ||
expect(onCombinationPress1).not.toHaveBeenCalled(); | ||
expect(onCombinationPress2).not.toHaveBeenCalled(); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyUp(window, { key: "b" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "c"])); | ||
expect(onCombinationPress1).not.toHaveBeenCalled(); | ||
expect(onCombinationPress2).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
it("should not call onCOmbinationPress when disabled", async () => { | ||
const onCombinationPress1 = jest.fn(); | ||
const onCombinationPress2 = jest.fn(); | ||
|
||
const { result } = renderHook(() => | ||
useKeyCombinations({ | ||
combinations: [ | ||
{ keys: ["Control", "b"], onCombinationPress: onCombinationPress1 }, | ||
{ keys: ["Control", "c"], onCombinationPress: onCombinationPress2, disabled: true }, | ||
], | ||
}), | ||
); | ||
|
||
expect(result.current?.activeKeys).toEqual([]); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "Control" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(["Control"]); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyDown(window, { key: "b" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "b"])); | ||
expect(onCombinationPress1).toHaveBeenCalled(); | ||
expect(onCombinationPress2).not.toHaveBeenCalled(); | ||
}); | ||
|
||
await act(async () => { | ||
onCombinationPress1.mockClear(); | ||
fireEvent.keyDown(window, { key: "c" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "b", "c"])); | ||
expect(onCombinationPress1).not.toHaveBeenCalled(); | ||
expect(onCombinationPress2).not.toHaveBeenCalled(); | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.keyUp(window, { key: "b" }); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(result.current?.activeKeys).toEqual(expect.arrayContaining(["Control", "c"])); | ||
expect(onCombinationPress1).not.toHaveBeenCalled(); | ||
expect(onCombinationPress2).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.