[lexical-table][lexical] Feature: Sticky scrollbar for overflowing tables#8790
[lexical-table][lexical] Feature: Sticky scrollbar for overflowing tables#8790mayrang wants to merge 4 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…bles Add a viewport-sticky horizontal scrollbar for tables that overflow their container. The scrollbar tracks the viewport bottom via position: sticky and syncs bidirectionally with the native scroll position. DOM structure: outerWrapper > scrollableWrapper + stickyScrollbar (sibling, not nested, because overflow-x traps position: sticky). New theme key: tableStickyScrollbar on EditorThemeClasses. Closes facebook#7826
…lbar DOM structure Strip stickyScrollbar from actual HTML in assertHTMLOnPageOrFrame (dynamic spacer width), update wrapTableHtml for outerWrapper > scrollableWrapper nesting, fix WRAPPER depth and nthTableSelector for ComponentPicker and Tables specs.
…for outerWrapper Add WRAPPER spread for inner table paths in nested table selection tests, update regression facebook#6870 WRAPPER depth.
fd7ff98 to
e498e66
Compare
etrepum
left a comment
There was a problem hiding this comment.
I think this is a breaking change to the Table's rendered DOM, and it doesn't look like it's opt-in?
| return wrapper; | ||
| } | ||
|
|
||
| function createStickyScrollbar( |
There was a problem hiding this comment.
Looks like this should be renamed to $createStickyScrollbar per the lint rules
There was a problem hiding this comment.
Done — renamed both createStickyScrollbar → $createStickyScrollbar and createScrollableWrapper → $createScrollableWrapper.
| ); | ||
|
|
||
| queueMicrotask(() => syncStickyScrollbar(scrollbar, tableElement)); | ||
| if (typeof ResizeObserver !== 'undefined') { |
There was a problem hiding this comment.
Originally added as a JSDOM guard — JSDOM doesn't provide ResizeObserver, so unit tests that call createDOM directly would throw. But since hasStickyScrollbar is opt-in now, $createStickyScrollbar only runs when explicitly enabled, so the guard is unnecessary. Removed it.
…rollbar config Address review feedback: add hasStickyScrollbar (default false) to TableConfig so the sticky scrollbar is fully opt-in. Rename createScrollableWrapper/createStickyScrollbar to $ prefix per lint rules. Remove typeof ResizeObserver guard (unnecessary with opt-in). Only hide native scrollbar when sticky scrollbar is active.
|
You're right — added |
Description
When a table is wider than its container and taller than the viewport, the native horizontal scrollbar sits at the very bottom of the table — users have to scroll all the way down to reach it. This PR adds a sticky scrollbar that stays at the bottom of the viewport while the table is in view.
What changed
The
createDOMpath for scrollable tables now produces a three-layer structure:outerWrapper > scrollableWrapper + stickyScrollbar. The scrollableWrapper's native scrollbar is hidden via CSS, and the stickyScrollbar — a sibling div withposition: sticky; bottom: 0— provides a replacement that tracks the viewport. A spacer element inside the scrollbar matches the table'sscrollWidth, and bidirectional scroll listeners keep the two in sync.The stickyScrollbar must be a sibling of the scrollableWrapper (not inside it) because
overflow-x: auto/scrollcreates a scroll container that trapsposition: sticky.A
ResizeObservertracks size changes to update the scrollbar width and toggle visibility.setDOMUnmanaged()prevents the reconciler from touching the scrollbar DOM.Theme
New theme key
tableStickyScrollbarfor the scrollbar element. The playground theme hides the native scrollbar on the wrapper (scrollbar-width: none+::-webkit-scrollbar { display: none }) and styles the sticky scrollbar with::-webkit-scrollbarpseudo-elements for Chrome/Safari. Firefox uses the OS overlay scrollbar.EditorThemeClasses.tableStickyScrollbaradded tolexical.Closes #7826
Test plan
pnpm vitest run --project unit -- LexicalTableNode— 38 tests passwrapTableHtmlhelper updated for the new outerWrapper + stickyScrollbar structure