Skip to content

Commit

Permalink
refactor(cutText): improve algorithm (#820)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranet authored Oct 27, 2024
1 parent 6752976 commit db34661
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 deletions.
20 changes: 15 additions & 5 deletions packages/utilities/src/lib/cutText.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { splitText } from './splitText';
const WordSeparatorCharacter = /[\p{Separator}\p{Punctuation}\p{Control}]/u;

/**
* Split a text by its latest space character in a range from the character 0 to the selected one.
Expand All @@ -8,8 +8,18 @@ import { splitText } from './splitText';
* @license Apache-2.0
*/
export function cutText(str: string, length: number) {
if (str.length < length) return str;
const cut = splitText(str, length - 3);
if (cut.length < length - 3) return `${cut}...`;
return `${cut.slice(0, length - 3)}...`;
if (str.length <= length) return str;

const codepoints = [...str];
if (codepoints.length <= length) return str;

let lastSeparator = length;
for (let i = 0; i < length; ++i) {
if (WordSeparatorCharacter.test(codepoints[i])) {
lastSeparator = i;
}
}

const lastCharacterIndex = lastSeparator === length ? length - 1 : lastSeparator;
return codepoints.slice(0, lastCharacterIndex).concat('…').join('');
}
32 changes: 26 additions & 6 deletions packages/utilities/tests/cutText.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
import { cutText } from '../src';

describe('cutText', () => {
test('GIVEN length much smaller than fist word THEN cuts up string', () => {
expect(cutText('Lorem Ipsum', 5)).toEqual('Lo...');
test('GIVEN length much smaller than the first word THEN cuts up string', () => {
expect(cutText('Lorem Ipsum', 2)).toEqual('L…');
});

test('GIVEN length 1 char smaller than first word length THEN cuts up string', () => {
expect(cutText('Lorem Ipsum', 7)).toEqual('Lore...');
test('GIVEN length 1 char smaller than the first word length THEN cuts up string', () => {
expect(cutText('Lorem Ipsum', 5)).toEqual('Lore');
});

test('GIVEN length 1 longer than firs word THEN first word with ...', () => {
expect(cutText('Lorem Ipsum', 9)).toEqual('Lorem...');
test('GIVEN length 1 char longer than the first word THEN first word with ', () => {
expect(cutText('Lorem Ipsum', 9)).toEqual('Lorem');
});

test('GIVEN length longer than total THEN returns unmodified', () => {
expect(cutText('Lorem Ipsum', 30)).toEqual('Lorem Ipsum');
});

test('GIVEN unicode characters that fit THEN returns the string as-is', () => {
expect(cutText('🔥🔥', 2)).toEqual('🔥🔥');
});

test('GIVEN unicode characters THEN returns a correctly-formatted string', () => {
expect(cutText('🔥🔥🔥', 2)).toEqual('🔥…');
});

test('GIVEN unicode characters THEN returns a correctly-formatted string', () => {
expect(cutText('Hi Barbie!\nHi Ken!', 13)).toEqual('Hi Barbie!…');
});

test('GIVEN a family THEN it only lets the parents in', () => {
expect(cutText('👨‍👨‍👧‍👧', 4)).toEqual('👨‍👨…');
});

test('GIVEN a family THEN it only shows the emoji if it fits', () => {
expect(cutText('Hello 👨‍👨‍👧‍👧!', 10)).toEqual('Hello…');
});
});

0 comments on commit db34661

Please sign in to comment.