Commit 53cd59a5 by Uchechukwu Obasi Committed by GitHub

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>
parent d953a56e
...@@ -14,7 +14,6 @@ export const Pages = { ...@@ -14,7 +14,6 @@ export const Pages = {
delete: 'Data source settings page Delete button', delete: 'Data source settings page Delete button',
saveAndTest: 'Data source settings page Save and Test button', saveAndTest: 'Data source settings page Save and Test button',
alert: 'Data source settings page Alert', alert: 'Data source settings page Alert',
alertMessage: 'Data source settings page Alert message',
}, },
DataSources: { DataSources: {
url: '/datasources', url: '/datasources',
......
...@@ -85,8 +85,10 @@ export const addDataSource = (config?: Partial<AddDataSourceConfig>) => { ...@@ -85,8 +85,10 @@ export const addDataSource = (config?: Partial<AddDataSourceConfig>) => {
form(); form();
e2e.pages.DataSource.saveAndTest().click(); e2e.pages.DataSource.saveAndTest().click();
e2e.pages.DataSource.alert().should('exist'); e2e.pages.DataSource.alert()
e2e.pages.DataSource.alertMessage().contains(expectedAlertMessage); // assertion .should('exist')
.contains(expectedAlertMessage); // assertion
e2e().logToConsole('Added data source with name:', name); e2e().logToConsole('Added data source with name:', name);
return e2e() return e2e()
......
import React, { FC, ReactNode } from 'react'; import React, { FC, HTMLAttributes, ReactNode } from 'react';
import { css } from 'emotion'; import { css } from 'emotion';
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
...@@ -9,7 +9,7 @@ import { getColorsFromSeverity } from '../../utils/colors'; ...@@ -9,7 +9,7 @@ import { getColorsFromSeverity } from '../../utils/colors';
export type AlertVariant = 'success' | 'warning' | 'error' | 'info'; export type AlertVariant = 'success' | 'warning' | 'error' | 'info';
export interface Props { export interface Props extends HTMLAttributes<HTMLElement> {
title: string; title: string;
/** On click handler for alert button, mostly used for dismissing the alert */ /** On click handler for alert button, mostly used for dismissing the alert */
onRemove?: (event: React.MouseEvent) => void; onRemove?: (event: React.MouseEvent) => void;
...@@ -39,40 +39,36 @@ function getIconFromSeverity(severity: AlertVariant): string { ...@@ -39,40 +39,36 @@ function getIconFromSeverity(severity: AlertVariant): string {
} }
} }
export const Alert: FC<Props> = ({ export const Alert: FC<Props> = React.forwardRef<HTMLElement, Props>(
title, ({ title, buttonText, onButtonClick, onRemove, children, buttonContent, severity = 'error', ...restProps }) => {
buttonText, const theme = useTheme();
onButtonClick, const styles = getStyles(theme, severity, !!buttonContent);
onRemove,
children,
buttonContent,
severity = 'error',
}) => {
const theme = useTheme();
const styles = getStyles(theme, severity, !!buttonContent);
return ( return (
<div className={styles.alert} aria-label={selectors.components.Alert.alert(severity)}> <div className={styles.alert} aria-label={selectors.components.Alert.alert(severity)} {...restProps}>
<div className={styles.icon}> <div className={styles.icon}>
<Icon size="xl" name={getIconFromSeverity(severity) as IconName} /> <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>
<div className={styles.body}> );
<div className={styles.title}>{title}</div> }
{children && <div>{children}</div>} );
</div>
{/* If onRemove is specified, giving preference to onRemove */} Alert.displayName = 'Alert';
{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>
);
};
const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean) => { const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean) => {
const { white } = theme.palette; const { white } = theme.palette;
...@@ -107,6 +103,8 @@ const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean ...@@ -107,6 +103,8 @@ const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean
body: css` body: css`
flex-grow: 1; flex-grow: 1;
margin: 0 ${theme.spacing.md} 0 0; margin: 0 ${theme.spacing.md} 0 0;
overflow-wrap: break-word;
word-break: break-word;
a { a {
color: ${white}; color: ${white};
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import isString from 'lodash/isString'; import isString from 'lodash/isString';
import { Icon } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
// Components // Components
import Page from 'app/core/components/Page/Page'; import Page from 'app/core/components/Page/Page';
import { GenericDataSourcePlugin, PluginSettings } from './PluginSettings'; import { GenericDataSourcePlugin, PluginSettings } from './PluginSettings';
...@@ -25,10 +23,12 @@ import { getRouteParamsId } from 'app/core/selectors/location'; ...@@ -25,10 +23,12 @@ import { getRouteParamsId } from 'app/core/selectors/location';
// Types // Types
import { CoreEvents, StoreState } from 'app/types/'; import { CoreEvents, StoreState } from 'app/types/';
import { DataSourcePluginMeta, DataSourceSettings, NavModel, UrlQueryMap } from '@grafana/data'; import { DataSourcePluginMeta, DataSourceSettings, NavModel, UrlQueryMap } from '@grafana/data';
import { Alert } from '@grafana/ui';
import { getDataSourceLoadingNav } from '../state/navModel'; import { getDataSourceLoadingNav } 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 { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers';
import { connectWithCleanUp } from 'app/core/components/connectWithCleanUp'; import { connectWithCleanUp } from 'app/core/components/connectWithCleanUp';
import { selectors } from '@grafana/e2e-selectors';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
...@@ -172,7 +172,7 @@ export class DataSourceSettingsPage extends PureComponent<Props> { ...@@ -172,7 +172,7 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
} }
renderSettings() { renderSettings() {
const { dataSourceMeta, setDataSourceName, setIsDefault, dataSource, testingStatus, plugin } = this.props; const { dataSourceMeta, setDataSourceName, setIsDefault, dataSource, plugin, testingStatus } = this.props;
return ( return (
<form onSubmit={this.onSubmit}> <form onSubmit={this.onSubmit}>
...@@ -204,16 +204,11 @@ export class DataSourceSettingsPage extends PureComponent<Props> { ...@@ -204,16 +204,11 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
<div className="gf-form-group"> <div className="gf-form-group">
{testingStatus && testingStatus.message && ( {testingStatus && testingStatus.message && (
<div className={`alert-${testingStatus.status} alert`} aria-label={selectors.pages.DataSource.alert}> <Alert
<div className="alert-icon"> severity={testingStatus.status === 'error' ? 'error' : 'success'}
{testingStatus.status === 'error' ? <Icon name="exclamation-triangle" /> : <Icon name="check" />} title={testingStatus.message}
</div> aria-label={selectors.pages.DataSource.alert}
<div className="alert-body"> />
<div className="alert-title" aria-label={selectors.pages.DataSource.alertMessage}>
{testingStatus.message}
</div>
</div>
</div>
)} )}
</div> </div>
......
...@@ -77,6 +77,8 @@ ...@@ -77,6 +77,8 @@
} }
.alert-body { .alert-body {
overflow-wrap: break-word;
word-break: break-word;
flex-grow: 1; flex-grow: 1;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment