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. 34
      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>
<Trans i18nKey="snapshot.share.info-alert"> {panelRef ? (
A Grafana dashboard snapshot publicly shares a dashboard while removing sensitive data such as queries and <Trans i18nKey="snapshot.share-panel.info-alert">
panel links, leaving only visible metrics and series names. Anyone with the link can access the snapshot. A Grafana panel snapshot publicly shares a panel while removing sensitive data such as queries and panel
</Trans> links, leaving only visible metrics and series names. Anyone with the link can access the snapshot.
</Trans>
) : (
<Trans i18nKey="snapshot.share.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.
</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