Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion apps/geolibre-desktop/src/components/panels/LayerPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3210,6 +3210,10 @@ export function LayerPanel({
aria-pressed={selectedLayerIds.has(layer.id)}
onClick={(e) => handleLayerSelection(e, layer.id)}
onKeyDown={(e) => {
// Only act on the card itself: preventDefault here would
// otherwise cancel the Enter activation of the action
// buttons nested inside it.
if (e.target !== e.currentTarget) return;
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setSelectedLayerIds(new Set([layer.id]));
Expand Down Expand Up @@ -3389,7 +3393,7 @@ export function LayerPanel({
onChange={(v) => setLayerOpacity(layer.id, v)}
/>
)}
<div className="mt-2 flex gap-1">
<div className="mt-2 flex flex-wrap gap-1">
<Button
variant="ghost"
size="icon"
Expand Down Expand Up @@ -3449,6 +3453,22 @@ export function LayerPanel({
>
<MousePointerClick className="h-3.5 w-3.5" />
</Button>
{onOpenStylePanel && (
<Button
variant="ghost"
size="icon"
className="h-7 w-7"
title={t("layers.openStylePanel")}
aria-label={t("layers.openStylePanel")}
onClick={(e) => {
e.stopPropagation();
selectLayer(layer.id);
onOpenStylePanel();
}}
>
<Palette className="h-3.5 w-3.5" />
</Button>
)}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
Expand Down
1 change: 1 addition & 0 deletions docs/user-guide/layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The **Layers panel** on the left lists every layer in the project, from the topm

Each layer exposes a set of actions:

- **Open Style panel**: when the built-in Style panel is enabled, use the palette button on the layer card to select the layer and open its styling controls.
- **Zoom to layer**: fit the map to the layer's extent (for layers whose bounds are known).
- **Identify features**: click features on the map to see their attributes in a popup. On a raster layer this reads the pixel value instead, and on a multiband raster it also builds a [spectral profile](styling.md#spectral-profile).
- **Labels**: toggle text labels for vector layers that have a label field.
Expand Down
43 changes: 43 additions & 0 deletions e2e/layer-panel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,46 @@ test("long layer names truncate without widening the layer panel", async ({ page
.poll(() => name.evaluate((element) => element.scrollWidth > element.clientWidth))
.toBe(true);
});

test("opens the selected layer in the Style panel from its card", async ({ page }) => {
await page.setViewportSize({ width: 768, height: 720 });
await waitForMap(page);
await dropGeoJson(page, "first", FIXTURE_TEXT);
await expect(layerRow(page, "first")).toBeVisible();
await dropGeoJson(page, "second", FIXTURE_TEXT);
await expect(layerRow(page, "second")).toBeVisible();

// Exact, so the collapsed rail ("Layer style (collapsed)") cannot satisfy the
// default substring match.
const stylePanel = page.getByRole("complementary", { name: "Layer style", exact: true });
await expect(stylePanel).toHaveCount(0);
Comment thread
giswqs marked this conversation as resolved.

await layerRow(page, "first").getByRole("button", { name: "Open Style panel" }).click();

await expect(stylePanel).toBeVisible();
await expect(stylePanel.getByText("Style - first", { exact: true })).toBeVisible();
});

test("opens the Style panel from the layer card by keyboard", async ({ page }) => {
await page.setViewportSize({ width: 768, height: 720 });
await waitForMap(page);
await dropGeoJson(page, "first", FIXTURE_TEXT);
await expect(layerRow(page, "first")).toBeVisible();
await dropGeoJson(page, "second", FIXTURE_TEXT);
await expect(layerRow(page, "second")).toBeVisible();

const stylePanel = page.getByRole("complementary", { name: "Layer style", exact: true });
const styleButton = (name: string) =>
layerRow(page, name).getByRole("button", { name: "Open Style panel" });

// The card is a role="button" wrapper; its key handler must not swallow the
// activation of the action buttons nested inside it.
await styleButton("first").focus();
await page.keyboard.press("Enter");
await expect(stylePanel).toBeVisible();
await expect(stylePanel.getByText("Style - first", { exact: true })).toBeVisible();

await styleButton("second").focus();
await page.keyboard.press("Space");
await expect(stylePanel.getByText("Style - second", { exact: true })).toBeVisible();
});
Loading