fix(ui): keep panel scroll position through rail layout passes (#731)

The right-rail layout pass lifts the Context, CCTV, Weather and Recent
Imagery panel scrollers to measure their natural heights, which clamps
their scroll offset to zero, but it only restored scrollers tagged
data-rail-scroller. A layer that refreshes its Layers-panel row every
second snapped the Context panel back to the top while scrolling. Tag
every lifted scroller and pin the pairing with a test that reads the
measuring CSS.

Co-authored-by: Bilawal Sidhu <[email protected]>
This commit is contained in:
Bilawal SidhuandBilawal Sidhu authored and GitHub committed 2026-09-23 15:55:41 -05:00
1 parent 005254fcd1
commit 143cffd7ca
4 files changed
+36 -5

No files matched your search

+2 -1
View File
@@ -808,5 +808,6 @@
"src/ui/imageryBoxTool.test.mjs",
"src/ui/recentImagery.test.mjs",
"src/layers/recentImagery/testDoubles.mjs",
"scripts/qa-recent-imagery.mjs"
"scripts/qa-recent-imagery.mjs",
"src/ui/railScrollers.test.mjs"
]
+30
View File
@@ -0,0 +1,30 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';
const read = (path) => readFileSync(new URL(path, import.meta.url), 'utf8');
// The rail's measuring pass lifts these panel scrollers to their natural
// height, which clamps their scroll offset to zero. Only scrollers tagged
// `data-rail-scroller` get their offset restored afterwards, so every lifted
// scroller must carry the tag or a layout pass snaps it back to the top.
test('every scroller the rail measuring pass lifts restores its scroll offset', () => {
const css = read('./styles/layers.css');
const templates = [
read('./templates/context.html'),
read('./templates/layer-panels.html'),
].join('\n');
const lifted = [
...css.matchAll(
/#right-context-rail\[data-rail-measuring\]\s*>\s*\[data-panel-id\]:not\(\.collapsed\)\s*>\s*\.([a-z-]+)/g,
),
].map((match) => match[1]);
assert.ok(lifted.length > 0, 'measuring CSS lists lifted scrollers');
for (const className of lifted) {
const tag = templates.match(
new RegExp(`<div class="${className}"[^>]*>`),
)?.[0];
if (!tag) continue;
assert.match(tag, /data-rail-scroller/, `${className} restores its scroll`);
}
});
+3 -3
View File
@@ -2,7 +2,7 @@
<aside id="right-context-rail">
<div id="weather-panel" class="panel-collapsible" data-panel-id="weather-panel" hidden>
<div class="panel-glow"></div>
<div class="weather-panel-inner">
<div class="weather-panel-inner" data-rail-scroller>
<div class="panel-header">
<span class="panel-title">WEATHER</span>
<span id="weather-panel-count" aria-label="Active weather products">0</span>
@@ -14,7 +14,7 @@
</div>
<div id="recent-imagery-panel" class="panel-collapsible" data-panel-id="recent-imagery-panel" hidden>
<div class="panel-glow"></div>
<div class="recent-imagery-panel-inner">
<div class="recent-imagery-panel-inner" data-rail-scroller>
<div class="panel-header">
<span class="panel-title">RECENT IMAGERY</span>
<span id="recent-imagery-panel-count" aria-label="Imagery days in the box"></span>
@@ -26,7 +26,7 @@
</div>
<div id="global-context-panel" class="panel-collapsible collapsed" data-panel-id="global-context-panel">
<div class="panel-glow"></div>
<div class="global-context-panel-inner">
<div class="global-context-panel-inner" data-rail-scroller>
<div class="panel-header">
<span class="panel-title">CONTEXT</span>
<span class="panel-divider"></span>
+1 -1
View File
@@ -15,7 +15,7 @@
<!-- CCTV Coverage Panel -->
<div id="cctv-panel" class="panel-collapsible collapsed" data-panel-id="cctv-panel">
<div class="panel-glow"></div>
<div class="cctv-panel-inner">
<div class="cctv-panel-inner" data-rail-scroller>
<div class="panel-header">
<span class="panel-title">CCTV</span>
<span class="panel-divider"></span>