Skip to content

Commit

Permalink
feat(keyboard): keep key pressed for multiple keydown events (#728)
Browse files Browse the repository at this point in the history
* Initial POC

* WIP: test

* Fix types

* Reduce getNextKeyDef complexity

* Fix tests

* Fix type import

* Implement feedback on triggering event

* fix: only trigger one `keyup` for repeated `keydown`

* fix: do not leak `keyboardState.repeatKey`

Co-authored-by: Philipp Fritsche <ph.fritsche@gmail.com>
  • Loading branch information
patricklizon and ph-fritsche authored Oct 18, 2021
1 parent fcbdf21 commit 5d946d5
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 96 deletions.
22 changes: 19 additions & 3 deletions src/__tests__/keyboard/getNextKeyDef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ cases(
text: '[Control>]',
modifiers: {releaseSelf: false},
},
'keep key pressed with repeatModifier': {
text: '{Control>2}',
modifiers: {releaseSelf: false},
},
'release after repeatModifier': {
text: '{Control>2/}',
modifiers: {releaseSelf: true},
},
'no releaseSelf on legacy modifier': {
text: '{ctrl}',
modifiers: {releaseSelf: false},
Expand All @@ -96,15 +104,23 @@ cases(
{
'invalid descriptor': {
text: '{!}',
expectedError: 'Expected key descriptor but found "!" in "{!}"',
expectedError: 'but found "!" in "{!}"',
},
'missing descriptor': {
text: '',
expectedError: 'Expected key descriptor but found "" in ""',
expectedError: 'but found "" in ""',
},
'missing closing bracket': {
text: '{a)',
expectedError: 'Expected closing bracket but found ")" in "{a)"',
expectedError: 'but found ")" in "{a)"',
},
'invalid repeat modifier': {
text: '{a>e}',
expectedError: 'but found "e" in "{a>e}"',
},
'missing bracket after repeat modifier': {
text: '{a>3)',
expectedError: 'but found ")" in "{a>3)"',
},
},
)
119 changes: 119 additions & 0 deletions src/__tests__/keyboard/keyboardImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,122 @@ test('no character input if `altKey` or `ctrlKey` is pressed', () => {
expect(eventWasFired('keypress')).toBe(false)
expect(eventWasFired('input')).toBe(false)
})

test('do not leak repeatKey in state', () => {
const {element} = setup(`<input/>`)
;(element as HTMLInputElement).focus()

const keyboardState = userEvent.keyboard('{a>2}')
expect(keyboardState).toHaveProperty('repeatKey', undefined)
})

describe('pressing and releasing keys', () => {
it('fires event with releasing key twice', () => {
const {element, getEventSnapshot, clearEventCalls} = setup(`<input/>`)

;(element as HTMLInputElement).focus()
clearEventCalls()

userEvent.keyboard('{ArrowLeft>}{ArrowLeft}')

expect(getEventSnapshot()).toMatchInlineSnapshot(`
Events fired on: input[value=""]
input[value=""] - keydown: ArrowLeft (37)
input[value=""] - select
input[value=""] - keyup: ArrowLeft (37)
input[value=""] - keydown: ArrowLeft (37)
input[value=""] - select
input[value=""] - keyup: ArrowLeft (37)
`)
})

it('fires event without releasing key', () => {
const {element, getEventSnapshot, clearEventCalls} = setup(`<input/>`)

;(element as HTMLInputElement).focus()
clearEventCalls()

userEvent.keyboard('{a>}')

expect(getEventSnapshot()).toMatchInlineSnapshot(`
Events fired on: input[value="a"]
input[value=""] - keydown: a (97)
input[value=""] - keypress: a (97)
input[value="a"] - input
`)
})

it('fires event multiple times without releasing key', () => {
const {element, getEventSnapshot, clearEventCalls} = setup(`<input/>`)
;(element as HTMLInputElement).focus()
clearEventCalls()

userEvent.keyboard('{a>2}')

expect(getEventSnapshot()).toMatchInlineSnapshot(`
Events fired on: input[value="aa"]
input[value=""] - keydown: a (97)
input[value=""] - keypress: a (97)
input[value="a"] - input
input[value="a"] - keydown: a (97)
input[value="a"] - keypress: a (97)
input[value="aa"] - input
`)
})

it('fires event multiple times and releases key', () => {
const {element, getEventSnapshot, clearEventCalls} = setup(`<input/>`)
;(element as HTMLInputElement).focus()
clearEventCalls()

userEvent.keyboard('{a>2/}')

expect(getEventSnapshot()).toMatchInlineSnapshot(`
Events fired on: input[value="aa"]
input[value=""] - keydown: a (97)
input[value=""] - keypress: a (97)
input[value="a"] - input
input[value="a"] - keydown: a (97)
input[value="a"] - keypress: a (97)
input[value="aa"] - input
input[value="aa"] - keyup: a (97)
`)
})

it('fires event multiple times for multiple keys', () => {
const {element, getEventSnapshot, clearEventCalls} = setup(`<input/>`)
;(element as HTMLInputElement).focus()
clearEventCalls()

userEvent.keyboard('{a>2}{b>2/}{c>2}{/a}')

expect(getEventSnapshot()).toMatchInlineSnapshot(`
Events fired on: input[value="aabbcc"]
input[value=""] - keydown: a (97)
input[value=""] - keypress: a (97)
input[value="a"] - input
input[value="a"] - keydown: a (97)
input[value="a"] - keypress: a (97)
input[value="aa"] - input
input[value="aa"] - keydown: b (98)
input[value="aa"] - keypress: b (98)
input[value="aab"] - input
input[value="aab"] - keydown: b (98)
input[value="aab"] - keypress: b (98)
input[value="aabb"] - input
input[value="aabb"] - keyup: b (98)
input[value="aabb"] - keydown: c (99)
input[value="aabb"] - keypress: c (99)
input[value="aabbc"] - input
input[value="aabbc"] - keydown: c (99)
input[value="aabbc"] - keypress: c (99)
input[value="aabbcc"] - input
input[value="aabbcc"] - keyup: a (97)
`)
})
})
Loading

0 comments on commit 5d946d5

Please sign in to comment.