Skip to content

feat(super-editor): ui.viewport scroll-container access + sidebar-toggle invalidation (SD-3324, SD-3326)#3581

Open
caio-pizzol wants to merge 2 commits into
mainfrom
caio/viewport-scroll-container-and-sidebar-observe
Open

feat(super-editor): ui.viewport scroll-container access + sidebar-toggle invalidation (SD-3324, SD-3326)#3581
caio-pizzol wants to merge 2 commits into
mainfrom
caio/viewport-scroll-container-and-sidebar-observe

Conversation

@caio-pizzol
Copy link
Copy Markdown
Contributor

Two small additions to the ui.viewport surface for overlay consumers, one commit per work item.

ui.viewport.getScrollContainer() (SD-3324) exposes the editor's resolved scroll container — the scrollable ancestor of the painted host (sometimes the host itself). getHost() returns the painted host, which is usually NOT the element that scrolls, so overlay code that attached scroll listeners there never fired. Returns HTMLElement | null; null means the document/window scrolls (fall back to window). Also fixes the visibleHost JSDoc that wrongly described it as the scroll container.

ui.viewport.observe now fires on sidebar-toggle (SD-3326). Toggling the comments rail shifts document geometry without a guaranteed layout repaint, so cached overlay rects went stale silently. This bridges the existing sidebar-toggle signal into a geometry invalidation, reusing the layout reason.

Closes the IT-1109 and IT-1111 follow-ups.

Review: getScrollContainer's HTMLElement | null contract (null = window scrolls, documented), and reusing the layout observe reason for the sidebar bridge rather than adding a new public reason.
Verified: vitest src/ui/viewport.test.ts → 31 passed; check:public:superdoc → 13/13 PASS; browser — getScrollContainer returns the real scroller, distinct from the host.

Exposes the editor's resolved scroll container — the scrollable ancestor
of the painted host (occasionally the host itself) — via a public
`scrollContainer` getter on PresentationEditor and `ui.viewport.getScrollContainer()`.
Overlay consumers need the element SuperDoc actually scrolls to attach
scroll listeners and measure against; getHost() returns the painted host,
which is usually not the scroller. Returns HTMLElement | null, with null
meaning the document/window scrolls (fall back to window). Also corrects
the visibleHost JSDoc that wrongly described it as the scroll container.
@caio-pizzol caio-pizzol requested a review from a team as a code owner May 29, 2026 21:28
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 29, 2026

SD-3324

SD-3326

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aca767aaa4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

* @returns The scroll container element, or `null` when the window scrolls
*/
get scrollContainer(): HTMLElement | null {
return this.#scrollContainer instanceof HTMLElement ? this.#scrollContainer : null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept iframe-owned scroll elements

When SuperDoc is mounted in a different browsing context, such as an iframe, the resolved scroll container is an element from that iframe's realm, so instanceof HTMLElement against the parent/global constructor is false. In that case ui.viewport.getScrollContainer() returns null, causing overlay consumers to fall back to the wrong window and miss scroll events/measurements even though an element scroller exists. Use the container's ownerDocument.defaultView?.HTMLElement (or a structural element check) so cross-realm HTML elements are returned.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

cubic analysis

No issues found across 4 files

Linked issue analysis

Linked issue: SD-3324: Add ui.viewport.getScrollContainer for the editor's resolved scroll element

Status Acceptance criteria Notes
Expose ui.viewport.getScrollContainer() that returns the resolved scroll container (HTMLElement) or null when the document/window scrolls. The PR adds a PresentationEditor.scrollContainer getter, exposes getScrollContainer() on the UI handle, and updates types to include the new API. Tests verify the call returns the mounted scroller, null when window scrolls, and null when no editor is mounted.
Bridge superdoc 'sidebar-toggle' into ui.viewport.observe as a geometry invalidation using reason 'layout' so observers re-query cached rects. The PR wires a sidebar-toggle handler to scheduleGeometry('layout') and registers/unregisters the listener with superdoc events. A unit test fires 'sidebar-toggle' and expects an observe notification with reason 'layout'.
Clarify visibleHost JSDoc to note it is the painted host and not necessarily the scroll container. The PresentationEditor.visibleHost JSDoc was updated to explicitly say the painted host is not necessarily the scroll container and points consumers to use scrollContainer for scroll listeners.

Tip: cubic could auto-approve low-risk PRs like this, if it thinks it's safe to merge. Learn more

Re-trigger cubic

…D-3326)

ui.viewport.observe now fires when the comments rail opens or closes.
The rail toggle shifts document geometry without a guaranteed layout
repaint, so overlay consumers' cached rects would silently go stale.
Bridges the existing sidebar-toggle signal into a geometry invalidation,
reusing the 'layout' reason (consumers only re-query, so no new public
reason is added).
@caio-pizzol caio-pizzol force-pushed the caio/viewport-scroll-container-and-sidebar-observe branch from aca767a to 4578ed8 Compare May 29, 2026 21:50
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

2 participants