mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DS Picker: Filter available DS based on component props (#70613)
* Apply filters consistently to every list in the picker * Display all built-in DS when editing a panel * Add `uploadFile` prop to toggle the CSV file DS
This commit is contained in:
parent
d87c2c4049
commit
6ad9e386ad
@ -2,8 +2,8 @@
|
||||
// however there are many cases where your component may not need an aria-label
|
||||
// (a <button> with clear text, for example, does not need an aria-label as it's already labeled)
|
||||
// but you still might need to select it for testing,
|
||||
// in that case please add the attribute data-test-id={selector} in the component and
|
||||
// prefix your selector string with 'data-test-id' so that when create the selectors we know to search for it on the right attribute
|
||||
// in that case please add the attribute data-testid={selector} in the component and
|
||||
// prefix your selector string with 'data-testid' so that when create the selectors we know to search for it on the right attribute
|
||||
/**
|
||||
* Selectors grouped/defined in Components
|
||||
*
|
||||
@ -78,7 +78,7 @@ export const Components = {
|
||||
headerCornerInfo: (mode: string) => `Panel header ${mode}`,
|
||||
loadingBar: () => `Panel loading bar`,
|
||||
HoverWidget: {
|
||||
container: 'data-test-id hover-header-container',
|
||||
container: 'data-testid hover-header-container',
|
||||
dragIcon: 'data-testid drag-icon',
|
||||
},
|
||||
},
|
||||
|
@ -259,7 +259,7 @@ export function FileDropzoneDefaultChildren({ primaryText = 'Drop file here or c
|
||||
const styles = getStyles(theme);
|
||||
|
||||
return (
|
||||
<div className={cx(styles.defaultDropZone)}>
|
||||
<div className={cx(styles.defaultDropZone)} data-testid="file-drop-zone-default-children">
|
||||
<Icon className={cx(styles.icon)} name="upload" size="xl" />
|
||||
<h6 className={cx(styles.primaryText)}>{primaryText}</h6>
|
||||
<small className={styles.small}>{secondaryText}</small>
|
||||
|
@ -20,9 +20,9 @@ describe('TopSearchBarSection', () => {
|
||||
matches: true,
|
||||
}));
|
||||
|
||||
const { container } = renderComponent();
|
||||
const component = renderComponent();
|
||||
|
||||
expect(container.querySelector('[data-test-id="wrapper"]')).toBeInTheDocument();
|
||||
expect(component.queryByTestId('wrapper')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /test item/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@ -33,9 +33,9 @@ describe('TopSearchBarSection', () => {
|
||||
matches: false,
|
||||
}));
|
||||
|
||||
const { container } = renderComponent();
|
||||
const component = renderComponent();
|
||||
|
||||
expect(container.querySelector('[data-test-id="wrapper"]')).not.toBeInTheDocument();
|
||||
expect(component.queryByTestId('wrapper')).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /test item/i })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ export function TopSearchBarSection({ children, align = 'left' }: TopSearchBarSe
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-test-id="wrapper" className={cx(styles.wrapper, { [styles[align]]: align === 'right' })}>
|
||||
<div data-testid="wrapper" className={cx(styles.wrapper, { [styles[align]]: align === 'right' })}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -18,16 +18,55 @@ interface BuiltInDataSourceListProps {
|
||||
className?: string;
|
||||
current: DataSourceRef | string | null | undefined;
|
||||
onChange: (ds: DataSourceInstanceSettings) => void;
|
||||
dashboard?: boolean;
|
||||
|
||||
// DS filters
|
||||
filter?: (ds: DataSourceInstanceSettings) => boolean;
|
||||
tracing?: boolean;
|
||||
mixed?: boolean;
|
||||
dashboard?: boolean;
|
||||
metrics?: boolean;
|
||||
type?: string | string[];
|
||||
annotations?: boolean;
|
||||
variables?: boolean;
|
||||
alerting?: boolean;
|
||||
pluginId?: string;
|
||||
logs?: boolean;
|
||||
}
|
||||
|
||||
export function BuiltInDataSourceList({ className, current, onChange, dashboard, mixed }: BuiltInDataSourceListProps) {
|
||||
const grafanaDataSources = useDatasources({ mixed, dashboard, filter: (ds) => !!ds.meta.builtIn });
|
||||
export function BuiltInDataSourceList({
|
||||
className,
|
||||
current,
|
||||
onChange,
|
||||
tracing,
|
||||
dashboard,
|
||||
mixed,
|
||||
metrics,
|
||||
type,
|
||||
annotations,
|
||||
variables,
|
||||
alerting,
|
||||
pluginId,
|
||||
logs,
|
||||
filter,
|
||||
}: BuiltInDataSourceListProps) {
|
||||
const grafanaDataSources = useDatasources({
|
||||
tracing,
|
||||
dashboard,
|
||||
mixed,
|
||||
metrics,
|
||||
type,
|
||||
annotations,
|
||||
variables,
|
||||
alerting,
|
||||
pluginId,
|
||||
logs,
|
||||
});
|
||||
|
||||
const filteredResults = grafanaDataSources.filter((ds) => (filter ? filter?.(ds) : true) && !!ds.meta.builtIn);
|
||||
|
||||
return (
|
||||
<div className={className} data-testid="built-in-data-sources-list">
|
||||
{grafanaDataSources.map((ds) => {
|
||||
{filteredResults.map((ds) => {
|
||||
return (
|
||||
<DataSourceCard
|
||||
key={ds.uid}
|
||||
|
@ -100,7 +100,7 @@ describe('DataSourceDropdown', () => {
|
||||
describe('configuration', () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
it('should call the dataSourceSrv.getDatasourceList with the correct filters', async () => {
|
||||
it('should fetch the DS applying the correct filters consistently across lists', async () => {
|
||||
const filters = {
|
||||
mixed: true,
|
||||
tracing: true,
|
||||
@ -119,12 +119,27 @@ describe('DataSourceDropdown', () => {
|
||||
current: mockDS1.name,
|
||||
...filters,
|
||||
};
|
||||
const dropdown = render(<DataSourceDropdown {...props}></DataSourceDropdown>);
|
||||
|
||||
const searchBox = dropdown.container.querySelector('input');
|
||||
render(
|
||||
<ModalsProvider>
|
||||
<DataSourceDropdown {...props}></DataSourceDropdown>
|
||||
<ModalRoot />
|
||||
</ModalsProvider>
|
||||
);
|
||||
|
||||
const searchBox = await screen.findByRole('textbox');
|
||||
expect(searchBox).toBeInTheDocument();
|
||||
|
||||
getListMock.mockClear();
|
||||
await user.click(searchBox!);
|
||||
expect(getListMock.mock.lastCall[0]).toEqual(filters);
|
||||
await user.click(await screen.findByText('Open advanced data source picker'));
|
||||
expect(await screen.findByText('Select data source')); //Data source modal is open
|
||||
// Every call to the service must contain same filters
|
||||
getListMock.mock.calls.forEach((call) =>
|
||||
expect(call[0]).toMatchObject({
|
||||
...filters,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should dispaly the current selected DS in the selector', async () => {
|
||||
@ -180,7 +195,7 @@ describe('DataSourceDropdown', () => {
|
||||
// Doesn't try to get the default DS
|
||||
expect(getListMock).not.toBeCalled();
|
||||
expect(getInstanceSettingsMock).not.toBeCalled();
|
||||
expect(screen.getByTestId('Select a data source')).toHaveAttribute('placeholder', 'Select a data source');
|
||||
expect(screen.getByTestId('Select a data source')).toHaveAttribute('placeholder', 'Select data source');
|
||||
});
|
||||
});
|
||||
|
||||
@ -251,7 +266,7 @@ describe('DataSourceDropdown', () => {
|
||||
it('should call onChange with the default query when add csv is clicked', async () => {
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
const onChange = jest.fn();
|
||||
await setupOpenDropdown(user, { onChange });
|
||||
await setupOpenDropdown(user, { onChange, uploadFile: true });
|
||||
|
||||
await user.click(await screen.findByText('Add csv or spreadsheet'));
|
||||
|
||||
|
@ -8,7 +8,7 @@ import { Observable } from 'rxjs';
|
||||
import { DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { DataQuery, DataSourceJsonData, DataSourceRef } from '@grafana/schema';
|
||||
import { DataQuery, DataSourceRef } from '@grafana/schema';
|
||||
import { Button, CustomScrollbar, Icon, Input, ModalsController, Portal, useStyles2 } from '@grafana/ui';
|
||||
import config from 'app/core/config';
|
||||
import { useKeyNavigationListener } from 'app/features/search/hooks/useSearchKeyboardSelection';
|
||||
@ -32,14 +32,15 @@ const INTERACTION_ITEM = {
|
||||
};
|
||||
|
||||
export interface DataSourceDropdownProps {
|
||||
onChange: (ds: DataSourceInstanceSettings<DataSourceJsonData>, defaultQueries?: DataQuery[] | GrafanaQuery[]) => void;
|
||||
current?: DataSourceInstanceSettings<DataSourceJsonData> | string | DataSourceRef | null | undefined;
|
||||
onChange: (ds: DataSourceInstanceSettings, defaultQueries?: DataQuery[] | GrafanaQuery[]) => void;
|
||||
current?: DataSourceInstanceSettings | string | DataSourceRef | null;
|
||||
recentlyUsed?: string[];
|
||||
hideTextValue?: boolean;
|
||||
width?: number;
|
||||
inputId?: string;
|
||||
noDefault?: boolean;
|
||||
disabled?: boolean;
|
||||
placeholder?: string;
|
||||
|
||||
// DS filters
|
||||
tracing?: boolean;
|
||||
@ -52,6 +53,8 @@ export interface DataSourceDropdownProps {
|
||||
alerting?: boolean;
|
||||
pluginId?: string;
|
||||
logs?: boolean;
|
||||
uploadFile?: boolean;
|
||||
filter?: (ds: DataSourceInstanceSettings) => boolean;
|
||||
}
|
||||
|
||||
export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
||||
@ -63,6 +66,7 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
||||
inputId,
|
||||
noDefault = false,
|
||||
disabled = false,
|
||||
placeholder = 'Select data source',
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
@ -161,7 +165,7 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
||||
data-testid={selectors.components.DataSourcePicker.inputV2}
|
||||
prefix={currentValue ? prefixIcon : undefined}
|
||||
suffix={<Icon name={isOpen ? 'search' : 'angle-down'} />}
|
||||
placeholder={hideTextValue ? '' : dataSourceLabel(currentValue)}
|
||||
placeholder={hideTextValue ? '' : dataSourceLabel(currentValue) || placeholder}
|
||||
onClick={openDropdown}
|
||||
onFocus={() => {
|
||||
setInputHasFocus(true);
|
||||
@ -196,10 +200,7 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
||||
<PickerContent
|
||||
keyboardEvents={keyboardEvents}
|
||||
filterTerm={filterTerm}
|
||||
onChange={(
|
||||
ds: DataSourceInstanceSettings<DataSourceJsonData>,
|
||||
defaultQueries?: DataQuery[] | GrafanaQuery[]
|
||||
) => {
|
||||
onChange={(ds: DataSourceInstanceSettings, defaultQueries?: DataQuery[] | GrafanaQuery[]) => {
|
||||
onClose();
|
||||
onChange(ds, defaultQueries);
|
||||
}}
|
||||
@ -248,9 +249,9 @@ export interface PickerContentProps extends DataSourceDropdownProps {
|
||||
}
|
||||
|
||||
const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((props, ref) => {
|
||||
const { filterTerm, onChange, onClose, onClickAddCSV, current } = props;
|
||||
const { filterTerm, onChange, onClose, onClickAddCSV, current, filter, uploadFile } = props;
|
||||
const changeCallback = useCallback(
|
||||
(ds: DataSourceInstanceSettings<DataSourceJsonData>) => {
|
||||
(ds: DataSourceInstanceSettings) => {
|
||||
onChange(ds);
|
||||
reportInteraction(INTERACTION_EVENT_NAME, { item: INTERACTION_ITEM.SELECT_DS, ds_type: ds.type });
|
||||
},
|
||||
@ -274,7 +275,7 @@ const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((prop
|
||||
className={styles.dataSourceList}
|
||||
current={current}
|
||||
onChange={changeCallback}
|
||||
filter={(ds) => matchDataSourceWithSearch(ds, filterTerm)}
|
||||
filter={(ds) => (filter ? filter?.(ds) : true) && matchDataSourceWithSearch(ds, filterTerm)}
|
||||
onClickEmptyStateCTA={() =>
|
||||
reportInteraction(INTERACTION_EVENT_NAME, {
|
||||
item: INTERACTION_ITEM.CONFIG_NEW_DS_EMPTY_STATE,
|
||||
@ -293,9 +294,19 @@ const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((prop
|
||||
onClose();
|
||||
showModal(DataSourceModal, {
|
||||
reportedInteractionFrom: 'ds_picker',
|
||||
tracing: props.tracing,
|
||||
dashboard: props.dashboard,
|
||||
mixed: props.mixed,
|
||||
current,
|
||||
metrics: props.metrics,
|
||||
type: props.type,
|
||||
annotations: props.annotations,
|
||||
variables: props.variables,
|
||||
alerting: props.alerting,
|
||||
pluginId: props.pluginId,
|
||||
logs: props.logs,
|
||||
filter: props.filter,
|
||||
uploadFile: props.uploadFile,
|
||||
current: props.current,
|
||||
onDismiss: hideModal,
|
||||
onChange: (ds, defaultQueries) => {
|
||||
onChange(ds, defaultQueries);
|
||||
@ -310,7 +321,7 @@ const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((prop
|
||||
</Button>
|
||||
)}
|
||||
</ModalsController>
|
||||
{onClickAddCSV && config.featureToggles.editPanelCSVDragAndDrop && (
|
||||
{uploadFile && config.featureToggles.editPanelCSVDragAndDrop && (
|
||||
<Button variant="secondary" size="sm" onClick={clickAddCSVCallback}>
|
||||
Add csv or spreadsheet
|
||||
</Button>
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { findByText, queryByText, render, screen } from '@testing-library/react';
|
||||
import { queryByTestId, render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import { DataSourceInstanceSettings, DataSourcePluginMeta, PluginMetaInfo, PluginType } from '@grafana/data';
|
||||
import { config, GetDataSourceListFilters } from '@grafana/runtime';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { DataSourceModal } from './DataSourceModal';
|
||||
import { DataSourceModal, DataSourceModalProps } from './DataSourceModal';
|
||||
|
||||
const pluginMetaInfo: PluginMetaInfo = {
|
||||
author: { name: '' },
|
||||
@ -40,10 +40,17 @@ const mockDSBuiltIn = createDS('mock.datasource.builtin', 3, true);
|
||||
|
||||
const mockDSList = [mockDS1, mockDS2, mockDSBuiltIn];
|
||||
|
||||
const setup = (onChange = () => {}, onDismiss = () => {}) => {
|
||||
const props = { onChange, onDismiss, current: mockDS1.name };
|
||||
const setup = (partialProps: Partial<DataSourceModalProps> = {}) => {
|
||||
window.HTMLElement.prototype.scrollIntoView = function () {};
|
||||
return render(<DataSourceModal {...props}></DataSourceModal>);
|
||||
|
||||
const props: DataSourceModalProps = {
|
||||
...partialProps,
|
||||
onChange: partialProps.onChange || jest.fn(),
|
||||
onDismiss: partialProps.onDismiss || jest.fn(),
|
||||
current: partialProps.current || mockDS1,
|
||||
};
|
||||
|
||||
return render(<DataSourceModal {...props} />);
|
||||
};
|
||||
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
@ -61,17 +68,19 @@ jest.mock('@grafana/runtime', () => {
|
||||
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
|
||||
return {
|
||||
getDataSourceSrv: () => ({
|
||||
getList: (filters: GetDataSourceListFilters) => {
|
||||
if (filters.filter) {
|
||||
return mockDSList.filter(filters.filter);
|
||||
}
|
||||
return mockDSList;
|
||||
},
|
||||
getInstanceSettings: () => mockDS1,
|
||||
getList: getListMock,
|
||||
getInstanceSettings: getInstanceSettingsMock,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const getListMock = jest.fn();
|
||||
const getInstanceSettingsMock = jest.fn();
|
||||
beforeEach(() => {
|
||||
getListMock.mockReturnValue(mockDSList);
|
||||
getInstanceSettingsMock.mockReturnValue(mockDS1);
|
||||
});
|
||||
|
||||
describe('DataSourceDropdown', () => {
|
||||
it('should render', () => {
|
||||
expect(() => setup()).not.toThrow();
|
||||
@ -90,24 +99,74 @@ describe('DataSourceDropdown', () => {
|
||||
});
|
||||
|
||||
it('only displays the file drop area when the the ff is enabled', async () => {
|
||||
const defaultValue = config.featureToggles.editPanelCSVDragAndDrop;
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
setup();
|
||||
expect(await screen.findByText('Drop file here or click to upload')).toBeInTheDocument();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = false;
|
||||
setup({ uploadFile: true });
|
||||
|
||||
expect(await screen.queryByTestId('file-drop-zone-default-children')).toBeInTheDocument();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = defaultValue;
|
||||
});
|
||||
|
||||
it('does not show the file drop area when the ff is disabled', async () => {
|
||||
setup();
|
||||
expect(screen.queryByText('Drop file here or click to upload')).toBeNull();
|
||||
const defaultValue = config.featureToggles.editPanelCSVDragAndDrop;
|
||||
config.featureToggles.editPanelCSVDragAndDrop = false;
|
||||
|
||||
setup({ uploadFile: true });
|
||||
expect(await screen.queryByTestId('file-drop-zone-default-children')).toBeNull();
|
||||
|
||||
config.featureToggles.editPanelCSVDragAndDrop = defaultValue;
|
||||
});
|
||||
|
||||
it('should only display built in datasources in the right column', async () => {
|
||||
setup();
|
||||
const dsList = await screen.findByTestId('data-sources-list');
|
||||
const builtInDSList = (await screen.findAllByTestId('built-in-data-sources-list'))[1]; //The second element needs to be selected as the first element is the one on the left, under the regular data sources.
|
||||
it('should not display the drop zone by default', async () => {
|
||||
const defaultValue = config.featureToggles.editPanelCSVDragAndDrop;
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
|
||||
expect(queryByText(dsList, mockDSBuiltIn.name)).toBeNull();
|
||||
expect(await findByText(builtInDSList, mockDSBuiltIn.name, { selector: 'span' })).toBeInTheDocument();
|
||||
const component = setup();
|
||||
|
||||
expect(queryByTestId(component.container, 'file-drop-zone-default-children')).toBeNull();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = defaultValue;
|
||||
});
|
||||
|
||||
it('should display the drop zone when uploadFile is enabled', async () => {
|
||||
const defaultValue = config.featureToggles.editPanelCSVDragAndDrop;
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
setup({ uploadFile: true });
|
||||
|
||||
expect(await screen.queryByTestId('file-drop-zone-default-children')).toBeInTheDocument();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = defaultValue;
|
||||
});
|
||||
|
||||
it('should fetch the DS applying the correct filters consistently across lists', async () => {
|
||||
const filters = {
|
||||
mixed: true,
|
||||
tracing: true,
|
||||
dashboard: true,
|
||||
metrics: true,
|
||||
type: 'foo',
|
||||
annotations: true,
|
||||
variables: true,
|
||||
alerting: true,
|
||||
pluginId: 'pluginid',
|
||||
logs: true,
|
||||
};
|
||||
|
||||
const props = {
|
||||
onChange: () => {},
|
||||
onDismiss: () => {},
|
||||
current: mockDS1.name,
|
||||
...filters,
|
||||
};
|
||||
|
||||
getListMock.mockClear();
|
||||
render(<DataSourceModal {...props}></DataSourceModal>);
|
||||
|
||||
// Every call to the service must contain same filters
|
||||
expect(getListMock).toHaveBeenCalled();
|
||||
getListMock.mock.calls.forEach((call) =>
|
||||
expect(call[0]).toMatchObject({
|
||||
...filters,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -134,9 +193,10 @@ describe('DataSourceDropdown', () => {
|
||||
const user = userEvent.setup();
|
||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||
const onChange = jest.fn();
|
||||
setup(onChange);
|
||||
setup({ onChange, uploadFile: true });
|
||||
|
||||
const fileInput = (
|
||||
await screen.findByText('Drop file here or click to upload')
|
||||
await screen.queryByTestId('file-drop-zone-default-children')!
|
||||
).parentElement!.parentElement!.querySelector('input');
|
||||
const file = new File([''], 'test.csv', { type: 'text/plain' });
|
||||
expect(fileInput).toBeInTheDocument();
|
||||
@ -154,7 +214,7 @@ describe('DataSourceDropdown', () => {
|
||||
it('should call the onChange handler with the correct datasource', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onChange = jest.fn();
|
||||
setup(onChange);
|
||||
setup({ onChange });
|
||||
await user.click(await screen.findByText(mockDS2.name, { selector: 'span' }));
|
||||
expect(onChange.mock.lastCall[0].name).toEqual(mockDS2.name);
|
||||
});
|
||||
|
@ -35,7 +35,7 @@ const INTERACTION_ITEM = {
|
||||
DISMISS: 'dismiss',
|
||||
};
|
||||
|
||||
interface DataSourceModalProps {
|
||||
export interface DataSourceModalProps {
|
||||
onChange: (ds: DataSourceInstanceSettings, defaultQueries?: DataQuery[] | GrafanaQuery[]) => void;
|
||||
current: DataSourceRef | string | null | undefined;
|
||||
onDismiss: () => void;
|
||||
@ -43,6 +43,7 @@ interface DataSourceModalProps {
|
||||
reportedInteractionFrom?: string;
|
||||
|
||||
// DS filters
|
||||
filter?: (ds: DataSourceInstanceSettings) => boolean;
|
||||
tracing?: boolean;
|
||||
mixed?: boolean;
|
||||
dashboard?: boolean;
|
||||
@ -53,11 +54,22 @@ interface DataSourceModalProps {
|
||||
alerting?: boolean;
|
||||
pluginId?: string;
|
||||
logs?: boolean;
|
||||
uploadFile?: boolean;
|
||||
}
|
||||
|
||||
export function DataSourceModal({
|
||||
tracing,
|
||||
dashboard,
|
||||
mixed,
|
||||
metrics,
|
||||
type,
|
||||
annotations,
|
||||
variables,
|
||||
alerting,
|
||||
pluginId,
|
||||
logs,
|
||||
uploadFile,
|
||||
filter,
|
||||
onChange,
|
||||
current,
|
||||
onDismiss,
|
||||
@ -106,6 +118,29 @@ export function DataSourceModal({
|
||||
}
|
||||
});
|
||||
|
||||
// Built-in data sources used twice because of mobile layout adjustments
|
||||
// In movile the list is appended to the bottom of the DS list
|
||||
const BuiltInList = ({ className }: { className?: string }) => {
|
||||
return (
|
||||
<BuiltInDataSourceList
|
||||
className={className}
|
||||
onChange={onChangeDataSource}
|
||||
current={current}
|
||||
filter={filter}
|
||||
variables={variables}
|
||||
tracing={tracing}
|
||||
metrics={metrics}
|
||||
type={type}
|
||||
annotations={annotations}
|
||||
alerting={alerting}
|
||||
pluginId={pluginId}
|
||||
logs={logs}
|
||||
dashboard={dashboard}
|
||||
mixed={mixed}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Select data source"
|
||||
@ -132,10 +167,6 @@ export function DataSourceModal({
|
||||
/>
|
||||
<CustomScrollbar>
|
||||
<DataSourceList
|
||||
dashboard={false}
|
||||
mixed={false}
|
||||
variables
|
||||
filter={(ds) => matchDataSourceWithSearch(ds, search) && !ds.meta.builtIn}
|
||||
onChange={onChangeDataSource}
|
||||
current={current}
|
||||
onClickEmptyStateCTA={() =>
|
||||
@ -144,27 +175,27 @@ export function DataSourceModal({
|
||||
src: analyticsInteractionSrc,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<BuiltInDataSourceList
|
||||
filter={(ds) => (filter ? filter?.(ds) : true) && matchDataSourceWithSearch(ds, search) && !ds.meta.builtIn}
|
||||
variables={variables}
|
||||
tracing={tracing}
|
||||
metrics={metrics}
|
||||
type={type}
|
||||
annotations={annotations}
|
||||
alerting={alerting}
|
||||
pluginId={pluginId}
|
||||
logs={logs}
|
||||
dashboard={dashboard}
|
||||
mixed={mixed}
|
||||
className={styles.appendBuiltInDataSourcesList}
|
||||
onChange={onChangeDataSource}
|
||||
current={current}
|
||||
/>
|
||||
<BuiltInList className={styles.appendBuiltInDataSourcesList} />
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
<div className={styles.rightColumn}>
|
||||
<div className={styles.builtInDataSources}>
|
||||
<CustomScrollbar className={styles.builtInDataSourcesList}>
|
||||
<BuiltInDataSourceList
|
||||
onChange={onChangeDataSource}
|
||||
current={current}
|
||||
dashboard={dashboard}
|
||||
mixed={mixed}
|
||||
/>
|
||||
<BuiltInList />
|
||||
</CustomScrollbar>
|
||||
{config.featureToggles.editPanelCSVDragAndDrop && (
|
||||
{uploadFile && config.featureToggles.editPanelCSVDragAndDrop && (
|
||||
<FileDropzone
|
||||
readAs="readAsArrayBuffer"
|
||||
fileListRenderer={() => undefined}
|
||||
|
@ -20,7 +20,7 @@ export function dataSourceLabel(
|
||||
dataSource: DataSourceInstanceSettings<DataSourceJsonData> | string | DataSourceRef | null | undefined
|
||||
) {
|
||||
if (!dataSource) {
|
||||
return 'Select a data source';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof dataSource === 'string') {
|
||||
@ -35,7 +35,7 @@ export function dataSourceLabel(
|
||||
return `${dataSource.uid} - not found`;
|
||||
}
|
||||
|
||||
return 'Select a data source';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getDataSourceCompareFn(
|
||||
|
@ -272,7 +272,12 @@ export class QueryGroup extends PureComponent<Props, State> {
|
||||
const { isDataSourceModalOpen } = this.state;
|
||||
|
||||
const commonProps = {
|
||||
metrics: true,
|
||||
mixed: true,
|
||||
dashboard: true,
|
||||
variables: true,
|
||||
current: this.props.options.dataSource,
|
||||
uploadFile: true,
|
||||
onChange: async (ds: DataSourceInstanceSettings, defaultQueries?: DataQuery[] | GrafanaQuery[]) => {
|
||||
await this.onChangeDataSource(ds, defaultQueries);
|
||||
this.onCloseDataSourceModal();
|
||||
@ -284,7 +289,7 @@ export class QueryGroup extends PureComponent<Props, State> {
|
||||
{isDataSourceModalOpen && config.featureToggles.advancedDataSourcePicker && (
|
||||
<DataSourceModal {...commonProps} onDismiss={this.onCloseDataSourceModal}></DataSourceModal>
|
||||
)}
|
||||
<DataSourcePicker {...commonProps} metrics={true} mixed={true} dashboard={true} variables={true} />
|
||||
<DataSourcePicker {...commonProps} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user