From 4a5b8643ae31b5c103bfe57c3276d1f8600e7dd5 Mon Sep 17 00:00:00 2001 From: kay delaney <45561153+kaydelaney@users.noreply.github.com> Date: Tue, 7 Nov 2023 09:19:17 +0000 Subject: [PATCH] ShareModal: Implement panel embed tab for scenes (#77062) * ShareModal: Implement panel embed tab for scenes * Fix url generation * Locale --------- Co-authored-by: Dominik Prokop --- .../dashboard-scene/sharing/ShareModal.tsx | 5 + .../sharing/SharePanelEmbedTab.tsx | 75 +++++++++ .../components/ShareModal/ShareEmbed.tsx | 151 ++++++++---------- .../dashboard/components/ShareModal/utils.ts | 14 +- public/locales/de-DE/grafana.json | 1 + public/locales/en-US/grafana.json | 1 + public/locales/es-ES/grafana.json | 1 + public/locales/fr-FR/grafana.json | 1 + public/locales/pseudo-LOCALE/grafana.json | 1 + public/locales/zh-Hans/grafana.json | 1 + 10 files changed, 157 insertions(+), 94 deletions(-) create mode 100644 public/app/features/dashboard-scene/sharing/SharePanelEmbedTab.tsx diff --git a/public/app/features/dashboard-scene/sharing/ShareModal.tsx b/public/app/features/dashboard-scene/sharing/ShareModal.tsx index e6ac04955a5..7bdef7e27c9 100644 --- a/public/app/features/dashboard-scene/sharing/ShareModal.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareModal.tsx @@ -11,6 +11,7 @@ import { getDashboardSceneFor } from '../utils/utils'; import { ShareExportTab } from './ShareExportTab'; import { ShareLinkTab } from './ShareLinkTab'; +import { SharePanelEmbedTab } from './SharePanelEmbedTab'; import { ShareSnapshotTab } from './ShareSnapshotTab'; import { ModalSceneObjectLike, SceneShareTab } from './types'; @@ -49,6 +50,10 @@ export class ShareModal extends SceneObjectBase implements Moda tabs.push(new ShareSnapshotTab({ panelRef, dashboardRef, modalRef: this.getRef() })); } + if (panelRef) { + tabs.push(new SharePanelEmbedTab({ panelRef, dashboardRef })); + } + this.setState({ tabs }); // if (panel) { diff --git a/public/app/features/dashboard-scene/sharing/SharePanelEmbedTab.tsx b/public/app/features/dashboard-scene/sharing/SharePanelEmbedTab.tsx new file mode 100644 index 00000000000..085ed4092bf --- /dev/null +++ b/public/app/features/dashboard-scene/sharing/SharePanelEmbedTab.tsx @@ -0,0 +1,75 @@ +import React from 'react'; + +import { TimeRange } from '@grafana/data'; +import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes'; +import { t } from 'app/core/internationalization'; +import { ShareEmbed } from 'app/features/dashboard/components/ShareModal/ShareEmbed'; +import { buildParams } from 'app/features/dashboard/components/ShareModal/utils'; + +import { DashboardScene } from '../scene/DashboardScene'; +import { getDashboardUrl } from '../utils/urlBuilders'; +import { getPanelIdForVizPanel } from '../utils/utils'; + +import { SceneShareTabState } from './types'; + +export interface SharePanelEmbedTabState extends SceneShareTabState { + panelRef: SceneObjectRef; + dashboardRef: SceneObjectRef; +} + +export class SharePanelEmbedTab extends SceneObjectBase { + static Component = SharePanelEmbedTabRenderer; + + public constructor(state: SharePanelEmbedTabState) { + super(state); + } + + public getTabLabel() { + return t('share-modal.tab-title.panel-embed', 'Embed'); + } +} + +function SharePanelEmbedTabRenderer({ model }: SceneComponentProps) { + const { panelRef, dashboardRef } = model.useState(); + const p = panelRef.resolve(); + + const dash = dashboardRef.resolve(); + const { uid: dashUid } = dash.useState(); + const id = getPanelIdForVizPanel(p); + const timeRangeState = sceneGraph.getTimeRange(p); + + return ( + + ); +} + +function buildIframe( + useCurrentTimeRange: boolean, + dashboardUid: string, + selectedTheme?: string, + panel?: { timeFrom?: string; id: number }, + range?: TimeRange +) { + const params = buildParams({ useCurrentTimeRange, selectedTheme, panel, range }); + const panelId = params.get('editPanel') ?? params.get('viewPanel') ?? ''; + params.set('panelId', panelId); + params.delete('editPanel'); + params.delete('viewPanel'); + + const soloUrl = getDashboardUrl({ + absolute: true, + soloRoute: true, + uid: dashboardUid, + currentQueryParams: params.toString(), + }); + return ``; +} diff --git a/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx b/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx index 57bc90df248..f1989c32b8a 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx @@ -1,6 +1,8 @@ -import React, { FormEvent, PureComponent } from 'react'; +import React, { FormEvent, useEffect, useState } from 'react'; +import { useEffectOnce } from 'react-use'; -import { reportInteraction } from '@grafana/runtime/src'; +import { RawTimeRange, TimeRange } from '@grafana/data'; +import { reportInteraction } from '@grafana/runtime'; import { ClipboardButton, Field, Modal, Switch, TextArea } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; @@ -8,103 +10,76 @@ import { ThemePicker } from './ThemePicker'; import { ShareModalTabProps } from './types'; import { buildIframeHtml } from './utils'; -interface Props extends ShareModalTabProps {} - -interface State { - useCurrentTimeRange: boolean; - selectedTheme: string; - iframeHtml: string; +interface Props extends Omit { + panel?: { timeFrom?: string; id: number }; + dashboard: { uid: string; time: RawTimeRange }; + range?: TimeRange; + buildIframe?: typeof buildIframeHtml; } -export class ShareEmbed extends PureComponent { - constructor(props: Props) { - super(props); - this.state = { - useCurrentTimeRange: true, - selectedTheme: 'current', - iframeHtml: '', - }; - } +export function ShareEmbed({ panel, dashboard, range, buildIframe = buildIframeHtml }: Props) { + const [useCurrentTimeRange, setUseCurrentTimeRange] = useState(true); + const [selectedTheme, setSelectedTheme] = useState('current'); + const [iframeHtml, setIframeHtml] = useState(''); - componentDidMount() { + useEffectOnce(() => { reportInteraction('grafana_dashboards_embed_share_viewed'); - this.buildIframeHtml(); - } - - buildIframeHtml = () => { - const { panel, dashboard } = this.props; - const { useCurrentTimeRange, selectedTheme } = this.state; - - const iframeHtml = buildIframeHtml(useCurrentTimeRange, dashboard.uid, selectedTheme, panel); - this.setState({ iframeHtml }); - }; + }); - onIframeHtmlChange = (event: FormEvent) => { - this.setState({ iframeHtml: event.currentTarget.value }); - }; + useEffect(() => { + const newIframeHtml = buildIframe(useCurrentTimeRange, dashboard.uid, selectedTheme, panel, range); + setIframeHtml(newIframeHtml); + }, [selectedTheme, useCurrentTimeRange, dashboard, panel, range, buildIframe]); - onUseCurrentTimeRangeChange = () => { - this.setState( - { - useCurrentTimeRange: !this.state.useCurrentTimeRange, - }, - this.buildIframeHtml - ); + const onIframeHtmlChange = (event: FormEvent) => { + setIframeHtml(event.currentTarget.value); }; - onThemeChange = (value: string) => { - this.setState({ selectedTheme: value }, this.buildIframeHtml); + const onUseCurrentTimeRangeChange = () => { + setUseCurrentTimeRange((useCurTimeRange) => !useCurTimeRange); }; - getIframeHtml = () => { - return this.state.iframeHtml; + const onThemeChange = (value: string) => { + setSelectedTheme(value); }; - render() { - const { useCurrentTimeRange, selectedTheme, iframeHtml } = this.state; - const isRelativeTime = this.props.dashboard ? this.props.dashboard.time.to === 'now' : false; - - const timeRangeDescription = isRelativeTime - ? t( - 'share-modal.embed.time-range-description', - 'Transforms the current relative time range to an absolute time range' - ) - : ''; + const isRelativeTime = dashboard.time.to === 'now'; + const timeRangeDescription = isRelativeTime + ? t( + 'share-modal.embed.time-range-description', + 'Transforms the current relative time range to an absolute time range' + ) + : ''; - return ( - <> -

- Generate HTML for embedding an iframe with this panel. -

- - - - - -