fix(web): wrap long record values in diff and zone view (no horizontal overflow)
RecordRow now splits into a top line (badge/name/read-only, unaffected by value length) and a plain block-level values line below it, so a ~400-char unbreakable DKIM key wraps via break-all instead of stretching the flex row and forcing page-wide horizontal scroll. Zone records table gets break-all on the values cell too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
This commit is contained in:
@@ -27,6 +27,30 @@ test("marks read-only records", () => {
|
||||
expect(screen.getByText(/NS/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("renders a very long unbreakable value (DKIM key) without crashing", () => {
|
||||
// Real DKIM records ship a ~400-char unbroken p= blob. This must not
|
||||
// throw and the value must land in the DOM (wrapping itself is a CSS
|
||||
// concern verified manually, not via jsdom layout).
|
||||
const longValue = "v=DKIM1; k=rsa; p=" + "A".repeat(400)
|
||||
const csWithDkim: ChangesetResponse = {
|
||||
updates: [
|
||||
{
|
||||
kind: "update",
|
||||
type: "TXT",
|
||||
name: "default._domainkey.example.com.",
|
||||
desired: [longValue],
|
||||
actual: [],
|
||||
readOnly: false,
|
||||
},
|
||||
],
|
||||
prunes: [],
|
||||
readOnly: [],
|
||||
inSyncCount: 0,
|
||||
}
|
||||
render(<DiffView changeset={csWithDkim} />)
|
||||
expect(screen.getByText(new RegExp(longValue))).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test("does not crash when changeset fields are null", () => {
|
||||
// An empty changeset from an older/edge backend can arrive with null slices
|
||||
// instead of []. DiffView must normalise them, not blow up on .length/.map.
|
||||
|
||||
@@ -47,43 +47,56 @@ function RecordRow({ record, tone }: { record: RecordView; tone: Tone }) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group/row flex items-center gap-3 border-l-2 px-3 py-2.5 transition-colors",
|
||||
"group/row flex flex-col gap-1.5 border-l-2 px-3 py-2.5 transition-colors",
|
||||
"hover:bg-foreground/[0.025]",
|
||||
)}
|
||||
style={{ borderLeftColor: meta.dot }}
|
||||
>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="font-dns w-11 shrink-0 justify-center border-border text-[10px] tracking-wide text-muted-foreground"
|
||||
>
|
||||
{record.type}
|
||||
</Badge>
|
||||
{/* Top line: type badge, name, read-only flag — always single-line,
|
||||
never affected by how long the record values are. */}
|
||||
<div className="flex items-center gap-3">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="font-dns w-11 shrink-0 justify-center border-border text-[10px] tracking-wide text-muted-foreground"
|
||||
>
|
||||
{record.type}
|
||||
</Badge>
|
||||
|
||||
<span className="font-dns min-w-0 flex-1 truncate text-sm text-foreground">
|
||||
{record.name}
|
||||
</span>
|
||||
<span className="font-dns min-w-0 flex-1 truncate text-sm text-foreground">
|
||||
{record.name}
|
||||
</span>
|
||||
|
||||
<span className="font-dns hidden shrink-0 items-center gap-1.5 text-xs text-muted-foreground sm:flex">
|
||||
{record.readOnly && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="shrink-0 gap-1 bg-muted text-[10px] text-muted-foreground"
|
||||
>
|
||||
<Lock className="size-2.5" strokeWidth={2} />
|
||||
read-only
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Values line: plain block-level text (not flex) so a long
|
||||
unbreakable value like a DKIM key wraps within the row's own
|
||||
width instead of stretching it — a flex item's content can
|
||||
otherwise refuse to shrink below its intrinsic width. Indented
|
||||
to align under the name (badge width + gap). */}
|
||||
<div className="font-dns hidden pl-14 text-xs leading-relaxed break-all text-muted-foreground sm:block">
|
||||
<Values values={record.actual} />
|
||||
{showArrow && (
|
||||
<>
|
||||
<ArrowRight className="size-3 text-muted-foreground/50" strokeWidth={1.75} />
|
||||
{" "}
|
||||
<ArrowRight
|
||||
className="mx-1 inline-block size-3 align-[-2px] text-muted-foreground/50"
|
||||
strokeWidth={1.75}
|
||||
/>{" "}
|
||||
<span style={{ color: meta.dot }}>
|
||||
<Values values={record.desired} />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
|
||||
{record.readOnly && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="ml-1 shrink-0 gap-1 bg-muted text-[10px] text-muted-foreground"
|
||||
>
|
||||
<Lock className="size-2.5" strokeWidth={2} />
|
||||
read-only
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -167,7 +167,9 @@ export function DomainDiffPage() {
|
||||
<TableCell className="font-dns">{r.type}</TableCell>
|
||||
<TableCell className="font-dns">{r.name}</TableCell>
|
||||
<TableCell className="font-dns">{r.ttl}</TableCell>
|
||||
<TableCell className="font-dns whitespace-pre-line">{r.values.join("\n")}</TableCell>
|
||||
<TableCell className="font-dns whitespace-pre-line break-all">
|
||||
{r.values.join("\n")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
||||
Reference in New Issue
Block a user