Stored Notifications: Add copy button
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { ReactNode } from 'react';
|
||||
import { ReactNode, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Card, Checkbox, useTheme2 } from '@grafana/ui';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { Card, Checkbox, IconButton, Tooltip, useTheme2 } from '@grafana/ui';
|
||||
|
||||
export type AlertVariant = 'success' | 'warning' | 'error' | 'info';
|
||||
|
||||
@@ -14,6 +15,7 @@ export interface Props {
|
||||
onClick: () => void;
|
||||
severity?: AlertVariant;
|
||||
title: string;
|
||||
text?: string;
|
||||
timestamp?: number;
|
||||
traceId?: string;
|
||||
}
|
||||
@@ -25,11 +27,51 @@ export const StoredNotificationItem = ({
|
||||
onClick,
|
||||
severity = 'error',
|
||||
title,
|
||||
text,
|
||||
traceId,
|
||||
timestamp,
|
||||
}: Props) => {
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme);
|
||||
const [hasCopied, setHasCopied] = useState(false);
|
||||
|
||||
const handleCopy = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const parts: string[] = [];
|
||||
parts.push(`Title: ${title}`);
|
||||
if (text) {
|
||||
parts.push(`Message: ${text}`);
|
||||
}
|
||||
if (traceId) {
|
||||
parts.push(`Trace ID: ${traceId}`);
|
||||
}
|
||||
if (timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
parts.push(`Timestamp: ${date.toISOString()}`);
|
||||
}
|
||||
const textToCopy = parts.join('\n');
|
||||
|
||||
try {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
} else {
|
||||
// Fallback for older browsers
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = textToCopy;
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
setHasCopied(true);
|
||||
setTimeout(() => setHasCopied(false), 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy text:', err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card noMargin className={className} onClick={onClick}>
|
||||
@@ -42,6 +84,21 @@ export const StoredNotificationItem = ({
|
||||
{traceId && <span>{`Trace ID: ${traceId}`}</span>}
|
||||
{timestamp && formatDistanceToNow(timestamp, { addSuffix: true })}
|
||||
</Card.Tags>
|
||||
<Card.SecondaryActions>
|
||||
<Tooltip
|
||||
content={
|
||||
hasCopied
|
||||
? t('notifications.stored-notification-item.copied', 'Copied')
|
||||
: t('notifications.stored-notification-item.copy-to-clipboard', 'Copy to clipboard')
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
name="copy"
|
||||
onClick={handleCopy}
|
||||
aria-label={t('notifications.stored-notification-item.copy-to-clipboard', 'Copy to clipboard')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Card.SecondaryActions>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -90,6 +90,7 @@ export function StoredNotifications() {
|
||||
onClick={() => handleCheckboxToggle(notif.id)}
|
||||
severity={notif.severity}
|
||||
title={notif.title}
|
||||
text={notif.text}
|
||||
timestamp={notif.timestamp}
|
||||
traceId={notif.traceId}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user