From 265e99d00a5edb6fb47a19eef3f98cd447f7f36e Mon Sep 17 00:00:00 2001 From: Geoff Pleiss Date: Tue, 13 Jan 2015 10:08:19 -0500 Subject: [PATCH] feat(react-alerts): create react alerts - React alerts can be dismissable (adds a close button) - Close button has optional callback - React alerts can have icons [Finishes #85646924] Signed-off-by: Nicole Sullivan --- src/pivotal-ui/components/alerts.scss | 107 +++++++++++++++ src/pivotal-ui/javascripts/alerts.jsx | 87 ++++++++++++ src/pivotal-ui/javascripts/components.js | 7 +- test/spec/javascripts/alerts_spec.jsx | 164 +++++++++++++++++++++++ 4 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 src/pivotal-ui/javascripts/alerts.jsx create mode 100644 test/spec/javascripts/alerts_spec.jsx diff --git a/src/pivotal-ui/components/alerts.scss b/src/pivotal-ui/components/alerts.scss index e79a608ca..b8768f2de 100644 --- a/src/pivotal-ui/components/alerts.scss +++ b/src/pivotal-ui/components/alerts.scss @@ -188,3 +188,110 @@ other content inside a container on the page (of a width 500px for this example) } } +/*doc +--- +title: React Alerts +name: react_alerts +categories: + - Beta +--- + +React alerts + +```html_example_table +
+``` + +```jsx_example +React.render( +
+ success + info + warning + error +
, + document.getElementById('react-alert-example') +); +``` + +*/ + +/*doc +--- +title: Dismissable React Alerts +name: react_alerts_dismissable +parent: react_alerts +--- + +Add the `dismissable` property to add a close button to the alert. + +```html_example_table +
+``` + +```jsx_example +React.render( + success, + document.getElementById('react-alert-dismissable-example') +); +``` + +If you want a callback to be called when the close button is +clicked, set the `dismissable` property to that callback. + +```html_example_table +
+``` + +```jsx_example +var callback = function() { + alert('Dismissed!'); +}; + +React.render( + with callback, + document.getElementById('react-alert-dismissable-callback-example') +); +``` + +*/ + +/*doc +--- +title: React Alerts with Icons +name: react_alerts_icon +parent: react_alerts +--- + +If you want an icon to be displayed, set the `withIcon` property. + +```html_example_table +
+``` + +```jsx_example +React.render( +
+ success + info + warning + error +
, + document.getElementById('react-alert-icon-example') +); +``` + +Here's a dismissable alert with an icon + +```html_example_table +
+``` + +```jsx_example +React.render( + warning, + document.getElementById('react-alert-icon-dismissable-example') +); +``` + +*/ diff --git a/src/pivotal-ui/javascripts/alerts.jsx b/src/pivotal-ui/javascripts/alerts.jsx new file mode 100644 index 000000000..6db702c30 --- /dev/null +++ b/src/pivotal-ui/javascripts/alerts.jsx @@ -0,0 +1,87 @@ +'use strict'; + +var React = require('react'); +var BsAlert = require('react-bootstrap/Alert'); +var Media = require('./media.jsx').Media; + +var Alert = React.createClass({ + getInitialState: function() { + return { + alertVisible: true + }; + }, + + render: function() { + if (this.state.alertVisible) { + var onDismiss = this.props.dismissable ? this.handleAlertDismiss : null; + + var children; + + if (this.props.withIcon) { + var icon = ; + children = ( + + {this.props.children} + + ); + } else { + children = this.props.children; + } + return ( + + {children} + + ); + } + + return ( + + ); + }, + + handleAlertDismiss: function() { + if (typeof this.props.dismissable === "function") { + this.props.dismissable(); + } + this.setState({alertVisible: false}); + } +}); + +var SuccessAlert = React.createClass({ + render: function() { + return ( + + ); + } +}); + +var InfoAlert = React.createClass({ + render: function() { + return ( + + ); + } +}); + +var WarningAlert = React.createClass({ + render: function() { + return ( + + ); + } +}); + +var ErrorAlert = React.createClass({ + render: function() { + return ( + + ); + } +}); + +module.exports = { + SuccessAlert: SuccessAlert, + InfoAlert: InfoAlert, + WarningAlert: WarningAlert, + ErrorAlert: ErrorAlert +}; diff --git a/src/pivotal-ui/javascripts/components.js b/src/pivotal-ui/javascripts/components.js index 2dd98d603..2e17f0009 100644 --- a/src/pivotal-ui/javascripts/components.js +++ b/src/pivotal-ui/javascripts/components.js @@ -82,5 +82,10 @@ module.exports = { Label: require('./labels.jsx').Label, CollapseBase: require('./collapse.jsx').CollapseBase, Collapse: require('./collapse.jsx').Collapse, - CollapseAlt: require('./collapse.jsx').CollapseAlt + CollapseAlt: require('./collapse.jsx').CollapseAlt, + + SuccessAlert: require('./alerts.jsx').SuccessAlert, + InfoAlert: require('./alerts.jsx').InfoAlert, + WarningAlert: require('./alerts.jsx').WarningAlert, + ErrorAlert: require('./alerts.jsx').ErrorAlert, }; diff --git a/test/spec/javascripts/alerts_spec.jsx b/test/spec/javascripts/alerts_spec.jsx new file mode 100644 index 000000000..0917b8ecc --- /dev/null +++ b/test/spec/javascripts/alerts_spec.jsx @@ -0,0 +1,164 @@ +'use strict'; + +var $ = require('jquery'); +var React = require('react/addons'); +var TestUtils = React.addons.TestUtils; + +var SuccessAlert = require('../../../src/pivotal-ui/javascripts/alerts.jsx').SuccessAlert; +var InfoAlert = require('../../../src/pivotal-ui/javascripts/alerts.jsx').InfoAlert; +var WarningAlert = require('../../../src/pivotal-ui/javascripts/alerts.jsx').WarningAlert; +var ErrorAlert = require('../../../src/pivotal-ui/javascripts/alerts.jsx').ErrorAlert; + +describe('Alert', function() { + beforeEach(function() { + this.node = $('
').appendTo('body').get(0); + }); + + afterEach(function() { + React.unmountComponentAtNode(this.node); + document.body.removeChild(this.node); + }); + + describe('when dismissable is set to true', function() { + beforeEach(function() { + React.render( + alert body, + this.node + ); + }); + + it('has a close button', function() { + expect($('#container button.close')).toExist(); + }); + + it('disappears when close button is clicked', function() { + TestUtils.Simulate.click($('#container button.close').get(0)); + expect($('#container .alert')).not.toExist(); + }); + }); + + describe('when dismissable is set to a callback', function() { + beforeEach(function() { + this.callback = jasmine.createSpy('dismissable callback'); + React.render( + alert body, + this.node + ); + }); + + it('has a close button', function() { + expect($('#container button.close')).toExist(); + }); + + describe('when close button is clicked', function() { + beforeEach(function() { + TestUtils.Simulate.click($('#container button.close').get(0)); + }); + + it('disappears', function() { + expect($('#container .alert')).not.toExist(); + }); + + it('calls the callback passed in', function() { + expect(this.callback).toHaveBeenCalled(); + }); + }); + }); + + describe('when dismissable is not present', function() { + beforeEach(function() { + React.render( + alert body, + this.node + ); + }); + + it('does not have a close button', function() { + expect($('#container button.close')).not.toExist(); + }); + }); +}); + +describe('SuccessAlert', function() { + describe('when withIcon is set to true', function() { + beforeEach(function() { + this.node = $('
').appendTo('body').get(0); + React.render( + alert body, + this.node + ); + }); + + afterEach(function() { + React.unmountComponentAtNode(this.node); + document.body.removeChild(this.node); + }); + + it('renders an icon in the alert', function() { + expect($('#container i')).toHaveClass('fa-check-circle'); + }); + }); +}); + +describe('InfoAlert', function() { + describe('when withIcon is set to true', function() { + beforeEach(function() { + this.node = $('
').appendTo('body').get(0); + React.render( + alert body, + this.node + ); + }); + + afterEach(function() { + React.unmountComponentAtNode(this.node); + document.body.removeChild(this.node); + }); + + it('renders an icon in the alert', function() { + expect($('#container i')).toHaveClass('fa-info-circle'); + }); + }); +}); + +describe('WarningAlert', function() { + describe('when withIcon is set to true', function() { + beforeEach(function() { + this.node = $('
').appendTo('body').get(0); + React.render( + alert body, + this.node + ); + }); + + afterEach(function() { + React.unmountComponentAtNode(this.node); + document.body.removeChild(this.node); + }); + + it('renders an icon in the alert', function() { + expect($('#container i')).toHaveClass('fa-exclamation-triangle'); + }); + }); +}); + +describe('ErrorAlert', function() { + describe('when withIcon is set to true', function() { + beforeEach(function() { + this.node = $('
').appendTo('body').get(0); + React.render( + alert body, + this.node + ); + }); + + afterEach(function() { + React.unmountComponentAtNode(this.node); + document.body.removeChild(this.node); + }); + + it('renders an icon in the alert', function() { + expect($('#container i')).toHaveClass('fa-exclamation-triangle'); + }); + }); +}); \ No newline at end of file