diff --git a/changelogs/upcoming/7495.md b/changelogs/upcoming/7495.md
new file mode 100644
index 00000000000..edfe954e183
--- /dev/null
+++ b/changelogs/upcoming/7495.md
@@ -0,0 +1,3 @@
+**Bug fixes**
+
+- Fixed `EuiTextTruncate` component to clean up timer from side effect on unmount
\ No newline at end of file
diff --git a/src/components/text_truncate/text_truncate.test.tsx b/src/components/text_truncate/text_truncate.test.tsx
index 640ab4afdbe..52c6af87a61 100644
--- a/src/components/text_truncate/text_truncate.test.tsx
+++ b/src/components/text_truncate/text_truncate.test.tsx
@@ -38,18 +38,51 @@ describe('EuiTextTruncate', () => {
expect(container.firstChild).toMatchSnapshot();
});
- it('allows delaying truncation calculation by `calculationDelayMs`', () => {
- jest.useFakeTimers();
+ describe('calculationDelayMs', () => {
+ beforeAll(jest.useFakeTimers);
+ afterAll(jest.useRealTimers);
- const { queryByTestSubject } = render(
-
- );
- expect(queryByTestSubject('truncatedText')).not.toBeInTheDocument();
+ it('allows delaying truncation calculation by the specified duration', () => {
+ const { queryByTestSubject } = render(
+
+ );
+ expect(queryByTestSubject('truncatedText')).not.toBeInTheDocument();
+
+ act(() => jest.advanceTimersByTime(50));
+ expect(queryByTestSubject('truncatedText')).toBeInTheDocument();
+ });
+
+ it('sets and clears the timeout on update or unmount', () => {
+ const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout');
+
+ const { unmount, rerender } = render(
+
+ );
+ expect(clearTimeoutSpy).toHaveBeenCalledTimes(0);
- act(() => jest.advanceTimersByTime(50));
- expect(queryByTestSubject('truncatedText')).toBeInTheDocument();
+ rerender(
+
+ );
+ expect(clearTimeoutSpy).toHaveBeenCalledTimes(1);
+ expect(clearTimeoutSpy).toHaveBeenLastCalledWith(expect.any(Number));
+
+ unmount();
+ expect(clearTimeoutSpy).toHaveBeenCalledTimes(2);
+ expect(clearTimeoutSpy).toHaveBeenLastCalledWith(expect.any(Number));
+ });
+
+ it('does not set or clear a timeout if a duration is not passed', () => {
+ const setTimeoutSpy = jest.spyOn(window, 'setTimeout');
+ const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout');
- jest.useRealTimers();
+ const { unmount } = render(
+
+ );
+
+ expect(setTimeoutSpy).not.toHaveBeenCalled();
+ unmount();
+ expect(clearTimeoutSpy).not.toHaveBeenCalled();
+ });
});
describe('resize observer', () => {
diff --git a/src/components/text_truncate/text_truncate.tsx b/src/components/text_truncate/text_truncate.tsx
index b2b38355e40..30022a43a8d 100644
--- a/src/components/text_truncate/text_truncate.tsx
+++ b/src/components/text_truncate/text_truncate.tsx
@@ -139,7 +139,8 @@ const EuiTextTruncateWithWidth: FunctionComponent<
const [ready, setReady] = useState(!calculationDelayMs);
useEffect(() => {
if (calculationDelayMs) {
- setTimeout(() => setReady(true), calculationDelayMs);
+ const timerId = setTimeout(() => setReady(true), calculationDelayMs);
+ return () => clearTimeout(timerId);
}
}, [calculationDelayMs]);