diff --git a/e2e/suite1/specs/panelEdit_transforms.spec.ts b/e2e/suite1/specs/panelEdit_transforms.spec.ts index 49e551c77ad..48cf725def1 100644 --- a/e2e/suite1/specs/panelEdit_transforms.spec.ts +++ b/e2e/suite1/specs/panelEdit_transforms.spec.ts @@ -15,7 +15,7 @@ e2e.scenario({ e2e.components.Tab.title('Transform').should('be.visible').click(); - e2e.components.TransformTab.newTransform('Reduce').should('be.visible').click(); + e2e.components.TransformTab.newTransform('Reduce').scrollIntoView().should('be.visible').click(); e2e.components.Transforms.Reduce.calculationsLabel().should('be.visible'); }, diff --git a/packages/grafana-data/src/transformations/standardTransformersRegistry.ts b/packages/grafana-data/src/transformations/standardTransformersRegistry.ts index 62a804ff641..9733fc5bc2c 100644 --- a/packages/grafana-data/src/transformations/standardTransformersRegistry.ts +++ b/packages/grafana-data/src/transformations/standardTransformersRegistry.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { DataFrame, DataTransformerInfo } from '../types'; +import { DataFrame, DataTransformerInfo, PluginState } from '../types'; import { Registry, RegistryItem } from '../utils/Registry'; export interface TransformerUIProps { @@ -19,6 +19,15 @@ export interface TransformerRegistryItem extends RegistryItem { * Object describing transformer configuration */ transformation: DataTransformerInfo; + + /** + * Optional feature state + */ + state?: PluginState; + + /** Markdown with more detailed description and help */ + help?: string; + /** * React component used as UI for the transformer */ diff --git a/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.mdx b/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.mdx deleted file mode 100644 index e0ac489850e..00000000000 --- a/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.mdx +++ /dev/null @@ -1,10 +0,0 @@ -import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks'; -import { AlphaNotice } from './AlphaNotice'; - - - -# AlphaNotice - -Used to indicate plugin state - Alpha, Beta or Deprecated. - - diff --git a/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.story.tsx b/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.story.tsx deleted file mode 100644 index 67b5236ba5a..00000000000 --- a/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.story.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { PluginState } from '@grafana/data'; -import { AlphaNotice } from './AlphaNotice'; -import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory'; -import mdx from './AlphaNotice.mdx'; - -export default { - title: 'Overlays/AlphaNotice', - component: AlphaNotice, - decorators: [withCenteredStory, withHorizontallyCenteredStory], - parameters: { - docs: { - page: mdx, - }, - }, -}; - -export const basic = () => { - return ; -}; diff --git a/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.tsx b/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.tsx deleted file mode 100644 index 26379a94135..00000000000 --- a/packages/grafana-ui/src/components/AlphaNotice/AlphaNotice.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { FC, useContext } from 'react'; -import { css, cx } from '@emotion/css'; -import { ThemeContext } from '../../index'; -import { PluginState } from '@grafana/data'; -import { Icon } from '../Icon/Icon'; - -export interface Props { - state?: PluginState; - text?: string; - className?: string; -} - -export const AlphaNotice: FC = ({ state, text, className }) => { - const tooltipContent = text || 'This feature is a work in progress and updates may include breaking changes'; - const theme = useContext(ThemeContext); - - const styles = cx( - className, - css` - background: ${theme.colors.primary.transparent}; - color: ${theme.colors.text.secondary}; - white-space: nowrap; - border-radius: 3px; - text-shadow: none; - font-size: ${theme.typography.size.sm}; - padding: 0 8px; - cursor: help; - display: inline-block; - ` - ); - - return ( -
- {state} -
- ); -}; diff --git a/packages/grafana-ui/src/components/Card/Card.tsx b/packages/grafana-ui/src/components/Card/Card.tsx index 1132c9d5f01..50b7800474d 100644 --- a/packages/grafana-ui/src/components/Card/Card.tsx +++ b/packages/grafana-ui/src/components/Card/Card.tsx @@ -1,4 +1,4 @@ -import React, { memo, cloneElement, FC, ReactNode, useCallback } from 'react'; +import React, { memo, cloneElement, FC, ReactNode } from 'react'; import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { useTheme2, stylesFactory } from '../../themes'; @@ -52,13 +52,14 @@ export const Card: CardInterface = ({ heading, description, disabled, href, onCl const hasActions = Boolean(actions || secondaryActions); const disableHover = disabled || (!onClick && !href); const disableEvents = disabled && !actions; - - const onCardClick = useCallback(() => (disableHover ? () => {} : onClick?.()), [disableHover, onClick]); + const onCardClick = onClick && !disabled ? onClick : undefined; + const onEnterKey = onClick && !disabled ? getEnterKeyHandler(onClick) : undefined; return ( void) { + return (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + onClick(); + } + }; +} + /** * @public */ diff --git a/packages/grafana-ui/src/components/IconButton/IconButton.tsx b/packages/grafana-ui/src/components/IconButton/IconButton.tsx index d057b097e7b..6b745461282 100644 --- a/packages/grafana-ui/src/components/IconButton/IconButton.tsx +++ b/packages/grafana-ui/src/components/IconButton/IconButton.tsx @@ -61,9 +61,9 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, size: IconSize, variant: let iconColor = theme.colors.text.primary; if (variant === 'primary') { - iconColor = theme.colors.primary.main; + iconColor = theme.colors.primary.text; } else if (variant === 'destructive') { - iconColor = theme.colors.error.main; + iconColor = theme.colors.error.text; } return { diff --git a/packages/grafana-ui/src/components/InfoBox/panelArt_dark.svg b/packages/grafana-ui/src/components/InfoBox/panelArt_dark.svg deleted file mode 100644 index 82a26c49b84..00000000000 --- a/packages/grafana-ui/src/components/InfoBox/panelArt_dark.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/grafana-ui/src/components/InfoBox/panelArt_light.svg b/packages/grafana-ui/src/components/InfoBox/panelArt_light.svg deleted file mode 100644 index 69a1a4611c1..00000000000 --- a/packages/grafana-ui/src/components/InfoBox/panelArt_light.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 11e94eb82e6..b20cff3be39 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -1,5 +1,5 @@ export { Icon } from './Icon/Icon'; -export { IconButton } from './IconButton/IconButton'; +export { IconButton, IconButtonVariant } from './IconButton/IconButton'; export { ConfirmButton } from './ConfirmButton/ConfirmButton'; export { DeleteButton } from './ConfirmButton/DeleteButton'; export { Tooltip, PopoverContent } from './Tooltip/Tooltip'; @@ -145,7 +145,6 @@ export { withErrorBoundary, } from './ErrorBoundary/ErrorBoundary'; export { ErrorWithStack } from './ErrorBoundary/ErrorWithStack'; -export { AlphaNotice } from './AlphaNotice/AlphaNotice'; export { DataSourceHttpSettings } from './DataSourceSettings/DataSourceHttpSettings'; export { TLSAuthSettings } from './DataSourceSettings/TLSAuthSettings'; export { CertificationKey } from './DataSourceSettings/CertificationKey'; diff --git a/packages/grafana-ui/src/themes/GlobalStyles/GlobalStyles.tsx b/packages/grafana-ui/src/themes/GlobalStyles/GlobalStyles.tsx index f66847dcf02..6ac89b14bc1 100644 --- a/packages/grafana-ui/src/themes/GlobalStyles/GlobalStyles.tsx +++ b/packages/grafana-ui/src/themes/GlobalStyles/GlobalStyles.tsx @@ -5,6 +5,7 @@ import { getElementStyles } from './elements'; import { getCardStyles } from './card'; import { getAgularPanelStyles } from './angularPanelStyles'; import { getPageStyles } from './page'; +import { getMarkdownStyles } from './markdownStyles'; /** @internal */ export function GlobalStyles() { @@ -12,7 +13,13 @@ export function GlobalStyles() { return ( ); } diff --git a/packages/grafana-ui/src/themes/GlobalStyles/elements.ts b/packages/grafana-ui/src/themes/GlobalStyles/elements.ts index d7878bf6d36..1d4db822f4b 100644 --- a/packages/grafana-ui/src/themes/GlobalStyles/elements.ts +++ b/packages/grafana-ui/src/themes/GlobalStyles/elements.ts @@ -156,6 +156,6 @@ export function getVariantStyles(variant: ThemeTypographyVariant) { font-weight: ${variant.fontWeight}; letter-spacing: ${variant.letterSpacing}; font-family: ${variant.fontFamily}; - margin-bottom: 0.35em; + margin-bottom: 0.45em; `; } diff --git a/packages/grafana-ui/src/themes/GlobalStyles/markdownStyles.ts b/packages/grafana-ui/src/themes/GlobalStyles/markdownStyles.ts new file mode 100644 index 00000000000..41ae1a23ea9 --- /dev/null +++ b/packages/grafana-ui/src/themes/GlobalStyles/markdownStyles.ts @@ -0,0 +1,8 @@ +import { css } from '@emotion/react'; +import { GrafanaTheme2 } from '@grafana/data'; + +export function getMarkdownStyles(theme: GrafanaTheme2) { + return css` + // TODO copy from _utils.scss + `; +} diff --git a/public/app/core/components/QueryOperationRow/OperationRowHelp.tsx b/public/app/core/components/QueryOperationRow/OperationRowHelp.tsx new file mode 100644 index 00000000000..874e900038e --- /dev/null +++ b/public/app/core/components/QueryOperationRow/OperationRowHelp.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { css, cx } from '@emotion/css'; +import { GrafanaTheme2, renderMarkdown } from '@grafana/data'; +import { useStyles2 } from '@grafana/ui'; + +export interface Props extends React.HTMLAttributes { + children?: React.ReactNode; + markdown?: string; + onRemove?: () => void; +} + +export const OperationRowHelp = React.memo( + React.forwardRef(({ className, children, markdown, onRemove, ...otherProps }, ref) => { + const styles = useStyles2(getStyles); + + return ( +
+ {markdown && markdownHelper(markdown)} + {children} +
+ ); + }) +); + +function markdownHelper(markdown: string) { + const helpHtml = renderMarkdown(markdown); + return
; +} + +OperationRowHelp.displayName = 'OperationRowHelp'; + +const getStyles = (theme: GrafanaTheme2) => { + const borderRadius = theme.shape.borderRadius(); + + return { + wrapper: css` + padding: ${theme.spacing(2)}; + border: 2px solid ${theme.colors.background.secondary}; + border-top: none; + border-radius: 0 0 ${borderRadius} ${borderRadius}; + position: relative; + top: -4px; + `, + }; +}; diff --git a/public/app/core/components/QueryOperationRow/QueryOperationAction.tsx b/public/app/core/components/QueryOperationRow/QueryOperationAction.tsx index 59108217d67..3f7f25806be 100644 --- a/public/app/core/components/QueryOperationRow/QueryOperationAction.tsx +++ b/public/app/core/components/QueryOperationRow/QueryOperationAction.tsx @@ -1,7 +1,7 @@ -import { IconButton, IconName, stylesFactory, useTheme } from '@grafana/ui'; +import { IconButton, IconName, useStyles2 } from '@grafana/ui'; import React from 'react'; -import { css } from '@emotion/css'; -import { GrafanaTheme } from '@grafana/data'; +import { css, cx } from '@emotion/css'; +import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; interface QueryOperationActionProps { @@ -9,37 +9,55 @@ interface QueryOperationActionProps { title: string; onClick: (e: React.MouseEvent) => void; disabled?: boolean; + active?: boolean; } -export const QueryOperationAction: React.FC = ({ icon, disabled, title, ...otherProps }) => { - const theme = useTheme(); - const styles = getStyles(theme); +export const QueryOperationAction: React.FC = ({ + icon, + active, + disabled, + title, + onClick, +}) => { + const styles = useStyles2(getStyles); - const onClick = (e: React.MouseEvent) => { - if (!disabled) { - otherProps.onClick(e); - } - }; return ( - +
+ +
); }; QueryOperationAction.displayName = 'QueryOperationAction'; -const getStyles = stylesFactory((theme: GrafanaTheme) => { +const getStyles = (theme: GrafanaTheme2) => { return { icon: css` - color: ${theme.colors.textWeak}; + display: flex; + position: relative; + color: ${theme.colors.text.secondary}; + `, + active: css` + &::before { + display: block; + content: ' '; + position: absolute; + left: -1px; + right: 2px; + height: 3px; + border-radius: 2px; + bottom: -8px; + background-image: ${theme.colors.gradients.brandHorizontal} !important; + } `, }; -}); +}; diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx index 277becf2506..2f64bf498ec 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { DataFrame, DataTransformerConfig, TransformerRegistryItem } from '@grafana/data'; import { HorizontalGroup } from '@grafana/ui'; @@ -9,6 +9,9 @@ import { } from 'app/core/components/QueryOperationRow/QueryOperationRow'; import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction'; import { TransformationsEditorTransformation } from './types'; +import { PluginStateInfo } from 'app/features/plugins/PluginStateInfo'; +import { useToggle } from 'react-use'; +import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp'; interface TransformationOperationRowProps { id: string; @@ -29,20 +32,20 @@ export const TransformationOperationRow: React.FC { - const [showDebug, setShowDebug] = useState(false); + const [showDebug, toggleDebug] = useToggle(false); + const [showHelp, toggleHelp] = useToggle(false); const renderActions = ({ isOpen }: QueryOperationRowRenderProps) => { return ( + {uiConfig.state && } { - setShowDebug(!showDebug); - }} + title="Show/hide transform help" + icon="info-circle" + onClick={toggleHelp} + active={showHelp} /> - + onRemove(index)} /> ); @@ -50,6 +53,7 @@ export const TransformationOperationRow: React.FC + {showHelp && } ); }; + +function prepMarkdown(uiConfig: TransformerRegistryItem) { + let helpMarkdown = uiConfig.help ?? uiConfig.description; + + return ` +${helpMarkdown} + + +Read more on the documentation site + +`; +} diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx index a028e13d775..40880a97b1b 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx @@ -10,6 +10,7 @@ import { Input, IconButton, useStyles2, + Card, } from '@grafana/ui'; import { DataFrame, @@ -19,8 +20,8 @@ import { PanelData, SelectableValue, standardTransformersRegistry, + TransformerRegistryItem, } from '@grafana/data'; -import { Card, CardProps } from '../../../../core/components/Card/Card'; import { css } from '@emotion/css'; import { selectors } from '@grafana/e2e-selectors'; import { Unsubscribable } from 'rxjs'; @@ -32,6 +33,7 @@ import { TransformationsEditorTransformation } from './types'; import { PanelNotSupported } from '../PanelEditor/PanelNotSupported'; import { AppNotificationSeverity } from '../../../../types'; import { LocalStorageValueProvider } from 'app/core/components/LocalStorageValueProvider'; +import { PluginStateInfo } from 'app/features/plugins/PluginStateInfo'; const LOCAL_STORAGE_KEY = 'dashboard.components.TransformationEditor.featureInfoBox.isDismissed'; @@ -214,13 +216,15 @@ class UnThemedTransformationsEditor extends React.PureComponent (a.name > b.name ? 1 : b.name > a.name ? -1 : 0)); + if (search) { const lower = search.toLowerCase(); const filtered = xforms.filter((t) => { const txt = (t.name + t.description).toLowerCase(); return txt.indexOf(lower) >= 0; }); + suffix = ( <> {filtered.length} / {xforms.length}    @@ -239,6 +243,7 @@ class UnThemedTransformationsEditor extends React.PureComponent { onDismiss(true); }} - severity="info" >

Transformations allow you to join, calculate, re-order, hide, and rename your query results before @@ -306,10 +311,7 @@ class UnThemedTransformationsEditor extends React.PureComponentSelect} - ariaLabel={selectors.components.TransformTab.newTransform(t.name)} + transform={t} onClick={() => { this.onTransformationAdd({ value: t.id }); }} @@ -363,28 +365,37 @@ class UnThemedTransformationsEditor extends React.PureComponent = (props) => { +interface TransformationCardProps { + transform: TransformerRegistryItem; + onClick: () => void; +} + +function TransformationCard({ transform, onClick }: TransformationCardProps) { const styles = useStyles2(getStyles); - return ; -}; + return ( + + {transform.description} + {transform.state && ( + + + + )} + + ); +} const getStyles = (theme: GrafanaTheme2) => { return { card: css` - background: ${theme.colors.background.secondary}; - width: 100%; - border: none; - padding: ${theme.spacing(1)}; + margin: 0; - // 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.action.hover}; - box-shadow: none; - border: none; + > div { + padding: ${theme.spacing(1)}; } `, }; diff --git a/public/app/features/dashboard/components/VizTypePicker/PanelTypeCard.tsx b/public/app/features/dashboard/components/VizTypePicker/PanelTypeCard.tsx index 58b2f7d3704..f9560d02fda 100644 --- a/public/app/features/dashboard/components/VizTypePicker/PanelTypeCard.tsx +++ b/public/app/features/dashboard/components/VizTypePicker/PanelTypeCard.tsx @@ -1,8 +1,9 @@ import React, { MouseEventHandler } from 'react'; import { GrafanaTheme2, isUnsignedPluginSignature, PanelPluginMeta, PluginState } from '@grafana/data'; -import { Badge, BadgeProps, IconButton, PluginSignatureBadge, useStyles2 } from '@grafana/ui'; +import { IconButton, PluginSignatureBadge, useStyles2 } from '@grafana/ui'; import { css, cx } from '@emotion/css'; import { selectors } from '@grafana/e2e-selectors'; +import { PluginStateInfo } from 'app/features/plugins/PluginStateInfo'; interface Props { isCurrent: boolean; @@ -142,42 +143,11 @@ interface PanelPluginBadgeProps { } const PanelPluginBadge: React.FC = ({ plugin }) => { - const display = getPanelStateBadgeDisplayModel(plugin); - if (isUnsignedPluginSignature(plugin.signature)) { return ; } - if (!display) { - return null; - } - - return ; + return ; }; -function getPanelStateBadgeDisplayModel(panel: PanelPluginMeta): BadgeProps | null { - switch (panel.state) { - case PluginState.deprecated: - return { - text: 'Deprecated', - color: 'red', - tooltip: `${panel.name} Panel is deprecated`, - }; - case PluginState.alpha: - return { - text: 'Alpha', - color: 'blue', - tooltip: `${panel.name} Panel is experimental`, - }; - case PluginState.beta: - return { - text: 'Beta', - color: 'blue', - tooltip: `${panel.name} Panel is in beta`, - }; - default: - return null; - } -} - PanelPluginBadge.displayName = 'PanelPluginBadge'; diff --git a/public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx b/public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx index 460c151b5f3..6af2999f3a1 100644 --- a/public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx +++ b/public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx @@ -61,9 +61,7 @@ describe('Render', () => { render(); - expect( - screen.getByTitle('Beta Plugin: There could be bugs and minor breaking changes to this plugin') - ).toBeInTheDocument(); + expect(screen.getByTitle('This feature is close to complete but not fully tested')).toBeInTheDocument(); }); it('should render alpha info text if plugin state is alpha', () => { @@ -73,7 +71,7 @@ describe('Render', () => { render(); expect( - screen.getByTitle('Alpha Plugin: This plugin is a work in progress and updates may include breaking changes') + screen.getByTitle('This feature is experimental and future updates might not be backward compatible') ).toBeInTheDocument(); }); diff --git a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx index b8605f417da..47b215dd1d4 100644 --- a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx +++ b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx @@ -23,7 +23,7 @@ import { StoreState } from 'app/types/'; import { DataSourceSettings } from '@grafana/data'; import { Alert, Button, LinkButton } from '@grafana/ui'; import { getDataSourceLoadingNav, buildNavModel, getDataSourceNav } from '../state/navModel'; -import PluginStateinfo from 'app/features/plugins/PluginStateInfo'; +import { PluginStateInfo } from 'app/features/plugins/PluginStateInfo'; import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers'; import { selectors } from '@grafana/e2e-selectors'; import { CloudInfoBox } from './CloudInfoBox'; @@ -229,7 +229,7 @@ export class DataSourceSettingsPage extends PureComponent {

)} diff --git a/public/app/features/plugins/PluginStateInfo.tsx b/public/app/features/plugins/PluginStateInfo.tsx index 401f9a5b587..b37f6d392d1 100644 --- a/public/app/features/plugins/PluginStateInfo.tsx +++ b/public/app/features/plugins/PluginStateInfo.tsx @@ -1,29 +1,50 @@ import React, { FC } from 'react'; -import { AlphaNotice } from '@grafana/ui'; +import { Badge, BadgeProps } from '@grafana/ui'; import { PluginState } from '@grafana/data'; interface Props { state?: PluginState; } -function getPluginStateInfoText(state?: PluginState): string | null { - switch (state) { - case PluginState.alpha: - return 'Alpha Plugin: This plugin is a work in progress and updates may include breaking changes'; - case PluginState.beta: - return 'Beta Plugin: There could be bugs and minor breaking changes to this plugin'; - } - return null; -} +export const PluginStateInfo: FC = (props) => { + const display = getFeatureStateInfo(props.state); -const PluginStateinfo: FC = (props) => { - const text = getPluginStateInfoText(props.state); - - if (!text) { + if (!display) { return null; } - return ; + return ( + + ); }; -export default PluginStateinfo; +function getFeatureStateInfo(state?: PluginState): BadgeProps | null { + switch (state) { + case PluginState.deprecated: + return { + text: 'Deprecated', + color: 'red', + tooltip: `This feature is deprecated and will be removed in a future release`, + }; + case PluginState.alpha: + return { + text: 'Alpha', + color: 'blue', + tooltip: `This feature is experimental and future updates might not be backward compatible`, + }; + case PluginState.beta: + return { + text: 'Beta', + color: 'blue', + tooltip: `This feature is close to complete but not fully tested`, + }; + default: + return null; + } +} diff --git a/public/app/features/query/components/QueryEditorRow.tsx b/public/app/features/query/components/QueryEditorRow.tsx index 6cb7e8179ed..0c49475ad48 100644 --- a/public/app/features/query/components/QueryEditorRow.tsx +++ b/public/app/features/query/components/QueryEditorRow.tsx @@ -6,7 +6,7 @@ import { has, cloneDeep } from 'lodash'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { AngularComponent, getAngularLoader } from '@grafana/runtime'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { ErrorBoundaryAlert, HorizontalGroup, InfoBox } from '@grafana/ui'; +import { ErrorBoundaryAlert, HorizontalGroup } from '@grafana/ui'; import { DataQuery, DataSourceApi, @@ -28,6 +28,7 @@ import { QueryOperationAction } from 'app/core/components/QueryOperationRow/Quer import { DashboardModel } from '../../dashboard/state/DashboardModel'; import { selectors } from '@grafana/e2e-selectors'; import { PanelModel } from 'app/features/dashboard/state'; +import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp'; interface Props { data: PanelData; @@ -274,7 +275,7 @@ export class QueryEditorRow extends PureComponent { const { query, hideDisableQuery = false } = this.props; - const { hasTextEditMode, datasource } = this.state; + const { hasTextEditMode, datasource, showingHelp } = this.state; const isDisabled = query.hide; const hasEditorHelp = datasource?.components?.QueryEditorHelp; @@ -282,7 +283,12 @@ export class QueryEditorRow extends PureComponent {hasEditorHelp && ( - + )} {hasTextEditMode && ( extends PureComponent ) : null} @@ -354,12 +361,12 @@ export class QueryEditorRow extends PureComponent {showingHelp && DatasourceCheatsheet && ( - + this.onClickExample(query)} datasource={datasource} /> - +
)} {editor} diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx index 958dc968ed0..b5d8261f435 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogsCheatSheet.tsx @@ -258,7 +258,7 @@ export default class LogsCheatSheet extends PureComponent< render() { return (
-

CloudWatch Logs Cheat Sheet

+

CloudWatch Logs cheat sheet

{CLIQ_EXAMPLES.map((cat, i) => (
{cat.category}
diff --git a/public/sass/base/_type.scss b/public/sass/base/_type.scss index 5ebb113bc26..1537734ec8a 100644 --- a/public/sass/base/_type.scss +++ b/public/sass/base/_type.scss @@ -214,13 +214,13 @@ a.external-link { } table { - margin-bottom: $line-height-base; + margin-bottom: $spacer; } table { td, th { - padding: $spacer * 0.5 $spacer; + padding: $space-xs $space-sm; } th { font-weight: $font-weight-semi-bold;