Skip to content

Improve atlas field panel styling#571

Merged
SvenVw merged 10 commits into
developmentfrom
FDM538
Apr 22, 2026
Merged

Improve atlas field panel styling#571
SvenVw merged 10 commits into
developmentfrom
FDM538

Conversation

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI commented Apr 16, 2026

Bug fixes

  • The cultivation and field count list seen on field selection atlas pages now becomes scrollable when there are
    too many different cultivations to display, ensuring that the "Sla geselecteerde percelen op" button is always reachable.

Enhancements

  • Now the clickable area around the atlas panels should be larger since some margin and padding is removed around it.

Summary by CodeRabbit

  • Bug Fixes

    • Field selection atlas pages: cultivation and field count list is now scrollable so the "Sla geselecteerde percelen op" button stays reachable.
    • More reliable field hover and selection interactions, and clearer selected-field outline/visibility.
  • Style

    • Updated field panel layout, sizing and spacing for improved readability and consistent placement.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@SvenVw has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 30 minutes and 50 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 30 minutes and 50 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c3010eea-39ac-4d1e-a73c-3c214f03ce84

📥 Commits

Reviewing files that changed from the base of the PR and between 500ad9c and 2a123b7.

📒 Files selected for processing (1)
  • fdm-app/app/components/blocks/atlas/atlas-panels.tsx
📝 Walkthrough

Walkthrough

Fields hover/selection UI was refactored to accept multiple layer IDs, query and prioritize features across those layers, render the hover panel consistently (mounted but toggled invisible when inactive), and make the fields-selection list scrollable with updated styling and an added selection-outline layer.

Changes

Cohort / File(s) Summary
Changeset Documentation
.changeset/moody-berries-wave.md
Adds a changeset marking a patch release and documents that the cultivation/field count list becomes scrollable so the “Sla geselecteerde percelen op” button remains reachable.
Atlas Panels & Interaction
fdm-app/app/components/blocks/atlas/atlas-panels.tsx
FieldsPanelHover now accepts `string[]
Atlas Styles
fdm-app/app/components/blocks/atlas/atlas-styles.tsx
Added explicit layerId === "fieldsSelected" branch to return a transparent fill layer (fill-opacity: 0) so selected fields remain clickable while invisible; positioned before fieldsSelectedOutline handling.
Atlas Route Integrations
fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx, fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx, fdm-app/app/routes/farm.create.$b_id_farm.$calendar.atlas.tsx
Replaced multiple hover panel components with single FieldsPanelHover using layer={[availableId, selectedId]} and consolidated layerExclude. Added fieldsSelectedOutline layer rendering where applicable. Removed previous Tailwind grid/gap/width classes from .fields-panel wrappers and adjusted map container height in one route.
Styling
fdm-app/app/tailwind.css
Refactored .fields-panel: fixed width 320px, max-height: 100%, box-sizing: border-box, display: flex; flex-direction: column; gap: 16px;, padding-block: 32px, and margin-left: 44px to support scrollable content.

Sequence Diagram

sequenceDiagram
    participant User
    participant Map
    participant FieldsPanelHover as FieldsPanelHover<br/>(Multi-layer)
    participant StyleEngine as StyleEngine
    participant PanelUI as PanelUI

    User->>Map: Hover / mousedown on map
    Map->>FieldsPanelHover: map event (mousedown/move)
    FieldsPanelHover->>FieldsPanelHover: Throttle / debounce handlers
    FieldsPanelHover->>Map: queryRenderedFeatures for each layerId
    Map-->>FieldsPanelHover: features per layer
    FieldsPanelHover->>FieldsPanelHover: filter valid layers, sort by priority (last first)
    FieldsPanelHover->>StyleEngine: request styles (fill/outline)
    StyleEngine-->>FieldsPanelHover: return styles (e.g. fill-opacity:0, outline)
    FieldsPanelHover->>PanelUI: update state with prioritized feature
    PanelUI->>PanelUI: render mounted Card (toggle invisible if empty), update scroll attributes
    PanelUI-->>User: show field details (scrollable)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

fdm-app, enhancement, branch:development

Suggested reviewers

  • gerardhros
  • SvenVw

Poem

🐰 I hopped across layers, one, two, three,

I sorted the last to come find me.
The panel stays mounted, but hides from the view,
When fields overflow, it scrolls for you.
Clicks and outlines — a neat little spree!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Improve atlas field panel styling' accurately reflects the main changes across the PR, which focus on refactoring panel styling, layout adjustments, and scrollable container implementations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch FDM538

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sentry
Copy link
Copy Markdown

sentry Bot commented Apr 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@BoraIneviNMI BoraIneviNMI linked an issue Apr 16, 2026 that may be closed by this pull request
4 tasks
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fdm-app/app/components/blocks/atlas/atlas-panels.tsx`:
- Around line 139-163: The cleanup must clear the pending delayed timeout and
cancel any pending throttled invocation to avoid calling setPanel with stale
state: inside the returned cleanup for the map listeners, call
clearTimeout(delayedUpdateTimeout) (guarding for undefined) and call
throttledUpdatePanel.cancel() if that method exists (throttledUpdatePanel is the
function wrapping throttledUpdatePanelInner); ensure you also clear the timeout
and cancel after removing listeners so delayedUpdatePanel, throttledUpdatePanel,
throttledUpdatePanelInner and updatePanel cannot run post-unmount.
- Around line 29-31: The effect is thrashing because the inline props layer and
layerExclude change identity each render; normalize them to stable string keys
inside the component (e.g., const layerKey = Array.isArray(layer) ?
layer.join(',') : String(layer || ''); and similarly for layerExclude) and then
use those derived keys (layerKey, layerExcludeKey) in the useEffect dependency
array and when registering/unregistering map event listeners (the effect that
uses zoomLevelFields, layer and layerExclude). Ensure you replace direct uses of
layer/layerExclude in dependencies with the stable keys and use the original
arrays only where actual array semantics are required (not in dependencies).
- Around line 41-48: The event handler in atlas-panels.tsx reads evt.point
unguarded (used in map.queryRenderedFeatures(evt.point, ...)) for both the
hover/zoom and load cases; add a type guard like "if (!('point' in evt)) return"
before any access to evt.point to avoid passing undefined to
map.queryRenderedFeatures and using stale pointer data, and ensure you call
throttledUpdatePanelInner.cancel() in the component cleanup to cancel any
pending trailing invocation of the throttled function
(throttledUpdatePanelInner) to prevent updates after unmount.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b51e56cb-57d4-4c3c-8a41-ca4b6b82429b

📥 Commits

Reviewing files that changed from the base of the PR and between ad65951 and dcd88b2.

📒 Files selected for processing (7)
  • .changeset/moody-berries-wave.md
  • fdm-app/app/components/blocks/atlas/atlas-panels.tsx
  • fdm-app/app/components/blocks/atlas/atlas-styles.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.atlas.tsx
  • fdm-app/app/tailwind.css

Comment thread fdm-app/app/components/blocks/atlas/atlas-panels.tsx
Comment thread fdm-app/app/components/blocks/atlas/atlas-panels.tsx
Comment thread fdm-app/app/components/blocks/atlas/atlas-panels.tsx
@BoraIneviNMI BoraIneviNMI marked this pull request as ready for review April 17, 2026 11:41
@BoraIneviNMI BoraIneviNMI requested a review from SvenVw April 17, 2026 14:16
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
fdm-app/app/components/blocks/atlas/atlas-panels.tsx (3)

465-478: Missing dependency array causes effect to re-run on every render.

This useEffect has no dependency array, so the scroll listener is torn down and re-registered (and a fresh setTimeout is scheduled) after every render — including every throttled panel update from the other effect. At minimum, gate by panel (the state that drives DOM creation) so the listener is (re)attached only when the scroll container is actually (re)created.

♻️ Suggested fix
     useEffect(() => {
         const scrollElement = scrollRef.current
         const scrollContainerElement = scrollContainerRef.current
         if (!scrollElement || !scrollContainerElement) return
         const handler = () => {
             handleScroll(scrollElement, scrollContainerElement)
         }
         const timeout = setTimeout(handler, 100)
         scrollElement.addEventListener("scroll", handler, { passive: true })
         return () => {
             scrollElement.removeEventListener("scroll", handler)
             clearTimeout(timeout)
         }
-    })
+    }, [panel])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/components/blocks/atlas/atlas-panels.tsx` around lines 465 - 478,
The effect attaching the scroll listener in useEffect is missing a dependency
array and thus runs on every render; update the useEffect that references
scrollRef, scrollContainerRef, and handleScroll so it only re-runs when the
DOM-backed panel changes (e.g., include panel in the dependency array) and
ensure the handler uses the current scrollRef/scrollContainerRef values; leave
the cleanup (removeEventListener and clearTimeout) as-is so the listener is only
torn down/re-attached when panel changes.

358-361: overflow-y-scroll always reserves a scrollbar track.

With overflow-y-scroll the scrollbar gutter is shown even when the list is short enough to fit without scrolling, which fights the new scroll-fade design. Prefer overflow-y-auto so the gutter (and the fade gradients gated by data-scroll-start/data-scroll-end) only appear when there is actually overflow.

♻️ Suggested fix
-                                <div
-                                    ref={scrollRef}
-                                    className="overflow-y-scroll"
-                                >
+                                <div
+                                    ref={scrollRef}
+                                    className="overflow-y-auto"
+                                >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/components/blocks/atlas/atlas-panels.tsx` around lines 358 - 361,
The container using ref scrollRef currently uses the Tailwind class
"overflow-y-scroll" which forces a scrollbar gutter even when content fits;
change that class to "overflow-y-auto" in the JSX for the div (the element with
ref={scrollRef}) so the vertical scrollbar and the scroll-fade gradients
governed by data-scroll-start/data-scroll-end only appear when content actually
overflows, leaving the rest of the markup and scrollRef usage unchanged.

46-139: Fragile TDZ-dependent closure in makePanel.

makePanel is declared inside the if (zoom > zoomLevelFields) block and closes over the outer const features (line 59), yet it is called at line 54 before features is declared. It currently works only because ternary short-circuiting avoids evaluating features[0] when feature/layer are undefined. A future refactor (e.g., always formatting the area, reading features.length, or adding a log statement) would break this silently with a TDZ ReferenceError.

Consider passing the needed feature in explicitly and decoupling makePanel from the outer features closure:

♻️ Suggested refactor
-                    if (
-                        features &&
-                        features.length > 0 &&
-                        features[0].properties
-                    ) {
-                        setPanel(
-                            makePanel({
-                                layer: features[0].layer.id,
-                                feature: features[0],
-                            }),
-                        )
-                    } else {
-                        setPanel(makePanel({}))
-                    }
-
-                    function makePanel({
-                        layer,
-                        feature,
-                    }: {
-                        layer?: string
-                        feature?: MapGeoJSONFeature
-                    }) {
-                        const active = layer && feature
-                        const name = feature
-                            ? layer === "fieldsSaved"
-                                ? features[0].properties.b_name
-                                : features[0].properties.b_lu_name
-                            : "Naam"
+                    const top = features[0]
+                    if (top?.properties) {
+                        setPanel(makePanel(top.layer.id, top))
+                    } else {
+                        setPanel(makePanel())
+                    }
+
+                    function makePanel(
+                        layer?: string,
+                        feature?: MapGeoJSONFeature,
+                    ) {
+                        const active = layer && feature
+                        const name = feature
+                            ? layer === "fieldsSaved"
+                                ? feature.properties.b_name
+                                : feature.properties.b_lu_name
+                            : "Naam"

…and use feature.properties.b_area in the description instead of features[0].properties.b_area.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/components/blocks/atlas/atlas-panels.tsx` around lines 46 - 139,
The inner makePanel function closes over the outer features array and is
defined/used in a way that can hit TDZ; refactor makePanel (and its calls) so it
does not reference the outer features variable: accept the specific feature and
layer as explicit parameters (e.g., makePanel({ layer?, feature? }) should only
read from the passed feature.properties), move or keep its declaration so it no
longer relies on the outer features closure, and update all calls (both the
empty-panel and features[0] cases) to pass either undefined or features[0]
explicitly; ensure all accesses use the passed feature (e.g.,
feature.properties.b_area, feature.properties.b_name/b_lu_name) instead of
features[0].properties.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fdm-app/app/components/blocks/atlas/atlas-panels.tsx`:
- Around line 166-180: The map 'load' handler is registered with
map.once("load", updatePanel) but not removed on early unmount; modify the
cleanup to ensure the 'load' listener is unregistered by calling map.off("load",
updatePanel) (or switch to map.on and then off) so updatePanel cannot run after
unmount; keep the existing removal of "mousemove", "mousedown", "zoom", the
clearTimeout(delayedUpdateTimeout), and throttledUpdatePanelInner.cancel()
intact.

---

Nitpick comments:
In `@fdm-app/app/components/blocks/atlas/atlas-panels.tsx`:
- Around line 465-478: The effect attaching the scroll listener in useEffect is
missing a dependency array and thus runs on every render; update the useEffect
that references scrollRef, scrollContainerRef, and handleScroll so it only
re-runs when the DOM-backed panel changes (e.g., include panel in the dependency
array) and ensure the handler uses the current scrollRef/scrollContainerRef
values; leave the cleanup (removeEventListener and clearTimeout) as-is so the
listener is only torn down/re-attached when panel changes.
- Around line 358-361: The container using ref scrollRef currently uses the
Tailwind class "overflow-y-scroll" which forces a scrollbar gutter even when
content fits; change that class to "overflow-y-auto" in the JSX for the div (the
element with ref={scrollRef}) so the vertical scrollbar and the scroll-fade
gradients governed by data-scroll-start/data-scroll-end only appear when content
actually overflows, leaving the rest of the markup and scrollRef usage
unchanged.
- Around line 46-139: The inner makePanel function closes over the outer
features array and is defined/used in a way that can hit TDZ; refactor makePanel
(and its calls) so it does not reference the outer features variable: accept the
specific feature and layer as explicit parameters (e.g., makePanel({ layer?,
feature? }) should only read from the passed feature.properties), move or keep
its declaration so it no longer relies on the outer features closure, and update
all calls (both the empty-panel and features[0] cases) to pass either undefined
or features[0] explicitly; ensure all accesses use the passed feature (e.g.,
feature.properties.b_area, feature.properties.b_name/b_lu_name) instead of
features[0].properties.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ee1ed706-fa44-4b0d-bc94-d89a8d1874e2

📥 Commits

Reviewing files that changed from the base of the PR and between dcd88b2 and 500ad9c.

📒 Files selected for processing (1)
  • fdm-app/app/components/blocks/atlas/atlas-panels.tsx

Comment thread fdm-app/app/components/blocks/atlas/atlas-panels.tsx
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw left a comment

Choose a reason for hiding this comment

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

Thanks!

@SvenVw SvenVw merged commit f0beb72 into development Apr 22, 2026
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Submit button unreachable when many different cultivations are selected

2 participants