mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Grafana-UI: Alert components tweaks (#27128)
* Grafana-UI: Add Alert docs and story * Grafana-UI: Move Alert styles to emotion * Grafana-UI: Tweak docs * Grafana-UI: Update test * Grafana-UI: Add outline to custom content * Grafana-UI: Remove class name * Grafana-UI: Remove _Alert.scss * Grafana-UI: Add e2e Alert selector * Grafana-UI: Use @deprecated * Grafana-UI: Remove circular reference * Grafana-UI: Final tweaks
This commit is contained in:
parent
88fbdd6716
commit
f5ee1f93cd
@ -95,6 +95,9 @@ export const Components = {
|
||||
AlertTab: {
|
||||
content: 'Alert editor tab content',
|
||||
},
|
||||
Alert: {
|
||||
alert: (severity: string) => `Alert ${severity}`,
|
||||
},
|
||||
TransformTab: {
|
||||
content: 'Transform editor tab content',
|
||||
newTransform: (title: string) => `New transform ${title}`,
|
||||
|
@ -2,6 +2,6 @@ import { e2e } from '../index';
|
||||
|
||||
export const assertSuccessNotification = () => {
|
||||
e2e()
|
||||
.get('.alert-success')
|
||||
.get('[aria-label^="Alert success"]')
|
||||
.should('exist');
|
||||
};
|
||||
|
17
packages/grafana-ui/src/components/Alert/Alert.mdx
Normal file
17
packages/grafana-ui/src/components/Alert/Alert.mdx
Normal file
@ -0,0 +1,17 @@
|
||||
import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
|
||||
import { Alert } from './Alert';
|
||||
|
||||
<Meta title="MDX|Alert" component={Alert} />
|
||||
|
||||
# Alert
|
||||
|
||||
An alert displays an important message in a way that attracts the user's attention without interrupting the user's task.
|
||||
`onRemove` handler can be used to enable manually dismissing the alert.
|
||||
|
||||
# Usage
|
||||
|
||||
```jsx
|
||||
<Alert title="Some very important message" severity="info" />
|
||||
```
|
||||
|
||||
<Props of={Alert}/>
|
41
packages/grafana-ui/src/components/Alert/Alert.story.tsx
Normal file
41
packages/grafana-ui/src/components/Alert/Alert.story.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { select } from '@storybook/addon-knobs';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { Alert, AlertVariant } from './Alert';
|
||||
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import mdx from '../Alert/Alert.mdx';
|
||||
|
||||
export default {
|
||||
title: 'Overlays/Alert',
|
||||
component: Alert,
|
||||
decorators: [withCenteredStory, withHorizontallyCenteredStory],
|
||||
parameters: {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const severities: AlertVariant[] = ['error', 'warning', 'info', 'success'];
|
||||
|
||||
export const basic = () => {
|
||||
const severity = select('Severity', severities, 'info');
|
||||
return <Alert title="Some very important message" severity={severity} />;
|
||||
};
|
||||
|
||||
export const withRemove = () => {
|
||||
const severity = select('Severity', severities, 'info');
|
||||
return <Alert title="Some very important message" severity={severity} onRemove={action('Remove button clicked')} />;
|
||||
};
|
||||
|
||||
export const customButtonContent = () => {
|
||||
const severity = select('Severity', severities, 'info');
|
||||
return (
|
||||
<Alert
|
||||
title="Some very important message"
|
||||
severity={severity}
|
||||
buttonContent={<span>Close</span>}
|
||||
onRemove={action('Remove button clicked')}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,62 +1,138 @@
|
||||
import React, { FC, ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { css } from 'emotion';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { useTheme } from '../../themes';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { IconName } from '../../types/icon';
|
||||
|
||||
export type AlertVariant = 'success' | 'warning' | 'error' | 'info';
|
||||
|
||||
interface AlertProps {
|
||||
export interface Props {
|
||||
title: string;
|
||||
buttonText?: string;
|
||||
onButtonClick?: (event: React.MouseEvent) => void;
|
||||
/** On click handler for alert button, mostly used for dismissing the alert */
|
||||
onRemove?: (event: React.MouseEvent) => void;
|
||||
severity?: AlertVariant;
|
||||
children?: ReactNode;
|
||||
/** Custom component or text for alert button */
|
||||
buttonContent?: ReactNode | string;
|
||||
/** @deprecated */
|
||||
/** Deprecated use onRemove instead */
|
||||
onButtonClick?: (event: React.MouseEvent) => void;
|
||||
/** @deprecated */
|
||||
/** Deprecated use buttonContent instead */
|
||||
buttonText?: string;
|
||||
}
|
||||
|
||||
function getIconFromSeverity(severity: AlertVariant): string {
|
||||
switch (severity) {
|
||||
case 'error': {
|
||||
case 'error':
|
||||
case 'warning':
|
||||
return 'exclamation-triangle';
|
||||
}
|
||||
case 'warning': {
|
||||
return 'exclamation-triangle';
|
||||
}
|
||||
case 'info': {
|
||||
case 'info':
|
||||
return 'info-circle';
|
||||
}
|
||||
case 'success': {
|
||||
case 'success':
|
||||
return 'check';
|
||||
}
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export const Alert: FC<AlertProps> = ({ title, buttonText, onButtonClick, onRemove, children, severity = 'error' }) => {
|
||||
const alertClass = classNames('alert', `alert-${severity}`);
|
||||
export const Alert: FC<Props> = ({
|
||||
title,
|
||||
buttonText,
|
||||
onButtonClick,
|
||||
onRemove,
|
||||
children,
|
||||
buttonContent,
|
||||
severity = 'error',
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme, severity, !!buttonContent);
|
||||
|
||||
return (
|
||||
<div className="alert-container">
|
||||
<div className={alertClass}>
|
||||
<div className="alert-icon">
|
||||
<div className={styles.container}>
|
||||
<div className={styles.alert} aria-label={selectors.components.Alert.alert(severity)}>
|
||||
<div className={styles.icon}>
|
||||
<Icon size="xl" name={getIconFromSeverity(severity) as IconName} />
|
||||
</div>
|
||||
<div className="alert-body">
|
||||
<div className="alert-title">{title}</div>
|
||||
{children && <div className="alert-text">{children}</div>}
|
||||
<div className={styles.body}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
{children && <div>{children}</div>}
|
||||
</div>
|
||||
{/* If onRemove is specified , giving preference to onRemove */}
|
||||
{onRemove && (
|
||||
<button type="button" className="alert-close" onClick={onRemove}>
|
||||
<Icon name="times" size="lg" />
|
||||
{/* If onRemove is specified, giving preference to onRemove */}
|
||||
{onRemove ? (
|
||||
<button type="button" className={styles.close} onClick={onRemove}>
|
||||
{buttonContent || <Icon name="times" size="lg" />}
|
||||
</button>
|
||||
)}
|
||||
{onButtonClick && (
|
||||
) : onButtonClick ? (
|
||||
<button type="button" className="btn btn-outline-danger" onClick={onButtonClick}>
|
||||
{buttonText}
|
||||
</button>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean) => {
|
||||
const { redBase, redShade, greenBase, greenShade, blue80, blue77, white } = theme.palette;
|
||||
const backgrounds = {
|
||||
error: css`
|
||||
background: linear-gradient(90deg, ${redBase}, ${redShade});
|
||||
`,
|
||||
warning: css`
|
||||
background: linear-gradient(90deg, ${redBase}, ${redShade});
|
||||
`,
|
||||
info: css`
|
||||
background: linear-gradient(100deg, ${blue80}, ${blue77});
|
||||
`,
|
||||
success: css`
|
||||
background: linear-gradient(100deg, ${greenBase}, ${greenShade});
|
||||
`,
|
||||
};
|
||||
|
||||
return {
|
||||
container: css`
|
||||
z-index: ${theme.zIndex.tooltip};
|
||||
`,
|
||||
alert: css`
|
||||
padding: 15px 20px;
|
||||
margin-bottom: ${theme.spacing.xs};
|
||||
position: relative;
|
||||
color: ${white};
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
|
||||
border-radius: ${theme.border.radius.md};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
${backgrounds[severity]}
|
||||
`,
|
||||
icon: css`
|
||||
padding: 0 ${theme.spacing.md} 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 35px;
|
||||
`,
|
||||
title: css`
|
||||
font-weight: ${theme.typography.weight.semibold};
|
||||
`,
|
||||
body: css`
|
||||
flex-grow: 1;
|
||||
margin: 0 ${theme.spacing.md} 0 0;
|
||||
|
||||
a {
|
||||
color: ${white};
|
||||
text-decoration: underline;
|
||||
}
|
||||
`,
|
||||
close: css`
|
||||
background: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: ${outline ? `1px solid ${white}` : 'none'};
|
||||
border-radius: ${theme.border.radius.sm};
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
@ -1,95 +0,0 @@
|
||||
//
|
||||
// Alerts
|
||||
// --------------------------------------------------
|
||||
|
||||
// Base styles
|
||||
// -------------------------
|
||||
|
||||
.alert {
|
||||
padding: 15px 20px;
|
||||
margin-bottom: $space-xs;
|
||||
text-shadow: 0 2px 0 rgba(255, 255, 255, 0.5);
|
||||
background: $alert-error-bg;
|
||||
position: relative;
|
||||
color: $white;
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
|
||||
border-radius: $border-radius;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// Alternate styles
|
||||
// -------------------------
|
||||
|
||||
.alert-success {
|
||||
background: $alert-success-bg;
|
||||
}
|
||||
|
||||
.alert-danger,
|
||||
.alert-error {
|
||||
background: $alert-error-bg;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: $alert-info-bg;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: $alert-warning-bg;
|
||||
}
|
||||
|
||||
.alert-container {
|
||||
z-index: $zindex-tooltip;
|
||||
}
|
||||
|
||||
.page-alert-list {
|
||||
z-index: 8000;
|
||||
min-width: 400px;
|
||||
max-width: 600px;
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
.alert-close {
|
||||
padding: 0 0 0 $space-md;
|
||||
border: none;
|
||||
background: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.fa {
|
||||
align-self: flex-end;
|
||||
font-size: 21px;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
font-weight: $font-weight-semi-bold;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
padding: 0 $space-md 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 35px;
|
||||
.fa {
|
||||
font-size: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-body {
|
||||
flex-grow: 1;
|
||||
|
||||
a {
|
||||
color: $white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-icon-on-top {
|
||||
align-items: flex-start;
|
||||
}
|
@ -12,5 +12,4 @@
|
||||
@import 'TableInputCSV/TableInputCSV';
|
||||
@import 'TimePicker/TimeOfDayPicker';
|
||||
@import 'Tooltip/Tooltip';
|
||||
@import 'Alert/Alert';
|
||||
@import 'Slider/Slider';
|
||||
|
Loading…
Reference in New Issue
Block a user