feat: add sticky inputs section to gaps and timeline pages - #1528
feat: add sticky inputs section to gaps and timeline pages#1528eran132 wants to merge 8 commits into
Conversation
Created a reusable StickyInputs component that keeps the filter controls visible when scrolling down through results. Applied to the gaps page and historic timeline page where long result tables cause the inputs to scroll out of view. Closes #1059 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a reusable sticky header wrapper for input controls and applies it to the Gaps and Historic Timeline pages so the filters remain visible while scrolling.
Changes:
- Created a reusable
StickyInputsstyled-component wrapper. - Wrapped the input/filter sections on the historic timeline and gaps pages with
StickyInputs. - Adjusted layout structure so only the input section is sticky while results/content scroll.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/pages/historicTimeline/index.tsx |
Wrapes the filter inputs in StickyInputs to keep them visible while scrolling the timeline content. |
src/pages/gaps/index.tsx |
Wrapes the filter inputs in StickyInputs so filters stay visible above the gaps table. |
src/pages/components/StickyInputs.tsx |
Introduces the reusable sticky wrapper component (positioning, layering, background). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import OperatorSelector from 'src/pages/components/OperatorSelector' | ||
| import RouteSelector from 'src/pages/components/RouteSelector' | ||
| import { Row } from 'src/pages/components/Row' | ||
| import { StickyInputs } from 'src/pages/components/StickyInputs' |
There was a problem hiding this comment.
Import style for StickyInputs is inconsistent across pages: this file uses an absolute src/... import while src/pages/gaps/index.tsx uses a relative ../components/StickyInputs. Standardizing on one convention improves consistency and avoids path-alias-only builds causing import churn (and can prevent accidental duplicate module instances in some setups).
| import { StickyInputs } from 'src/pages/components/StickyInputs' | |
| import { StickyInputs } from '../components/StickyInputs' |
| z-index: 100; | ||
| background: inherit; | ||
| padding-bottom: 8px; |
There was a problem hiding this comment.
The z-index: 100 and padding-bottom: 8px are hard-coded magic numbers. Consider deriving these from the app/theme system (e.g., theme spacing and z-index scale) so stacking and spacing stay consistent across pages and don’t require manual tuning when global layout changes.
| z-index: 100; | |
| background: inherit; | |
| padding-bottom: 8px; | |
| z-index: var(--z-index-sticky-inputs, 100); | |
| background: inherit; | |
| padding-bottom: var(--spacing-sticky-inputs-bottom, 8px); |
| position: sticky; | ||
| top: 0; | ||
| z-index: 100; | ||
| background: inherit; |
There was a problem hiding this comment.
background: inherit can resolve to transparent if the parent doesn’t set a background, which can reduce contrast/readability when the sticky section overlays scrolling content. Prefer setting an explicit background color (typically the page/surface background from the theme) and optionally a subtle border/shadow to clearly separate the sticky inputs from the content underneath.
| background: inherit; | |
| background: #fff; | |
| border-bottom: 1px solid rgba(0, 0, 0, 0.08); |
# Conflicts: # src/pages/historicTimeline/index.tsx
background: inherit resolved to transparent (PageContainer has no background), so scrolling results bled through the pinned inputs in both light and dark mode. Use MUI theme palette.background.default so the sticky bar matches the page background ScopedCssBaseline paints. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@NoamGaash — verified the layout manually as you asked, and good thing I did: while testing the scroll behavior with real data loaded on both Fixed in 7261786: Verified after the fix, both pages, both themes, RTL:
Also rebased on main — there was a conflict in tsc + eslint + tests all green. Re-requesting review. |
# Conflicts: # src/pages/gaps/index.tsx # src/pages/historicTimeline/index.tsx
|
Resolved the conflict with main (commit 11b0992). Main had refactored the timeline + gaps pages substantially (new `date`/`timeOfDay` state, timezone-aware `dayjs.tz(date, ISRAEL_TIMEZONE)`, `?? undefined` null-safety, `search.stopKey` from context, new computed `time` for `TimelineBoard`). I kept the `` wrapper around the input rows, adopted all of main's improvements inside it, and kept the hits-timeline outside the wrapper so results still scroll under the pinned bar (the whole point of this PR). Net diff vs current main is still small and focused: 3 files (+162/-141) — `StickyInputs.tsx`, `gaps/index.tsx`, `historicTimeline/index.tsx`. Verified: `tsc` ✅, full lint ✅, unit tests 64/64 ✅, plus Playwright + visual review of sticky-scroll behavior on timeline (light + dark) and gaps (light) — inputs stay pinned at top with opaque themed background (the bug fixed earlier in this PR), results scroll cleanly underneath, all inputs populate correctly with main's new state shape. |
NoamGaash
left a comment
There was a problem hiding this comment.
seems like there are few visual regressions
NoamGaash flagged a visual regression on the gaps page: the always-opaque sticky bar showed as a faint container box around the inputs at rest, because StickyInputs' background.default didn't match the surrounding PageContainer color exactly on that page. Fix: paint the background ONLY when the scrollable ancestor has actually scrolled (scrollTop > 0). That's exactly when content would otherwise bleed through the sticky bar (the original #1528 fix). At rest there's nothing behind the bar to hide, so transparency matches the pre-PR visual baseline pixel-for-pixel. Detection walks up the DOM for the nearest scrollable ancestor (overflowY auto/scroll/overlay) and listens to its scroll. This works for this codebase's inner-scrollable layout; falls back to window scroll if none found. Logic also asserted via Playwright (data-stuck='true' after wheel). Verified visually: gaps-dark at rest is now pixel-identical to main (no box); timeline-dark scrolled has solid bg with no bleed-through (station name reads cleanly, no timestamps overlapping the Hebrew text). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@NoamGaash you were right — there was a real regression. Investigated and fixed in 27fba87. What was wrong: the always-opaque sticky bar (which I'd added earlier to stop scrolling timeline content from bleeding through it) was being painted with The fix: conditional opacity. The bar is transparent at rest, becomes opaque only when its scrollable ancestor has actually scrolled (
Detection walks up the DOM for the nearest scrollable ancestor (overflow-y auto/scroll/overlay) and listens to its scroll event. Works for this codebase's inner-scrollable layout; falls back to window scroll if no inner scroller is found. Verified locally (via my own Playwright + frame inspection, since I don't have Applitools access):
Re-requesting review. Applitools should also be happy this time — at-rest baselines should match. Let me know if the diffs look right on your end. |
|
what's your email address? I'll add you. |

Re-submitted from upstream branch (previously #1513).
Summary
Created reusable StickyInputs component. Applied to gaps and timeline pages.
Closes #1059
Test plan
🤖 Generated with Claude Code