Datasources: fixed long error message overflowing container (#29440)

* Fixed: error message overflow container

Signed-off-by: Uchechukwu Obasi <obasiuche62@gmail.com>

* used min-width property for better styling

Signed-off-by: Uchechukwu Obasi <obasiuche62@gmail.com>

* Switched to Alert component

* Fixed passing aria-label to Alert component

* Fixed e2e test (I hope)

* another attempt to fix e2e

* Fixed display name

Co-authored-by: Uchechukwu Obasi <obate@Uchechukwus-MacBook-Pro.local>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Uchechukwu Obasi 2020-12-02 09:30:28 +01:00 committed by GitHub
parent d953a56eb6
commit 53cd59a5a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 50 deletions

View File

@ -14,7 +14,6 @@ export const Pages = {
delete: 'Data source settings page Delete button',
saveAndTest: 'Data source settings page Save and Test button',
alert: 'Data source settings page Alert',
alertMessage: 'Data source settings page Alert message',
},
DataSources: {
url: '/datasources',

View File

@ -85,8 +85,10 @@ export const addDataSource = (config?: Partial<AddDataSourceConfig>) => {
form();
e2e.pages.DataSource.saveAndTest().click();
e2e.pages.DataSource.alert().should('exist');
e2e.pages.DataSource.alertMessage().contains(expectedAlertMessage); // assertion
e2e.pages.DataSource.alert()
.should('exist')
.contains(expectedAlertMessage); // assertion
e2e().logToConsole('Added data source with name:', name);
return e2e()

View File

@ -1,4 +1,4 @@
import React, { FC, ReactNode } from 'react';
import React, { FC, HTMLAttributes, ReactNode } from 'react';
import { css } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
@ -9,7 +9,7 @@ import { getColorsFromSeverity } from '../../utils/colors';
export type AlertVariant = 'success' | 'warning' | 'error' | 'info';
export interface Props {
export interface Props extends HTMLAttributes<HTMLElement> {
title: string;
/** On click handler for alert button, mostly used for dismissing the alert */
onRemove?: (event: React.MouseEvent) => void;
@ -39,40 +39,36 @@ function getIconFromSeverity(severity: AlertVariant): string {
}
}
export const Alert: FC<Props> = ({
title,
buttonText,
onButtonClick,
onRemove,
children,
buttonContent,
severity = 'error',
}) => {
const theme = useTheme();
const styles = getStyles(theme, severity, !!buttonContent);
export const Alert: FC<Props> = React.forwardRef<HTMLElement, Props>(
({ title, buttonText, onButtonClick, onRemove, children, buttonContent, severity = 'error', ...restProps }) => {
const theme = useTheme();
const styles = getStyles(theme, severity, !!buttonContent);
return (
<div className={styles.alert} aria-label={selectors.components.Alert.alert(severity)}>
<div className={styles.icon}>
<Icon size="xl" name={getIconFromSeverity(severity) as IconName} />
return (
<div className={styles.alert} 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>}
</div>
{/* 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 ? (
<button type="button" className="btn btn-outline-danger" onClick={onButtonClick}>
{buttonText}
</button>
) : null}
</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={styles.close} onClick={onRemove}>
{buttonContent || <Icon name="times" size="lg" />}
</button>
) : onButtonClick ? (
<button type="button" className="btn btn-outline-danger" onClick={onButtonClick}>
{buttonText}
</button>
) : null}
</div>
);
};
);
}
);
Alert.displayName = 'Alert';
const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean) => {
const { white } = theme.palette;
@ -107,6 +103,8 @@ const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean
body: css`
flex-grow: 1;
margin: 0 ${theme.spacing.md} 0 0;
overflow-wrap: break-word;
word-break: break-word;
a {
color: ${white};

View File

@ -2,8 +2,6 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import isString from 'lodash/isString';
import { Icon } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
// Components
import Page from 'app/core/components/Page/Page';
import { GenericDataSourcePlugin, PluginSettings } from './PluginSettings';
@ -25,10 +23,12 @@ import { getRouteParamsId } from 'app/core/selectors/location';
// Types
import { CoreEvents, StoreState } from 'app/types/';
import { DataSourcePluginMeta, DataSourceSettings, NavModel, UrlQueryMap } from '@grafana/data';
import { Alert } from '@grafana/ui';
import { getDataSourceLoadingNav } from '../state/navModel';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers';
import { connectWithCleanUp } from 'app/core/components/connectWithCleanUp';
import { selectors } from '@grafana/e2e-selectors';
export interface Props {
navModel: NavModel;
@ -172,7 +172,7 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
}
renderSettings() {
const { dataSourceMeta, setDataSourceName, setIsDefault, dataSource, testingStatus, plugin } = this.props;
const { dataSourceMeta, setDataSourceName, setIsDefault, dataSource, plugin, testingStatus } = this.props;
return (
<form onSubmit={this.onSubmit}>
@ -204,16 +204,11 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
<div className="gf-form-group">
{testingStatus && testingStatus.message && (
<div className={`alert-${testingStatus.status} alert`} aria-label={selectors.pages.DataSource.alert}>
<div className="alert-icon">
{testingStatus.status === 'error' ? <Icon name="exclamation-triangle" /> : <Icon name="check" />}
</div>
<div className="alert-body">
<div className="alert-title" aria-label={selectors.pages.DataSource.alertMessage}>
{testingStatus.message}
</div>
</div>
</div>
<Alert
severity={testingStatus.status === 'error' ? 'error' : 'success'}
title={testingStatus.message}
aria-label={selectors.pages.DataSource.alert}
/>
)}
</div>

View File

@ -77,6 +77,8 @@
}
.alert-body {
overflow-wrap: break-word;
word-break: break-word;
flex-grow: 1;
}