Skip to content

Commit d0494b5

Browse files
TT-14336: Update text ellipsis with functionality to truncate values in front and in the end (#416)
* feat: add truncateFrom property to support dots and truncation logic * build: add alpha version * feat: add className property to text ellipsis * feat: add style and overlayClassName property to ellipsis to make it possible to pass z-index to floating container * build: changes after prod build run and version grade without alpha suffix --------- Co-authored-by: Vlad Zabolotnyi <vlad.z@tyk.io>
1 parent 81dd7bf commit d0494b5

9 files changed

Lines changed: 213 additions & 38 deletions

File tree

lib/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/tyk-ui.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/tyk-ui.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tyk-technologies/tyk-ui",
3-
"version": "4.4.25",
3+
"version": "4.4.26",
44
"description": "Tyk UI - ui reusable components",
55
"main": "src/index.js",
66
"scripts": {
Lines changed: 165 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,180 @@
1-
import React from 'react';
2-
import TextEllipsis from './index';
1+
import React from "react";
2+
import TextEllipsis from "./index";
33

4-
function Component() {
4+
/* eslint-disable react/prop-types */
5+
function Component({ truncateFrom = "end" }) {
56
return (
6-
<div className="text-ellipsis" style={{ marginTop: '200px', textAlign: 'center' }}>
7+
<div
8+
className="text-ellipsis"
9+
style={{ marginTop: "200px", textAlign: "center" }}
10+
>
711
<TextEllipsis
812
text="alpha, bravo, charlie, delta, forgot the rest"
913
limit={10}
14+
truncateFrom={truncateFrom}
1015
/>
1116
</div>
1217
);
1318
}
1419

15-
describe('TextEllipsis', () => {
20+
function TruncateFromStartComponent() {
21+
return (
22+
<div
23+
className="text-ellipsis-start"
24+
style={{ marginTop: "200px", textAlign: "center" }}
25+
>
26+
<TextEllipsis
27+
text="alpha, bravo, charlie, delta, forgot the rest"
28+
limit={8}
29+
truncateFrom="start"
30+
/>
31+
</div>
32+
);
33+
}
34+
35+
function ShortTextComponent() {
36+
return (
37+
<div
38+
className="text-ellipsis-short"
39+
style={{ marginTop: "200px", textAlign: "center" }}
40+
>
41+
<TextEllipsis text="short" limit={10} truncateFrom="start" />
42+
</div>
43+
);
44+
}
45+
46+
describe("TextEllipsis", () => {
1647
const classes = {
17-
floatingContainer: 'floating-container',
48+
floatingContainer: "floating-container",
1849
};
19-
it('should show just first 10 chars followed by "..."', () => {
20-
cy.mount(<Component />)
21-
.get('.text-ellipsis')
22-
.should('have.text', 'alpha, bra...');
50+
51+
describe("Default behavior (truncate from end)", () => {
52+
it('should show first 10 chars followed by "..." when no truncateFrom is provided', () => {
53+
cy.mount(<Component />)
54+
.get(".text-ellipsis")
55+
.should("have.text", "alpha, bra...");
56+
});
57+
58+
it('should show first 10 chars followed by "..." when truncateFrom is explicitly set to end', () => {
59+
cy.mount(<Component truncateFrom="end" />)
60+
.get(".text-ellipsis")
61+
.should("have.text", "alpha, bra...");
62+
});
63+
64+
it("when hovering the truncated text, shows the tooltip with full text", () => {
65+
cy.mount(<Component />);
66+
cy.get(`.${classes.floatingContainer}`)
67+
.should("not.exist")
68+
.get(".text-ellipsis")
69+
.trigger("mouseover");
70+
71+
cy.get(`.${classes.floatingContainer}`)
72+
.should(
73+
"contain.text",
74+
"alpha, bravo, charlie, delta, forgot the rest",
75+
);
76+
});
77+
});
78+
79+
describe("Truncate from start behavior", () => {
80+
it('should show "..." at the beginning followed by last 10 chars when truncateFrom is start', () => {
81+
cy.mount(<TruncateFromStartComponent />)
82+
.get(".text-ellipsis-start")
83+
.should("have.text", "...the rest");
84+
});
85+
86+
it("when hovering the start-truncated text, shows the tooltip with full text", () => {
87+
cy.mount(<TruncateFromStartComponent />);
88+
cy.get(`.${classes.floatingContainer}`)
89+
.should("not.exist")
90+
.get(".text-ellipsis-start")
91+
.trigger("mouseover");
92+
93+
cy.get(`.${classes.floatingContainer}`)
94+
.should(
95+
"contain.text",
96+
"alpha, bravo, charlie, delta, forgot the rest",
97+
);
98+
});
99+
100+
it("should handle certificate-like IDs properly when truncating from start", () => {
101+
const certificateId = "69ba61ddb8365d96f9917d7222cee600fadd3b0cd35d060b20e33182f7edae9f3a0373def3f4a50004d181ad";
102+
cy.mount(
103+
<div className="certificate-test">
104+
<TextEllipsis text={certificateId} limit={10} truncateFrom="start" />
105+
</div>,
106+
)
107+
.get(".certificate-test")
108+
.should("have.text", "...0004d181ad");
109+
});
23110
});
24-
it('when hovering the shrinked texts, shows the tooltip with full text', () => {
25-
cy.mount(<Component />);
26-
cy.get(`.${classes.floatingContainer}`)
27-
.should('not.exist')
28-
.get('.text-ellipsis')
29-
.trigger('mouseover');
30-
31-
cy.get(`.${classes.floatingContainer}`)
32-
.should('exist');
111+
112+
describe("Edge cases", () => {
113+
it("should display full text without ellipsis when text length is less than limit", () => {
114+
cy.mount(<ShortTextComponent />)
115+
.get(".text-ellipsis-short")
116+
.should("have.text", "short")
117+
.should("not.contain", "...");
118+
});
119+
120+
it("should display full text without ellipsis when text length equals limit", () => {
121+
cy.mount(
122+
<div className="text-ellipsis-equal">
123+
<TextEllipsis text="exactly10c" limit={10} truncateFrom="start" />
124+
</div>,
125+
)
126+
.get(".text-ellipsis-equal")
127+
.should("have.text", "exactly10c")
128+
.should("not.contain", "...");
129+
});
130+
131+
it("should handle single character limit correctly for truncateFrom start", () => {
132+
cy.mount(
133+
<div className="single-char-start">
134+
<TextEllipsis text="hello world" limit={1} truncateFrom="start" />
135+
</div>,
136+
)
137+
.get(".single-char-start")
138+
.should("have.text", "...d");
139+
});
140+
141+
it("should handle single character limit correctly for truncateFrom end", () => {
142+
cy.mount(
143+
<div className="single-char-end">
144+
<TextEllipsis text="hello world" limit={1} truncateFrom="end" />
145+
</div>,
146+
)
147+
.get(".single-char-end")
148+
.should("have.text", "h...");
149+
});
150+
});
151+
152+
describe("Backward compatibility", () => {
153+
it("should maintain existing behavior when truncateFrom prop is not provided", () => {
154+
cy.mount(
155+
<div className="text-ellipsis-backward">
156+
<TextEllipsis
157+
text="this is a long text for testing backward compatibility"
158+
limit={15}
159+
/>
160+
</div>,
161+
)
162+
.get(".text-ellipsis-backward")
163+
.should("have.text", "this is a long ...");
164+
});
165+
166+
it("should work with existing props combination", () => {
167+
cy.mount(
168+
<div className="existing-usage">
169+
<TextEllipsis
170+
text="existing implementation should still work"
171+
limit={8}
172+
position="top"
173+
/>
174+
</div>,
175+
)
176+
.get(".existing-usage")
177+
.should("have.text", "existing...");
178+
});
33179
});
34180
});
Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,58 @@
1-
import React from 'react';
2-
import PropTypes from 'prop-types';
3-
import Tooltip from '../Tooltip';
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
import Tooltip from "../Tooltip";
44
/**
55
* TextEllipsis component helps you to hide a part of a text,
66
* but displaying it when it's being hovered.
77
* The entire text is displayed with the help of Tooltip component
88
*/
99

10-
function TextEllipsis({ text, limit, position }) {
10+
function TextEllipsis({
11+
style,
12+
className,
13+
overlayClassName,
14+
text,
15+
limit,
16+
position,
17+
truncateFrom = "end",
18+
}) {
19+
if (text.length <= limit) {
20+
return text;
21+
}
22+
23+
let displayText;
24+
25+
if (truncateFrom === "start") {
26+
displayText = `...${text.substring(text.length - limit)}`;
27+
} else {
28+
displayText = `${text.substring(0, limit)}...`;
29+
}
30+
1131
return (
12-
text.length > limit
13-
? (
14-
<Tooltip render={text} position={position}>
15-
{text.substring(0, limit)}
16-
...
17-
</Tooltip>
18-
)
19-
: text
32+
<Tooltip
33+
style={style}
34+
overlayClassName={overlayClassName}
35+
className={className}
36+
render={text}
37+
position={position}
38+
>
39+
{displayText}
40+
</Tooltip>
2041
);
2142
}
2243

2344
TextEllipsis.propTypes = {
45+
style: PropTypes.instanceOf(Object),
46+
className: PropTypes.string,
47+
overlayClassName: PropTypes.string,
2448
/** Text to be shrinked by TextEllipsis */
2549
text: PropTypes.string,
2650
/** Number of characters that TextEllipsis would leave visible */
2751
limit: PropTypes.number,
2852
/** tooltip position */
2953
position: PropTypes.string,
54+
/** Where to truncate text from - 'end' (default) shows beginning of text, 'start' shows end of text */
55+
truncateFrom: PropTypes.oneOf(["end", "start"]),
3056
};
3157

3258
export default TextEllipsis;

src/components/Tooltip/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Icon from '../Icon';
99
function Tooltip({
1010
render,
1111
className,
12+
overlayClassName,
1213
children,
1314
position = 'auto',
1415
style,
@@ -60,6 +61,7 @@ function Tooltip({
6061
{children}
6162
{isActive && (
6263
<FloatingContainer
64+
className={overlayClassName}
6365
element={wrapperRef}
6466
forceDisplay={position}
6567
preferredPosition="top"
@@ -107,6 +109,7 @@ Tooltip.propTypes = {
107109
]),
108110
/** additional tooltip classes */
109111
className: PropTypes.string,
112+
overlayClassName: PropTypes.string,
110113
/** if `true` displays the "question mark" icon;
111114
* optionally you can pass a different icon
112115
*/

0 commit comments

Comments
 (0)