Commit 50444c32 by Peter Holmberg

actions and reducers for search filter

parent 42aaa2b9
import React from 'react';
import { shallow } from 'enzyme';
import AlertRuleItem, { Props } from './AlertRuleItem';
const setup = (propOverrides?: object) => {
const props: Props = {
rule: {
id: 1,
dashboardId: 1,
panelId: 1,
name: 'Some rule',
state: 'Open',
stateText: 'state text',
stateIcon: 'icon',
stateClass: 'state class',
stateAge: 'age',
url: 'https://something.something.darkside',
},
search: '',
};
Object.assign(props, propOverrides);
return shallow(<AlertRuleItem {...props} />);
};
describe('Render', () => {
it('should render component', () => {
const wrapper = setup();
expect(wrapper).toMatchSnapshot();
});
});
import React from 'react';
import Highlighter from 'react-highlight-words';
import classNames from 'classnames/bind';
import { AlertRule } from '../../types';
export interface Props {
rule: AlertRule;
search: string;
}
export default class AlertRuleItem extends React.Component<Props, any> {
toggleState = () => {
// this.props.rule.togglePaused();
};
renderText(text: string) {
return (
<Highlighter
highlightClassName="highlight-search-match"
textToHighlight={text}
searchWords={[this.props.search]}
/>
);
}
render() {
const { rule } = this.props;
const stateClass = classNames({
fa: true,
'fa-play': rule.state === 'paused',
'fa-pause': rule.state !== 'paused',
});
const ruleUrl = `${rule.url}?panelId=${rule.panelId}&fullscreen=true&edit=true&tab=alert`;
return (
<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">
<button
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} />
</button>
<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>
);
}
}
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import classNames from 'classnames';
import PageHeader from 'app/core/components/PageHeader/PageHeader'; import PageHeader from 'app/core/components/PageHeader/PageHeader';
import AlertRuleItem from './AlertRuleItem';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import Highlighter from 'react-highlight-words';
import { updateLocation } from 'app/core/actions'; import { updateLocation } from 'app/core/actions';
import { getNavModel } from 'app/core/selectors/navModel'; import { getNavModel } from 'app/core/selectors/navModel';
import { NavModel, StoreState, AlertRule } from 'app/types'; import { NavModel, StoreState, AlertRule } from 'app/types';
import { getAlertRulesAsync } from './state/actions'; import { getAlertRulesAsync, setSearchQuery } from './state/actions';
import { getAlertRuleItems, getSearchQuery } from './state/selectors';
interface Props { interface Props {
navModel: NavModel; navModel: NavModel;
alertRules: AlertRule[]; alertRules: AlertRule[];
updateLocation: typeof updateLocation; updateLocation: typeof updateLocation;
getAlertRulesAsync: typeof getAlertRulesAsync; getAlertRulesAsync: typeof getAlertRulesAsync;
setSearchQuery: typeof setSearchQuery;
stateFilter: string; stateFilter: string;
search: string;
} }
interface State { interface State {
...@@ -32,14 +34,6 @@ export class AlertRuleList extends PureComponent<Props, State> { ...@@ -32,14 +34,6 @@ export class AlertRuleList extends PureComponent<Props, State> {
{ text: 'Paused', value: 'paused' }, { text: 'Paused', value: 'paused' },
]; ];
constructor(props) {
super(props);
this.state = {
search: '',
};
}
componentDidMount() { componentDidMount() {
console.log('did mount'); console.log('did mount');
this.fetchRules(); this.fetchRules();
...@@ -77,13 +71,21 @@ export class AlertRuleList extends PureComponent<Props, State> { ...@@ -77,13 +71,21 @@ export class AlertRuleList extends PureComponent<Props, State> {
}); });
}; };
onSearchQueryChange = evt => { onSearchQueryChange = event => {
// this.props.alertList.setSearchQuery(evt.target.value); const { value } = event.target;
this.props.setSearchQuery(value);
}; };
alertStateFilterOption({ text, value }) {
return (
<option key={value} value={value}>
{text}
</option>
);
}
render() { render() {
const { navModel, alertRules } = this.props; const { navModel, alertRules, search } = this.props;
const { search } = this.state;
return ( return (
<div> <div>
...@@ -107,7 +109,7 @@ export class AlertRuleList extends PureComponent<Props, State> { ...@@ -107,7 +109,7 @@ export class AlertRuleList extends PureComponent<Props, State> {
<div className="gf-form-select-wrapper width-13"> <div className="gf-form-select-wrapper width-13">
<select className="gf-form-input" onChange={this.onStateFilterChanged} value={this.getStateFilter()}> <select className="gf-form-input" onChange={this.onStateFilterChanged} value={this.getStateFilter()}>
{this.stateFilters.map(AlertStateFilterOption)} {this.stateFilters.map(this.alertStateFilterOption)}
</select> </select>
</div> </div>
</div> </div>
...@@ -130,89 +132,17 @@ export class AlertRuleList extends PureComponent<Props, State> { ...@@ -130,89 +132,17 @@ export class AlertRuleList extends PureComponent<Props, State> {
} }
} }
function AlertStateFilterOption({ text, value }) {
return (
<option key={value} value={value}>
{text}
</option>
);
}
export interface AlertRuleItemProps {
rule: AlertRule;
search: string;
}
export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
toggleState = () => {
// this.props.rule.togglePaused();
};
renderText(text: string) {
return (
<Highlighter
highlightClassName="highlight-search-match"
textToHighlight={text}
searchWords={[this.props.search]}
/>
);
}
render() {
const { rule } = this.props;
const stateClass = classNames({
fa: true,
'fa-play': rule.state === 'paused',
'fa-pause': rule.state !== 'paused',
});
const ruleUrl = `${rule.url}?panelId=${rule.panelId}&fullscreen=true&edit=true&tab=alert`;
return (
<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">
<button
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} />
</button>
<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>
);
}
}
const mapStateToProps = (state: StoreState) => ({ const mapStateToProps = (state: StoreState) => ({
navModel: getNavModel(state.navIndex, 'alert-list'), navModel: getNavModel(state.navIndex, 'alert-list'),
alertRules: state.alertRules, alertRules: getAlertRuleItems(state.alertRules),
stateFilter: state.location.query.state, stateFilter: state.location.query.state,
search: getSearchQuery(state.alertRules),
}); });
const mapDispatchToProps = { const mapDispatchToProps = {
updateLocation, updateLocation,
getAlertRulesAsync, getAlertRulesAsync,
setSearchQuery,
}; };
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(AlertRuleList)); export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(AlertRuleList));
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = `
<li
className="alert-rule-item"
>
<span
className="alert-rule-item__icon state class"
>
<i
className="icon"
/>
</span>
<div
className="alert-rule-item__body"
>
<div
className="alert-rule-item__header"
>
<div
className="alert-rule-item__name"
>
<a
href="https://something.something.darkside?panelId=1&fullscreen=true&edit=true&tab=alert"
>
<Highlighter
highlightClassName="highlight-search-match"
searchWords={
Array [
"",
]
}
textToHighlight="Some rule"
/>
</a>
</div>
<div
className="alert-rule-item__text"
>
<span
className="state class"
>
<Highlighter
highlightClassName="highlight-search-match"
searchWords={
Array [
"",
]
}
textToHighlight="state text"
/>
</span>
<span
className="alert-rule-item__time"
>
for
age
</span>
</div>
</div>
</div>
<div
className="alert-rule-item__actions"
>
<button
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"
/>
</button>
<a
className="btn btn-small btn-inverse alert-list__btn width-2"
href="https://something.something.darkside?panelId=1&fullscreen=true&edit=true&tab=alert"
title="Edit alert rule"
>
<i
className="icon-gf icon-gf-settings"
/>
</a>
</div>
</li>
`;
...@@ -2,17 +2,32 @@ import { Dispatch } from 'redux'; ...@@ -2,17 +2,32 @@ import { Dispatch } from 'redux';
import { getBackendSrv } from 'app/core/services/backend_srv'; import { getBackendSrv } from 'app/core/services/backend_srv';
import { AlertRule } from 'app/types'; import { AlertRule } from 'app/types';
export enum ActionTypes {
LoadAlertRules = 'LOAD_ALERT_RULES',
SetSearchQuery = 'SET_SEARCH_QUERY',
}
export interface LoadAlertRulesAction { export interface LoadAlertRulesAction {
type: 'LOAD_ALERT_RULES'; type: ActionTypes.LoadAlertRules;
payload: AlertRule[]; payload: AlertRule[];
} }
export interface SetSearchQueryAction {
type: ActionTypes.SetSearchQuery;
payload: string;
}
export const loadAlertRules = (rules: AlertRule[]): LoadAlertRulesAction => ({ export const loadAlertRules = (rules: AlertRule[]): LoadAlertRulesAction => ({
type: 'LOAD_ALERT_RULES', type: ActionTypes.LoadAlertRules,
payload: rules, payload: rules,
}); });
export type Action = LoadAlertRulesAction; export const setSearchQuery = (query: string): SetSearchQueryAction => ({
type: ActionTypes.SetSearchQuery,
payload: query,
});
export type Action = LoadAlertRulesAction | SetSearchQueryAction;
export const getAlertRulesAsync = (options: { state: string }) => async ( export const getAlertRulesAsync = (options: { state: string }) => async (
dispatch: Dispatch<Action> dispatch: Dispatch<Action>
......
import { Action } from './actions';
import { AlertRule } from 'app/types';
import alertDef from './alertDef';
import moment from 'moment'; import moment from 'moment';
import { AlertRulesState } from 'app/types';
import { Action, ActionTypes } from './actions';
import alertDef from './alertDef';
export const initialState: AlertRule[] = []; export const initialState: AlertRulesState = { items: [], searchQuery: '' };
export function setStateFields(rule, state) { export function setStateFields(rule, state) {
const stateModel = alertDef.getStateDisplayModel(state); const stateModel = alertDef.getStateDisplayModel(state);
...@@ -16,9 +16,9 @@ export function setStateFields(rule, state) { ...@@ -16,9 +16,9 @@ export function setStateFields(rule, state) {
.replace(' ago', ''); .replace(' ago', '');
} }
export const alertRulesReducer = (state = initialState, action: Action): AlertRule[] => { export const alertRulesReducer = (state = initialState, action: Action): AlertRulesState => {
switch (action.type) { switch (action.type) {
case 'LOAD_ALERT_RULES': { case ActionTypes.LoadAlertRules: {
const alertRules = action.payload; const alertRules = action.payload;
for (const rule of alertRules) { for (const rule of alertRules) {
...@@ -34,8 +34,11 @@ export const alertRulesReducer = (state = initialState, action: Action): AlertRu ...@@ -34,8 +34,11 @@ export const alertRulesReducer = (state = initialState, action: Action): AlertRu
} }
} }
return alertRules; return { items: alertRules, searchQuery: state.searchQuery };
} }
case ActionTypes.SetSearchQuery:
return { items: state.items, searchQuery: action.payload };
} }
return state; return state;
......
export const getSearchQuery = state => state.searchQuery;
export const getAlertRuleItems = state => {
const regex = new RegExp(state.searchQuery, 'i');
return state.items.filter(item => {
return regex.test(item.name) || regex.test(item.stateText) || regex.test(item.info);
});
};
...@@ -69,8 +69,13 @@ export type NavIndex = { [s: string]: NavModelItem }; ...@@ -69,8 +69,13 @@ export type NavIndex = { [s: string]: NavModelItem };
// Store // Store
// //
export interface AlertRulesState {
items: AlertRule[];
searchQuery: string;
}
export interface StoreState { export interface StoreState {
navIndex: NavIndex; navIndex: NavIndex;
location: LocationState; location: LocationState;
alertRules: AlertRule[]; alertRules: AlertRulesState;
} }
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