Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Implement cursor navigation with navigation keys #404

Merged
merged 4 commits into from Nov 3, 2020

Conversation

juanca
Copy link
Collaborator

@juanca juanca commented Jul 18, 2020

Note: WIP! I am running into a few problems and would like advice.

What:

Implements navigation keys support in type method:

  • Arrows
  • Home and end
  • Page up and down

Why:

It is reasonable for a user to use keyboard navigation while interacting with an interactive element. An interactive element could be an input (where a user would type into) or an accessible UI with keyboard support (no typing but it handles key events).

How:

  • Handles new key events while typing: e.g. Brek{arrowleft}a{arrowright}fast -> Breakfast
  • Handles new key events with modifier while typing: e.g. Breakfast{shift}{arrowleft}{/shift} -> Breakfas{selection}t{/selection}

Checklist:

  • Documentation
  • Tests
  • Typings
  • Ready to be merged

Closes: #354

src/__tests__/type.js Outdated Show resolved Hide resolved
@@ -0,0 +1,58 @@
import {fireEvent} from '@testing-library/dom'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your opinions on using this kind of code organization? I noticed there is 3 files for typing:

  1. utils
  2. type
  3. type-modifiers

I think navigation keys will end up adding a chunk (but not massive) amount of code. I opted for a new file but I am willing to move things around.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably wouldn't have bothered making the new file myself, but I'm ok with it.

"Bre{CURSOR}" -> "Brea{CURSOR}"
input[value="Brea"] - keyup: a (97)
input[value="Brea"] - keydown: ArrowLeft (37)
input[value="Brea"] - select
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking a select event should show the before and after of the cursor position:

    input[value="Brea"] - keydown: ArrowLeft (37)
    input[value="Brea"] - select
      "Brea{CURSOR}" -> "Bre{CURSOR}a"
    input[value="Brea"] - keyup: ArrowLeft (37)

What do y'all think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For posterity's sake: there was an earlier comment and it got 1 response.

I'm not sure. Based on the playground we shouldn't fire a select event at all I think (though it may be unfortunately unavoidable).

I made a new comment thread to leave the unexpected {CURSOR} position bits out of this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

@CWSites
Copy link

CWSites commented Oct 15, 2020

Any update here? We could really use the ability to use type the arrow key for a couple reasons.

  1. Test/verify accessibility issues
  2. Get around complex components such as Material UI autocomplete by using our arrow key to select an option since we are unable to pick a specific option any other way.

@DovRine
Copy link

DovRine commented Oct 15, 2020

Agree with CWSites. This would greatly simplify testing things like: MaterialUI TextField[type="time"].

@kentcdodds
Copy link
Member

I'm sure @juanca would be more than happy to have either of you pick up where they left off and address the merge conflicts and other issues in this PR :)

@jesujcastillom
Copy link

Hi! This is something I recently came across with, and I'd be happy to help if it needs picking up 😃

@kentcdodds
Copy link
Member

Go for it 👍

@juanca
Copy link
Collaborator Author

juanca commented Oct 19, 2020

@jesujcastillom Thanks for the offer! I would greatly appreciate the help. I've been nerd-sniped into other projects for my free time.

Let me know if anyone needs any context, happy to provide it whenever.

expect(() => {
navigationKey('SOME_INVALID_KEY')
}).toThrowError(TypeError)
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've modified the function to hit all the lines (default wasn't reachable) now the question is, should I include this test to make sure we throw a TypeError (and also throw it in the function)` if we don't get a valid key, or leave it without throwing, since it's an internal function anyway.

In summary, removing the last commit entirely, we still get 💯 coverage, but it's not really covered if an invalid key is passed, any thoughts? @juanca @kentcdodds

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, modified the commit because I didn't have my git config setup properly in this machine, but yeah, same changes 😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this even possible? We're the only ones who call navigationKey and we always call it properly. I'd just remove that logic.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it isn't I'll remove it :)

@codecov
Copy link

codecov bot commented Nov 1, 2020

Codecov Report

Merging #404 into master will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##            master      #404   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           12        13    +1     
  Lines          482       494   +12     
  Branches       134       136    +2     
=========================================
+ Hits           482       494   +12     
Impacted Files Coverage Δ
src/type.js 100.00% <ø> (ø)
src/keys/navigation-key.js 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ee7c74f...522a362. Read the comment docs.

Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up! Here's some feedback for you.

src/type.js Outdated
@@ -44,6 +45,8 @@ const modifierCallbackMap = {
}

const specialCharCallbackMap = {
'{arrowleft}': navigationKey('ArrowLeft'),
'{arrowright}': navigationKey( 'ArrowRight'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'{arrowright}': navigationKey( 'ArrowRight'),
'{arrowright}': navigationKey('ArrowRight'),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

expect(getEventSnapshot()).toMatchInlineSnapshot(`
Events fired on: input[value="Brekafast"]

input[value=""] - pointerover
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried this in the playground and here's what I got for the events (Note, I ignored mouse/pointer move events because they're too noisy in the playground):

textarea#textarea[value=""] - pointerdown
textarea#textarea[value=""] - mousedown
textarea#textarea[value=""] - focusin
textarea#textarea[value=""] - pointerup
textarea#textarea[value=""] - mouseup
textarea#textarea[value=""] - click
textarea#textarea[value=""] - keydown
textarea#textarea[value=""] - keypress
textarea#textarea[value="b"] - input
textarea#textarea[value="b"] - keyup
textarea#textarea[value="b"] - keydown
textarea#textarea[value="b"] - keypress
textarea#textarea[value="br"] - input
textarea#textarea[value="br"] - keyup
textarea#textarea[value="br"] - keydown
textarea#textarea[value="br"] - keypress
textarea#textarea[value="bre"] - input
textarea#textarea[value="bre"] - keyup
textarea#textarea[value="bre"] - keydown
textarea#textarea[value="bre"] - keypress
textarea#textarea[value="brea"] - input
textarea#textarea[value="brea"] - keyup
textarea#textarea[value="brea"] - keydown // <- here's the arrow down to go left
textarea#textarea[value="brea"] - keyup
textarea#textarea[value="brea"] - keydown
textarea#textarea[value="brea"] - keypress
textarea#textarea[value="breka"] - input
textarea#textarea[value="breka"] - keyup
textarea#textarea[value="breka"] - keydown // <- here's the arrow down to go right
textarea#textarea[value="breka"] - keyup
textarea#textarea[value="breka"] - keydown
textarea#textarea[value="breka"] - keypress
textarea#textarea[value="brekaf"] - input
textarea#textarea[value="brekaf"] - keyup
textarea#textarea[value="brekaf"] - keydown
textarea#textarea[value="brekaf"] - keypress
textarea#textarea[value="brekafa"] - input
textarea#textarea[value="brekafa"] - keyup
textarea#textarea[value="brekafa"] - keydown
textarea#textarea[value="brekafa"] - keypress
textarea#textarea[value="brekafas"] - input
textarea#textarea[value="brekafas"] - keyup
textarea#textarea[value="brekafas"] - keydown
textarea#textarea[value="brekafas"] - keypress
textarea#textarea[value="brekafast"] - input
textarea#textarea[value="brekafast"] - keyup

Notice that there's no "select" event in there. I'm not sure whether it's possible to avoid that event, but it would be good to avoid if we can.

Also, I think this test could be simplified if we change the input to: b{arrowleft}a{arrowright}c (which would result in abc, and much fewer events for the snapshot).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least from what I could investigate, the [setSelectionRange](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-setselectionrange] will fire a select event, and I can't really seem to find another way to move the cursor.

expect(() => {
navigationKey('SOME_INVALID_KEY')
}).toThrowError(TypeError)
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this even possible? We're the only ones who call navigationKey and we always call it properly. I'd just remove that logic.

Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I don't think I can merge this until @juanca removes the draft status of this pr.

@kentcdodds kentcdodds marked this pull request as ready for review November 3, 2020 17:34
@kentcdodds kentcdodds merged commit 01c3a56 into testing-library:master Nov 3, 2020
@kentcdodds
Copy link
Member

@all-contributors please add @juanca for code and tests

@all-contributors please add @jesujcastillom for code and tests

@allcontributors
Copy link
Contributor

@kentcdodds

I could not determine your intention.

Basic usage: @all-contributors please add @Someone for code, doc and infra

For other usages see the documentation

@kentcdodds
Copy link
Member

@all-contributors please add @juanca for code and tests

@allcontributors
Copy link
Contributor

@kentcdodds

I've put up a pull request to add @juanca! 🎉

@kentcdodds
Copy link
Member

@all-contributors please add @jesujcastillom for code and tests

@allcontributors
Copy link
Contributor

@kentcdodds

I've put up a pull request to add @jesujcastillom! 🎉

@testing-library-bot
Copy link

🎉 This PR is included in version 12.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@brandonpittman
Copy link

brandonpittman commented Nov 4, 2020

Forgive my ignorance, but why only implement arrowright and arrowleft? Why not include up and down? My original interest in this issue was checking number inputs and the value associated when changing step by step. arrowleft and arrowright won't address that issue.

@kentcdodds
Copy link
Member

Up and down would be extremely difficult 😬

@brandonpittman
Copy link

@kentcdodds What makes up and down harder than left and right?

@kentcdodds
Copy link
Member

It comes down to where the cursor goes. In a textarea it depends on the size of the textarea as well as the font size. This would take a ridiculous amount of work (would probably require an environment that supports layout, so jsdom is out). Just too much work in afraid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

arrow keys?
7 participants