diff --git a/lerna.json b/lerna.json index 18ae4a15842..828194eeac9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,8 +1,6 @@ { "npmClient": "yarn", "useWorkspaces": true, - "packages": [ - "packages/*" - ], + "packages": ["packages/*"], "version": "7.5.0-pre.0" } diff --git a/public/app/features/alerting/AlertRuleItem.tsx b/public/app/features/alerting/AlertRuleItem.tsx index e4ea70ed7ac..cd170062b05 100644 --- a/public/app/features/alerting/AlertRuleItem.tsx +++ b/public/app/features/alerting/AlertRuleItem.tsx @@ -1,7 +1,6 @@ import React, { useCallback } from 'react'; // @ts-ignore import Highlighter from 'react-highlight-words'; -import { css } from 'emotion'; import { Icon, IconName, Button, LinkButton, Card } from '@grafana/ui'; import { AlertRule } from '../../types'; @@ -26,39 +25,33 @@ const AlertRuleItem = ({ rule, search, onTogglePause }: Props) => { ); return ( -
  • - {renderText(rule.name)}}> - - - - - - - {renderText(rule.stateText)}{' '} - - for {rule.stateAge} + {renderText(rule.name)}}> + + + + + + + {renderText(rule.stateText)}{' '} - {rule.info ? renderText(rule.info) : null} - - - - - Edit alert - - - -
  • + for {rule.stateAge} + + {rule.info ? renderText(rule.info) : null} + + + + + Edit alert + + + ); }; diff --git a/public/app/features/alerting/AlertRuleList.tsx b/public/app/features/alerting/AlertRuleList.tsx index 8ccc1408d51..6515d13befe 100644 --- a/public/app/features/alerting/AlertRuleList.tsx +++ b/public/app/features/alerting/AlertRuleList.tsx @@ -6,17 +6,18 @@ import AlertRuleItem from './AlertRuleItem'; import appEvents from 'app/core/app_events'; import { updateLocation } from 'app/core/actions'; import { getNavModel } from 'app/core/selectors/navModel'; -import { AlertRule, CoreEvents, StoreState } from 'app/types'; +import { AlertDefinition, AlertRule, CoreEvents, StoreState } from 'app/types'; import { getAlertRulesAsync, togglePauseAlertRule } from './state/actions'; import { getAlertRuleItems, getSearchQuery } from './state/selectors'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { NavModel, SelectableValue } from '@grafana/data'; import { setSearchQuery } from './state/reducers'; -import { Button, Select } from '@grafana/ui'; +import { Button, Select, VerticalGroup } from '@grafana/ui'; +import { AlertDefinitionItem } from './components/AlertDefinitionItem'; export interface Props { navModel: NavModel; - alertRules: AlertRule[]; + alertRules: Array; updateLocation: typeof updateLocation; getAlertRulesAsync: typeof getAlertRulesAsync; setSearchQuery: typeof setSearchQuery; @@ -121,18 +122,28 @@ export class AlertRuleList extends PureComponent { How to add an alert -
    -
      - {alertRules.map((rule) => ( - + {alertRules.map((rule, index) => { + // Alert definition has "title" as name property. + if (rule.hasOwnProperty('name')) { + return ( + this.onTogglePause(rule as AlertRule)} + /> + ); + } + return ( + this.onTogglePause(rule)} /> - ))} -
    -
    + ); + })} + ); @@ -141,10 +152,11 @@ export class AlertRuleList extends PureComponent { const mapStateToProps = (state: StoreState) => ({ navModel: getNavModel(state.navIndex, 'alert-list'), - alertRules: getAlertRuleItems(state.alertRules), + alertRules: getAlertRuleItems(state), stateFilter: state.location.query.state, search: getSearchQuery(state.alertRules), isLoading: state.alertRules.isLoading, + ngAlertDefinitions: state.alertDefinition.alertDefinitions, }); const mapDispatchToProps = { diff --git a/public/app/features/alerting/NextGenAlertingPage.tsx b/public/app/features/alerting/NextGenAlertingPage.tsx index 089ed5d01d5..59352247da8 100644 --- a/public/app/features/alerting/NextGenAlertingPage.tsx +++ b/public/app/features/alerting/NextGenAlertingPage.tsx @@ -162,13 +162,10 @@ export default hot(module)( const getStyles = stylesFactory((theme: GrafanaTheme) => ({ wrapper: css` - width: 100%; + width: calc(100% - 55px); height: 100%; position: fixed; - z-index: ${theme.zIndex.sidemenu}; top: 0; - left: 0; - right: 0; bottom: 0; background: ${theme.colors.dashboardBg}; display: flex; diff --git a/public/app/features/alerting/__snapshots__/AlertRuleList.test.tsx.snap b/public/app/features/alerting/__snapshots__/AlertRuleList.test.tsx.snap index 1417adbade2..bb9a2e3fabb 100644 --- a/public/app/features/alerting/__snapshots__/AlertRuleList.test.tsx.snap +++ b/public/app/features/alerting/__snapshots__/AlertRuleList.test.tsx.snap @@ -80,54 +80,52 @@ exports[`Render should render alert rules 1`] = ` How to add an alert -
    -
      - + - + -
    -
    + } + search="" + /> + `; @@ -212,11 +210,9 @@ exports[`Render should render component 1`] = ` How to add an alert -
    -
      -
    + `; diff --git a/public/app/features/alerting/components/AlertDefinitionItem.tsx b/public/app/features/alerting/components/AlertDefinitionItem.tsx new file mode 100644 index 00000000000..4d52a5a5c56 --- /dev/null +++ b/public/app/features/alerting/components/AlertDefinitionItem.tsx @@ -0,0 +1,38 @@ +import React, { FC } from 'react'; +// @ts-ignore +import Highlighter from 'react-highlight-words'; +import { Card, FeatureBadge, Icon } from '@grafana/ui'; +import { AlertDefinition } from 'app/types'; +import { FeatureState } from '@grafana/data'; + +interface Props { + alertDefinition: AlertDefinition; + search: string; +} + +export const AlertDefinitionItem: FC = ({ alertDefinition, search }) => { + return ( + + + + + + + {alertDefinition.description} + + + + ); +}; + +const CardTitle = (title: string, search: string) => ( +
    + + +
    +); diff --git a/public/app/features/alerting/state/actions.ts b/public/app/features/alerting/state/actions.ts index a323034e823..fe1362d126e 100644 --- a/public/app/features/alerting/state/actions.ts +++ b/public/app/features/alerting/state/actions.ts @@ -1,5 +1,5 @@ import { AppEvents, dateMath } from '@grafana/data'; -import { getBackendSrv, getDataSourceSrv } from '@grafana/runtime'; +import { config, getBackendSrv, getDataSourceSrv } from '@grafana/runtime'; import { appEvents } from 'app/core/core'; import { updateLocation } from 'app/core/actions'; import store from 'app/core/store'; @@ -12,6 +12,7 @@ import { ALERT_DEFINITION_UI_STATE_STORAGE_KEY, updateAlertDefinition, setQueryOptions, + setAlertDefinitions, } from './reducers'; import { AlertDefinition, @@ -29,6 +30,12 @@ export function getAlertRulesAsync(options: { state: string }): ThunkResult { dispatch(loadAlertRules()); const rules: AlertRuleDTO[] = await getBackendSrv().get('/api/alerts', options); + + if (config.featureToggles.ngalert) { + const ngAlertDefinitions = await getBackendSrv().get('/api/alert-definitions'); + dispatch(setAlertDefinitions(ngAlertDefinitions.results)); + } + dispatch(loadedAlertRules(rules)); }; } diff --git a/public/app/features/alerting/state/reducers.ts b/public/app/features/alerting/state/reducers.ts index 603c8064c2e..4cc051f0ed9 100644 --- a/public/app/features/alerting/state/reducers.ts +++ b/public/app/features/alerting/state/reducers.ts @@ -63,6 +63,7 @@ export const initialAlertDefinitionState: AlertDefinitionState = { queryRunner: new PanelQueryRunner(dataConfig), uiState: { ...store.getObject(ALERT_DEFINITION_UI_STATE_STORAGE_KEY, DEFAULT_ALERT_DEFINITION_UI_STATE) }, data: [], + alertDefinitions: [] as AlertDefinition[], }; function convertToAlertRule(dto: AlertRuleDTO, state: string): AlertRule { @@ -170,6 +171,9 @@ const alertDefinitionSlice = createSlice({ queryOptions: action.payload, }; }, + setAlertDefinitions: (state: AlertDefinitionState, action: PayloadAction) => { + return { ...state, alertDefinitions: action.payload }; + }, }, }); @@ -181,7 +185,7 @@ export const { resetSecureField, } = notificationChannelSlice.actions; -export const { setUiState, updateAlertDefinition, setQueryOptions } = alertDefinitionSlice.actions; +export const { setUiState, updateAlertDefinition, setQueryOptions, setAlertDefinitions } = alertDefinitionSlice.actions; export const alertRulesReducer = alertRulesSlice.reducer; export const notificationChannelReducer = notificationChannelSlice.reducer; diff --git a/public/app/features/alerting/state/selectors.test.ts b/public/app/features/alerting/state/selectors.test.ts index 8ee3f01a167..a38bb798b86 100644 --- a/public/app/features/alerting/state/selectors.test.ts +++ b/public/app/features/alerting/state/selectors.test.ts @@ -12,21 +12,23 @@ describe('Get search query', () => { describe('Get alert rule items', () => { it('should get alert rule items', () => { const state = { - items: [ - { - id: 1, - dashboardId: 1, - panelId: 1, - name: '', - state: '', - stateText: '', - stateIcon: '', - stateClass: '', - stateAge: '', - url: '', - }, - ], - searchQuery: '', + alertRules: { + items: [ + { + id: 1, + dashboardId: 1, + panelId: 1, + name: '', + state: '', + stateText: '', + stateIcon: '', + stateClass: '', + stateAge: '', + url: '', + }, + ], + searchQuery: '', + }, }; const result = getAlertRuleItems(state as any); @@ -35,57 +37,59 @@ describe('Get alert rule items', () => { it('should filter rule items based on search query', () => { const state = { - items: [ - { - id: 1, - dashboardId: 1, - panelId: 1, - name: 'dashboard', - state: '', - stateText: '', - stateIcon: '', - stateClass: '', - stateAge: '', - url: '', - }, - { - id: 2, - dashboardId: 3, - panelId: 1, - name: 'dashboard2', - state: '', - stateText: '', - stateIcon: '', - stateClass: '', - stateAge: '', - url: '', - }, - { - id: 3, - dashboardId: 5, - panelId: 1, - name: 'hello', - state: '', - stateText: '', - stateIcon: '', - stateClass: '', - stateAge: '', - url: '', - }, - { - id: 4, - dashboardId: 7, - panelId: 1, - name: 'test', - state: '', - stateText: 'dashboard', - stateIcon: '', - stateClass: '', - stateAge: '', - url: '', - }, - ], - searchQuery: 'dashboard', + alertRules: { + items: [ + { + id: 1, + dashboardId: 1, + panelId: 1, + name: 'dashboard', + state: '', + stateText: '', + stateIcon: '', + stateClass: '', + stateAge: '', + url: '', + }, + { + id: 2, + dashboardId: 3, + panelId: 1, + name: 'dashboard2', + state: '', + stateText: '', + stateIcon: '', + stateClass: '', + stateAge: '', + url: '', + }, + { + id: 3, + dashboardId: 5, + panelId: 1, + name: 'hello', + state: '', + stateText: '', + stateIcon: '', + stateClass: '', + stateAge: '', + url: '', + }, + { + id: 4, + dashboardId: 7, + panelId: 1, + name: 'test', + state: '', + stateText: 'dashboard', + stateIcon: '', + stateClass: '', + stateAge: '', + url: '', + }, + ], + searchQuery: 'dashboard', + }, }; const result = getAlertRuleItems(state as any); diff --git a/public/app/features/alerting/state/selectors.ts b/public/app/features/alerting/state/selectors.ts index 83c8f7adf02..3db0c20e2da 100644 --- a/public/app/features/alerting/state/selectors.ts +++ b/public/app/features/alerting/state/selectors.ts @@ -1,13 +1,27 @@ -import { AlertRulesState, NotificationChannelState } from 'app/types'; +import { AlertDefinition, AlertRule, AlertRulesState, NotificationChannelState, StoreState } from 'app/types'; +import { config } from '@grafana/runtime'; export const getSearchQuery = (state: AlertRulesState) => state.searchQuery; -export const getAlertRuleItems = (state: AlertRulesState) => { - const regex = new RegExp(state.searchQuery, 'i'); +export const getAlertRuleItems = (state: StoreState) => { + const regex = new RegExp(state.alertRules.searchQuery, 'i'); + const result: Array = []; - return state.items.filter((item) => { - return regex.test(item.name) || regex.test(item.stateText) || regex.test(item.info!); - }); + result.push( + ...state.alertRules.items.filter((item) => { + return regex.test(item.name) || regex.test(item.stateText) || regex.test(item.info!); + }) + ); + + if (config.featureToggles.ngalert) { + result.push( + ...state.alertDefinition.alertDefinitions.filter((item) => { + return regex.test(item.title); + }) + ); + } + + return result; }; export const getNotificationChannel = (state: NotificationChannelState, channelId: number) => { diff --git a/public/app/types/alerting.ts b/public/app/types/alerting.ts index 8c3ab5f49d2..fe040e54e73 100644 --- a/public/app/types/alerting.ts +++ b/public/app/types/alerting.ts @@ -142,6 +142,7 @@ export interface AlertDefinitionState { queryOptions: QueryGroupOptions; queryRunner: PanelQueryRunner; data: PanelData[]; + alertDefinitions: AlertDefinition[]; } export interface AlertDefinition {