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]);