浏览代码

actions and reducers for search filter

Peter Holmberg 7 年之前
父节点
当前提交
50444c32e0

+ 32 - 0
public/app/features/alerting/AlertRuleItem.test.tsx

@@ -0,0 +1,32 @@
+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();
+  });
+});

+ 70 - 0
public/app/features/alerting/AlertRuleItem.tsx

@@ -0,0 +1,70 @@
+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>
+    );
+  }
+}

+ 21 - 91
public/app/features/alerting/AlertRuleList.tsx

@@ -1,21 +1,23 @@
 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> {
     { 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> {
     });
     });
   };
   };
 
 
-  onSearchQueryChange = evt => {
-    // this.props.alertList.setSearchQuery(evt.target.value);
+  onSearchQueryChange = event => {
+    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 { search } = this.state;
+    const { navModel, alertRules, search } = this.props;
 
 
     return (
     return (
       <div>
       <div>
@@ -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> {
   }
   }
 }
 }
 
 
-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));

+ 85 - 0
public/app/features/alerting/__snapshots__/AlertRuleItem.test.tsx.snap

@@ -0,0 +1,85 @@
+// 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>
+`;

+ 18 - 3
public/app/features/alerting/state/actions.ts

@@ -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>

+ 10 - 7
public/app/features/alerting/state/reducers.ts

@@ -1,9 +1,9 @@
-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) {
     .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
         }
         }
       }
       }
 
 
-      return alertRules;
+      return { items: alertRules, searchQuery: state.searchQuery };
     }
     }
+
+    case ActionTypes.SetSearchQuery:
+      return { items: state.items, searchQuery: action.payload };
   }
   }
 
 
   return state;
   return state;

+ 9 - 0
public/app/features/alerting/state/selectors.ts

@@ -0,0 +1,9 @@
+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);
+  });
+};

+ 6 - 1
public/app/types/index.ts

@@ -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;
 }
 }