ShareDrawer: Share snapshot panel (#90678)

pull/91916/head
Juan Cabanas 9 months ago committed by GitHub
parent 7cc925d319
commit 8a97143120
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx
  2. 16
      public/app/features/dashboard-scene/scene/keyboardShortcuts.ts
  3. 30
      public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/CreateSnapshot.tsx
  4. 3
      public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/ShareSnapshot.tsx
  5. 11
      public/locales/en-US/grafana.json
  6. 11
      public/locales/pseudo-LOCALE/grafana.json

@ -13,6 +13,7 @@ import { LocalValueVariable, sceneGraph, SceneGridRow, VizPanel, VizPanelMenu }
import { DataQuery, OptionsWithLegend } from '@grafana/schema'; import { DataQuery, OptionsWithLegend } from '@grafana/schema';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import { t } from 'app/core/internationalization'; 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 { scenesPanelToRuleFormValues } from 'app/features/alerting/unified/utils/rule-form';
import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils'; import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
import { InspectTab } from 'app/features/inspector/types'; 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 { addDataTrailPanelAction } from 'app/features/trails/Integrations/dashboardIntegration';
import { ShowConfirmModalEvent } from 'app/types/events'; import { ShowConfirmModalEvent } from 'app/types/events';
import { ShareSnapshot } from '../sharing/ShareButton/share-snapshot/ShareSnapshot';
import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
import { ShareModal } from '../sharing/ShareModal'; import { ShareModal } from '../sharing/ShareModal';
import { SharePanelEmbedTab } from '../sharing/SharePanelEmbedTab'; 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({ items.push({
type: 'submenu', type: 'submenu',
text: t('panel.header-menu.share', 'Share'), text: t('panel.header-menu.share', 'Share'),

@ -4,7 +4,9 @@ import { sceneGraph, VizPanel } from '@grafana/scenes';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import { t } from 'app/core/internationalization'; import { t } from 'app/core/internationalization';
import { KeybindingSet } from 'app/core/services/KeybindingSet'; 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 { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
import { ShareModal } from '../sharing/ShareModal'; import { ShareModal } from '../sharing/ShareModal';
import { SharePanelEmbedTab } from '../sharing/SharePanelEmbedTab'; import { SharePanelEmbedTab } from '../sharing/SharePanelEmbedTab';
@ -72,6 +74,20 @@ export function setupKeyboardShortcuts(scene: DashboardScene) {
scene.showModal(drawer); 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 { } else {
keybindings.addBinding({ keybindings.addBinding({
key: 'p s', key: 'p s',

@ -1,6 +1,7 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { SceneObjectRef, VizPanel } from '@grafana/scenes';
import { import {
Alert, Alert,
Button, Button,
@ -20,7 +21,10 @@ import { Trans } from 'app/core/internationalization';
import { SnapshotSharingOptions } from '../../../../dashboard/services/SnapshotSrv'; import { SnapshotSharingOptions } from '../../../../dashboard/services/SnapshotSrv';
import { getExpireOptions } from '../../ShareSnapshotTab'; 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 { interface Props {
isLoading: boolean; isLoading: boolean;
@ -31,6 +35,7 @@ interface Props {
onCreateClick: (isExternal?: boolean) => void; onCreateClick: (isExternal?: boolean) => void;
onNameChange: (v: string) => void; onNameChange: (v: string) => void;
onExpireChange: (v: number) => void; onExpireChange: (v: number) => void;
panelRef?: SceneObjectRef<VizPanel>;
} }
export function CreateSnapshot({ export function CreateSnapshot({
name, name,
@ -41,6 +46,7 @@ export function CreateSnapshot({
onCancelClick, onCancelClick,
onCreateClick, onCreateClick,
isLoading, isLoading,
panelRef,
}: Props) { }: Props) {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
@ -49,18 +55,30 @@ export function CreateSnapshot({
<Alert severity="info" title={''}> <Alert severity="info" title={''}>
<Stack justifyContent="space-between" gap={2} alignItems="center"> <Stack justifyContent="space-between" gap={2} alignItems="center">
<Text> <Text>
{panelRef ? (
<Trans i18nKey="snapshot.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.
</Trans>
) : (
<Trans i18nKey="snapshot.share.info-alert"> <Trans i18nKey="snapshot.share.info-alert">
A Grafana dashboard snapshot publicly shares a dashboard while removing sensitive data such as queries and A Grafana dashboard snapshot publicly shares a dashboard while removing sensitive data such as queries
panel links, leaving only visible metrics and series names. Anyone with the link can access the snapshot. and panel links, leaving only visible metrics and series names. Anyone with the link can access the
snapshot.
</Trans> </Trans>
)}
</Text> </Text>
<Button variant="secondary" onClick={() => window.open(SNAPSHOT_URL, '_blank')} type="button"> <Button
variant="secondary"
onClick={() => window.open(panelRef ? PANEL_SNAPSHOT_URL : DASHBOARD_SNAPSHOT_URL, '_blank')}
type="button"
>
<Trans i18nKey="snapshot.share.learn-more-button">Learn more</Trans> <Trans i18nKey="snapshot.share.learn-more-button">Learn more</Trans>
</Button> </Button>
</Stack> </Stack>
</Alert> </Alert>
<Field label={t('snapshot.share.name-label', 'Snapshot name*')}> <Field label={t('snapshot.share.name-label', 'Snapshot name')}>
<Input id="snapshot-name-input" defaultValue={name} onBlur={(e) => onNameChange(e.target.value)} /> <Input id="snapshot-name-input" defaultValue={name} onChange={(e) => onNameChange(e.currentTarget.value)} />
</Field> </Field>
<Field label={t('snapshot.share.expiration-label', 'Expires in')}> <Field label={t('snapshot.share.expiration-label', 'Expires in')}>
<RadioButtonGroup<number> <RadioButtonGroup<number>

@ -23,7 +23,7 @@ function ShareSnapshotRenderer({ model }: SceneComponentProps<ShareSnapshot>) {
const [showDeletedAlert, setShowDeletedAlert] = useState(false); const [showDeletedAlert, setShowDeletedAlert] = useState(false);
const [step, setStep] = useState(1); 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 [snapshotResult, createSnapshot] = useAsyncFn(async (external = false) => {
const response = await model.onSnapshotCreate(external); const response = await model.onSnapshotCreate(external);
@ -76,6 +76,7 @@ function ShareSnapshotRenderer({ model }: SceneComponentProps<ShareSnapshot>) {
onExpireChange={model.onExpireChange} onExpireChange={model.onExpireChange}
onCreateClick={createSnapshot} onCreateClick={createSnapshot}
isLoading={snapshotResult.loading} isLoading={snapshotResult.loading}
panelRef={panelRef}
/> />
</> </>
)} )}

@ -2130,11 +2130,13 @@
"share-panel": { "share-panel": {
"drawer": { "drawer": {
"share-embed-title": "Share embed", "share-embed-title": "Share embed",
"share-link-title": "Link settings" "share-link-title": "Link settings",
"share-snapshot-title": "Share snapshot"
}, },
"menu": { "menu": {
"share-embed-title": "Share embed", "share-embed-title": "Share embed",
"share-link-title": "Share link" "share-link-title": "Share link",
"share-snapshot-title": "Share snapshot"
} }
}, },
"share-playlist": { "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.", "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", "learn-more-button": "Learn more",
"local-button": "Publish snapshot", "local-button": "Publish snapshot",
"name-label": "Snapshot name*", "name-label": "Snapshot name",
"new-snapshot-button": "New snapshot", "new-snapshot-button": "New snapshot",
"success-creation": "Your snapshot has been created", "success-creation": "Your snapshot has been created",
"success-delete": "Your snapshot has been deleted", "success-delete": "Your snapshot has been deleted",
"view-all-button": "View all snapshots" "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", "url-column-header": "Snapshot url",
"view-button": "View" "view-button": "View"
}, },

@ -2130,11 +2130,13 @@
"share-panel": { "share-panel": {
"drawer": { "drawer": {
"share-embed-title": "Ŝĥäřę ęmþęđ", "share-embed-title": "Ŝĥäřę ęmþęđ",
"share-link-title": "Ŀįʼnĸ şęŧŧįʼnģş" "share-link-title": "Ŀįʼnĸ şęŧŧįʼnģş",
"share-snapshot-title": "Ŝĥäřę şʼnäpşĥőŧ"
}, },
"menu": { "menu": {
"share-embed-title": "Ŝĥäřę ęmþęđ", "share-embed-title": "Ŝĥäřę ęmþęđ",
"share-link-title": "Ŝĥäřę ľįʼnĸ" "share-link-title": "Ŝĥäřę ľįʼnĸ",
"share-snapshot-title": "Ŝĥäřę şʼnäpşĥőŧ"
} }
}, },
"share-playlist": { "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şĥőŧ.", "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őřę", "learn-more-button": "Ŀęäřʼn mőřę",
"local-button": "Pūþľįşĥ şʼnäpşĥőŧ", "local-button": "Pūþľįşĥ şʼnäpşĥőŧ",
"name-label": "Ŝʼnäpşĥőŧ ʼnämę*", "name-label": "Ŝʼnäpşĥőŧ ʼnämę",
"new-snapshot-button": "Ńęŵ şʼnäpşĥőŧ", "new-snapshot-button": "Ńęŵ şʼnäpşĥőŧ",
"success-creation": "Ÿőūř şʼnäpşĥőŧ ĥäş þęęʼn čřęäŧęđ", "success-creation": "Ÿőūř şʼnäpşĥőŧ ĥäş þęęʼn čřęäŧęđ",
"success-delete": "Ÿőūř şʼnäpşĥőŧ ĥäş þęęʼn đęľęŧęđ", "success-delete": "Ÿőūř şʼnäpşĥőŧ ĥäş þęęʼn đęľęŧęđ",
"view-all-button": "Vįęŵ äľľ şʼnäpşĥőŧş" "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şĥőŧ ūřľ", "url-column-header": "Ŝʼnäpşĥőŧ ūřľ",
"view-button": "Vįęŵ" "view-button": "Vįęŵ"
}, },

Loading…
Cancel
Save