Skip to content

Commit b73e806

Browse files
author
eturner
committed
Merge in ConfirmDialog changes
2 parents c1da634 + c6a72bf commit b73e806

24 files changed

+760
-23
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Display complex data with our pie chart's drill in/out functionality, hover anim
2828

2929
A simple single page modal that renders in it's own DOM tree and operates outside the render cycles of an application.
3030

31+
#### [Confirm Dialog Component](./docs/confirmdialog.md)
32+
33+
A page level component that displays a confirmation dialog to the user with OK/Cancel buttons.
34+
3135
#### [Page Message Component](./docs/pagemessage.md)
3236

3337
A page level component that animates in and out for success, error, warning, info, and custom messages.

bower.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-components",
3-
"version": "0.3.5",
3+
"version": "0.4.0",
44
"main": "dist/react-components.css",
55
"keywords": [
66
"react-component"
@@ -23,7 +23,7 @@
2323
},
2424
"homepage": "https://github.com/dataminr/react-components",
2525
"devDependencies": {
26-
"expanded-react-test-utils": "0.1.0",
26+
"expanded-react-test-utils": "0.2.0",
2727
"jasmine-expect": "1.22.3",
2828
"react-router": "0.13.3"
2929
},

dist/react-components.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/confirmdialog.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# ConfirmDialog Component
2+
3+
### Usage
4+
5+
##### This component is not meant to be used directly. This leverages the portalMixins and is accessed by using the confirmDialog utility function.
6+
7+
```
8+
title
9+
type: string
10+
required: false
11+
description: The title to display in the modal dialog
12+
13+
subTitle
14+
type: string
15+
required: false
16+
description: The subtitle to display within the dialog
17+
18+
okHandler
19+
type: function
20+
required: false
21+
description: Function handler to call when ok button is clicked. After this function is run the dialog will automatically be closed unless this method explicitly returns false.
22+
23+
cancelHandler
24+
type: function
25+
required: false
26+
description: Function handler to call when cancel button is clicked. After this function is run the dialog will automatically be closed unless this method explicitly returns false.
27+
28+
options
29+
type: object
30+
required: false
31+
definition: Configuration overrides for button icons and labels.
32+
33+
okButtonText
34+
type: string
35+
default: 'OK'
36+
description: The label to display for the OK button
37+
38+
cancelButtonText
39+
type: string
40+
default: 'Cancel'
41+
description: The label to display for the cancel button
42+
43+
okIconClasses
44+
type: string
45+
default: 'okButton fa fa-check'
46+
description: The class names to use for the OK button icon. Set to null to remove icon.
47+
48+
cancelIconClasses
49+
type: string
50+
default: 'cancelButton fa fa-ban'
51+
description: The class names to use for the cancel button icon. Set to null to remove icon.
52+
```
53+
54+
#### Example Usage
55+
56+
```javascript
57+
var Utils = require('drc/utils/Utils');
58+
59+
Utils.confirmDialog('Confirm Delete', 'Are you sure you want to delete this?', function(){
60+
//handle OK Click
61+
});
62+
63+
Utils.confirmDialog('Are you sure you want to delete this?', null, function(){
64+
//handle OK Click
65+
}, function(){
66+
//handle cancel Click
67+
});
68+
69+
Utils.confirmDialog('Are you sure you want to delete this?', null, function(){
70+
//handle OK Click
71+
}, function(){
72+
//handle cancel Click
73+
}, {
74+
okButtonText: 'Confirm',
75+
cancelButtonText: 'Nope',
76+
okIconClasses: 'okButton fa fa-check-circle',
77+
cancelIconClasses: null //Clear cancel icon
78+
});
79+
```

docs/modal.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ backgroundClickToClose
1919
default: true
2020
description: Used to control whether or not a click outside the modal will cause it to close
2121
22+
showCloseIcon
23+
type: boolean
24+
required: false
25+
default: true
26+
description: Used to control whether the modal should show a close text and icon in the top right. Also disables using the escape key to close the modal.
27+
2228
iconClasses
2329
type: object
2430
required: false

grunt/test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ var jasmineConfig = {
1111
compiledDir: 'src/compiled',
1212
paths: {
1313
'drc/lib/EventEmitter': '../../src/compiled/lib/EventEmitter',
14-
ExpandedTestUtils: '../../bower_components/expanded-react-test-utils/dist/ExpandedTestUtils.min',
14+
ExpandedTestUtils: '../../bower_components/expanded-react-test-utils/dist/ExpandedTestUtils',
1515
RequestHandler: '../../src/compiled/utils/RequestHandler'
1616
}
1717
};
@@ -54,7 +54,7 @@ module.exports = function(grunt, options) {
5454
lines: 98,
5555
statements: 98,
5656
branches: 98,
57-
functions: 98
57+
functions: 97
5858
} : {},
5959
template: require('grunt-template-jasmine-requirejs'),
6060
templateOptions: {
@@ -93,6 +93,7 @@ module.exports = function(grunt, options) {
9393
target: [
9494
'src/**/*.js',
9595
'!src/compiled/**/*.js',
96+
'!src/js/examples/*.js',
9697
'!src/js/tests/*.js',
9798
'!src/js/lib/EventEmitter.js',
9899
'!src/**/*.test.js'

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-components",
3-
"version": "0.3.5",
3+
"version": "0.4.0",
44
"keywords": [
55
"react-component"
66
],

src/compiled/examples/App.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ define(function(require) {
144144
)
145145
);
146146
break;
147+
case 'confirmDialog':
148+
componentSet = (
149+
React.createElement("div", {className: "component modal"},
150+
React.createElement("button", {type: "button", onClick: this.showConfirmDialog}, "Confirm Dialog")
151+
)
152+
);
153+
break;
147154
case 'pageMessage':
148155
componentSet = (
149156
React.createElement("div", {className: "component"},
@@ -201,7 +208,9 @@ define(function(require) {
201208
React.createElement("div", {className: "sidebar"},
202209
React.createElement("ul", {className: "nav no-select"},
203210
React.createElement("li", {className: this.state.selectedComponentSet === 'modal' ? 'active' : null,
204-
onClick: this.handleLinkClick.bind(this, 'modal')}, "Modal"),
211+
onClick: this.handleLinkClick.bind(this, 'modal')}, "Modal"),
212+
React.createElement("li", {className: this.state.selectedComponentSet === 'confirmDialog' ? 'active' : null,
213+
onClick: this.handleLinkClick.bind(this, 'confirmDialog')}, "Confirm Dialog"),
205214
React.createElement("li", {className: this.state.selectedComponentSet === 'pageMessage' ? 'active' : null,
206215
onClick: this.handleLinkClick.bind(this, 'pageMessage')}, "Page Message"),
207216
React.createElement("li", {className: this.state.selectedComponentSet === 'piechart' ? 'active' : null,
@@ -231,6 +240,10 @@ define(function(require) {
231240
);
232241
},
233242

243+
showConfirmDialog: function(){
244+
Utils.confirmDialog('Confirm Delete', 'Are you sure you want to delete this?');
245+
},
246+
234247
handleBulkActionClick: function() {
235248
/* eslint-disable no-alert */
236249
alert('You have selected the following items from the table:\n\n' + TableStore.getSelectedItems('tableId'));

src/compiled/modal/Modal.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ define(function(require) {
1515
closeModalCallback: React.PropTypes.func,
1616
backgroundClickToClose: React.PropTypes.bool,
1717
iconClasses: React.PropTypes.object,
18+
showCloseIcon: React.PropTypes.bool,
1819
title: React.PropTypes.string
1920
},
2021

2122
getDefaultProps: function() {
2223
return {
23-
backgroundClickToClose: true
24+
backgroundClickToClose: true,
25+
showCloseIcon: true
2426
};
2527
},
2628

@@ -38,15 +40,29 @@ define(function(require) {
3840
this.refs.content.getDOMNode().focus();
3941
},
4042

43+
/**
44+
* Gets markup to display close icon in upper right corner. Only returns markup if
45+
* the showCloseIcon prop is set.
46+
* @return {null|ReactElement} Icon markup or null
47+
*/
48+
getCloseIconMarkup: function(){
49+
if(!this.props.showCloseIcon){
50+
return null;
51+
}
52+
return (
53+
React.createElement("span", {className: "close", onClick: this.closeModalHandler},
54+
React.createElement("span", {className: "close-text"}, "esc to close"), " | ", React.createElement("i", {className: this.iconClasses.close})
55+
)
56+
);
57+
},
58+
4159
render: function() {
4260
return (
4361
React.createElement("div", {onClick: this.backgroundClickHandler, id: "modal-container", 'data-clickcatcher': "true"},
4462
React.createElement("div", {ref: "content", className: "content", tabIndex: "-1", onKeyDown: this.keyDownHandler},
4563
React.createElement("div", {className: "header"},
4664
React.createElement("span", {className: "title"}, this.props.title),
47-
React.createElement("span", {className: "close", onClick: this.closeModalHandler},
48-
React.createElement("span", {className: "close-text"}, "esc to close"), " | ", React.createElement("i", {className: this.iconClasses.close})
49-
)
65+
this.getCloseIconMarkup()
5066
),
5167
React.createElement("div", {className: "body"},
5268
this.props.children
@@ -57,12 +73,12 @@ define(function(require) {
5773
},
5874

5975
/**
60-
* If the key pressed is the escape key, the close modal handler will be called.
76+
* If the key pressed is the escape key and the close icon is being shown, the close modal handler will be called.
6177
* @param {object} e - The simulated React event.
6278
*/
6379
keyDownHandler: function(e) {
6480
// escape key pressed
65-
if (e.keyCode === 27) {
81+
if (e.keyCode === 27 && this.props.showCloseIcon) {
6682
this.closeModalHandler(e);
6783
}
6884
},

src/compiled/modal/tests/Modal.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ define(function(require) {
5353
});
5454
});
5555

56+
describe('getCloseIconMarkup', function(){
57+
it('returns null when not showing icon', function(){
58+
modal = React.render(React.createElement(Modal, React.__spread({}, props, {showCloseIcon: false}), React.createElement("span", {id: "text"}, "Text")), node);
59+
60+
expect(modal.getCloseIconMarkup()).toBeNull();
61+
});
62+
63+
it('returns ReactElement for close content', function(){
64+
modal = React.render(React.createElement(Modal, React.__spread({}, props), React.createElement("span", {id: "text"}, "Text")), node);
65+
66+
expect(modal.getCloseIconMarkup()).toBeObject();
67+
});
68+
});
69+
5670
describe('keyDownHandler function', function() {
5771
it('should close the modal if the escape key is pressed', function() {
5872
spyOn(modal.props, 'closeModalCallback');
@@ -65,6 +79,16 @@ define(function(require) {
6579

6680
expect(modal.props.closeModalCallback.calls.count()).toEqual(1);
6781
});
82+
83+
it('should do nothing if not showing close icon', function(){
84+
modal = React.render(React.createElement(Modal, React.__spread({}, props, {showCloseIcon: false}), React.createElement("span", {id: "text"}, "Text")), node);
85+
86+
spyOn(modal, 'closeModalHandler');
87+
88+
TestUtils.Simulate.keyDown(document.activeElement, {keyCode: 27});
89+
90+
expect(modal.closeModalHandler).not.toHaveBeenCalled();
91+
});
6892
});
6993

7094
describe('backgroundClickHandler function', function() {

src/compiled/utils/ConfirmDialog.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
define(function(require) {
2+
'use strict';
3+
4+
var PortalMixins = require('drc/mixins/PortalMixins');
5+
var React = require('react');
6+
var _ = require('lodash');
7+
8+
var ConfirmDialog = React.createClass({displayName: 'ConfirmDialog',
9+
propTypes: {
10+
message: React.PropTypes.string,
11+
okButtonText: React.PropTypes.string,
12+
cancelButtonText: React.PropTypes.string,
13+
okButtonClickHandler: React.PropTypes.func,
14+
cancelButtonClickHandler: React.PropTypes.func,
15+
okIconClasses: React.PropTypes.string,
16+
cancelIconClasses: React.PropTypes.string
17+
},
18+
19+
getDefaultProps: function() {
20+
return {
21+
okButtonText: 'OK',
22+
cancelButtonText: 'Cancel',
23+
okButtonClickHandler: function(){},
24+
cancelButtonClickHandler: function(){},
25+
okIconClasses: 'okButton fa fa-check',
26+
cancelIconClasses: 'cancelButton fa fa-ban'
27+
};
28+
},
29+
30+
/**
31+
* Closes the portal, but using async defer to delay the unmounting that happens. It does
32+
* this because of this bug - https://github.com/facebook/react/issues/3298
33+
*/
34+
closePortal: function(){
35+
_.defer(function(){
36+
PortalMixins.closePortal();
37+
});
38+
},
39+
40+
/**
41+
* Handler for when OK button is clicked. Invokes the handler prop and if it doesn't
42+
* return false, closes the portal.
43+
*/
44+
handleOkClick: function(){
45+
if(this.props.okButtonClickHandler() !== false){
46+
this.closePortal();
47+
}
48+
},
49+
50+
/**
51+
* Handler for when cancel button is clicked. Invokes the handler prop and if it doesn't
52+
* return false, closes the portal.
53+
*/
54+
handleCancelClick: function(){
55+
if(this.props.cancelButtonClickHandler() !== false){
56+
this.closePortal();
57+
}
58+
},
59+
60+
/**
61+
* Gets markup for inner content of OK button with optional icon and
62+
* button text.
63+
* @param {String} icon Classes for icon. No icon will be added if not set
64+
* @param {String} text Text label for button
65+
* @return {Array} Markup for button content
66+
*/
67+
getButtonText: function(icon, text){
68+
var markup = [];
69+
if(icon){
70+
markup.push(React.createElement("i", {className: icon, key: "icon"}));
71+
}
72+
markup.push(React.createElement("span", {key: "label"}, text));
73+
return markup;
74+
},
75+
76+
render: function() {
77+
return (
78+
React.createElement("div", {className: "confirm-dialog"},
79+
React.createElement("div", {className: "message"},
80+
this.props.message
81+
),
82+
React.createElement("div", {className: "confirm-buttons"},
83+
React.createElement("button", {className: "button", onClick: this.handleOkClick}, this.getButtonText(this.props.okIconClasses, this.props.okButtonText)),
84+
React.createElement("button", {className: "button", onClick: this.handleCancelClick}, this.getButtonText(this.props.cancelIconClasses, this.props.cancelButtonText))
85+
)
86+
)
87+
);
88+
}
89+
});
90+
91+
return ConfirmDialog;
92+
});

0 commit comments

Comments
 (0)