diff --git a/docs/src/content/guides/buttons.mdx b/docs/src/content/guides/buttons.mdx index 502ddf0..1f23b04 100644 --- a/docs/src/content/guides/buttons.mdx +++ b/docs/src/content/guides/buttons.mdx @@ -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", () => { diff --git a/docs/src/content/guides/theming.mdx b/docs/src/content/guides/theming.mdx index 0cebbee..de3406d 100644 --- a/docs/src/content/guides/theming.mdx +++ b/docs/src/content/guides/theming.mdx @@ -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 {} ``` diff --git a/src/driver.css b/src/driver.css index 69fc383..1584a62 100644 --- a/src/driver.css +++ b/src/driver.css @@ -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; } diff --git a/src/popover.ts b/src/popover.ts index 6902a91..534809c 100644 --- a/src/popover.ts +++ b/src/popover.ts @@ -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); diff --git a/tests/custom-buttons.test.ts b/tests/custom-buttons.test.ts new file mode 100644 index 0000000..a4f5fe1 --- /dev/null +++ b/tests/custom-buttons.test.ts @@ -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
(the harness only resets , 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