Dashboard Panel: Expose Image Renderer URL to Copy (#111502)

* Dashboard Panel: Expose Image Renderer URL to Copy

* Fixed i18n

* Feedback fixes

* Tweaked analytics event
This commit is contained in:
Collin Fingar
2025-09-24 12:16:23 -04:00
committed by GitHub
parent b2f09a41ad
commit 0ec3110d41
4 changed files with 29 additions and 1 deletions
@@ -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');
});
});
@@ -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 }
>
<Trans i18nKey="link.share-panel.download-image">Download image</Trans>
</Button>
<ClipboardButton
icon="link"
variant="secondary"
disabled={disabled || !isValid}
aria-describedby={disabled || !isValid ? 'copy-image-link-disabled-help' : undefined}
getText={() => imageUrl}
onClipboardCopy={() => DashboardInteractions.copyImageUrlClicked({ shareResource: 'panel' })}
>
<Trans i18nKey="link.share-panel.copy-image-link">Copy image link</Trans>
</ClipboardButton>
</Stack>
{disabled && (
<Text variant="bodySmall" color="secondary" id="generate-button-disabled-help">
@@ -187,6 +187,9 @@ export const DashboardInteractions = {
downloadDashboardImageClicked: (properties?: Record<string, unknown>) => {
reportDashboardInteraction('dashboard_image_downloaded', properties);
},
copyImageUrlClicked: (properties?: Record<string, unknown>) => {
reportDashboardInteraction('dashboard_image_url_copied', properties);
},
};
const reportDashboardInteraction: typeof reportInteraction = (name, properties) => {
+1
View File
@@ -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"
}