Skip to content
Open
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
40 changes: 26 additions & 14 deletions frontend/src/sqlpage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ function sqlpage_form() {
}
}

function get_tabler_color(name) {
function get_tabler_color(name: string) {
return getComputedStyle(document.documentElement).getPropertyValue(
`--tblr-${name}`,
);
Expand All @@ -394,16 +394,16 @@ function load_scripts() {
}
}

function normalize_hash(hash) {
const normalized = hash?.replace(/^#/, "");
function normalize_hash(hash: string | undefined) {
const normalized = hash?.replace(/^#/, "") ?? "";
try {
return decodeURIComponent(normalized);
} catch {
return normalized;
}
}

function open_toasts_for_hash(toasts) {
function open_toasts_for_hash(toasts: Iterable<HTMLElement>) {
const Toast = page_bootstrap().Toast;
const hash = normalize_hash(window.location.hash);
if (!hash) return;
Expand All @@ -414,9 +414,9 @@ function open_toasts_for_hash(toasts) {
}
}

function restore_focus_after_toast(toast, container) {
function restore_focus_after_toast(toast: HTMLElement, container: HTMLElement) {
if (!toast.contains(document.activeElement)) return;
const next_close = container.querySelector(
const next_close = container.querySelector<HTMLElement>(
'.toast.show [data-bs-dismiss="toast"]',
);
if (next_close) {
Expand Down Expand Up @@ -501,27 +501,39 @@ add_init_fn(sqlpage_modal);
add_init_fn(load_scripts);
add_init_fn(sqlpage_toast);
window.addEventListener("hashchange", () =>
open_toasts_for_hash(document.querySelectorAll("[data-toast-trigger]")),
open_toasts_for_hash(
document.querySelectorAll<HTMLElement>("[data-toast-trigger]"),
),
);

function init_bootstrap_components(event) {
function init_bootstrap_components(fragment: Element | Document) {
const bootstrap = page_bootstrap();
const fragment = event.target;
for (const el of fragment.querySelectorAll('[data-bs-toggle="tooltip"]')) {
for (const el of fragment.querySelectorAll<HTMLElement>(
'[data-bs-toggle="tooltip"]',
)) {
new bootstrap.Tooltip(el);
}
for (const el of fragment.querySelectorAll('[data-bs-toggle="popover"]')) {
for (const el of fragment.querySelectorAll<HTMLElement>(
'[data-bs-toggle="popover"]',
)) {
new bootstrap.Popover(el);
}
for (const el of fragment.querySelectorAll('[data-bs-toggle="dropdown"]')) {
for (const el of fragment.querySelectorAll<HTMLElement>(
'[data-bs-toggle="dropdown"]',
)) {
new bootstrap.Dropdown(el);
}
for (const el of fragment.querySelectorAll('[data-bs-ride="carousel"]')) {
for (const el of fragment.querySelectorAll<HTMLElement>(
'[data-bs-ride="carousel"]',
)) {
new bootstrap.Carousel(el);
}
}

document.addEventListener("fragment-loaded", init_bootstrap_components);
document.addEventListener("fragment-loaded", ({ target }) => {
if (target instanceof Element || target instanceof Document)
init_bootstrap_components(target);
});

function open_modal_for_hash() {
const hash = window.location.hash.substring(1);
Expand Down
1 change: 1 addition & 0 deletions tests/end-to-end/fixtures/fragment-loaded/index.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT 'text' AS component, 'Markup added after load is initialized when a fragment is announced.' AS contents;
40 changes: 40 additions & 0 deletions tests/end-to-end/fixtures/fragment-loaded/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect, type Page, test } from "../../fixture.ts";

const INJECTED_HINT = "injected hint";

async function addTooltip(page: Page) {
await page.evaluate((hint) => {
const span = document.createElement("span");
span.id = "added";
span.textContent = "added";
span.setAttribute("data-bs-toggle", "tooltip");
span.setAttribute("title", hint);
document.querySelector("main")?.appendChild(span);
}, INJECTED_HINT);
}

test("shows a tooltip added before the document announces a fragment", async ({
page,
}) => {
await addTooltip(page);
await page.evaluate(() =>
document.dispatchEvent(new CustomEvent("fragment-loaded")),
);
await page.locator("#added").hover();

await expect(page.locator(".tooltip")).toHaveText(INJECTED_HINT);
});

test("shows a tooltip added before an element announces a fragment", async ({
page,
}) => {
await addTooltip(page);
await page.evaluate(() =>
document
.querySelector("main")
?.dispatchEvent(new CustomEvent("fragment-loaded", { bubbles: true })),
);
await page.locator("#added").hover();

await expect(page.locator(".tooltip")).toHaveText(INJECTED_HINT);
});
Loading