diff --git a/public/app/core/utils/navBarItem-translations.ts b/public/app/core/utils/navBarItem-translations.ts index 0af9a4519c1..98673763803 100644 --- a/public/app/core/utils/navBarItem-translations.ts +++ b/public/app/core/utils/navBarItem-translations.ts @@ -211,7 +211,7 @@ export function getNavSubTitle(navId: string | undefined) { case 'dashboards/snapshots': return t( 'nav.snapshots.subtitle', - 'Interactive, publically available, point-in-time representations of dashboards' + 'Interactive, publically available, point-in-time representations of dashboards and panels' ); case 'dashboards/public': return config.featureToggles.newDashboardSharingComponent diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareConfiguration.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareConfiguration.tsx index f27a9d2e84b..9297a628285 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareConfiguration.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareConfiguration.tsx @@ -1,11 +1,13 @@ +import { css, cx } from '@emotion/css'; import { Controller, useForm } from 'react-hook-form'; +import { GrafanaTheme2 } from '@grafana/data'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors'; import { sceneGraph } from '@grafana/scenes'; -import { FieldSet, Icon, Label, Spinner, Stack, Text, TimeRangeInput, Tooltip } from '@grafana/ui'; +import { FieldSet, Icon, Label, Spinner, Stack, Text, TimeRangeLabel, Tooltip, useStyles2 } from '@grafana/ui'; import { Switch } from '@grafana/ui/src/components/Switch/Switch'; import { contextSrv } from 'app/core/core'; -import { Trans, t } from 'app/core/internationalization'; +import { t, Trans } from 'app/core/internationalization'; import { publicDashboardApi, useUpdatePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi'; import { ConfigPublicDashboardForm } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/ConfigPublicDashboard/ConfigPublicDashboard'; import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions'; @@ -18,6 +20,7 @@ const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard; type FormInput = Omit; export default function ShareConfiguration() { + const styles = useStyles2(getStyles); const { dashboard } = useShareDrawerContext(); const [update, { isLoading }] = useUpdatePublicDashboardMutation(); @@ -119,8 +122,13 @@ export default function ShareConfiguration() { Display annotations - - {}} /> + +
+ Time range +
+
+ +
- +
@@ -139,3 +147,13 @@ export default function ShareConfiguration() { ); } + +const getStyles = (theme: GrafanaTheme2) => ({ + timeRange: css({ + fontSize: theme.typography.bodySmall.fontSize, + fontWeight: theme.typography.bodySmall.fontWeight, + }), + timeRangeValue: css({ + color: theme.colors.text.secondary, + }), +}); 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 b893d98d7cc..394f1555517 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 @@ -3,15 +3,15 @@ import useAsyncFn from 'react-use/lib/useAsyncFn'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors'; import { SceneComponentProps } from '@grafana/scenes'; -import { Alert } from '@grafana/ui'; +import { Alert, Button, ClipboardButton, Spinner, Stack, TextLink } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; +import { SnapshotSharingOptions } from '../../../../dashboard/services/SnapshotSrv'; import { ShareDrawerConfirmAction } from '../../ShareDrawer/ShareDrawerConfirmAction'; import { ShareSnapshotTab } from '../../ShareSnapshotTab'; import { ShareView } from '../../types'; -import { CreateSnapshot } from './CreateSnapshot'; -import { SnapshotActions } from './SnapshotActions'; +import { UpsertSnapshot } from './UpsertSnapshot'; const selectors = e2eSelectors.pages.ShareDashboardDrawer.ShareSnapshot; @@ -28,7 +28,8 @@ function ShareSnapshotRenderer({ model }: SceneComponentProps) { const [showDeletedAlert, setShowDeletedAlert] = useState(false); const [step, setStep] = useState(1); - const { snapshotName, snapshotSharingOptions, selectedExpireOption, panelRef, onDismiss } = model.useState(); + const { snapshotName, snapshotSharingOptions, selectedExpireOption, panelRef, onDismiss, dashboardRef } = + model.useState(); const [snapshotResult, createSnapshot] = useAsyncFn(async (external = false) => { const response = await model.onSnapshotCreate(external); @@ -47,12 +48,22 @@ function ShareSnapshotRenderer({ model }: SceneComponentProps) { onDismiss?.(); }; + const reset = () => { + model.onSnasphotNameChange(dashboardRef.resolve().state.title); + setStep(1); + }; + + const onDeleteSnapshotClick = async () => { + await deleteSnapshot(snapshotResult.value?.deleteUrl!); + reset(); + }; + if (showDeleteConfirmation) { return ( deleteSnapshot(snapshotResult.value?.deleteUrl!)} + onConfirm={onDeleteSnapshotClick} onDismiss={() => setShowDeleteConfirmation(false)} description={t('snapshot.share.delete-description', 'Are you sure you want to delete this snapshot?')} isActionLoading={deleteSnapshotResult.loading} @@ -62,36 +73,95 @@ function ShareSnapshotRenderer({ model }: SceneComponentProps) { return (
- {step === 1 && ( - <> - {showDeletedAlert && ( - setShowDeletedAlert(false)}> - - Your snapshot has been deleted. It might take up to an hour before the snapshot is cleared from any CDN - caches. - - - )} - - - )} - {step === 2 && ( - setShowDeleteConfirmation(true)} - onNewSnapshotClick={() => setStep(1)} - /> - )} + <> + {step === 1 && showDeletedAlert && ( + setShowDeletedAlert(false)}> + + Snapshot deleted. It could take an hour to be cleared from CDN caches. + + + )} + + + {step === 1 ? ( + + ) : ( + step === 2 && + snapshotResult.value && ( + setShowDeleteConfirmation(true)} + onNewSnapshotClick={reset} + /> + ) + )} + + {t('snapshot.share.view-all-button', 'View all snapshots')} + + + +
); } + +const CreateSnapshotActions = ({ + isLoading, + onCreateClick, + onCancelClick, + sharingOptions, +}: { + isLoading: boolean; + sharingOptions?: SnapshotSharingOptions; + onCancelClick: () => void; + onCreateClick: (isExternal?: boolean) => void; +}) => ( + + + {sharingOptions?.externalEnabled && ( + + )} + + {isLoading && } + +); + +const UpsertSnapshotActions = ({ + url, + onDeleteClick, + onNewSnapshotClick, +}: { + url: string; + onDeleteClick: () => void; + onNewSnapshotClick: () => void; +}) => ( + + url}> + Copy link + + + + +); diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/SnapshotActions.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/SnapshotActions.tsx deleted file mode 100644 index 79e5b827151..00000000000 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/SnapshotActions.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Button, ClipboardButton, Stack } from '@grafana/ui'; -import { Trans } from 'app/core/internationalization'; - -interface Props { - url: string; - onDeleteClick: () => void; - onNewSnapshotClick: () => void; -} -export const SnapshotActions = ({ url, onDeleteClick, onNewSnapshotClick }: Props) => { - return ( - - url}> - Copy link - - - - - ); -}; diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/CreateSnapshot.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/UpsertSnapshot.tsx similarity index 54% rename from public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/CreateSnapshot.tsx rename to public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/UpsertSnapshot.tsx index 85cf3b3e0c9..6dbd3a5dcb9 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/CreateSnapshot.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/UpsertSnapshot.tsx @@ -1,24 +1,13 @@ import { css } from '@emotion/css'; +import { PropsWithChildren } from 'react'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { SceneObjectRef, VizPanel } from '@grafana/scenes'; -import { - Alert, - Button, - Divider, - Field, - RadioButtonGroup, - Spinner, - Stack, - Text, - TextLink, - useStyles2, -} from '@grafana/ui'; +import { Alert, Button, Divider, Field, RadioButtonGroup, Stack, Text, useStyles2 } from '@grafana/ui'; import { Input } from '@grafana/ui/src/components/Input/Input'; import { t } from '@grafana/ui/src/utils/i18n'; import { Trans } from 'app/core/internationalization'; -import { SnapshotSharingOptions } from '../../../../dashboard/services/SnapshotSrv'; import { getExpireOptions } from '../../ShareSnapshotTab'; const DASHBOARD_SNAPSHOT_URL = @@ -27,27 +16,22 @@ const PANEL_SNAPSHOT_URL = 'https://grafana.com/docs/grafana/latest/dashboards/share-dashboards-panels/#publish-a-snapshot-1'; interface Props { - isLoading: boolean; name: string; selectedExpireOption: SelectableValue; - sharingOptions?: SnapshotSharingOptions; - onCancelClick: () => void; - onCreateClick: (isExternal?: boolean) => void; onNameChange: (v: string) => void; onExpireChange: (v: number) => void; panelRef?: SceneObjectRef; + disableInputs: boolean; } -export function CreateSnapshot({ +export function UpsertSnapshot({ name, onNameChange, onExpireChange, selectedExpireOption, - sharingOptions, - onCancelClick, - onCreateClick, - isLoading, panelRef, -}: Props) { + disableInputs, + children, +}: Props & PropsWithChildren) { const styles = useStyles2(getStyles); return ( @@ -77,37 +61,21 @@ export function CreateSnapshot({ - - onNameChange(e.currentTarget.value)} /> - - - - id="expire-select-input" - options={getExpireOptions()} - value={selectedExpireOption?.value} - onChange={onExpireChange} - /> - +
+ + onNameChange(e.currentTarget.value)} /> + + + + id="expire-select-input" + options={getExpireOptions()} + value={selectedExpireOption?.value} + onChange={onExpireChange} + /> + +
- - - - {sharingOptions?.externalEnabled && ( - - )} - - {isLoading && } - - - {t('snapshot.share.view-all-button', 'View all snapshots')} - - + {children} ); } diff --git a/public/app/features/dashboard-scene/sharing/ShareSnapshotTab.tsx b/public/app/features/dashboard-scene/sharing/ShareSnapshotTab.tsx index 32eb43f1041..f4e95c33e78 100644 --- a/public/app/features/dashboard-scene/sharing/ShareSnapshotTab.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareSnapshotTab.tsx @@ -90,7 +90,7 @@ export class ShareSnapshotTab extends SceneObjectBase imp } public onSnasphotNameChange = (snapshotName: string) => { - this.setState({ snapshotName: snapshotName.trim() }); + this.setState({ snapshotName }); }; public onExpireChange = (option: number) => { @@ -105,7 +105,7 @@ export class ShareSnapshotTab extends SceneObjectBase imp const saveModel = transformSceneToSaveModel(dashboardRef.resolve(), true); return trimDashboardForSnapshot( - this.state.snapshotName || '', + this.state.snapshotName.trim() || '', timeRange.state.value, saveModel, panelRef?.resolve() diff --git a/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx b/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx index 26b99f5e126..31bdbc6a894 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx @@ -65,7 +65,7 @@ export function ShareEmbed({ panel, dashboard, range, onCancelClick, buildIframe return ( <>

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

@@ -90,7 +90,7 @@ export function ShareEmbed({ panel, dashboard, range, onCancelClick, buildIframe label={t('share-modal.embed.html', 'Embed HTML')} description={t( 'share-modal.embed.html-description', - 'The HTML code below can be pasted and included in another web page. Unless anonymous access is enabled, the user viewing that page need to be signed into Grafana for the graph to load.' + 'The HTML code below can be pasted and included in another web page. Unless anonymous access is enabled, the users viewing that page need to be signed into Grafana for the graph to load.' )} >