-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Pass modifier flags when triggering Buttons via keypress #561
Conversation
Do we really have to futz with an event's fields? Something feels off about this whole thing... AbstractButton seems to be more trouble than it's worth. |
because we're using |
@@ -46,6 +46,7 @@ export abstract class AbstractButton<T> extends React.Component<React.HTMLProps< | |||
}, | |||
}; | |||
|
|||
// define these as private members to avoid polluting component state with sneaky fields from this scope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we still need this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, deleted.
@@ -59,17 +59,31 @@ function buttonTestSuite(component: React.ComponentClass<any>, tagName: string) | |||
assert.equal(onClick.callCount, 0); | |||
}); | |||
|
|||
it("calls onClick when enter key pressed", () => { | |||
// clewis: After adding explicit done()s that wee missing before, these |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
were missing
😭
@leebyp FYI - fixed the unit-test issues with some |
@adidahiya @giladgray @leebyp - Check out the new approach (I've updated the PR description above). |
// wait for the whole lifecycle to run | ||
setTimeout(() => { | ||
assert.equal(onClick.callCount, 1); | ||
done(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im pretty sure you do, but did you check if this is still needed with the change in the logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
checkKeyEventCallbackInvoked("onKeyUp", "keyup", Keys.SPACE); | ||
}); | ||
|
||
it("calls onClick when enter key pressed", (done) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"... when enter key released"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
// IButtonProps doesn't include onKeyDown or onKeyUp in its | ||
// definition, even though Buttons support those props. Casting as | ||
// `any` gets around that for the purpose of these tests. | ||
const wrapper = button({ [callbackPropName]: callback } as any); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can edit the button
helper function to use IButtonProps & ButtonHTMLProps
(I cant remember the exact nomenclature)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying it - it's leading to a soupy syntactic mess that'll percolate throughout the file. Okay if I leave as is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ouch, k
@@ -75,22 +75,24 @@ export abstract class AbstractButton<T> extends React.Component<React.HTMLProps< | |||
}; | |||
} | |||
|
|||
protected handleKeyDown = (e: React.KeyboardEvent<HTMLElement>) => { | |||
protected handleKeyDown = (e: React.KeyboardEvent<any>) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this need to be any
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// CC @giladgray, who looked at this with me before committing
I kept seeing compiler errors to the effect of KeyboardEvent<T> is not a supertype of KeyboardEvent<HTMLElement>
for the safeInvoke
line below. If there's a better way to address that issue, let me know. 👍
// doesn't properly propagate events on non-string refs | ||
// (https://github.com/airbnb/enzyme/issues/566), we need to dig in | ||
// and attach a spy to the ref's .click function ourselves. | ||
const buttonRef = (wrapper.instance() as any).buttonRef; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as AbstractButton
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will try.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a no-go. This change:
const buttonRef = (wrapper.instance() as AbstractButton<HTMLButtonElement>).buttonRef;
yields the following error:
[ts] Property 'buttonRef' is protected and only accessible within class 'AbstractButton<T>' and its subclasses.
(property) AbstractButton<HTMLButtonElement>.buttonRef: HTMLElement
// we're casting as `any` to get around a somewhat opaque safeInvoke error | ||
// that "Type argument candidate 'KeyboardEvent<T>' is not a valid type | ||
// argument because it is not a supertype of candidate | ||
// 'KeyboardEvent<HTMLElement>'." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't need to duplicate this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
const onClick = sinon.spy(); | ||
button({ onClick }, true).simulate("keyup", { which: Keys.SPACE }); | ||
setTimeout(() => assert.equal(onClick.callCount, 1), 0); | ||
it("pressing enter triggers onKeyUp props with any modifier flags", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
truthfully, these tests are not particularly useful. you're really just testing the safeInvoke
line as it reuses the exact same event so of course the modifier flags come through. there's nothing special here, it's just vanilla React.
i'd be fine with removing these tests, or simplifying to one for ENTER and one for SPACE
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these tests had been in place, the NumericInput
functionality wouldn't have broken without warning. I want some kind of safeguard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay if we leave them? I guess I'm okay with deleting the tests for onKeyUp
, but I definitely want a test in place for onKeyDown
, since that's what broke before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
K, deleted tests for onKeyUp.
// that it is being called. casting as `any` eliminates the button's | ||
// knowledge of which members are public, private, or protected. | ||
// this allows us to access the private buttonRef for the purposes | ||
// of this test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment feels too verbose. should be enough to say "mock the DOM click()
function because enzyme only handles simulated React events"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does not seem fixed in the diff
// knowledge of which members are public, private, or protected. | ||
// this allows us to access the private buttonRef for the purposes | ||
// of this test. | ||
const buttonRef = (wrapper.instance() as any).buttonRef; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adi suggested casting to AbstractButton
instead of any
. does that work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not when I tried, no. Still complained that buttonRef
was protected
and therefore inaccessible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh of course
// that it is being called. casting as `any` eliminates the button's | ||
// knowledge of which members are public, private, or protected. | ||
// this allows us to access the private buttonRef for the purposes | ||
// of this test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does not seem fixed in the diff
Checklist
Changes proposed in this pull request:
When we introduced
AbstractButton
, we unintentionally eliminated support foronKeyDown
andonKeyUp
callbacks on our buttons. This PR adds back that support, and it also fixes or adds tests to make sure all of this works as expected. In particular, this PR fixes an issue where a coupleassert
s wrapped insetTimeout
s were causing button tests to pass without actually reaching those checks.Reviewers should focus on:
master
before merging.any
in a few spots to get around TS compiler errors. LMK if any of these castings look unacceptable.