Transforms: Adds beta notice and updates transform descriptions (#24158)

* Transforms: Adds beta notice and updates transform descriptions

* Rename organize fields

* Webpack - enable images import

* Introduce FeatureState type

* Alow Container component grow/shrink config

* Enable svg import in main app

* Jest + webpack for svgs

* InfoBox refactor (+ added feature info box), Badge component introduced

* Update packages/grafana-ui/src/components/TransformersUI/FilterByNameTransformerEditor.tsx

Co-authored-by: Carl Bergquist <carl@grafana.com>

* Minor fixes

* Update packages/grafana-ui/src/components/TransformersUI/OrganizeFieldsTransformerEditor.tsx

Co-authored-by: Carl Bergquist <carl@grafana.com>

* Update packages/grafana-ui/src/components/TransformersUI/SeriesToFieldsTransformerEditor.tsx

Co-authored-by: Carl Bergquist <carl@grafana.com>

* fix typo

* Build storybook fixed

* Fix padding

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Carl Bergquist <carl@grafana.com>
This commit is contained in:
Torkel Ödegaard
2020-05-04 15:05:31 +02:00
committed by GitHub
parent 0fe9e7e242
commit 92a16d2e10
31 changed files with 585 additions and 184 deletions

View File

@@ -0,0 +1,10 @@
import { DocsId } from '@grafana/data';
// TODO: Documentation links
const DOCS_LINKS: Record<DocsId, string> = {
[DocsId.Transformations]: 'https://docs.grafana.com',
[DocsId.FieldConfig]: 'https://docs.grafana.com',
[DocsId.FieldConfigOverrides]: 'https://docs.grafana.com',
};
export const getDocsLink = (id: DocsId) => DOCS_LINKS[id];

View File

@@ -2,18 +2,20 @@ import React, { useCallback } from 'react';
import cloneDeep from 'lodash/cloneDeep';
import {
DataFrame,
FeatureState,
FieldConfigPropertyItem,
FieldConfigSource,
PanelPlugin,
SelectableValue,
VariableSuggestionsScope,
} from '@grafana/data';
import { Container, Counter, Field, fieldMatchersUI, Label, ValuePicker } from '@grafana/ui';
import { Container, Counter, FeatureInfoBox, Field, fieldMatchersUI, Label, useTheme, ValuePicker } from '@grafana/ui';
import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv';
import { OverrideEditor } from './OverrideEditor';
import groupBy from 'lodash/groupBy';
import { OptionsGroup } from './OptionsGroup';
import { selectors } from '@grafana/e2e-selectors';
import { css } from 'emotion';
interface Props {
plugin: PanelPlugin;
@@ -27,6 +29,8 @@ interface Props {
* Expects the container div to have size set and will fill it 100%
*/
export const OverrideFieldConfigEditor: React.FC<Props> = props => {
const theme = useTheme();
const { config } = props;
const onOverrideChange = (index: number, override: any) => {
const { config } = props;
let overrides = cloneDeep(config.overrides);
@@ -104,6 +108,19 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
return (
<div aria-label={selectors.components.OverridesConfigEditor.content}>
{config.overrides.length === 0 && (
<FeatureInfoBox
title="Overrides"
featureState={FeatureState.beta}
// url={getDocsLink(DocsId.FieldConfigOverrides)}
className={css`
margin: ${theme.spacing.md};
`}
>
Field options overrides give you a fine grained control over how your data is displayed.
</FeatureInfoBox>
)}
{renderOverrides()}
{renderAddOverride()}
</div>

View File

@@ -1,8 +1,18 @@
import React from 'react';
import { Button, Container, CustomScrollbar, stylesFactory, useTheme, ValuePicker, VerticalGroup } from '@grafana/ui';
import {
Button,
Container,
CustomScrollbar,
FeatureInfoBox,
stylesFactory,
useTheme,
ValuePicker,
VerticalGroup,
} from '@grafana/ui';
import {
DataFrame,
DataTransformerConfig,
FeatureState,
GrafanaTheme,
SelectableValue,
standardTransformersRegistry,
@@ -114,13 +124,23 @@ export class TransformationsEditor extends React.PureComponent<Props> {
renderNoAddedTransformsState() {
return (
<>
<p className="muted">
Transformations allow you to combine, re-order, hide and rename specific parts the the data set before being
visualized. <br />
Choose one of the transformations below to start with:
</p>
<VerticalGroup spacing={'lg'}>
<Container grow={1}>
<FeatureInfoBox
title="Transformations"
featureState={FeatureState.beta}
// url={getDocsLink(DocsId.Transformations)}
>
<p>
Transformations allow you to join, calculate, re-order, hide and rename your query results before being
visualized. <br />
Many transforms are not suitable if your using the Graph visualisation as it currently only supports time
series. <br />
It can help to switch to Table visualisation to understand what a transformation is doing. <br />
</p>
<p>Select one of the transformations below to start.</p>
</FeatureInfoBox>
</Container>
<VerticalGroup>
{standardTransformersRegistry.list().map(t => {
return (
@@ -136,7 +156,7 @@ export class TransformationsEditor extends React.PureComponent<Props> {
);
})}
</VerticalGroup>
</>
</VerticalGroup>
);
}
@@ -170,6 +190,11 @@ const getTransformationCardStyles = stylesFactory((theme: GrafanaTheme) => {
border: none;
padding: ${theme.spacing.sm};
// hack because these cards use classes from a very different card for some reason
.add-data-source-item-text {
font-size: ${theme.typography.size.md};
}
&:hover {
background: ${theme.colors.bg3};
box-shadow: none;

View File

@@ -1,9 +1,8 @@
import React from 'react';
import { GrafanaTheme, PanelPluginMeta, PluginState } from '@grafana/data';
import { styleMixins, stylesFactory, useTheme } from '@grafana/ui';
import { Badge, BadgeProps, styleMixins, stylesFactory, useTheme } from '@grafana/ui';
import { css, cx } from 'emotion';
import { selectors } from '@grafana/e2e-selectors';
import { PanelPluginBadge } from '../../plugins/PluginSignatureBadge';
interface Props {
isCurrent: boolean;
@@ -126,3 +125,36 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
});
export default VizTypePickerPlugin;
interface PanelPluginBadgeProps {
plugin: PanelPluginMeta;
}
const PanelPluginBadge: React.FC<PanelPluginBadgeProps> = ({ plugin }) => {
const display = getPanelStateBadgeDisplayModel(plugin);
if (plugin.state !== PluginState.deprecated && plugin.state !== PluginState.alpha) {
return null;
}
return <Badge color={display.color} text={display.text} icon={display.icon} tooltip={display.tooltip} />;
};
function getPanelStateBadgeDisplayModel(panel: PanelPluginMeta): BadgeProps {
switch (panel.state) {
case PluginState.deprecated:
return {
text: 'Deprecated',
icon: 'exclamation-triangle',
color: 'red',
tooltip: `${panel.name} panel is deprecated`,
};
}
return {
text: 'Alpha',
icon: 'rocket',
color: 'blue',
tooltip: `${panel.name} panel is experimental`,
};
}
PanelPluginBadge.displayName = 'PanelPluginBadge';

View File

@@ -1,61 +1,17 @@
import React from 'react';
import { Icon, IconName, stylesFactory, Tooltip, useTheme } from '@grafana/ui';
import {
getColorFromHexRgbOrName,
GrafanaTheme,
PanelPluginMeta,
PluginSignatureStatus,
PluginState,
} from '@grafana/data';
import { css } from 'emotion';
import tinycolor from 'tinycolor2';
import { Badge, BadgeProps } from '@grafana/ui';
import { PluginSignatureStatus } from '@grafana/data';
interface Props {
status: PluginSignatureStatus;
}
export const PluginSignatureBadge: React.FC<Props> = ({ status }) => {
const theme = useTheme();
const display = getSignatureDisplayModel(status);
const styles = getStyles(theme, display);
return (
<Tooltip content={display.tooltip} placement="left">
<div className={styles.wrapper}>
<Icon name={display.icon} size="sm" />
<span>{display.text}</span>
</div>
</Tooltip>
);
return <Badge text={display.text} color={display.color} icon={display.icon} tooltip={display.tooltip} />;
};
interface PanelPluginBadgeProps {
plugin: PanelPluginMeta;
}
export const PanelPluginBadge: React.FC<PanelPluginBadgeProps> = ({ plugin }) => {
const theme = useTheme();
const display = getPanelStateBadgeDisplayModel(plugin);
const styles = getStyles(theme, display);
if (plugin.state !== PluginState.deprecated && plugin.state !== PluginState.alpha) {
return null;
}
return (
<div className={styles.wrapper}>
<Icon name={display.icon} size="sm" />
<span>{display.text}</span>
</div>
);
};
interface DisplayModel {
text: string;
icon: IconName;
color: string;
tooltip: string;
}
function getSignatureDisplayModel(signature: PluginSignatureStatus): DisplayModel {
function getSignatureDisplayModel(signature: PluginSignatureStatus): BadgeProps {
switch (signature) {
case PluginSignatureStatus.internal:
return { text: 'Core', icon: 'cube', color: 'blue', tooltip: 'Core plugin that is bundled with Grafana' };
@@ -80,71 +36,4 @@ function getSignatureDisplayModel(signature: PluginSignatureStatus): DisplayMode
return { text: 'Unsigned', icon: 'exclamation-triangle', color: 'red', tooltip: 'Unsigned external plugin' };
}
function getPanelStateBadgeDisplayModel(panel: PanelPluginMeta): DisplayModel {
switch (panel.state) {
case PluginState.deprecated:
return {
text: 'Deprecated',
icon: 'exclamation-triangle',
color: 'red',
tooltip: `${panel.name} panel is deprecated`,
};
}
return {
text: 'Alpha',
icon: 'rocket',
color: 'blue',
tooltip: `${panel.name} panel is experimental`,
};
}
const getStyles = stylesFactory((theme: GrafanaTheme, model: DisplayModel) => {
let sourceColor = getColorFromHexRgbOrName(model.color);
let borderColor = '';
let bgColor = '';
let textColor = '';
if (theme.isDark) {
bgColor = tinycolor(sourceColor)
.darken(38)
.toString();
borderColor = tinycolor(sourceColor)
.darken(25)
.toString();
textColor = tinycolor(sourceColor)
.lighten(45)
.toString();
} else {
bgColor = tinycolor(sourceColor)
.lighten(30)
.toString();
borderColor = tinycolor(sourceColor)
.lighten(15)
.toString();
textColor = tinycolor(sourceColor)
.darken(40)
.toString();
}
return {
wrapper: css`
font-size: ${theme.typography.size.sm};
display: inline-flex;
padding: 1px 4px;
border-radius: 3px;
margin-top: 6px;
background: ${bgColor};
border: 1px solid ${borderColor};
color: ${textColor};
> span {
position: relative;
top: 1px;
margin-left: 2px;
}
`,
};
});
PluginSignatureBadge.displayName = 'PluginSignatureBadge';

4
public/app/types/svg.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module '*.svg' {
const content: string;
export default content;
}

1
public/test/mocks/svg.ts Normal file
View File

@@ -0,0 +1 @@
export const svg = 'svg';