Skip to content

fix(split-view): use real checkboxes in the secondary pane layer menu (#840)#865

Merged
giswqs merged 2 commits into
mainfrom
fix/issue-840-pane-checkboxes
Jun 25, 2026
Merged

fix(split-view): use real checkboxes in the secondary pane layer menu (#840)#865
giswqs merged 2 commits into
mainfrom
fix/issue-840-pane-checkboxes

Conversation

@giswqs

@giswqs giswqs commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary

Addresses the "real checkboxes for map views" request in #840 (Issue 1).

In a split view, each secondary map pane has a Layer visibility dropdown so it can show a different subset of the shared layers. That dropdown drew only a bare checkmark next to selected layers, with nothing in front of unselected ones. As reported, this breaks the checkbox design language the rest of the app uses for layer selection.

This PR makes every layer in that menu show a real checkbox: an empty bordered box when the layer is off, a filled box with a checkmark when it is on.

Changes

  • packages/uiDropdownMenuCheckboxItem gains an optional indicator="box" prop that renders an always-visible checkbox box (border when unchecked, filled + check when checked). The default ("check") is unchanged, so the menu's other callers (View menu, Settings, Attribute Table) keep the existing checkmark style.
  • apps/geolibre-desktop — the per-pane layer-visibility menu in MapGrid.tsx opts into indicator="box".

Scope

Limited to the checkbox UI (Issue 1). The split-view layer-stack-order and independent top-pane controls raised elsewhere in the issue are separate and not in scope here.

Verification

Drove the real app in a browser (two XYZ layers, two-column split view) and confirmed in both light and dark themes that each layer in the pane menu now renders a real checkbox, toggling between empty box and filled-with-check.

Fixes #840

Summary by CodeRabbit

  • New Features

    • Added a new indicator option for checkbox items in dropdown menus, supporting a boxed indicator style.
    • Updated layer visibility toggles to use the boxed indicator for clearer selection feedback.
  • Bug Fixes

    • Improved the visual rendering of checked and indeterminate states in dropdown checkbox menus, making selected items easier to identify.

…#840)

The per-pane layer-visibility dropdown drew only a bare checkmark for
selected layers, which broke the checkbox design language used elsewhere
for layer selection. Add an opt-in box-style indicator to
DropdownMenuCheckboxItem and use it in the pane menu so every layer shows
a real checkbox (empty when off, filled with a check when on). The default
checkmark style is unchanged for the menu's other callers.
@netlify

netlify Bot commented Jun 25, 2026

Copy link
Copy Markdown

Deploy Preview for geolibre-app ready!

Name Link
🔨 Latest commit fd42de3
🔍 Latest deploy log https://app.netlify.com/projects/geolibre-app/deploys/6a3d1d7a3f35cb000842f4df
😎 Deploy Preview https://deploy-preview-865--geolibre-app.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 366b1c50-7189-4c86-8a21-e4f9e7de0eef

📥 Commits

Reviewing files that changed from the base of the PR and between 42927ba and fd42de3.

📒 Files selected for processing (1)
  • packages/ui/src/components/dropdown-menu.tsx

📝 Walkthrough

Walkthrough

The shared dropdown checkbox component now supports a box-style indicator variant. The split-view layer toggle uses that variant for its layer visibility items.

Changes

Checkbox indicator variant

Layer / File(s) Summary
Dropdown checkbox indicator
packages/ui/src/components/dropdown-menu.tsx
DropdownMenuCheckboxItem accepts an indicator prop and renders either the existing checkmark path or a box-style checked indicator.
Layer toggle uses box indicator
apps/geolibre-desktop/src/components/layout/MapGrid.tsx
PaneLayerToggle passes indicator="box" to each layer visibility dropdown item.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A bunny hopped with twitchy nose,
To see the checkbox path it chose.
One boxy tick, one checkmark light,
Now layer menus feel just right.
🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: using checkbox-style indicators in the split-view secondary pane layer menu.
Linked Issues check ✅ Passed The PR satisfies Issue 1 by switching the split-view layer menu to box-style checkbox indicators, matching the requested UI.
Out of Scope Changes check ✅ Passed The added UI prop support is required to render the new checkbox style, and no unrelated behavior changes are evident.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-840-pane-checkboxes

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: one or more packages not found in the registry.


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.

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

⚡ Cloudflare Pages preview

Item Value
Preview https://af85cdef.geolibre-preview.pages.dev
Demo app https://af85cdef.geolibre-preview.pages.dev/demo/
Commit ad6ccc4

Comment thread packages/ui/src/components/dropdown-menu.tsx Outdated
Comment thread packages/ui/src/components/dropdown-menu.tsx Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Code review

This is a small, well-scoped UI change. The core approach is sound: adding an indicator prop to DropdownMenuCheckboxItem with a backward-compatible default keeps all existing call sites untouched, and opting into "box" only in MapGrid.tsx is exactly the right scope. No regressions were found in the existing "check" path. The visual logic (border + fill + check icon when checked === true, bare border otherwise) is correct for the current caller.

Two findings worth addressing:


CLAUDE.md (Medium confidence)

Multi-line comment block on indicator prop — CLAUDE.md says "Never write multi-paragraph docstrings or multi-line comment blocks — one short line max." The new 4-line JSDoc block violates this. A single inline comment or the bare union type is preferred (see inline suggestion).


Quality / Edge case (Medium confidence)

"indeterminate" not visually handled in "box" mode — Radix types checked as boolean | "indeterminate". The "box" branch checks checked === true for both the class and the icon, so "indeterminate" renders identically to false. This is not a live bug — MapGrid.tsx always computes a plain boolean — but the component's public API admits "indeterminate" and a future caller could pass it expecting distinct rendering. A dash/minus icon for the indeterminate case would close this gap (see inline suggestion).


Bugs — none found

Security — none found

Performance — none found

What I checked: the full diff; surrounding context in dropdown-menu.tsx and MapGrid.tsx; Radix CheckboxItem prop types; all existing callers of DropdownMenuCheckboxItem in the desktop app (AttributeTable.tsx, SettingsDialog.tsx, ViewMenu.tsx); the layer.visible / layerVisibility types in @geolibre/core (both are boolean).

- Condense the `indicator` prop's JSDoc from a 4-line block to a single line.
- Handle the `"indeterminate"` checked state in the box indicator (a half-filled
  box with a dash) so the public API no longer conflates it with `false`.
Comment on lines +131 to +147
{indicator === "box" ? (
<span
className={cn(
"absolute left-2 flex h-4 w-4 items-center justify-center rounded-[4px] border transition-colors",
checked === true
? "border-primary bg-primary text-primary-foreground"
: checked === "indeterminate"
? "border-primary bg-primary/40 text-primary-foreground"
: "border-input",
)}
>
{checked === true ? (
<Check className="h-3 w-3" />
) : checked === "indeterminate" ? (
<span className="h-0.5 w-2 rounded-full bg-current" />
) : null}
</span>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug (medium confidence): indicator="box" silently breaks in uncontrolled mode

The "box" branch reads visual state directly from the checked prop, bypassing DropdownMenuPrimitive.ItemIndicator. When a caller uses uncontrolled mode (passing defaultChecked instead of checked), checked will be undefined here and the box will always render empty—Radix manages the internal checked state but the visual never updates.

The "check" path avoids this because ItemIndicator is wired to Radix's internal state and works in both modes.

Mitigations (pick one):

  1. Guard against uncontrolled usage in the JSDoc / TypeScript so callers know indicator="box" requires a controlled checked:
Suggested change
{indicator === "box" ? (
<span
className={cn(
"absolute left-2 flex h-4 w-4 items-center justify-center rounded-[4px] border transition-colors",
checked === true
? "border-primary bg-primary text-primary-foreground"
: checked === "indeterminate"
? "border-primary bg-primary/40 text-primary-foreground"
: "border-input",
)}
>
{checked === true ? (
<Check className="h-3 w-3" />
) : checked === "indeterminate" ? (
<span className="h-0.5 w-2 rounded-full bg-current" />
) : null}
</span>
{indicator === "box" ? (
// NOTE: "box" mode reads `checked` directly and only works when `checked`
// is explicitly controlled (not with `defaultChecked`).
<span
aria-hidden="true"
className={cn(
"absolute left-2 flex h-4 w-4 items-center justify-center rounded-[4px] border transition-colors",
checked === true
? "border-primary bg-primary text-primary-foreground"
: checked === "indeterminate"
? "border-primary bg-primary/40 text-primary-foreground"
: "border-input",
)}
>
{checked === true ? (
<Check className="h-3 w-3" />
) : checked === "indeterminate" ? (
<span className="h-0.5 w-2 rounded-full bg-current" />
) : null}
</span>
  1. Or add a checked-state ref/state and sync it with Radix's onCheckedChange to handle uncontrolled mode (more involved).

The aria-hidden="true" addition in the suggestion above is also recommended regardless — the outer <span> is a decorative indicator and the parent menuitemcheckbox already carries the semantic state via aria-checked; leaving the span un-hidden can cause some screen readers to double-announce the item.

React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> & {
/** `"check"` (default): bare checkmark when checked. `"box"`: always-visible bordered square that fills on check. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit (quality): The JSDoc describes visual behaviour but doesn't flag the controlled-only constraint. Adding a note here makes the limitation discoverable at the call site:

Suggested change
/** `"check"` (default): bare checkmark when checked. `"box"`: always-visible bordered square that fills on check. */
/** `"check"` (default): bare checkmark when checked. `"box"`: always-visible bordered square that fills on check. `indicator="box"` requires a controlled `checked` prop (not `defaultChecked`). */

@github-actions

Copy link
Copy Markdown
Contributor

Code review

Reviewed the two changed files (packages/ui/src/components/dropdown-menu.tsx, apps/geolibre-desktop/src/components/layout/MapGrid.tsx) plus all other call sites of DropdownMenuCheckboxItem (AttributeTable, ViewMenu, SettingsDialog).


Bugs

Finding Confidence
indicator="box" silently misbehaves in uncontrolled mode. The new branch reads visual state directly from the checked prop, bypassing DropdownMenuPrimitive.ItemIndicator. When defaultChecked is used instead of checked, checked is undefined and the box always renders empty even as Radix toggles internal state. All current callers happen to pass a controlled checked, so no user-visible regression today — but it's a latent trap for future callers of the shared UI library. See inline comment on lines 131–147. Medium

Quality

Finding Confidence
Decorative indicator <span> lacks aria-hidden="true". The outer <span> that forms the custom checkbox box is not hidden from the accessibility tree. The parent menuitemcheckbox element already carries aria-checked (set by Radix), so the span is purely decorative. Lucide-react ≥0.365 does add aria-hidden to SVGs, but the outer span wrapper is still un-hidden and may be traversed by some AT. Adding aria-hidden="true" to the span is low-cost and removes the ambiguity. Covered in the same inline comment. Medium
JSDoc doesn't document the controlled-only constraint. Future callers reading the prop description have no indication that indicator="box" requires a controlled checked prop. Small one-liner fix suggested in the inline comment on line 118. Low

Security / Performance / CLAUDE.md

Nothing to flag. The change is purely UI/visual, adds no external I/O, and no direct-to-main commit. Backward compatibility with all existing callers ("check" default) is preserved correctly.

@giswqs giswqs merged commit 4fac78c into main Jun 25, 2026
23 checks passed
@giswqs giswqs deleted the fix/issue-840-pane-checkboxes branch June 25, 2026 12:29
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.

UX Report: Split-View Layer Management Disconnect and Rendering Flaws - Also Feature request

1 participant