From 67b16866f4284dd405a9b1500a34d13521666528 Mon Sep 17 00:00:00 2001 From: Arno V Date: Tue, 24 Sep 2024 18:12:59 -0400 Subject: [PATCH] fix(ui-truncate): truncate should not split words appart (#690) ## Summary by CodeRabbit - **New Features** - Updated the truncation logic to focus on an 'ideal length' for string truncation, simplifying how text is displayed. - **Bug Fixes** - Adjusted test cases to reflect the new expected output for truncated strings, ensuring accuracy in displayed text. - **Documentation** - Enhanced clarity in the `Truncate` component's functionality by refining parameter names and logic. --- .../src/components/Truncate/Truncate.tsx | 2 +- .../Truncate/__tests__/Truncate.test.tsx | 2 +- .../Truncate/__tests__/utilities.test.ts | 40 +++++++------------ .../src/components/Truncate/utilities.ts | 31 ++++++++------ 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/packages/ui-truncate/src/components/Truncate/Truncate.tsx b/packages/ui-truncate/src/components/Truncate/Truncate.tsx index 73c56a7a..5ada4930 100644 --- a/packages/ui-truncate/src/components/Truncate/Truncate.tsx +++ b/packages/ui-truncate/src/components/Truncate/Truncate.tsx @@ -17,7 +17,7 @@ export const Truncate = ({ } const { string, isTruncated } = truncate({ string: children, - length, + idealLength: length, }); const handleToggleExpanded = (e: { preventDefault: () => void }) => { diff --git a/packages/ui-truncate/src/components/Truncate/__tests__/Truncate.test.tsx b/packages/ui-truncate/src/components/Truncate/__tests__/Truncate.test.tsx index 7c1a3e67..6df65cb5 100644 --- a/packages/ui-truncate/src/components/Truncate/__tests__/Truncate.test.tsx +++ b/packages/ui-truncate/src/components/Truncate/__tests__/Truncate.test.tsx @@ -18,7 +18,7 @@ describe("Truncate Component", () => { it('should render truncated text and "more..." button when children is a string and exceeds length', () => { const text = "This is a long text that needs to be truncated."; render({text}); - expect(screen.getByText("This is")).toBeInTheDocument(); + expect(screen.getByText("This is a long text that")).toBeInTheDocument(); expect(screen.getByText("more...")).toBeInTheDocument(); }); diff --git a/packages/ui-truncate/src/components/Truncate/__tests__/utilities.test.ts b/packages/ui-truncate/src/components/Truncate/__tests__/utilities.test.ts index 7239abd4..953f1492 100644 --- a/packages/ui-truncate/src/components/Truncate/__tests__/utilities.test.ts +++ b/packages/ui-truncate/src/components/Truncate/__tests__/utilities.test.ts @@ -1,48 +1,36 @@ import { describe, expect, it } from "vitest"; -import { truncate } from "../utilities.ts"; // Adjust the import path as needed +import { truncate } from "../utilities.ts"; describe("truncate", () => { - it("should return the original string if length is greater than or equal to string length", () => { + it("should truncate at the ideal length", () => { const result = truncate({ string: "Hello, World!", - length: 20, + idealLength: 6, }); - expect(result).toEqual({ string: "Hello, World!", isTruncated: false }); - }); - - it("should truncate the string if length is less than string length", () => { - const result = truncate({ - string: "Hello, World!", - length: 5, - omission: "...", - }); - expect(result).toEqual({ string: "He", isTruncated: true }); + expect(result).toEqual({ string: "Hello,", isTruncated: true }); }); - it("should return an empty string if length is less than omission length", () => { + it("should truncate at the next space found right after the ideal length", () => { const result = truncate({ string: "Hello, World!", - length: 2, - omission: "...", + idealLength: 4, }); - expect(result).toEqual({ string: "", isTruncated: true }); + expect(result).toEqual({ string: "Hello,", isTruncated: true }); }); - it("should handle edge case where length equals omission length", () => { + it("should truncate at the next space found right after the ideal length", () => { const result = truncate({ - string: "Hello, World!", - length: 3, - omission: "...", + string: "Hello, World! This is your time to shine!", + idealLength: 8, }); - expect(result).toEqual({ string: "", isTruncated: true }); + expect(result).toEqual({ string: "Hello, World!", isTruncated: true }); }); - it("should handle edge case where omission is an empty string", () => { + it("should handle edge case where ideal length is more than string length", () => { const result = truncate({ string: "Hello, World!", - length: 5, - omission: "", + idealLength: 20, }); - expect(result).toEqual({ string: "Hello", isTruncated: true }); + expect(result).toEqual({ string: "Hello, World!", isTruncated: false }); }); }); diff --git a/packages/ui-truncate/src/components/Truncate/utilities.ts b/packages/ui-truncate/src/components/Truncate/utilities.ts index f4ba2af3..e1a743d6 100644 --- a/packages/ui-truncate/src/components/Truncate/utilities.ts +++ b/packages/ui-truncate/src/components/Truncate/utilities.ts @@ -1,30 +1,37 @@ export const DEFAULT_LENGTH = 200; -export const DEFAULT_OMISSION = "===+-av-+==="; type TruncateOptions = { string: string; - length?: number; - omission?: string; + idealLength?: number; }; type TruncateResult = { string: string; isTruncated: boolean; }; +/** + * This function will truncate the string at the last word boundary + * before the ideal length. + * - If ideal length ends up in the middle of a word, truncate at the next space. + * - If ideal length ends up on a space, truncate at the ideal length. + */ export const truncate = ({ string, - length = DEFAULT_LENGTH, - omission = DEFAULT_OMISSION, + idealLength = DEFAULT_LENGTH, }: TruncateOptions): TruncateResult => { const strLength = string.length; - if (length >= strLength) { - return { string, isTruncated: false }; + if (strLength <= idealLength) { + return { string: string, isTruncated: false }; } - const end = length - omission.length; - if (end < 1) { - return { string: "", isTruncated: true }; + + const originalTrunc = string.charAt(idealLength); + if (originalTrunc === " ") { + return { string: string.slice(0, idealLength), isTruncated: true }; } - const result = string.slice(0, end); - return { string: result, isTruncated: true }; + const nextSpace = string.slice(idealLength).search(" "); + return { + string: string.slice(0, idealLength + nextSpace), + isTruncated: true, + }; };