Add allowScroll config to lock body scroll during tour

This commit is contained in:
Kamran Ahmed
2026-06-24 12:30:08 +01:00
parent bb2dbff280
commit 431c0367b7
7 changed files with 89 additions and 1 deletions
@@ -36,6 +36,9 @@ type Config = {
smoothScroll?: boolean;
// Whether to allow closing the popover by clicking on the backdrop. (default: true)
allowClose?: boolean;
// Whether to allow scrolling the page while the driver is active.
// Set to false to lock body scrolling for the duration of the tour. (default: true)
allowScroll?: boolean;
// Opacity of the backdrop. (default: 0.5)
overlayOpacity?: number;
// What to do when the overlay backdrop is clicked.
+2
View File
@@ -5,6 +5,7 @@ import { arrowGroup } from "./arrow";
import { tourGroup } from "./tour";
import { apiGroup } from "./api";
import { durationGroup } from "./duration";
import { scrollGroup } from "./scroll";
export const exampleGroups: ExampleGroup[] = [
highlightGroup,
@@ -12,6 +13,7 @@ export const exampleGroups: ExampleGroup[] = [
arrowGroup,
tourGroup,
durationGroup,
scrollGroup,
apiGroup,
];
+36
View File
@@ -0,0 +1,36 @@
import { driver } from "driver.js";
import type { ExampleGroup } from "./types";
import { basicTourSteps } from "./shared";
// Both examples run the same tour; only `allowScroll` differs so the page's
// scrollability during the tour can be compared directly.
function tourWithScroll(allowScroll: boolean) {
driver({
allowScroll,
showProgress: true,
showButtons: ["next", "previous", "close"],
steps: basicTourSteps,
}).drive();
}
export const scrollGroup: ExampleGroup = {
title: "Body Scrolling (test)",
examples: [
{
id: "scroll-allowed",
title: "Scroll Allowed (default)",
description: "LOOK: while the tour is active you can still scroll the page with the wheel or trackpad.",
run() {
tourWithScroll(true);
},
},
{
id: "scroll-locked",
title: "Scroll Locked",
description: "LOOK: with allowScroll:false the body is frozen — wheel and trackpad scrolling do nothing.",
run() {
tourWithScroll(false);
},
},
],
};
+2
View File
@@ -17,6 +17,7 @@ export type Config = {
overlayOpacity?: number;
smoothScroll?: boolean;
allowClose?: boolean;
allowScroll?: boolean;
overlayClickBehavior?: "close" | "nextStep" | DriverHook;
stagePadding?: number;
stageRadius?: number;
@@ -63,6 +64,7 @@ export function configure(config: Config = {}) {
animate: true,
duration: 400,
allowClose: true,
allowScroll: true,
overlayClickBehavior: "close",
overlayOpacity: 0.7,
smoothScroll: false,
+4
View File
@@ -2,6 +2,10 @@
pointer-events: none;
}
.driver-active.driver-no-scroll {
overflow: hidden;
}
.driver-active * {
pointer-events: none;
}
+4 -1
View File
@@ -222,6 +222,9 @@ export function driver(options: Config = {}): Driver {
setState("isInitialized", true);
document.body.classList.add("driver-active", getConfig("animate") ? "driver-fade" : "driver-simple");
if (!getConfig("allowScroll")) {
document.body.classList.add("driver-no-scroll");
}
document.body.style.setProperty("--driver-animation-duration", `${getConfig("duration") || 400}ms`);
initEvents();
@@ -332,7 +335,7 @@ export function driver(options: Config = {}): Driver {
const onDeselected = activeStep?.onDeselected || getConfig("onDeselected");
const onDestroyed = getConfig("onDestroyed");
document.body.classList.remove("driver-active", "driver-fade", "driver-simple");
document.body.classList.remove("driver-active", "driver-fade", "driver-simple", "driver-no-scroll");
document.body.style.removeProperty("--driver-animation-duration");
destroyEvents();
+38
View File
@@ -0,0 +1,38 @@
import { describe, expect, it } from "vitest";
import { createDriver, SAMPLE_STEPS, useDriverHarness } from "./utils";
useDriverHarness();
const hasNoScroll = () => document.body.classList.contains("driver-no-scroll");
describe("allowScroll", () => {
it("allows body scrolling by default", () => {
const d = createDriver({ animate: false });
d.highlight({ element: "#intro", popover: { title: "Intro" } });
expect(d.getConfig().allowScroll).toBe(true);
expect(hasNoScroll()).toBe(false);
});
it("locks body scrolling when allowScroll is false", () => {
const d = createDriver({ animate: false, allowScroll: false });
d.highlight({ element: "#intro", popover: { title: "Intro" } });
expect(hasNoScroll()).toBe(true);
});
it("locks body scrolling for a tour when allowScroll is false", () => {
const d = createDriver({ animate: false, allowScroll: false, steps: SAMPLE_STEPS });
d.drive();
expect(hasNoScroll()).toBe(true);
});
it("releases the scroll lock on destroy", () => {
const d = createDriver({ animate: false, allowScroll: false });
d.highlight({ element: "#intro", popover: { title: "Intro" } });
d.destroy();
expect(hasNoScroll()).toBe(false);
});
});