-
Notifications
You must be signed in to change notification settings - Fork 252
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Improve performance of tabbing #967
Improve performance of tabbing #967
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit e732ee9:
|
Thanks for contributing to this project. ❤️ Avoiding unnecessary calls to I've opened a PR simplifying the implementation: camchenry#1 |
PR testing-library#967 code suggestions
Thank you for the suggestions! I have gone ahead and merged those in this branch. I noticed that CI was failing previously due to a typecheck issue, which I'm not sure what that was about. |
Codecov Report
@@ Coverage Diff @@
## main #967 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 85 85
Lines 1820 1825 +5
Branches 667 669 +2
=========================================
+ Hits 1820 1825 +5
Continue to review full report at Codecov.
|
👋 @ph-fritsche Is there anything else that I can do to help get this merged? I'm more than happy to help contribute any additional changes or tests as needed. |
Sorry about the delay. Wanted to dispel one minor concern: Feeding our logic for radio groups with hidden elements does in fact match what is observed in the browser (Chrome): user-event/src/utils/focus/getTabDestination.ts Lines 42 to 44 in e732ee9
|
@all-contributors add @camchenry code |
I've put up a pull request to add @camchenry! 🎉 |
🎉 This PR is included in version 14.2.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@testing-library/user-event](https://github.com/testing-library/user-event) | dependencies | minor | [`14.1.1` -> `14.5.2`](https://renovatebot.com/diffs/npm/@testing-library%2fuser-event/14.1.1/14.5.2) | --- ### Release Notes <details> <summary>testing-library/user-event (@​testing-library/user-event)</summary> ### [`v14.5.2`](https://github.com/testing-library/user-event/releases/tag/v14.5.2) [Compare Source](testing-library/user-event@v14.5.1...v14.5.2) ##### Bug Fixes - remove interop and deep DTL imports ([6a3c896](testing-library/user-event@6a3c896)) ### [`v14.5.1`](https://github.com/testing-library/user-event/releases/tag/v14.5.1) [Compare Source](testing-library/user-event@v14.5.0...v14.5.1) ##### Bug Fixes - incorrect default import from [@​testing-library/dom](https://github.com/testing-library/dom) ([#​1162](testing-library/user-event#1162)) ([d7483f0](testing-library/user-event@d7483f0)) ### [`v14.5.0`](https://github.com/testing-library/user-event/releases/tag/v14.5.0) [Compare Source](testing-library/user-event@v14.4.3...v14.5.0) ##### Bug Fixes - **exports:** add a named export for userEvent ([4019cee](testing-library/user-event@4019cee)), closes [#​1146](testing-library/user-event#1146) ##### Features - **types:** Add additional type exports for UserEvent & Options ([#​1112](testing-library/user-event#1112)) ([da00e8d](testing-library/user-event@da00e8d)) ### [`v14.4.3`](https://github.com/testing-library/user-event/releases/tag/v14.4.3) [Compare Source](testing-library/user-event@v14.4.2...v14.4.3) ##### Bug Fixes - **build:** add `types` field in `exports` ([#​1029](testing-library/user-event#1029)) ([5bed8c6](testing-library/user-event@5bed8c6)) - remove circular dependencies ([#​1027](testing-library/user-event#1027)) ([1aa2027](testing-library/user-event@1aa2027)) ### [`v14.4.2`](https://github.com/testing-library/user-event/releases/tag/v14.4.2) [Compare Source](testing-library/user-event@v14.4.1...v14.4.2) ##### Bug Fixes - **build:** add `exports` field ([#​1022](testing-library/user-event#1022)) ([7839e29](testing-library/user-event@7839e29)) ### [`v14.4.1`](https://github.com/testing-library/user-event/releases/tag/v14.4.1) [Compare Source](testing-library/user-event@v14.4.0...v14.4.1) ##### Bug Fixes - **build:** transpile to es2019 syntax ([#​1016](testing-library/user-event#1016)) ([4291cb8](testing-library/user-event@4291cb8)) ### [`v14.4.0`](https://github.com/testing-library/user-event/releases/tag/v14.4.0) [Compare Source](testing-library/user-event@v14.3.0...v14.4.0) ##### Features - **pointer**: dispatch `auxclick` events ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) ##### Bug Fixes - **event:** be robust against incomplete event implementations ([#​1009](testing-library/user-event#1009)) ([289828b](testing-library/user-event@289828b)) - **upload:** be robust against missing FileList implementation ([#​1007](testing-library/user-event#1007)) ([a46b4d7](testing-library/user-event@a46b4d7)) - **keyboard**: switch modifier state of lock keys on the correct event ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **keyboard**: remove platform-specific additional key events for `Control` on `AltGraph` ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: dispatch `contextmenu` events with `detail: 0` ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: always set `PointerEvent.isPrimary` ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: set `button` property on pointer events separately from legacy mouse events ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: click closest common ancestor if `mousedown` and `mouseup` happen on different elements ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: omit click event on release if another button is released first ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: dispatch `mouseover`, `mouseenter` and `mousemove` on disabled elements ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: prevent `mouse*` events per `pointerdown` event handler ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: dispatch `*out` and `*over` events when moving into / out of nested elements ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) - **pointer**: dispatch `*enter` and `*leave` events on ancestors ([#​1003](testing-library/user-event#1003)) ([2852509](testing-library/user-event@2852509)) ### [`v14.3.0`](https://github.com/testing-library/user-event/releases/tag/v14.3.0) [Compare Source](testing-library/user-event@v14.2.6...v14.3.0) ##### Features - **keyboard:** change radio group per arrow keys ([#​995](testing-library/user-event#995)) ([e1c22af](testing-library/user-event@e1c22af)) ### [`v14.2.6`](https://github.com/testing-library/user-event/releases/tag/v14.2.6) [Compare Source](testing-library/user-event@v14.2.5...v14.2.6) ##### Bug Fixes - **document:** reduce impact of React@17 workaround ([#​992](testing-library/user-event#992)) ([9816d38](testing-library/user-event@9816d38)) - **pointer:** do not throw for `pointer-events: none` on previous target ([#​991](testing-library/user-event#991)) ([6e4058b](testing-library/user-event@6e4058b)) ### [`v14.2.5`](https://github.com/testing-library/user-event/releases/tag/v14.2.5) [Compare Source](testing-library/user-event@v14.2.4...v14.2.5) ##### Bug Fixes - **document:** do not track `value` on `HTMLSelectElement` ([#​989](testing-library/user-event#989)) ([77a7fa8](testing-library/user-event@77a7fa8)) ### [`v14.2.4`](https://github.com/testing-library/user-event/releases/tag/v14.2.4) [Compare Source](testing-library/user-event@v14.2.3...v14.2.4) ##### Bug Fixes - use `window.FileList` instead of implicit global ([c88865d](testing-library/user-event@c88865d)) ### [`v14.2.3`](https://github.com/testing-library/user-event/releases/tag/v14.2.3) [Compare Source](testing-library/user-event@v14.2.2...v14.2.3) ##### Bug Fixes - **document:** use setters/methods on element as default ([#​987](testing-library/user-event#987)) ([c40e614](testing-library/user-event@c40e614)) ### [`v14.2.2`](https://github.com/testing-library/user-event/releases/tag/v14.2.2) [Compare Source](testing-library/user-event@v14.2.1...v14.2.2) ##### Bug Fixes - **document:** track `HTMLInputElement.setRangeText()` ([#​984](testing-library/user-event#984)) ([73443ec](testing-library/user-event@73443ec)) ### [`v14.2.1`](https://github.com/testing-library/user-event/releases/tag/v14.2.1) [Compare Source](testing-library/user-event@v14.2.0...v14.2.1) ##### Performance Improvements - **tab:** avert visibility check on irrelevant elements ([#​967](testing-library/user-event#967)) ([d2d8a39](testing-library/user-event@d2d8a39)) ### [`v14.2.0`](https://github.com/testing-library/user-event/releases/tag/v14.2.0) [Compare Source](testing-library/user-event@v14.1.1...v14.2.0) ##### Features - report element with declaration in `pointerEventsCheck` ([#​950](testing-library/user-event#950)) ([31b7091](testing-library/user-event@31b7091)) ##### Bug Fixes - guard against selection without range ([#​953](testing-library/user-event#953)) ([ab78f3f](testing-library/user-event@ab78f3f)) - **selectOptions:** wait after each click ([#​951](testing-library/user-event#951)) ([7ea7a77](testing-library/user-event@7ea7a77)) - wait after each method before leaving `asyncWrapper` ([#​952](testing-library/user-event#952)) ([6f55fee](testing-library/user-event@6f55fee)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4xNDIuNSIsInVwZGF0ZWRJblZlciI6IjM4LjE0Mi41IiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=--> Reviewed-on: https://gitea.bruyant.xyz/alexandre/PaletteSwitcher/pulls/47 Co-authored-by: Renovate <renovate@bruyant.xyz> Co-committed-by: Renovate <renovate@bruyant.xyz>
What:
This PR changes the implementation of
getTabDestination
to only calculate the visibility of an element "just in time," rather than calculating the visibility of all elements ahead of time.(As an additional change, this also fixes elements with a
tabindex
less than-1
from being focusable.)Why:
Currently,
getTabDestination
computes the visibility of all elements ahead of time, which can be a costly operation due to callinggetComputedStyle
repeatedly. Even thoughgetComputedStyle
only takes a few milliseconds to complete, this time can add up for tests that have many calls touserEvent.tab()
or lots of focusable DOM elements.Computing the visibility only for adjacent elements in the tab order saves many calls to
getComputedStyle
which results in dramatically improved performance for calls totab
. Assuming there few hidden elements in the tab order, then this makes the visibility run in constant time rather than linear time in the number of focusable elements.Here is a rough benchmark with just tabbing between a few elements with 100 focusable buttons in the DOM.
Before:


After:
This results in a roughly ~10x (-90%) improvement in the peformance of
userEvent.tab
.Benchmark Code
How:
getTabDestination
to use new focusable utility function and check focusability in a loop until we find the next focusable element in the tab ordertabindex
of less than-1
by checking for negativetabindex
, rather than just-1
Checklist: