adobe / adobe/react-spectrum

Table keyboard navigation: scrollIntoViewport containingElement centering causes vertical viewport jumps in bounded scroll containers

Open
#10,196 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
15.9k
Forks
1.6k
Avg merge
3d 9m
Merged PRs (30d)
59

Description

### Provide a general summary of the issue here

When keyboard-navigating a React Aria `Table` inside a **bounded scroll container** (explicit `height`/`maxHeight`) with **sticky header/column** and **`scroll-padding`** on the scroll container, `scrollIntoViewport` can cause sudden **vertical viewport jumps** even though focus remains on the correct cell.

This is especially visible when:
- Moving **horizontally** along a row (ArrowLeft/ArrowRight) — `scrollTop` changes unexpectedly
- Moving **vertically** at the edges (e.g. ArrowUp from the first body row to the column header, ArrowDown near the bottom) — viewport recenters to the middle of the table instead of scrolling one row

Stock RAC Storybook stories without `scroll-padding` + sticky + bounded vertical scroll do not reproduce the issue.

https://github.com/user-attachments/assets/c7541004-8823-42d3-b68a-cf3c2c86d55f

### 🤔 Expected Behavior?

Keyboard navigation should scroll the bounded container just enough to keep the focused cell visible (respecting `scroll-padding`), without recentering the entire `` in the viewport.

### 😯 Current Behavior

`useSelectableCollection` calls `scrollIntoView(scrollContainer, element)` (padding-aware), then `scrollIntoViewport(element, { containingElement: table })`.

In `scrollIntoViewport` (`@react-aria/utils` / `react-aria` `scrollIntoView.ts`), after `targetElement.scrollIntoView({ block: 'nearest' })`, if the cell's bounding rect moved by >1px in **either** axis, the fallback runs:

```ts
containingElement?.scrollIntoView?.({ block: 'center', inline: 'center' });
targetElement.scrollIntoView?.({ block: 'nearest' });
```

Horizontal keyboard moves change left, which trips this fallback and calls table.scrollIntoView({ block: 'center', ... }), causing unwanted vertical scroll. Vertical edge navigation has the same problem when top changes.

The padding-aware scrollIntoView on the scroll container already positions the cell correctly when scroll-padding accounts for sticky regions.

Repro in StackBlitz: https://stackblitz.com/edit/github-budc6mta-fdxhpe8x?file=src%2Findex.css

### 💁 Possible Solution

`scrollIntoViewport` runs a padding-aware scroll on scroll parents, then a native `targetElement.scrollIntoView({ block: 'nearest' })`, then — if position changed by >1px in **either** axis — calls `containingElement.scrollIntoView({block: 'center', inline: 'center' })`. That last step recenters the entire table and causes vertical viewport jumps during horizontal keyboard navigation.

**Possible fix:**
1. Skip `containingElement` centering when the padding-aware scroll parent pass
already satisfied visibility (especially with `scroll-padding` for sticky UI).
2. If a containing-element fallback is still needed, only trigger it when
**vertical** position remains unsatisfactory (`originalTop` vs `newTop`), not
when only `left` changed.

**Optional:** expose `centerContainingElement?: boolean` (default `false`) for consumers who need the legacy behavior.

Related: #8977 (same fallback mechanism, different scroll context).

### 🔦 Context

We hit this building a Table with sticky header/column and scroll-padding on ResizableTableContainer to keep keyboard focus clear of pinned regions. We worked around it with a yarn patch on react-aria@3.48.0 that removes the containing-element centering fallback.

RAC version: 1.17.0 / react-aria 3.48.0

### 🖥️ Steps to Reproduce

Render a Table inside ResizableTableContainer with bounded height (e.g. maxHeight: 400px) and overflow: auto
Enable sticky header and/or sticky column
Set scroll-padding-block-start / scroll-padding-inline-start on the scroll container for sticky clearance
Keyboard-focus cells and move horizontally along a row, or ArrowUp/ArrowDown at the top/bottom of the visible area
Observe scrollTop jumping / viewport recentering while focus stays correct

### Version

react-aria-components@1.17.0 (react-aria@3.48.0)

### What browsers are you seeing the problem on?

Chrome, Safari, Firefox, Microsoft Edge

### If other, please specify.

_No response_

### What operating system are you using?

masOS

### 🧢 Your Company/Team

MaintainX

### 🕷 Tracking Issue

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.