Skip to content

Commit a06be42

Browse files
authored
Feat/text highlight refactor (#1669)
* Add tests for array highlightString and small fixes * Move text highlight to utils * Minor refactor * Handle full string * Prettify * Review and another bug fix
1 parent 3e9dd59 commit a06be42

File tree

10 files changed

+265
-137
lines changed

10 files changed

+265
-137
lines changed

generatedTypes/src/components/text/index.d.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,6 @@ declare type PropsTypes = BaseComponentInjectedProps & ForwardRefInjectedProps &
4242
declare class Text extends PureComponent<PropsTypes> {
4343
static displayName: string;
4444
private TextContainer;
45-
getPartsByHighlight(targetString: string | undefined, highlightString: string | string[]): {
46-
string: string;
47-
shouldHighlight: boolean;
48-
}[];
49-
getTextPartsByHighlight(targetString?: string, highlightString?: string): {
50-
string: string;
51-
shouldHighlight: boolean;
52-
}[];
53-
getArrayPartsByHighlight(targetString?: string, highlightString?: string[]): {
54-
string: string;
55-
shouldHighlight: boolean;
56-
}[];
5745
renderText(children: any): any;
5846
render(): JSX.Element;
5947
}

generatedTypes/src/utils/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import * as TextUtils from './textUtils';
2+
export { TextUtils };
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
declare function getPartsByHighlight(targetString: string | undefined, highlightString: string | string[]): {
2+
string: string;
3+
shouldHighlight: boolean;
4+
}[];
5+
declare function getTextPartsByHighlight(targetString?: string, highlightString?: string): {
6+
string: string;
7+
shouldHighlight: boolean;
8+
}[];
9+
declare function getArrayPartsByHighlight(targetString?: string, highlightString?: string[]): {
10+
string: string;
11+
shouldHighlight: boolean;
12+
}[];
13+
export { getPartsByHighlight, getTextPartsByHighlight, getArrayPartsByHighlight };

src/.babelrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"alias": {
1010
"commons": "./src/commons",
1111
"helpers": "./src/helpers",
12+
"utils": "./src/utils",
1213
"hooks": "./src/hooks",
1314
"optionalDeps": "./src/optionalDependencies",
1415
"services": "./src/services",

src/components/text/__tests__/index.spec.js

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/components/text/index.tsx

Lines changed: 2 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
ColorsModifiers
1212
} from '../../commons/new';
1313
import {Colors} from 'style';
14+
import {TextUtils} from 'utils';
1415

1516
export type TextProps = RNTextProps &
1617
TypographyModifiers &
@@ -65,72 +66,6 @@ class Text extends PureComponent<PropsTypes> {
6566
// this._root.setNativeProps(nativeProps); // eslint-disable-line
6667
// }
6768

68-
getPartsByHighlight(targetString = '', highlightString: string | string[]) {
69-
if (typeof highlightString === 'string') {
70-
if (_.isEmpty(highlightString.trim())) {
71-
return [{string: targetString, shouldHighlight: false}];
72-
}
73-
return this.getTextPartsByHighlight(targetString, highlightString);
74-
} else {
75-
return this.getArrayPartsByHighlight(targetString, highlightString);
76-
}
77-
}
78-
79-
getTextPartsByHighlight(targetString = '', highlightString = '') {
80-
if (highlightString === '') {
81-
return [{string: targetString, shouldHighlight: false}];
82-
}
83-
const textParts = [];
84-
let highlightIndex;
85-
do {
86-
highlightIndex = targetString.toLowerCase().indexOf(highlightString.toLowerCase());
87-
if (highlightIndex !== -1) {
88-
if (highlightIndex > 0) {
89-
textParts.push({string: targetString.substring(0, highlightIndex), shouldHighlight: false});
90-
}
91-
textParts.push({string: targetString.substr(highlightIndex, highlightString.length), shouldHighlight: true});
92-
targetString = targetString.substr(highlightIndex + highlightString.length);
93-
} else {
94-
textParts.push({string: targetString, shouldHighlight: false});
95-
}
96-
} while (highlightIndex !== -1);
97-
98-
return textParts;
99-
}
100-
101-
getArrayPartsByHighlight(targetString = '', highlightString = ['']) {
102-
const target = _.toLower(targetString);
103-
const indices = [];
104-
let index = 0;
105-
let lastWordLength = 0;
106-
for (let j = 0; j < highlightString.length; j++) {
107-
const word = _.toLower(highlightString[j]);
108-
const targetSuffix = target.substring(index + lastWordLength);
109-
const i = targetSuffix.indexOf(word);
110-
if (i >= 0) {
111-
const newIndex = index + lastWordLength + i;
112-
indices.push({start: index + lastWordLength + i, end: index + lastWordLength + i + word.length});
113-
index = newIndex;
114-
lastWordLength = word.length;
115-
} else {
116-
break;
117-
}
118-
}
119-
const parts = [];
120-
for (let k = 0; k < indices.length; k++) {
121-
if (k === 0 && indices[k].start !== 0) {
122-
parts.push({string: targetString.substring(0, indices[k].start), shouldHighlight: false});
123-
}
124-
parts.push({string: targetString.substring(indices[k].start, indices[k].end), shouldHighlight: true});
125-
if (k === indices.length - 1) {
126-
parts.push({string: targetString.substring(indices[k].end), shouldHighlight: false});
127-
} else {
128-
parts.push({string: targetString.substring(indices[k].end, indices[k + 1].start), shouldHighlight: false});
129-
}
130-
}
131-
return parts;
132-
}
133-
13469
renderText(children: any): any {
13570
const {highlightString, highlightStyle} = this.props;
13671

@@ -142,7 +77,7 @@ class Text extends PureComponent<PropsTypes> {
14277
}
14378

14479
if (_.isString(children)) {
145-
const textParts = highlightString && this.getPartsByHighlight(children, highlightString);
80+
const textParts = highlightString && TextUtils.getPartsByHighlight(children, highlightString);
14681
return (
14782
textParts &&
14883
_.map(textParts, (text, index) => {

src/utils/__tests__/textUtils.spec.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import {getTextPartsByHighlight, getArrayPartsByHighlight} from '../textUtils';
2+
3+
describe('Text', () => {
4+
describe('getTextPartsByHighlight', () => {
5+
it('should return the whole string as a single part when highlight string is undefined', () => {
6+
const result = getTextPartsByHighlight('Playground Screen', undefined);
7+
expect(result).toEqual([{string: 'Playground Screen', shouldHighlight: false}]);
8+
});
9+
it('should return the whole string as a single part when highlight string is empty', () => {
10+
const result = getTextPartsByHighlight('Playground Screen', '');
11+
expect(result).toEqual([{string: 'Playground Screen', shouldHighlight: false}]);
12+
});
13+
it('should return the whole string as a single part when highlight string dont match', () => {
14+
const result = getTextPartsByHighlight('Playground Screen', 'aaa');
15+
expect(result).toEqual([{string: 'Playground Screen', shouldHighlight: false}]);
16+
});
17+
it('should break text to parts according to highlight string', () => {
18+
const result = getTextPartsByHighlight('Playground Screen', 'Scr');
19+
expect(result).toEqual([
20+
{string: 'Playground ', shouldHighlight: false},
21+
{string: 'Scr', shouldHighlight: true},
22+
{string: 'een', shouldHighlight: false}
23+
]);
24+
});
25+
26+
it('should handle case when highlight repeats more than once', () => {
27+
const result = getTextPartsByHighlight('Dancing in the Dark', 'Da');
28+
expect(result).toEqual([
29+
{string: 'Da', shouldHighlight: true},
30+
{string: 'ncing in the ', shouldHighlight: false},
31+
{string: 'Da', shouldHighlight: true},
32+
{string: 'rk', shouldHighlight: false}
33+
]);
34+
});
35+
36+
it('should be case-insensitive', () => {
37+
const result = getTextPartsByHighlight('Dancing in the Dark', 'da');
38+
expect(result).toEqual([
39+
{string: 'Da', shouldHighlight: true},
40+
{string: 'ncing in the ', shouldHighlight: false},
41+
{string: 'Da', shouldHighlight: true},
42+
{string: 'rk', shouldHighlight: false}
43+
]);
44+
});
45+
46+
it('Should handle special characters @', () => {
47+
const result = getTextPartsByHighlight('@ancing in the @ark', '@a');
48+
expect(result).toEqual([
49+
{string: '@a', shouldHighlight: true},
50+
{string: 'ncing in the ', shouldHighlight: false},
51+
{string: '@a', shouldHighlight: true},
52+
{string: 'rk', shouldHighlight: false}
53+
]);
54+
});
55+
56+
it('Should handle special characters !', () => {
57+
const result = getTextPartsByHighlight('!ancing in the !ark', '!a');
58+
expect(result).toEqual([
59+
{string: '!a', shouldHighlight: true},
60+
{string: 'ncing in the ', shouldHighlight: false},
61+
{string: '!a', shouldHighlight: true},
62+
{string: 'rk', shouldHighlight: false}
63+
]);
64+
});
65+
66+
it('Should handle special characters starts with @', () => {
67+
const result = getTextPartsByHighlight('[email protected]', '@wix');
68+
expect(result).toEqual([
69+
{string: 'uilib', shouldHighlight: false},
70+
{string: '@wix', shouldHighlight: true},
71+
{string: '.com', shouldHighlight: false}
72+
]);
73+
});
74+
75+
it('Should handle empty string.', () => {
76+
const result = getTextPartsByHighlight('@ancing in the @ark', '');
77+
expect(result).toEqual([{string: '@ancing in the @ark', shouldHighlight: false}]);
78+
});
79+
80+
it('Should handle full string.', () => {
81+
const result = getTextPartsByHighlight('Dancing in the Dark', 'Dancing in the Dark');
82+
expect(result).toEqual([{string: 'Dancing in the Dark', shouldHighlight: true}]);
83+
});
84+
85+
it('Should handle longer string.', () => {
86+
const result = getTextPartsByHighlight('Dancing in the Dark', 'Dancing in the Darker');
87+
expect(result).toEqual([{string: 'Dancing in the Dark', shouldHighlight: false}]);
88+
});
89+
});
90+
91+
describe('getArrayPartsByHighlight', () => {
92+
it('should return the whole string as a single part when highlight array is empty', () => {
93+
const result = getArrayPartsByHighlight('Playground Screen', []);
94+
expect(result).toEqual([{string: 'Playground Screen', shouldHighlight: false}]);
95+
});
96+
it('should return the whole string as a single part when highlight string is empty', () => {
97+
const result = getArrayPartsByHighlight('Playground Screen', ['']);
98+
expect(result).toEqual([{string: 'Playground Screen', shouldHighlight: false}]);
99+
});
100+
it('should return the whole string as a single part when highlight string dont match', () => {
101+
const result = getArrayPartsByHighlight('Playground Screen', ['aaa']);
102+
expect(result).toEqual([{string: 'Playground Screen', shouldHighlight: false}]);
103+
});
104+
it('should break text to parts according to highlight string', () => {
105+
const result = getArrayPartsByHighlight('Playground Screen', ['Scr']);
106+
expect(result).toEqual([
107+
{string: 'Playground ', shouldHighlight: false},
108+
{string: 'Scr', shouldHighlight: true},
109+
{string: 'een', shouldHighlight: false}
110+
]);
111+
});
112+
113+
it('highlight repeats more than once should color the first match', () => {
114+
const result = getArrayPartsByHighlight('Dancing in the Dark', ['Da']);
115+
expect(result).toEqual([
116+
{string: 'Da', shouldHighlight: true},
117+
{string: 'ncing in the Dark', shouldHighlight: false}
118+
]);
119+
});
120+
121+
it('should be case-insensitive', () => {
122+
const result = getArrayPartsByHighlight('Dancing in the Dark', ['da']);
123+
expect(result).toEqual([
124+
{string: 'Da', shouldHighlight: true},
125+
{string: 'ncing in the Dark', shouldHighlight: false}
126+
]);
127+
});
128+
129+
it('Should handle special characters @', () => {
130+
const result = getArrayPartsByHighlight('@ancing in the @ark', ['@a']);
131+
expect(result).toEqual([
132+
{string: '@a', shouldHighlight: true},
133+
{string: 'ncing in the @ark', shouldHighlight: false}
134+
]);
135+
});
136+
137+
it('Should handle special characters !', () => {
138+
const result = getArrayPartsByHighlight('!ancing in the !ark', ['!a']);
139+
expect(result).toEqual([
140+
{string: '!a', shouldHighlight: true},
141+
{string: 'ncing in the !ark', shouldHighlight: false}
142+
]);
143+
});
144+
145+
it('Should handle special characters starts with @', () => {
146+
const result = getArrayPartsByHighlight('[email protected]', ['@wix']);
147+
expect(result).toEqual([
148+
{string: 'uilib', shouldHighlight: false},
149+
{string: '@wix', shouldHighlight: true},
150+
{string: '.com', shouldHighlight: false}
151+
]);
152+
});
153+
154+
it('Should handle full string.', () => {
155+
const result = getArrayPartsByHighlight('Dancing in the Dark', ['Dancing in the Dark']);
156+
expect(result).toEqual([{string: 'Dancing in the Dark', shouldHighlight: true}]);
157+
});
158+
159+
it('Should handle longer string.', () => {
160+
const result = getArrayPartsByHighlight('Dancing in the Dark', ['Dancing in the Darker']);
161+
expect(result).toEqual([{string: 'Dancing in the Dark', shouldHighlight: false}]);
162+
});
163+
});
164+
});

src/utils/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import * as TextUtils from './textUtils';
2+
3+
export {TextUtils};

0 commit comments

Comments
 (0)