mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
10
public/app/core/utils/docsLinks.ts
Normal file
10
public/app/core/utils/docsLinks.ts
Normal 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];
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
4
public/app/types/svg.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
declare module '*.svg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
1
public/test/mocks/svg.ts
Normal file
1
public/test/mocks/svg.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const svg = 'svg';
|
||||
Reference in New Issue
Block a user