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

Do not virtualize items around the last focused item #32646

Closed
wants to merge 13 commits into from

Conversation

NickGerleman
Copy link
Contributor

@NickGerleman NickGerleman commented Nov 23, 2021

Summary

This change also includes the contents of the active PR #32638 which adds usage of the CellRenderMask structure. That PR builds upon the CellRenderMask data structure added with #31420, and VirtualizedList coverage added with #31401 The first commit can be skipped to review this change independently.

This change makes VirtualizedList track the last focused cell, through the capture phase of onFocus. It will keep the last focus cell, and its neighbors rendered. This allows for some basic keyboard interactions, like tab/up/down when on an item out of viewport. We keep the last focused rendered even if blurred for the scenario of tabbing in and and out of the VirtualizedList.

Changelog

[General] [Fixed] - Do not virtualize items adjacent to the last focused item

Test Plan

Validated via UT (#31401), and in a complex product scenario. Here's a concrete video (of code you can try in Office Beta), where we retain keyboard focus after pagedown'ing hundreds of items away, and can resume navigation where the user had selected.

Commenting.Example.mp4

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. p: Microsoft Partner: Microsoft Partner labels Nov 23, 2021
@analysis-bot
Copy link

analysis-bot commented Nov 23, 2021

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: b66db7a
Branch: main

@analysis-bot
Copy link

analysis-bot commented Nov 23, 2021

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 7,825,063 +1,992
android hermes armeabi-v7a 7,218,263 +1,981
android hermes x86 8,138,180 +1,986
android hermes x86_64 8,116,036 +1,987
android jsc arm64-v8a 9,702,188 +1,391
android jsc armeabi-v7a 8,457,664 +1,388
android jsc x86 9,653,534 +1,391
android jsc x86_64 10,251,257 +1,387

Base commit: b66db7a
Branch: main

@NickGerleman NickGerleman changed the title Do not virtualize items adjacent to the last focused item Do not virtualize items around the last focused item Dec 8, 2021
Builds upon the `CellRenderMask` data structure added with facebook#31420, and VirtualizedList coverage added with facebook#31401.

VirtualizedList currently keeps a [first, last] range as state, tracking the region of cells to render. The render functions uses this as an input, along with a few special cases to render more (sticky headers, initial render region.)

This change moves to instead keep state which describes discontiguous render regions. This mask is continually updated as the viewport changes, batch renders expand the region, etc. Special cases are baked into the render mask, with a relatively simple tranformation from the mask to render function.

This representation makes it much easier to support keyboarding scenarios, which require keeping distinct regions (e.g. for last focused) realized while out of viewport.

MS/FB folks have a video discussion about VirtualizedList here: https://msit.microsoftstream.com/video/fe01a1ff-0400-94b1-d4f1-f1eb924b1809

facebook#31401 added quite a few snapshot tests, centering around the logic this change is touching. I manually validated RNTester FlatList examples (and their should be some upstream UI testing for them).
This change also includes the contents of facebook#32638

This change makes VirtualizedList track the last focused cell, through the capture phase of `onFocus`. It will keep the last focus cell, and its neighbors rendered. This allows for some basic keyboard interactions, like tab/up/down when on an item out of viewport. We keep the last focused rendered even if blurred for the scenario of tabbing in and and out of the VirtualizedList.

Validated via UT.
@NickGerleman NickGerleman force-pushed the realize-last-focused branch from 1bd3342 to 8437121 Compare July 20, 2022 00:12
facebook-github-bot pushed a commit that referenced this pull request Jul 27, 2022
Summary:
# This Change

react-native-community/discussions-and-proposals#335  discussed a set of problems with VirtualizedList and focus. These were seen as severe externally for a11y on desktop. The issues center on users of keyboard and accessibility tools, where users expect to be able to move focus in an uninterrupted loop.

The design and implementation evolved to be a bit more general, and without any API-surface behavior changes. It was implemented and rolled out externally as a series of changes. The remaining changes that were not upstreamed into RN are rolled into #32646

This diff brings this change into the repo, as a separate copy of VirtualizedList, to measure its impact to guardrail metrics, without yet making it the default implementation. The intention is for this to be temporary, until there is confidence the implementation is correct.

## List Implementation (more on GitHub)

This change makes it possible to synchronously move native focus to arbitrary items in a VirtualizedList. This is implemented by switching component state to a sparse bitset. This was previously implemented and upstreamed as `CellRenderMask`.

A usage of this is added, to keep the last focused item rendered. This allows the focus loop to remain unbroken, when scrolling away, or tab loops which leave/re-enter the list.

VirtualizedList tracks the last focused cell through the capture phase of `onFocus`. It will keep the cell, and a viewport above and below the last focused cell rendered, to allow movement to it without blanking (without using too much memory).

## Experimentation Implementation

A mechanism is added to gate the change via VirtualizedListInjection, mirroring the approach taken for Switch with D27381306 (683b825).

It allows VirtualizedList to delegate to a global override. It has a slight penalty to needing to import both modules, but means code which imports VirtualizedList directly is affected the changes.

Changelog:
[Internal][Added] - Add VirtualizedList_EXPERIMENTAL (CellRenderMask Usage)

Reviewed By: lunaleaps

Differential Revision: D38020408

fbshipit-source-id: ad0aaa6791f3f4455e3068502a2841f3ffb40b41
roryabraham pushed a commit to Expensify/react-native that referenced this pull request Aug 17, 2022
Summary:
# This Change

react-native-community/discussions-and-proposals#335  discussed a set of problems with VirtualizedList and focus. These were seen as severe externally for a11y on desktop. The issues center on users of keyboard and accessibility tools, where users expect to be able to move focus in an uninterrupted loop.

The design and implementation evolved to be a bit more general, and without any API-surface behavior changes. It was implemented and rolled out externally as a series of changes. The remaining changes that were not upstreamed into RN are rolled into facebook#32646

This diff brings this change into the repo, as a separate copy of VirtualizedList, to measure its impact to guardrail metrics, without yet making it the default implementation. The intention is for this to be temporary, until there is confidence the implementation is correct.

## List Implementation (more on GitHub)

This change makes it possible to synchronously move native focus to arbitrary items in a VirtualizedList. This is implemented by switching component state to a sparse bitset. This was previously implemented and upstreamed as `CellRenderMask`.

A usage of this is added, to keep the last focused item rendered. This allows the focus loop to remain unbroken, when scrolling away, or tab loops which leave/re-enter the list.

VirtualizedList tracks the last focused cell through the capture phase of `onFocus`. It will keep the cell, and a viewport above and below the last focused cell rendered, to allow movement to it without blanking (without using too much memory).

## Experimentation Implementation

A mechanism is added to gate the change via VirtualizedListInjection, mirroring the approach taken for Switch with D27381306 (facebook@683b825).

It allows VirtualizedList to delegate to a global override. It has a slight penalty to needing to import both modules, but means code which imports VirtualizedList directly is affected the changes.

Changelog:
[Internal][Added] - Add VirtualizedList_EXPERIMENTAL (CellRenderMask Usage)

Reviewed By: lunaleaps

Differential Revision: D38020408

fbshipit-source-id: ad0aaa6791f3f4455e3068502a2841f3ffb40b41
roryabraham pushed a commit to Expensify/react-native that referenced this pull request Aug 17, 2022
Summary:
# This Change

react-native-community/discussions-and-proposals#335  discussed a set of problems with VirtualizedList and focus. These were seen as severe externally for a11y on desktop. The issues center on users of keyboard and accessibility tools, where users expect to be able to move focus in an uninterrupted loop.

The design and implementation evolved to be a bit more general, and without any API-surface behavior changes. It was implemented and rolled out externally as a series of changes. The remaining changes that were not upstreamed into RN are rolled into facebook#32646

This diff brings this change into the repo, as a separate copy of VirtualizedList, to measure its impact to guardrail metrics, without yet making it the default implementation. The intention is for this to be temporary, until there is confidence the implementation is correct.

## List Implementation (more on GitHub)

This change makes it possible to synchronously move native focus to arbitrary items in a VirtualizedList. This is implemented by switching component state to a sparse bitset. This was previously implemented and upstreamed as `CellRenderMask`.

A usage of this is added, to keep the last focused item rendered. This allows the focus loop to remain unbroken, when scrolling away, or tab loops which leave/re-enter the list.

VirtualizedList tracks the last focused cell through the capture phase of `onFocus`. It will keep the cell, and a viewport above and below the last focused cell rendered, to allow movement to it without blanking (without using too much memory).

## Experimentation Implementation

A mechanism is added to gate the change via VirtualizedListInjection, mirroring the approach taken for Switch with D27381306 (facebook@683b825).

It allows VirtualizedList to delegate to a global override. It has a slight penalty to needing to import both modules, but means code which imports VirtualizedList directly is affected the changes.

Changelog:
[Internal][Added] - Add VirtualizedList_EXPERIMENTAL (CellRenderMask Usage)

Reviewed By: lunaleaps

Differential Revision: D38020408

fbshipit-source-id: ad0aaa6791f3f4455e3068502a2841f3ffb40b41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Needs: React Native Team Attention p: Microsoft Partner: Microsoft Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants