[Dashboard] Skip automatic scroll when panel is visible#233226
[Dashboard] Skip automatic scroll when panel is visible#233226cqliu1 merged 24 commits intoelastic:mainfrom
Conversation
|
Pinging @elastic/kibana-presentation (Team:Presentation) |
| const clientBottom = window.innerHeight; | ||
| const { top: panelTop, bottom: panelBottom } = panelRef.getBoundingClientRect(); | ||
|
|
||
| console.log( |
| scrollPosition = undefined; | ||
| } else { | ||
| panelRef.scrollIntoView({ block: 'start', behavior: 'smooth' }); | ||
| const dashboardTop = |
There was a problem hiding this comment.
Can we add a unit test for this?
There was a problem hiding this comment.
looks like the test file did not make the commit
…rd/skip-scroll-to-panel-in-view
…ub.com/cqliu1/kibana into dashboard/skip-scroll-to-panel-in-view
…rd/skip-scroll-to-panel-in-view
| data-test-subj="dashboardContainer" | ||
| css={styles.renderer} | ||
| ref={(e) => (dashboardContainerRef.current = e)} | ||
| ref={dashboardInternalApi.setDashboardContainerRef} |
There was a problem hiding this comment.
This will cause problems. dashboardInternalApi is loaded async and may not be available when ref is provided. To avoid race conditions, how about
ref={(e) => {
if (dashboardInternalApi) {
dashboardInternalApi.setDashboardContainerRef(dashboardInternalApi);
return;
}
dashboardContainerRef.current = e;
}}
Then when dashboardInternalApi is set, if dashboardContainerRef.current call setDashboardContainerRef
| if (dashboardInternalApi) { | ||
| dashboardInternalApi.setDashboardContainerRef(dashboardContainerRef.current); | ||
| } | ||
| }, [dashboardInternalApi, dashboardContainerRef]); |
There was a problem hiding this comment.
refs do not trigger re-renders so lets remove dashboardContainerRef from this useEffect dependency array.
dashboardContainerRef$ may already be this value if ref callback fires first. So how about checking equality before calling set.
Heenawter
left a comment
There was a problem hiding this comment.
FYI We no longer scroll to the bottom of the dashboard when adding a new section:
Screen.Recording.2025-09-02.at.12.23.00.PM.mov
…rd/skip-scroll-to-panel-in-view
…rd/skip-scroll-to-panel-in-view
|
@cqliu1 Oops! Looks like I had the following in my Ignore my above comment 🤦 |
| if (dashboardInternalApi) { | ||
| dashboardInternalApi.setDashboardContainerRef(e); | ||
| } |
There was a problem hiding this comment.
Why are we setting dashboardContainerRef twice? Do we need this if we have the useEffect above?
There was a problem hiding this comment.
The useEffect is a back up to account for a race condition where dashboardInternalApi may not be initialized when the ref is set initially. This might be redundant though. I can remove this, and we can just rely on the useEffect
There was a problem hiding this comment.
Why not just keep this and then listen for changes to dashboardContainerRef$ via usePublishingSubjects? Rather than declaring dashboardContainerRef locally - that feels confusing to me
There was a problem hiding this comment.
From earlier feedback, @nreese wanted to avoid a race condition where the dashboardInternalApi potentially isn't available when setting the ref. #233226 (comment)
There was a problem hiding this comment.
Let's just use the useEffect to set dashboardContainerRef (dependant on dashboardInternalApi) then - the double setting seems redundant to me
…rd/skip-scroll-to-panel-in-view
src/platform/plugins/shared/dashboard/public/dashboard_renderer/dashboard_renderer.tsx
Outdated
Show resolved
Hide resolved
| const dashboardContainerRef = useStateFromPublishingSubject( | ||
| dashboardInternalApi?.dashboardContainerRef$ || new BehaviorSubject(null) | ||
| ); |
There was a problem hiding this comment.
sorry - I was wrong but didn't delete my original comment in time :) see the updated comment here: #233226 (comment)
This reverts commit 04360de.
| return; | ||
| } | ||
|
|
||
| // only store ref locally if dashboardInternalApi is not yet available |
There was a problem hiding this comment.
I think this is going to cause some problems when savedObjectId changes and useEffect that loads dashboard re-runs. Here is the scenario
- load dashboard returns
dashboardInternalApiandsetDashboardApiis called. - dashboardInternalApi useEffect calls
dashboardInternalApi.setDashboardContainerRef(undefined) refreturns.dashboardInternalApi.setDashboardContainerRef(newRef)is called. dashboardContainer ref is not set.- saved object id changes, causing load dashboard to run again. Now when
dashboardInternalApiis returned andsetDashboardApiis called triggering dashboardInternalApi useEffect - dashboardContainer ref is undefined so
dashboardInternalApi.setDashboardContainerRefis not called and dashboardInteralApi does not have dashboard container ref.
Regardless of whether dashboardInteralApi has the right ref, this component should always set dashboardContainerRef.current.
💚 Build Succeeded
Metrics [docs]Async chunks
History
|
Heenawter
left a comment
There was a problem hiding this comment.
LGTM - tested locally + code review. 💃 This makes the editing experience so much better! I'm excited to see it land 🎉
nreese
left a comment
There was a problem hiding this comment.
kibana-presentation changes LGTM. Thanks for taking the extra time to clean up dashboardContainerRef usage and consolidating this into a single location
Summary
Closes #230175.
This adds a check in
dashboardApi.scrollToPanelcomparing the panel's position and viewport and skips the automatic scroll if the panel is already in view. This improves the UX by removing the slightly jarring auto scroll whenever you edit any panel, especially noticeable with any panel that supports inline editing in Dashboard.Before
After
Per @nreese feedback, I add a
dashboardContainerRefto thedashboardInternalApiso we don't have to rely on classes to retrieve the dashboard container DOM element. This way we also avoid prop drilling the container ref from thedashboardRenderercomponent and make it accessible from anywhere thedashboardApiis available.Checklist
Check the PR satisfies following conditions.
Reviewers should verify this PR satisfies this list as well.
release_note:breakinglabel should be applied in these situations.release_note:*label is applied per the guidelinesbackport:*labels.Identify risks
Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss.
Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging.