From a940a60c684ad729bcfea47cacf974de1804fe2e Mon Sep 17 00:00:00 2001 From: Yuriy Demidov Date: Tue, 20 Oct 2026 20:00:07 +0300 Subject: [PATCH 1/3] fix(Gallery): show feedback after copying a link --- src/components/Gallery/README.md | 6 +- .../getGalleryItemCopyLinkAction.test.tsx | 72 +++++++++++++++++++ .../utils/getGalleryItemCopyLinkAction.tsx | 30 ++++---- 3 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 src/components/Gallery/__tests__/getGalleryItemCopyLinkAction.test.tsx diff --git a/src/components/Gallery/README.md b/src/components/Gallery/README.md index 4af211cd..a5639583 100644 --- a/src/components/Gallery/README.md +++ b/src/components/Gallery/README.md @@ -139,7 +139,9 @@ import { actions={[ getGalleryItemCopyLinkAction({ copyUrl: 'https://example.jpg', - onCopy: () => console.log('link copied'), + onCopy: (text, success) => { + if (success) console.log('Link copied:', text); + }, }), getGalleryItemDownloadAction({ downloadUrl: 'https://example.jpg', @@ -149,6 +151,8 @@ import { /> ``` +The copy action shows feedback on success. `onCopy(text, success)` reports the result. + ### Examples #### Simple images gallery diff --git a/src/components/Gallery/__tests__/getGalleryItemCopyLinkAction.test.tsx b/src/components/Gallery/__tests__/getGalleryItemCopyLinkAction.test.tsx new file mode 100644 index 00000000..c0402cfd --- /dev/null +++ b/src/components/Gallery/__tests__/getGalleryItemCopyLinkAction.test.tsx @@ -0,0 +1,72 @@ +import {MobileProvider} from '@gravity-ui/uikit'; +import userEvent from '@testing-library/user-event'; + +import {render, screen, waitFor} from '../../../../test-utils/utils'; +import {Gallery} from '../Gallery'; +import {GalleryItem} from '../GalleryItem'; +import {getGalleryItemCopyLinkAction} from '../utils/getGalleryItemCopyLinkAction'; + +const copyUrl = 'https://example.com/image.png'; + +describe('getGalleryItemCopyLinkAction', () => { + beforeAll(() => { + HTMLElement.prototype.scrollIntoView = jest.fn(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should show copy feedback and report the copied URL', async () => { + const user = userEvent.setup(); + const onCopy = jest.fn(); + renderCopyAction(onCopy); + + const button = screen.getByRole('button', {name: 'Copy link'}); + await user.hover(button); + expect(await screen.findByText('Copy link')).toBeVisible(); + await user.click(button); + + expect(onCopy).toHaveBeenCalledWith(copyUrl, true); + expect(await navigator.clipboard.readText()).toBe(copyUrl); + expect(await screen.findByText('Copied')).toBeVisible(); + }); + + it('should keep the copy label and action in the mobile menu', async () => { + const user = userEvent.setup(); + renderCopyAction(undefined, true); + + await user.click(screen.getByRole('button', {name: 'Close'})); + const button = await screen.findByRole('button', {name: 'Copy link'}); + expect(button).toHaveTextContent('Copy link'); + await user.click(button); + + expect(await navigator.clipboard.readText()).toBe(copyUrl); + }); + + it('should report a failed copy', async () => { + const user = userEvent.setup(); + jest.spyOn(navigator.clipboard, 'writeText').mockRejectedValue(new Error('Copy denied')); + const onCopy = jest.fn(); + renderCopyAction(onCopy); + + await user.click(screen.getByRole('button', {name: 'Copy link'})); + + await waitFor(() => expect(onCopy).toHaveBeenCalledWith(copyUrl, false)); + expect(screen.queryByText('Copied')).not.toBeInTheDocument(); + }); +}); + +function renderCopyAction(onCopy?: (text: string, success: boolean) => void, mobile = false) { + return render( + + + Image} + thumbnail={Thumbnail} + actions={[getGalleryItemCopyLinkAction({copyUrl, onCopy})]} + /> + + , + ); +} diff --git a/src/components/Gallery/utils/getGalleryItemCopyLinkAction.tsx b/src/components/Gallery/utils/getGalleryItemCopyLinkAction.tsx index b7e13749..93de3223 100644 --- a/src/components/Gallery/utils/getGalleryItemCopyLinkAction.tsx +++ b/src/components/Gallery/utils/getGalleryItemCopyLinkAction.tsx @@ -1,11 +1,10 @@ -import {Link} from '@gravity-ui/icons'; -import {ActionTooltip, Button, CopyToClipboard, Icon} from '@gravity-ui/uikit'; +import {ClipboardButton, type ClipboardButtonProps} from '@gravity-ui/uikit'; import {GalleryItemAction} from '../GalleryItem'; export type GetGalleryItemCopyLinkActionArgs = { copyUrl: string; - onCopy?: () => void; + onCopy?: ClipboardButtonProps['onCopy']; }; export function getGalleryItemCopyLinkAction({ @@ -16,17 +15,18 @@ export function getGalleryItemCopyLinkAction({ id: 'copy-url', title: 'copy-url', __titleT: ({t}) => t('copy-link'), - icon: , - __renderT: (props, {t}) => ( - - {() => ( -
- -
- )} -
- ), + icon: null, + __renderT: (props, {t}) => { + if (props.href !== undefined) return null; + + return ( + + ); + }, }; } From 3b9407a2fb0d6556eda2e6530b3ac3c1e9e26628 Mon Sep 17 00:00:00 2001 From: Yuriy Demidov Date: Wed, 21 Oct 2026 12:38:07 +0300 Subject: [PATCH 2/3] test: remove gallery copy link test --- .../getGalleryItemCopyLinkAction.test.tsx | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 src/components/Gallery/__tests__/getGalleryItemCopyLinkAction.test.tsx diff --git a/src/components/Gallery/__tests__/getGalleryItemCopyLinkAction.test.tsx b/src/components/Gallery/__tests__/getGalleryItemCopyLinkAction.test.tsx deleted file mode 100644 index c0402cfd..00000000 --- a/src/components/Gallery/__tests__/getGalleryItemCopyLinkAction.test.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import {MobileProvider} from '@gravity-ui/uikit'; -import userEvent from '@testing-library/user-event'; - -import {render, screen, waitFor} from '../../../../test-utils/utils'; -import {Gallery} from '../Gallery'; -import {GalleryItem} from '../GalleryItem'; -import {getGalleryItemCopyLinkAction} from '../utils/getGalleryItemCopyLinkAction'; - -const copyUrl = 'https://example.com/image.png'; - -describe('getGalleryItemCopyLinkAction', () => { - beforeAll(() => { - HTMLElement.prototype.scrollIntoView = jest.fn(); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should show copy feedback and report the copied URL', async () => { - const user = userEvent.setup(); - const onCopy = jest.fn(); - renderCopyAction(onCopy); - - const button = screen.getByRole('button', {name: 'Copy link'}); - await user.hover(button); - expect(await screen.findByText('Copy link')).toBeVisible(); - await user.click(button); - - expect(onCopy).toHaveBeenCalledWith(copyUrl, true); - expect(await navigator.clipboard.readText()).toBe(copyUrl); - expect(await screen.findByText('Copied')).toBeVisible(); - }); - - it('should keep the copy label and action in the mobile menu', async () => { - const user = userEvent.setup(); - renderCopyAction(undefined, true); - - await user.click(screen.getByRole('button', {name: 'Close'})); - const button = await screen.findByRole('button', {name: 'Copy link'}); - expect(button).toHaveTextContent('Copy link'); - await user.click(button); - - expect(await navigator.clipboard.readText()).toBe(copyUrl); - }); - - it('should report a failed copy', async () => { - const user = userEvent.setup(); - jest.spyOn(navigator.clipboard, 'writeText').mockRejectedValue(new Error('Copy denied')); - const onCopy = jest.fn(); - renderCopyAction(onCopy); - - await user.click(screen.getByRole('button', {name: 'Copy link'})); - - await waitFor(() => expect(onCopy).toHaveBeenCalledWith(copyUrl, false)); - expect(screen.queryByText('Copied')).not.toBeInTheDocument(); - }); -}); - -function renderCopyAction(onCopy?: (text: string, success: boolean) => void, mobile = false) { - return render( - - - Image} - thumbnail={Thumbnail} - actions={[getGalleryItemCopyLinkAction({copyUrl, onCopy})]} - /> - - , - ); -} From de7d3de454691c7c849c36197425be9da11f6e2e Mon Sep 17 00:00:00 2001 From: Yuriy Demidov Date: Wed, 21 Oct 2026 13:02:36 +0300 Subject: [PATCH 3/3] fix(Gallery): use link icon for copy action --- .../Gallery/utils/getGalleryItemCopyLinkAction.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Gallery/utils/getGalleryItemCopyLinkAction.tsx b/src/components/Gallery/utils/getGalleryItemCopyLinkAction.tsx index 93de3223..1d0fef35 100644 --- a/src/components/Gallery/utils/getGalleryItemCopyLinkAction.tsx +++ b/src/components/Gallery/utils/getGalleryItemCopyLinkAction.tsx @@ -1,4 +1,5 @@ -import {ClipboardButton, type ClipboardButtonProps} from '@gravity-ui/uikit'; +import {Link} from '@gravity-ui/icons'; +import {ClipboardButton, type ClipboardButtonProps, Icon} from '@gravity-ui/uikit'; import {GalleryItemAction} from '../GalleryItem'; @@ -15,7 +16,7 @@ export function getGalleryItemCopyLinkAction({ id: 'copy-url', title: 'copy-url', __titleT: ({t}) => t('copy-link'), - icon: null, + icon: , __renderT: (props, {t}) => { if (props.href !== undefined) return null; @@ -25,6 +26,7 @@ export function getGalleryItemCopyLinkAction({ text={copyUrl} tooltipInitialText={t('copy-link')} onCopy={onCopy} + icon={} /> ); },