Skip to content
Closed
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
15 changes: 15 additions & 0 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,21 @@
font-size: 0.85rem;
}

/* Reproducibility digest prefixes read inline in the '·'-separated post-meta line. */
.analysis-run-digest {
display: inline;
}

.analysis-run-digest > summary {
display: inline;
cursor: pointer;
list-style: none;
}

.analysis-run-digest > summary::-webkit-details-marker {
display: none;
}

.visually-hidden {
position: absolute;
width: 1px;
Expand Down
30 changes: 22 additions & 8 deletions frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2658,17 +2658,27 @@ describe("App, authenticated", () => {
expect(screen.getByText(/Cutoff 2026-01-12/)).toBeInTheDocument();
expect(screen.getByText(/Requested 2026-01-12/)).toBeInTheDocument();
const digests = screen.getByLabelText("Analysis run reproducibility digests");
expect(digests).toHaveTextContent("Hover a prefix to read the full digest for verification.");
expect(digests).toHaveTextContent("Open a prefix to read the full digest for verification.");
expect(digests).toHaveTextContent("Code abcdef012345");
expect(digests).toHaveTextContent("Config 0123456789ab");
expect(digests).not.toHaveTextContent("abcdef0123456789deadbeefcafebabe");
expect(digests).not.toHaveTextContent(
// The disclosures are native <details>/<summary> -- no tabIndex/role/onClick hack --
// so they are keyboard- and touch-operable with zero JS, unlike a hover-only title tooltip.
const codeDisclosure = screen.getByText("Code abcdef012345").closest("details");
const configDisclosure = screen.getByText("Config 0123456789ab").closest("details");
expect(codeDisclosure).not.toHaveAttribute("open");
expect(configDisclosure).not.toHaveAttribute("open");
expect(screen.getByText("Code abcdef012345").tagName).toBe("SUMMARY");
expect(screen.getByText("Code abcdef012345")).not.toHaveAttribute("tabIndex");
expect(screen.getByText("Code abcdef012345")).not.toHaveAttribute("role");
await userEvent.click(screen.getByText("Code abcdef012345"));
expect(codeDisclosure).toHaveAttribute("open");
expect(codeDisclosure).toHaveTextContent("abcdef0123456789deadbeefcafebabe");
expect(configDisclosure).not.toHaveAttribute("open");
await userEvent.click(screen.getByText("Config 0123456789ab"));
expect(configDisclosure).toHaveAttribute("open");
expect(configDisclosure).toHaveTextContent(
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
);
expect(screen.getByTitle("abcdef0123456789deadbeefcafebabe")).toHaveTextContent("Code abcdef012345");
expect(
screen.getByTitle("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
).toHaveTextContent("Config 0123456789ab");
const history = screen.getByRole("list", { name: "Analysis run status history" });
expect(history).toHaveTextContent("Pending 2026-01-12 12:31");
expect(history).toHaveTextContent("Running 2026-01-12 12:32");
Expand Down Expand Up @@ -3305,7 +3315,11 @@ describe("App, authenticated", () => {
);
const digests = screen.getByLabelText("Analysis run reproducibility digests");
expect(digests).toHaveTextContent("Result aaaaaaaaaaaa");
expect(screen.getByTitle("aa".repeat(32))).toHaveTextContent("Result aaaaaaaaaaaa");
const resultDisclosure = screen.getByText("Result aaaaaaaaaaaa").closest("details");
expect(resultDisclosure).not.toHaveAttribute("open");
await userEvent.click(screen.getByText("Result aaaaaaaaaaaa"));
expect(resultDisclosure).toHaveAttribute("open");
expect(resultDisclosure).toHaveTextContent("aa".repeat(32));
const startCall = fetchMock.mock.calls.find((call) =>
String(call[0]).endsWith("/api/analysis-runs/run-demo-lineage-pending/start"),
);
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2527,7 +2527,7 @@ function analysisRunCorpusHint(run: AnalysisRun): string | null {
}
}

/** Git-style prefix. The full digest stays on `title` for verification. */
/** Git-style prefix. The full digest is behind a `<details>` disclosure for verification. */
const ANALYSIS_RUN_DIGEST_PREFIX_LENGTH = 12;

function analysisRunDigestPrefix(digest: string): string {
Expand Down Expand Up @@ -2606,17 +2606,20 @@ function AnalysisRunReproducibilityDigests({
}
return (
<div role="group" aria-label="Analysis run reproducibility digests">
<p className="post-meta">
<div className="post-meta">
<span className="visually-hidden">
Hover a prefix to read the full digest for verification.{" "}
Open a prefix to read the full digest for verification.{" "}
</span>
{parts.map((part, index) => (
<span key={part.label}>
{index > 0 ? " · " : null}
<span title={part.digest}>{`${part.label} ${analysisRunDigestPrefix(part.digest)}`}</span>
<details className="analysis-run-digest">
<summary>{`${part.label} ${analysisRunDigestPrefix(part.digest)}`}</summary>
{part.digest}
</details>
Comment on lines +2616 to +2619

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Full digest now always present in DOM text

The full digest was previously only in the title attribute; it is now text inside the closed <details>, so it is always in textContent. The component is only mounted in the detail popup (AnalysisRunReproducibilityDigests at frontend/src/App.tsx:2913), so the home-list negative assertion still holds. Updated tests click to reveal it.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

</span>
Comment on lines 2614 to 2620

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Disclosure nested inside inline span

Each <details> disclosure sits directly inside a <span>, but a span accepts only phrasing content while <details> is flow content, so the markup is invalid. This is the same validity problem that prompted changing the wrapping element from <p> to <div>, left unfixed for the inner span.

Suggested change
<span key={part.label}>
{index > 0 ? " · " : null}
<span title={part.digest}>{`${part.label} ${analysisRunDigestPrefix(part.digest)}`}</span>
<details className="analysis-run-digest">
<summary>{`${part.label} ${analysisRunDigestPrefix(part.digest)}`}</summary>
{part.digest}
</details>
</span>
<Fragment key={part.label}>
{index > 0 ? " · " : null}
<details className="analysis-run-digest">
<summary>{`${part.label} ${analysisRunDigestPrefix(part.digest)}`}</summary>
{part.digest}
</details>
</Fragment>
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

))}
</p>
</div>
</div>
);
}
Expand Down
Loading