mirror of
https://github.com/kamranahmedse/driver.js.git
synced 2026-10-30 19:27:40 +08:00
Style footer buttons via class so custom buttons can opt out
This commit is contained in:
@@ -297,6 +297,9 @@ You can add custom buttons using `onPopoverRender` callback. This callback is ca
|
||||
onPopoverRender: (popover, { config, state }) => {
|
||||
const firstButton = document.createElement("button");
|
||||
firstButton.innerText = "Go to First";
|
||||
// Add this class to give the button the default driver.js styling.
|
||||
// Leave it out if you want to apply your own styles instead.
|
||||
firstButton.classList.add("driver-popover-footer-btn");
|
||||
popover.footerButtons.appendChild(firstButton);
|
||||
|
||||
firstButton.addEventListener("click", () => {
|
||||
|
||||
@@ -64,6 +64,11 @@ Here is the list of classes applied to the popover which you can use in conjunct
|
||||
.driver-popover-prev-btn {}
|
||||
.driver-popover-next-btn {}
|
||||
|
||||
/* Shared style for the footer buttons. The built-in prev/next buttons carry it,
|
||||
and you can add it to custom buttons created in onPopoverRender to give them
|
||||
the default button look (or leave it off to apply your own styles). */
|
||||
.driver-popover-footer-btn {}
|
||||
|
||||
/* Added to the next button on the last step, when it acts as the "Done" button */
|
||||
.driver-popover-next-btn.driver-popover-done-btn {}
|
||||
```
|
||||
|
||||
+3
-3
@@ -117,7 +117,7 @@
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.driver-popover-footer button {
|
||||
.driver-popover-footer-btn {
|
||||
all: unset;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
@@ -148,8 +148,8 @@
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.driver-popover-footer button:hover,
|
||||
.driver-popover-footer button:focus {
|
||||
.driver-popover-footer-btn:hover,
|
||||
.driver-popover-footer-btn:focus {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
|
||||
+11
-9
@@ -223,13 +223,15 @@ export function renderPopover(element: Element, step: DriveStep) {
|
||||
return undefined;
|
||||
},
|
||||
target => {
|
||||
// Only prevent the default action if we're clicking on a driver button
|
||||
// This allows us to have links inside the popover title and description
|
||||
return (
|
||||
!popover?.description.contains(target) &&
|
||||
!popover?.title.contains(target) &&
|
||||
typeof target.className === "string" &&
|
||||
target.className.includes("driver-popover")
|
||||
// Only prevent the default action if we're clicking on a driver button.
|
||||
// This allows links inside the popover title/description, as well as
|
||||
// custom buttons added through onPopoverRender, to behave normally.
|
||||
if (popover?.description.contains(target) || popover?.title.contains(target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!target.closest(
|
||||
".driver-popover-prev-btn, .driver-popover-next-btn, .driver-popover-close-btn"
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -293,12 +295,12 @@ function createPopover(): PopoverDOM {
|
||||
|
||||
const previousButton = document.createElement("button");
|
||||
previousButton.type = "button";
|
||||
previousButton.classList.add("driver-popover-prev-btn");
|
||||
previousButton.classList.add("driver-popover-prev-btn", "driver-popover-footer-btn");
|
||||
previousButton.innerHTML = "Previous";
|
||||
|
||||
const nextButton = document.createElement("button");
|
||||
nextButton.type = "button";
|
||||
nextButton.classList.add("driver-popover-next-btn");
|
||||
nextButton.classList.add("driver-popover-next-btn", "driver-popover-footer-btn");
|
||||
nextButton.innerHTML = "Next";
|
||||
|
||||
footerButtons.appendChild(previousButton);
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { readFileSync } from "fs";
|
||||
import { resolve } from "path";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { createDriver, navButton, SAMPLE_STEPS, useDriverHarness } from "./utils";
|
||||
|
||||
useDriverHarness();
|
||||
|
||||
// The footer button styling lives in src/driver.css. jsdom resolves the
|
||||
// selectors below through getComputedStyle, so we load the real stylesheet
|
||||
// once into <head> (the harness only resets <body>, so it survives each test).
|
||||
beforeAll(() => {
|
||||
const style = document.createElement("style");
|
||||
style.textContent = readFileSync(resolve(process.cwd(), "src/driver.css"), "utf8");
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
function addCustomFooterButton(opts: { keepDefaultStyle: boolean }): {
|
||||
button: HTMLButtonElement;
|
||||
onClick: ReturnType<typeof vi.fn>;
|
||||
} {
|
||||
const onClick = vi.fn();
|
||||
const button = document.createElement("button");
|
||||
button.id = "custom-footer-btn";
|
||||
button.innerText = "Custom";
|
||||
if (opts.keepDefaultStyle) {
|
||||
button.classList.add("driver-popover-footer-btn");
|
||||
}
|
||||
button.addEventListener("click", onClick);
|
||||
|
||||
const d = createDriver({
|
||||
animate: false,
|
||||
steps: SAMPLE_STEPS,
|
||||
onPopoverRender: popover => {
|
||||
popover.footerButtons.appendChild(button);
|
||||
},
|
||||
});
|
||||
d.drive();
|
||||
|
||||
return { button, onClick };
|
||||
}
|
||||
|
||||
describe("footer button styling", () => {
|
||||
it("styles the built-in navigation buttons through the footer button class", () => {
|
||||
const d = createDriver({ animate: false, steps: SAMPLE_STEPS });
|
||||
d.drive();
|
||||
|
||||
const next = navButton("next")!;
|
||||
expect(next.classList.contains("driver-popover-footer-btn")).toBe(true);
|
||||
expect(navButton("prev")!.classList.contains("driver-popover-footer-btn")).toBe(true);
|
||||
|
||||
const styles = getComputedStyle(next);
|
||||
expect(styles.padding).toBe("3px 7px");
|
||||
expect(styles.borderRadius).toBe("3px");
|
||||
});
|
||||
|
||||
it("lets a custom footer button opt out of the default button styling", () => {
|
||||
const { button } = addCustomFooterButton({ keepDefaultStyle: false });
|
||||
|
||||
const styles = getComputedStyle(button);
|
||||
expect(styles.padding).not.toBe("3px 7px");
|
||||
expect(styles.borderRadius).not.toBe("3px");
|
||||
});
|
||||
|
||||
it("lets a custom footer button opt in to the default button styling", () => {
|
||||
const { button } = addCustomFooterButton({ keepDefaultStyle: true });
|
||||
|
||||
const styles = getComputedStyle(button);
|
||||
expect(styles.padding).toBe("3px 7px");
|
||||
expect(styles.borderRadius).toBe("3px");
|
||||
});
|
||||
});
|
||||
|
||||
describe("custom footer button click handling", () => {
|
||||
it("does not swallow clicks on a custom footer button that opted in to the style", () => {
|
||||
const { button, onClick } = addCustomFooterButton({ keepDefaultStyle: true });
|
||||
|
||||
const event = new MouseEvent("click", { bubbles: true, cancelable: true });
|
||||
button.dispatchEvent(event);
|
||||
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
expect(event.defaultPrevented).toBe(false);
|
||||
});
|
||||
|
||||
it("still intercepts clicks on the built-in navigation buttons", () => {
|
||||
const onNextClick = vi.fn();
|
||||
const d = createDriver({ animate: false, steps: SAMPLE_STEPS, onNextClick });
|
||||
d.drive();
|
||||
|
||||
const event = new MouseEvent("click", { bubbles: true, cancelable: true });
|
||||
navButton("next")!.dispatchEvent(event);
|
||||
|
||||
expect(onNextClick).toHaveBeenCalledTimes(1);
|
||||
expect(event.defaultPrevented).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user