From 0ec3110d41adbf58afdd82a9a6ecd709588ec4df Mon Sep 17 00:00:00 2001 From: Collin Fingar Date: Wed, 24 Sep 2025 12:16:23 -0400 Subject: [PATCH] Dashboard Panel: Expose Image Renderer URL to Copy (#111502) * Dashboard Panel: Expose Image Renderer URL to Copy * Fixed i18n * Feedback fixes * Tweaked analytics event --- .../panel-share/SharePanelInternally.test.tsx | 14 ++++++++++++++ .../sharing/panel-share/SharePanelPreview.tsx | 12 +++++++++++- .../features/dashboard-scene/utils/interactions.ts | 3 +++ public/locales/en-US/grafana.json | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard-scene/sharing/panel-share/SharePanelInternally.test.tsx b/public/app/features/dashboard-scene/sharing/panel-share/SharePanelInternally.test.tsx index a02ed3e5a1d..8f26a824e19 100644 --- a/public/app/features/dashboard-scene/sharing/panel-share/SharePanelInternally.test.tsx +++ b/public/app/features/dashboard-scene/sharing/panel-share/SharePanelInternally.test.tsx @@ -30,6 +30,7 @@ describe('SharePanelInternally', () => { expect(screen.getByPlaceholderText('1')).toBeDisabled(); // Scale factor input expect(screen.getByRole('button', { name: /generate image/i })).toBeDisabled(); expect(screen.getByRole('button', { name: /download image/i })).toBeDisabled(); + expect(screen.getByRole('button', { name: /copy image link/i })).toBeDisabled(); }); it('should enable all image generation inputs when renderer is available', async () => { @@ -55,6 +56,19 @@ describe('SharePanelInternally', () => { expect(screen.getByRole('button', { name: /generate image/i })).toBeEnabled(); expect(screen.getByRole('button', { name: /download image/i })).toBeDisabled(); + expect(screen.getByRole('button', { name: /copy image link/i })).toBeEnabled(); + }); + + it('should copy image link when copy image link button is clicked', async () => { + document.execCommand = jest.fn(); + + config.rendererAvailable = true; + buildAndRenderScenario(); + + const copyImageLinkButton = screen.getByRole('button', { name: /copy image link/i }); + await userEvent.click(copyImageLinkButton); + + expect(document.execCommand).toHaveBeenCalledWith('copy'); }); }); diff --git a/public/app/features/dashboard-scene/sharing/panel-share/SharePanelPreview.tsx b/public/app/features/dashboard-scene/sharing/panel-share/SharePanelPreview.tsx index 57ea1bde7af..2fdc7bb89b2 100644 --- a/public/app/features/dashboard-scene/sharing/panel-share/SharePanelPreview.tsx +++ b/public/app/features/dashboard-scene/sharing/panel-share/SharePanelPreview.tsx @@ -8,7 +8,7 @@ import { lastValueFrom } from 'rxjs'; import { GrafanaTheme2, UrlQueryMap } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { config, getBackendSrv } from '@grafana/runtime'; -import { Button, Field, FieldSet, Icon, Input, Stack, Text, Tooltip, useStyles2 } from '@grafana/ui'; +import { Button, ClipboardButton, Field, FieldSet, Icon, Input, Stack, Text, Tooltip, useStyles2 } from '@grafana/ui'; import { DashboardInteractions } from '../../utils/interactions'; import { ImagePreview } from '../components/ImagePreview'; @@ -196,6 +196,16 @@ export function SharePanelPreview({ title, imageUrl, buildUrl, disabled, theme } > Download image + imageUrl} + onClipboardCopy={() => DashboardInteractions.copyImageUrlClicked({ shareResource: 'panel' })} + > + Copy image link + {disabled && ( diff --git a/public/app/features/dashboard-scene/utils/interactions.ts b/public/app/features/dashboard-scene/utils/interactions.ts index 9990c4e197e..67615b083d1 100644 --- a/public/app/features/dashboard-scene/utils/interactions.ts +++ b/public/app/features/dashboard-scene/utils/interactions.ts @@ -187,6 +187,9 @@ export const DashboardInteractions = { downloadDashboardImageClicked: (properties?: Record) => { reportDashboardInteraction('dashboard_image_downloaded', properties); }, + copyImageUrlClicked: (properties?: Record) => { + reportDashboardInteraction('dashboard_image_url_copied', properties); + }, }; const reportDashboardInteraction: typeof reportInteraction = (name, properties) => { diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 95d771db840..c9dfbd95d55 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -9347,6 +9347,7 @@ }, "share-panel": { "config-description": "Create a personalized, direct link to share your panel within your organization, with the following customization settings:", + "copy-image-link": "Copy image link", "download-image": "Download image", "render-image": "Generate image" }