diff --git a/docs/sources/explore/index.md b/docs/sources/explore/index.md index 2530a3aae5f..7dca10ce46c 100644 --- a/docs/sources/explore/index.md +++ b/docs/sources/explore/index.md @@ -49,9 +49,15 @@ In split view, timepickers for both panels can be linked (if you change one, the You can close the newly created query by clicking on the Close Split button. +## Share shortened link + +> Share shortened link is only available in Grafana 7.3 and above. + +The Share shortened link capability allows you to create smaller and simpler URLs of the format /goto/:uid instead of using longer URLs containing complex query parameters. You can create a shortened link by clicking on the **Share** option in Explore toolbar. + ## Query history -Query history is a list of queries that you have used in Explore. The history is local to your browser and is not shared with others. To open and interact with your history, click the **Query history** button in Explore. +Query history is a list of queries that you have used in Explore. The history is local to your browser and is not shared. To open and interact with your history, click the **Query history** button in Explore. ### View query history @@ -60,7 +66,7 @@ Query history lets you view the history of your querying. For each individual qu - Run a query. - Create and/or edit a comment. - Copy a query to the clipboard. -- Copy a URL link with the query to the clipboard. +- Copy a shortened link with the query to the clipboard. - Star a query. ### Manage favorite queries diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index b801ba3453d..624881ea6dc 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -481,3 +481,12 @@ export const getFirstNonQueryRowSpecificError = (queryErrors?: DataQueryError[]) const refId = getValueWithRefId(queryErrors); return refId ? undefined : getFirstQueryErrorWithoutRefId(queryErrors); }; + +export const copyStringToClipboard = (string: string) => { + const el = document.createElement('textarea'); + el.value = string; + document.body.appendChild(el); + el.select(); + document.execCommand('copy'); + document.body.removeChild(el); +}; diff --git a/public/app/core/utils/richHistory.ts b/public/app/core/utils/richHistory.ts index b0eb3e7b97e..2ad5fb00f98 100644 --- a/public/app/core/utils/richHistory.ts +++ b/public/app/core/utils/richHistory.ts @@ -170,15 +170,6 @@ export const sortQueries = (array: RichHistoryQuery[], sortOrder: SortOrder) => return array.sort(sortFunc); }; -export const copyStringToClipboard = (string: string) => { - const el = document.createElement('textarea'); - el.value = string; - document.body.appendChild(el); - el.select(); - document.execCommand('copy'); - document.body.removeChild(el); -}; - export const createUrlFromRichHistory = (query: RichHistoryQuery) => { const exploreState: ExploreUrlState = { /* Default range, as we are not saving timerange in rich history */ diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index 210316f8753..6e8a5125053 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -7,9 +7,11 @@ import { css } from 'emotion'; import { ExploreId, ExploreItemState } from 'app/types/explore'; import { Icon, IconButton, LegacyForms, SetInterval, Tooltip } from '@grafana/ui'; -import { DataQuery, RawTimeRange, TimeRange, TimeZone } from '@grafana/data'; +import { DataQuery, RawTimeRange, TimeRange, TimeZone, AppEvents } from '@grafana/data'; import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { StoreState } from 'app/types/store'; +import { copyStringToClipboard } from 'app/core/utils/explore'; +import appEvents from 'app/core/app_events'; import { cancelQueries, changeDatasource, @@ -25,6 +27,7 @@ import { getTimeZone } from '../profile/state/selectors'; import { updateTimeZoneForSession } from '../profile/state/reducers'; import { getDashboardSrv } from '../dashboard/services/DashboardSrv'; import kbn from '../../core/utils/kbn'; +import { createShortLink } from './utils/links'; import { ExploreTimeControls } from './ExploreTimeControls'; import { LiveTailButton } from './LiveTailButton'; import { ResponsiveButton } from './ResponsiveButton'; @@ -152,6 +155,16 @@ export class UnConnectedExploreToolbar extends PureComponent { return datasourceName ? exploreDatasources.find(datasource => datasource.name === datasourceName) : undefined; }; + copyAndSaveShortLink = async () => { + const shortLink = await createShortLink(window.location.href); + if (shortLink) { + copyStringToClipboard(shortLink); + appEvents.emit(AppEvents.alertSuccess, ['Shortened link copied to clipboard']); + } else { + appEvents.emit(AppEvents.alertError, ['Error generating shortened link']); + } + }; + render() { const { datasourceMissing, @@ -262,6 +275,13 @@ export class UnConnectedExploreToolbar extends PureComponent { /> ) : null} +
+ + + +
{!isLive && (
{ - const url = createUrlFromRichHistory(query); - copyStringToClipboard(url); - appEvents.emit(AppEvents.alertSuccess, ['Link copied to clipboard']); + const onCreateLink = async () => { + const link = createUrlFromRichHistory(query); + const shortLink = await createShortLink(link); + if (shortLink) { + copyStringToClipboard(shortLink); + appEvents.emit(AppEvents.alertSuccess, ['Shortened link copied to clipboard']); + } else { + appEvents.emit(AppEvents.alertError, ['Error generating shortened link']); + } }; const onDeleteQuery = () => { @@ -254,7 +261,7 @@ export function RichHistoryCard(props: Props) { title={query.comment?.length > 0 ? 'Edit comment' : 'Add comment'} /> - {!isRemoved && } + {!isRemoved && }