diff --git a/scripts/docs-site/docs-layout.css b/scripts/docs-site/docs-layout.css index 471a9968be..d416bd0ee1 100644 --- a/scripts/docs-site/docs-layout.css +++ b/scripts/docs-site/docs-layout.css @@ -207,6 +207,10 @@ @media (prefers-reduced-motion:reduce) { .docs-layout .doc .release-source-toggle > summary::before { transition: none; }} .docs-layout .doc .release-source-toggle > summary:focus-visible { outline: 2px solid var(--oc-focus-ring); outline-offset: 2px; } +/* Patch lists keep their detail control in the sentence's inline flow. */ +.docs-layout .doc li:has(> .release-source-inline) > p:first-child { display: inline; margin: 0; } +.docs-layout .doc .release-source-inline { display: inline; } +.docs-layout .doc .release-source-inline > summary { display: inline-flex; vertical-align: baseline; margin-inline-start: .35em; } @media (hover:hover) and (pointer:fine) { .docs-layout .doc .release-source-toggle > summary:hover::before { color: var(--ink); }} .docs-layout .doc > :is(p,ul,ol,blockquote) { max-inline-size: var(--docs-prose-measure); margin-block: 0 var(--docs-paragraph-gap); } diff --git a/scripts/docs-site/release-inline-details.test.mjs b/scripts/docs-site/release-inline-details.test.mjs new file mode 100644 index 0000000000..9e74fbf845 --- /dev/null +++ b/scripts/docs-site/release-inline-details.test.mjs @@ -0,0 +1,59 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import { test } from "node:test"; +import { chromium } from "playwright"; +import { createMarkdownRenderer, renderMdxish } from "./mdx-ish.mjs"; + +test("patch details follow the fix inline and retain native keyboard access", async () => { + const browser = await chromium.launch({ headless: true }); + const source = `- **Conversation recovery.** Fixes replies getting stuck after an update. + +
+ More details + + [#123](https://github.com/openclaw/openclaw/pull/123), thanks to [@example](https://github.com/example). + +
+ +- An unrelated list item. +`; + const css = fs.readFileSync(new URL("./docs-layout.css", import.meta.url), "utf8"); + try { + for (const width of [1200, 390]) { + const page = await browser.newPage({ viewport: { width, height: 700 } }); + try { + await page.setContent(`
${renderMdxish(source, createMarkdownRenderer())}
`); + const summary = page.locator(".release-source-inline > summary"); + const link = page.getByRole("link", { name: "#123", exact: true }); + assert.equal(await link.isVisible(), false); + const layout = await summary.evaluate((element) => { + const paragraph = element.parentElement.previousElementSibling; + const range = document.createRange(); + range.setStart(paragraph.lastChild, paragraph.lastChild.textContent.length - 1); + range.setEnd(paragraph.lastChild, paragraph.lastChild.textContent.length); + const end = range.getBoundingClientRect(); + const control = element.getBoundingClientRect(); + return { end: { left: end.left, right: end.right, top: end.top, bottom: end.bottom }, control: { left: control.left, top: control.top, bottom: control.bottom, height: control.height }, overflow: document.documentElement.scrollWidth > innerWidth, paragraphDisplay: getComputedStyle(paragraph).display }; + }); + assert.equal(layout.paragraphDisplay, "inline"); + assert.ok(layout.control.height >= 44, "retain the existing touch target"); + assert.equal(layout.overflow, false, "narrow view must not scroll horizontally"); + if (width === 1200) { + assert.ok(layout.control.left >= layout.end.right, "control follows the final character"); + assert.ok(layout.control.top < layout.end.bottom && layout.control.bottom > layout.end.top, "control and sentence share a line"); + } + await summary.click(); + assert.equal(await link.isVisible(), true); + await summary.focus(); + await page.keyboard.press("Space"); + assert.equal(await link.isVisible(), false); + await page.keyboard.press("Enter"); + assert.equal(await link.isVisible(), true); + await page.keyboard.press("Tab"); + assert.equal(await link.evaluate((element) => element === document.activeElement), true); + await page.keyboard.press("Shift+Tab"); + assert.equal(await summary.evaluate((element) => element === document.activeElement), true); + } finally { await page.close(); } + } + } finally { await browser.close(); } +});