Query Library: Edit Query Library in explore using queryLibraryRef (#108863)

* add suport for queryLibraryRef in explore

* add feature flag check on badge

* add test for migrator

* Add QueryEditor.test and useStateSync test

* implement onCancelEditingQuery library

* run i18n extract

* push mod changes

* Revert "push mod changes"

This reverts commit 29c410f126.

* remove queryRef from the URL

* remove queryRef from url in explore page, keep redux logic

* restore tests and remove old queryRef from URL behaviour

* clean up test

* remove unnecessary useCallback from onCancelQueryLibraryEdit

* apply PR suggestions about naming of functions

* rename queryRef to queryLibraryRef

* update missing files with the new queryLibraryRef

* fix i18n check

* Disabled add query buttons when queryLibraryRef is present

* Add unit test to components

* add missing queryLibraryRef after merge

* add placeholder for highlighted code

* clear up queryLibraryRef after sucess update

* add monitoring

* remove unnecessary change

* remove placeholder and integrate highlightQuery

* change "Editing From Query Library" to "Update query from library"

* update tootlip text

* fix interaction importing issue

* updating i18n for query library editing badge

* update copy on saving actions

* keep copy short

---------

Co-authored-by: Juan Cabanas <juan.cabanas@grafana.com>
This commit is contained in:
Alexa Vargas
2025-08-13 14:46:45 +02:00
committed by GitHub
co-authored by Juan Cabanas
parent 94f7602786
commit 48edf1808e
14 changed files with 327 additions and 33 deletions
+5 -1
View File
@@ -93,7 +93,11 @@ export async function getExploreUrl(args: GetExploreUrlArguments): Promise<strin
.map((q) => q.value);
const exploreState = JSON.stringify({
[generateExploreId()]: { range: toURLRange(timeRange.raw), queries: interpolatedQueries, datasource: dsRef?.uid },
[generateExploreId()]: {
range: toURLRange(timeRange.raw),
queries: interpolatedQueries,
datasource: dsRef?.uid,
},
});
return locationUtil.assureBaseUrl(urlUtil.renderUrl('/explore', { panes: exploreState, schemaVersion: 1 }));
}
@@ -17,6 +17,7 @@ import { configureStore } from 'app/store/configureStore';
import { ContentOutlineContextProvider } from './ContentOutline/ContentOutlineContext';
import { Explore, Props } from './Explore';
import { QueryLibraryContextProviderMock } from './QueryLibrary/mocks';
import { initialExploreState } from './state/main';
import { scanStopAction } from './state/query';
import { createEmptyQueryResponse, makeExplorePaneState } from './state/utils';
@@ -110,6 +111,7 @@ const dummyProps: Props = {
changeDatasource: jest.fn(),
compact: false,
changeCompactMode: jest.fn(),
queryLibraryRef: undefined,
};
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
@@ -240,4 +242,57 @@ describe('Explore', () => {
getBoolMock.mockRestore();
});
});
describe('Query Library Integration', () => {
it('should enable add query buttons when queryLibraryRef is undefined', async () => {
setup({ queryLibraryRef: undefined });
// Wait for the Explore component to render
await screen.findByTestId(selectors.components.DataSourcePicker.container);
const addQueryButton = screen.getByRole('button', { name: /Add query$/i });
expect(addQueryButton).toBeEnabled();
});
it('should disable add query buttons when queryLibraryRef is set (editing from library)', async () => {
setup({ queryLibraryRef: 'library-query-123' });
// Wait for the Explore component to render
await screen.findByTestId(selectors.components.DataSourcePicker.container);
const addQueryButton = screen.getByRole('button', { name: /Add query$/i });
expect(addQueryButton).toBeDisabled();
});
it('should disable both add query and add from library buttons when editing from library', async () => {
const store = configureStore({
explore: {
...initialExploreState,
panes: {
left: makeExplorePaneState(),
},
},
});
const exploreProps = { ...dummyProps, queryLibraryRef: 'library-query-123' };
render(
<TestProvider store={store}>
<QueryLibraryContextProviderMock queryLibraryEnabled={true}>
<ContentOutlineContextProvider>
<Explore {...exploreProps} />
</ContentOutlineContextProvider>
</QueryLibraryContextProviderMock>
</TestProvider>
);
// Wait for the Explore component to render
await screen.findByTestId(selectors.components.DataSourcePicker.container);
const addQueryButton = screen.getByRole('button', { name: /Add query$/i });
const addFromLibraryButton = screen.getByRole('button', { name: /Add query from library/i });
expect(addQueryButton).toBeDisabled();
expect(addFromLibraryButton).toBeDisabled();
});
});
});
+4 -1
View File
@@ -580,6 +580,7 @@ export class Explore extends PureComponent<Props, ExploreState> {
setShowQueryInspector,
splitted,
compact,
queryLibraryRef,
} = this.props;
const { contentOutlineVisible } = this.state;
const styles = getStyles(theme);
@@ -654,7 +655,7 @@ export class Explore extends PureComponent<Props, ExploreState> {
<SecondaryActions
// do not allow people to add queries with potentially different datasources in correlations editor mode
addQueryRowButtonDisabled={
isLive || (isCorrelationsEditorMode && datasourceInstance.meta.mixed)
isLive || (isCorrelationsEditorMode && datasourceInstance.meta.mixed) || !!queryLibraryRef
}
// We cannot show multiple traces at the same time right now so we do not show add query button.
//TODO:unification
@@ -766,6 +767,7 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
supplementaryQueries,
correlationEditorHelperData,
compact,
queryLibraryRef,
} = item;
const loading = selectIsWaitingForData(exploreId)(state);
@@ -799,6 +801,7 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
correlationEditorHelperData,
correlationEditorDetails: explore.correlationEditorDetails,
exploreActiveDS: selectExploreDSMaps(state),
queryLibraryRef,
};
}
@@ -51,7 +51,12 @@ export type QueryLibraryContextType = {
* Returns a predefined small button that can be used to save a query to the library.
* @param query
*/
renderSaveQueryButton: (query: DataQuery, app?: CoreApp) => ReactNode;
renderSaveQueryButton: (
query: DataQuery,
app?: CoreApp,
queryLibraryRef?: string,
onUpdateSuccess?: () => void
) => ReactNode;
queryLibraryEnabled: boolean;
context: string;
};
+31 -4
View File
@@ -11,7 +11,9 @@ import { getDatasourceSrv } from '../plugins/datasource_srv';
import { QueryEditorRows } from '../query/components/QueryEditorRows';
import { ContentOutlineItem } from './ContentOutline/ContentOutlineItem';
import { useQueryLibraryContext } from './QueryLibrary/QueryLibraryContext';
import { changeDatasource } from './state/datasource';
import { updateQueryLibraryRefAction } from './state/explorePane';
import { changeQueries, runQueries } from './state/query';
import { getExploreItemSelector } from './state/selectors';
@@ -32,21 +34,28 @@ const makeSelectors = (exploreId: string) => {
exploreItemSelector,
(s: ExploreItemState | undefined) => getDatasourceSrv().getInstanceSettings(s!.datasourceInstance?.uid)!
),
getQueryLibraryRef: createSelector(exploreItemSelector, (s) => s!.queryLibraryRef),
};
};
export const QueryRows = ({ exploreId, isOpen, changeCompactMode }: Props) => {
const dispatch = useDispatch();
const { getQueries, getDatasourceInstanceSettings, getQueryResponse, getHistory, getEventBridge } = useMemo(
() => makeSelectors(exploreId),
[exploreId]
);
const { openDrawer } = useQueryLibraryContext();
const {
getQueries,
getDatasourceInstanceSettings,
getQueryResponse,
getHistory,
getEventBridge,
getQueryLibraryRef,
} = useMemo(() => makeSelectors(exploreId), [exploreId]);
const queries = useSelector(getQueries);
const dsSettings = useSelector(getDatasourceInstanceSettings);
const queryResponse = useSelector(getQueryResponse);
const history = useSelector(getHistory);
const eventBridge = useSelector(getEventBridge);
const queryLibraryRef = useSelector(getQueryLibraryRef);
const onRunQueries = useCallback(() => {
dispatch(runQueries({ exploreId }));
@@ -89,6 +98,22 @@ export const QueryRows = ({ exploreId, isOpen, changeCompactMode }: Props) => {
reportInteraction('grafana_query_row_toggle', queryStatus === undefined ? {} : { queryEnabled: queryStatus });
};
const onCancelQueryLibraryEdit = () => {
// Store the current queryLibraryRef before clearing it
const originalQueryRef = queryLibraryRef;
// Clear the queryLibraryRef to exit editing mode
dispatch(updateQueryLibraryRefAction({ exploreId, queryLibraryRef: undefined }));
// Open drawer with the original query highlighted
if (originalQueryRef) {
openDrawer([], () => {}, {
context: 'explore',
highlightQuery: originalQueryRef,
});
}
};
const onQueryOpenChanged = () => {
// Disables compact mode when query is opened.
// Compact mode can also be disabled by opening Content Outline.
@@ -112,6 +137,8 @@ export const QueryRows = ({ exploreId, isOpen, changeCompactMode }: Props) => {
app={CoreApp.Explore}
history={history}
eventBus={eventBridge}
queryLibraryRef={queryLibraryRef}
onCancelQueryLibraryEdit={onCancelQueryLibraryEdit}
isOpen={isOpen}
queryRowWrapper={(children, refId) => (
<ContentOutlineItem
@@ -64,6 +64,23 @@ describe('SecondaryActions', () => {
expect(screen.getByRole('button', { name: /Query inspector/i })).toBeInTheDocument();
});
it('should disable both add query buttons when addQueryRowButtonDisabled=true and query library is enabled', () => {
render(
<QueryLibraryContextProviderMock queryLibraryEnabled={true}>
<SecondaryActions
addQueryRowButtonDisabled={true}
onClickAddQueryRowButton={noop}
onClickQueryInspectorButton={noop}
onSelectQueryFromLibrary={noop}
/>
</QueryLibraryContextProviderMock>
);
expect(screen.getByRole('button', { name: /Add query$/i })).toBeDisabled();
expect(screen.getByRole('button', { name: /Add query from library/i })).toBeDisabled();
expect(screen.getByRole('button', { name: /Query inspector/i })).toBeInTheDocument();
});
it('should map click handlers correctly', async () => {
const user = userEvent.setup();
@@ -82,6 +82,7 @@ export function SecondaryActions({
})
}
icon="plus"
disabled={addQueryRowButtonDisabled}
>
<Trans i18nKey="explore.secondary-actions.add-from-query-library">Add query from library</Trans>
</ToolbarButton>
@@ -12,6 +12,7 @@ import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSou
import { configureStore } from 'app/store/configureStore';
import { makeDatasourceSetup } from '../../spec/helper/setup';
import { updateQueryLibraryRefAction } from '../../state/explorePane';
import { splitClose, splitOpen } from '../../state/main';
import { useStateSync } from './';
@@ -584,4 +585,34 @@ describe('useStateSync', () => {
expect(store.getState().explore.panes['one']?.queries[0]).toMatchObject({ expr: 'b', refId: 'B' });
});
});
it('should keep queryLibraryRef in state but not in URL', async () => {
const { store, location } = setup({
queryParams: {
panes: JSON.stringify({
one: {
datasource: 'loki-uid',
queries: [{ expr: 'test', refId: 'A' }],
},
}),
schemaVersion: 1,
},
});
await waitFor(() => {
expect(store.getState().explore.panes['one']).toBeDefined();
});
act(() => {
store.dispatch(updateQueryLibraryRefAction({ exploreId: 'one', queryLibraryRef: 'library-query-456' }));
});
await waitFor(() => {
expect(store.getState().explore.panes['one']?.queryLibraryRef).toBe('library-query-456');
const search = location.getSearchObject();
const panes = search.panes && typeof search.panes === 'string' ? JSON.parse(search.panes) : {};
expect(panes.one?.queryLibraryRef).toBeUndefined();
});
});
});
@@ -97,6 +97,13 @@ export const changeCorrelationHelperData = createAction<ChangeCorrelationHelperD
'explore/changeCorrelationHelperData'
);
export interface UpdateQueryLibraryRefPayload {
exploreId: string;
queryLibraryRef?: string;
}
export const updateQueryLibraryRefAction = createAction<UpdateQueryLibraryRefPayload>('explore/updateQueryLibraryRef');
/**
* Initialize Explore state with state from the URL and the React component.
* Call this only on components for with the Explore state has not been initialized.
@@ -109,6 +116,7 @@ interface InitializeExplorePayload {
datasourceInstance?: DataSourceApi;
compact: boolean;
eventBridge: EventBusExtended;
queryLibraryRef?: string;
}
const initializeExploreAction = createAction<InitializeExplorePayload>('explore/initializeExploreAction');
@@ -147,6 +155,7 @@ export interface InitializeExploreOptions {
correlationHelperData?: ExploreCorrelationHelperData;
position?: number;
eventBridge: EventBusExtended;
queryLibraryRef?: string;
compact: boolean;
}
@@ -170,6 +179,7 @@ export const initializeExplore = createAsyncThunk(
compact,
correlationHelperData,
eventBridge,
queryLibraryRef,
}: InitializeExploreOptions,
{ dispatch, getState, fulfillWithValue }
) => {
@@ -192,6 +202,7 @@ export const initializeExplore = createAsyncThunk(
history,
compact,
eventBridge,
queryLibraryRef,
})
);
if (panelsState !== undefined) {
@@ -262,8 +273,15 @@ export const paneReducer = (state: ExploreItemState = makeExplorePaneState(), ac
};
}
if (updateQueryLibraryRefAction.match(action)) {
return {
...state,
queryLibraryRef: action.payload.queryLibraryRef,
};
}
if (initializeExploreAction.match(action)) {
const { queries, range, datasourceInstance, history, eventBridge, compact } = action.payload;
const { queries, range, datasourceInstance, history, eventBridge, compact, queryLibraryRef } = action.payload;
return {
...state,
@@ -277,6 +295,7 @@ export const paneReducer = (state: ExploreItemState = makeExplorePaneState(), ac
queryResponse: createEmptyQueryResponse(),
cache: [],
correlations: [],
queryLibraryRef,
compact,
};
}
@@ -5,15 +5,28 @@ import { DataQueryRequest, dateTime, LoadingState, PanelData, toDataFrame } from
import { DataQuery } from '@grafana/schema';
import { mockDataSource } from 'app/features/alerting/unified/mocks';
import { DataSourceType } from '../../alerting/unified/utils/datasource';
import { filterPanelDataToQuery, Props, QueryEditorRow } from './QueryEditorRow';
import { filterPanelDataToQuery, Props, QueryEditorRow, QueryLibraryEditingBadge } from './QueryEditorRow';
const mockDS = mockDataSource({
name: 'test',
type: DataSourceType.Alertmanager,
type: 'testdata',
});
// Mock the QueryLibraryContext
const mockQueryLibraryContext = {
queryLibraryEnabled: true,
};
jest.mock('app/features/explore/QueryLibrary/QueryLibraryContext', () => ({
useQueryLibraryContext: () => mockQueryLibraryContext,
}));
// Mock the internationalization function
jest.mock('@grafana/i18n', () => ({
...jest.requireActual('@grafana/i18n'),
t: (key: string, defaultValue: string) => defaultValue,
}));
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: () => ({
@@ -392,3 +405,30 @@ describe('QueryEditorRow', () => {
});
});
});
describe('QueryLibraryBadge', () => {
beforeEach(() => {
mockQueryLibraryContext.queryLibraryEnabled = true;
});
it('should display badge when queryLibraryEnabled is true and queryLibraryRef is provided', () => {
render(<QueryLibraryEditingBadge queryLibraryRef="library-query-123" />);
expect(screen.getByText('Updating query from library')).toBeInTheDocument();
});
it('should not display badge when queryLibraryEnabled is false', () => {
mockQueryLibraryContext.queryLibraryEnabled = false;
render(<QueryLibraryEditingBadge queryLibraryRef="library-query-123" />);
expect(screen.queryByText('Updating query from library')).not.toBeInTheDocument();
});
it('should not display badge when queryLibraryRef is not provided', () => {
render(<QueryLibraryEditingBadge />);
expect(screen.queryByText('Updating query from library')).not.toBeInTheDocument();
});
it('should not display badge when queryLibraryRef is empty string', () => {
render(<QueryLibraryEditingBadge queryLibraryRef="" />);
expect(screen.queryByText('Updating query from library')).not.toBeInTheDocument();
});
});
@@ -22,7 +22,7 @@ import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { getDataSourceSrv, renderLimitedComponents, reportInteraction, usePluginComponents } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema';
import { Badge, ErrorBoundaryAlert, List } from '@grafana/ui';
import { Badge, Divider, ErrorBoundaryAlert, List } from '@grafana/ui';
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
import {
QueryOperationAction,
@@ -68,6 +68,8 @@ export interface Props<TQuery extends DataQuery> {
onQueryReplacedFromLibrary?: () => void;
collapsable?: boolean;
hideRefId?: boolean;
queryLibraryRef?: string;
onCancelQueryLibraryEdit?: () => void;
isOpen?: boolean;
}
@@ -233,6 +235,19 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
}
};
onCancelQueryLibraryEdit = () => {
const { query } = this.props;
reportInteraction('query_library-update_query_from_explore_cancelled', {
datasourceType: query.datasource?.type,
});
this.props.onCancelQueryLibraryEdit?.();
};
onExitQueryLibraryEditingMode = () => {
// Exit query library editing mode after successful update
this.props.onCancelQueryLibraryEdit?.();
};
onCopyQuery = () => {
const { query, onAddQuery, onQueryCopied } = this.props;
const copy = cloneDeep(query);
@@ -326,6 +341,11 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
);
};
renderQueryLibraryEditingBadge = () => {
const { queryLibraryRef } = this.props;
return <QueryLibraryEditingBadge key="query-library-editing-badge" queryLibraryRef={queryLibraryRef} />;
};
renderExtraActions = () => {
const { query, queries, data, onAddQuery, dataSource, app } = this.props;
@@ -363,24 +383,46 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
hideHideQueryButton: hideHideQueryButton = false,
onReplace,
onQueryReplacedFromLibrary,
queryLibraryRef,
} = this.props;
const { datasource, showingHelp } = this.state;
const isHidden = !!query.hide;
const hasEditorHelp = datasource?.components?.QueryEditorHelp;
const isEditingQueryLibrary = queryLibraryRef !== undefined;
return (
<>
<MaybeQueryLibrarySaveButton query={query} app={this.props.app} />
<ReplaceQueryFromLibrary
datasourceFilters={datasource?.name ? [datasource.name] : []}
onSelectQuery={(query) => {
onQueryReplacedFromLibrary?.();
onReplace?.(query);
}}
{isEditingQueryLibrary && this.renderQueryLibraryEditingBadge()}
<MaybeQueryLibrarySaveButton
query={query}
queryLibraryRef={queryLibraryRef}
app={this.props.app}
onUpdateSuccess={this.onExitQueryLibraryEditingMode}
/>
{!isEditingQueryLibrary && (
<ReplaceQueryFromLibrary
datasourceFilters={datasource?.name ? [datasource.name] : []}
onSelectQuery={(query) => {
onQueryReplacedFromLibrary?.();
onReplace?.(query);
}}
app={this.props.app}
/>
)}
{isEditingQueryLibrary && (
<>
<QueryOperationAction
title={t('query-operation.header.cancel-query-library-edit', 'Discard changes')}
icon="times"
onClick={this.onCancelQueryLibraryEdit}
/>
<Divider direction="vertical" spacing={0} />
</>
)}
{hasEditorHelp && (
<QueryOperationToggleAction
title={t('query-operation.header.datasource-help', 'Show data source help')}
@@ -390,11 +432,14 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
/>
)}
{this.renderExtraActions()}
<QueryOperationAction
title={t('query-operation.header.duplicate-query', 'Duplicate query')}
icon="copy"
onClick={this.onCopyQuery}
/>
{!isEditingQueryLibrary && (
<QueryOperationAction
title={t('query-operation.header.duplicate-query', 'Duplicate query')}
icon="copy"
onClick={this.onCopyQuery}
/>
)}
{!hideHideQueryButton ? (
<QueryOperationToggleAction
dataTestId={selectors.components.QueryEditorRow.actionButton('Hide response')}
@@ -408,11 +453,13 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
onClick={this.onHideQuery}
/>
) : null}
<QueryOperationAction
title={t('query-operation.header.remove-query', 'Remove query')}
icon="trash-alt"
onClick={this.onRemoveQuery}
/>
{!isEditingQueryLibrary && (
<QueryOperationAction
title={t('query-operation.header.remove-query', 'Remove query')}
icon="trash-alt"
onClick={this.onRemoveQuery}
/>
)}
</>
);
};
@@ -488,6 +535,30 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
}
}
export function QueryLibraryEditingBadge(props: { queryLibraryRef?: string }) {
const { queryLibraryEnabled } = useQueryLibraryContext();
const { queryLibraryRef } = props;
if (!queryLibraryEnabled || !queryLibraryRef) {
return null;
}
return (
<Badge
color="blue"
icon="book"
text={t('query-operation.query-library.from-library', 'Updating query from library')}
tooltip={t(
'query-operation.query-library.editing-tooltip',
'Updating query from library\nUID: {{queryLibraryRef}}',
{
queryLibraryRef,
}
)}
/>
);
}
/**
* Get a version of the PanelData limited to the query we are looking at
*/
@@ -530,9 +601,14 @@ export function filterPanelDataToQuery(data: PanelData, refId: string): PanelDat
}
// Will render anything only if query library is enabled
function MaybeQueryLibrarySaveButton(props: { query: DataQuery; app?: CoreApp }) {
function MaybeQueryLibrarySaveButton(props: {
query: DataQuery;
app?: CoreApp;
queryLibraryRef?: string;
onUpdateSuccess?: () => void;
}) {
const { renderSaveQueryButton } = useQueryLibraryContext();
return renderSaveQueryButton(props.query, props.app);
return renderSaveQueryButton(props.query, props.app, props.queryLibraryRef, props.onUpdateSuccess);
}
interface ReplaceQueryFromLibraryProps<TQuery extends DataQuery> {
@@ -41,6 +41,8 @@ export interface Props {
onUpdateDatasources?: (datasource: DataSourceRef) => void;
onQueryReplacedFromLibrary?: () => void;
queryRowWrapper?: (children: ReactNode, refId: string) => ReactNode;
queryLibraryRef?: string;
onCancelQueryLibraryEdit?: () => void;
isOpen?: boolean;
}
@@ -178,6 +180,8 @@ export class QueryEditorRows extends PureComponent<Props> {
onQueryOpenChanged,
onQueryReplacedFromLibrary,
queryRowWrapper,
queryLibraryRef,
onCancelQueryLibraryEdit,
isOpen,
} = this.props;
@@ -217,6 +221,8 @@ export class QueryEditorRows extends PureComponent<Props> {
range={getTimeSrv().timeRange()}
history={history}
eventBus={eventBus}
queryLibraryRef={queryLibraryRef}
onCancelQueryLibraryEdit={onCancelQueryLibraryEdit}
isOpen={isOpen}
/>
);
+5
View File
@@ -140,6 +140,11 @@ export interface ExploreItemState {
* Used to distinguish URL state injection versus split view state injection.
*/
initialized: boolean;
/**
* Query library reference identifier when editing a query from the query library
*
*/
queryLibraryRef?: string;
/**
* Log query result to be displayed in the logs result viewer.
*/
+6 -1
View File
@@ -11850,6 +11850,7 @@
},
"query-operation": {
"header": {
"cancel-query-library-edit": "Discard changes",
"collapse-row": "Collapse query row",
"datasource-help": "Show data source help",
"drag-and-drop": "Drag and drop to reorder",
@@ -11860,7 +11861,11 @@
"replace-query-from-library": "Replace with query from library",
"show-response": "Show response"
},
"query-editor-not-exported": "Data source plugin does not export any Query Editor component"
"query-editor-not-exported": "Data source plugin does not export any Query Editor component",
"query-library": {
"editing-tooltip": "Updating query from library\nUID: {{queryLibraryRef}}",
"from-library": "Updating query from library"
}
},
"recently-deleted": {
"buttons": {