mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DS Picker: Add missing props and improve autoheight logic (#70393)
* DS Picker: Add `inputId` and `noDefault` options * DS Picker: Add `disabled` state * Add tests for `disabled` * Select default DS if `current` is not provided * Remove `width` from style * Move types next to components * Only calculate height when opening
This commit is contained in:
parent
9cf685cfda
commit
3bf8dc1397
@ -8,7 +8,7 @@ import { ModalRoot, ModalsProvider } from '@grafana/ui';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import { defaultFileUploadQuery } from 'app/plugins/datasource/grafana/types';
|
import { defaultFileUploadQuery } from 'app/plugins/datasource/grafana/types';
|
||||||
|
|
||||||
import { DataSourceDropdown } from './DataSourceDropdown';
|
import { DataSourceDropdown, DataSourceDropdownProps } from './DataSourceDropdown';
|
||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
|
|
||||||
const pluginMetaInfo: PluginMetaInfo = {
|
const pluginMetaInfo: PluginMetaInfo = {
|
||||||
@ -44,14 +44,8 @@ const MockDSBuiltIn = createDS('mock.datasource.builtin', 3, true);
|
|||||||
|
|
||||||
const mockDSList = [mockDS1, mockDS2, MockDSBuiltIn];
|
const mockDSList = [mockDS1, mockDS2, MockDSBuiltIn];
|
||||||
|
|
||||||
const setup = (onChange = () => {}, current = mockDS1.name) => {
|
async function setupOpenDropdown(user: UserEvent, props: DataSourceDropdownProps) {
|
||||||
const props = { onChange, current };
|
const dropdown = render(<DataSourceDropdown {...props}></DataSourceDropdown>);
|
||||||
window.HTMLElement.prototype.scrollIntoView = jest.fn();
|
|
||||||
return render(<DataSourceDropdown {...props}></DataSourceDropdown>);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function setupOpenDropdown(user: UserEvent, onChange?: () => void, current?: string) {
|
|
||||||
const dropdown = setup(onChange, current);
|
|
||||||
const searchBox = dropdown.container.querySelector('input');
|
const searchBox = dropdown.container.querySelector('input');
|
||||||
expect(searchBox).toBeInTheDocument();
|
expect(searchBox).toBeInTheDocument();
|
||||||
await user.click(searchBox!);
|
await user.click(searchBox!);
|
||||||
@ -87,6 +81,10 @@ jest.mock('../../hooks', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
window.HTMLElement.prototype.scrollIntoView = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
const getListMock = jest.fn();
|
const getListMock = jest.fn();
|
||||||
const getInstanceSettingsMock = jest.fn();
|
const getInstanceSettingsMock = jest.fn();
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -96,7 +94,7 @@ beforeEach(() => {
|
|||||||
|
|
||||||
describe('DataSourceDropdown', () => {
|
describe('DataSourceDropdown', () => {
|
||||||
it('should render', () => {
|
it('should render', () => {
|
||||||
expect(() => setup()).not.toThrow();
|
expect(() => render(<DataSourceDropdown onChange={jest.fn()}></DataSourceDropdown>)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('configuration', () => {
|
describe('configuration', () => {
|
||||||
@ -121,7 +119,6 @@ describe('DataSourceDropdown', () => {
|
|||||||
current: mockDS1.name,
|
current: mockDS1.name,
|
||||||
...filters,
|
...filters,
|
||||||
};
|
};
|
||||||
window.HTMLElement.prototype.scrollIntoView = jest.fn();
|
|
||||||
const dropdown = render(<DataSourceDropdown {...props}></DataSourceDropdown>);
|
const dropdown = render(<DataSourceDropdown {...props}></DataSourceDropdown>);
|
||||||
|
|
||||||
const searchBox = dropdown.container.querySelector('input');
|
const searchBox = dropdown.container.querySelector('input');
|
||||||
@ -130,40 +127,74 @@ describe('DataSourceDropdown', () => {
|
|||||||
expect(getListMock.mock.lastCall[0]).toEqual(filters);
|
expect(getListMock.mock.lastCall[0]).toEqual(filters);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should dispaly the current selected DS in the selector', async () => {
|
||||||
|
getInstanceSettingsMock.mockReturnValue(mockDS2);
|
||||||
|
render(<DataSourceDropdown onChange={jest.fn()} current={mockDS2}></DataSourceDropdown>);
|
||||||
|
expect(screen.getByTestId('Select a data source')).toHaveAttribute('placeholder', mockDS2.name);
|
||||||
|
expect(screen.getByAltText(`${mockDS2.meta.name} logo`)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
it('should display the current ds on top', async () => {
|
it('should display the current ds on top', async () => {
|
||||||
//Mock ds is set as current, it appears on top
|
//Mock ds is set as current, it appears on top
|
||||||
getInstanceSettingsMock.mockReturnValue(mockDS1);
|
getInstanceSettingsMock.mockReturnValue(mockDS1);
|
||||||
await setupOpenDropdown(user, jest.fn(), mockDS1.name);
|
await setupOpenDropdown(user, { onChange: jest.fn(), current: mockDS1.name });
|
||||||
let cards = await screen.findAllByTestId('data-source-card');
|
let cards = await screen.findAllByTestId('data-source-card');
|
||||||
expect(await findByText(cards[0], mockDS1.name, { selector: 'span' })).toBeInTheDocument();
|
expect(await findByText(cards[0], mockDS1.name, { selector: 'span' })).toBeInTheDocument();
|
||||||
|
|
||||||
//xMock ds is set as current, it appears on top
|
//xMock ds is set as current, it appears on top
|
||||||
getInstanceSettingsMock.mockReturnValue(mockDS2);
|
getInstanceSettingsMock.mockReturnValue(mockDS2);
|
||||||
await setupOpenDropdown(user, jest.fn(), mockDS2.name);
|
await setupOpenDropdown(user, { onChange: jest.fn(), current: mockDS2.name });
|
||||||
cards = await screen.findAllByTestId('data-source-card');
|
cards = await screen.findAllByTestId('data-source-card');
|
||||||
expect(await findByText(cards[0], mockDS2.name, { selector: 'span' })).toBeInTheDocument();
|
expect(await findByText(cards[0], mockDS2.name, { selector: 'span' })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should dispaly the default DS as selected when `current` is not set', async () => {
|
||||||
|
getInstanceSettingsMock.mockReturnValue(mockDS2);
|
||||||
|
render(<DataSourceDropdown onChange={jest.fn()} current={undefined}></DataSourceDropdown>);
|
||||||
|
expect(screen.getByTestId('Select a data source')).toHaveAttribute('placeholder', mockDS2.name);
|
||||||
|
expect(screen.getByAltText(`${mockDS2.meta.name} logo`)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
it('should get the sorting function using the correct parameters', async () => {
|
it('should get the sorting function using the correct parameters', async () => {
|
||||||
//The actual sorting is tested in utils.test but let's make sure we're calling getDataSourceCompareFn with the correct parameters
|
//The actual sorting is tested in utils.test but let's make sure we're calling getDataSourceCompareFn with the correct parameters
|
||||||
const spy = jest.spyOn(utils, 'getDataSourceCompareFn');
|
const spy = jest.spyOn(utils, 'getDataSourceCompareFn');
|
||||||
await setupOpenDropdown(user);
|
await setupOpenDropdown(user, { onChange: jest.fn(), current: mockDS1 });
|
||||||
|
|
||||||
expect(spy.mock.lastCall).toEqual([mockDS1, [mockDS2.name], ['${foo}']]);
|
expect(spy.mock.lastCall).toEqual([mockDS1, [mockDS2.name], ['${foo}']]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should disable the dropdown when `disabled` is true', () => {
|
||||||
|
render(<DataSourceDropdown onChange={jest.fn()} disabled></DataSourceDropdown>);
|
||||||
|
expect(screen.getByTestId('Select a data source')).toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should assign the correct `id` to the input element to pair it with a label', () => {
|
||||||
|
render(<DataSourceDropdown onChange={jest.fn()} inputId={'custom.input.id'}></DataSourceDropdown>);
|
||||||
|
expect(screen.getByTestId('Select a data source')).toHaveAttribute('id', 'custom.input.id');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not set the default DS when setting `noDefault` to true and `current` is not provided', () => {
|
||||||
|
render(<DataSourceDropdown onChange={jest.fn()} current={null} noDefault></DataSourceDropdown>);
|
||||||
|
getListMock.mockClear();
|
||||||
|
getInstanceSettingsMock.mockClear();
|
||||||
|
// 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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('interactions', () => {
|
describe('interactions', () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
|
|
||||||
it('should open when clicked', async () => {
|
it('should open when clicked', async () => {
|
||||||
await setupOpenDropdown(user);
|
await setupOpenDropdown(user, { onChange: jest.fn() });
|
||||||
expect(await screen.findByText(mockDS1.name, { selector: 'span' })).toBeInTheDocument();
|
expect(await screen.findByText(mockDS1.name, { selector: 'span' })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onChange when a data source is clicked', async () => {
|
it('should call onChange when a data source is clicked', async () => {
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
await setupOpenDropdown(user, onChange);
|
await setupOpenDropdown(user, { onChange });
|
||||||
|
|
||||||
await user.click(await screen.findByText(mockDS2.name, { selector: 'span' }));
|
await user.click(await screen.findByText(mockDS2.name, { selector: 'span' }));
|
||||||
expect(onChange.mock.lastCall[0]['name']).toEqual(mockDS2.name);
|
expect(onChange.mock.lastCall[0]['name']).toEqual(mockDS2.name);
|
||||||
@ -172,7 +203,7 @@ describe('DataSourceDropdown', () => {
|
|||||||
|
|
||||||
it('should push recently used datasources when a data source is clicked', async () => {
|
it('should push recently used datasources when a data source is clicked', async () => {
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
await setupOpenDropdown(user, onChange);
|
await setupOpenDropdown(user, { onChange });
|
||||||
|
|
||||||
await user.click(await screen.findByText(mockDS2.name, { selector: 'span' }));
|
await user.click(await screen.findByText(mockDS2.name, { selector: 'span' }));
|
||||||
expect(pushRecentlyUsedDataSourceMock.mock.lastCall[0]).toEqual(mockDS2);
|
expect(pushRecentlyUsedDataSourceMock.mock.lastCall[0]).toEqual(mockDS2);
|
||||||
@ -180,7 +211,7 @@ describe('DataSourceDropdown', () => {
|
|||||||
|
|
||||||
it('should be navigatable by keyboard', async () => {
|
it('should be navigatable by keyboard', async () => {
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
await setupOpenDropdown(user, onChange);
|
await setupOpenDropdown(user, { onChange });
|
||||||
|
|
||||||
await user.keyboard('[ArrowDown]');
|
await user.keyboard('[ArrowDown]');
|
||||||
//Arrow down, second item is selected
|
//Arrow down, second item is selected
|
||||||
@ -202,7 +233,7 @@ describe('DataSourceDropdown', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be searchable', async () => {
|
it('should be searchable', async () => {
|
||||||
await setupOpenDropdown(user);
|
await setupOpenDropdown(user, { onChange: jest.fn() });
|
||||||
|
|
||||||
await user.keyboard(mockDS2.name); //Search for xMockDS
|
await user.keyboard(mockDS2.name); //Search for xMockDS
|
||||||
|
|
||||||
@ -220,7 +251,7 @@ describe('DataSourceDropdown', () => {
|
|||||||
it('should call onChange with the default query when add csv is clicked', async () => {
|
it('should call onChange with the default query when add csv is clicked', async () => {
|
||||||
config.featureToggles.editPanelCSVDragAndDrop = true;
|
config.featureToggles.editPanelCSVDragAndDrop = true;
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
await setupOpenDropdown(user, onChange);
|
await setupOpenDropdown(user, { onChange });
|
||||||
|
|
||||||
await user.click(await screen.findByText('Add csv or spreadsheet'));
|
await user.click(await screen.findByText('Add csv or spreadsheet'));
|
||||||
|
|
||||||
@ -231,7 +262,6 @@ describe('DataSourceDropdown', () => {
|
|||||||
|
|
||||||
it('should open the modal when open advanced is clicked', async () => {
|
it('should open the modal when open advanced is clicked', async () => {
|
||||||
const props = { onChange: jest.fn(), current: mockDS1.name };
|
const props = { onChange: jest.fn(), current: mockDS1.name };
|
||||||
window.HTMLElement.prototype.scrollIntoView = jest.fn();
|
|
||||||
render(
|
render(
|
||||||
<ModalsProvider>
|
<ModalsProvider>
|
||||||
<DataSourceDropdown {...props}></DataSourceDropdown>
|
<DataSourceDropdown {...props}></DataSourceDropdown>
|
||||||
|
@ -3,11 +3,12 @@ import { useDialog } from '@react-aria/dialog';
|
|||||||
import { useOverlay } from '@react-aria/overlays';
|
import { useOverlay } from '@react-aria/overlays';
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { usePopper } from 'react-popper';
|
import { usePopper } from 'react-popper';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data';
|
import { DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { DataQuery, DataSourceJsonData } from '@grafana/schema';
|
import { DataQuery, DataSourceJsonData, DataSourceRef } from '@grafana/schema';
|
||||||
import { Button, CustomScrollbar, Icon, Input, ModalsController, Portal, useStyles2 } from '@grafana/ui';
|
import { Button, CustomScrollbar, Icon, Input, ModalsController, Portal, useStyles2 } from '@grafana/ui';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import { useKeyNavigationListener } from 'app/features/search/hooks/useSearchKeyboardSelection';
|
import { useKeyNavigationListener } from 'app/features/search/hooks/useSearchKeyboardSelection';
|
||||||
@ -19,7 +20,6 @@ import { DataSourceList } from './DataSourceList';
|
|||||||
import { DataSourceLogo, DataSourceLogoPlaceHolder } from './DataSourceLogo';
|
import { DataSourceLogo, DataSourceLogoPlaceHolder } from './DataSourceLogo';
|
||||||
import { DataSourceModal } from './DataSourceModal';
|
import { DataSourceModal } from './DataSourceModal';
|
||||||
import { applyMaxSize, maxSize } from './popperModifiers';
|
import { applyMaxSize, maxSize } from './popperModifiers';
|
||||||
import { PickerContentProps, DataSourceDropdownProps } from './types';
|
|
||||||
import { dataSourceLabel, matchDataSourceWithSearch } from './utils';
|
import { dataSourceLabel, matchDataSourceWithSearch } from './utils';
|
||||||
|
|
||||||
const INTERACTION_EVENT_NAME = 'dashboards_dspicker_clicked';
|
const INTERACTION_EVENT_NAME = 'dashboards_dspicker_clicked';
|
||||||
@ -31,8 +31,40 @@ const INTERACTION_ITEM = {
|
|||||||
CONFIG_NEW_DS_EMPTY_STATE: 'config_new_ds_empty_state',
|
CONFIG_NEW_DS_EMPTY_STATE: 'config_new_ds_empty_state',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface DataSourceDropdownProps {
|
||||||
|
onChange: (ds: DataSourceInstanceSettings<DataSourceJsonData>, defaultQueries?: DataQuery[] | GrafanaQuery[]) => void;
|
||||||
|
current?: DataSourceInstanceSettings<DataSourceJsonData> | string | DataSourceRef | null | undefined;
|
||||||
|
recentlyUsed?: string[];
|
||||||
|
hideTextValue?: boolean;
|
||||||
|
width?: number;
|
||||||
|
inputId?: string;
|
||||||
|
noDefault?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
|
||||||
|
// DS filters
|
||||||
|
tracing?: boolean;
|
||||||
|
mixed?: boolean;
|
||||||
|
dashboard?: boolean;
|
||||||
|
metrics?: boolean;
|
||||||
|
type?: string | string[];
|
||||||
|
annotations?: boolean;
|
||||||
|
variables?: boolean;
|
||||||
|
alerting?: boolean;
|
||||||
|
pluginId?: string;
|
||||||
|
logs?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
||||||
const { current, onChange, hideTextValue, width, ...restProps } = props;
|
const {
|
||||||
|
current,
|
||||||
|
onChange,
|
||||||
|
hideTextValue = false,
|
||||||
|
width,
|
||||||
|
inputId,
|
||||||
|
noDefault = false,
|
||||||
|
disabled = false,
|
||||||
|
...restProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
const [isOpen, setOpen] = useState(false);
|
const [isOpen, setOpen] = useState(false);
|
||||||
const [inputHasFocus, setInputHasFocus] = useState(false);
|
const [inputHasFocus, setInputHasFocus] = useState(false);
|
||||||
@ -44,6 +76,10 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
|||||||
setOpen(true);
|
setOpen(true);
|
||||||
markerElement?.focus();
|
markerElement?.focus();
|
||||||
};
|
};
|
||||||
|
const currentDataSourceInstanceSettings = useDatasource(current);
|
||||||
|
const currentValue = Boolean(!current && noDefault) ? undefined : currentDataSourceInstanceSettings;
|
||||||
|
const prefixIcon =
|
||||||
|
filterTerm && isOpen ? <DataSourceLogoPlaceHolder /> : <DataSourceLogo dataSource={currentValue} />;
|
||||||
|
|
||||||
const { onKeyDown, keyboardEvents } = useKeyNavigationListener();
|
const { onKeyDown, keyboardEvents } = useKeyNavigationListener();
|
||||||
|
|
||||||
@ -79,8 +115,6 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
|||||||
onChange(grafanaDS, [defaultFileUploadQuery]);
|
onChange(grafanaDS, [defaultFileUploadQuery]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentDataSourceInstanceSettings = useDatasource(current);
|
|
||||||
|
|
||||||
const popper = usePopper(markerElement, selectorElement, {
|
const popper = usePopper(markerElement, selectorElement, {
|
||||||
placement: 'bottom-start',
|
placement: 'bottom-start',
|
||||||
modifiers: [
|
modifiers: [
|
||||||
@ -114,25 +148,20 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
|||||||
);
|
);
|
||||||
const { dialogProps } = useDialog({}, ref);
|
const { dialogProps } = useDialog({}, ref);
|
||||||
|
|
||||||
const styles = useStyles2(getStylesDropdown);
|
const styles = useStyles2((theme: GrafanaTheme2) => getStylesDropdown(theme, props));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container} data-testid={selectors.components.DataSourcePicker.container} style={{ width }}>
|
<div className={styles.container} data-testid={selectors.components.DataSourcePicker.container}>
|
||||||
{/* This clickable div is just extending the clickable area on the input element to include the prefix and suffix. */}
|
{/* This clickable div is just extending the clickable area on the input element to include the prefix and suffix. */}
|
||||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
|
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
|
||||||
<div className={styles.trigger} onClick={openDropdown}>
|
<div className={styles.trigger} onClick={openDropdown}>
|
||||||
<Input
|
<Input
|
||||||
|
id={inputId || 'data-source-picker'}
|
||||||
className={inputHasFocus ? undefined : styles.input}
|
className={inputHasFocus ? undefined : styles.input}
|
||||||
data-testid={selectors.components.DataSourcePicker.inputV2}
|
data-testid={selectors.components.DataSourcePicker.inputV2}
|
||||||
prefix={
|
prefix={currentValue ? prefixIcon : undefined}
|
||||||
filterTerm && isOpen ? (
|
|
||||||
<DataSourceLogoPlaceHolder />
|
|
||||||
) : (
|
|
||||||
<DataSourceLogo dataSource={currentDataSourceInstanceSettings} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
suffix={<Icon name={isOpen ? 'search' : 'angle-down'} />}
|
suffix={<Icon name={isOpen ? 'search' : 'angle-down'} />}
|
||||||
placeholder={hideTextValue ? '' : dataSourceLabel(currentDataSourceInstanceSettings)}
|
placeholder={hideTextValue ? '' : dataSourceLabel(currentValue)}
|
||||||
onClick={openDropdown}
|
onClick={openDropdown}
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
setInputHasFocus(true);
|
setInputHasFocus(true);
|
||||||
@ -148,6 +177,7 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
|||||||
setFilterTerm(e.currentTarget.value);
|
setFilterTerm(e.currentTarget.value);
|
||||||
}}
|
}}
|
||||||
ref={setMarkerElement}
|
ref={setMarkerElement}
|
||||||
|
disabled={disabled}
|
||||||
></Input>
|
></Input>
|
||||||
</div>
|
</div>
|
||||||
{isOpen ? (
|
{isOpen ? (
|
||||||
@ -172,7 +202,7 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
|||||||
onChange(ds, defaultQueries);
|
onChange(ds, defaultQueries);
|
||||||
}}
|
}}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
current={currentDataSourceInstanceSettings}
|
current={currentValue}
|
||||||
style={popper.styles.popper}
|
style={popper.styles.popper}
|
||||||
ref={setSelectorElement}
|
ref={setSelectorElement}
|
||||||
onClickAddCSV={onClickAddCSV}
|
onClickAddCSV={onClickAddCSV}
|
||||||
@ -187,25 +217,34 @@ export function DataSourceDropdown(props: DataSourceDropdownProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStylesDropdown(theme: GrafanaTheme2) {
|
function getStylesDropdown(theme: GrafanaTheme2, props: DataSourceDropdownProps) {
|
||||||
return {
|
return {
|
||||||
container: css`
|
container: css`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: ${props.disabled ? 'not-allowed' : 'pointer'};
|
||||||
|
width: ${theme.spacing(props.width || 'auto')};
|
||||||
`,
|
`,
|
||||||
trigger: css`
|
trigger: css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
${props.disabled && `pointer-events: none;`}
|
||||||
`,
|
`,
|
||||||
input: css`
|
input: css`
|
||||||
input {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
input::placeholder {
|
input::placeholder {
|
||||||
color: ${theme.colors.text.primary};
|
color: ${props.disabled ? theme.colors.action.disabledText : theme.colors.text.primary};
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PickerContentProps extends DataSourceDropdownProps {
|
||||||
|
onClickAddCSV?: () => void;
|
||||||
|
keyboardEvents: Observable<React.KeyboardEvent>;
|
||||||
|
style: React.CSSProperties;
|
||||||
|
filterTerm?: string;
|
||||||
|
onClose: () => void;
|
||||||
|
onDismiss: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((props, ref) => {
|
const PickerContent = React.forwardRef<HTMLDivElement, PickerContentProps>((props, ref) => {
|
||||||
const { filterTerm, onChange, onClose, onClickAddCSV, current } = props;
|
const { filterTerm, onChange, onClose, onClickAddCSV, current } = props;
|
||||||
const changeCallback = useCallback(
|
const changeCallback = useCallback(
|
||||||
|
@ -40,9 +40,19 @@ interface DataSourceModalProps {
|
|||||||
current: DataSourceRef | string | null | undefined;
|
current: DataSourceRef | string | null | undefined;
|
||||||
onDismiss: () => void;
|
onDismiss: () => void;
|
||||||
recentlyUsed?: string[];
|
recentlyUsed?: string[];
|
||||||
dashboard?: boolean;
|
|
||||||
mixed?: boolean;
|
|
||||||
reportedInteractionFrom?: string;
|
reportedInteractionFrom?: string;
|
||||||
|
|
||||||
|
// DS filters
|
||||||
|
tracing?: boolean;
|
||||||
|
mixed?: boolean;
|
||||||
|
dashboard?: boolean;
|
||||||
|
metrics?: boolean;
|
||||||
|
type?: string | string[];
|
||||||
|
annotations?: boolean;
|
||||||
|
variables?: boolean;
|
||||||
|
alerting?: boolean;
|
||||||
|
pluginId?: string;
|
||||||
|
logs?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DataSourceModal({
|
export function DataSourceModal({
|
||||||
|
@ -6,8 +6,7 @@ import {
|
|||||||
} from '@grafana/runtime';
|
} from '@grafana/runtime';
|
||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
|
|
||||||
import { DataSourceDropdown } from './DataSourceDropdown';
|
import { DataSourceDropdown, DataSourceDropdownProps } from './DataSourceDropdown';
|
||||||
import { DataSourceDropdownProps } from './types';
|
|
||||||
|
|
||||||
type DataSourcePickerProps = DeprecatedDataSourcePickerProps | DataSourceDropdownProps;
|
type DataSourcePickerProps = DeprecatedDataSourcePickerProps | DataSourceDropdownProps;
|
||||||
|
|
||||||
|
@ -31,8 +31,16 @@ export const applyMaxSize: Modifier<'applyMaxSize', {}> = {
|
|||||||
requires: ['maxSize'],
|
requires: ['maxSize'],
|
||||||
fn({ state }: ModifierArguments<{}>) {
|
fn({ state }: ModifierArguments<{}>) {
|
||||||
const { height, width } = state.modifiersData.maxSize;
|
const { height, width } = state.modifiersData.maxSize;
|
||||||
state.styles.popper.maxHeight = `${height - MODAL_MARGIN}px`;
|
|
||||||
state.styles.popper.minHeight = `${FLIP_THRESHOLD}px`;
|
if (!state.styles.popper.maxHeight) {
|
||||||
state.styles.popper.maxWidth = width;
|
state.styles.popper.maxHeight = `${height - MODAL_MARGIN}px`;
|
||||||
|
}
|
||||||
|
if (!state.styles.popper.minHeight) {
|
||||||
|
state.styles.popper.minHeight = `${FLIP_THRESHOLD}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.styles.popper.maxWidth) {
|
||||||
|
state.styles.popper.maxWidth = width;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
|
|
||||||
import { DataSourceInstanceSettings } from '@grafana/data';
|
|
||||||
import { DataQuery, DataSourceJsonData, DataSourceRef } from '@grafana/schema';
|
|
||||||
import { GrafanaQuery } from 'app/plugins/datasource/grafana/types';
|
|
||||||
|
|
||||||
export interface DataSourceDropdownProps {
|
|
||||||
onChange: (ds: DataSourceInstanceSettings<DataSourceJsonData>, defaultQueries?: DataQuery[] | GrafanaQuery[]) => void;
|
|
||||||
current: DataSourceInstanceSettings<DataSourceJsonData> | string | DataSourceRef | null | undefined;
|
|
||||||
tracing?: boolean;
|
|
||||||
mixed?: boolean;
|
|
||||||
dashboard?: boolean;
|
|
||||||
metrics?: boolean;
|
|
||||||
type?: string | string[];
|
|
||||||
annotations?: boolean;
|
|
||||||
variables?: boolean;
|
|
||||||
alerting?: boolean;
|
|
||||||
pluginId?: string;
|
|
||||||
logs?: boolean;
|
|
||||||
recentlyUsed?: string[];
|
|
||||||
hideTextValue?: boolean;
|
|
||||||
width?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PickerContentProps extends DataSourceDropdownProps {
|
|
||||||
onClickAddCSV?: () => void;
|
|
||||||
keyboardEvents: Observable<React.KeyboardEvent>;
|
|
||||||
style: React.CSSProperties;
|
|
||||||
filterTerm?: string;
|
|
||||||
dashboard?: boolean;
|
|
||||||
mixed?: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
onDismiss: () => void;
|
|
||||||
}
|
|
@ -20,7 +20,7 @@ export function dataSourceLabel(
|
|||||||
dataSource: DataSourceInstanceSettings<DataSourceJsonData> | string | DataSourceRef | null | undefined
|
dataSource: DataSourceInstanceSettings<DataSourceJsonData> | string | DataSourceRef | null | undefined
|
||||||
) {
|
) {
|
||||||
if (!dataSource) {
|
if (!dataSource) {
|
||||||
return 'Unknown';
|
return 'Select a data source';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof dataSource === 'string') {
|
if (typeof dataSource === 'string') {
|
||||||
@ -35,7 +35,7 @@ export function dataSourceLabel(
|
|||||||
return `${dataSource.uid} - not found`;
|
return `${dataSource.uid} - not found`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Unknown';
|
return 'Select a data source';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDataSourceCompareFn(
|
export function getDataSourceCompareFn(
|
||||||
|
@ -46,10 +46,6 @@ export function useDatasources(filters: GetDataSourceListFilters) {
|
|||||||
export function useDatasource(dataSource: string | DataSourceRef | DataSourceInstanceSettings | null | undefined) {
|
export function useDatasource(dataSource: string | DataSourceRef | DataSourceInstanceSettings | null | undefined) {
|
||||||
const dataSourceSrv = getDataSourceSrv();
|
const dataSourceSrv = getDataSourceSrv();
|
||||||
|
|
||||||
if (!dataSource) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof dataSource === 'string') {
|
if (typeof dataSource === 'string') {
|
||||||
return dataSourceSrv.getInstanceSettings(dataSource);
|
return dataSourceSrv.getInstanceSettings(dataSource);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user