mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Query library: Refactor to use onSelectQuery
callback (#100360)
* starting to refactor query library to use callback * replace QueryActionButton with onSelectQuery * hook up properly in explore * fix unit tests * i18n * extract types * fix refId in explore * fix unit tests * handle changing datasource to mixed * enrich queries with datasource * move out into separate function * filter out expression datasources
This commit is contained in:
parent
9cff383830
commit
b9034f413e
@ -13,7 +13,7 @@ import {
|
||||
} from '@grafana/scenes';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { Button, Stack, Tab } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { addQuery } from 'app/core/utils/query';
|
||||
import { getLastUsedDatasourceFromStorage } from 'app/features/dashboard/utils/dashboard';
|
||||
import { storeLastUsedDataSourceInLocalStorage } from 'app/features/datasources/components/picker/utils';
|
||||
@ -25,8 +25,10 @@ import { updateQueries } from 'app/features/query/state/updateQueries';
|
||||
import { isSharedDashboardQuery } from 'app/plugins/datasource/dashboard/runSharedRequest';
|
||||
import { QueryGroupOptions } from 'app/types';
|
||||
|
||||
import { MIXED_DATASOURCE_NAME } from '../../../../plugins/datasource/mixed/MixedDataSource';
|
||||
import { useQueryLibraryContext } from '../../../explore/QueryLibrary/QueryLibraryContext';
|
||||
import { QueryActionButtonProps } from '../../../explore/QueryLibrary/types';
|
||||
import { ExpressionDatasourceUID } from '../../../expressions/types';
|
||||
import { getDatasourceSrv } from '../../../plugins/datasource_srv';
|
||||
import { PanelTimeRange } from '../../scene/PanelTimeRange';
|
||||
import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '../../utils/utils';
|
||||
import { getUpdatedHoverHeader } from '../getPanelFrameOptions';
|
||||
@ -315,13 +317,35 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<Panel
|
||||
return null;
|
||||
}
|
||||
const showAddButton = !isSharedDashboardQuery(dsSettings.name);
|
||||
|
||||
// Make the final query library action button by injecting actual addQuery functionality into the button.
|
||||
const addQueryActionButton = makeQueryActionButton((queries) => {
|
||||
for (const query of queries) {
|
||||
model.onQueriesChange(addQuery(model.getQueries(), query));
|
||||
const onSelectQueryFromLibrary = async (query: DataQuery) => {
|
||||
// ensure all queries explicitly define a datasource
|
||||
const enrichedQueries = queries.map((q) =>
|
||||
q.datasource
|
||||
? q
|
||||
: {
|
||||
...q,
|
||||
datasource: datasource.getRef(),
|
||||
}
|
||||
});
|
||||
);
|
||||
const newQueries = addQuery(enrichedQueries, query);
|
||||
model.onQueriesChange(newQueries);
|
||||
if (query.datasource?.uid) {
|
||||
const uniqueDatasources = new Set(
|
||||
newQueries.map((q) => q.datasource?.uid).filter((uid) => uid !== ExpressionDatasourceUID)
|
||||
);
|
||||
const isMixed = uniqueDatasources.size > 1;
|
||||
const newDatasourceRef = {
|
||||
uid: isMixed ? MIXED_DATASOURCE_NAME : query.datasource.uid,
|
||||
};
|
||||
const shouldChangeDatasource = datasource.uid !== newDatasourceRef.uid;
|
||||
if (shouldChangeDatasource) {
|
||||
const newDatasource = getDatasourceSrv().getInstanceSettings(newDatasourceRef);
|
||||
if (newDatasource) {
|
||||
await model.onChangeDataSource(newDatasource);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div data-testid={selectors.components.QueryTab.content}>
|
||||
@ -358,9 +382,9 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<Panel
|
||||
{queryLibraryEnabled && (
|
||||
<Button
|
||||
icon="plus"
|
||||
onClick={() => {
|
||||
openQueryLibraryDrawer(getDatasourceNames(datasource, queries), addQueryActionButton);
|
||||
}}
|
||||
onClick={() =>
|
||||
openQueryLibraryDrawer(getDatasourceNames(datasource, queries), onSelectQueryFromLibrary)
|
||||
}
|
||||
variant="secondary"
|
||||
data-testid={selectors.components.QueryTab.addQuery}
|
||||
>
|
||||
@ -385,28 +409,6 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<Panel
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a button component that will be used in query library as action next to each query.
|
||||
* @param addQueries
|
||||
*/
|
||||
function makeQueryActionButton(addQueries: (queries: DataQuery[]) => void) {
|
||||
return function AddQueryFromLibraryButton(props: QueryActionButtonProps) {
|
||||
const label = t('dashboards.query-library.add-query-button', 'Add query');
|
||||
return (
|
||||
<Button
|
||||
variant={'primary'}
|
||||
aria-label={label}
|
||||
onClick={() => {
|
||||
addQueries(props.queries);
|
||||
props.onClick();
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function getDatasourceNames(datasource: DataSourceApi, queries: DataQuery[]): string[] {
|
||||
if (datasource.uid === '-- Mixed --') {
|
||||
// If datasource is mixed, the datasource UID is on the query. Here we map the UIDs to datasource names.
|
||||
|
@ -103,6 +103,11 @@ const dummyProps: Props = {
|
||||
setSupplementaryQueryEnabled: jest.fn(),
|
||||
correlationEditorDetails: undefined,
|
||||
correlationEditorHelperData: undefined,
|
||||
exploreActiveDS: {
|
||||
exploreToDS: [],
|
||||
dsToExplore: [],
|
||||
},
|
||||
changeDatasource: jest.fn(),
|
||||
};
|
||||
|
||||
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
AbsoluteTimeRange,
|
||||
DataFrame,
|
||||
EventBus,
|
||||
getNextRefId,
|
||||
GrafanaTheme2,
|
||||
hasToggleableQueryFiltersSupport,
|
||||
LoadingState,
|
||||
@ -54,6 +55,7 @@ import { ResponseErrorContainer } from './ResponseErrorContainer';
|
||||
import { SecondaryActions } from './SecondaryActions';
|
||||
import TableContainer from './Table/TableContainer';
|
||||
import { TraceViewContainer } from './TraceView/TraceViewContainer';
|
||||
import { changeDatasource } from './state/datasource';
|
||||
import { changeSize } from './state/explorePane';
|
||||
import { splitOpen } from './state/main';
|
||||
import {
|
||||
@ -65,7 +67,7 @@ import {
|
||||
setQueries,
|
||||
setSupplementaryQueryEnabled,
|
||||
} from './state/query';
|
||||
import { isSplit } from './state/selectors';
|
||||
import { isSplit, selectExploreDSMaps } from './state/selectors';
|
||||
import { updateTimeRange } from './state/time';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
@ -605,6 +607,28 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
||||
queryInspectorButtonActive={showQueryInspector}
|
||||
onClickAddQueryRowButton={this.onClickAddQueryRowButton}
|
||||
onClickQueryInspectorButton={() => setShowQueryInspector(!showQueryInspector)}
|
||||
onSelectQueryFromLibrary={async (query) => {
|
||||
const { changeDatasource, queries, setQueries } = this.props;
|
||||
const newQueries = [
|
||||
...queries,
|
||||
{
|
||||
...query,
|
||||
refId: getNextRefId(queries),
|
||||
},
|
||||
];
|
||||
setQueries(exploreId, newQueries);
|
||||
if (query.datasource?.uid) {
|
||||
const uniqueDatasources = new Set(newQueries.map((q) => q.datasource?.uid));
|
||||
const isMixed = uniqueDatasources.size > 1;
|
||||
const newDatasourceRef = {
|
||||
uid: isMixed ? MIXED_DATASOURCE_NAME : query.datasource.uid,
|
||||
};
|
||||
const shouldChangeDatasource = datasourceInstance.uid !== newDatasourceRef.uid;
|
||||
if (shouldChangeDatasource) {
|
||||
await changeDatasource({ exploreId, datasource: newDatasourceRef });
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ResponseErrorContainer exploreId={exploreId} />
|
||||
</PanelContainer>
|
||||
@ -716,10 +740,12 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
showLogsSample,
|
||||
correlationEditorHelperData,
|
||||
correlationEditorDetails: explore.correlationEditorDetails,
|
||||
exploreActiveDS: selectExploreDSMaps(state),
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
changeDatasource,
|
||||
changeSize,
|
||||
modifyQueries,
|
||||
scanStart,
|
||||
|
@ -28,8 +28,6 @@ import { getFiscalYearStartMonth, getTimeZone } from '../profile/state/selectors
|
||||
import { ExploreTimeControls } from './ExploreTimeControls';
|
||||
import { LiveTailButton } from './LiveTailButton';
|
||||
import { useQueriesDrawerContext } from './QueriesDrawer/QueriesDrawerContext';
|
||||
import { QueriesDrawerDropdown } from './QueriesDrawer/QueriesDrawerDropdown';
|
||||
import { useQueryLibraryContext } from './QueryLibrary/QueryLibraryContext';
|
||||
import { ShortLinkButtonMenu } from './ShortLinkButtonMenu';
|
||||
import { ToolbarExtensionPoint } from './extensions/ToolbarExtensionPoint';
|
||||
import { changeDatasource } from './state/datasource';
|
||||
@ -93,7 +91,6 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
||||
const isCorrelationsEditorMode = correlationDetails?.editorMode || false;
|
||||
const isLeftPane = useSelector(isLeftPaneSelector(exploreId));
|
||||
const { drawerOpened, setDrawerOpened } = useQueriesDrawerContext();
|
||||
const { queryLibraryEnabled } = useQueryLibraryContext();
|
||||
|
||||
const shouldRotateSplitIcon = useMemo(
|
||||
() => (isLeftPane && isLargerPane) || (!isLeftPane && !isLargerPane),
|
||||
@ -206,12 +203,7 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
||||
dispatch(changeRefreshInterval({ exploreId, refreshInterval }));
|
||||
};
|
||||
|
||||
const navBarActions = [<ShortLinkButtonMenu key="share" />];
|
||||
|
||||
if (queryLibraryEnabled) {
|
||||
navBarActions.unshift(<QueriesDrawerDropdown key="queryLibrary" variant="full" />);
|
||||
} else {
|
||||
navBarActions.unshift(
|
||||
const navBarActions = [
|
||||
<ToolbarButton
|
||||
variant={drawerOpened ? 'active' : 'canvas'}
|
||||
aria-label={t('explore.secondary-actions.query-history-button-aria-label', 'Query history')}
|
||||
@ -220,9 +212,9 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
||||
icon="history"
|
||||
>
|
||||
<Trans i18nKey="explore.secondary-actions.query-history-button">Query history</Trans>
|
||||
</ToolbarButton>
|
||||
);
|
||||
}
|
||||
</ToolbarButton>,
|
||||
<ShortLinkButtonMenu key="share" />,
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -1,120 +0,0 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { ComponentProps, useState } from 'react';
|
||||
|
||||
import { Button, ButtonGroup, Dropdown, Menu, ToolbarButton } from '@grafana/ui';
|
||||
import { useStyles2 } from '@grafana/ui/';
|
||||
import { t } from 'app/core/internationalization';
|
||||
|
||||
import { createDatasourcesList } from '../../../core/utils/richHistory';
|
||||
import { useSelector } from '../../../types';
|
||||
import ExploreRunQueryButton from '../ExploreRunQueryButton';
|
||||
import { useQueryLibraryContext } from '../QueryLibrary/QueryLibraryContext';
|
||||
import { QueryActionButton } from '../QueryLibrary/types';
|
||||
import { selectExploreDSMaps } from '../state/selectors';
|
||||
|
||||
import { useQueriesDrawerContext } from './QueriesDrawerContext';
|
||||
import { i18n } from './utils';
|
||||
|
||||
// This makes TS happy as ExploreRunQueryButton has optional onClick prop while QueryActionButton doesn't
|
||||
// in addition to map the rootDatasourceUid prop.
|
||||
function ExploreRunQueryButtonWrapper(props: ComponentProps<QueryActionButton>) {
|
||||
return <ExploreRunQueryButton {...props} rootDatasourceUid={props.datasourceUid} />;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
variant: 'compact' | 'full';
|
||||
};
|
||||
|
||||
/**
|
||||
* Dropdown button that can either open a Query History drawer or a Query Library drawer.
|
||||
* @param variant
|
||||
* @constructor
|
||||
*/
|
||||
export function QueriesDrawerDropdown({ variant }: Props) {
|
||||
const { drawerOpened, setDrawerOpened } = useQueriesDrawerContext();
|
||||
|
||||
const {
|
||||
openDrawer: openQueryLibraryDrawer,
|
||||
closeDrawer: closeQueryLibraryDrawer,
|
||||
isDrawerOpen: isQueryLibraryDrawerOpen,
|
||||
queryLibraryEnabled,
|
||||
} = useQueryLibraryContext();
|
||||
|
||||
const [queryOption, setQueryOption] = useState<'library' | 'history'>('library');
|
||||
|
||||
const exploreActiveDS = useSelector(selectExploreDSMaps);
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
// In case query library is not enabled we show only simple button for query history in the parent.
|
||||
if (!queryLibraryEnabled) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function toggleRichHistory() {
|
||||
setQueryOption('history');
|
||||
setDrawerOpened(!drawerOpened);
|
||||
}
|
||||
|
||||
function toggleQueryLibrary() {
|
||||
setQueryOption('library');
|
||||
if (isQueryLibraryDrawerOpen) {
|
||||
closeQueryLibraryDrawer();
|
||||
} else {
|
||||
// Prefill the query library filter with the dataSource.
|
||||
// Get current dataSource that is open. As this is only used in Explore we get it from Explore state.
|
||||
const listOfDatasources = createDatasourcesList();
|
||||
const activeDatasources = exploreActiveDS.dsToExplore
|
||||
.map((eDs) => {
|
||||
return listOfDatasources.find((ds) => ds.uid === eDs.datasource?.uid)?.name;
|
||||
})
|
||||
.filter((name): name is string => !!name);
|
||||
|
||||
openQueryLibraryDrawer(activeDatasources, ExploreRunQueryButtonWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item label={i18n.queryLibrary} onClick={() => toggleQueryLibrary()} />
|
||||
<Menu.Item label={i18n.queryHistory} onClick={() => toggleRichHistory()} />
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const buttonLabel = queryOption === 'library' ? i18n.queryLibrary : i18n.queryHistory;
|
||||
const toggle = queryOption === 'library' ? toggleQueryLibrary : toggleRichHistory;
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<ToolbarButton
|
||||
icon="book"
|
||||
variant={drawerOpened || isQueryLibraryDrawerOpen ? 'active' : 'canvas'}
|
||||
onClick={() => toggle()}
|
||||
aria-label={buttonLabel}
|
||||
>
|
||||
{variant === 'full' ? buttonLabel : undefined}
|
||||
</ToolbarButton>
|
||||
|
||||
{/* Show either a drops down button so that user can select QL or QH, or show a close button if one of them is
|
||||
already open.*/}
|
||||
{drawerOpened || isQueryLibraryDrawerOpen ? (
|
||||
<Button className={styles.close} variant="secondary" icon="times" onClick={() => toggle()}></Button>
|
||||
) : (
|
||||
<Dropdown overlay={menu}>
|
||||
<ToolbarButton
|
||||
className={styles.toggle}
|
||||
variant="canvas"
|
||||
icon="angle-down"
|
||||
aria-label={t('explore.rich-history.library-history-dropdown', 'Open query library or query history')}
|
||||
/>
|
||||
</Dropdown>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = () => ({
|
||||
toggle: css({ width: '36px' }),
|
||||
// tweaking icon position so it's nicely aligned when dropdown turns into a close button
|
||||
close: css({ width: '36px', '> svg': { position: 'relative', left: 2 } }),
|
||||
});
|
@ -2,7 +2,7 @@ import { createContext, ReactNode, useContext } from 'react';
|
||||
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
|
||||
import { QueryActionButton } from './types';
|
||||
import { OnSelectQueryType } from './types';
|
||||
|
||||
/**
|
||||
* Context with state and action to interact with Query Library. The Query Library feature consists of a drawer
|
||||
@ -20,11 +20,7 @@ export type QueryLibraryContextType = {
|
||||
* @param options.context Used for tracking. Should identify the context this is called from, like 'explore' or
|
||||
* 'dashboard'.
|
||||
*/
|
||||
openDrawer: (
|
||||
datasourceFilters: string[],
|
||||
queryActionButton: QueryActionButton,
|
||||
options?: { context?: string }
|
||||
) => void;
|
||||
openDrawer: (datasourceFilters: string[], onSelectQuery: OnSelectQueryType, options?: { context?: string }) => void;
|
||||
closeDrawer: () => void;
|
||||
isDrawerOpen: boolean;
|
||||
|
||||
|
25
public/app/features/explore/QueryLibrary/mocks.tsx
Normal file
25
public/app/features/explore/QueryLibrary/mocks.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { QueryLibraryContext } from './QueryLibraryContext';
|
||||
|
||||
type Props = {
|
||||
queryLibraryAvailable?: boolean;
|
||||
};
|
||||
|
||||
export function QueryLibraryContextProviderMock(props: PropsWithChildren<Props>) {
|
||||
return (
|
||||
<QueryLibraryContext.Provider
|
||||
value={{
|
||||
openDrawer: jest.fn(),
|
||||
closeDrawer: jest.fn(),
|
||||
isDrawerOpen: false,
|
||||
openAddQueryModal: jest.fn(),
|
||||
closeAddQueryModal: jest.fn(),
|
||||
renderSaveQueryButton: jest.fn(),
|
||||
queryLibraryEnabled: false,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</QueryLibraryContext.Provider>
|
||||
);
|
||||
}
|
@ -1,11 +1,3 @@
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
|
||||
export type QueryActionButtonProps = {
|
||||
queries: DataQuery[];
|
||||
datasourceUid?: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
export type QueryActionButton = ComponentType<QueryActionButtonProps>;
|
||||
export type OnSelectQueryType = (query: DataQuery) => void;
|
||||
|
@ -1,13 +1,34 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import { render } from '../../../test/test-utils';
|
||||
|
||||
import { QueriesDrawerContextProviderMock } from './QueriesDrawer/mocks';
|
||||
import { QueryLibraryContextProviderMock } from './QueryLibrary/mocks';
|
||||
import { SecondaryActions } from './SecondaryActions';
|
||||
|
||||
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
|
||||
return {
|
||||
getDataSourceSrv: () => ({
|
||||
get: () => Promise.resolve({}),
|
||||
getList: () => [],
|
||||
getInstanceSettings: () => {},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('SecondaryActions', () => {
|
||||
it('should render component with two buttons', () => {
|
||||
render(<SecondaryActions onClickAddQueryRowButton={noop} onClickQueryInspectorButton={noop} />);
|
||||
render(
|
||||
<QueryLibraryContextProviderMock>
|
||||
<SecondaryActions
|
||||
onClickAddQueryRowButton={noop}
|
||||
onClickQueryInspectorButton={noop}
|
||||
onSelectQueryFromLibrary={noop}
|
||||
/>
|
||||
</QueryLibraryContextProviderMock>
|
||||
);
|
||||
|
||||
expect(screen.getByRole('button', { name: /Add query/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Query inspector/i })).toBeInTheDocument();
|
||||
@ -21,6 +42,7 @@ describe('SecondaryActions', () => {
|
||||
richHistoryRowButtonHidden={true}
|
||||
onClickAddQueryRowButton={noop}
|
||||
onClickQueryInspectorButton={noop}
|
||||
onSelectQueryFromLibrary={noop}
|
||||
/>
|
||||
</QueriesDrawerContextProviderMock>
|
||||
);
|
||||
@ -35,6 +57,7 @@ describe('SecondaryActions', () => {
|
||||
addQueryRowButtonDisabled={true}
|
||||
onClickAddQueryRowButton={noop}
|
||||
onClickQueryInspectorButton={noop}
|
||||
onSelectQueryFromLibrary={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -54,6 +77,7 @@ describe('SecondaryActions', () => {
|
||||
<SecondaryActions
|
||||
onClickAddQueryRowButton={onClickAddRow}
|
||||
onClickQueryInspectorButton={onClickQueryInspector}
|
||||
onSelectQueryFromLibrary={noop}
|
||||
/>
|
||||
</QueriesDrawerContextProviderMock>
|
||||
);
|
||||
|
@ -3,6 +3,14 @@ import { css } from '@emotion/css';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { ToolbarButton, useTheme2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { useSelector } from 'app/types';
|
||||
|
||||
import { createDatasourcesList } from '../../core/utils/richHistory';
|
||||
import { MIXED_DATASOURCE_NAME } from '../../plugins/datasource/mixed/MixedDataSource';
|
||||
|
||||
import { useQueryLibraryContext } from './QueryLibrary/QueryLibraryContext';
|
||||
import { type OnSelectQueryType } from './QueryLibrary/types';
|
||||
import { selectExploreDSMaps } from './state/selectors';
|
||||
|
||||
type Props = {
|
||||
addQueryRowButtonDisabled?: boolean;
|
||||
@ -12,6 +20,7 @@ type Props = {
|
||||
|
||||
onClickAddQueryRowButton: () => void;
|
||||
onClickQueryInspectorButton: () => void;
|
||||
onSelectQueryFromLibrary: OnSelectQueryType;
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
@ -25,27 +34,57 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
};
|
||||
};
|
||||
|
||||
export function SecondaryActions(props: Props) {
|
||||
export function SecondaryActions({
|
||||
addQueryRowButtonDisabled,
|
||||
addQueryRowButtonHidden,
|
||||
onClickAddQueryRowButton,
|
||||
onClickQueryInspectorButton,
|
||||
onSelectQueryFromLibrary,
|
||||
queryInspectorButtonActive,
|
||||
}: Props) {
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme);
|
||||
const exploreActiveDS = useSelector(selectExploreDSMaps);
|
||||
// Prefill the query library filter with the dataSource.
|
||||
// Get current dataSource that is open. As this is only used in Explore we get it from Explore state.
|
||||
const listOfDatasources = createDatasourcesList();
|
||||
const activeDatasources = exploreActiveDS.dsToExplore
|
||||
.map((eDs) => {
|
||||
return listOfDatasources.find((ds) => ds.uid === eDs.datasource?.uid)?.name;
|
||||
})
|
||||
.filter((name): name is string => !!name && name !== MIXED_DATASOURCE_NAME);
|
||||
|
||||
const { queryLibraryEnabled, openDrawer: openQueryLibraryDrawer } = useQueryLibraryContext();
|
||||
|
||||
return (
|
||||
<div className={styles.containerMargin}>
|
||||
{!props.addQueryRowButtonHidden && (
|
||||
{!addQueryRowButtonHidden && (
|
||||
<>
|
||||
<ToolbarButton
|
||||
variant="canvas"
|
||||
aria-label={t('explore.secondary-actions.query-add-button-aria-label', 'Add query')}
|
||||
onClick={props.onClickAddQueryRowButton}
|
||||
disabled={props.addQueryRowButtonDisabled}
|
||||
onClick={onClickAddQueryRowButton}
|
||||
disabled={addQueryRowButtonDisabled}
|
||||
icon="plus"
|
||||
>
|
||||
<Trans i18nKey="explore.secondary-actions.query-add-button">Add query</Trans>
|
||||
</ToolbarButton>
|
||||
{queryLibraryEnabled && (
|
||||
<ToolbarButton
|
||||
aria-label={t('explore.secondary-actions.add-from-query-library', 'Add query from library')}
|
||||
variant="canvas"
|
||||
onClick={() => openQueryLibraryDrawer(activeDatasources, onSelectQueryFromLibrary)}
|
||||
icon="plus"
|
||||
>
|
||||
<Trans i18nKey="explore.secondary-actions.add-from-query-library">Add query from library</Trans>
|
||||
</ToolbarButton>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<ToolbarButton
|
||||
variant={props.queryInspectorButtonActive ? 'active' : 'canvas'}
|
||||
variant={queryInspectorButtonActive ? 'active' : 'canvas'}
|
||||
aria-label={t('explore.secondary-actions.query-inspector-button-aria-label', 'Query inspector')}
|
||||
onClick={props.onClickQueryInspectorButton}
|
||||
onClick={onClickQueryInspectorButton}
|
||||
icon="info-circle"
|
||||
>
|
||||
<Trans i18nKey="explore.secondary-actions.query-inspector-button">Query inspector</Trans>
|
||||
|
@ -40,7 +40,7 @@ export const openQueryHistory = async () => {
|
||||
};
|
||||
|
||||
export const openQueryLibrary = async () => {
|
||||
const button = screen.getByRole('button', { name: 'Query library' });
|
||||
const button = screen.getByRole('button', { name: 'Add query from library' });
|
||||
await userEvent.click(button);
|
||||
await waitFor(async () => {
|
||||
screen.getByRole('tab', {
|
||||
|
@ -1287,9 +1287,6 @@
|
||||
"panel-queries": {
|
||||
"add-query-from-library": "Add query from library"
|
||||
},
|
||||
"query-library": {
|
||||
"add-query-button": "Add query"
|
||||
},
|
||||
"settings": {
|
||||
"variables": {
|
||||
"dependencies": {
|
||||
@ -1371,7 +1368,6 @@
|
||||
"close-tooltip": "Close query history",
|
||||
"datasource-a-z": "Data source A-Z",
|
||||
"datasource-z-a": "Data source Z-A",
|
||||
"library-history-dropdown": "Open query library or query history",
|
||||
"newest-first": "Newest first",
|
||||
"oldest-first": "Oldest first",
|
||||
"query-history": "Query history",
|
||||
@ -1475,6 +1471,7 @@
|
||||
"switch-datasource-button": "Switch data source and run query"
|
||||
},
|
||||
"secondary-actions": {
|
||||
"add-from-query-library": "Add query from library",
|
||||
"query-add-button": "Add query",
|
||||
"query-add-button-aria-label": "Add query",
|
||||
"query-history-button": "Query history",
|
||||
|
@ -1287,9 +1287,6 @@
|
||||
"panel-queries": {
|
||||
"add-query-from-library": "Åđđ qūęřy ƒřőm ľįþřäřy"
|
||||
},
|
||||
"query-library": {
|
||||
"add-query-button": "Åđđ qūęřy"
|
||||
},
|
||||
"settings": {
|
||||
"variables": {
|
||||
"dependencies": {
|
||||
@ -1371,7 +1368,6 @@
|
||||
"close-tooltip": "Cľőşę qūęřy ĥįşŧőřy",
|
||||
"datasource-a-z": "Đäŧä şőūřčę Å-Ż",
|
||||
"datasource-z-a": "Đäŧä şőūřčę Ż-Å",
|
||||
"library-history-dropdown": "Øpęʼn qūęřy ľįþřäřy őř qūęřy ĥįşŧőřy",
|
||||
"newest-first": "Ńęŵęşŧ ƒįřşŧ",
|
||||
"oldest-first": "Øľđęşŧ ƒįřşŧ",
|
||||
"query-history": "Qūęřy ĥįşŧőřy",
|
||||
@ -1475,6 +1471,7 @@
|
||||
"switch-datasource-button": "Ŝŵįŧčĥ đäŧä şőūřčę äʼnđ řūʼn qūęřy"
|
||||
},
|
||||
"secondary-actions": {
|
||||
"add-from-query-library": "Åđđ qūęřy ƒřőm ľįþřäřy",
|
||||
"query-add-button": "Åđđ qūęřy",
|
||||
"query-add-button-aria-label": "Åđđ qūęřy",
|
||||
"query-history-button": "Qūęřy ĥįşŧőřy",
|
||||
|
Loading…
Reference in New Issue
Block a user