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

fix(Checkbox): prevent onClick from being called twice #3351

Merged
merged 16 commits into from
Jan 16, 2019
Merged

fix(Checkbox): prevent onClick from being called twice #3351

merged 16 commits into from
Jan 16, 2019

Conversation

Fabianopb
Copy link
Member

Alright, I have to say that this was not trivial at all, but now the handlers seem to work for all cases. After plenty of investigation and trying different approaches, the solution was to leave the call of onClick to the click handler.

Here are gifs showing the component working for three cases (in all of them I'm checking and unchecking with clicks and then with the spacebar):

Cheers and Merry Xmas! 🎄

(Closes #3348)

@Fabianopb Fabianopb changed the title Checkbox double onclick Checkbox: Prevent onClick from being called twice Dec 24, 2018
@codecov-io
Copy link

codecov-io commented Dec 24, 2018

Codecov Report

Merging #3351 into master will decrease coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3351      +/-   ##
==========================================
- Coverage   99.89%   99.89%   -0.01%     
==========================================
  Files         170      170              
  Lines        2812     2809       -3     
==========================================
- Hits         2809     2806       -3     
  Misses          3        3
Impacted Files Coverage Δ
src/modules/Checkbox/Checkbox.js 100% <100%> (ø) ⬆️

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 cda2a2f...a5a76ef. Read the comment docs.

@layershifter layershifter changed the title Checkbox: Prevent onClick from being called twice fix(Checkbox): prevent onClick from being called twice Dec 24, 2018
@layershifter
Copy link
Member

layershifter commented Dec 24, 2018

Please check the DOM Comparison example.

Case 1 mouse click: FAILED

SUIR checkbox

Click      { checked: false }
Change     { checked: true }

DOM checkbox

DOM Change { checked: true }
DOM Click  { checked: true }

Case 2 space key click: PASSED

SUIR checkbox

Change     { checked: true }
Click      { checked: true }

DOM checkbox

DOM Change { checked: true }
DOM Click  { checked: true }

Case 3 ID: mouse click on checkbox: FAILED

SUIR checkbox with ID

Change     { checked: true }

DOM checkbox with ID

DOM Change { checked: true }
DOM Click  { checked: true }

Case 4 ID: mouse click on label: FAILED

SUIR checkbox with ID

Change     { checked: true }

DOM checkbox with ID

DOM Change { checked: true }
DOM Click  { checked: true }

@Fabianopb I briefly checked the changes, they shown that we need more strict tests there and changes to match DOM checkbox.

Something like this:

wrapper.find('label').simulate('click')

onClick.should.have.been.calledOnce()
onClick.should.have.been.calledWith(e, { checked: true })
onChange.should.have.been.calledOnce()
onChange.should.have.been.calledWith(e, { checked: true })

onChange.should.have.been.calledAfter(onClick)

https://github.com/domenic/sinon-chai

@Fabianopb
Copy link
Member Author

Sorry @layershifter where can I find these DOM Comparison examples?

@layershifter
Copy link
Member

@Fabianopb
Copy link
Member Author

Alright thanks, I'll take a look!

@Fabianopb
Copy link
Member Author

@layershifter I've found a quite curious solution, which was to remove the onClick call completely from handleClick (in other words, completely removing the click handler from Checkbox).

By doing that, the interactions still work as I've shown in all those gifs and the DOM Comparisons are all valid (you can already check from my latest commit)!

However the test handles events transparently fails as it can't find onClick being fired from handleClick. As everything works as expected, should we treat this case as an exception?

@Fabianopb
Copy link
Member Author

Fabianopb commented Dec 26, 2018

@layershifter I've created four tests for DOM Comparisons, simulating the change event on:

  • input without id
  • label without id
  • input with id
  • label with id

The change event is used because onClick is invoked within handleChange (as already done previously). By doing that we also cover spacebar key "click" as it fires the same events as a click in this component.

However, I had to disable the common test isConformant due to the empty handleClick handler.

My proposition is that we merge this PR because the bug is fixed and the DOM behavior is conformant, however, we should open a new issue to discuss and tackle the empty handler and disabled test (I could open that and start the discussion). What do you say? :)

@layershifter
Copy link
Member

@Fabianopb sorry for delay, I will check it after the New Year 🎄 Have happy holidays!

@Fabianopb
Copy link
Member Author

Thanks @layershifter and Happy New Year! 🎉

@layershifter
Copy link
Member

@Fabianopb I updated tests to work with DOM deeply. It will be awesome if you will be able to take a look and check them 👍

@Fabianopb
Copy link
Member Author

It looks good to me @layershifter, nice set up with the assertMatrix and domEvent.fire!

I've added two more tests for onChange and onClick on controlled components with setState as function, which cover the functionality of this bug fix and it's also a kind of confirmation test for #2748. From me that should be it, I think this is ready to merge! :)

// The browser calls onClick AFTER onChange since onClick is on the root element.
// Because of this, we call onClick in handleChange to preserve the correct order.
// In that case, we skip calls to onClick here to avoid duplicate calls.
if (onChange) return
Copy link
Member Author

Choose a reason for hiding this comment

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

Wouldn't this cause issues in case the user has both onClick and onChange defined?

Copy link
Member Author

Choose a reason for hiding this comment

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

Digging a bit deeper, this change actually breaks the use of onClick like described in #3348 , I've updated the tests on controlled components to warn us accordingly. You will notice that if you revert this commit, the tests will pass.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the double checking. I have a couple more updates I'll push, then I think we can get a final on this and merge it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thinking again, it's a bit misleading to have an empty handler like I had done before, I'll push a new commit removing it completely and adding the explanation close to the onClick invocation.

@Fabianopb
Copy link
Member Author

Some 10 days since the last discussions, just checking, what's the status? :)

@layershifter
Copy link
Member

I can't promise anything, but I'm going to check this PR again tomorrow and merge it. I know that @levithomason worked on another solution (may be cleaner), but for now, it's better to fix the issue.

@layershifter layershifter merged commit 9ca7bc3 into Semantic-Org:master Jan 16, 2019
@layershifter
Copy link
Member

Merged, @Fabianopb thanks for working on it 👍

@Fabianopb
Copy link
Member Author

Awesome @layershifter thanks!

@@ -13,6 +13,7 @@ import hasValidTypings from './hasValidTypings'
* Assert Component conforms to guidelines that are applicable to all components.
* @param {React.Component|Function} Component A component that should conform.
* @param {Object} [options={}]
* @param {String[]} [options.disabledHandlers=[]] An array of listeners that are disabled.
Copy link
Member

Choose a reason for hiding this comment

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

Heads up @layershifter we'll need to undo this sometime soon. Ignoring events means we aren't testing that all events work correctly for the end user. I don't think it is high priority, but something to keep on our tech debt list.

@levithomason
Copy link
Member

Released in [email protected].

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

Successfully merging this pull request may close these issues.

Checkbox: double call of onClick when event fired by mouse
4 participants