From 25525e5125f3c02b0823c4a0c4483f7e6e2cfaf9 Mon Sep 17 00:00:00 2001 From: Oana Badiu Date: Thu, 21 Nov 2024 17:44:05 +0200 Subject: [PATCH] Add onCopy property to Copiable component --- package-lock.json | 4 +- package.json | 2 +- src/components/copiable/copiable.test.tsx | 48 +++++++++++++++++++---- src/components/copiable/copiable.tsx | 32 +++++++++++---- 4 files changed, 69 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ea242ff..44ea86af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mapbox/mr-ui", - "version": "2.11.0", + "version": "2.12.0", "lockfileVersion": 2, "requires": true, "packages": { @@ -49294,4 +49294,4 @@ "dev": true } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index a86fb2e0..b5e7e4f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/mr-ui", - "version": "2.11.0", + "version": "2.12.0", "description": "UI components for Mapbox projects", "main": "index.js", "homepage": "./", diff --git a/src/components/copiable/copiable.test.tsx b/src/components/copiable/copiable.test.tsx index 94399f63..6ed4fec9 100644 --- a/src/components/copiable/copiable.test.tsx +++ b/src/components/copiable/copiable.test.tsx @@ -4,6 +4,7 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import CopyButton from '../copy-button'; import Copiable from './copiable'; import select from 'select'; +import userEvent from '@testing-library/user-event'; const FEEDBACK_TIME = 2000; @@ -13,20 +14,20 @@ jest.mock('os-key', () => ); describe('Copiable', () => { - describe('basic', () => { const props = { - value: 'thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy ' + value: + 'thetextyoucopythetextyoucopythetextyoucopythetext you copy the text you copy ' }; test('renders as expected', () => { - const { baseElement } = render() + const { baseElement } = render(); expect(baseElement).toMatchSnapshot(); }); test('calls select library with element holding text to copy', async () => { jest.spyOn(CopyButton, 'isCopySupported').mockImplementation(() => true); - render() + render(); act(() => { screen.getByTestId('copiable-text-el').focus(); @@ -39,7 +40,7 @@ describe('Copiable', () => { test('shows copy hint when focused', async () => { jest.spyOn(CopyButton, 'isCopySupported').mockImplementation(() => true); - render() + render(); act(() => { screen.getByTestId('copiable-text-el').focus(); @@ -58,12 +59,45 @@ describe('Copiable', () => { describe('truncated', () => { const props = { truncated: true, - value: 'the text you copy the text you copy the text you copy the text you copy the text you copy ' + value: + 'the text you copy the text you copy the text you copy the text you copy the text you copy ' }; test('renders as expected', () => { - const { baseElement } = render() + const { baseElement } = render(); expect(baseElement).toMatchSnapshot(); }); }); + + describe('onCopy', () => { + const mockOnCopy = jest.fn(); + const text = + 'the text you copy the text you copy the text you copy the text you copy the text you copy'; + + const props = { + onCopy: mockOnCopy, + value: text + }; + + test('calls onCopy', async () => { + jest.spyOn(CopyButton, 'isCopySupported').mockImplementation(() => true); + render(); + + await userEvent.click(screen.getByTestId('copy-button')); + + expect(mockOnCopy).toHaveBeenCalledTimes(1); + expect(mockOnCopy).toHaveBeenCalledWith(text); + }); + + test('calls onCopy when text is truncated', async () => { + jest.spyOn(CopyButton, 'isCopySupported').mockImplementation(() => true); + const propsWithTruncated = { ...props, truncated: true }; + render(); + + await userEvent.click(screen.getByTestId('copy-button')); + + expect(mockOnCopy).toHaveBeenCalledTimes(1); + expect(mockOnCopy).toHaveBeenCalledWith(text); + }); + }); }); diff --git a/src/components/copiable/copiable.tsx b/src/components/copiable/copiable.tsx index d64987e5..56863c01 100644 --- a/src/components/copiable/copiable.tsx +++ b/src/components/copiable/copiable.tsx @@ -1,4 +1,10 @@ -import React, { ReactElement, useState, useEffect, useRef, CSSProperties } from 'react'; +import React, { + ReactElement, + useState, + useEffect, + useRef, + CSSProperties +} from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import CopyButton from '../copy-button'; @@ -25,6 +31,7 @@ interface Props { value: string; focusTrapPaused?: boolean; truncated?: boolean; + onCopy?: (text: string) => void; } /** @@ -39,6 +46,7 @@ interface Props { export default function Copiable({ value, focusTrapPaused, + onCopy, truncated = false }: Props): ReactElement { const textEl = useRef(null); @@ -58,7 +66,7 @@ export default function Copiable({ }, [copyTooltipActive]); const handleTextFocus = () => { - if (typeof window === 'undefined') return + if (typeof window === 'undefined') return; if (window.innerWidth < DISABLE_CLICK_TO_SELECT_THRESHOLD) return; select(() => textEl.current); setCopyTooltipActive(true); @@ -76,12 +84,13 @@ export default function Copiable({ text={value} block={true} focusTrapPaused={focusTrapPaused} + onCopy={onCopy} /> ); const renderCopyHintText = () => { - if (typeof window === 'undefined') return + if (typeof window === 'undefined') return; return ( @@ -90,8 +99,8 @@ export default function Copiable({ {' '} to copy - ) - } + ); + }; const textClasses = classnames('my3 txt-mono txt-s mr24', { 'txt-truncate': truncated @@ -106,7 +115,11 @@ export default function Copiable({
{showCopyButton.current && renderCopyButton} {showCopyButton.current && renderCopyHintText}
} + content={ +
+ {showCopyButton.current && renderCopyHintText} +
+ } active={copyTooltipActive} placement="top" alignment="center" @@ -158,5 +171,10 @@ Copiable.propTypes = { * Horizontal scrolling is not an option because of things end up getting * pretty gross across browsers. */ - truncated: PropTypes.bool + truncated: PropTypes.bool, + /** + * Invoked when the button is clicked. + * Passed one argument: the `text` prop. + */ + onCopy: PropTypes.func };