diff --git a/scripts/docs-site/docs-layout.css b/scripts/docs-site/docs-layout.css index 471a9968be..d8a69fa130 100644 --- a/scripts/docs-site/docs-layout.css +++ b/scripts/docs-site/docs-layout.css @@ -338,6 +338,8 @@ .docs-layout .doc .oc-code { box-shadow: var(--docs-surface-shadow); background: var(--code-block); } .docs-layout .doc .oc-code figcaption { background: transparent; min-height: 40px; } .docs-layout .doc .oc-code figcaption .oc-code-label { font: 500 11px/1.4 var(--oc-font-mono); letter-spacing: .06em; } +.docs-layout .doc .oc-code-group .oc-code figcaption { display: flex; justify-content: flex-end; } +.docs-layout .doc .oc-code-group .oc-code figcaption .oc-code-label { display: none; } .docs-layout .doc .oc-code-tabs { gap: 24px; padding: 0 18px; border-block-end: 1px solid var(--docs-border); background: transparent; overflow-x: auto; scrollbar-width: thin; } .docs-layout .doc .oc-code-tab { position: relative; flex: 0 0 auto; min-block-size: 44px; padding: 10px 0; border-radius: 4px; background: transparent; color: var(--muted); font: 500 12px/1.4 var(--oc-font-mono); white-space: nowrap; } .docs-layout .doc .oc-code-tab.active { color: var(--ink); background: transparent; } diff --git a/scripts/docs-site/smoke.mjs b/scripts/docs-site/smoke.mjs index 4eb082d2c8..379a5b6f86 100644 --- a/scripts/docs-site/smoke.mjs +++ b/scripts/docs-site/smoke.mjs @@ -768,6 +768,10 @@ assertEditSourceLinks(); if (!/function initCodeGroups/.test(siteJs) || !/className="oc-code-tab"/.test(siteJs) || !/preferredCodeTab/.test(siteJs)) { throw new Error("assets: code group tabs are missing"); } +if (!/\.docs-layout \.doc \.oc-code-group \.oc-code figcaption\s*\{\s*display:\s*flex;\s*justify-content:\s*flex-end;\s*\}/.test(siteCss) + || !/\.docs-layout \.doc \.oc-code-group \.oc-code figcaption \.oc-code-label\s*\{\s*display:\s*none;\s*\}/.test(siteCss)) { + throw new Error("assets: grouped code must expose copy controls without repeating tab labels"); +} if (!/function handleDocsControlClick/.test(siteJs) || !/async function copyText/.test(siteJs)) { throw new Error("assets: copy and feedback controls are missing"); } diff --git a/scripts/docs-site/visual-smoke.mjs b/scripts/docs-site/visual-smoke.mjs index aafa403eba..d991244a4c 100644 --- a/scripts/docs-site/visual-smoke.mjs +++ b/scripts/docs-site/visual-smoke.mjs @@ -42,6 +42,7 @@ try { await checkHeaderSurface(); await checkFooterSocials(); await checkDesktop(); + await checkGroupedCodeCopy(); await checkTocScrollspy(); await checkTocRailScrolling(); await checkCompactToc(); @@ -55,6 +56,48 @@ try { server.close(); } +async function checkGroupedCodeCopy() { + for (const theme of ["light", "dark"]) { + const page = await browser.newPage({ viewport: { width: 390, height: 672 } }); + try { + await page.addInitScript((theme) => { + localStorage.setItem("theme", theme); + window.__groupedCodeCopies = []; + Object.defineProperty(navigator, "clipboard", { + configurable: true, + value: { writeText: async (text) => window.__groupedCodeCopies.push(text) }, + }); + }, theme); + await page.goto(`${base}/__elements`, { waitUntil: "networkidle" }); + const group = page.locator(".oc-code-group").first(); + for (const width of [390, 667, 1440]) { + await page.setViewportSize({ width, height: 672 }); + const tabs = group.locator(".oc-code-tab"); + for (let index = 0; index < await tabs.count(); index += 1) { + await tabs.nth(index).click(); + const control = group.locator(".oc-code.active [data-code-copy]"); + if (!await control.isVisible() + || await group.locator("[data-code-copy]:visible").count() !== 1 + || await group.locator(".oc-code.active .oc-code-label").isVisible()) { + throw new Error(`grouped copy visibility failed: ${theme}, ${width}, tab ${index}`); + } + const expected = await group.locator(".oc-code.active").evaluate((block) => + [...block.querySelectorAll(".code-line")].map((line) => line.textContent).join("\n")); + await control.focus(); + await page.keyboard.press("Enter"); + await page.waitForFunction(() => window.__groupedCodeCopies.length > 0); + const copied = await page.evaluate(() => window.__groupedCodeCopies.pop()); + if (copied !== expected || await control.getAttribute("data-copy-state") !== "copied") { + throw new Error(`grouped copy content failed: ${theme}, ${width}, tab ${index}`); + } + } + } + } finally { + await page.close(); + } + } +} + async function checkLanguageSelection() { const page = await browser.newPage({ viewport: { width: 390, height: 600 } }); try {