diff --git a/public/app/features/explore/RichHistory/RichHistoryAddToLibrary.tsx b/public/app/features/explore/RichHistory/RichHistoryAddToLibrary.tsx
index 89d5fde0aab..47de3d424b8 100644
--- a/public/app/features/explore/RichHistory/RichHistoryAddToLibrary.tsx
+++ b/public/app/features/explore/RichHistory/RichHistoryAddToLibrary.tsx
@@ -1,18 +1,21 @@
import { t } from 'i18next';
-import React from 'react';
+import React, { useState } from 'react';
import { AppEvents, dateTime } from '@grafana/data';
import { getAppEvents } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema';
-import { Button } from '@grafana/ui';
+import { Button, Modal } from '@grafana/ui';
import { isQueryLibraryEnabled, useAddQueryTemplateMutation } from 'app/features/query-library';
import { AddQueryTemplateCommand } from 'app/features/query-library/types';
+import { QueryDetails, RichHistoryAddToLibraryForm } from './RichHistoryAddToLibraryForm';
+
type Props = {
query: DataQuery;
};
export const RichHistoryAddToLibrary = ({ query }: Props) => {
+ const [isOpen, setIsOpen] = useState(false);
const [addQueryTemplate, { isSuccess }] = useAddQueryTemplateMutation();
const handleAddQueryTemplate = async (addQueryTemplateCommand: AddQueryTemplateCommand) => {
@@ -29,17 +32,31 @@ export const RichHistoryAddToLibrary = ({ query }: Props) => {
const buttonLabel = t('explore.rich-history-card.add-to-library', 'Add to library');
+ const submit = (data: QueryDetails) => {
+ const timestamp = dateTime().toISOString();
+ const temporaryDefaultTitle = data.description || `Imported from Explore - ${timestamp}`;
+ handleAddQueryTemplate({ title: temporaryDefaultTitle, targets: [query] });
+ };
+
return isQueryLibraryEnabled() && !isSuccess ? (
-
+ <>
+
+ setIsOpen(false)}
+ >
+ setIsOpen(() => false)}
+ query={query}
+ onSave={(data) => {
+ submit(data);
+ setIsOpen(false);
+ }}
+ />
+
+ >
) : undefined;
};
diff --git a/public/app/features/explore/RichHistory/RichHistoryAddToLibraryForm.tsx b/public/app/features/explore/RichHistory/RichHistoryAddToLibraryForm.tsx
new file mode 100644
index 00000000000..b7bed1dce81
--- /dev/null
+++ b/public/app/features/explore/RichHistory/RichHistoryAddToLibraryForm.tsx
@@ -0,0 +1,83 @@
+import React, { useMemo } from 'react';
+import { useForm } from 'react-hook-form';
+
+import { DataSourcePicker } from '@grafana/runtime';
+import { DataQuery } from '@grafana/schema';
+import { Button, InlineSwitch, Modal, RadioButtonGroup, TextArea } from '@grafana/ui';
+import { Field } from '@grafana/ui/';
+import { Input } from '@grafana/ui/src/components/Input/Input';
+import { t } from 'app/core/internationalization';
+
+import { getQueryDisplayText } from '../../../core/utils/richHistory';
+import { useDatasource } from '../QueryLibrary/utils/useDatasource';
+
+type Props = {
+ onCancel: () => void;
+ onSave: (details: QueryDetails) => void;
+ query: DataQuery;
+};
+
+export type QueryDetails = {
+ description: string;
+};
+
+const VisibilityOptions = [
+ { value: 'Public', label: 'Public' },
+ { value: 'Private', label: 'Private' },
+];
+
+const info = t(
+ 'explore.add-to-library-modal.info',
+ `You're about to save this query. Once saved, you can easily access it in the Query Library tab for future use and reference.`
+);
+
+export const RichHistoryAddToLibraryForm = ({ onCancel, onSave, query }: Props) => {
+ const { register, handleSubmit } = useForm();
+
+ const datasource = useDatasource(query.datasource);
+
+ const displayText = useMemo(() => {
+ return datasource?.getQueryDisplayText?.(query) || getQueryDisplayText(query);
+ }, [datasource, query]);
+
+ const onSubmit = (data: QueryDetails) => {
+ onSave(data);
+ };
+
+ return (
+
+ );
+};
diff --git a/public/app/features/explore/spec/helper/interactions.ts b/public/app/features/explore/spec/helper/interactions.ts
index fa63c6554a9..e3deed335aa 100644
--- a/public/app/features/explore/spec/helper/interactions.ts
+++ b/public/app/features/explore/spec/helper/interactions.ts
@@ -55,6 +55,15 @@ export const addQueryHistoryToQueryLibrary = async () => {
await userEvent.click(button);
};
+export const submitAddToQueryLibrary = async ({ description }: { description: string }) => {
+ const input = within(screen.getByRole('dialog')).getByLabelText('Description');
+ await userEvent.type(input, description);
+ const saveButton = screen.getByRole('button', {
+ name: /save/i,
+ });
+ await userEvent.click(saveButton);
+};
+
export const closeQueryHistory = async () => {
const selector = withinQueryHistory();
const closeButton = selector.getByRole('button', { name: 'Close query history' });
diff --git a/public/app/features/explore/spec/queryLibrary.test.tsx b/public/app/features/explore/spec/queryLibrary.test.tsx
index 4b0c0ba2b92..8b9b53aa38f 100644
--- a/public/app/features/explore/spec/queryLibrary.test.tsx
+++ b/public/app/features/explore/spec/queryLibrary.test.tsx
@@ -16,6 +16,7 @@ import {
addQueryHistoryToQueryLibrary,
openQueryHistory,
openQueryLibrary,
+ submitAddToQueryLibrary,
switchToQueryHistory,
} from './helper/interactions';
import { setupExplore, waitForExplore } from './helper/setup';
@@ -134,6 +135,7 @@ describe('QueryLibrary', () => {
await switchToQueryHistory();
await assertQueryHistory(['{"expr":"TEST"}']);
await addQueryHistoryToQueryLibrary();
+ await submitAddToQueryLibrary({ description: 'Test' });
expect(testEventBus.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: 'alert-success',
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index b0e163a7deb..3454d9220f9 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -465,6 +465,16 @@
},
"explore": {
"add-to-dashboard": "Add to dashboard",
+ "add-to-library-modal": {
+ "auto-star": "Auto-star this query to add it to your starred list in the Query Library.",
+ "data-source-name": "Data source name",
+ "data-source-type": "Data source type",
+ "description": "Description",
+ "info": "You're about to save this query. Once saved, you can easily access it in the Query Library tab for future use and reference.",
+ "query": "Query",
+ "title": "Add query to Query Library",
+ "visibility": "Visibility"
+ },
"rich-history": {
"close-tooltip": "Close query history",
"datasource-a-z": "Data source A-Z",
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index 33b61703f2e..7e68c61c29f 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -465,6 +465,16 @@
},
"explore": {
"add-to-dashboard": "Åđđ ŧő đäşĥþőäřđ",
+ "add-to-library-modal": {
+ "auto-star": "Åūŧő-şŧäř ŧĥįş qūęřy ŧő äđđ įŧ ŧő yőūř şŧäřřęđ ľįşŧ įʼn ŧĥę Qūęřy Ŀįþřäřy.",
+ "data-source-name": "Đäŧä şőūřčę ʼnämę",
+ "data-source-type": "Đäŧä şőūřčę ŧypę",
+ "description": "Đęşčřįpŧįőʼn",
+ "info": "Ÿőū'řę äþőūŧ ŧő şävę ŧĥįş qūęřy. Øʼnčę şävęđ, yőū čäʼn ęäşįľy äččęşş įŧ įʼn ŧĥę Qūęřy Ŀįþřäřy ŧäþ ƒőř ƒūŧūřę ūşę äʼnđ řęƒęřęʼnčę.",
+ "query": "Qūęřy",
+ "title": "Åđđ qūęřy ŧő Qūęřy Ŀįþřäřy",
+ "visibility": "Vįşįþįľįŧy"
+ },
"rich-history": {
"close-tooltip": "Cľőşę qūęřy ĥįşŧőřy",
"datasource-a-z": "Đäŧä şőūřčę Å-Ż",