QueryLibrary: Change context params (#109294)

This commit is contained in:
Juan Cabanas
2025-08-14 17:06:24 -03:00
committed by GitHub
parent a50e44c27a
commit 03811b26a0
9 changed files with 104 additions and 55 deletions
@@ -394,8 +394,12 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<Panel
<Button
icon="plus"
onClick={() =>
openQueryLibraryDrawer(getDatasourceNames(datasource, queries), onSelectQueryFromLibrary, {
context: CoreApp.PanelEditor,
openQueryLibraryDrawer({
datasourceFilters: getDatasourceNames(datasource, queries),
onSelectQuery: onSelectQueryFromLibrary,
options: {
context: CoreApp.PanelEditor,
},
})
}
variant="secondary"
@@ -3,7 +3,14 @@ import { createContext, ReactNode, useContext } from 'react';
import { CoreApp } from '@grafana/data';
import { DataQuery } from '@grafana/schema';
import { OnSelectQueryType } from './types';
import { OnSelectQueryType, QueryTemplate } from './types';
export type QueryLibraryDrawerOptions = {
datasourceFilters?: string[];
onSelectQuery?: OnSelectQueryType;
options?: { isReplacingQuery?: boolean; onSave?: () => void; context?: string; highlightQuery?: string };
query?: DataQuery;
};
/**
* Context with state and action to interact with Query Library. The Query Library feature consists of a drawer
@@ -16,36 +23,15 @@ export type QueryLibraryContextType = {
/**
* Opens a drawer with query library.
* @param datasourceFilters Data source names that will be used for initial filter in the library.
* @param queryActionButton Action button will be shown in the library next to the query and can implement context
* specific actions with the library, like running the query or updating some query in the current app.
* @param onSelectQuery Callback to be called when a query is selected from the library.
* @param options.context Used for QueryEditor. Should identify the context this is called from, like 'explore' or
* 'dashboard'.
* @param newQuery New query to be added to the library.
*/
openDrawer: (
datasourceFilters: string[],
onSelectQuery: OnSelectQueryType,
options?: {
isReplacingQuery?: boolean;
context?: string;
highlightQuery?: string;
}
) => void;
openDrawer: (options: QueryLibraryDrawerOptions) => void;
closeDrawer: () => void;
isDrawerOpen: boolean;
/**
* Opens a modal for adding a query to the library.
* @param query Query to be saved
* @param options.onSave Callback that will be called after the query is saved.
* @param options.context Used for rendering QueryEditor. Should identify the context this is called from, like 'explore' or
* 'dashboard'.
* @param options.title Default title for the modal, can be overridden by the query title.
*/
openAddQueryModal: (
query: DataQuery,
options?: { onSave?: () => void; context?: string; title?: string; isDuplicating?: boolean }
) => void;
closeAddQueryModal: () => void;
onSave?: () => void;
/**
* Returns a predefined small button that can be used to save a query to the library.
@@ -55,10 +41,12 @@ export type QueryLibraryContextType = {
query: DataQuery,
app?: CoreApp,
queryLibraryRef?: string,
onUpdateSuccess?: () => void
onUpdateSuccess?: () => void,
onSelectQuery?: (query: DataQuery) => void
) => ReactNode;
queryLibraryEnabled: boolean;
context: string;
setNewQuery: (query?: QueryTemplate) => void;
};
export const QueryLibraryContext = createContext<QueryLibraryContextType>({
@@ -66,8 +54,8 @@ export const QueryLibraryContext = createContext<QueryLibraryContextType>({
closeDrawer: () => {},
isDrawerOpen: false,
openAddQueryModal: () => {},
closeAddQueryModal: () => {},
setNewQuery: () => {},
onSave: () => {},
renderSaveQueryButton: () => {
return null;
@@ -13,11 +13,10 @@ export function QueryLibraryContextProviderMock(props: PropsWithChildren<Props>)
openDrawer: jest.fn(),
closeDrawer: jest.fn(),
isDrawerOpen: false,
openAddQueryModal: jest.fn(),
closeAddQueryModal: jest.fn(),
renderSaveQueryButton: jest.fn(),
queryLibraryEnabled: Boolean(props.queryLibraryEnabled),
context: 'explore',
setNewQuery: jest.fn(),
}}
>
{props.children}
@@ -1,3 +1,25 @@
import { DataQuery } from '@grafana/schema';
import { DataQuery, DataSourceRef } from '@grafana/schema';
export type User = {
uid: string;
displayName?: string;
avatarUrl?: string;
};
export type OnSelectQueryType = (query: DataQuery) => void;
export type QueryTemplate = {
query: DataQuery;
datasourceName?: string;
title?: string;
description?: string;
tags?: string[];
isLocked?: boolean;
isVisible?: boolean;
queryText?: string;
datasourceRef?: DataSourceRef | null;
datasourceType?: string;
createdAtTimestamp?: number;
user?: User;
uid?: string;
};
+6 -3
View File
@@ -107,9 +107,12 @@ export const QueryRows = ({ exploreId, isOpen, changeCompactMode }: Props) => {
// Open drawer with the original query highlighted
if (originalQueryRef) {
openDrawer([], () => {}, {
context: 'explore',
highlightQuery: originalQueryRef,
openDrawer({
datasourceFilters: [],
options: {
context: 'explore',
highlightQuery: originalQueryRef,
},
});
}
};
@@ -1,10 +1,14 @@
import { useState } from 'react';
import { t } from '@grafana/i18n';
import { reportInteraction } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema';
import { Button } from '@grafana/ui';
import { useDispatch, useSelector } from 'app/types/store';
import { useQueryLibraryContext } from '../QueryLibrary/QueryLibraryContext';
import { changeQueries } from '../state/query';
import { selectExploreDSMaps } from '../state/selectors';
type Props = {
query: DataQuery;
@@ -12,7 +16,17 @@ type Props = {
export const RichHistoryAddToLibrary = ({ query }: Props) => {
const [hasBeenSaved, setHasBeenSaved] = useState(false);
const { openAddQueryModal, queryLibraryEnabled } = useQueryLibraryContext();
const { openDrawer, queryLibraryEnabled } = useQueryLibraryContext();
const dispatch = useDispatch();
const exploreActiveDS = useSelector(selectExploreDSMaps);
const exploreId = exploreActiveDS.exploreToDS[0]?.exploreId;
const onSelectQuery = (newQuery: DataQuery) => {
reportInteraction('grafana_explore_query_replaced_from_library');
if (exploreId) {
dispatch(changeQueries({ exploreId, queries: [newQuery] }));
}
};
const buttonLabel = t('explore.rich-history-card.add-to-library', 'Add to library');
@@ -22,7 +36,16 @@ export const RichHistoryAddToLibrary = ({ query }: Props) => {
variant="secondary"
aria-label={buttonLabel}
onClick={() => {
openAddQueryModal(query, { onSave: () => setHasBeenSaved(true), context: 'rich-history' });
openDrawer({
query,
onSelectQuery,
options: {
onSave: () => {
setHasBeenSaved(true);
},
context: 'rich-history',
},
});
}}
>
{buttonLabel}
@@ -77,8 +77,10 @@ export function SecondaryActions({
aria-label={t('explore.secondary-actions.add-from-query-library', 'Add query from library')}
variant="canvas"
onClick={() =>
openQueryLibraryDrawer(activeDatasources, onSelectQueryFromLibrary, {
context: CoreApp.Explore,
openQueryLibraryDrawer({
datasourceFilters: activeDatasources,
onSelectQuery: onSelectQueryFromLibrary,
options: { context: CoreApp.Explore },
})
}
icon="plus"
@@ -56,7 +56,11 @@ export const addQueryHistoryToQueryLibrary = async () => {
};
export const submitAddToQueryLibrary = async ({ title }: { title: string }) => {
const input = within(screen.getByRole('dialog')).getByLabelText('Title');
const container = screen.getByRole('dialog', {
name: /Drawer title/i,
});
const input = within(container).getByRole('textbox', { name: /title/i });
await userEvent.type(input, title);
const saveButton = screen.getByRole('button', {
name: /^save$/i,
@@ -290,6 +290,11 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
this.onToggleHelp();
};
onSelectQueryFromLibrary = (query: DataQuery) => {
this.props.onQueryReplacedFromLibrary?.();
this.props.onReplace?.(query);
};
renderCollapsedText(): string | null {
const { datasource } = this.state;
if (datasource?.getQueryDisplayText) {
@@ -378,13 +383,7 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
};
renderActions = (props: QueryOperationRowRenderProps) => {
const {
query,
hideHideQueryButton: hideHideQueryButton = false,
onReplace,
onQueryReplacedFromLibrary,
queryLibraryRef,
} = this.props;
const { query, hideHideQueryButton: hideHideQueryButton = false, queryLibraryRef } = this.props;
const { datasource, showingHelp } = this.state;
const isHidden = !!query.hide;
@@ -398,16 +397,14 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
query={query}
queryLibraryRef={queryLibraryRef}
app={this.props.app}
onSelectQuery={this.onSelectQueryFromLibrary}
onUpdateSuccess={this.onExitQueryLibraryEditingMode}
/>
{!isEditingQueryLibrary && (
<ReplaceQueryFromLibrary
datasourceFilters={datasource?.name ? [datasource.name] : []}
onSelectQuery={(query) => {
onQueryReplacedFromLibrary?.();
onReplace?.(query);
}}
onSelectQuery={this.onSelectQueryFromLibrary}
app={this.props.app}
/>
)}
@@ -606,9 +603,16 @@ function MaybeQueryLibrarySaveButton(props: {
app?: CoreApp;
queryLibraryRef?: string;
onUpdateSuccess?: () => void;
onSelectQuery: (query: DataQuery) => void;
}) {
const { renderSaveQueryButton } = useQueryLibraryContext();
return renderSaveQueryButton(props.query, props.app, props.queryLibraryRef, props.onUpdateSuccess);
return renderSaveQueryButton(
props.query,
props.app,
props.queryLibraryRef,
props.onUpdateSuccess,
props.onSelectQuery
);
}
interface ReplaceQueryFromLibraryProps<TQuery extends DataQuery> {
@@ -625,7 +629,7 @@ function ReplaceQueryFromLibrary<TQuery extends DataQuery>({
const { openDrawer, queryLibraryEnabled } = useQueryLibraryContext();
const onReplaceQueryFromLibrary = () => {
openDrawer(datasourceFilters, onSelectQuery, { isReplacingQuery: true, context: app });
openDrawer({ datasourceFilters, onSelectQuery, options: { isReplacingQuery: true, context: app } });
};
return queryLibraryEnabled ? (