From 8a971431201953b1e38c7f662e0120a44751e3b7 Mon Sep 17 00:00:00 2001 From: Juan Cabanas Date: Wed, 14 Aug 2024 13:15:47 -0300 Subject: [PATCH] ShareDrawer: Share snapshot panel (#90678) --- .../scene/PanelMenuBehavior.tsx | 18 ++++++++++ .../scene/keyboardShortcuts.ts | 16 +++++++++ .../share-snapshot/CreateSnapshot.tsx | 34 ++++++++++++++----- .../share-snapshot/ShareSnapshot.tsx | 3 +- public/locales/en-US/grafana.json | 11 ++++-- public/locales/pseudo-LOCALE/grafana.json | 11 ++++-- 6 files changed, 78 insertions(+), 15 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx index c649726905f..9a736dc2936 100644 --- a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx +++ b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx @@ -13,6 +13,7 @@ import { LocalValueVariable, sceneGraph, SceneGridRow, VizPanel, VizPanelMenu } import { DataQuery, OptionsWithLegend } from '@grafana/schema'; import appEvents from 'app/core/app_events'; import { t } from 'app/core/internationalization'; +import { contextSrv } from 'app/core/services/context_srv'; import { scenesPanelToRuleFormValues } from 'app/features/alerting/unified/utils/rule-form'; import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils'; import { InspectTab } from 'app/features/inspector/types'; @@ -21,6 +22,7 @@ import { createExtensionSubMenu } from 'app/features/plugins/extensions/utils'; import { addDataTrailPanelAction } from 'app/features/trails/Integrations/dashboardIntegration'; import { ShowConfirmModalEvent } from 'app/types/events'; +import { ShareSnapshot } from '../sharing/ShareButton/share-snapshot/ShareSnapshot'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareModal } from '../sharing/ShareModal'; import { SharePanelEmbedTab } from '../sharing/SharePanelEmbedTab'; @@ -109,6 +111,22 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { }, }); + if (contextSrv.isSignedIn && config.snapshotEnabled && dashboard.canEditDashboard()) { + subMenu.push({ + text: t('share-panel.menu.share-snapshot-title', 'Share snapshot'), + iconClassName: 'camera', + shortcut: 'p s', + onClick: () => { + const drawer = new ShareDrawer({ + title: t('share-panel.drawer.share-snapshot-title', 'Share snapshot'), + body: new ShareSnapshot({ dashboardRef: dashboard.getRef(), panelRef: panel.getRef() }), + }); + + dashboard.showModal(drawer); + }, + }); + } + items.push({ type: 'submenu', text: t('panel.header-menu.share', 'Share'), diff --git a/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts b/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts index 1991aef027b..90d2baa7093 100644 --- a/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts +++ b/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts @@ -4,7 +4,9 @@ import { sceneGraph, VizPanel } from '@grafana/scenes'; import appEvents from 'app/core/app_events'; import { t } from 'app/core/internationalization'; import { KeybindingSet } from 'app/core/services/KeybindingSet'; +import { contextSrv } from 'app/core/services/context_srv'; +import { ShareSnapshot } from '../sharing/ShareButton/share-snapshot/ShareSnapshot'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareModal } from '../sharing/ShareModal'; import { SharePanelEmbedTab } from '../sharing/SharePanelEmbedTab'; @@ -72,6 +74,20 @@ export function setupKeyboardShortcuts(scene: DashboardScene) { scene.showModal(drawer); }), }); + + if (contextSrv.isSignedIn && config.snapshotEnabled && scene.canEditDashboard()) { + keybindings.addBinding({ + key: 'p s', + onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => { + const drawer = new ShareDrawer({ + title: t('share-panel.drawer.share-snapshot-title', 'Share snapshot'), + body: new ShareSnapshot({ dashboardRef: scene.getRef(), panelRef: vizPanel.getRef() }), + }); + + scene.showModal(drawer); + }), + }); + } } else { keybindings.addBinding({ key: 'p s', diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/CreateSnapshot.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/CreateSnapshot.tsx index 73aaf435fde..85cf3b3e0c9 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/CreateSnapshot.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/CreateSnapshot.tsx @@ -1,6 +1,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; +import { SceneObjectRef, VizPanel } from '@grafana/scenes'; import { Alert, Button, @@ -20,7 +21,10 @@ import { Trans } from 'app/core/internationalization'; import { SnapshotSharingOptions } from '../../../../dashboard/services/SnapshotSrv'; import { getExpireOptions } from '../../ShareSnapshotTab'; -const SNAPSHOT_URL = 'https://grafana.com/docs/grafana/latest/dashboards/share-dashboards-panels/#publish-a-snapshot'; +const DASHBOARD_SNAPSHOT_URL = + 'https://grafana.com/docs/grafana/latest/dashboards/share-dashboards-panels/#publish-a-snapshot'; +const PANEL_SNAPSHOT_URL = + 'https://grafana.com/docs/grafana/latest/dashboards/share-dashboards-panels/#publish-a-snapshot-1'; interface Props { isLoading: boolean; @@ -31,6 +35,7 @@ interface Props { onCreateClick: (isExternal?: boolean) => void; onNameChange: (v: string) => void; onExpireChange: (v: number) => void; + panelRef?: SceneObjectRef; } export function CreateSnapshot({ name, @@ -41,6 +46,7 @@ export function CreateSnapshot({ onCancelClick, onCreateClick, isLoading, + panelRef, }: Props) { const styles = useStyles2(getStyles); @@ -49,18 +55,30 @@ export function CreateSnapshot({ - - A Grafana dashboard snapshot publicly shares a dashboard while removing sensitive data such as queries and - panel links, leaving only visible metrics and series names. Anyone with the link can access the snapshot. - + {panelRef ? ( + + A Grafana panel snapshot publicly shares a panel while removing sensitive data such as queries and panel + links, leaving only visible metrics and series names. Anyone with the link can access the snapshot. + + ) : ( + + A Grafana dashboard snapshot publicly shares a dashboard while removing sensitive data such as queries + and panel links, leaving only visible metrics and series names. Anyone with the link can access the + snapshot. + + )} - - - onNameChange(e.target.value)} /> + + onNameChange(e.currentTarget.value)} /> diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/ShareSnapshot.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/ShareSnapshot.tsx index d225a75b1e3..60ba8c30992 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/ShareSnapshot.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/ShareSnapshot.tsx @@ -23,7 +23,7 @@ function ShareSnapshotRenderer({ model }: SceneComponentProps) { const [showDeletedAlert, setShowDeletedAlert] = useState(false); const [step, setStep] = useState(1); - const { snapshotName, snapshotSharingOptions, selectedExpireOption, dashboardRef } = model.useState(); + const { snapshotName, snapshotSharingOptions, selectedExpireOption, dashboardRef, panelRef } = model.useState(); const [snapshotResult, createSnapshot] = useAsyncFn(async (external = false) => { const response = await model.onSnapshotCreate(external); @@ -76,6 +76,7 @@ function ShareSnapshotRenderer({ model }: SceneComponentProps) { onExpireChange={model.onExpireChange} onCreateClick={createSnapshot} isLoading={snapshotResult.loading} + panelRef={panelRef} /> )} diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index d0d99aaffcb..22ea985ed88 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -2130,11 +2130,13 @@ "share-panel": { "drawer": { "share-embed-title": "Share embed", - "share-link-title": "Link settings" + "share-link-title": "Link settings", + "share-snapshot-title": "Share snapshot" }, "menu": { "share-embed-title": "Share embed", - "share-link-title": "Share link" + "share-link-title": "Share link", + "share-snapshot-title": "Share snapshot" } }, "share-playlist": { @@ -2215,12 +2217,15 @@ "info-alert": "A Grafana dashboard snapshot publicly shares a dashboard while removing sensitive data such as queries and panel links, leaving only visible metrics and series names. Anyone with the link can access the snapshot.", "learn-more-button": "Learn more", "local-button": "Publish snapshot", - "name-label": "Snapshot name*", + "name-label": "Snapshot name", "new-snapshot-button": "New snapshot", "success-creation": "Your snapshot has been created", "success-delete": "Your snapshot has been deleted", "view-all-button": "View all snapshots" }, + "share-panel": { + "info-alert": "A Grafana panel snapshot publicly shares a panel while removing sensitive data such as queries and panel links, leaving only visible metrics and series names. Anyone with the link can access the snapshot." + }, "url-column-header": "Snapshot url", "view-button": "View" }, diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 3f31878d512..63d19c67f5a 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -2130,11 +2130,13 @@ "share-panel": { "drawer": { "share-embed-title": "Ŝĥäřę ęmþęđ", - "share-link-title": "Ŀįʼnĸ şęŧŧįʼnģş" + "share-link-title": "Ŀįʼnĸ şęŧŧįʼnģş", + "share-snapshot-title": "Ŝĥäřę şʼnäpşĥőŧ" }, "menu": { "share-embed-title": "Ŝĥäřę ęmþęđ", - "share-link-title": "Ŝĥäřę ľįʼnĸ" + "share-link-title": "Ŝĥäřę ľįʼnĸ", + "share-snapshot-title": "Ŝĥäřę şʼnäpşĥőŧ" } }, "share-playlist": { @@ -2215,12 +2217,15 @@ "info-alert": "Å Ğřäƒäʼnä đäşĥþőäřđ şʼnäpşĥőŧ pūþľįčľy şĥäřęş ä đäşĥþőäřđ ŵĥįľę řęmővįʼnģ şęʼnşįŧįvę đäŧä şūčĥ äş qūęřįęş äʼnđ päʼnęľ ľįʼnĸş, ľęävįʼnģ őʼnľy vįşįþľę męŧřįčş äʼnđ şęřįęş ʼnämęş. Åʼnyőʼnę ŵįŧĥ ŧĥę ľįʼnĸ čäʼn äččęşş ŧĥę şʼnäpşĥőŧ.", "learn-more-button": "Ŀęäřʼn mőřę", "local-button": "Pūþľįşĥ şʼnäpşĥőŧ", - "name-label": "Ŝʼnäpşĥőŧ ʼnämę*", + "name-label": "Ŝʼnäpşĥőŧ ʼnämę", "new-snapshot-button": "Ńęŵ şʼnäpşĥőŧ", "success-creation": "Ÿőūř şʼnäpşĥőŧ ĥäş þęęʼn čřęäŧęđ", "success-delete": "Ÿőūř şʼnäpşĥőŧ ĥäş þęęʼn đęľęŧęđ", "view-all-button": "Vįęŵ äľľ şʼnäpşĥőŧş" }, + "share-panel": { + "info-alert": "Å Ğřäƒäʼnä päʼnęľ şʼnäpşĥőŧ pūþľįčľy şĥäřęş ä päʼnęľ ŵĥįľę řęmővįʼnģ şęʼnşįŧįvę đäŧä şūčĥ äş qūęřįęş äʼnđ päʼnęľ ľįʼnĸş, ľęävįʼnģ őʼnľy vįşįþľę męŧřįčş äʼnđ şęřįęş ʼnämęş. Åʼnyőʼnę ŵįŧĥ ŧĥę ľįʼnĸ čäʼn äččęşş ŧĥę şʼnäpşĥőŧ." + }, "url-column-header": "Ŝʼnäpşĥőŧ ūřľ", "view-button": "Vįęŵ" },