diff --git a/packages/shared/src/String.test.ts b/packages/shared/src/String.test.ts new file mode 100644 index 0000000000000..ae06e4153e4fb --- /dev/null +++ b/packages/shared/src/String.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from "vite-plus/test"; + +import { truncate } from "./String.ts"; + +describe("truncate", () => { + it("leaves short text alone", () => { + expect(truncate("hello", 50)).toBe("hello"); + }); + + it("cuts by Unicode code point so emoji stay whole", () => { + expect(truncate("👍👍👍", 5)).toBe("👍👍👍"); + expect(truncate("👍👍👍👍", 3)).toBe("👍👍👍..."); + }); + + it("trims before measuring", () => { + expect(truncate(" hello ", 5)).toBe("hello"); + }); +}); diff --git a/packages/shared/src/String.ts b/packages/shared/src/String.ts index c93d0c90cb6bb..f06282c030c47 100644 --- a/packages/shared/src/String.ts +++ b/packages/shared/src/String.ts @@ -1,8 +1,9 @@ export function truncate(text: string, maxLength = 50): string { const trimmed = text.trim(); - if (trimmed.length <= maxLength) { + const characters = Array.from(trimmed); + if (characters.length <= maxLength) { return trimmed; } - return `${trimmed.slice(0, maxLength)}...`; + return `${characters.slice(0, maxLength).join("")}...`; }