Skip to content

Commit

Permalink
Fix #1884. Add cookie policy notification (#1890)
Browse files Browse the repository at this point in the history
* Fix #1884. Add cookie policy notification

 - Add Notification system
 - Add cookie policy epic to show the cookie policy notification

* add missing dependency

* Add documentation

* last doc fixes

* Removed early optimization and simplified reducers
  • Loading branch information
offtherailz authored May 29, 2017
1 parent 553146b commit 2fadbf1
Show file tree
Hide file tree
Showing 20 changed files with 535 additions and 8 deletions.
5 changes: 5 additions & 0 deletions docma-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
"web/client/components/buttons/GlobeViewSwitcherButton.jsx",
"web/client/components/buttons/GoFullButton.jsx",
"web/client/components/mapcontrols/search/SearchBar.jsx",
"web/client/components/notifications/NotificationContainer.jsx",
"web/client/components/buttons/ToggleButton.jsx",
"web/client/components/plugins/PluginsContainer.jsx",

Expand All @@ -118,6 +119,7 @@
"web/client/actions/globeswitcher.js",
"web/client/actions/maps.js",
"web/client/actions/maptype.js",
"web/client/actions/notifications.js",
"web/client/actions/search.js",

"web/client/selectors/index.jsdoc",
Expand All @@ -130,9 +132,11 @@
"web/client/reducers/globeswitcher.js",
"web/client/reducers/maps.js",
"web/client/reducers/maptype.js",
"web/client/reducers/notifications.js",
"web/client/reducers/search.js",

"web/client/epics/index.jsdoc",
"web/client/epics/cookies.js",
"web/client/epics/fullscreen.js",
"web/client/epics/globeswitcher.js",
"web/client/epics/maptype.js",
Expand Down Expand Up @@ -162,6 +166,7 @@
"web/client/plugins/Identify.jsx",
"web/client/plugins/Locate.jsx",
"web/client/plugins/Login.jsx",
"web/client/plugins/Notifications.jsx",
"web/client/plugins/ScaleBox.jsx",
"web/client/plugins/ScrollTop.jsx",
"web/client/plugins/Search.jsx",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"react-input-autosize": "1.1.0",
"react-intl": "2.2.3",
"react-joyride": "1.10.1",
"react-notification-system": "0.2.14",
"react-nouislider": "1.11.0",
"react-overlays": "0.6.3",
"react-pdf": "1.6.1",
Expand Down
71 changes: 71 additions & 0 deletions web/client/actions/__tests__/notifications-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2017, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

var expect = require('expect');
var {
SHOW_NOTIFICATION,
HIDE_NOTIFICATION,
CLEAR_NOTIFICATIONS,
show,
success,
warning,
error,
info,
hide,
clear
} = require('../notifications');

describe('Test correctness of the notifications actions', () => {

it('show', () => {
const action = show({title: "test"});
expect(action.type).toBe(SHOW_NOTIFICATION);
expect(action.title).toBe('test');
expect(action.level).toBe('success');
expect(action.uid).toExist();
});
it('hide', () => {
const action = hide("1234");
expect(action.type).toBe(HIDE_NOTIFICATION);
expect(action.uid).toBe('1234');
});
it('clear', () => {
const action = clear();
expect(action.type).toBe(CLEAR_NOTIFICATIONS);
});
it('success', () => {
const action = success({title: "test"});
expect(action.type).toBe(SHOW_NOTIFICATION);
expect(action.title).toBe('test');
expect(action.level).toBe('success');
expect(action.uid).toExist();
});
it('warning', () => {
const action = warning({title: "test"});
expect(action.type).toBe(SHOW_NOTIFICATION);
expect(action.title).toBe('test');
expect(action.level).toBe('warning');
expect(action.uid).toExist();
});
it('error', () => {
const action = error({title: "test"});
expect(action.type).toBe(SHOW_NOTIFICATION);
expect(action.title).toBe('test');
expect(action.level).toBe('error');
expect(action.uid).toExist();
});
it('info', () => {
const action = info({title: "test"});
expect(action.type).toBe(SHOW_NOTIFICATION);
expect(action.title).toBe('test');
expect(action.level).toBe('info');
expect(action.uid).toExist();
});


});
109 changes: 109 additions & 0 deletions web/client/actions/notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const SHOW_NOTIFICATION = 'SHOW_NOTIFICATION';
const HIDE_NOTIFICATION = 'HIDE_NOTIFICATION';
const CLEAR_NOTIFICATIONS = 'CLEAR_NOTIFICATIONS';
/**
* Show a notification
* @memberof actions.notifications
* @param {Object} [opts={}] the notification configuration. [Here the compete ref](https://github.com/igorprado/react-notification-system#creating-a-notification)
* ```
* {
* title: "title.translation.path" // or the message directly
* message: "message.translation.path" // or the message directly
* uid: "1234" // a unique identifier (if not present, current time is used),
* action: {
* label: "label.translation.path" // or the message directly
* }
* }
* ```
* @param {String} [level='success'] The level of the notification. (one of "success"|"warning"|"info"|"error")
* @return {object} action of type `SHOW_NOTIFICATION`
*/
function show(opts = {}, level = 'success') {
return {
type: SHOW_NOTIFICATION,
...opts,
uid: opts.uid || Date.now(),
level
};
}

/**
* Hides (removes) the notification with the id provided
* @memberof actions.notifications
* @param {string|number} uid the identifier
* @returns {object} action of type `HIDE_NOTIFICATION`
*/
function hide(uid) {
return {
type: HIDE_NOTIFICATION,
uid
};
}

/**
* Show a success notification. {@see actions.notifications.show}
* @memberof actions.notifications
* @param {object} opts notification opts
* @returns {object} action of type `HIDE_NOTIFICATION`
*/
function success(opts) {
return show(opts, 'success');
}
/**
* Show a error notification. {@see actions.notifications.show}
* @memberof actions.notifications
* @param {object} opts notification opts
* @returns {object} action of type `HIDE_NOTIFICATION`
*/
function error(opts) {
return show(opts, 'error');
}

/**
* Show a warning notification. {@see actions.notifications.show}
* @memberof actions.notifications
* @param {object} opts notification opts
* @returns {object} action of type `HIDE_NOTIFICATION`
*/
function warning(opts) {
return show(opts, 'warning');
}

/**
* Show a info notification. {@see actions.notifications.show}
* @memberof actions.notifications
* @param {object} opts notification opts
* @returns {object} action of type `HIDE_NOTIFICATION`
*/
function info(opts) {
return show(opts, 'info');
}

/**
* Clear all the notifications
* @memberof actions.notifications
* @returns {object} action of type `CLEAR_NOTIFICATIONS`
*/
function clear() {
return {
type: CLEAR_NOTIFICATIONS
};
}

/**
* actions for notifications
* @name notifications
* @memberof actions
*/
module.exports = {
SHOW_NOTIFICATION,
HIDE_NOTIFICATION,
CLEAR_NOTIFICATIONS,
show,
success,
warning,
error,
info,
hide,
clear
};
88 changes: 88 additions & 0 deletions web/client/components/notifications/NotificationContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2017, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const NotificationSystem = require('react-notification-system');
var LocaleUtils = require('../../utils/LocaleUtils');
/**
* Container for Notifications. Allows to display notifications by passing
* them in the notification property
* @class
* @memberof components.notifications
* @prop {object[]} notifications the notifications. Each element have this form
* ```
* {
* title: "title.translation.path" // or the message directly
* message: "message.translation.path" // or the message directly
* uid: "1234" // a unique identifier (if not present, current time is used),
* action: {
* label: "label.translation.path" // or the message directly
* }
* }
* ```
* @see https://github.com/igorprado/react-notification-system#creating-a-notification for othe options
* @example
* <NotificationContainer notifications={[{uid: 1, title: "notification", level: "success"}]} />;
*/
const NotificationContainer = React.createClass({
propTypes: {
notifications: React.PropTypes.array,
onRemove: React.PropTypes.func
},
contextTypes: {
messages: React.PropTypes.object
},
getDefaultProps() {
return {
notifications: [],
onRemove: () => {}
};
},
componentDidMount() {
this.updateNotifications(this.props.notifications);
},
componentDidUpdate() {
const {notifications} = this.props || [];
this.updateNotifications(notifications);
},
render() {
const {notifications, onRemove, ...rest} = this.props;
return (<NotificationSystem ref="notify" { ...rest } />);
},
system() {
return this.refs.notify;
},
updateNotifications(notifications) {
const notificationIds = notifications.map(notification => notification.uid);
const systemNotifications = this.system().state.notifications || [];
// Get all active notifications from react-notification-system
// and remove all where uid is not found in the reducer
systemNotifications.forEach(notification => {
if (notificationIds.indexOf(notification.uid) < 0) {
this.system().removeNotification(notification.uid);
}
});
notifications.forEach(notification => {
if (systemNotifications.indexOf(notification.uid) < 0) {
this.system().addNotification({
...notification,
title: LocaleUtils.getMessageById(this.context.messages, notification.title) || notification.title,
message: LocaleUtils.getMessageById(this.context.messages, notification.message) || notification.message,
action: notification.action && {
label: LocaleUtils.getMessageById(this.context.messages, notification.action.label) || notification.action.label
},
onRemove: () => {
this.props.onRemove(notification.uid);
if (notification.onRemove) notification.onRemove();
}
});
}
});
}
});

module.exports = NotificationContainer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2017, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const ReactDOM = require('react-dom');
const NotificationContainer = require('../NotificationContainer.jsx');
const expect = require('expect');

const TestUtils = require('react-addons-test-utils');
const N1 = {
uid: "1",
title: "test 1",
message: "test 1",
autodismiss: 0,
level: "success"
};

const N2 = {
uid: "2",
title: "test 2",
message: "test 2",
autodismiss: 0,
level: "success"
};

describe('NotificationContainer tests', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});

afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});
// test DEFAULTS
it('creates the component with defaults', () => {
const item = ReactDOM.render(<NotificationContainer />, document.getElementById("container"));
expect(item).toExist();

});
it('creates the component with notifications', () => {
const item = ReactDOM.render(<NotificationContainer notifications={[N1]} />, document.getElementById("container"));
expect(item).toExist();
let elems = TestUtils.scryRenderedDOMComponentsWithClass(item, "notifications-tr");
expect(elems.length).toBe(1);
});
it('update notifications', () => {
let item = ReactDOM.render(<NotificationContainer notifications={[N1]} />, document.getElementById("container"));
expect(item).toExist();
let elems = TestUtils.scryRenderedDOMComponentsWithClass(item, "notification");
expect(elems.length).toBe(1);

// add notification
item = ReactDOM.render(<NotificationContainer notifications={[N1, N2]} />, document.getElementById("container"));
elems = TestUtils.scryRenderedDOMComponentsWithClass(item, "notification");
expect(elems.length).toBe(2);

// remove notification
item = ReactDOM.render(<NotificationContainer notifications={[N2]} />, document.getElementById("container"));
elems = TestUtils.scryRenderedDOMComponentsWithClass(item, "notification").filter( (e) => e.className.indexOf("notification-hidden") < 0);
expect(elems.length).toBe(1);
});
});
Loading

0 comments on commit 2fadbf1

Please sign in to comment.