From bdec4d2fa97b272abb74560c3d4c1bc8d87e1825 Mon Sep 17 00:00:00 2001 From: Scott Cooper Date: Fri, 27 Dec 2024 15:23:36 -0800 Subject: [PATCH 1/3] feat(react19): Remove findDomNode in textCopyInput part of https://github.com/getsentry/frontend-tsc/issues/68 --- static/app/components/textCopyInput.spec.tsx | 39 ++++++++++++++++++-- static/app/components/textCopyInput.tsx | 20 ++++------ 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/static/app/components/textCopyInput.spec.tsx b/static/app/components/textCopyInput.spec.tsx index 01dc466730c96c..2648deff824b70 100644 --- a/static/app/components/textCopyInput.spec.tsx +++ b/static/app/components/textCopyInput.spec.tsx @@ -1,10 +1,43 @@ -import {render, screen} from 'sentry-test/reactTestingLibrary'; +import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary'; import TextCopyInput from 'sentry/components/textCopyInput'; describe('TextCopyInput', function () { - it('renders', function () { + beforeEach(() => { + Object.assign(navigator, { + clipboard: { + writeText: jest.fn().mockResolvedValue(''), + }, + }); + }); + + it('copies text to clipboard on click', async function () { + render(Text to Copy); + const button = screen.getByRole('button', {name: 'Copy'}); + expect(button).toBeInTheDocument(); + + await userEvent.click(button); + + expect(navigator.clipboard.writeText).toHaveBeenCalledWith('Text to Copy'); + }); + + it('selects text in input on click', async function () { render(Text to Copy); - expect(screen.getByDisplayValue('Text to Copy')).toBeInTheDocument(); + const input = screen.getByRole('textbox'); + expect(input).toHaveValue('Text to Copy'); + const selectSpy = jest.spyOn(input, 'select'); + + await userEvent.click(input); + + expect(selectSpy).toHaveBeenCalled(); + }); + + it('handles RTL text selection', async function () { + render(Text to Copy); + const input = screen.getByRole('textbox'); + const setSelectionRangeSpy = jest.spyOn(input, 'setSelectionRange'); + + await userEvent.click(input); + expect(setSelectionRangeSpy).toHaveBeenCalledWith(1, input.value.length - 1); }); }); diff --git a/static/app/components/textCopyInput.tsx b/static/app/components/textCopyInput.tsx index 5b32a3fb84981b..e3b37794994c8a 100644 --- a/static/app/components/textCopyInput.tsx +++ b/static/app/components/textCopyInput.tsx @@ -1,5 +1,4 @@ -import {useCallback, useRef} from 'react'; -import {findDOMNode} from 'react-dom'; +import {useCallback, useId} from 'react'; import styled from '@emotion/styled'; import {CopyToClipboardButton} from 'sentry/components/copyToClipboardButton'; @@ -33,17 +32,11 @@ function TextCopyInput({ children, ...inputProps }: Props) { - const textRef = useRef(null); + const textNodeId = useId(); const handleSelectText = useCallback(() => { - if (!textRef.current) { - return; - } - - // We use findDOMNode here because `this.textRef` is not a dom node, - // it's a ref to AutoSelectText - const node = findDOMNode(textRef.current); // eslint-disable-line react/no-find-dom-node - if (!node || !(node instanceof HTMLElement)) { + const node = document.getElementById(textNodeId); + if (!node) { return; } @@ -53,7 +46,7 @@ function TextCopyInput({ } else { selectText(node); } - }, [rtl]); + }, [rtl, textNodeId]); /** * We are using direction: rtl; to always show the ending of a long overflowing text in input. @@ -67,10 +60,11 @@ function TextCopyInput({ return ( + hello Date: Fri, 27 Dec 2024 15:24:20 -0800 Subject: [PATCH 2/3] lol --- static/app/components/textCopyInput.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/app/components/textCopyInput.tsx b/static/app/components/textCopyInput.tsx index e3b37794994c8a..536d3c7921a853 100644 --- a/static/app/components/textCopyInput.tsx +++ b/static/app/components/textCopyInput.tsx @@ -60,7 +60,6 @@ function TextCopyInput({ return ( - hello Date: Thu, 2 Jan 2025 10:47:40 -0800 Subject: [PATCH 3/3] cast type instead of check --- static/app/components/textCopyInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/app/components/textCopyInput.tsx b/static/app/components/textCopyInput.tsx index 536d3c7921a853..c21915c274b4c7 100644 --- a/static/app/components/textCopyInput.tsx +++ b/static/app/components/textCopyInput.tsx @@ -35,12 +35,12 @@ function TextCopyInput({ const textNodeId = useId(); const handleSelectText = useCallback(() => { - const node = document.getElementById(textNodeId); + const node = document.getElementById(textNodeId) as HTMLInputElement | null; if (!node) { return; } - if (rtl && node instanceof HTMLInputElement) { + if (rtl) { // we don't want to select the first character - \u202A, nor the last - \u202C node.setSelectionRange(1, node.value.length - 1); } else {