Alerts/InfoBox: Replaces all uses of InfoBox & FeatureInfoBox with Alert (#33352)

* Alerts/InfoBox: Replaces all uses of InfoBox & FeatureInfoBox with Alert

* Fixes some more stuff

* fixed border radius
pull/33362/head
Torkel Ödegaard 4 years ago committed by GitHub
parent 19739f4af2
commit cf2d557974
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      packages/grafana-ui/src/components/Alert/Alert.tsx
  2. 2
      packages/grafana-ui/src/components/InfoBox/FeatureInfoBox.tsx
  3. 21
      packages/grafana-ui/src/components/InfoBox/InfoBox.story.tsx
  4. 88
      packages/grafana-ui/src/components/InfoBox/InfoBox.tsx
  5. 4
      public/app/features/alerting/components/ChannelSettings.tsx
  6. 6
      public/app/features/alerting/unified/AmRoutes.tsx
  7. 6
      public/app/features/alerting/unified/Receivers.tsx
  8. 6
      public/app/features/alerting/unified/RuleEditor.tsx
  9. 15
      public/app/features/alerting/unified/RuleList.tsx
  10. 6
      public/app/features/alerting/unified/Silences.tsx
  11. 9
      public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx
  12. 54
      public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx
  13. 36
      public/app/features/dashboard/components/ShareModal/ShareExport.tsx
  14. 12
      public/app/features/dashboard/components/ShareModal/ShareGlobalPanel.tsx
  15. 120
      public/app/features/dashboard/components/ShareModal/ShareLink.tsx
  16. 16
      public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx
  17. 0
      public/app/features/dashboard/components/ShareModal/ShareSnapshotCtrl.ts
  18. 21
      public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx
  19. 57
      public/app/features/datasources/settings/CloudInfoBox.tsx
  20. 6
      public/app/features/datasources/settings/DataSourceSettingsPage.tsx
  21. 14
      public/app/plugins/datasource/grafana/components/QueryEditor.tsx
  22. 13
      public/app/plugins/panel/live/LiveChannelEditor.tsx
  23. 22
      public/app/plugins/panel/live/LivePanel.tsx

@ -1,5 +1,5 @@
import React, { FC, HTMLAttributes, ReactNode } from 'react';
import { css } from '@emotion/css';
import React, { HTMLAttributes, ReactNode } from 'react';
import { css, cx } from '@emotion/css';
import { GrafanaThemeV2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { useTheme2 } from '../../themes';
@ -18,6 +18,7 @@ export interface Props extends HTMLAttributes<HTMLDivElement> {
children?: ReactNode;
elevated?: boolean;
buttonContent?: React.ReactNode | string;
bottomSpacing?: number;
}
function getIconFromSeverity(severity: AlertVariant): string {
@ -34,19 +35,27 @@ function getIconFromSeverity(severity: AlertVariant): string {
}
}
export const Alert: FC<Props> = React.forwardRef<HTMLDivElement, Props>(
({ title, onRemove, children, buttonContent, elevated, severity = 'error', ...restProps }, ref) => {
export const Alert = React.forwardRef<HTMLDivElement, Props>(
(
{ title, onRemove, children, buttonContent, elevated, bottomSpacing, className, severity = 'error', ...restProps },
ref
) => {
const theme = useTheme2();
const styles = getStyles(theme, severity, elevated);
const styles = getStyles(theme, severity, elevated, bottomSpacing);
return (
<div ref={ref} className={styles.alert} aria-label={selectors.components.Alert.alert(severity)} {...restProps}>
<div
ref={ref}
className={cx(styles.alert, className)}
aria-label={selectors.components.Alert.alert(severity)}
{...restProps}
>
<div className={styles.icon}>
<Icon size="xl" name={getIconFromSeverity(severity) as IconName} />
</div>
<div className={styles.body}>
<div className={styles.title}>{title}</div>
{children && <div>{children}</div>}
{children && <div className={styles.content}>{children}</div>}
</div>
{/* If onRemove is specified, giving preference to onRemove */}
{onRemove && !buttonContent && (
@ -68,19 +77,21 @@ export const Alert: FC<Props> = React.forwardRef<HTMLDivElement, Props>(
Alert.displayName = 'Alert';
const getStyles = (theme: GrafanaThemeV2, severity: AlertVariant, elevated?: boolean) => {
const getStyles = (theme: GrafanaThemeV2, severity: AlertVariant, elevated?: boolean, bottomSpacing?: number) => {
const color = theme.colors[severity];
const borderRadius = theme.shape.borderRadius();
return {
alert: css`
flex-grow: 1;
position: relative;
border-radius: ${theme.shape.borderRadius()};
border-radius: ${borderRadius};
display: flex;
flex-direction: row;
align-items: stretch;
background: ${theme.colors.background.secondary};
box-shadow: ${elevated ? theme.shadows.z3 : theme.shadows.z1};
margin-bottom: ${theme.spacing(bottomSpacing ?? 2)};
&:before {
content: '';
@ -96,6 +107,7 @@ const getStyles = (theme: GrafanaThemeV2, severity: AlertVariant, elevated?: boo
icon: css`
padding: ${theme.spacing(2, 3)};
background: ${color.main};
border-radius: ${borderRadius} 0 0 ${borderRadius};
color: ${color.contrastText};
display: flex;
align-items: center;
@ -115,6 +127,10 @@ const getStyles = (theme: GrafanaThemeV2, severity: AlertVariant, elevated?: boo
overflow-wrap: break-word;
word-break: break-word;
`,
content: css`
color: ${theme.colors.text.secondary};
padding-top: ${theme.spacing(1)};
`,
buttonWrapper: css`
padding: ${theme.spacing(1)};
background: none;
@ -122,9 +138,8 @@ const getStyles = (theme: GrafanaThemeV2, severity: AlertVariant, elevated?: boo
align-items: center;
`,
close: css`
padding: ${theme.spacing(1)};
padding: ${theme.spacing(2, 1)};
background: none;
align-items: center;
display: flex;
`,
};

@ -10,6 +10,7 @@ export interface FeatureInfoBoxProps extends Omit<InfoBoxProps, 'title' | 'urlTi
featureState?: FeatureState;
}
/** @deprecated use Alert with severity info */
export const FeatureInfoBox = React.memo(
React.forwardRef<HTMLDivElement, FeatureInfoBoxProps>(({ title, featureState, ...otherProps }, ref) => {
const styles = useStyles(getFeatureInfoBoxStyles);
@ -27,6 +28,7 @@ export const FeatureInfoBox = React.memo(
return <InfoBox branded title={titleEl} urlTitle="Read documentation" ref={ref} {...otherProps} />;
})
);
FeatureInfoBox.displayName = 'FeatureInfoBox';
const getFeatureInfoBoxStyles = stylesFactory((theme: GrafanaTheme) => {

@ -1,6 +1,6 @@
import React from 'react';
import { FeatureState } from '@grafana/data';
import { InfoBox, FeatureInfoBox } from '@grafana/ui';
import { InfoBox, FeatureInfoBox, VerticalGroup } from '@grafana/ui';
import mdx from './InfoBox.mdx';
import { Story } from '@storybook/react';
import { FeatureInfoBoxProps } from './FeatureInfoBox';
@ -44,10 +44,25 @@ const defaultProps: FeatureInfoBoxProps = {
),
};
const InfoBoxTemplate: Story<InfoBoxProps> = (args) => <InfoBox {...args} />;
const InfoBoxTemplate: Story<InfoBoxProps> = (args) => {
return (
<VerticalGroup>
<div>Deprecrated component, use Alert with info severity</div>
<InfoBox {...args} />;
</VerticalGroup>
);
};
export const infoBox = InfoBoxTemplate.bind({});
infoBox.args = defaultProps;
const FeatureInfoBoxTemplate: Story<FeatureInfoBoxProps> = (args) => <FeatureInfoBox {...args}></FeatureInfoBox>;
const FeatureInfoBoxTemplate: Story<FeatureInfoBoxProps> = (args) => {
return (
<VerticalGroup>
<div>Deprecrated component, use Alert with info severity</div>
<FeatureInfoBox {...args} />
</VerticalGroup>
);
};
export const featureInfoBox = FeatureInfoBoxTemplate.bind({});
featureInfoBox.args = defaultProps;

@ -2,12 +2,8 @@ import React from 'react';
import { css, cx } from '@emotion/css';
import { GrafanaThemeV2 } from '@grafana/data';
import { Icon } from '../Icon/Icon';
import { IconButton } from '../IconButton/IconButton';
import { HorizontalGroup } from '../Layout/Layout';
import { AlertVariant } from '../Alert/Alert';
import panelArtDark from './panelArt_dark.svg';
import panelArtLight from './panelArt_light.svg';
import { stylesFactory, useTheme2 } from '../../themes';
import { Alert, AlertVariant } from '../Alert/Alert';
import { stylesFactory, useStyles2 } from '../../themes';
export interface InfoBoxProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
children: React.ReactNode;
@ -25,97 +21,33 @@ export interface InfoBoxProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
onDismiss?: () => void;
}
/**
@public
*/
/** @deprecated use Alert with severity info */
export const InfoBox = React.memo(
React.forwardRef<HTMLDivElement, InfoBoxProps>(
({ title, className, children, branded, url, urlTitle, onDismiss, severity = 'info', ...otherProps }, ref) => {
const theme = useTheme2();
const styles = getInfoBoxStyles(theme, severity);
const wrapperClassName = cx(branded ? styles.wrapperBranded : styles.wrapper, className);
const styles = useStyles2(getStyles);
return (
<div className={wrapperClassName} {...otherProps} ref={ref}>
<div>
<HorizontalGroup justify={'space-between'} align={'flex-start'}>
<div>{typeof title === 'string' ? <h4>{title}</h4> : title}</div>
{onDismiss && <IconButton name={'times'} onClick={onDismiss} />}
</HorizontalGroup>
</div>
<Alert severity={severity} className={className} {...otherProps} ref={ref} title={title as string}>
<div>{children}</div>
{url && (
<a href={url} className={styles.docsLink} target="_blank" rel="noreferrer">
<a href={url} className={cx('external-link', styles.docsLink)} target="_blank" rel="noreferrer">
<Icon name="book" /> {urlTitle || 'Read more'}
</a>
)}
</div>
</Alert>
);
}
)
);
InfoBox.displayName = 'InfoBox';
const getInfoBoxStyles = stylesFactory((theme: GrafanaThemeV2, severity: AlertVariant) => {
const color = theme.colors[severity];
InfoBox.displayName = 'InfoBox';
const getStyles = stylesFactory((theme: GrafanaThemeV2) => {
return {
wrapper: css`
position: relative;
padding: ${theme.v1.spacing.md};
background-color: ${theme.v1.colors.bg2};
border-left: 3px solid ${color.border};
margin-bottom: ${theme.v1.spacing.md};
flex-grow: 1;
color: ${theme.v1.colors.textSemiWeak};
box-shadow: ${theme.shadows.z1};
code {
font-size: ${theme.typography.size.sm};
background-color: ${theme.v1.colors.bg1};
color: ${theme.v1.colors.text};
border: 1px solid ${theme.v1.colors.border2};
border-radius: 4px;
}
p:last-child {
margin-bottom: 0;
}
&--max-lg {
max-width: ${theme.v1.breakpoints.lg};
}
`,
wrapperBranded: css`
padding: ${theme.v1.spacing.md};
border-radius: ${theme.v1.border.radius.md};
position: relative;
z-index: 0;
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url(${theme.isLight ? panelArtLight : panelArtDark});
border-radius: ${theme.v1.border.radius.md};
background-position: 50% 50%;
background-size: cover;
filter: saturate(80%);
z-index: -1;
}
p:last-child {
margin-bottom: 0;
}
`,
docsLink: css`
display: inline-block;
margin-top: ${theme.v1.spacing.md};
font-size: ${theme.v1.typography.size.sm};
color: ${theme.v1.colors.textSemiWeak};
margin-top: ${theme.spacing(2)};
`,
};
});

@ -1,5 +1,5 @@
import React, { FC } from 'react';
import { CollapsableSection, InfoBox } from '@grafana/ui';
import { Alert, CollapsableSection } from '@grafana/ui';
import { NotificationChannelOptions } from './NotificationChannelOptions';
import { NotificationSettingsProps } from './NotificationChannelForm';
import { NotificationChannelSecureFields, NotificationChannelType } from '../../../types';
@ -21,7 +21,7 @@ export const ChannelSettings: FC<Props> = ({
}) => {
return (
<CollapsableSection label={`Optional ${selectedChannel.heading}`} isOpen={false}>
{selectedChannel.info !== '' && <InfoBox>{selectedChannel.info}</InfoBox>}
{selectedChannel.info !== '' && <Alert severity="info" title={selectedChannel.info ?? ''} />}
<NotificationChannelOptions
selectedChannelOptions={selectedChannel.options.filter((o) => !o.required)}
currentFormValues={currentFormValues}

@ -1,4 +1,4 @@
import { Field, InfoBox, LoadingPlaceholder } from '@grafana/ui';
import { Alert, Field, LoadingPlaceholder } from '@grafana/ui';
import React, { FC, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
@ -28,9 +28,9 @@ const AmRoutes: FC = () => {
<br />
<br />
{error && !loading && (
<InfoBox severity="error" title={<h4>Error loading alert manager config</h4>}>
<Alert severity="error" title="Error loading alert manager config">
{error.message || 'Unknown error.'}
</InfoBox>
</Alert>
)}
{loading && <LoadingPlaceholder text="loading alert manager config..." />}
{result && !loading && !error && <pre>{JSON.stringify(result, null, 2)}</pre>}

@ -1,4 +1,4 @@
import { Field, InfoBox, LoadingPlaceholder } from '@grafana/ui';
import { Field, Alert, LoadingPlaceholder } from '@grafana/ui';
import React, { FC, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
@ -36,9 +36,9 @@ const Receivers: FC = () => {
<AlertManagerPicker current={alertManagerSourceName} onChange={setAlertManagerSourceName} />
</Field>
{error && !loading && (
<InfoBox severity="error" title={<h4>Error loading alert manager config</h4>}>
<Alert severity="error" title="Error loading alert manager config">
{error.message || 'Unknown error.'}
</InfoBox>
</Alert>
)}
{loading && <LoadingPlaceholder text="loading receivers..." />}
{result && !loading && !error && (

@ -1,4 +1,4 @@
import { Alert, Button, InfoBox, LoadingPlaceholder } from '@grafana/ui';
import { Alert, Button, LoadingPlaceholder } from '@grafana/ui';
import Page from 'app/core/components/Page/Page';
import { useCleanup } from 'app/core/hooks/useCleanup';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
@ -43,12 +43,12 @@ const ExistingRuleEditor: FC<ExistingRuleEditorProps> = ({ identifier }) => {
if (!result) {
return (
<Page.Contents>
<InfoBox severity="warning" title="Rule not found">
<Alert severity="warning" title="Rule not found">
<p>Sorry! This rule does not exist.</p>
<a href="/alerting/list">
<Button>To rule list</Button>
</a>
</InfoBox>
</Alert>
</Page.Contents>
);
}

@ -1,5 +1,5 @@
import { DataSourceInstanceSettings, GrafanaTheme, urlUtil } from '@grafana/data';
import { Icon, InfoBox, useStyles, Button, ButtonGroup, ToolbarButton } from '@grafana/ui';
import { useStyles, Button, ButtonGroup, ToolbarButton, Alert } from '@grafana/ui';
import { SerializedError } from '@reduxjs/toolkit';
import React, { FC, useEffect, useMemo } from 'react';
import { useDispatch } from 'react-redux';
@ -88,16 +88,7 @@ export const RuleList: FC = () => {
return (
<AlertingPageWrapper pageId="alert-list" isLoading={loading && !haveResults}>
{(promReqeustErrors.length || rulerRequestErrors.length || grafanaPromError) && (
<InfoBox
data-testid="cloud-rulessource-errors"
title={
<h4>
<Icon className={styles.iconError} name="exclamation-triangle" size="xl" />
Errors loading rules
</h4>
}
severity="error"
>
<Alert data-testid="cloud-rulessource-errors" title="Errors loading rules" severity="error">
{grafanaPromError && (
<div>Failed to load Grafana threshold rules state: {grafanaPromError.message || 'Unknown error.'}</div>
)}
@ -118,7 +109,7 @@ export const RuleList: FC = () => {
{error.message || 'Unknown error.'}
</div>
))}
</InfoBox>
</Alert>
)}
{!showNewAlertSplash && (
<>

@ -1,4 +1,4 @@
import { Field, InfoBox, LoadingPlaceholder } from '@grafana/ui';
import { Field, Alert, LoadingPlaceholder } from '@grafana/ui';
import React, { FC, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
@ -28,9 +28,9 @@ const Silences: FC = () => {
<br />
<br />
{error && !loading && (
<InfoBox severity="error" title={<h4>Error loading silences</h4>}>
<Alert severity="error" title="Error loading silences">
{error.message || 'Unknown error.'}
</InfoBox>
</Alert>
)}
{loading && <LoadingPlaceholder text="loading silences..." />}
{result && !loading && !error && <pre>{JSON.stringify(result, null, 2)}</pre>}

@ -1,6 +1,6 @@
import React, { FC, useMemo } from 'react';
import { GrafanaTheme } from '@grafana/data';
import { PageToolbar, ToolbarButton, useStyles, CustomScrollbar, Spinner, Alert, InfoBox } from '@grafana/ui';
import { PageToolbar, ToolbarButton, useStyles, CustomScrollbar, Spinner, Alert } from '@grafana/ui';
import { css } from '@emotion/css';
import { AlertTypeStep } from './AlertTypeStep';
@ -100,9 +100,10 @@ export const AlertRuleForm: FC<Props> = ({ existing }) => {
<CustomScrollbar autoHeightMin="100%" hideHorizontalTrack={true}>
<div className={styles.contentInner}>
{hasErrors && (
<InfoBox severity="error">
There are errors in the form below. Please fix them and try saving again.
</InfoBox>
<Alert
severity="error"
title="There are errors in the form below. Please fix them and try saving again"
/>
)}
{submitState.error && (
<Alert severity="error" title="Error saving rule">

@ -74,36 +74,32 @@ export class ShareEmbed extends PureComponent<Props, State> {
const isRelativeTime = this.props.dashboard ? this.props.dashboard.time.to === 'now' : false;
return (
<div className="share-modal-body">
<div className="share-modal-header">
<div className="share-modal-content">
<p className="share-modal-info-text">Generate HTML for embedding an iframe with this panel.</p>
<Field
label="Current time range"
description={isRelativeTime ? 'Transforms the current relative time range to an absolute time range' : ''}
>
<Switch
id="share-current-time-range"
value={useCurrentTimeRange}
onChange={this.onUseCurrentTimeRangeChange}
/>
</Field>
<Field label="Theme">
<RadioButtonGroup options={themeOptions} value={selectedTheme} onChange={this.onThemeChange} />
</Field>
<Field
label="Embed HTML"
description="The HTML code below can be pasted and included in another web page. Unless anonymous access is enabled,
<>
<p className="share-modal-info-text">Generate HTML for embedding an iframe with this panel.</p>
<Field
label="Current time range"
description={isRelativeTime ? 'Transforms the current relative time range to an absolute time range' : ''}
>
<Switch
id="share-current-time-range"
value={useCurrentTimeRange}
onChange={this.onUseCurrentTimeRangeChange}
/>
</Field>
<Field label="Theme">
<RadioButtonGroup options={themeOptions} value={selectedTheme} onChange={this.onThemeChange} />
</Field>
<Field
label="Embed HTML"
description="The HTML code below can be pasted and included in another web page. Unless anonymous access is enabled,
the user viewing that page need to be signed into Grafana for the graph to load."
>
<TextArea rows={5} value={iframeHtml} onChange={this.onIframeHtmlChange}></TextArea>
</Field>
<ClipboardButton variant="primary" getText={this.getIframeHtml} onClipboardCopy={this.onIframeHtmlCopy}>
Copy to clipboard
</ClipboardButton>
</div>
</div>
</div>
>
<TextArea rows={5} value={iframeHtml} onChange={this.onIframeHtmlChange}></TextArea>
</Field>
<ClipboardButton variant="primary" getText={this.getIframeHtml} onClipboardCopy={this.onIframeHtmlCopy}>
Copy to clipboard
</ClipboardButton>
</>
);
}
}

@ -90,27 +90,23 @@ export class ShareExport extends PureComponent<Props, State> {
const { shareExternally } = this.state;
return (
<div className="share-modal-body">
<div className="share-modal-header">
<div className="share-modal-content">
<p className="share-modal-info-text">Export this dashboard.</p>
<Field label="Export for sharing externally">
<Switch value={shareExternally} onChange={this.onShareExternallyChange} />
</Field>
<div className="gf-form-button-row">
<Button variant="primary" onClick={this.onSaveAsFile}>
Save to file
</Button>
<Button variant="secondary" onClick={this.onViewJson}>
View JSON
</Button>
<Button variant="secondary" onClick={onDismiss}>
Cancel
</Button>
</div>
</div>
<>
<p className="share-modal-info-text">Export this dashboard.</p>
<Field label="Export for sharing externally">
<Switch value={shareExternally} onChange={this.onShareExternallyChange} />
</Field>
<div className="gf-form-button-row">
<Button variant="primary" onClick={this.onSaveAsFile}>
Save to file
</Button>
<Button variant="secondary" onClick={this.onViewJson}>
View JSON
</Button>
<Button variant="secondary" onClick={onDismiss}>
Cancel
</Button>
</div>
</div>
</>
);
}
}

@ -14,13 +14,9 @@ export const ShareGlobalPanel = ({ panel, initialFolderId, onDismiss }: Props) =
}
return (
<div className="share-modal-body">
<div className="share-modal-header">
<div className="share-modal-content">
<p className="share-modal-info-text">Add this panel to the panel library.</p>
<AddLibraryPanelContents panel={panel} initialFolderId={initialFolderId} onDismiss={onDismiss!} />
</div>
</div>
</div>
<>
<p className="share-modal-info-text">Add this panel to the panel library.</p>
<AddLibraryPanelContents panel={panel} initialFolderId={initialFolderId} onDismiss={onDismiss!} />
</>
);
};

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { Field, RadioButtonGroup, Switch, ClipboardButton, Icon, InfoBox, Input, FieldSet } from '@grafana/ui';
import { Field, RadioButtonGroup, Switch, ClipboardButton, Icon, Input, FieldSet, Alert } from '@grafana/ui';
import { SelectableValue, PanelModel, AppEvents } from '@grafana/data';
import { DashboardModel } from 'app/features/dashboard/state';
import { buildImageUrl, buildShareUrl } from './utils';
@ -90,70 +90,62 @@ export class ShareLink extends PureComponent<Props, State> {
const selectors = e2eSelectors.pages.SharePanelModal;
return (
<div className="share-modal-body">
<div className="share-modal-header">
<div className="share-modal-content">
<p className="share-modal-info-text">
Create a direct link to this dashboard or panel, customized with the options below.
</p>
<FieldSet>
<Field
label="Lock time range"
description={
isRelativeTime ? 'Transforms the current relative time range to an absolute time range' : ''
}
>
<Switch
id="share-current-time-range"
value={useCurrentTimeRange}
onChange={this.onUseCurrentTimeRangeChange}
/>
</Field>
<Field label="Theme">
<RadioButtonGroup options={themeOptions} value={selectedTheme} onChange={this.onThemeChange} />
</Field>
<Field label="Shorten URL">
<Switch id="share-shorten-url" value={useShortUrl} onChange={this.onUrlShorten} />
</Field>
<Field label="Link URL">
<Input
value={shareUrl}
readOnly
addonAfter={
<ClipboardButton variant="primary" getText={this.getShareUrl} onClipboardCopy={this.onShareUrlCopy}>
<Icon name="copy" /> Copy
</ClipboardButton>
}
/>
</Field>
</FieldSet>
{panel && config.rendererAvailable && (
<div className="gf-form">
<a href={imageUrl} target="_blank" rel="noreferrer" aria-label={selectors.linkToRenderedImage}>
<Icon name="camera" /> Direct link rendered image
</a>
</div>
)}
{panel && !config.rendererAvailable && (
<InfoBox>
<p>
<>To render a panel image, you must install the </>
<a
href="https://grafana.com/grafana/plugins/grafana-image-renderer"
target="_blank"
rel="noopener noreferrer"
className="external-link"
>
Grafana Image Renderer plugin
</a>
. Please contact your Grafana administrator to install the plugin.
</p>
</InfoBox>
)}
<>
<p className="share-modal-info-text">
Create a direct link to this dashboard or panel, customized with the options below.
</p>
<FieldSet>
<Field
label="Lock time range"
description={isRelativeTime ? 'Transforms the current relative time range to an absolute time range' : ''}
>
<Switch
id="share-current-time-range"
value={useCurrentTimeRange}
onChange={this.onUseCurrentTimeRangeChange}
/>
</Field>
<Field label="Theme">
<RadioButtonGroup options={themeOptions} value={selectedTheme} onChange={this.onThemeChange} />
</Field>
<Field label="Shorten URL">
<Switch id="share-shorten-url" value={useShortUrl} onChange={this.onUrlShorten} />
</Field>
<Field label="Link URL">
<Input
value={shareUrl}
readOnly
addonAfter={
<ClipboardButton variant="primary" getText={this.getShareUrl} onClipboardCopy={this.onShareUrlCopy}>
<Icon name="copy" /> Copy
</ClipboardButton>
}
/>
</Field>
</FieldSet>
{panel && config.rendererAvailable && (
<div className="gf-form">
<a href={imageUrl} target="_blank" rel="noreferrer" aria-label={selectors.linkToRenderedImage}>
<Icon name="camera" /> Direct link rendered image
</a>
</div>
</div>
</div>
)}
{panel && !config.rendererAvailable && (
<Alert severity="info" title="Image renderer plugin not installed" bottomSpacing={0}>
<>To render a panel image, you must install the </>
<a
href="https://grafana.com/grafana/plugins/grafana-image-renderer"
target="_blank"
rel="noopener noreferrer"
className="external-link"
>
Grafana image renderer plugin
</a>
. Please contact your Grafana administrator to install the plugin.
</Alert>
)}
</>
);
}
}

@ -292,16 +292,12 @@ export class ShareSnapshot extends PureComponent<Props, State> {
const { isLoading, step } = this.state;
return (
<div className="share-modal-body">
<div className="share-modal-header">
<div className="share-modal-content">
{step === 1 && this.renderStep1()}
{step === 2 && this.renderStep2()}
{step === 3 && this.renderStep3()}
{isLoading && <Spinner inline={true} />}
</div>
</div>
</div>
<>
{step === 1 && this.renderStep1()}
{step === 2 && this.renderStep2()}
{step === 3 && this.renderStep3()}
{isLoading && <Spinner inline={true} />}
</>
);
}
}

@ -5,7 +5,6 @@ import {
Container,
CustomScrollbar,
Themeable,
FeatureInfoBox,
VerticalGroup,
withTheme,
Input,
@ -263,15 +262,12 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
}
return (
<FeatureInfoBox
<Alert
title="Transformations"
className={css`
margin-bottom: ${this.props.theme.spacing.lg};
`}
onDismiss={() => {
onRemove={() => {
onDismiss(true);
}}
url={getDocsLink(DocsId.Transformations)}
severity="info"
>
<p>
Transformations allow you to join, calculate, re-order, hide, and rename your query results before
@ -279,9 +275,16 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
Many transforms are not suitable if you&apos;re using the Graph visualization, as it currently
only only supports time series data. <br />
It can help to switch to the Table visualization to understand what a transformation is doing.{' '}
<br />
</p>
</FeatureInfoBox>
<a
href={getDocsLink(DocsId.Transformations)}
className="external-link"
target="_blank"
rel="noreferrer"
>
Read more
</a>
</Alert>
);
}}
</LocalStorageValueProvider>

@ -1,6 +1,5 @@
import { DataSourceSettings, GrafanaTheme } from '@grafana/data';
import { FeatureInfoBox, useStyles } from '@grafana/ui';
import { css } from '@emotion/css';
import { DataSourceSettings } from '@grafana/data';
import { Alert } from '@grafana/ui';
import React, { FC } from 'react';
import { config } from 'app/core/config';
import { GrafanaEdition } from '@grafana/data/src/types/config';
@ -13,7 +12,6 @@ export interface Props {
}
export const CloudInfoBox: FC<Props> = ({ dataSource }) => {
const styles = useStyles(getStyles);
let mainDS = '';
let extraDS = '';
@ -47,46 +45,29 @@ export const CloudInfoBox: FC<Props> = ({ dataSource }) => {
return null;
}
return (
<FeatureInfoBox
<Alert
title={`Configure your ${mainDS} data source below`}
className={styles.box}
branded={false}
onDismiss={() => {
severity="info"
bottomSpacing={4}
onRemove={() => {
onDismiss(true);
}}
>
<div className={styles.text}>
Or skip the effort and get {mainDS} (and {extraDS}) as fully-managed, scalable, and hosted data sources
from Grafana Labs with the{' '}
<a
className="external-link"
href={`https://grafana.com/signup/cloud/connect-account?src=grafana-oss&cnt=${dataSource.type}-settings`}
target="_blank"
rel="noreferrer"
title="The free plan includes 10k active metrics and 50gb storage."
>
free-forever Grafana Cloud plan
</a>
.
</div>
</FeatureInfoBox>
Or skip the effort and get {mainDS} (and {extraDS}) as fully-managed, scalable, and hosted data sources from
Grafana Labs with the{' '}
<a
className="external-link"
href={`https://grafana.com/signup/cloud/connect-account?src=grafana-oss&cnt=${dataSource.type}-settings`}
target="_blank"
rel="noreferrer"
title="The free plan includes 10k active metrics and 50gb storage."
>
free-forever Grafana Cloud plan
</a>
.
</Alert>
);
}}
</LocalStorageValueProvider>
);
};
const getStyles = (theme: GrafanaTheme) => {
return {
box: css`
margin: 0 0 ${theme.spacing.lg} 0;
`,
text: css`
color: ${theme.colors.textSemiWeak};
padding: ${theme.spacing.sm} 0;
a {
text-decoration: underline;
}
`,
};
};

@ -21,7 +21,7 @@ import { getNavModel } from 'app/core/selectors/navModel';
// Types
import { StoreState } from 'app/types/';
import { DataSourceSettings } from '@grafana/data';
import { Alert, Button, InfoBox, LinkButton } from '@grafana/ui';
import { Alert, Button, LinkButton } from '@grafana/ui';
import { getDataSourceLoadingNav } from '../state/navModel';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers';
@ -127,10 +127,10 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
renderIsReadOnlyMessage() {
return (
<InfoBox aria-label={selectors.pages.DataSource.readOnly} severity="info">
<Alert aria-label={selectors.pages.DataSource.readOnly} severity="info" title="Provisioned data source">
This data source was added by config and cannot be modified using the UI. Please contact your server admin to
update this data source.
</InfoBox>
</Alert>
);
}

@ -1,8 +1,8 @@
import { defaults } from 'lodash';
import React, { PureComponent } from 'react';
import { InlineField, Select, FeatureInfoBox, Input } from '@grafana/ui';
import { QueryEditorProps, SelectableValue, FeatureState, dataFrameFromJSON, rangeUtil } from '@grafana/data';
import { InlineField, Select, Alert, Input } from '@grafana/ui';
import { QueryEditorProps, SelectableValue, dataFrameFromJSON, rangeUtil } from '@grafana/data';
import { GrafanaDatasource } from '../datasource';
import { defaultQuery, GrafanaQuery, GrafanaQueryType } from '../types';
import { getBackendSrv } from '@grafana/runtime';
@ -232,12 +232,10 @@ export class QueryEditor extends PureComponent<Props, State> {
</div>
)}
<FeatureInfoBox title="Grafana Live - Measurements" featureState={FeatureState.alpha}>
<p>
This supports real-time event streams in Grafana core. This feature is under heavy development. Expect the
interfaces and structures to change as this becomes more production ready.
</p>
</FeatureInfoBox>
<Alert title="Grafana Live - Measurements" severity="info">
This supports real-time event streams in Grafana core. This feature is under heavy development. Expect the
interfaces and structures to change as this becomes more production ready.
</Alert>
</>
);
}

@ -1,13 +1,12 @@
import React, { PureComponent } from 'react';
import { css } from '@emotion/css';
import { Select, FeatureInfoBox, Label, stylesFactory } from '@grafana/ui';
import { Select, Alert, Label, stylesFactory } from '@grafana/ui';
import {
LiveChannelScope,
LiveChannelAddress,
LiveChannelSupport,
SelectableValue,
StandardEditorProps,
FeatureState,
GrafanaTheme,
} from '@grafana/data';
@ -100,12 +99,10 @@ export class LiveChannelEditor extends PureComponent<Props, State> {
return (
<>
<FeatureInfoBox title="Grafana Live" featureState={FeatureState.alpha}>
<p>
This supports real-time event streams in grafana core. This feature is under heavy development. Expect the
intefaces and structures to change as this becomes more production ready.
</p>
</FeatureInfoBox>
<Alert title="Grafana Live" severity="info">
This supports real-time event streams in grafana core. This feature is under heavy development. Expect the
intefaces and structures to change as this becomes more production ready.
</Alert>
<div>
<div className={style.dropWrap}>

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
import { Unsubscribable, PartialObserver } from 'rxjs';
import { FeatureInfoBox, stylesFactory, Button, JSONFormatter, CustomScrollbar, CodeEditor } from '@grafana/ui';
import { Alert, stylesFactory, Button, JSONFormatter, CustomScrollbar, CodeEditor } from '@grafana/ui';
import {
GrafanaTheme,
PanelProps,
@ -126,17 +126,12 @@ export class LivePanel extends PureComponent<Props, State> {
const preformatted = `[feature_toggles]
enable = live`;
return (
<FeatureInfoBox
title="Grafana Live"
style={{
height: this.props.height,
}}
>
<Alert title="Grafana Live" severity="info">
<p>Grafana live requires a feature flag to run</p>
<b>custom.ini:</b>
<pre>{preformatted}</pre>
</FeatureInfoBox>
</Alert>
);
}
@ -298,14 +293,9 @@ export class LivePanel extends PureComponent<Props, State> {
const { addr, error } = this.state;
if (!addr) {
return (
<FeatureInfoBox
title="Grafana Live"
style={{
height: this.props.height,
}}
>
<p>Use the panel editor to pick a channel</p>
</FeatureInfoBox>
<Alert title="Grafana Live" severity="info">
Use the panel editor to pick a channel
</Alert>
);
}
if (error) {

Loading…
Cancel
Save