mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: moving the DataSourcePicker to grafana/runtime so it can be reused in plugins (#31628)
* moved the datasource picker to grafana-runtime. * fixed imports. * added e2e selectors as an external package. * adding react as external package. * exposing dependent types for DataSourcePicker. * added docs for ui components. * moving component to components.
This commit is contained in:
parent
8fafe95728
commit
69201bbf8c
@ -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';
|
||||
|
11
packages/grafana-data/src/types/pluginSignature.ts
Normal file
11
packages/grafana-data/src/types/pluginSignature.ts
Normal file
@ -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;
|
||||
}
|
@ -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"
|
||||
|
@ -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/,
|
||||
|
@ -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<Props, State> {
|
||||
/**
|
||||
* 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<DataSourcePickerProps, DataSourcePickerState> {
|
||||
dataSourceSrv = getDataSourceSrv();
|
||||
|
||||
static defaultProps: Partial<Props> = {
|
||||
static defaultProps: Partial<DataSourcePickerProps> = {
|
||||
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<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
private getCurrentValue() {
|
||||
private getCurrentValue(): SelectableValue<string> | 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<Props, State> {
|
||||
|
||||
return {
|
||||
label: (current ?? 'no name') + ' - not found',
|
||||
value: current,
|
||||
value: current === null ? undefined : current,
|
||||
imgUrl: '',
|
||||
hideText: hideTextValue,
|
||||
};
|
@ -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';
|
||||
|
@ -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 <PluginSignatureBadge status={status} />;
|
||||
};
|
@ -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<HTMLDivElement> {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface PluginSignatureBadgeProps extends HTMLAttributes<HTMLDivElement> {
|
||||
status?: PluginSignatureStatus;
|
||||
}
|
||||
|
||||
export const PluginSignatureBadge: React.FC<Props> = ({ status, ...otherProps }) => {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const PluginSignatureBadge: React.FC<PluginSignatureBadgeProps> = ({ status, ...otherProps }) => {
|
||||
const display = getSignatureDisplayModel(status);
|
||||
return (
|
||||
<Badge
|
||||
@ -19,22 +25,7 @@ export const PluginSignatureBadge: React.FC<Props> = ({ 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';
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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';
|
||||
|
@ -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<DispatchProps, OwnProps> = {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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<QueryVariableModel> {}
|
||||
|
||||
|
@ -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`
|
||||
|
@ -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', () => ({
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
Loading…
Reference in New Issue
Block a user