diff --git a/packages/grafana-data/src/types/index.ts b/packages/grafana-data/src/types/index.ts index 3e88ca59bf5..d7d22d6308f 100644 --- a/packages/grafana-data/src/types/index.ts +++ b/packages/grafana-data/src/types/index.ts @@ -30,5 +30,5 @@ export * from './legacyEvents'; export * from './live'; export * from './variables'; export * from './geometry'; - +export { isUnsignedPluginSignature } from './pluginSignature'; export { GrafanaConfig, BuildInfo, FeatureToggles, LicenseInfo } from './config'; diff --git a/packages/grafana-data/src/types/pluginSignature.ts b/packages/grafana-data/src/types/pluginSignature.ts new file mode 100644 index 00000000000..214970a2edc --- /dev/null +++ b/packages/grafana-data/src/types/pluginSignature.ts @@ -0,0 +1,11 @@ +import { PluginSignatureStatus } from './plugin'; + +/** + * Utility function to check if a plugin is unsigned. + * + * @param signature - the plugin meta signature + * @internal + */ +export function isUnsignedPluginSignature(signature?: PluginSignatureStatus) { + return signature && signature !== PluginSignatureStatus.valid && signature !== PluginSignatureStatus.internal; +} diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 539f42b5031..40fc9b06a34 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -24,6 +24,7 @@ "dependencies": { "@grafana/data": "7.5.0-pre.0", "@grafana/ui": "7.5.0-pre.0", + "@grafana/e2e-selectors": "7.5.0-pre.0", "systemjs": "0.20.19", "systemjs-plugin-css": "0.1.37", "history": "4.10.1" diff --git a/packages/grafana-runtime/rollup.config.ts b/packages/grafana-runtime/rollup.config.ts index 6b9900df1b2..e6b71219a0b 100644 --- a/packages/grafana-runtime/rollup.config.ts +++ b/packages/grafana-runtime/rollup.config.ts @@ -20,7 +20,7 @@ const buildCjsPackage = ({ env }) => { globals: {}, }, ], - external: ['lodash', '@grafana/ui', '@grafana/data'], // Use Lodash from grafana + external: ['lodash', 'react', '@grafana/ui', '@grafana/data', '@grafana/e2e-selectors'], // Use Lodash from grafana plugins: [ commonjs({ include: /node_modules/, diff --git a/public/app/core/components/Select/DataSourcePicker.tsx b/packages/grafana-runtime/src/components/DataSourcePicker.tsx similarity index 76% rename from public/app/core/components/Select/DataSourcePicker.tsx rename to packages/grafana-runtime/src/components/DataSourcePicker.tsx index 2be378f9f55..7872969bdcf 100644 --- a/public/app/core/components/Select/DataSourcePicker.tsx +++ b/packages/grafana-runtime/src/components/DataSourcePicker.tsx @@ -2,13 +2,17 @@ import React, { PureComponent } from 'react'; // Components -import { HorizontalGroup, Select } from '@grafana/ui'; -import { DataSourceInstanceSettings, SelectableValue } from '@grafana/data'; +import { HorizontalGroup, PluginSignatureBadge, Select } from '@grafana/ui'; +import { DataSourceInstanceSettings, isUnsignedPluginSignature, SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { isUnsignedPluginSignature, PluginSignatureBadge } from '../../../features/plugins/PluginSignatureBadge'; -import { getDataSourceSrv } from '@grafana/runtime'; +import { getDataSourceSrv } from '../services/dataSourceSrv'; -export interface Props { +/** + * Component props description for the {@link DataSourcePicker} + * + * @internal + */ +export interface DataSourcePickerProps { onChange: (ds: DataSourceInstanceSettings) => void; current: string | null; hideTextValue?: boolean; @@ -26,22 +30,33 @@ export interface Props { noDefault?: boolean; } -export interface State { +/** + * Component state description for the {@link DataSourcePicker} + * + * @internal + */ +export interface DataSourcePickerState { error?: string; } -export class DataSourcePicker extends PureComponent { +/** + * Component to be able to select a datasource from the list of installed and enabled + * datasources in the current Grafana instance. + * + * @internal + */ +export class DataSourcePicker extends PureComponent { dataSourceSrv = getDataSourceSrv(); - static defaultProps: Partial = { + static defaultProps: Partial = { autoFocus: false, openMenuOnFocus: false, placeholder: 'Select datasource', }; - state: State = {}; + state: DataSourcePickerState = {}; - constructor(props: Props) { + constructor(props: DataSourcePickerProps) { super(props); } @@ -62,11 +77,11 @@ export class DataSourcePicker extends PureComponent { } }; - private getCurrentValue() { + private getCurrentValue(): SelectableValue | undefined { const { current, hideTextValue, noDefault } = this.props; if (!current && noDefault) { - return null; + return; } const ds = this.dataSourceSrv.getInstanceSettings(current); @@ -83,7 +98,7 @@ export class DataSourcePicker extends PureComponent { return { label: (current ?? 'no name') + ' - not found', - value: current, + value: current === null ? undefined : current, imgUrl: '', hideText: hideTextValue, }; diff --git a/packages/grafana-runtime/src/index.ts b/packages/grafana-runtime/src/index.ts index a8f139042df..2b111dd7a93 100644 --- a/packages/grafana-runtime/src/index.ts +++ b/packages/grafana-runtime/src/index.ts @@ -12,3 +12,4 @@ export { reportMetaAnalytics } from './utils/analytics'; export { logInfo, logDebug, logWarning, logError } from './utils/logging'; export { DataSourceWithBackend, HealthCheckResult, HealthStatus } from './utils/DataSourceWithBackend'; export { toDataQueryError, toDataQueryResponse, frameToMetricFindValue } from './utils/queryResponse'; +export { DataSourcePicker, DataSourcePickerProps, DataSourcePickerState } from './components/DataSourcePicker'; diff --git a/packages/grafana-ui/src/components/PluginSignatureBadge/PluginSignatureBadge.story.tsx b/packages/grafana-ui/src/components/PluginSignatureBadge/PluginSignatureBadge.story.tsx new file mode 100644 index 00000000000..040d1f3584b --- /dev/null +++ b/packages/grafana-ui/src/components/PluginSignatureBadge/PluginSignatureBadge.story.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { select } from '@storybook/addon-knobs'; +import { PluginSignatureBadge } from '@grafana/ui'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { PluginSignatureStatus } from '@grafana/data'; + +export default { + title: 'Data Display/PluginSignatureBadge', + decorators: [withCenteredStory], + component: PluginSignatureBadge, +}; + +const getKnobs = () => { + return { + status: select( + 'status', + [ + PluginSignatureStatus.missing, + PluginSignatureStatus.invalid, + PluginSignatureStatus.modified, + PluginSignatureStatus.valid, + PluginSignatureStatus.internal, + ], + PluginSignatureStatus.valid + ), + }; +}; + +export const basic = () => { + const { status } = getKnobs(); + return ; +}; diff --git a/public/app/features/plugins/PluginSignatureBadge.tsx b/packages/grafana-ui/src/components/PluginSignatureBadge/PluginSignatureBadge.tsx similarity index 60% rename from public/app/features/plugins/PluginSignatureBadge.tsx rename to packages/grafana-ui/src/components/PluginSignatureBadge/PluginSignatureBadge.tsx index d21787ec3e9..d237060127e 100644 --- a/public/app/features/plugins/PluginSignatureBadge.tsx +++ b/packages/grafana-ui/src/components/PluginSignatureBadge/PluginSignatureBadge.tsx @@ -1,12 +1,18 @@ import React, { HTMLAttributes } from 'react'; -import { Badge, BadgeProps } from '@grafana/ui'; -import { PluginErrorCode, PluginSignatureStatus } from '@grafana/data'; +import { PluginSignatureStatus } from '@grafana/data'; +import { Badge, BadgeProps } from '../Badge/Badge'; -interface Props extends HTMLAttributes { +/** + * @public + */ +export interface PluginSignatureBadgeProps extends HTMLAttributes { status?: PluginSignatureStatus; } -export const PluginSignatureBadge: React.FC = ({ status, ...otherProps }) => { +/** + * @public + */ +export const PluginSignatureBadge: React.FC = ({ status, ...otherProps }) => { const display = getSignatureDisplayModel(status); return ( = ({ status, ...otherProps }) ); }; -export function isUnsignedPluginSignature(signature?: PluginSignatureStatus) { - return signature && signature !== PluginSignatureStatus.valid && signature !== PluginSignatureStatus.internal; -} - -export function mapPluginErrorCodeToSignatureStatus(code: PluginErrorCode) { - switch (code) { - case PluginErrorCode.invalidSignature: - return PluginSignatureStatus.invalid; - case PluginErrorCode.missingSignature: - return PluginSignatureStatus.missing; - case PluginErrorCode.modifiedSignature: - return PluginSignatureStatus.modified; - default: - return PluginSignatureStatus.missing; - } -} +PluginSignatureBadge.displayName = 'PluginSignatureBadge'; function getSignatureDisplayModel(signature?: PluginSignatureStatus): BadgeProps { if (!signature) { @@ -67,9 +58,12 @@ function getSignatureDisplayModel(signature?: PluginSignatureStatus): BadgeProps color: 'red', tooltip: 'Missing plugin signature', }; + default: + return { + text: 'Unsigned', + icon: 'exclamation-triangle', + color: 'red', + tooltip: 'Unsigned external plugin', + }; } - - return { text: 'Unsigned', icon: 'exclamation-triangle', color: 'red', tooltip: 'Unsigned external plugin' }; } - -PluginSignatureBadge.displayName = 'PluginSignatureBadge'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index c62c65e07e0..a17d900a7d2 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -179,6 +179,7 @@ export { Card, Props as CardProps, ContainerProps, CardInnerProps, getCardStyles export { FormattedValueDisplay } from './FormattedValueDisplay/FormattedValueDisplay'; export { ButtonSelect } from './Dropdown/ButtonSelect'; +export { PluginSignatureBadge, PluginSignatureBadgeProps } from './PluginSignatureBadge/PluginSignatureBadge'; // Legacy forms diff --git a/public/app/core/components/TraceToLogsSettings.tsx b/public/app/core/components/TraceToLogsSettings.tsx index e732a6ee5ed..fcdcc643ef8 100644 --- a/public/app/core/components/TraceToLogsSettings.tsx +++ b/public/app/core/components/TraceToLogsSettings.tsx @@ -4,10 +4,10 @@ import { GrafanaTheme, updateDatasourcePluginJsonDataOption, } from '@grafana/data'; +import { DataSourcePicker } from '@grafana/runtime'; import { InlineFormLabel, TagsInput, useStyles } from '@grafana/ui'; import { css } from 'emotion'; import React from 'react'; -import { DataSourcePicker } from './Select/DataSourcePicker'; export interface TraceToLogsOptions { datasourceUid?: string; diff --git a/public/app/features/dashboard/components/VizTypePicker/VizTypePickerPlugin.tsx b/public/app/features/dashboard/components/VizTypePicker/VizTypePickerPlugin.tsx index 4aa96f02c85..0e89fc70fb6 100644 --- a/public/app/features/dashboard/components/VizTypePicker/VizTypePickerPlugin.tsx +++ b/public/app/features/dashboard/components/VizTypePicker/VizTypePickerPlugin.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import { GrafanaTheme, PanelPluginMeta, PluginState } from '@grafana/data'; -import { Badge, BadgeProps, styleMixins, stylesFactory, useTheme } from '@grafana/ui'; +import { GrafanaTheme, isUnsignedPluginSignature, PanelPluginMeta, PluginState } from '@grafana/data'; +import { Badge, BadgeProps, PluginSignatureBadge, styleMixins, stylesFactory, useTheme } from '@grafana/ui'; import { css, cx } from 'emotion'; import { selectors } from '@grafana/e2e-selectors'; -import { isUnsignedPluginSignature, PluginSignatureBadge } from '../../../plugins/PluginSignatureBadge'; interface Props { isCurrent: boolean; diff --git a/public/app/features/datasources/NewDataSourcePage.tsx b/public/app/features/datasources/NewDataSourcePage.tsx index 041dbbbd2fa..051180e6906 100644 --- a/public/app/features/datasources/NewDataSourcePage.tsx +++ b/public/app/features/datasources/NewDataSourcePage.tsx @@ -2,7 +2,7 @@ import React, { FC, PureComponent } from 'react'; import { connect } from 'react-redux'; import { hot } from 'react-hot-loader'; import { DataSourcePluginMeta, NavModel } from '@grafana/data'; -import { Button, LinkButton, List } from '@grafana/ui'; +import { Button, LinkButton, List, PluginSignatureBadge } from '@grafana/ui'; import { selectors } from '@grafana/e2e-selectors'; import Page from 'app/core/components/Page/Page'; @@ -11,7 +11,6 @@ import { addDataSource, loadDataSourcePlugins } from './state/actions'; import { getDataSourcePlugins } from './state/selectors'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { setDataSourceTypeSearchQuery } from './state/reducers'; -import { PluginSignatureBadge } from '../plugins/PluginSignatureBadge'; import { Card } from 'app/core/components/Card/Card'; import { PluginsErrorsInfo } from '../plugins/PluginsErrorsInfo'; diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index c57ffeda9f1..bd9c4af18cf 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -7,7 +7,7 @@ import { css } from 'emotion'; import { ExploreId, ExploreItemState } from 'app/types/explore'; import { Icon, IconButton, SetInterval, ToolbarButton, ToolbarButtonRow, Tooltip } from '@grafana/ui'; import { DataSourceInstanceSettings, RawTimeRange, TimeRange, TimeZone } from '@grafana/data'; -import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; +import { DataSourcePicker } from '@grafana/runtime'; import { StoreState } from 'app/types/store'; import { createAndCopyShortLink } from 'app/core/utils/shortLinks'; import { changeDatasource } from './state/datasource'; diff --git a/public/app/features/manage-dashboards/components/ImportDashboardForm.tsx b/public/app/features/manage-dashboards/components/ImportDashboardForm.tsx index 566dcd5bb24..5de324d6123 100644 --- a/public/app/features/manage-dashboards/components/ImportDashboardForm.tsx +++ b/public/app/features/manage-dashboards/components/ImportDashboardForm.tsx @@ -10,8 +10,8 @@ import { InputControl, Legend, } from '@grafana/ui'; +import { DataSourcePicker } from '@grafana/runtime'; import { FolderPicker } from 'app/core/components/Select/FolderPicker'; -import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { DashboardInput, DashboardInputs, DataSourceInput, ImportDashboardDTO } from '../state/reducers'; import { validateTitle, validateUid } from '../utils/validation'; diff --git a/public/app/features/plugins/PluginListItem.tsx b/public/app/features/plugins/PluginListItem.tsx index f138fec65af..c0d92d30d96 100644 --- a/public/app/features/plugins/PluginListItem.tsx +++ b/public/app/features/plugins/PluginListItem.tsx @@ -1,6 +1,6 @@ import React, { FC } from 'react'; import { PluginMeta } from '@grafana/data'; -import { PluginSignatureBadge } from './PluginSignatureBadge'; +import { PluginSignatureBadge } from '@grafana/ui'; import { selectors } from '@grafana/e2e-selectors'; interface Props { diff --git a/public/app/features/plugins/PluginPage.tsx b/public/app/features/plugins/PluginPage.tsx index 0f99ae36da0..c570e2f0489 100644 --- a/public/app/features/plugins/PluginPage.tsx +++ b/public/app/features/plugins/PluginPage.tsx @@ -17,7 +17,8 @@ import { UrlQueryMap, } from '@grafana/data'; import { AppNotificationSeverity } from 'app/types'; -import { Alert, InfoBox, Tooltip } from '@grafana/ui'; +import { Alert, InfoBox, Tooltip, PluginSignatureBadge } from '@grafana/ui'; + import Page from 'app/core/components/Page/Page'; import { getPluginSettings } from './PluginSettingsCache'; import { importAppPlugin, importDataSourcePlugin, importPanelPlugin } from './plugin_loader'; @@ -29,7 +30,6 @@ import { appEvents } from 'app/core/core'; import { config } from 'app/core/config'; import { contextSrv } from '../../core/services/context_srv'; import { css } from 'emotion'; -import { PluginSignatureBadge } from './PluginSignatureBadge'; import { selectors } from '@grafana/e2e-selectors'; import { ShowModalEvent } from 'app/types/events'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; diff --git a/public/app/features/plugins/PluginsErrorsInfo.tsx b/public/app/features/plugins/PluginsErrorsInfo.tsx index 8e0c8c698f4..53c50b2556e 100644 --- a/public/app/features/plugins/PluginsErrorsInfo.tsx +++ b/public/app/features/plugins/PluginsErrorsInfo.tsx @@ -1,14 +1,13 @@ import React from 'react'; import { selectors } from '@grafana/e2e-selectors'; -import { HorizontalGroup, InfoBox, List, useTheme } from '@grafana/ui'; -import { mapPluginErrorCodeToSignatureStatus, PluginSignatureBadge } from './PluginSignatureBadge'; +import { HorizontalGroup, InfoBox, List, PluginSignatureBadge, useTheme } from '@grafana/ui'; import { StoreState } from '../../types'; import { getAllPluginsErrors } from './state/selectors'; import { loadPlugins, loadPluginsErrors } from './state/actions'; import useAsync from 'react-use/lib/useAsync'; import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; import { hot } from 'react-hot-loader'; -import { PluginError } from '@grafana/data'; +import { PluginError, PluginErrorCode, PluginSignatureStatus } from '@grafana/data'; import { css } from 'emotion'; interface ConnectedProps { @@ -102,3 +101,16 @@ const mapDispatchToProps: MapDispatchToProps = { export const PluginsErrorsInfo = hot(module)( connect(mapStateToProps, mapDispatchToProps)(PluginsErrorsInfoUnconnected) ); + +function mapPluginErrorCodeToSignatureStatus(code: PluginErrorCode) { + switch (code) { + case PluginErrorCode.invalidSignature: + return PluginSignatureStatus.invalid; + case PluginErrorCode.missingSignature: + return PluginSignatureStatus.missing; + case PluginErrorCode.modifiedSignature: + return PluginSignatureStatus.modified; + default: + return PluginSignatureStatus.missing; + } +} diff --git a/public/app/features/query/components/QueryGroup.tsx b/public/app/features/query/components/QueryGroup.tsx index 086561780e9..76cd6838f68 100644 --- a/public/app/features/query/components/QueryGroup.tsx +++ b/public/app/features/query/components/QueryGroup.tsx @@ -1,9 +1,8 @@ // Libraries import React, { PureComponent } from 'react'; // Components -import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { Button, CustomScrollbar, HorizontalGroup, Icon, Modal, stylesFactory, Tooltip } from '@grafana/ui'; -import { getDataSourceSrv } from '@grafana/runtime'; +import { getDataSourceSrv, DataSourcePicker } from '@grafana/runtime'; import { QueryEditorRows } from './QueryEditorRows'; // Services import { backendSrv } from 'app/core/services/backend_srv'; diff --git a/public/app/features/variables/query/QueryVariableEditor.tsx b/public/app/features/variables/query/QueryVariableEditor.tsx index 6650fa91499..405f240cb2a 100644 --- a/public/app/features/variables/query/QueryVariableEditor.tsx +++ b/public/app/features/variables/query/QueryVariableEditor.tsx @@ -3,7 +3,7 @@ import { css } from 'emotion'; import { MapDispatchToProps, MapStateToProps } from 'react-redux'; import { InlineField, InlineFieldRow, VerticalGroup } from '@grafana/ui'; import { selectors } from '@grafana/e2e-selectors'; -import { getTemplateSrv } from '@grafana/runtime'; +import { getTemplateSrv, DataSourcePicker } from '@grafana/runtime'; import { DataSourceInstanceSettings, LoadingState, SelectableValue } from '@grafana/data'; import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor'; @@ -23,7 +23,6 @@ import { VariableTextField } from '../editor/VariableTextField'; import { VariableSwitchField } from '../editor/VariableSwitchField'; import { QueryVariableRefreshSelect } from './QueryVariableRefreshSelect'; import { QueryVariableSortSelect } from './QueryVariableSortSelect'; -import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; export interface OwnProps extends VariableEditorProps {} diff --git a/public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx b/public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx index 87d62860aa8..4ae6ce274fb 100644 --- a/public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx +++ b/public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx @@ -1,11 +1,11 @@ import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { css } from 'emotion'; import { VariableSuggestion } from '@grafana/data'; +import { DataSourcePicker } from '@grafana/runtime'; import { Button, LegacyForms, DataLinkInput, stylesFactory } from '@grafana/ui'; const { FormField, Switch } = LegacyForms; import { DataLinkConfig } from '../types'; import { usePrevious } from 'react-use'; -import { DataSourcePicker } from '../../../../core/components/Select/DataSourcePicker'; const getStyles = stylesFactory(() => ({ firstRow: css` diff --git a/public/app/plugins/datasource/loki/configuration/DerivedField.test.tsx b/public/app/plugins/datasource/loki/configuration/DerivedField.test.tsx index 64bf6d58c15..cc65287af2b 100644 --- a/public/app/plugins/datasource/loki/configuration/DerivedField.test.tsx +++ b/public/app/plugins/datasource/loki/configuration/DerivedField.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { DerivedField } from './DerivedField'; -import { DataSourcePicker } from '../../../../core/components/Select/DataSourcePicker'; +import { DataSourcePicker } from '@grafana/runtime'; import { DataSourceInstanceSettings } from '@grafana/data'; jest.mock('app/features/plugins/datasource_srv', () => ({ diff --git a/public/app/plugins/datasource/loki/configuration/DerivedField.tsx b/public/app/plugins/datasource/loki/configuration/DerivedField.tsx index a93b194f9bc..49e674d6941 100644 --- a/public/app/plugins/datasource/loki/configuration/DerivedField.tsx +++ b/public/app/plugins/datasource/loki/configuration/DerivedField.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from 'react'; import { css } from 'emotion'; import { Button, DataLinkInput, stylesFactory, LegacyForms } from '@grafana/ui'; import { VariableSuggestion } from '@grafana/data'; +import { DataSourcePicker } from '@grafana/runtime'; import { DerivedFieldConfig } from '../types'; -import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { usePrevious } from 'react-use'; const { Switch, FormField } = LegacyForms; diff --git a/public/app/plugins/datasource/prometheus/configuration/ExemplarSetting.tsx b/public/app/plugins/datasource/prometheus/configuration/ExemplarSetting.tsx index 859b3749afb..670a3d054a1 100644 --- a/public/app/plugins/datasource/prometheus/configuration/ExemplarSetting.tsx +++ b/public/app/plugins/datasource/prometheus/configuration/ExemplarSetting.tsx @@ -1,5 +1,5 @@ import { Button, InlineField, InlineSwitch, Input } from '@grafana/ui'; -import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; +import { DataSourcePicker } from '@grafana/runtime'; import { css } from 'emotion'; import React, { useState } from 'react'; import { ExemplarTraceIdDestination } from '../types';