mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
refactoring: alert list improvments PR #10452
This commit is contained in:
@@ -45,7 +45,7 @@ describe('AlertRuleList', () => {
|
||||
|
||||
it('should render 1 rule', () => {
|
||||
page.update();
|
||||
let ruleNode = page.find('.card-item-wrapper');
|
||||
let ruleNode = page.find('.alert-rule-item');
|
||||
expect(toJson(ruleNode)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import PageHeader from 'app/core/components/PageHeader/PageHeader';
|
||||
import { IAlertRule } from 'app/stores/AlertListStore/AlertListStore';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import IContainerProps from 'app/containers/IContainerProps';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
|
||||
@inject('view', 'nav', 'alertList')
|
||||
@observer
|
||||
@@ -23,11 +24,6 @@ export class AlertRuleList extends React.Component<IContainerProps, any> {
|
||||
|
||||
this.props.nav.load('alerting', 'alert-list');
|
||||
this.fetchRules();
|
||||
this.handleTooltipPositionChange = this.handleTooltipPositionChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
tooltipPosition: 'auto',
|
||||
};
|
||||
}
|
||||
|
||||
onStateFilterChanged = evt => {
|
||||
@@ -49,12 +45,10 @@ export class AlertRuleList extends React.Component<IContainerProps, any> {
|
||||
});
|
||||
};
|
||||
|
||||
handleTooltipPositionChange(evt) {
|
||||
evt.preventDefault();
|
||||
this.setState({
|
||||
tooltipPosition: evt.target.value,
|
||||
});
|
||||
}
|
||||
onSearchQueryChange = evt => {
|
||||
this.props.alertList.setSearchQuery(evt.target.value);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { nav, alertList } = this.props;
|
||||
|
||||
@@ -63,8 +57,20 @@ export class AlertRuleList extends React.Component<IContainerProps, any> {
|
||||
<PageHeader model={nav as any} />
|
||||
<div className="page-container page-body">
|
||||
<div className="page-action-bar">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<label className="gf-form--has-input-icon gf-form--grow">
|
||||
<input
|
||||
type="text"
|
||||
className="gf-form-input"
|
||||
placeholder="Search alert"
|
||||
value={alertList.search}
|
||||
onChange={this.onSearchQueryChange}
|
||||
/>
|
||||
<i className="gf-form-input-icon fa fa-search" />
|
||||
</label>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<label className="gf-form-label">Filter by state</label>
|
||||
<label className="gf-form-label">States</label>
|
||||
|
||||
<div className="gf-form-select-wrapper width-13">
|
||||
<select className="gf-form-input" onChange={this.onStateFilterChanged} value={alertList.stateFilter}>
|
||||
@@ -80,8 +86,12 @@ export class AlertRuleList extends React.Component<IContainerProps, any> {
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<section className="card-section card-list-layout-list">
|
||||
<ol className="card-list">{alertList.rules.map(rule => <AlertRuleItem rule={rule} key={rule.id} />)}</ol>
|
||||
<section>
|
||||
<ol className="alert-rule-list">
|
||||
{alertList.filteredRules.map(rule => (
|
||||
<AlertRuleItem rule={rule} key={rule.id} search={alertList.search} />
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@@ -99,6 +109,7 @@ function AlertStateFilterOption({ text, value }) {
|
||||
|
||||
export interface AlertRuleItemProps {
|
||||
rule: IAlertRule;
|
||||
search: string;
|
||||
}
|
||||
|
||||
@observer
|
||||
@@ -107,6 +118,16 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
|
||||
this.props.rule.togglePaused();
|
||||
};
|
||||
|
||||
renderText(text: string) {
|
||||
return (
|
||||
<Highlighter
|
||||
highlightClassName="highlight-search-match"
|
||||
textToHighlight={text}
|
||||
searchWords={[this.props.search]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { rule } = this.props;
|
||||
|
||||
@@ -119,36 +140,33 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
|
||||
let ruleUrl = `dashboard/${rule.dashboardUri}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`;
|
||||
|
||||
return (
|
||||
<li className="card-item-wrapper">
|
||||
<div className="card-item card-item--alert">
|
||||
<div className="card-item-header">
|
||||
<div className="card-item-type">
|
||||
<a
|
||||
className="card-item-cog"
|
||||
title="Pausing an alert rule prevents it from executing"
|
||||
onClick={this.toggleState}
|
||||
>
|
||||
<i className={stateClass} />
|
||||
</a>
|
||||
<a className="card-item-cog" href={ruleUrl} title="Edit alert rule">
|
||||
<i className="icon-gf icon-gf-settings" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-item-body">
|
||||
<div className="card-item-details">
|
||||
<div className="card-item-name">
|
||||
<a href={ruleUrl}>{rule.name}</a>
|
||||
</div>
|
||||
<div className="card-item-sub-name">
|
||||
<span className={`alert-list-item-state ${rule.stateClass}`}>
|
||||
<i className={rule.stateIcon} /> {rule.stateText}
|
||||
</span>
|
||||
<span> for {rule.stateAge}</span>
|
||||
</div>
|
||||
{rule.info && <div className="small muted">{rule.info}</div>}
|
||||
<li className="alert-rule-item">
|
||||
<span className={`alert-rule-item__icon ${rule.stateClass}`}>
|
||||
<i className={rule.stateIcon} />
|
||||
</span>
|
||||
<div className="alert-rule-item__body">
|
||||
<div className="alert-rule-item__header">
|
||||
<div className="alert-rule-item__name">
|
||||
<a href={ruleUrl}>{this.renderText(rule.name)}</a>
|
||||
</div>
|
||||
<div className="alert-rule-item__text">
|
||||
<span className={`${rule.stateClass}`}>{this.renderText(rule.stateText)}</span>
|
||||
<span className="alert-rule-item__time"> for {rule.stateAge}</span>
|
||||
</div>
|
||||
</div>
|
||||
{rule.info && <div className="small muted alert-rule-item__info">{this.renderText(rule.info)}</div>}
|
||||
</div>
|
||||
<div className="alert-rule-item__actions">
|
||||
<a
|
||||
className="btn btn-small btn-inverse alert-list__btn width-2"
|
||||
title="Pausing an alert rule prevents it from executing"
|
||||
onClick={this.toggleState}
|
||||
>
|
||||
<i className={stateClass} />
|
||||
</a>
|
||||
<a className="btn btn-small btn-inverse alert-list__btn width-2" href={ruleUrl} title="Edit alert rule">
|
||||
<i className="icon-gf icon-gf-settings" />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -2,71 +2,102 @@
|
||||
|
||||
exports[`AlertRuleList should render 1 rule 1`] = `
|
||||
<li
|
||||
className="card-item-wrapper"
|
||||
className="alert-rule-item"
|
||||
>
|
||||
<span
|
||||
className="alert-rule-item__icon alert-state-ok"
|
||||
>
|
||||
<i
|
||||
className="icon-gf icon-gf-online"
|
||||
/>
|
||||
</span>
|
||||
<div
|
||||
className="card-item card-item--alert"
|
||||
className="alert-rule-item__body"
|
||||
>
|
||||
<div
|
||||
className="card-item-header"
|
||||
className="alert-rule-item__header"
|
||||
>
|
||||
<div
|
||||
className="card-item-type"
|
||||
className="alert-rule-item__name"
|
||||
>
|
||||
<a
|
||||
className="card-item-cog"
|
||||
onClick={[Function]}
|
||||
title="Pausing an alert rule prevents it from executing"
|
||||
>
|
||||
<i
|
||||
className="fa fa-pause"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
className="card-item-cog"
|
||||
href="dashboard/db/mygool?panelId=3&fullscreen&edit&tab=alert"
|
||||
title="Edit alert rule"
|
||||
>
|
||||
<i
|
||||
className="icon-gf icon-gf-settings"
|
||||
/>
|
||||
<Highlighter
|
||||
highlightClassName="highlight-search-match"
|
||||
searchWords={
|
||||
Array [
|
||||
"",
|
||||
]
|
||||
}
|
||||
textToHighlight="Panel Title alert"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
className=""
|
||||
key="0"
|
||||
>
|
||||
Panel Title alert
|
||||
</span>
|
||||
</span>
|
||||
</Highlighter>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="card-item-body"
|
||||
>
|
||||
<div
|
||||
className="card-item-details"
|
||||
className="alert-rule-item__text"
|
||||
>
|
||||
<div
|
||||
className="card-item-name"
|
||||
<span
|
||||
className="alert-state-ok"
|
||||
>
|
||||
<a
|
||||
href="dashboard/db/mygool?panelId=3&fullscreen&edit&tab=alert"
|
||||
<Highlighter
|
||||
highlightClassName="highlight-search-match"
|
||||
searchWords={
|
||||
Array [
|
||||
"",
|
||||
]
|
||||
}
|
||||
textToHighlight="OK"
|
||||
>
|
||||
Panel Title alert
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
className="card-item-sub-name"
|
||||
<span>
|
||||
<span
|
||||
className=""
|
||||
key="0"
|
||||
>
|
||||
OK
|
||||
</span>
|
||||
</span>
|
||||
</Highlighter>
|
||||
</span>
|
||||
<span
|
||||
className="alert-rule-item__time"
|
||||
>
|
||||
<span
|
||||
className="alert-list-item-state alert-state-ok"
|
||||
>
|
||||
<i
|
||||
className="icon-gf icon-gf-online"
|
||||
/>
|
||||
|
||||
OK
|
||||
</span>
|
||||
<span>
|
||||
for
|
||||
5 minutes
|
||||
</span>
|
||||
</div>
|
||||
for
|
||||
5 minutes
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="alert-rule-item__actions"
|
||||
>
|
||||
<a
|
||||
className="btn btn-small btn-inverse alert-list__btn width-2"
|
||||
onClick={[Function]}
|
||||
title="Pausing an alert rule prevents it from executing"
|
||||
>
|
||||
<i
|
||||
className="fa fa-pause"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
className="btn btn-small btn-inverse alert-list__btn width-2"
|
||||
href="dashboard/db/mygool?panelId=3&fullscreen&edit&tab=alert"
|
||||
title="Edit alert rule"
|
||||
>
|
||||
<i
|
||||
className="icon-gf icon-gf-settings"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
|
||||
@@ -138,10 +138,6 @@ function getAlertAnnotationInfo(ah) {
|
||||
return 'Error: ' + ah.data.error;
|
||||
}
|
||||
|
||||
if (ah.data.noData || ah.data.no_data) {
|
||||
return 'No Data';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<li ng-class="{active: ctrl.subTabIndex === 2}">
|
||||
<a ng-click="ctrl.changeTabIndex(2)">State history</a>
|
||||
</li>
|
||||
<li>
|
||||
<li>
|
||||
<a ng-click="ctrl.delete()">Delete</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -143,36 +143,33 @@
|
||||
<i>No state changes recorded</i>
|
||||
</div>
|
||||
|
||||
<section class="card-section card-list-layout-list">
|
||||
<ol class="card-list" >
|
||||
<li class="card-item-wrapper" ng-repeat="ah in ctrl.alertHistory">
|
||||
<div class="alert-list card-item card-item--alert">
|
||||
<div class="alert-list-body">
|
||||
<div class="alert-list-icon alert-list-item-state {{ah.stateModel.stateClass}}">
|
||||
<i class="{{ah.stateModel.iconClass}}"></i>
|
||||
</div>
|
||||
<div class="alert-list-main alert-list-text">
|
||||
<span class="alert-list-state {{ah.stateModel.stateClass}}">{{ah.stateModel.text}}</span>
|
||||
<span class="alert-list-info">{{ah.info}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert-list-footer alert-list-text">
|
||||
<span>{{ah.time}}</span>
|
||||
<span><!--Img Link--></span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="alert-rule-list" >
|
||||
<li class="alert-rule-item" ng-repeat="al in ctrl.alertHistory">
|
||||
<div class="alert-rule-item__icon {{al.stateModel.stateClass}}">
|
||||
<i class="{{al.stateModel.iconClass}}"></i>
|
||||
</div>
|
||||
<div class="alert-rule-item__body">
|
||||
<div class="alert-rule-item__header">
|
||||
<div class="alert-rule-item__text">
|
||||
<span class="{{al.stateModel.stateClass}}">{{al.stateModel.text}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="alert-list-info">{{al.info}}</span>
|
||||
</div>
|
||||
<div class="alert-rule-item__time">
|
||||
<span>{{al.time}}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group" ng-if="!ctrl.alert">
|
||||
<div class="gf-form-button-row">
|
||||
<button class="btn btn-inverse" ng-click="ctrl.enable()">
|
||||
<i class="icon-gf icon-gf-alert"></i>
|
||||
Create Alert
|
||||
</button>
|
||||
</div>
|
||||
<div class="gf-form-button-row">
|
||||
<button class="btn btn-inverse" ng-click="ctrl.enable()">
|
||||
<i class="icon-gf icon-gf-alert"></i>
|
||||
Create Alert
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,24 +3,22 @@
|
||||
{{ctrl.noAlertsMessage}}
|
||||
</div>
|
||||
|
||||
<section class="card-section card-list-layout-list" ng-if="ctrl.panel.show === 'current'">
|
||||
<ol class="card-list">
|
||||
<li class="card-item-wrapper" ng-repeat="alert in ctrl.currentAlerts">
|
||||
<div class="alert-list card-item card-item--alert">
|
||||
<div class="alert-list-body">
|
||||
<div class="alert-list-icon alert-list-item-state {{alert.stateModel.stateClass}}">
|
||||
<i class="{{alert.stateModel.iconClass}}"></i>
|
||||
</div>
|
||||
<div class="alert-list-main">
|
||||
<p class="alert-list-title">
|
||||
<a href="dashboard/{{alert.dashboardUri}}?panelId={{alert.panelId}}&fullscreen&edit&tab=alert">
|
||||
{{alert.name}}
|
||||
</a>
|
||||
</p>
|
||||
<p class="alert-list-text">
|
||||
<span class="alert-list-state {{alert.stateModel.stateClass}}">{{alert.stateModel.text}}</span>
|
||||
for {{alert.newStateDateAgo}}
|
||||
</p>
|
||||
<section ng-if="ctrl.panel.show === 'current'">
|
||||
<ol class="alert-rule-list">
|
||||
<li class="alert-rule-item" ng-repeat="alert in ctrl.currentAlerts">
|
||||
<div class="alert-rule-item__body">
|
||||
<div class="alert-rule-item__icon {{alert.stateModel.stateClass}}">
|
||||
<i class="{{alert.stateModel.iconClass}}"></i>
|
||||
</div>
|
||||
<div class="alert-rule-item__header">
|
||||
<p class="alert-rule-item__name">
|
||||
<a href="dashboard/{{alert.dashboardUri}}?panelId={{alert.panelId}}&fullscreen&edit&tab=alert">
|
||||
{{alert.name}}
|
||||
</a>
|
||||
</p>
|
||||
<div class="alert-rule-item__text">
|
||||
<span class="{{alert.stateModel.stateClass}}">{{alert.stateModel.text}}</span>
|
||||
<span class="alert-rule-item__time">for {{alert.newStateDateAgo}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,30 +26,25 @@
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="card-section card-list-layout-list" ng-if="ctrl.panel.show === 'changes'">
|
||||
<ol class="card-list">
|
||||
<li class="card-item-wrapper" ng-repeat="al in ctrl.alertHistory">
|
||||
<div class="alert-list card-item card-item--alert">
|
||||
<div class="alert-list-body">
|
||||
<div class="alert-list-icon alert-list-item-state {{al.stateModel.stateClass}}">
|
||||
<i class="{{al.stateModel.iconClass}}"></i>
|
||||
</div>
|
||||
<div class="alert-list-main">
|
||||
<p class="alert-list-title">{{al.alertName}}</p>
|
||||
<div class="alert-list-text">
|
||||
<span class="alert-list-state {{al.stateModel.stateClass}}">{{al.stateModel.text}}</span>
|
||||
<span class="alert-list-info alert-list-info-left">{{al.info}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert-list-footer">
|
||||
<span class="alert-list-text">{{al.time}}</span>
|
||||
<span class="alert-list-text">
|
||||
<!--Img Link-->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
<section ng-if="ctrl.panel.show === 'changes'">
|
||||
<ol class="alert-rule-list">
|
||||
<li class="alert-rule-item" ng-repeat="al in ctrl.alertHistory">
|
||||
<div class="alert-rule-item__icon {{al.stateModel.stateClass}}">
|
||||
<i class="{{al.stateModel.iconClass}}"></i>
|
||||
</div>
|
||||
<div class="alert-rule-item__body">
|
||||
<div class="alert-rule-item__header">
|
||||
<p class="alert-rule-item__name">{{al.alertName}}</p>
|
||||
<div class="alert-rule-item__text">
|
||||
<span class="{{al.stateModel.stateClass}}">{{al.stateModel.text}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="alert-rule-item__info">{{al.info}}</span>
|
||||
</div>
|
||||
<div class="alert-rule-item__time">
|
||||
<span>{{al.time}}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
65
public/app/stores/AlertListStore/AlertListStore.jest.ts
Normal file
65
public/app/stores/AlertListStore/AlertListStore.jest.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { AlertListStore } from './AlertListStore';
|
||||
import { backendSrv } from 'test/mocks/common';
|
||||
import moment from 'moment';
|
||||
|
||||
function getRule(name, state, info) {
|
||||
return {
|
||||
id: 11,
|
||||
dashboardId: 58,
|
||||
panelId: 3,
|
||||
name: name,
|
||||
state: state,
|
||||
newStateDate: moment()
|
||||
.subtract(5, 'minutes')
|
||||
.format(),
|
||||
evalData: {},
|
||||
executionError: '',
|
||||
dashboardUri: 'db/mygool',
|
||||
stateText: state,
|
||||
stateIcon: 'fa',
|
||||
stateClass: 'asd',
|
||||
stateAge: '10m',
|
||||
info: info,
|
||||
};
|
||||
}
|
||||
|
||||
describe('AlertListStore', () => {
|
||||
let store;
|
||||
|
||||
beforeAll(() => {
|
||||
store = AlertListStore.create(
|
||||
{
|
||||
rules: [
|
||||
getRule('Europe', 'OK', 'backend-01'),
|
||||
getRule('Google', 'ALERTING', 'backend-02'),
|
||||
getRule('Amazon', 'PAUSED', 'backend-03'),
|
||||
getRule('West-Europe', 'PAUSED', 'backend-03'),
|
||||
],
|
||||
search: '',
|
||||
},
|
||||
{
|
||||
backendSrv: backendSrv,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('search should filter list on name', () => {
|
||||
store.setSearchQuery('urope');
|
||||
expect(store.filteredRules).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('search should filter list on state', () => {
|
||||
store.setSearchQuery('ale');
|
||||
expect(store.filteredRules).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('search should filter list on info', () => {
|
||||
store.setSearchQuery('-0');
|
||||
expect(store.filteredRules).toHaveLength(4);
|
||||
});
|
||||
|
||||
it('search should be equal', () => {
|
||||
store.setSearchQuery('alert');
|
||||
expect(store.search).toBe('alert');
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,16 @@ export const AlertListStore = types
|
||||
.model('AlertListStore', {
|
||||
rules: types.array(AlertRule),
|
||||
stateFilter: types.optional(types.string, 'all'),
|
||||
search: types.optional(types.string, ''),
|
||||
})
|
||||
.views(self => ({
|
||||
get filteredRules() {
|
||||
let regex = new RegExp(self.search, 'i');
|
||||
return self.rules.filter(alert => {
|
||||
return regex.test(alert.name) || regex.test(alert.stateText) || regex.test(alert.info);
|
||||
});
|
||||
},
|
||||
}))
|
||||
.actions(self => ({
|
||||
loadRules: flow(function* load(filters) {
|
||||
const backendSrv = getEnv(self).backendSrv;
|
||||
@@ -31,4 +40,7 @@ export const AlertListStore = types
|
||||
self.rules.push(AlertRule.create(rule));
|
||||
}
|
||||
}),
|
||||
setSearchQuery(query: string) {
|
||||
self.search = query;
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user