fix: keep headers visible when scrolling left panes (#1485)

Keeps headers visible when scrolling left panes as described in the
[issue 951
](https://github.com/maplibre/maputnik/issues/951)

The fix was manually confirmed to be working, see video below.

## Before (taken from the issue)
<img width="713" height="231" alt="image"
src="https://github.com/user-attachments/assets/c1eadb0d-6dbf-4199-8732-68b07d626003"
/>


## After

https://github.com/user-attachments/assets/ab5e4a6f-c5f9-44fd-850d-8eac58c35c68

---------

Co-authored-by: Harel M <harel.mazor@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Jere Suikkila
2025-11-04 02:11:13 -05:00
committed by GitHub
parent 46f0d7620d
commit 5b34a3791f
7 changed files with 63 additions and 6 deletions

View File

@@ -22,6 +22,7 @@
- Fix missing spec info when clicking next to a property
- Fix Firefox open file that stopped working due to react upgrade
- Fix issue with missing bottom error panel
- Fixed headers in left panes (Layers list and Layer editor) to remain visible when scrolling
- _...Add new stuff here..._
## 3.0.0

View File

@@ -16,6 +16,7 @@ export default defineConfig({
return config;
},
baseUrl: "http://localhost:8888",
scrollBehavior: "center",
retries: {
runMode: 2,
openMode: 0,

View File

@@ -257,4 +257,24 @@ describe("layer editor", () => {
then(get.element(".cm-lint-marker-error")).shouldExist();
});
});
describe("sticky header", () => {
it("should keep layer header visible when scrolling properties", () => {
// Setup: Create a layer with many properties (e.g., symbol layer)
when.modal.fillLayers({
type: "symbol",
layer: "example",
});
when.wait(500);
const header = get.elementByTestId("layer-editor.header");
then(header).shouldBeVisible();
get.element(".maputnik-scroll-container").scrollTo("bottom", { ensureScrollable: false });
when.wait(200);
then(header).shouldBeVisible();
then(get.elementByTestId("skip-target-layer-editor")).shouldBeVisible();
});
});
});

View File

@@ -515,4 +515,27 @@ describe("layers list", () => {
});
});
});
describe("sticky header", () => {
it("should keep header visible when scrolling layer list", () => {
// Setup: Create multiple layers to enable scrolling
for (let i = 0; i < 20; i++) {
when.modal.open();
when.modal.fillLayers({
id: `layer-${i}`,
type: "background",
});
}
when.wait(500);
const header = get.elementByTestId("layer-list.header");
then(header).shouldBeVisible();
// Scroll the layer list container (use ensureScrollable: false to avoid flakiness)
get.elementByTestId("layer-list").scrollTo("bottom", { ensureScrollable: false });
when.wait(200);
then(header).shouldBeVisible();
then(get.elementByTestId("layer-list:add-layer")).shouldBeVisible();
});
});
});

View File

@@ -370,7 +370,7 @@ class LayerEditorInternal extends React.Component<LayerEditorInternalProps, Laye
aria-label={t("Layer editor")}
data-wd-key="layer-editor"
>
<header>
<header data-wd-key="layer-editor.header">
<div className="layer-header">
<h2 className="layer-header__title">
{t("Layer: {{layerId}}", { layerId: formatLayerId(this.props.layer.id) })}

View File

@@ -289,7 +289,7 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
onOpenToggle={this.toggleModal.bind(this, "add")}
onLayersChange={this.props.onLayersChange}
/>
<header className="maputnik-layer-list-header">
<header className="maputnik-layer-list-header" data-wd-key="layer-list.header">
<span className="maputnik-layer-list-header-title">{t("Layers")}</span>
<span className="maputnik-space" />
<div className="maputnik-default-property">

View File

@@ -5,15 +5,20 @@
// LAYER LIST
.maputnik-layer-list {
height: 100%;
overflow: auto;
overflow-y: auto;
overflow-x: hidden;
display: flex;
flex-direction: column;
&-header {
padding: vars.$margin-2 vars.$margin-2 vars.$margin-3;
position: sticky;
top: 0;
z-index: 2001;
background-color: vars.$color-black;
@include mixins.flex-row;
flex: 0 0;
flex: 0 0 auto;
> * {
vertical-align: middle;
@@ -32,9 +37,9 @@
padding: 0;
margin: 0;
padding-bottom: vars.$margin-5;
flex: 1;
flex: 1 1 auto;
overflow-x: hidden;
overflow-y: auto;
overflow-y: visible;
}
&-item-handle {
@@ -326,6 +331,13 @@
}
}
.maputnik-layer-editor > header {
position: sticky;
top: 0;
z-index: 10;
background: vars.$color-black;
}
// Clone of the element which is sorted
.sortableHelper {
font-family: vars.$font-family;