Skip to content

Commit ee90bdc

Browse files
fix(web): CSV/TSV and large-text previews say "1 row", "1 column" and "1 line" (#34992)
1 parent b68aedf commit ee90bdc

2 files changed

Lines changed: 24 additions & 3 deletions

File tree

‎apps/web/src/components/files/AttachmentFilePreview.browser.test.tsx‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,23 @@ it("copies every indexed line rather than the mounted preview rows", async () =>
12971297
expect(screen.container.querySelector('[role="alert"]')).toBeNull();
12981298
});
12991299

1300+
it.each([
1301+
["one.csv", "text/csv", "name\nAda\n", "1 row · 1 column ·"],
1302+
["two.csv", "text/csv", "name,count\nAda,1\nGrace,2\n", "2 rows · 2 columns ·"],
1303+
["one.tsv", "text/tab-separated-values", "name\nAda\n", "1 row · 1 column ·"],
1304+
["one.txt", "text/plain", "a".repeat(1024 * 1024) + "\n", "1 line ·"],
1305+
["two.txt", "text/plain", ("a".repeat(512 * 1024) + "\n").repeat(2), "2 lines ·"],
1306+
] as const)("labels the counts of %s in the status line", async (name, mimeType, text, label) => {
1307+
const file = new Blob([text], { type: mimeType });
1308+
await using screen = await render(
1309+
<AttachmentFilePreview name={name} mimeType={mimeType} sizeBytes={file.size} file={file} />,
1310+
);
1311+
const status = () => screen.container.querySelector('[role="status"]')?.textContent ?? "";
1312+
// A cold large-text worker can take longer than waitFor's default to boot on CI.
1313+
await vi.waitFor(() => expect(status()).toMatch(/ [KM]?B$/), { timeout: 10_000 });
1314+
expect(status()).toContain(label);
1315+
});
1316+
13001317
it.each(["archive", "sqlite"] as const)(
13011318
"reauthorizes and retries a failed %s attachment",
13021319
async (kind) => {

‎apps/web/src/components/files/LargeTextViewer.tsx‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import VirtualTablePreview from "./VirtualTablePreview";
88

99
const FIRST_SCREEN_BYTES = 16 * 1024;
1010

11+
function countLabel(count: number, noun: string) {
12+
return `${count.toLocaleString()} ${noun}${count === 1 ? "" : "s"}`;
13+
}
14+
1115
function firstScreen(text: string, delimiter: "," | "\t" | null, complete: boolean) {
1216
if (!text || text.includes("\0")) return [];
1317
const rows = delimiter
@@ -216,9 +220,9 @@ export default function LargeTextViewer(props: {
216220
className="flex min-h-10 shrink-0 flex-wrap items-center gap-x-1 border-b border-border/60 px-3 py-2 text-xs text-muted-foreground tabular-nums"
217221
role="status"
218222
>
219-
{Math.max(0, meta.count - (props.delimiter && meta.count ? 1 : 0)).toLocaleString()}{" "}
220-
{props.delimiter ? "rows" : "lines"}
221-
{props.delimiter ? ` · ${meta.count ? meta.columns.toLocaleString() : 0} columns` : ""}
223+
{props.delimiter
224+
? `${countLabel(Math.max(0, meta.count - 1), "row")} · ${countLabel(meta.count ? meta.columns : 0, "column")}`
225+
: countLabel(meta.count, "line")}
222226
{meta.complete && meta.sizeBytes !== undefined
223227
? ` · ${meta.sizeBytes === 0 ? "0 bytes" : formatAttachmentSize(meta.sizeBytes)}`
224228
: " · Indexing…"}

0 commit comments

Comments
 (0)