mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 00:08:10 -05:00
Alerting: Allow copying error messages from query badges (#80078)
* Add interactive mode to the Badge component
* Add expression errors as Alert components instead of badges
* Revert "Add interactive mode to the Badge component"
This reverts commit 9558743fc7.
This commit is contained in:
@@ -4,7 +4,7 @@ import React, { FC, useCallback, useState } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import { DataFrame, dateTimeFormat, GrafanaTheme2, isTimeSeriesFrames, LoadingState, PanelData } from '@grafana/data';
|
||||
import { AutoSizeInput, Button, clearButtonStyles, IconButton, Stack, useStyles2 } from '@grafana/ui';
|
||||
import { Alert, AutoSizeInput, Button, clearButtonStyles, IconButton, Stack, useStyles2 } from '@grafana/ui';
|
||||
import { ClassicConditions } from 'app/features/expressions/components/ClassicConditions';
|
||||
import { Math } from 'app/features/expressions/components/Math';
|
||||
import { Reduce } from 'app/features/expressions/components/Reduce';
|
||||
@@ -136,12 +136,20 @@ export const Expression: FC<ExpressionProps> = ({
|
||||
onUpdateRefId={(newRefId) => onUpdateRefId(query.refId, newRefId)}
|
||||
onUpdateExpressionType={(type) => onUpdateExpressionType(query.refId, type)}
|
||||
onSetCondition={onSetCondition}
|
||||
warning={warning}
|
||||
error={error}
|
||||
query={query}
|
||||
alertCondition={alertCondition}
|
||||
/>
|
||||
<div className={styles.expression.body}>
|
||||
{error && (
|
||||
<Alert title="Expression failed" severity="error">
|
||||
{error.message}
|
||||
</Alert>
|
||||
)}
|
||||
{warning && (
|
||||
<Alert title="Expression warning" severity="warning">
|
||||
{warning.message}
|
||||
</Alert>
|
||||
)}
|
||||
<div className={styles.expression.description}>{selectedExpressionDescription}</div>
|
||||
{renderExpressionType(query)}
|
||||
</div>
|
||||
@@ -275,8 +283,6 @@ interface HeaderProps {
|
||||
onUpdateRefId: (refId: string) => void;
|
||||
onRemoveExpression: () => void;
|
||||
onUpdateExpressionType: (type: ExpressionQueryType) => void;
|
||||
warning?: Error;
|
||||
error?: Error;
|
||||
onSetCondition: (refId: string) => void;
|
||||
query: ExpressionQuery;
|
||||
alertCondition: boolean;
|
||||
@@ -287,11 +293,9 @@ const Header: FC<HeaderProps> = ({
|
||||
queryType,
|
||||
onUpdateRefId,
|
||||
onRemoveExpression,
|
||||
warning,
|
||||
onSetCondition,
|
||||
alertCondition,
|
||||
query,
|
||||
error,
|
||||
}) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const clearButton = useStyles2(clearButtonStyles);
|
||||
@@ -335,12 +339,7 @@ const Header: FC<HeaderProps> = ({
|
||||
<div>{getExpressionLabel(queryType)}</div>
|
||||
</Stack>
|
||||
<Spacer />
|
||||
<ExpressionStatusIndicator
|
||||
error={error}
|
||||
warning={warning}
|
||||
onSetCondition={() => onSetCondition(query.refId)}
|
||||
isCondition={alertCondition}
|
||||
/>
|
||||
<ExpressionStatusIndicator onSetCondition={() => onSetCondition(query.refId)} isCondition={alertCondition} />
|
||||
<IconButton
|
||||
name="trash-alt"
|
||||
variant="secondary"
|
||||
|
||||
-32
@@ -4,47 +4,15 @@ import React from 'react';
|
||||
import { ExpressionStatusIndicator } from './ExpressionStatusIndicator';
|
||||
|
||||
describe('ExpressionStatusIndicator', () => {
|
||||
it('should render two elements when error and not condition', () => {
|
||||
render(<ExpressionStatusIndicator isCondition={false} warning={new Error('this is a warning')} />);
|
||||
|
||||
expect(screen.getByText('Warning')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Set as alert condition' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render one element when warning and condition', () => {
|
||||
render(<ExpressionStatusIndicator isCondition warning={new Error('this is a warning')} />);
|
||||
|
||||
expect(screen.getByText('Alert condition')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Set as alert condition' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render two elements when error and not condition', () => {
|
||||
render(<ExpressionStatusIndicator isCondition={false} error={new Error('this is a error')} />);
|
||||
|
||||
expect(screen.getByText('Error')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Set as alert condition' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render one element when error and condition', () => {
|
||||
render(<ExpressionStatusIndicator isCondition error={new Error('this is a error')} />);
|
||||
|
||||
expect(screen.getByText('Alert condition')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Set as alert condition' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render one element if condition', () => {
|
||||
render(<ExpressionStatusIndicator isCondition />);
|
||||
|
||||
expect(screen.queryByText('Error')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Warning')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Alert condition')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render one element if not condition', () => {
|
||||
render(<ExpressionStatusIndicator isCondition={false} />);
|
||||
|
||||
expect(screen.queryByText('Error')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Warning')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Alert condition' })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Set as alert condition' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
+3
-23
@@ -5,35 +5,17 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Badge, clearButtonStyles, useStyles2 } from '@grafana/ui';
|
||||
|
||||
interface AlertConditionProps {
|
||||
warning?: Error;
|
||||
error?: Error;
|
||||
isCondition?: boolean;
|
||||
onSetCondition?: () => void;
|
||||
}
|
||||
|
||||
export const ExpressionStatusIndicator = ({ error, warning, isCondition, onSetCondition }: AlertConditionProps) => {
|
||||
export const ExpressionStatusIndicator = ({ isCondition, onSetCondition }: AlertConditionProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const elements: JSX.Element[] = [];
|
||||
|
||||
if (error && isCondition) {
|
||||
return <Badge color="red" icon="exclamation-circle" text="Alert condition" tooltip={error.message} />;
|
||||
} else if (error) {
|
||||
elements.push(<Badge key="error" color="red" icon="exclamation-circle" text="Error" tooltip={error.message} />);
|
||||
}
|
||||
|
||||
if (warning && isCondition) {
|
||||
return <Badge color="orange" icon="exclamation-triangle" text="Alert condition" tooltip={warning.message} />;
|
||||
} else if (warning) {
|
||||
elements.push(
|
||||
<Badge key="warning" color="orange" icon="exclamation-triangle" text="Warning" tooltip={warning.message} />
|
||||
);
|
||||
}
|
||||
|
||||
if (isCondition) {
|
||||
elements.unshift(<Badge key="condition" color="green" icon="check" text="Alert condition" />);
|
||||
return <Badge key="condition" color="green" icon="check" text="Alert condition" />;
|
||||
} else {
|
||||
elements.unshift(
|
||||
return (
|
||||
<button
|
||||
key="make-condition"
|
||||
type="button"
|
||||
@@ -44,8 +26,6 @@ export const ExpressionStatusIndicator = ({ error, warning, isCondition, onSetCo
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{elements}</>;
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
|
||||
@@ -130,11 +130,7 @@ export const QueryWrapper = ({
|
||||
onChangeQueryOptions={onChangeQueryOptions}
|
||||
index={index}
|
||||
/>
|
||||
<ExpressionStatusIndicator
|
||||
error={error}
|
||||
onSetCondition={() => onSetCondition(query.refId)}
|
||||
isCondition={isAlertCondition}
|
||||
/>
|
||||
<ExpressionStatusIndicator onSetCondition={() => onSetCondition(query.refId)} isCondition={isAlertCondition} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user