import { css, cx } from '@emotion/css'; import { useCallback, useRef } from 'react'; import * as React from 'react'; import { Observable } from 'rxjs'; import { DataSourceInstanceSettings, DataSourceJsonData, DataSourceRef, GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Trans } from '@grafana/i18n'; import { config, getTemplateSrv, useFavoriteDatasources } from '@grafana/runtime'; import { useStyles2, useTheme2 } from '@grafana/ui'; import { useDatasources, useKeyboardNavigatableList, useRecentlyUsedDataSources } from '../../hooks'; import { AddNewDataSourceButton } from './AddNewDataSourceButton'; import { DataSourceCard } from './DataSourceCard'; import { getDataSourceCompareFn, isDataSourceMatch } from './utils'; /** * Component props description for the {@link DataSourceList} * * @internal */ export interface DataSourceListProps { className?: string; onChange: (ds: DataSourceInstanceSettings) => void; current: DataSourceRef | DataSourceInstanceSettings | string | null | undefined; /** Would be nicer if these parameters were part of a filtering object */ tracing?: boolean; mixed?: boolean; dashboard?: boolean; metrics?: boolean; type?: string | string[]; annotations?: boolean; variables?: boolean; alerting?: boolean; pluginId?: string; /** If true,we show only DSs with logs; and if true, pluginId shouldnt be passed in */ logs?: boolean; width?: number; keyboardEvents?: Observable; inputId?: string; filter?: (dataSource: DataSourceInstanceSettings) => boolean; onClear?: () => void; onClickEmptyStateCTA?: () => void; enableKeyboardNavigation?: boolean; dataSources?: Array>; } export function DataSourceList(props: DataSourceListProps) { const containerRef = useRef(null); const [navigatableProps, selectedItemCssSelector] = useKeyboardNavigatableList({ keyboardEvents: props.keyboardEvents, containerRef: containerRef, }); const theme = useTheme2(); const styles = getStyles(theme, selectedItemCssSelector); const { className, current, onChange, enableKeyboardNavigation, onClickEmptyStateCTA } = props; const dataSources = props.dataSources || useDatasources({ alerting: props.alerting, annotations: props.annotations, dashboard: props.dashboard, logs: props.logs, metrics: props.metrics, mixed: props.mixed, pluginId: props.pluginId, tracing: props.tracing, type: props.type, variables: props.variables, }); const [recentlyUsedDataSources, pushRecentlyUsedDataSource] = useRecentlyUsedDataSources(); const favoriteDataSourcesHook = config.featureToggles.favoriteDatasources ? useFavoriteDatasources() : null; const storedFavoriteDataSources = favoriteDataSourcesHook?.initialFavoriteDataSources; const isFavoriteDatasource = favoriteDataSourcesHook?.isFavoriteDatasource; const toggleFavoriteDatasource = useCallback( (ds: DataSourceInstanceSettings) => { if (!favoriteDataSourcesHook) { return; } const { isFavoriteDatasource, addFavoriteDatasource, removeFavoriteDatasource } = favoriteDataSourcesHook; if (isFavoriteDatasource(ds.uid)) { removeFavoriteDatasource(ds); } else { addFavoriteDatasource(ds); } }, [favoriteDataSourcesHook] ); const filteredDataSources = props.filter ? dataSources.filter(props.filter) : dataSources; return (
{filteredDataSources.length === 0 && ( )} {filteredDataSources .sort( getDataSourceCompareFn( current, recentlyUsedDataSources, getDataSourceVariableIDs(), storedFavoriteDataSources ) ) .map((ds) => ( { pushRecentlyUsedDataSource(ds); onChange(ds); }} selected={isDataSourceMatch(ds, current)} isFavorite={isFavoriteDatasource ? isFavoriteDatasource(ds.uid) : undefined} onToggleFavorite={toggleFavoriteDatasource} {...(enableKeyboardNavigation ? navigatableProps : {})} /> ))}
); } function EmptyState({ className, onClickCTA }: { className?: string; onClickCTA?: () => void }) { const styles = useStyles2(getEmptyStateStyles); return (

No data sources found

); } function getEmptyStateStyles(theme: GrafanaTheme2) { return { container: css({ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', }), message: css({ marginBottom: theme.spacing(3), }), }; } function getDataSourceVariableIDs() { const templateSrv = getTemplateSrv(); /** Unforunately there is no easy way to identify data sources that are variables. The uid of the data source will be the name of the variable in a templating syntax $([name]) **/ return templateSrv .getVariables() .filter((v) => v.type === 'datasource') .map((v) => `\${${v.id}}`); } function getStyles(theme: GrafanaTheme2, selectedItemCssSelector: string) { return { container: css({ display: 'flex', flexDirection: 'column', padding: theme.spacing(0.5), [`${selectedItemCssSelector}`]: { backgroundColor: theme.colors.background.secondary, }, }), emptyState: css({ height: '100%', flex: 1, }), }; }