diff --git a/.eslintrc b/.eslintrc index 6b0a502..1d72f1b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -17,7 +17,7 @@ } }, "rules": { - "react/display-name": 2, + "react/display-name": 0, "react/jsx-curly-spacing": [2, "always"], "react/jsx-no-duplicate-props": 2, "react/jsx-no-undef": 2, diff --git a/CHANGELOG.md b/CHANGELOG.md index 18f9e48..a994f53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## 0.2.14 - May 3, 2017 + +* Ability to [edit notifications](https://github.com/igorprado/react-notification-system#removenotificationnotification). (thanks to @syndbg) +* Removed deprecation warning. Now using `prop-types` and `create-react-class`packages. (thanks to @andrewBalekha) +* Fix calling `onRemove` before updating the notifications state. (thanks to @szdc) + ## 0.2.13 - Mar 14, 2017 * UMD support. (thanks to @jochenberger) diff --git a/README.md b/README.md index f8dc04b..6677d0b 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,12 @@ Returns the notification object to be used to programmatically dismiss a notific Remove a notification programmatically. You can pass an object returned by `addNotification()` or by `onAdd()` callback. If passing an object, you need to make sure it must contain the `uid` property. You can pass only the `uid` too: `removeNotification(uid)`. + +### `editNotification(notification)` + +Edit a notification programmatically. You can pass an object previously returned by `addNotification()` or by `onAdd()` callback. If passing an object, you need to make sure it must contain the `uid` property. You can pass only the `uid` too: `editNotification(uid)`. + + ### `clearNotifications()` Removes ALL notifications programatically. diff --git a/example/src/scripts/App.jsx b/example/src/scripts/App.jsx index 429abfb..0615311 100644 --- a/example/src/scripts/App.jsx +++ b/example/src/scripts/App.jsx @@ -1,5 +1,6 @@ var React = require('react'); var ReactDOM = require('react-dom'); +var createReactClass = require('create-react-class'); var NotificationSystem = require('NotificationSystem'); var constants = require('constants'); var NotificationGenerator = require('./NotificationGenerator'); @@ -14,7 +15,7 @@ var _getRandomPosition = function() { // Styles require('styles/base'); -NotificationSystemExample = React.createClass({ +NotificationSystemExample = createReactClass({ displayName: 'App', diff --git a/example/src/scripts/CustomElement.jsx b/example/src/scripts/CustomElement.jsx index 6889204..751bd51 100644 --- a/example/src/scripts/CustomElement.jsx +++ b/example/src/scripts/CustomElement.jsx @@ -1,4 +1,5 @@ var React = require('react'); +var PropTypes = require('prop-types'); function buttonClicked() { alert('I\'m a custom button inside a custom element that was clicked'); @@ -14,7 +15,7 @@ function CustomElement(props) { } CustomElement.propTypes = { - name: React.PropTypes.string + name: PropTypes.string }; module.exports = CustomElement; diff --git a/example/src/scripts/NotificationGenerator.jsx b/example/src/scripts/NotificationGenerator.jsx index 57fa4bb..6eadf46 100644 --- a/example/src/scripts/NotificationGenerator.jsx +++ b/example/src/scripts/NotificationGenerator.jsx @@ -1,9 +1,11 @@ var React = require('react'); +var createReactClass = require('create-react-class'); +var PropTypes = require('prop-types'); // Styles require('styles/generator'); -module.exports = React.createClass({ +module.exports = createReactClass({ displayName: 'NotificationGenerator', @@ -134,8 +136,8 @@ module.exports = React.createClass({ }, propTypes: { - notifications: React.PropTypes.func.isRequired, - allowHTML: React.PropTypes.func + notifications: PropTypes.func.isRequired, + allowHTML: PropTypes.func }, render: function() { diff --git a/package.json b/package.json index 54f326b..31cc5a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-notification-system", - "version": "0.2.13", + "version": "0.2.14", "description": "A React Notification System fully customized", "main": "dist/NotificationSystem.js", "scripts": { @@ -32,7 +32,9 @@ }, "homepage": "https://github.com/igorprado/react-notification-system", "dependencies": { - "object-assign": "^4.0.1" + "create-react-class": "^15.5.1", + "object-assign": "^4.0.1", + "prop-types": "^15.5.6" }, "peerDependencies": { "react": "0.14.x || ^15.0.0", diff --git a/src/NotificationContainer.jsx b/src/NotificationContainer.jsx index 2c1dbec..0b6e044 100644 --- a/src/NotificationContainer.jsx +++ b/src/NotificationContainer.jsx @@ -1,13 +1,15 @@ var React = require('react'); +var createReactClass = require('create-react-class'); +var PropTypes = require('prop-types'); var NotificationItem = require('./NotificationItem'); var Constants = require('./constants'); -var NotificationContainer = React.createClass({ +var NotificationContainer = createReactClass({ propTypes: { - position: React.PropTypes.string.isRequired, - notifications: React.PropTypes.array.isRequired, - getStyles: React.PropTypes.object + position: PropTypes.string.isRequired, + notifications: PropTypes.array.isRequired, + getStyles: PropTypes.object }, _style: {}, diff --git a/src/NotificationItem.jsx b/src/NotificationItem.jsx index a8e1fbb..7c015b4 100644 --- a/src/NotificationItem.jsx +++ b/src/NotificationItem.jsx @@ -1,4 +1,6 @@ var React = require('react'); +var createReactClass = require('create-react-class'); +var PropTypes = require('prop-types'); var ReactDOM = require('react-dom'); var Constants = require('./constants'); var Helpers = require('./helpers'); @@ -24,17 +26,17 @@ var whichTransitionEvent = function() { return transition; }; -var NotificationItem = React.createClass({ +var NotificationItem = createReactClass({ propTypes: { - notification: React.PropTypes.object, - getStyles: React.PropTypes.object, - onRemove: React.PropTypes.func, - allowHTML: React.PropTypes.bool, - noAnimation: React.PropTypes.bool, - children: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.element + notification: PropTypes.object, + getStyles: PropTypes.object, + onRemove: PropTypes.func, + allowHTML: PropTypes.bool, + noAnimation: PropTypes.bool, + children: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.element ]) }, diff --git a/src/NotificationSystem.jsx b/src/NotificationSystem.jsx index adbef3e..f35c464 100644 --- a/src/NotificationSystem.jsx +++ b/src/NotificationSystem.jsx @@ -1,10 +1,12 @@ var React = require('react'); +var createReactClass = require('create-react-class'); +var PropTypes = require('prop-types'); var merge = require('object-assign'); var NotificationContainer = require('./NotificationContainer'); var Constants = require('./constants'); var Styles = require('./styles'); -var NotificationSystem = React.createClass({ +var NotificationSystem = createReactClass({ uid: 3400, @@ -66,17 +68,18 @@ var NotificationSystem = React.createClass({ var notifications = this.state.notifications.filter(function(toCheck) { if (toCheck.uid === uid) { notification = toCheck; + return false; } - return toCheck.uid !== uid; + return true; }); - if (notification && notification.onRemove) { - notification.onRemove(notification); - } - if (this._isMounted) { this.setState({ notifications: notifications }); } + + if (notification && notification.onRemove) { + notification.onRemove(notification); + } }, getInitialState: function() { @@ -86,12 +89,12 @@ var NotificationSystem = React.createClass({ }, propTypes: { - style: React.PropTypes.oneOfType([ - React.PropTypes.bool, - React.PropTypes.object + style: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.object ]), - noAnimation: React.PropTypes.bool, - allowHTML: React.PropTypes.bool + noAnimation: PropTypes.bool, + allowHTML: PropTypes.bool }, getDefaultProps: function() { @@ -152,18 +155,63 @@ var NotificationSystem = React.createClass({ return _notification; }, - removeNotification: function(notification) { + getNotificationRef: function(notification) { var self = this; + var foundNotification = null; + Object.keys(this.refs).forEach(function(container) { if (container.indexOf('container') > -1) { Object.keys(self.refs[container].refs).forEach(function(_notification) { var uid = notification.uid ? notification.uid : notification; if (_notification === 'notification-' + uid) { - self.refs[container].refs[_notification]._hideNotification(); + // NOTE: Stop iterating further and return the found notification. + // Since UIDs are uniques and there won't be another notification found. + foundNotification = self.refs[container].refs[_notification]; + return; } }); } }); + + return foundNotification; + }, + + removeNotification: function(notification) { + var foundNotification = this.getNotificationRef(notification); + return foundNotification && foundNotification._hideNotification(); + }, + + editNotification: function(notification, newNotification) { + var foundNotification = null; + // NOTE: Find state notification to update by using + // `setState` and forcing React to re-render the component. + var uid = notification.uid ? notification.uid : notification; + + var newNotifications = this.state.notifications.filter(function(stateNotification) { + if (uid === stateNotification.uid) { + foundNotification = stateNotification; + return false; + } + + return true; + }); + + + if (!foundNotification) { + return; + } + + newNotifications.push( + merge( + {}, + foundNotification, + newNotification + ) + ); + + this.setState({ + notifications: newNotifications + }); }, clearNotifications: function() { @@ -221,7 +269,6 @@ var NotificationSystem = React.createClass({
{ containers }
- ); } }); diff --git a/test/notification-system.test.js b/test/notification-system.test.js index f2a5564..e8139bd 100644 --- a/test/notification-system.test.js +++ b/test/notification-system.test.js @@ -172,6 +172,38 @@ describe('Notification Component', function() { done(); }); + it('should edit an existing notification using returned object', (done) => { + const notificationCreated = component.addNotification(defaultNotification); + const notification = TestUtils.scryRenderedDOMComponentsWithClass(instance, 'notification'); + expect(notification.length).toEqual(1); + + const newTitle = 'foo'; + const newContent = 'foobar'; + + component.editNotification(notificationCreated, { title: newTitle, message: newContent }); + clock.tick(1000); + const notificationEdited = TestUtils.findRenderedDOMComponentWithClass(instance, 'notification'); + expect(notificationEdited.getElementsByClassName('notification-title')[0].textContent).toEqual(newTitle); + expect(notificationEdited.getElementsByClassName('notification-message')[0].textContent).toEqual(newContent); + done(); + }); + + it('should edit an existing notification using uid', (done) => { + const notificationCreated = component.addNotification(defaultNotification); + const notification = TestUtils.scryRenderedDOMComponentsWithClass(instance, 'notification'); + expect(notification.length).toEqual(1); + + const newTitle = 'foo'; + const newContent = 'foobar'; + + component.editNotification(notificationCreated.uid, { title: newTitle, message: newContent }); + clock.tick(1000); + const notificationEdited = TestUtils.findRenderedDOMComponentWithClass(instance, 'notification'); + expect(notificationEdited.getElementsByClassName('notification-title')[0].textContent).toEqual(newTitle); + expect(notificationEdited.getElementsByClassName('notification-message')[0].textContent).toEqual(newContent); + done(); + }); + it('should remove all notifications', done => { component.addNotification(defaultNotification); component.addNotification(defaultNotification);