From 50444c32e00b82c903bdf280499d1c4641cb43f1 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 3 Sep 2018 13:46:39 +0200 Subject: [PATCH] actions and reducers for search filter --- .../features/alerting/AlertRuleItem.test.tsx | 32 +++++ .../app/features/alerting/AlertRuleItem.tsx | 70 +++++++++++ .../app/features/alerting/AlertRuleList.tsx | 112 ++++-------------- .../__snapshots__/AlertRuleItem.test.tsx.snap | 85 +++++++++++++ public/app/features/alerting/state/actions.ts | 21 +++- .../app/features/alerting/state/reducers.ts | 17 +-- .../app/features/alerting/state/selectors.ts | 9 ++ public/app/types/index.ts | 7 +- 8 files changed, 251 insertions(+), 102 deletions(-) create mode 100644 public/app/features/alerting/AlertRuleItem.test.tsx create mode 100644 public/app/features/alerting/AlertRuleItem.tsx create mode 100644 public/app/features/alerting/__snapshots__/AlertRuleItem.test.tsx.snap create mode 100644 public/app/features/alerting/state/selectors.ts diff --git a/public/app/features/alerting/AlertRuleItem.test.tsx b/public/app/features/alerting/AlertRuleItem.test.tsx new file mode 100644 index 00000000000..0a1c5cbe437 --- /dev/null +++ b/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(); +}; + +describe('Render', () => { + it('should render component', () => { + const wrapper = setup(); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/public/app/features/alerting/AlertRuleItem.tsx b/public/app/features/alerting/AlertRuleItem.tsx new file mode 100644 index 00000000000..4c8d74cd6e3 --- /dev/null +++ b/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 { + toggleState = () => { + // this.props.rule.togglePaused(); + }; + + renderText(text: string) { + return ( + + ); + } + + 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 ( +
  • + + + +
    +
    + +
    + {this.renderText(rule.stateText)} + for {rule.stateAge} +
    +
    + {rule.info &&
    {this.renderText(rule.info)}
    } +
    + +
    + + + + +
    +
  • + ); + } +} diff --git a/public/app/features/alerting/AlertRuleList.tsx b/public/app/features/alerting/AlertRuleList.tsx index 77e5af520fc..0adadb0f6d0 100644 --- a/public/app/features/alerting/AlertRuleList.tsx +++ b/public/app/features/alerting/AlertRuleList.tsx @@ -1,21 +1,23 @@ import React, { PureComponent } from 'react'; import { hot } from 'react-hot-loader'; import { connect } from 'react-redux'; -import classNames from 'classnames'; import PageHeader from 'app/core/components/PageHeader/PageHeader'; +import AlertRuleItem from './AlertRuleItem'; import appEvents from 'app/core/app_events'; -import Highlighter from 'react-highlight-words'; import { updateLocation } from 'app/core/actions'; import { getNavModel } from 'app/core/selectors/navModel'; 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 { navModel: NavModel; alertRules: AlertRule[]; updateLocation: typeof updateLocation; getAlertRulesAsync: typeof getAlertRulesAsync; + setSearchQuery: typeof setSearchQuery; stateFilter: string; + search: string; } interface State { @@ -32,14 +34,6 @@ export class AlertRuleList extends PureComponent { { text: 'Paused', value: 'paused' }, ]; - constructor(props) { - super(props); - - this.state = { - search: '', - }; - } - componentDidMount() { console.log('did mount'); this.fetchRules(); @@ -77,13 +71,21 @@ export class AlertRuleList extends PureComponent { }); }; - onSearchQueryChange = evt => { - // this.props.alertList.setSearchQuery(evt.target.value); + onSearchQueryChange = event => { + const { value } = event.target; + this.props.setSearchQuery(value); }; + alertStateFilterOption({ text, value }) { + return ( + + ); + } + render() { - const { navModel, alertRules } = this.props; - const { search } = this.state; + const { navModel, alertRules, search } = this.props; return (
    @@ -107,7 +109,7 @@ export class AlertRuleList extends PureComponent {
    @@ -130,89 +132,17 @@ export class AlertRuleList extends PureComponent { } } -function AlertStateFilterOption({ text, value }) { - return ( - - ); -} - -export interface AlertRuleItemProps { - rule: AlertRule; - search: string; -} - -export class AlertRuleItem extends React.Component { - toggleState = () => { - // this.props.rule.togglePaused(); - }; - - renderText(text: string) { - return ( - - ); - } - - 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 ( -
  • - - - -
    -
    - -
    - {this.renderText(rule.stateText)} - for {rule.stateAge} -
    -
    - {rule.info &&
    {this.renderText(rule.info)}
    } -
    - -
    - - - - -
    -
  • - ); - } -} - const mapStateToProps = (state: StoreState) => ({ navModel: getNavModel(state.navIndex, 'alert-list'), - alertRules: state.alertRules, + alertRules: getAlertRuleItems(state.alertRules), stateFilter: state.location.query.state, + search: getSearchQuery(state.alertRules), }); const mapDispatchToProps = { updateLocation, getAlertRulesAsync, + setSearchQuery, }; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(AlertRuleList)); diff --git a/public/app/features/alerting/__snapshots__/AlertRuleItem.test.tsx.snap b/public/app/features/alerting/__snapshots__/AlertRuleItem.test.tsx.snap new file mode 100644 index 00000000000..7d3c446fc55 --- /dev/null +++ b/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`] = ` +
  • + + + +
    +
    +
    + + + +
    +
    + + + + + for + age + +
    +
    +
    +
    + + + + +
    +
  • +`; diff --git a/public/app/features/alerting/state/actions.ts b/public/app/features/alerting/state/actions.ts index 9103b34e81d..3b80cb19c39 100644 --- a/public/app/features/alerting/state/actions.ts +++ b/public/app/features/alerting/state/actions.ts @@ -2,17 +2,32 @@ import { Dispatch } from 'redux'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { AlertRule } from 'app/types'; +export enum ActionTypes { + LoadAlertRules = 'LOAD_ALERT_RULES', + SetSearchQuery = 'SET_SEARCH_QUERY', +} + export interface LoadAlertRulesAction { - type: 'LOAD_ALERT_RULES'; + type: ActionTypes.LoadAlertRules; payload: AlertRule[]; } +export interface SetSearchQueryAction { + type: ActionTypes.SetSearchQuery; + payload: string; +} + export const loadAlertRules = (rules: AlertRule[]): LoadAlertRulesAction => ({ - type: 'LOAD_ALERT_RULES', + type: ActionTypes.LoadAlertRules, 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 ( dispatch: Dispatch diff --git a/public/app/features/alerting/state/reducers.ts b/public/app/features/alerting/state/reducers.ts index 0718c511106..a18d112dd94 100644 --- a/public/app/features/alerting/state/reducers.ts +++ b/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 { 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) { const stateModel = alertDef.getStateDisplayModel(state); @@ -16,9 +16,9 @@ export function setStateFields(rule, state) { .replace(' ago', ''); } -export const alertRulesReducer = (state = initialState, action: Action): AlertRule[] => { +export const alertRulesReducer = (state = initialState, action: Action): AlertRulesState => { switch (action.type) { - case 'LOAD_ALERT_RULES': { + case ActionTypes.LoadAlertRules: { const alertRules = action.payload; 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; diff --git a/public/app/features/alerting/state/selectors.ts b/public/app/features/alerting/state/selectors.ts new file mode 100644 index 00000000000..7c72520d773 --- /dev/null +++ b/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); + }); +}; diff --git a/public/app/types/index.ts b/public/app/types/index.ts index a409f586f33..6aa4dc23d97 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -69,8 +69,13 @@ export type NavIndex = { [s: string]: NavModelItem }; // Store // +export interface AlertRulesState { + items: AlertRule[]; + searchQuery: string; +} + export interface StoreState { navIndex: NavIndex; location: LocationState; - alertRules: AlertRule[]; + alertRules: AlertRulesState; }