mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ShareModal: Share link redesign under newDashboardSharingComponent FF (#87011)
This commit is contained in:
@@ -180,6 +180,7 @@ Experimental features might be changed or removed without prior notice.
|
||||
| `queryLibrary` | Enables Query Library feature in Explore |
|
||||
| `autofixDSUID` | Automatically migrates invalid datasource UIDs |
|
||||
| `logsExploreTableDefaultVisualization` | Sets the logs table as default visualisation in logs explore |
|
||||
| `newDashboardSharingComponent` | Enables the new sharing drawer design |
|
||||
|
||||
## Development feature toggles
|
||||
|
||||
|
||||
@@ -183,4 +183,5 @@ export interface FeatureToggles {
|
||||
queryLibrary?: boolean;
|
||||
autofixDSUID?: boolean;
|
||||
logsExploreTableDefaultVisualization?: boolean;
|
||||
newDashboardSharingComponent?: boolean;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,15 @@ export const Pages = {
|
||||
publicDashboardTag: 'data-testid public dashboard tag',
|
||||
shareButton: 'data-testid share-button',
|
||||
scrollContainer: 'data-testid Dashboard canvas scroll container',
|
||||
newShareButton: {
|
||||
container: 'data-testid new share button',
|
||||
shareLink: 'data-testid new share link-button',
|
||||
arrowMenu: 'data-testid new share button arrow menu',
|
||||
menu: {
|
||||
container: 'data-testid new share button menu',
|
||||
shareInternally: 'data-testid new share button share internally',
|
||||
},
|
||||
},
|
||||
playlistControls: {
|
||||
prev: 'data-testid playlist previous dashboard button',
|
||||
stop: 'data-testid playlist stop dashboard button',
|
||||
|
||||
@@ -1233,6 +1233,13 @@ var (
|
||||
Owner: grafanaObservabilityLogsSquad,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "newDashboardSharingComponent",
|
||||
Description: "Enables the new sharing drawer design",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaSharingSquad,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -164,3 +164,4 @@ grafanaManagedRecordingRules,experimental,@grafana/alerting-squad,false,false,fa
|
||||
queryLibrary,experimental,@grafana/explore-squad,false,false,false
|
||||
autofixDSUID,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
logsExploreTableDefaultVisualization,experimental,@grafana/observability-logs,false,false,true
|
||||
newDashboardSharingComponent,experimental,@grafana/sharing-squad,false,false,true
|
||||
|
||||
|
@@ -666,4 +666,8 @@ const (
|
||||
// FlagLogsExploreTableDefaultVisualization
|
||||
// Sets the logs table as default visualisation in logs explore
|
||||
FlagLogsExploreTableDefaultVisualization = "logsExploreTableDefaultVisualization"
|
||||
|
||||
// FlagNewDashboardSharingComponent
|
||||
// Enables the new sharing drawer design
|
||||
FlagNewDashboardSharingComponent = "newDashboardSharingComponent"
|
||||
)
|
||||
|
||||
@@ -2136,6 +2136,19 @@
|
||||
"codeowner": "@grafana/observability-logs",
|
||||
"frontend": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "newDashboardSharingComponent",
|
||||
"resourceVersion": "1713982966391",
|
||||
"creationTimestamp": "2024-04-24T18:22:46Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enables the new sharing drawer design",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/sharing-squad",
|
||||
"frontend": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createShortLink, createAndCopyShortLink } from './shortLinks';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getBackendSrv: () => {
|
||||
return {
|
||||
post: () => {
|
||||
@@ -8,9 +9,6 @@ jest.mock('@grafana/runtime', () => ({
|
||||
},
|
||||
};
|
||||
},
|
||||
config: {
|
||||
appSubUrl: '',
|
||||
},
|
||||
}));
|
||||
|
||||
describe('createShortLink', () => {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import memoizeOne from 'memoize-one';
|
||||
|
||||
import { getBackendSrv, config } from '@grafana/runtime';
|
||||
import { UrlQueryMap } from '@grafana/data';
|
||||
import { getBackendSrv, config, locationService } from '@grafana/runtime';
|
||||
import { sceneGraph, SceneTimeRangeLike, VizPanel } from '@grafana/scenes';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createErrorNotification, createSuccessNotification } from 'app/core/copy/appNotification';
|
||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||
import { getDashboardUrl } from 'app/features/dashboard-scene/utils/urlBuilders';
|
||||
import { dispatch } from 'app/store/store';
|
||||
|
||||
import { copyStringToClipboard } from './explore';
|
||||
@@ -37,3 +41,54 @@ export const createAndCopyShortLink = async (path: string) => {
|
||||
dispatch(notifyApp(createErrorNotification('Error generating shortened link')));
|
||||
}
|
||||
};
|
||||
|
||||
export const createAndCopyDashboardShortLink = async (
|
||||
dashboard: DashboardScene,
|
||||
opts: { useAbsoluteTimeRange: boolean; theme: string },
|
||||
panel?: VizPanel
|
||||
) => {
|
||||
const shareUrl = await createDashboardShareUrl(dashboard, opts, panel);
|
||||
await createAndCopyShortLink(shareUrl);
|
||||
};
|
||||
|
||||
export const createDashboardShareUrl = async (
|
||||
dashboard: DashboardScene,
|
||||
opts: { useAbsoluteTimeRange: boolean; theme: string },
|
||||
panel?: VizPanel
|
||||
) => {
|
||||
const location = locationService.getLocation();
|
||||
const timeRange = sceneGraph.getTimeRange(panel ?? dashboard);
|
||||
|
||||
const urlParamsUpdate = getShareUrlParams(opts, timeRange, panel);
|
||||
|
||||
return getDashboardUrl({
|
||||
uid: dashboard.state.uid,
|
||||
slug: dashboard.state.meta.slug,
|
||||
currentQueryParams: location.search,
|
||||
updateQuery: urlParamsUpdate,
|
||||
absolute: true,
|
||||
});
|
||||
};
|
||||
|
||||
export const getShareUrlParams = (
|
||||
opts: { useAbsoluteTimeRange: boolean; theme: string },
|
||||
timeRange: SceneTimeRangeLike,
|
||||
panel?: VizPanel
|
||||
) => {
|
||||
const urlParamsUpdate: UrlQueryMap = {};
|
||||
|
||||
if (panel) {
|
||||
urlParamsUpdate.viewPanel = panel.state.key;
|
||||
}
|
||||
|
||||
if (opts.useAbsoluteTimeRange) {
|
||||
urlParamsUpdate.from = timeRange.state.value.from.toISOString();
|
||||
urlParamsUpdate.to = timeRange.state.value.to.toISOString();
|
||||
}
|
||||
|
||||
if (opts.theme !== 'current') {
|
||||
urlParamsUpdate.theme = opts.theme;
|
||||
}
|
||||
|
||||
return urlParamsUpdate;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import { TestProvider } from 'test/helpers/TestProvider';
|
||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
|
||||
|
||||
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
||||
@@ -25,7 +26,7 @@ jest.mock('app/features/playlist/PlaylistSrv', () => ({
|
||||
}));
|
||||
|
||||
describe('NavToolbarActions', () => {
|
||||
describe('Give an already saved dashboard', () => {
|
||||
describe('Given an already saved dashboard', () => {
|
||||
it('Should show correct buttons when not in editing', async () => {
|
||||
setup();
|
||||
|
||||
@@ -35,7 +36,7 @@ describe('NavToolbarActions', () => {
|
||||
expect(await screen.findByText('Share')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should the correct buttons when playing a playlist', async () => {
|
||||
it('Should show the correct buttons when playing a playlist', async () => {
|
||||
jest.mocked(playlistSrv).useState.mockReturnValueOnce({ isPlaying: true });
|
||||
setup();
|
||||
|
||||
@@ -101,6 +102,24 @@ describe('NavToolbarActions', () => {
|
||||
expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.next)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Given new sharing button', () => {
|
||||
it('Should show old share button when newDashboardSharingComponent FF is disabled', async () => {
|
||||
setup();
|
||||
|
||||
expect(await screen.findByText('Share')).toBeInTheDocument();
|
||||
const newShareButton = screen.queryByTestId(selectors.pages.Dashboard.DashNav.newShareButton.container);
|
||||
expect(newShareButton).not.toBeInTheDocument();
|
||||
});
|
||||
it('Should show new share button when newDashboardSharingComponent FF is enabled', async () => {
|
||||
config.featureToggles.newDashboardSharingComponent = true;
|
||||
setup();
|
||||
|
||||
expect(screen.queryByTestId(selectors.pages.Dashboard.DashNav.shareButton)).not.toBeInTheDocument();
|
||||
const newShareButton = screen.getByTestId(selectors.pages.Dashboard.DashNav.newShareButton.container);
|
||||
expect(newShareButton).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let cleanUp = () => {};
|
||||
|
||||
@@ -23,6 +23,7 @@ import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
|
||||
|
||||
import { PanelEditor } from '../panel-edit/PanelEditor';
|
||||
import ShareButton from '../sharing/ShareButton/ShareButton';
|
||||
import { ShareModal } from '../sharing/ShareModal';
|
||||
import { DashboardInteractions } from '../utils/interactions';
|
||||
import { DynamicDashNavButtonModel, dynamicDashNavActions } from '../utils/registerDynamicDashNavAction';
|
||||
@@ -303,9 +304,10 @@ export function ToolbarActions({ dashboard }: Props) {
|
||||
),
|
||||
});
|
||||
|
||||
const showShareButton = uid && !isEditing && !meta.isSnapshot && !isPlaying;
|
||||
toolbarActions.push({
|
||||
group: 'main-buttons',
|
||||
condition: uid && !isEditing && !meta.isSnapshot && !isPlaying,
|
||||
condition: !config.featureToggles.newDashboardSharingComponent && showShareButton,
|
||||
render: () => (
|
||||
<Button
|
||||
key="share-dashboard-button"
|
||||
@@ -335,7 +337,7 @@ export function ToolbarActions({ dashboard }: Props) {
|
||||
tooltip="Enter edit mode"
|
||||
key="edit"
|
||||
className={styles.buttonWithExtraMargin}
|
||||
variant="primary"
|
||||
variant={config.featureToggles.newDashboardSharingComponent ? 'secondary' : 'primary'}
|
||||
size="sm"
|
||||
data-testid={selectors.components.NavToolbar.editDashboard.editButton}
|
||||
>
|
||||
@@ -344,6 +346,12 @@ export function ToolbarActions({ dashboard }: Props) {
|
||||
),
|
||||
});
|
||||
|
||||
toolbarActions.push({
|
||||
group: 'new-share-dashboard-button',
|
||||
condition: config.featureToggles.newDashboardSharingComponent && showShareButton,
|
||||
render: () => <ShareButton key="new-share-dashboard-button" dashboard={dashboard} />,
|
||||
});
|
||||
|
||||
toolbarActions.push({
|
||||
group: 'settings',
|
||||
condition: isEditing && dashboard.canEditDashboard() && isShowingDashboard,
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||
import { SceneGridLayout, SceneTimeRange, VizPanel } from '@grafana/scenes';
|
||||
|
||||
import { DashboardGridItem } from '../../scene/DashboardGridItem';
|
||||
import { DashboardScene } from '../../scene/DashboardScene';
|
||||
|
||||
import ShareButton from './ShareButton';
|
||||
|
||||
const createAndCopyDashboardShortLinkMock = jest.fn();
|
||||
jest.mock('app/core/utils/shortLinks', () => ({
|
||||
...jest.requireActual('app/core/utils/shortLinks'),
|
||||
createAndCopyDashboardShortLink: () => createAndCopyDashboardShortLinkMock(),
|
||||
}));
|
||||
|
||||
const selector = e2eSelectors.pages.Dashboard.DashNav.newShareButton;
|
||||
describe('ShareButton', () => {
|
||||
it('should render share link button and menu', async () => {
|
||||
setup();
|
||||
|
||||
expect(await screen.findByTestId(selector.shareLink)).toBeInTheDocument();
|
||||
expect(await screen.findByTestId(selector.arrowMenu)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call createAndCopyDashboardShortLink when share link clicked', async () => {
|
||||
setup();
|
||||
|
||||
const shareLink = await screen.findByTestId(selector.shareLink);
|
||||
|
||||
await userEvent.click(shareLink);
|
||||
expect(createAndCopyDashboardShortLinkMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render menu when arrow button clicked', async () => {
|
||||
setup();
|
||||
|
||||
const arrowMenu = await screen.findByTestId(selector.arrowMenu);
|
||||
await userEvent.click(arrowMenu);
|
||||
|
||||
expect(await screen.findByTestId(selector.menu.container)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
function setup() {
|
||||
const panel = new VizPanel({
|
||||
title: 'Panel A',
|
||||
pluginId: 'table',
|
||||
key: 'panel-12',
|
||||
});
|
||||
|
||||
const dashboard = new DashboardScene({
|
||||
title: 'hello',
|
||||
uid: 'dash-1',
|
||||
$timeRange: new SceneTimeRange({}),
|
||||
body: new SceneGridLayout({
|
||||
children: [
|
||||
new DashboardGridItem({
|
||||
key: 'griditem-1',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 10,
|
||||
height: 12,
|
||||
body: panel,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
render(<ShareButton dashboard={dashboard} />);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useAsyncFn } from 'react-use';
|
||||
|
||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||
import { Button, ButtonGroup, Dropdown } from '@grafana/ui';
|
||||
import { createAndCopyDashboardShortLink } from 'app/core/utils/shortLinks';
|
||||
|
||||
import { DashboardScene } from '../../scene/DashboardScene';
|
||||
import { DashboardInteractions } from '../../utils/interactions';
|
||||
|
||||
import ShareMenu from './ShareMenu';
|
||||
|
||||
const newShareButtonSelector = e2eSelectors.pages.Dashboard.DashNav.newShareButton;
|
||||
|
||||
export default function ShareButton({ dashboard }: { dashboard: DashboardScene }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const [_, buildUrl] = useAsyncFn(async () => {
|
||||
return await createAndCopyDashboardShortLink(dashboard, { useAbsoluteTimeRange: true, theme: 'current' });
|
||||
}, [dashboard]);
|
||||
|
||||
const onMenuClick = useCallback((isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
DashboardInteractions.toolbarShareClick();
|
||||
}
|
||||
|
||||
setIsOpen(isOpen);
|
||||
}, []);
|
||||
|
||||
const MenuActions = () => <ShareMenu dashboard={dashboard} />;
|
||||
|
||||
return (
|
||||
<ButtonGroup data-testid={newShareButtonSelector.container}>
|
||||
<Button data-testid={newShareButtonSelector.shareLink} size="sm" tooltip="Copy shortened URL" onClick={buildUrl}>
|
||||
Share
|
||||
</Button>
|
||||
<Dropdown overlay={MenuActions} placement="bottom-end" onVisibleChange={onMenuClick}>
|
||||
<Button data-testid={newShareButtonSelector.arrowMenu} size="sm" icon={isOpen ? 'angle-up' : 'angle-down'} />
|
||||
</Dropdown>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||
import { SceneGridLayout, SceneTimeRange, VizPanel } from '@grafana/scenes';
|
||||
|
||||
import { DashboardGridItem } from '../../scene/DashboardGridItem';
|
||||
import { DashboardScene } from '../../scene/DashboardScene';
|
||||
|
||||
import ShareMenu from './ShareMenu';
|
||||
|
||||
const createAndCopyDashboardShortLinkMock = jest.fn();
|
||||
jest.mock('app/core/utils/shortLinks', () => ({
|
||||
...jest.requireActual('app/core/utils/shortLinks'),
|
||||
createAndCopyDashboardShortLink: () => createAndCopyDashboardShortLinkMock(),
|
||||
}));
|
||||
|
||||
const selector = e2eSelectors.pages.Dashboard.DashNav.newShareButton.menu;
|
||||
describe('ShareMenu', () => {
|
||||
it('should call createAndCopyDashboardShortLink when share internally clicked', async () => {
|
||||
setup();
|
||||
|
||||
const shareLink = await screen.findByTestId(selector.shareInternally);
|
||||
|
||||
await userEvent.click(shareLink);
|
||||
expect(createAndCopyDashboardShortLinkMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
function setup() {
|
||||
const panel = new VizPanel({
|
||||
title: 'Panel A',
|
||||
pluginId: 'table',
|
||||
key: 'panel-12',
|
||||
});
|
||||
|
||||
const dashboard = new DashboardScene({
|
||||
title: 'hello',
|
||||
uid: 'dash-1',
|
||||
$timeRange: new SceneTimeRange({}),
|
||||
body: new SceneGridLayout({
|
||||
children: [
|
||||
new DashboardGridItem({
|
||||
key: 'griditem-1',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 10,
|
||||
height: 12,
|
||||
body: panel,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
render(<ShareMenu dashboard={dashboard} />);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { useAsyncFn } from 'react-use';
|
||||
|
||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||
import { Menu } from '@grafana/ui';
|
||||
|
||||
import { createAndCopyDashboardShortLink } from '../../../../core/utils/shortLinks';
|
||||
import { DashboardScene } from '../../scene/DashboardScene';
|
||||
|
||||
const newShareButtonSelector = e2eSelectors.pages.Dashboard.DashNav.newShareButton.menu;
|
||||
|
||||
export default function ShareMenu({ dashboard }: { dashboard: DashboardScene }) {
|
||||
const [_, buildUrl] = useAsyncFn(async () => {
|
||||
return await createAndCopyDashboardShortLink(dashboard, { useAbsoluteTimeRange: true, theme: 'current' });
|
||||
}, [dashboard]);
|
||||
|
||||
return (
|
||||
<Menu data-testid={newShareButtonSelector.container}>
|
||||
<Menu.Item
|
||||
testId={newShareButtonSelector.shareInternally}
|
||||
label="Share internally"
|
||||
description="Copy link"
|
||||
icon="building"
|
||||
onClick={buildUrl}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { ShareLinkTab } from './ShareLinkTab';
|
||||
|
||||
jest.mock('app/core/utils/shortLinks', () => ({
|
||||
...jest.requireActual('app/core/utils/shortLinks'),
|
||||
createShortLink: jest.fn().mockResolvedValue(`http://localhost:3000/goto/shortend-uid`),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
import { dateTime, UrlQueryMap } from '@grafana/data';
|
||||
import { dateTime } from '@grafana/data';
|
||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||
import { config, locationService } from '@grafana/runtime';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { SceneComponentProps, SceneObjectBase, SceneObjectRef, VizPanel, sceneGraph } from '@grafana/scenes';
|
||||
import { TimeZone } from '@grafana/schema';
|
||||
import { Alert, ClipboardButton, Field, FieldSet, Icon, Input, Switch } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { createShortLink } from 'app/core/utils/shortLinks';
|
||||
import { createDashboardShareUrl, createShortLink, getShareUrlParams } from 'app/core/utils/shortLinks';
|
||||
import { ThemePicker } from 'app/features/dashboard/components/ShareModal/ThemePicker';
|
||||
import { getTrackingSource, shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
||||
|
||||
@@ -51,36 +51,17 @@ export class ShareLinkTab extends SceneObjectBase<ShareLinkTabState> {
|
||||
const { panelRef, dashboardRef, useLockedTime: useAbsoluteTimeRange, useShortUrl, selectedTheme } = this.state;
|
||||
const dashboard = dashboardRef.resolve();
|
||||
const panel = panelRef?.resolve();
|
||||
const location = locationService.getLocation();
|
||||
const timeRange = sceneGraph.getTimeRange(panel ?? dashboard);
|
||||
|
||||
const urlParamsUpdate: UrlQueryMap = {};
|
||||
|
||||
if (panel) {
|
||||
urlParamsUpdate.viewPanel = panel.state.key;
|
||||
}
|
||||
|
||||
if (useAbsoluteTimeRange) {
|
||||
urlParamsUpdate.from = timeRange.state.value.from.toISOString();
|
||||
urlParamsUpdate.to = timeRange.state.value.to.toISOString();
|
||||
}
|
||||
|
||||
if (selectedTheme !== 'current') {
|
||||
urlParamsUpdate.theme = selectedTheme!;
|
||||
}
|
||||
|
||||
let shareUrl = getDashboardUrl({
|
||||
uid: dashboard.state.uid,
|
||||
slug: dashboard.state.meta.slug,
|
||||
currentQueryParams: location.search,
|
||||
updateQuery: urlParamsUpdate,
|
||||
absolute: true,
|
||||
});
|
||||
const opts = { useAbsoluteTimeRange, theme: selectedTheme };
|
||||
let shareUrl = await createDashboardShareUrl(dashboard, opts, panel);
|
||||
|
||||
if (useShortUrl) {
|
||||
shareUrl = await createShortLink(shareUrl);
|
||||
}
|
||||
|
||||
const timeRange = sceneGraph.getTimeRange(panel ?? dashboard);
|
||||
const urlParamsUpdate = getShareUrlParams(opts, timeRange, panel);
|
||||
|
||||
// the image panel solo route uses panelId instead of viewPanel
|
||||
let imageQueryParams = urlParamsUpdate;
|
||||
if (panel) {
|
||||
|
||||
Reference in New Issue
Block a user