From 5f515bb3fc450841934b4d74ab4e3cba7e57626c Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 19 Oct 2018 15:33:16 +0200 Subject: [PATCH 1/9] using react component --- public/app/core/angular_wrappers.ts | 2 ++ .../app/core/components/Alerts/AlertList.tsx | 36 +++++++++++++++++++ public/app/core/services/alert_srv.ts | 2 ++ public/views/index.template.html | 15 +------- 4 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 public/app/core/components/Alerts/AlertList.tsx diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 6974d40aac8..03d402ce86d 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -5,10 +5,12 @@ import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA'; import { SearchResult } from './components/search/SearchResult'; import { TagFilter } from './components/TagFilter/TagFilter'; import { SideMenu } from './components/sidemenu/SideMenu'; +import { AlertList } from './components/Alerts/AlertList'; export function registerAngularDirectives() { react2AngularDirective('passwordStrength', PasswordStrength, ['password']); react2AngularDirective('sidemenu', SideMenu, []); + react2AngularDirective('pageAlertList', AlertList, []); react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']); react2AngularDirective('emptyListCta', EmptyListCTA, ['model']); react2AngularDirective('searchResult', SearchResult, []); diff --git a/public/app/core/components/Alerts/AlertList.tsx b/public/app/core/components/Alerts/AlertList.tsx new file mode 100644 index 00000000000..bd9fc49a007 --- /dev/null +++ b/public/app/core/components/Alerts/AlertList.tsx @@ -0,0 +1,36 @@ +import React, { PureComponent } from 'react'; + +export interface Props { + alerts: any[]; +} + +export class AlertList extends PureComponent { + onClearAlert = alert => { + console.log('clear alert', alert); + }; + + render() { + const alerts = [{ severity: 'success', icon: 'warning', title: 'test', text: 'test text' }]; + + return ( +
+ {alerts.map((alert, index) => { + return ( +
+
+ +
+
+
{alert.title}
+
{alert.text}
+
+ +
+ ); + })} +
+ ); + } +} diff --git a/public/app/core/services/alert_srv.ts b/public/app/core/services/alert_srv.ts index 2d447651b75..9a4fabb761a 100644 --- a/public/app/core/services/alert_srv.ts +++ b/public/app/core/services/alert_srv.ts @@ -20,6 +20,8 @@ export class AlertSrv { this.$rootScope ); + this.list.push({ severity: 'success', icon: 'warning', title: 'test', text: 'test text' }); + this.$rootScope.onAppEvent( 'alert-warning', (e, alert) => { diff --git a/public/views/index.template.html b/public/views/index.template.html index c39d5e08321..597fc8d8f59 100644 --- a/public/views/index.template.html +++ b/public/views/index.template.html @@ -200,21 +200,8 @@ + -
-
-
- -
-
-
{{alert.title}}
-
-
- -
-
From b7d821b524c0b671da59f5df670375dcb0582a07 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 22 Oct 2018 14:22:40 +0200 Subject: [PATCH 2/9] component working --- public/app/core/angular_wrappers.ts | 2 +- .../app/core/components/Alerts/AlertList.tsx | 11 ++++- .../core/components/Alerts/state/actions.ts | 23 +++++++++++ .../components/Alerts/state/reducers.test.ts | 41 +++++++++++++++++++ .../core/components/Alerts/state/reducers.ts | 23 +++++++++++ public/app/core/services/alert_srv.ts | 2 - public/app/types/alerts.ts | 10 +++++ public/app/types/index.ts | 4 ++ 8 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 public/app/core/components/Alerts/state/actions.ts create mode 100644 public/app/core/components/Alerts/state/reducers.test.ts create mode 100644 public/app/core/components/Alerts/state/reducers.ts create mode 100644 public/app/types/alerts.ts diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 03d402ce86d..5b14ebe46fa 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -5,7 +5,7 @@ import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA'; import { SearchResult } from './components/search/SearchResult'; import { TagFilter } from './components/TagFilter/TagFilter'; import { SideMenu } from './components/sidemenu/SideMenu'; -import { AlertList } from './components/Alerts/AlertList'; +import AlertList from './components/Alerts/AlertList'; export function registerAngularDirectives() { react2AngularDirective('passwordStrength', PasswordStrength, ['password']); diff --git a/public/app/core/components/Alerts/AlertList.tsx b/public/app/core/components/Alerts/AlertList.tsx index bd9fc49a007..e384924d96f 100644 --- a/public/app/core/components/Alerts/AlertList.tsx +++ b/public/app/core/components/Alerts/AlertList.tsx @@ -1,4 +1,5 @@ import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; export interface Props { alerts: any[]; @@ -10,7 +11,7 @@ export class AlertList extends PureComponent { }; render() { - const alerts = [{ severity: 'success', icon: 'warning', title: 'test', text: 'test text' }]; + const { alerts } = this.props; return (
@@ -34,3 +35,11 @@ export class AlertList extends PureComponent { ); } } + +function mapStateToProps(state) { + return { + alerts: state.alerts.alerts, + }; +} + +export default connect(mapStateToProps)(AlertList); diff --git a/public/app/core/components/Alerts/state/actions.ts b/public/app/core/components/Alerts/state/actions.ts new file mode 100644 index 00000000000..cc82f21a3e7 --- /dev/null +++ b/public/app/core/components/Alerts/state/actions.ts @@ -0,0 +1,23 @@ +import { Alert } from 'app/types'; + +export enum ActionTypes { + AddAlert = 'ADD_ALERT', + ClearAlert = 'CLEAR_ALERT', +} + +interface AddAlertAction { + type: ActionTypes.AddAlert; + payload: Alert; +} + +interface ClearAlertAction { + type: ActionTypes.ClearAlert; + payload: Alert; +} + +export type Action = AddAlertAction | ClearAlertAction; + +export const clearAlert = (alert: Alert) => ({ + type: ActionTypes.ClearAlert, + payload: alert, +}); diff --git a/public/app/core/components/Alerts/state/reducers.test.ts b/public/app/core/components/Alerts/state/reducers.test.ts new file mode 100644 index 00000000000..16848e6b233 --- /dev/null +++ b/public/app/core/components/Alerts/state/reducers.test.ts @@ -0,0 +1,41 @@ +import { alertsReducer } from './reducers'; +import { ActionTypes } from './actions'; + +describe('clear alert', () => { + it('should filter alert', () => { + const initialState = { + alerts: [ + { + severity: 'success', + icon: 'success', + title: 'test', + text: 'test alert', + }, + { + severity: 'fail', + icon: 'warning', + title: 'test2', + text: 'test alert fail 2', + }, + ], + }; + + const result = alertsReducer(initialState, { + type: ActionTypes.ClearAlert, + payload: initialState.alerts[1], + }); + + const expectedResult = { + alerts: [ + { + severity: 'success', + icon: 'success', + title: 'test', + text: 'test alert', + }, + ], + }; + + expect(result).toEqual(expectedResult); + }); +}); diff --git a/public/app/core/components/Alerts/state/reducers.ts b/public/app/core/components/Alerts/state/reducers.ts new file mode 100644 index 00000000000..efbfa96aa2c --- /dev/null +++ b/public/app/core/components/Alerts/state/reducers.ts @@ -0,0 +1,23 @@ +import { Alert, AlertsState } from 'app/types'; +import { Action, ActionTypes } from './actions'; + +export const initialState: AlertsState = { + alerts: [] as Alert[], +}; + +export const alertsReducer = (state = initialState, action: Action): AlertsState => { + switch (action.type) { + case ActionTypes.AddAlert: + return { ...state, alerts: state.alerts.concat([action.payload]) }; + case ActionTypes.ClearAlert: + return { + ...state, + alerts: state.alerts.filter(alert => alert !== action.payload), + }; + } + return state; +}; + +export default { + alerts: alertsReducer, +}; diff --git a/public/app/core/services/alert_srv.ts b/public/app/core/services/alert_srv.ts index 9a4fabb761a..2d447651b75 100644 --- a/public/app/core/services/alert_srv.ts +++ b/public/app/core/services/alert_srv.ts @@ -20,8 +20,6 @@ export class AlertSrv { this.$rootScope ); - this.list.push({ severity: 'success', icon: 'warning', title: 'test', text: 'test text' }); - this.$rootScope.onAppEvent( 'alert-warning', (e, alert) => { diff --git a/public/app/types/alerts.ts b/public/app/types/alerts.ts new file mode 100644 index 00000000000..2e744ddc9b7 --- /dev/null +++ b/public/app/types/alerts.ts @@ -0,0 +1,10 @@ +export interface Alert { + severity: string; + icon: string; + title: string; + text: string; +} + +export interface AlertsState { + alerts: Alert[]; +} diff --git a/public/app/types/index.ts b/public/app/types/index.ts index 7b35f3d6787..af0dacf27ee 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,6 +9,7 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins'; +import { Alert, AlertsState } from './alerts'; export { Team, @@ -46,6 +47,8 @@ export { User, UsersState, PluginDashboard, + Alert, + AlertsState, }; export interface StoreState { @@ -58,4 +61,5 @@ export interface StoreState { dashboard: DashboardState; dataSources: DataSourcesState; users: UsersState; + alerts: AlertsState; } From bbd02dd616d5a27f2de23db04a4036714f1243ad Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 23 Oct 2018 13:34:27 +0200 Subject: [PATCH 3/9] renaming things --- public/app/core/angular_wrappers.ts | 4 +- .../app/core/components/Alerts/AlertList.tsx | 45 --------- .../core/components/Alerts/state/actions.ts | 23 ----- .../AppNotifications/AppNotificationList.tsx | 96 +++++++++++++++++++ .../AppNotifications/state/actions.ts | 28 ++++++ .../state/reducers.test.ts | 2 +- .../state/reducers.ts | 8 +- public/app/types/alerts.ts | 4 +- public/app/types/index.ts | 4 +- 9 files changed, 135 insertions(+), 79 deletions(-) delete mode 100644 public/app/core/components/Alerts/AlertList.tsx delete mode 100644 public/app/core/components/Alerts/state/actions.ts create mode 100644 public/app/core/components/AppNotifications/AppNotificationList.tsx create mode 100644 public/app/core/components/AppNotifications/state/actions.ts rename public/app/core/components/{Alerts => AppNotifications}/state/reducers.test.ts (94%) rename public/app/core/components/{Alerts => AppNotifications}/state/reducers.ts (72%) diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 5b14ebe46fa..14a0dcdb234 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -5,12 +5,12 @@ import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA'; import { SearchResult } from './components/search/SearchResult'; import { TagFilter } from './components/TagFilter/TagFilter'; import { SideMenu } from './components/sidemenu/SideMenu'; -import AlertList from './components/Alerts/AlertList'; +import AppNotificationList from './components/AppNotifications/AppNotificationList'; export function registerAngularDirectives() { react2AngularDirective('passwordStrength', PasswordStrength, ['password']); react2AngularDirective('sidemenu', SideMenu, []); - react2AngularDirective('pageAlertList', AlertList, []); + react2AngularDirective('pageAlertList', AppNotificationList, []); react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']); react2AngularDirective('emptyListCta', EmptyListCTA, ['model']); react2AngularDirective('searchResult', SearchResult, []); diff --git a/public/app/core/components/Alerts/AlertList.tsx b/public/app/core/components/Alerts/AlertList.tsx deleted file mode 100644 index e384924d96f..00000000000 --- a/public/app/core/components/Alerts/AlertList.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { PureComponent } from 'react'; -import { connect } from 'react-redux'; - -export interface Props { - alerts: any[]; -} - -export class AlertList extends PureComponent { - onClearAlert = alert => { - console.log('clear alert', alert); - }; - - render() { - const { alerts } = this.props; - - return ( -
- {alerts.map((alert, index) => { - return ( -
-
- -
-
-
{alert.title}
-
{alert.text}
-
- -
- ); - })} -
- ); - } -} - -function mapStateToProps(state) { - return { - alerts: state.alerts.alerts, - }; -} - -export default connect(mapStateToProps)(AlertList); diff --git a/public/app/core/components/Alerts/state/actions.ts b/public/app/core/components/Alerts/state/actions.ts deleted file mode 100644 index cc82f21a3e7..00000000000 --- a/public/app/core/components/Alerts/state/actions.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Alert } from 'app/types'; - -export enum ActionTypes { - AddAlert = 'ADD_ALERT', - ClearAlert = 'CLEAR_ALERT', -} - -interface AddAlertAction { - type: ActionTypes.AddAlert; - payload: Alert; -} - -interface ClearAlertAction { - type: ActionTypes.ClearAlert; - payload: Alert; -} - -export type Action = AddAlertAction | ClearAlertAction; - -export const clearAlert = (alert: Alert) => ({ - type: ActionTypes.ClearAlert, - payload: alert, -}); diff --git a/public/app/core/components/AppNotifications/AppNotificationList.tsx b/public/app/core/components/AppNotifications/AppNotificationList.tsx new file mode 100644 index 00000000000..a637d741541 --- /dev/null +++ b/public/app/core/components/AppNotifications/AppNotificationList.tsx @@ -0,0 +1,96 @@ +import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; +import appEvents from 'app/core/app_events'; +import { addAppNotification, clearAppNotification } from './state/actions'; + +export interface Props { + alerts: any[]; + addAppNotification: typeof addAppNotification; + clearAppNotification: typeof clearAppNotification; +} + +enum AppNotificationSeverity { + Success = 'success', + Warning = 'warning', + Error = 'error', + Info = 'info', +} + +export class AppNotificationList extends PureComponent { + componentDidMount() { + appEvents.on('alert-warning', options => this.addAppNotification(options[0], options[1], 'warning', 5000)); + appEvents.on('alert-success', options => this.addAppNotification(options[0], options[1], 'success', 3000)); + appEvents.on('alert-error', options => this.addAppNotification(options[0], options[1], 'error', 7000)); + } + + addAppNotification(title, text, severity, timeout) { + const newAlert = { + title: title || '', + text: text || '', + severity: severity || AppNotificationSeverity.Info, + icon: this.getIconForSeverity(severity), + remove: this.clearAutomatically(this, timeout), + }; + + this.props.addAppNotification(newAlert); + } + + getIconForSeverity(severity) { + switch (severity) { + case AppNotificationSeverity.Success: + return 'fa fa-check'; + case AppNotificationSeverity.Error: + return 'fa fa-exclamation-triangle'; + default: + return 'fa fa-exclamation'; + } + } + + clearAutomatically = (alert, timeout) => { + setTimeout(() => { + this.props.clearAppNotification(alert); + }, timeout); + }; + + onClearAppNotification = alert => { + this.props.clearAppNotification(alert); + }; + + render() { + const { alerts } = this.props; + + return ( +
+ {alerts.map((alert, index) => { + return ( +
+
+ +
+
+
{alert.title}
+
{alert.text}
+
+ +
+ ); + })} +
+ ); + } +} + +function mapStateToProps(state) { + return { + alerts: state.alerts.alerts, + }; +} + +const mapDispatchToProps = { + addAppNotification, + clearAppNotification, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(AppNotificationList); diff --git a/public/app/core/components/AppNotifications/state/actions.ts b/public/app/core/components/AppNotifications/state/actions.ts new file mode 100644 index 00000000000..dfdb066e978 --- /dev/null +++ b/public/app/core/components/AppNotifications/state/actions.ts @@ -0,0 +1,28 @@ +import { AppNotification } from 'app/types'; + +export enum ActionTypes { + AddAppNotification = 'ADD_APP_NOTIFICATION', + ClearAppNotification = 'CLEAR_APP_NOTIFICATION', +} + +interface AddAppNotificationAction { + type: ActionTypes.AddAppNotification; + payload: AppNotification; +} + +interface ClearAppNotificationAction { + type: ActionTypes.ClearAppNotification; + payload: AppNotification; +} + +export type Action = AddAppNotificationAction | ClearAppNotificationAction; + +export const clearAppNotification = (alert: AppNotification) => ({ + type: ActionTypes.ClearAppNotification, + payload: alert, +}); + +export const addAppNotification = (alert: AppNotification) => ({ + type: ActionTypes.AddAppNotification, + payload: alert, +}); diff --git a/public/app/core/components/Alerts/state/reducers.test.ts b/public/app/core/components/AppNotifications/state/reducers.test.ts similarity index 94% rename from public/app/core/components/Alerts/state/reducers.test.ts rename to public/app/core/components/AppNotifications/state/reducers.test.ts index 16848e6b233..e81bbf27967 100644 --- a/public/app/core/components/Alerts/state/reducers.test.ts +++ b/public/app/core/components/AppNotifications/state/reducers.test.ts @@ -21,7 +21,7 @@ describe('clear alert', () => { }; const result = alertsReducer(initialState, { - type: ActionTypes.ClearAlert, + type: ActionTypes.ClearAppNotification, payload: initialState.alerts[1], }); diff --git a/public/app/core/components/Alerts/state/reducers.ts b/public/app/core/components/AppNotifications/state/reducers.ts similarity index 72% rename from public/app/core/components/Alerts/state/reducers.ts rename to public/app/core/components/AppNotifications/state/reducers.ts index efbfa96aa2c..7bb7b2f65bf 100644 --- a/public/app/core/components/Alerts/state/reducers.ts +++ b/public/app/core/components/AppNotifications/state/reducers.ts @@ -1,15 +1,15 @@ -import { Alert, AlertsState } from 'app/types'; +import { AppNotification, AlertsState } from 'app/types'; import { Action, ActionTypes } from './actions'; export const initialState: AlertsState = { - alerts: [] as Alert[], + alerts: [] as AppNotification[], }; export const alertsReducer = (state = initialState, action: Action): AlertsState => { switch (action.type) { - case ActionTypes.AddAlert: + case ActionTypes.AddAppNotification: return { ...state, alerts: state.alerts.concat([action.payload]) }; - case ActionTypes.ClearAlert: + case ActionTypes.ClearAppNotification: return { ...state, alerts: state.alerts.filter(alert => alert !== action.payload), diff --git a/public/app/types/alerts.ts b/public/app/types/alerts.ts index 2e744ddc9b7..96b764243ff 100644 --- a/public/app/types/alerts.ts +++ b/public/app/types/alerts.ts @@ -1,4 +1,4 @@ -export interface Alert { +export interface AppNotification { severity: string; icon: string; title: string; @@ -6,5 +6,5 @@ export interface Alert { } export interface AlertsState { - alerts: Alert[]; + alerts: AppNotification[]; } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index af0dacf27ee..fbb3b3d7d65 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,7 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins'; -import { Alert, AlertsState } from './alerts'; +import { AppNotification, AlertsState } from './alerts'; export { Team, @@ -47,7 +47,7 @@ export { User, UsersState, PluginDashboard, - Alert, + AppNotification, AlertsState, }; From bb6409384e585b4926dd588c66b12d6e8aa870bd Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 23 Oct 2018 16:00:04 +0200 Subject: [PATCH 4/9] connected to store, self remove logic --- .../AppNotifications/AppNotificationList.tsx | 45 ++++++++++--------- .../AppNotifications/state/actions.ts | 6 +-- .../AppNotifications/state/reducers.test.ts | 16 ++++--- .../AppNotifications/state/reducers.ts | 14 +++--- .../app/core/utils/connectWithReduxStore.tsx | 11 +++++ .../permissions/DashboardPermissions.tsx | 10 +---- public/app/store/configureStore.ts | 2 + public/app/types/alerts.ts | 5 ++- public/app/types/index.ts | 6 +-- 9 files changed, 66 insertions(+), 49 deletions(-) create mode 100644 public/app/core/utils/connectWithReduxStore.tsx diff --git a/public/app/core/components/AppNotifications/AppNotificationList.tsx b/public/app/core/components/AppNotifications/AppNotificationList.tsx index a637d741541..a5953156aab 100644 --- a/public/app/core/components/AppNotifications/AppNotificationList.tsx +++ b/public/app/core/components/AppNotifications/AppNotificationList.tsx @@ -1,10 +1,11 @@ import React, { PureComponent } from 'react'; -import { connect } from 'react-redux'; import appEvents from 'app/core/app_events'; import { addAppNotification, clearAppNotification } from './state/actions'; +import { connectWithStore } from 'app/core/utils/connectWithReduxStore'; +import { AppNotification, StoreState } from '../../../types'; export interface Props { - alerts: any[]; + appNotifications: AppNotification[]; addAppNotification: typeof addAppNotification; clearAppNotification: typeof clearAppNotification; } @@ -24,12 +25,14 @@ export class AppNotificationList extends PureComponent { } addAppNotification(title, text, severity, timeout) { + const id = Date.now(); const newAlert = { + id: id, title: title || '', text: text || '', severity: severity || AppNotificationSeverity.Info, icon: this.getIconForSeverity(severity), - remove: this.clearAutomatically(this, timeout), + remove: this.clearAutomatically(id, timeout), }; this.props.addAppNotification(newAlert); @@ -46,32 +49,36 @@ export class AppNotificationList extends PureComponent { } } - clearAutomatically = (alert, timeout) => { + clearAutomatically = (id, timeout) => { setTimeout(() => { - this.props.clearAppNotification(alert); + this.props.clearAppNotification(id); }, timeout); }; - onClearAppNotification = alert => { - this.props.clearAppNotification(alert); + onClearAppNotification = id => { + this.props.clearAppNotification(id); }; render() { - const { alerts } = this.props; + const { appNotifications } = this.props; return (
- {alerts.map((alert, index) => { + {appNotifications.map((appNotification, index) => { return ( -
+
- +
-
{alert.title}
-
{alert.text}
+
{appNotification.title}
+
{appNotification.text}
-
@@ -82,15 +89,13 @@ export class AppNotificationList extends PureComponent { } } -function mapStateToProps(state) { - return { - alerts: state.alerts.alerts, - }; -} +const mapStateToProps = (state: StoreState) => ({ + appNotifications: state.appNotifications.appNotifications, +}); const mapDispatchToProps = { addAppNotification, clearAppNotification, }; -export default connect(mapStateToProps, mapDispatchToProps)(AppNotificationList); +export default connectWithStore(AppNotificationList, mapStateToProps, mapDispatchToProps); diff --git a/public/app/core/components/AppNotifications/state/actions.ts b/public/app/core/components/AppNotifications/state/actions.ts index dfdb066e978..c6cfe9c3e9e 100644 --- a/public/app/core/components/AppNotifications/state/actions.ts +++ b/public/app/core/components/AppNotifications/state/actions.ts @@ -12,14 +12,14 @@ interface AddAppNotificationAction { interface ClearAppNotificationAction { type: ActionTypes.ClearAppNotification; - payload: AppNotification; + payload: number; } export type Action = AddAppNotificationAction | ClearAppNotificationAction; -export const clearAppNotification = (alert: AppNotification) => ({ +export const clearAppNotification = (appNotificationId: number) => ({ type: ActionTypes.ClearAppNotification, - payload: alert, + payload: appNotificationId, }); export const addAppNotification = (alert: AppNotification) => ({ diff --git a/public/app/core/components/AppNotifications/state/reducers.test.ts b/public/app/core/components/AppNotifications/state/reducers.test.ts index e81bbf27967..bdc78b4fe0b 100644 --- a/public/app/core/components/AppNotifications/state/reducers.test.ts +++ b/public/app/core/components/AppNotifications/state/reducers.test.ts @@ -1,17 +1,22 @@ -import { alertsReducer } from './reducers'; +import { appNotificationsReducer } from './reducers'; import { ActionTypes } from './actions'; describe('clear alert', () => { it('should filter alert', () => { + const id1 = 1540301236048; + const id2 = 1540301248293; + const initialState = { - alerts: [ + appNotifications: [ { + id: id1, severity: 'success', icon: 'success', title: 'test', text: 'test alert', }, { + id: id2, severity: 'fail', icon: 'warning', title: 'test2', @@ -20,14 +25,15 @@ describe('clear alert', () => { ], }; - const result = alertsReducer(initialState, { + const result = appNotificationsReducer(initialState, { type: ActionTypes.ClearAppNotification, - payload: initialState.alerts[1], + payload: id2, }); const expectedResult = { - alerts: [ + appNotifications: [ { + id: id1, severity: 'success', icon: 'success', title: 'test', diff --git a/public/app/core/components/AppNotifications/state/reducers.ts b/public/app/core/components/AppNotifications/state/reducers.ts index 7bb7b2f65bf..7887a91a616 100644 --- a/public/app/core/components/AppNotifications/state/reducers.ts +++ b/public/app/core/components/AppNotifications/state/reducers.ts @@ -1,23 +1,23 @@ -import { AppNotification, AlertsState } from 'app/types'; +import { AppNotification, AppNotificationsState } from 'app/types'; import { Action, ActionTypes } from './actions'; -export const initialState: AlertsState = { - alerts: [] as AppNotification[], +export const initialState: AppNotificationsState = { + appNotifications: [] as AppNotification[], }; -export const alertsReducer = (state = initialState, action: Action): AlertsState => { +export const appNotificationsReducer = (state = initialState, action: Action): AppNotificationsState => { switch (action.type) { case ActionTypes.AddAppNotification: - return { ...state, alerts: state.alerts.concat([action.payload]) }; + return { ...state, appNotifications: state.appNotifications.concat([action.payload]) }; case ActionTypes.ClearAppNotification: return { ...state, - alerts: state.alerts.filter(alert => alert !== action.payload), + appNotifications: state.appNotifications.filter(appNotification => appNotification.id !== action.payload), }; } return state; }; export default { - alerts: alertsReducer, + appNotifications: appNotificationsReducer, }; diff --git a/public/app/core/utils/connectWithReduxStore.tsx b/public/app/core/utils/connectWithReduxStore.tsx new file mode 100644 index 00000000000..92c61db4e77 --- /dev/null +++ b/public/app/core/utils/connectWithReduxStore.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { store } from '../../store/configureStore'; + +export function connectWithStore(WrappedComponent, ...args) { + const ConnectedWrappedComponent = connect(...args)(WrappedComponent); + + return props => { + return ; + }; +} diff --git a/public/app/features/dashboard/permissions/DashboardPermissions.tsx b/public/app/features/dashboard/permissions/DashboardPermissions.tsx index 5651242a485..c07bef42930 100644 --- a/public/app/features/dashboard/permissions/DashboardPermissions.tsx +++ b/public/app/features/dashboard/permissions/DashboardPermissions.tsx @@ -1,5 +1,4 @@ import React, { PureComponent } from 'react'; -import { connect } from 'react-redux'; import Tooltip from 'app/core/components/Tooltip/Tooltip'; import SlideDown from 'app/core/components/Animations/SlideDown'; import { StoreState, FolderInfo } from 'app/types'; @@ -13,7 +12,7 @@ import { import PermissionList from 'app/core/components/PermissionList/PermissionList'; import AddPermission from 'app/core/components/PermissionList/AddPermission'; import PermissionsInfo from 'app/core/components/PermissionList/PermissionsInfo'; -import { store } from 'app/store/configureStore'; +import { connectWithStore } from '../../../core/utils/connectWithReduxStore'; export interface Props { dashboardId: number; @@ -95,13 +94,6 @@ export class DashboardPermissions extends PureComponent { } } -function connectWithStore(WrappedComponent, ...args) { - const ConnectedWrappedComponent = connect(...args)(WrappedComponent); - return props => { - return ; - }; -} - const mapStateToProps = (state: StoreState) => ({ permissions: state.dashboard.permissions, }); diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index ccd027a0b6d..c1c6103bab9 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -10,6 +10,7 @@ import dashboardReducers from 'app/features/dashboard/state/reducers'; import pluginReducers from 'app/features/plugins/state/reducers'; import dataSourcesReducers from 'app/features/datasources/state/reducers'; import usersReducers from 'app/features/users/state/reducers'; +import appNotificationReducers from 'app/core/components/AppNotifications/state/reducers'; const rootReducers = { ...sharedReducers, @@ -21,6 +22,7 @@ const rootReducers = { ...pluginReducers, ...dataSourcesReducers, ...usersReducers, + ...appNotificationReducers, }; export let store; diff --git a/public/app/types/alerts.ts b/public/app/types/alerts.ts index 96b764243ff..3f25fedbf8b 100644 --- a/public/app/types/alerts.ts +++ b/public/app/types/alerts.ts @@ -1,10 +1,11 @@ export interface AppNotification { + id: number; severity: string; icon: string; title: string; text: string; } -export interface AlertsState { - alerts: AppNotification[]; +export interface AppNotificationsState { + appNotifications: AppNotification[]; } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index fbb3b3d7d65..bc446bf3e36 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,7 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins'; -import { AppNotification, AlertsState } from './alerts'; +import { AppNotification, AppNotificationsState } from './alerts'; export { Team, @@ -48,7 +48,7 @@ export { UsersState, PluginDashboard, AppNotification, - AlertsState, + AppNotificationsState, }; export interface StoreState { @@ -61,5 +61,5 @@ export interface StoreState { dashboard: DashboardState; dataSources: DataSourcesState; users: UsersState; - alerts: AlertsState; + appNotifications: AppNotificationsState; } From bd2f9a38d9ba7e7b17a2ff50ec1110f562553e2c Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 23 Oct 2018 17:25:16 +0200 Subject: [PATCH 5/9] Added margin and correct border radius --- public/sass/components/_alerts.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/sass/components/_alerts.scss b/public/sass/components/_alerts.scss index 3420dcfdfaf..710c4d1ec0f 100644 --- a/public/sass/components/_alerts.scss +++ b/public/sass/components/_alerts.scss @@ -7,13 +7,13 @@ .alert { padding: 1.25rem 2rem 1.25rem 1.5rem; - margin-bottom: $line-height-base; + margin-bottom: $panel-margin / 2; text-shadow: 0 2px 0 rgba(255, 255, 255, 0.5); background: $alert-error-bg; position: relative; color: $white; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); - border-radius: 2px; + border-radius: $border-radius; display: flex; flex-direction: row; } From 3e0a34ceca449ace93f1206e01c270723bc6fb74 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 24 Oct 2018 10:18:28 +0200 Subject: [PATCH 6/9] typing changes --- .../AppNotifications/AppNotificationList.tsx | 9 +-------- .../AppNotifications/state/reducers.test.ts | 7 ++++--- public/app/types/alerts.ts | 11 ----------- public/app/types/appNotifications.ts | 18 ++++++++++++++++++ public/app/types/index.ts | 3 ++- 5 files changed, 25 insertions(+), 23 deletions(-) delete mode 100644 public/app/types/alerts.ts create mode 100644 public/app/types/appNotifications.ts diff --git a/public/app/core/components/AppNotifications/AppNotificationList.tsx b/public/app/core/components/AppNotifications/AppNotificationList.tsx index a5953156aab..10b5997ed98 100644 --- a/public/app/core/components/AppNotifications/AppNotificationList.tsx +++ b/public/app/core/components/AppNotifications/AppNotificationList.tsx @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; import appEvents from 'app/core/app_events'; import { addAppNotification, clearAppNotification } from './state/actions'; import { connectWithStore } from 'app/core/utils/connectWithReduxStore'; -import { AppNotification, StoreState } from '../../../types'; +import { AppNotification, AppNotificationSeverity, StoreState } from 'app/types'; export interface Props { appNotifications: AppNotification[]; @@ -10,13 +10,6 @@ export interface Props { clearAppNotification: typeof clearAppNotification; } -enum AppNotificationSeverity { - Success = 'success', - Warning = 'warning', - Error = 'error', - Info = 'info', -} - export class AppNotificationList extends PureComponent { componentDidMount() { appEvents.on('alert-warning', options => this.addAppNotification(options[0], options[1], 'warning', 5000)); diff --git a/public/app/core/components/AppNotifications/state/reducers.test.ts b/public/app/core/components/AppNotifications/state/reducers.test.ts index bdc78b4fe0b..b46955a087f 100644 --- a/public/app/core/components/AppNotifications/state/reducers.test.ts +++ b/public/app/core/components/AppNotifications/state/reducers.test.ts @@ -1,5 +1,6 @@ import { appNotificationsReducer } from './reducers'; import { ActionTypes } from './actions'; +import { AppNotificationSeverity } from 'app/types'; describe('clear alert', () => { it('should filter alert', () => { @@ -10,14 +11,14 @@ describe('clear alert', () => { appNotifications: [ { id: id1, - severity: 'success', + severity: AppNotificationSeverity.Success, icon: 'success', title: 'test', text: 'test alert', }, { id: id2, - severity: 'fail', + severity: AppNotificationSeverity.Warning, icon: 'warning', title: 'test2', text: 'test alert fail 2', @@ -34,7 +35,7 @@ describe('clear alert', () => { appNotifications: [ { id: id1, - severity: 'success', + severity: AppNotificationSeverity.Success, icon: 'success', title: 'test', text: 'test alert', diff --git a/public/app/types/alerts.ts b/public/app/types/alerts.ts deleted file mode 100644 index 3f25fedbf8b..00000000000 --- a/public/app/types/alerts.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface AppNotification { - id: number; - severity: string; - icon: string; - title: string; - text: string; -} - -export interface AppNotificationsState { - appNotifications: AppNotification[]; -} diff --git a/public/app/types/appNotifications.ts b/public/app/types/appNotifications.ts new file mode 100644 index 00000000000..1bbc66a4baa --- /dev/null +++ b/public/app/types/appNotifications.ts @@ -0,0 +1,18 @@ +export interface AppNotification { + id?: number; + severity: AppNotificationSeverity; + icon: string; + title: string; + text: string; +} + +export enum AppNotificationSeverity { + Success = 'success', + Warning = 'warning', + Error = 'error', + Info = 'info', +} + +export interface AppNotificationsState { + appNotifications: AppNotification[]; +} diff --git a/public/app/types/index.ts b/public/app/types/index.ts index bc446bf3e36..b888ba83877 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,7 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins'; -import { AppNotification, AppNotificationsState } from './alerts'; +import { AppNotification, AppNotificationSeverity, AppNotificationsState } from './appNotifications'; export { Team, @@ -49,6 +49,7 @@ export { PluginDashboard, AppNotification, AppNotificationsState, + AppNotificationSeverity, }; export interface StoreState { From ed99a543a53afe6700eed3180f4ab4bd9369656a Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 24 Oct 2018 10:23:11 +0200 Subject: [PATCH 7/9] moving things --- .../state/actions.ts => actions/appNotification.ts} | 2 +- public/app/core/angular_wrappers.ts | 2 +- .../components/AppNotifications/AppNotificationList.tsx | 2 +- .../reducers.test.ts => reducers/appNotification.test.ts} | 6 +++--- .../state/reducers.ts => reducers/appNotification.ts} | 4 ++-- public/app/store/configureStore.ts | 2 +- public/views/index.template.html | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) rename public/app/core/{components/AppNotifications/state/actions.ts => actions/appNotification.ts} (93%) rename public/app/core/{components/AppNotifications/state/reducers.test.ts => reducers/appNotification.test.ts} (84%) rename public/app/core/{components/AppNotifications/state/reducers.ts => reducers/appNotification.ts} (90%) diff --git a/public/app/core/components/AppNotifications/state/actions.ts b/public/app/core/actions/appNotification.ts similarity index 93% rename from public/app/core/components/AppNotifications/state/actions.ts rename to public/app/core/actions/appNotification.ts index c6cfe9c3e9e..009e99b1245 100644 --- a/public/app/core/components/AppNotifications/state/actions.ts +++ b/public/app/core/actions/appNotification.ts @@ -1,4 +1,4 @@ -import { AppNotification } from 'app/types'; +import { AppNotification } from 'app/types/'; export enum ActionTypes { AddAppNotification = 'ADD_APP_NOTIFICATION', diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 14a0dcdb234..7be28272f11 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -10,7 +10,7 @@ import AppNotificationList from './components/AppNotifications/AppNotificationLi export function registerAngularDirectives() { react2AngularDirective('passwordStrength', PasswordStrength, ['password']); react2AngularDirective('sidemenu', SideMenu, []); - react2AngularDirective('pageAlertList', AppNotificationList, []); + react2AngularDirective('appNotificationsList', AppNotificationList, []); react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']); react2AngularDirective('emptyListCta', EmptyListCTA, ['model']); react2AngularDirective('searchResult', SearchResult, []); diff --git a/public/app/core/components/AppNotifications/AppNotificationList.tsx b/public/app/core/components/AppNotifications/AppNotificationList.tsx index 10b5997ed98..53c88a41155 100644 --- a/public/app/core/components/AppNotifications/AppNotificationList.tsx +++ b/public/app/core/components/AppNotifications/AppNotificationList.tsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import appEvents from 'app/core/app_events'; -import { addAppNotification, clearAppNotification } from './state/actions'; +import { addAppNotification, clearAppNotification } from '../../actions/appNotification'; import { connectWithStore } from 'app/core/utils/connectWithReduxStore'; import { AppNotification, AppNotificationSeverity, StoreState } from 'app/types'; diff --git a/public/app/core/components/AppNotifications/state/reducers.test.ts b/public/app/core/reducers/appNotification.test.ts similarity index 84% rename from public/app/core/components/AppNotifications/state/reducers.test.ts rename to public/app/core/reducers/appNotification.test.ts index b46955a087f..5098abc9e74 100644 --- a/public/app/core/components/AppNotifications/state/reducers.test.ts +++ b/public/app/core/reducers/appNotification.test.ts @@ -1,6 +1,6 @@ -import { appNotificationsReducer } from './reducers'; -import { ActionTypes } from './actions'; -import { AppNotificationSeverity } from 'app/types'; +import { appNotificationsReducer } from './appNotification'; +import { ActionTypes } from '../actions/appNotification'; +import { AppNotificationSeverity } from 'app/types/index'; describe('clear alert', () => { it('should filter alert', () => { diff --git a/public/app/core/components/AppNotifications/state/reducers.ts b/public/app/core/reducers/appNotification.ts similarity index 90% rename from public/app/core/components/AppNotifications/state/reducers.ts rename to public/app/core/reducers/appNotification.ts index 7887a91a616..8812546356a 100644 --- a/public/app/core/components/AppNotifications/state/reducers.ts +++ b/public/app/core/reducers/appNotification.ts @@ -1,5 +1,5 @@ -import { AppNotification, AppNotificationsState } from 'app/types'; -import { Action, ActionTypes } from './actions'; +import { AppNotification, AppNotificationsState } from 'app/types/index'; +import { Action, ActionTypes } from '../actions/appNotification'; export const initialState: AppNotificationsState = { appNotifications: [] as AppNotification[], diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index c1c6103bab9..6b3205dc53e 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -10,7 +10,7 @@ import dashboardReducers from 'app/features/dashboard/state/reducers'; import pluginReducers from 'app/features/plugins/state/reducers'; import dataSourcesReducers from 'app/features/datasources/state/reducers'; import usersReducers from 'app/features/users/state/reducers'; -import appNotificationReducers from 'app/core/components/AppNotifications/state/reducers'; +import appNotificationReducers from 'app/core/reducers/appNotification'; const rootReducers = { ...sharedReducers, diff --git a/public/views/index.template.html b/public/views/index.template.html index 597fc8d8f59..ced39d9af28 100644 --- a/public/views/index.template.html +++ b/public/views/index.template.html @@ -200,7 +200,7 @@ - +
From f34cbae2dddbed0d04e5a8323197ea72b8dbe895 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 24 Oct 2018 14:33:53 +0200 Subject: [PATCH 8/9] cleaned up the flow --- public/app/core/actions/appNotification.ts | 4 +- public/app/core/actions/index.ts | 3 +- .../AppNotifications/AppNotificationItem.tsx | 38 ++++++++++ .../AppNotifications/AppNotificationList.tsx | 74 +++++-------------- public/app/core/copy/appNotification.ts | 46 ++++++++++++ .../app/core/reducers/appNotification.test.ts | 5 +- public/app/core/reducers/appNotification.ts | 6 +- public/app/core/reducers/index.ts | 2 + public/app/store/configureStore.ts | 2 - public/app/types/appNotifications.ts | 7 ++ public/app/types/index.ts | 8 +- 11 files changed, 129 insertions(+), 66 deletions(-) create mode 100644 public/app/core/components/AppNotifications/AppNotificationItem.tsx create mode 100644 public/app/core/copy/appNotification.ts diff --git a/public/app/core/actions/appNotification.ts b/public/app/core/actions/appNotification.ts index 009e99b1245..b79b642eef1 100644 --- a/public/app/core/actions/appNotification.ts +++ b/public/app/core/actions/appNotification.ts @@ -22,7 +22,7 @@ export const clearAppNotification = (appNotificationId: number) => ({ payload: appNotificationId, }); -export const addAppNotification = (alert: AppNotification) => ({ +export const notifyApp = (appNotification: AppNotification) => ({ type: ActionTypes.AddAppNotification, - payload: alert, + payload: appNotification, }); diff --git a/public/app/core/actions/index.ts b/public/app/core/actions/index.ts index 451a13dae99..f7ce2dda945 100644 --- a/public/app/core/actions/index.ts +++ b/public/app/core/actions/index.ts @@ -1,4 +1,5 @@ import { updateLocation } from './location'; import { updateNavIndex, UpdateNavIndexAction } from './navModel'; +import { notifyApp, clearAppNotification } from './appNotification'; -export { updateLocation, updateNavIndex, UpdateNavIndexAction }; +export { updateLocation, updateNavIndex, UpdateNavIndexAction, notifyApp, clearAppNotification }; diff --git a/public/app/core/components/AppNotifications/AppNotificationItem.tsx b/public/app/core/components/AppNotifications/AppNotificationItem.tsx new file mode 100644 index 00000000000..5169c39e7a0 --- /dev/null +++ b/public/app/core/components/AppNotifications/AppNotificationItem.tsx @@ -0,0 +1,38 @@ +import React, { Component } from 'react'; +import { AppNotification } from 'app/types'; + +interface Props { + appNotification: AppNotification; + onClearNotification: (id) => void; +} + +export default class AppNotificationItem extends Component { + shouldComponentUpdate(nextProps) { + return this.props.appNotification.id !== nextProps.appNotification.id; + } + + componentDidMount() { + const { appNotification, onClearNotification } = this.props; + setTimeout(() => { + onClearNotification(appNotification.id); + }, appNotification.timeout); + } + + render() { + const { appNotification, onClearNotification } = this.props; + return ( +
+
+ +
+
+
{appNotification.title}
+
{appNotification.text}
+
+ +
+ ); + } +} diff --git a/public/app/core/components/AppNotifications/AppNotificationList.tsx b/public/app/core/components/AppNotifications/AppNotificationList.tsx index 53c88a41155..c91f8372384 100644 --- a/public/app/core/components/AppNotifications/AppNotificationList.tsx +++ b/public/app/core/components/AppNotifications/AppNotificationList.tsx @@ -1,53 +1,30 @@ import React, { PureComponent } from 'react'; import appEvents from 'app/core/app_events'; -import { addAppNotification, clearAppNotification } from '../../actions/appNotification'; +import AppNotificationItem from './AppNotificationItem'; +import { notifyApp, clearAppNotification } from 'app/core/actions'; import { connectWithStore } from 'app/core/utils/connectWithReduxStore'; -import { AppNotification, AppNotificationSeverity, StoreState } from 'app/types'; +import { AppNotification, StoreState } from 'app/types'; +import { + createErrorNotification, + createSuccessNotification, + createWarningNotification, +} from '../../copy/appNotification'; export interface Props { appNotifications: AppNotification[]; - addAppNotification: typeof addAppNotification; + notifyApp: typeof notifyApp; clearAppNotification: typeof clearAppNotification; } export class AppNotificationList extends PureComponent { componentDidMount() { - appEvents.on('alert-warning', options => this.addAppNotification(options[0], options[1], 'warning', 5000)); - appEvents.on('alert-success', options => this.addAppNotification(options[0], options[1], 'success', 3000)); - appEvents.on('alert-error', options => this.addAppNotification(options[0], options[1], 'error', 7000)); + const { notifyApp } = this.props; + + appEvents.on('alert-warning', options => notifyApp(createWarningNotification(options[0], options[1]))); + appEvents.on('alert-success', options => notifyApp(createSuccessNotification(options[0], options[1]))); + appEvents.on('alert-error', options => notifyApp(createErrorNotification(options[0], options[1]))); } - addAppNotification(title, text, severity, timeout) { - const id = Date.now(); - const newAlert = { - id: id, - title: title || '', - text: text || '', - severity: severity || AppNotificationSeverity.Info, - icon: this.getIconForSeverity(severity), - remove: this.clearAutomatically(id, timeout), - }; - - this.props.addAppNotification(newAlert); - } - - getIconForSeverity(severity) { - switch (severity) { - case AppNotificationSeverity.Success: - return 'fa fa-check'; - case AppNotificationSeverity.Error: - return 'fa fa-exclamation-triangle'; - default: - return 'fa fa-exclamation'; - } - } - - clearAutomatically = (id, timeout) => { - setTimeout(() => { - this.props.clearAppNotification(id); - }, timeout); - }; - onClearAppNotification = id => { this.props.clearAppNotification(id); }; @@ -59,22 +36,11 @@ export class AppNotificationList extends PureComponent {
{appNotifications.map((appNotification, index) => { return ( -
-
- -
-
-
{appNotification.title}
-
{appNotification.text}
-
- -
+ this.onClearAppNotification(id)} + /> ); })}
@@ -87,7 +53,7 @@ const mapStateToProps = (state: StoreState) => ({ }); const mapDispatchToProps = { - addAppNotification, + notifyApp, clearAppNotification, }; diff --git a/public/app/core/copy/appNotification.ts b/public/app/core/copy/appNotification.ts new file mode 100644 index 00000000000..c34480d7aad --- /dev/null +++ b/public/app/core/copy/appNotification.ts @@ -0,0 +1,46 @@ +import { AppNotification, AppNotificationSeverity, AppNotificationTimeout } from 'app/types'; + +const defaultSuccessNotification: AppNotification = { + title: '', + text: '', + severity: AppNotificationSeverity.Success, + icon: 'fa fa-check', + timeout: AppNotificationTimeout.Success, +}; + +const defaultWarningNotification: AppNotification = { + title: '', + text: '', + severity: AppNotificationSeverity.Warning, + icon: 'fa fa-exclamation', + timeout: AppNotificationTimeout.Warning, +}; + +const defaultErrorNotification: AppNotification = { + title: '', + text: '', + severity: AppNotificationSeverity.Error, + icon: 'fa fa-exclamation-triangle', + timeout: AppNotificationTimeout.Error, +}; + +export const createSuccessNotification = (title: string, text?: string): AppNotification => ({ + ...defaultSuccessNotification, + title: title, + text: text, + id: Date.now(), +}); + +export const createErrorNotification = (title: string, text?: string): AppNotification => ({ + ...defaultErrorNotification, + title: title, + text: text, + id: Date.now(), +}); + +export const createWarningNotification = (title: string, text?: string): AppNotification => ({ + ...defaultWarningNotification, + title: title, + text: text, + id: Date.now(), +}); diff --git a/public/app/core/reducers/appNotification.test.ts b/public/app/core/reducers/appNotification.test.ts index 5098abc9e74..183b699f5fc 100644 --- a/public/app/core/reducers/appNotification.test.ts +++ b/public/app/core/reducers/appNotification.test.ts @@ -1,6 +1,6 @@ import { appNotificationsReducer } from './appNotification'; import { ActionTypes } from '../actions/appNotification'; -import { AppNotificationSeverity } from 'app/types/index'; +import { AppNotificationSeverity, AppNotificationTimeout } from 'app/types/'; describe('clear alert', () => { it('should filter alert', () => { @@ -15,6 +15,7 @@ describe('clear alert', () => { icon: 'success', title: 'test', text: 'test alert', + timeout: AppNotificationTimeout.Success, }, { id: id2, @@ -22,6 +23,7 @@ describe('clear alert', () => { icon: 'warning', title: 'test2', text: 'test alert fail 2', + timeout: AppNotificationTimeout.Warning, }, ], }; @@ -39,6 +41,7 @@ describe('clear alert', () => { icon: 'success', title: 'test', text: 'test alert', + timeout: AppNotificationTimeout.Success, }, ], }; diff --git a/public/app/core/reducers/appNotification.ts b/public/app/core/reducers/appNotification.ts index 8812546356a..2c8bbbbd84d 100644 --- a/public/app/core/reducers/appNotification.ts +++ b/public/app/core/reducers/appNotification.ts @@ -1,4 +1,4 @@ -import { AppNotification, AppNotificationsState } from 'app/types/index'; +import { AppNotification, AppNotificationsState } from 'app/types/'; import { Action, ActionTypes } from '../actions/appNotification'; export const initialState: AppNotificationsState = { @@ -17,7 +17,3 @@ export const appNotificationsReducer = (state = initialState, action: Action): A } return state; }; - -export default { - appNotifications: appNotificationsReducer, -}; diff --git a/public/app/core/reducers/index.ts b/public/app/core/reducers/index.ts index be13528c91c..1c8670ed0d6 100644 --- a/public/app/core/reducers/index.ts +++ b/public/app/core/reducers/index.ts @@ -1,7 +1,9 @@ import { navIndexReducer as navIndex } from './navModel'; import { locationReducer as location } from './location'; +import { appNotificationsReducer as appNotifications } from './appNotification'; export default { navIndex, location, + appNotifications, }; diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index 6b3205dc53e..ccd027a0b6d 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -10,7 +10,6 @@ import dashboardReducers from 'app/features/dashboard/state/reducers'; import pluginReducers from 'app/features/plugins/state/reducers'; import dataSourcesReducers from 'app/features/datasources/state/reducers'; import usersReducers from 'app/features/users/state/reducers'; -import appNotificationReducers from 'app/core/reducers/appNotification'; const rootReducers = { ...sharedReducers, @@ -22,7 +21,6 @@ const rootReducers = { ...pluginReducers, ...dataSourcesReducers, ...usersReducers, - ...appNotificationReducers, }; export let store; diff --git a/public/app/types/appNotifications.ts b/public/app/types/appNotifications.ts index 1bbc66a4baa..81e6cfd55e1 100644 --- a/public/app/types/appNotifications.ts +++ b/public/app/types/appNotifications.ts @@ -4,6 +4,7 @@ export interface AppNotification { icon: string; title: string; text: string; + timeout: AppNotificationTimeout; } export enum AppNotificationSeverity { @@ -13,6 +14,12 @@ export enum AppNotificationSeverity { Info = 'info', } +export enum AppNotificationTimeout { + Warning = 5000, + Success = 3000, + Error = 7000, +} + export interface AppNotificationsState { appNotifications: AppNotification[]; } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index b888ba83877..0cb1b196419 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,12 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins'; -import { AppNotification, AppNotificationSeverity, AppNotificationsState } from './appNotifications'; +import { + AppNotification, + AppNotificationSeverity, + AppNotificationsState, + AppNotificationTimeout, +} from './appNotifications'; export { Team, @@ -50,6 +55,7 @@ export { AppNotification, AppNotificationsState, AppNotificationSeverity, + AppNotificationTimeout, }; export interface StoreState { From f1660aa21a86349e411eba49ea0f475af57f37dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 25 Oct 2018 17:05:17 +0200 Subject: [PATCH 9/9] fix: updated backend srv to use appEvents and removed parts of alertsSrv --- public/app/core/components/grafana_app.ts | 4 - public/app/core/services/alert_srv.ts | 96 +---------------------- public/app/core/services/backend_srv.ts | 13 +-- public/app/core/specs/backend_srv.test.ts | 2 +- public/app/features/dashboard/upload.ts | 4 +- 5 files changed, 14 insertions(+), 105 deletions(-) diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index 2774ab99426..c2b6808d586 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -16,7 +16,6 @@ export class GrafanaCtrl { /** @ngInject */ constructor( $scope, - alertSrv, utilSrv, $rootScope, $controller, @@ -37,11 +36,8 @@ export class GrafanaCtrl { $scope._ = _; profiler.init(config, $rootScope); - alertSrv.init(); utilSrv.init(); bridgeSrv.init(); - - $scope.dashAlerts = alertSrv; }; $rootScope.colors = colors; diff --git a/public/app/core/services/alert_srv.ts b/public/app/core/services/alert_srv.ts index 2d447651b75..4995b148abd 100644 --- a/public/app/core/services/alert_srv.ts +++ b/public/app/core/services/alert_srv.ts @@ -1,100 +1,12 @@ -import angular from 'angular'; -import _ from 'lodash'; import coreModule from 'app/core/core_module'; -import appEvents from 'app/core/app_events'; export class AlertSrv { - list: any[]; + constructor() {} - /** @ngInject */ - constructor(private $timeout, private $rootScope) { - this.list = []; - } - - init() { - this.$rootScope.onAppEvent( - 'alert-error', - (e, alert) => { - this.set(alert[0], alert[1], 'error', 12000); - }, - this.$rootScope - ); - - this.$rootScope.onAppEvent( - 'alert-warning', - (e, alert) => { - this.set(alert[0], alert[1], 'warning', 5000); - }, - this.$rootScope - ); - - this.$rootScope.onAppEvent( - 'alert-success', - (e, alert) => { - this.set(alert[0], alert[1], 'success', 3000); - }, - this.$rootScope - ); - - appEvents.on('alert-warning', options => this.set(options[0], options[1], 'warning', 5000)); - appEvents.on('alert-success', options => this.set(options[0], options[1], 'success', 3000)); - appEvents.on('alert-error', options => this.set(options[0], options[1], 'error', 7000)); - } - - getIconForSeverity(severity) { - switch (severity) { - case 'success': - return 'fa fa-check'; - case 'error': - return 'fa fa-exclamation-triangle'; - default: - return 'fa fa-exclamation'; - } - } - - set(title, text, severity, timeout) { - if (_.isObject(text)) { - console.log('alert error', text); - if (text.statusText) { - text = `HTTP Error (${text.status}) ${text.statusText}`; - } - } - - const newAlert = { - title: title || '', - text: text || '', - severity: severity || 'info', - icon: this.getIconForSeverity(severity), - }; - - const newAlertJson = angular.toJson(newAlert); - - // remove same alert if it already exists - _.remove(this.list, value => { - return angular.toJson(value) === newAlertJson; - }); - - this.list.push(newAlert); - if (timeout > 0) { - this.$timeout(() => { - this.list = _.without(this.list, newAlert); - }, timeout); - } - - if (!this.$rootScope.$$phase) { - this.$rootScope.$digest(); - } - - return newAlert; - } - - clear(alert) { - this.list = _.without(this.list, alert); - } - - clearAll() { - this.list = []; + set() { + console.log('old depricated alert srv being used'); } } +// this is just added to not break old plugins that might be using it coreModule.service('alertSrv', AlertSrv); diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index 3e8132a695b..144567efeb9 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -9,7 +9,7 @@ export class BackendSrv { private noBackendCache: boolean; /** @ngInject */ - constructor(private $http, private alertSrv, private $q, private $timeout, private contextSrv) {} + constructor(private $http, private $q, private $timeout, private contextSrv) {} get(url, params?) { return this.request({ method: 'GET', url: url, params: params }); @@ -49,14 +49,14 @@ export class BackendSrv { } if (err.status === 422) { - this.alertSrv.set('Validation failed', data.message, 'warning', 4000); + appEvents.emit('alert-warning', ['Validation failed', data.message]); throw data; } - data.severity = 'error'; + let severity = 'error'; if (err.status < 500) { - data.severity = 'warning'; + severity = 'warning'; } if (data.message) { @@ -66,7 +66,8 @@ export class BackendSrv { description = message; message = 'Error'; } - this.alertSrv.set(message, description, data.severity, 10000); + + appEvents.emit('alert-' + severity, [message, description]); } throw data; @@ -93,7 +94,7 @@ export class BackendSrv { if (options.method !== 'GET') { if (results && results.data.message) { if (options.showSuccessAlert !== false) { - this.alertSrv.set(results.data.message, '', 'success', 3000); + appEvents.emit('alert-success', [results.data.message]); } } } diff --git a/public/app/core/specs/backend_srv.test.ts b/public/app/core/specs/backend_srv.test.ts index 2e35b87deb4..a6cb5a7d331 100644 --- a/public/app/core/specs/backend_srv.test.ts +++ b/public/app/core/specs/backend_srv.test.ts @@ -9,7 +9,7 @@ describe('backend_srv', () => { return Promise.resolve({}); }; - const _backendSrv = new BackendSrv(_httpBackend, {}, {}, {}, {}); + const _backendSrv = new BackendSrv(_httpBackend, {}, {}, {}); describe('when handling errors', () => { it('should return the http status code', async () => { diff --git a/public/app/features/dashboard/upload.ts b/public/app/features/dashboard/upload.ts index 42871327eb6..ec4ad9a03cb 100644 --- a/public/app/features/dashboard/upload.ts +++ b/public/app/features/dashboard/upload.ts @@ -11,7 +11,7 @@ const template = ` `; /** @ngInject */ -function uploadDashboardDirective(timer, alertSrv, $location) { +function uploadDashboardDirective(timer, $location) { return { restrict: 'E', template: template, @@ -59,7 +59,7 @@ function uploadDashboardDirective(timer, alertSrv, $location) { // Something elem[0].addEventListener('change', file_selected, false); } else { - alertSrv.set('Oops', 'Sorry, the HTML5 File APIs are not fully supported in this browser.', 'error'); + appEvents.emit('alert-error', ['Oops', 'The HTML5 File APIs are not fully supported in this browser']); } }, };