Merge pull request #145 from ralder/fix-tabIndex-channel_invite_modal

[webui] add missing tabIndex for channel members modal
This commit is contained in:
Joram Wilander
2015-07-09 09:37:39 -04:00
2 changed files with 66 additions and 93 deletions

View File

@@ -10,111 +10,95 @@ var AsyncClient = require('../utils/async_client.jsx');
function getStateFromStores() { function getStateFromStores() {
var users = UserStore.getActiveOnlyProfiles(); var users = UserStore.getActiveOnlyProfiles();
var member_list = ChannelStore.getCurrentExtraInfo().members; var memberIds = ChannelStore.getCurrentExtraInfo().members.map(function(user) { return user.id; });
var nonmember_list = []; var nonmembers = [];
for (var id in users) { for (var id in users) {
var found = false; if (memberIds.indexOf(id) == -1) {
for (var i = 0; i < member_list.length; i++) { nonmembers.push(users[id]);
if (member_list[i].id === id) {
found = true;
break;
}
}
if (!found) {
nonmember_list.push(users[id]);
} }
} }
member_list.sort(function(a,b) { nonmembers.sort(function(a,b) {
if (a.username < b.username) return -1; return a.username.localeCompare(b.username);
if (a.username > b.username) return 1;
return 0;
});
nonmember_list.sort(function(a,b) {
if (a.username < b.username) return -1;
if (a.username > b.username) return 1;
return 0;
}); });
var channel_name = ChannelStore.getCurrent() ? ChannelStore.getCurrent().display_name : ""; var channel_name = ChannelStore.getCurrent() ? ChannelStore.getCurrent().display_name : "";
return { return {
nonmember_list: nonmember_list, nonmembers: nonmembers,
member_list: member_list, memberIds: memberIds,
channel_name: channel_name channel_name: channel_name
}; };
} }
module.exports = React.createClass({ module.exports = React.createClass({
displayName: "ChannelInviteModal",
isShown: false,
getInitialState: function() {
return {};
},
componentDidMount: function() { componentDidMount: function() {
$(React.findDOMNode(this))
.on('hidden.bs.modal', this._onHide)
.on('show.bs.modal', this._onShow);
},
_onShow: function() {
ChannelStore.addExtraInfoChangeListener(this._onChange); ChannelStore.addExtraInfoChangeListener(this._onChange);
ChannelStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange);
this.isShown = true;
var self = this; this._onChange();
$(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) {
self.setState({ render_members: false });
});
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
self.setState({ render_members: true });
});
}, },
componentWillUnmount: function() {
_onHide: function() {
ChannelStore.removeExtraInfoChangeListener(this._onChange); ChannelStore.removeExtraInfoChangeListener(this._onChange);
ChannelStore.removeChangeListener(this._onChange); ChannelStore.removeChangeListener(this._onChange);
this.isShown = false;
}, },
_onChange: function() { _onChange: function() {
var new_state = getStateFromStores(); this.setState(getStateFromStores());
if (!utils.areStatesEqual(this.state, new_state)) {
this.setState(new_state);
}
}, },
handleInvite: function(user_id) { handleInvite: function(user_id) {
// Make sure the user isn't already a member of the channel // Make sure the user isn't already a member of the channel
var member_list = this.state.member_list; if (this.state.memberIds.indexOf(user_id) > -1) {
for (var i = 0; i < member_list; i++) { return;
if (member_list[i].id === user_id) {
return;
}
} }
var data = {}; var data = {};
data['user_id'] = user_id; data.user_id = user_id;
client.addChannelMember(ChannelStore.getCurrentId(), data, client.addChannelMember(ChannelStore.getCurrentId(), data,
function(data) { function() {
var nonmember_list = this.state.nonmember_list; var nonmembers = this.state.nonmembers;
var new_member; var memberIds = this.state.memberIds;
for (var i = 0; i < nonmember_list.length; i++) {
if (user_id === nonmember_list[i].id) { for (var i = 0; i < nonmembers.length; i++) {
nonmember_list[i].invited = true; if (user_id === nonmembers[i].id) {
new_member = nonmember_list[i]; nonmembers[i].invited = true;
memberIds.push(user_id);
break; break;
} }
} }
if (new_member) { this.setState({ invite_error: null, memberIds: memberIds, nonmembers: nonmembers });
member_list.push(new_member);
member_list.sort(function(a,b) {
if (a.username < b.username) return -1;
if (a.username > b.username) return 1;
return 0;
});
}
this.setState({ invite_error: null, member_list: member_list, nonmember_list: nonmember_list });
AsyncClient.getChannelExtraInfo(true); AsyncClient.getChannelExtraInfo(true);
}.bind(this), }.bind(this),
function(err) { function(err) {
this.setState({ invite_error: err.message }); this.setState({ invite_error: err.message });
}.bind(this) }.bind(this)
); );
}, },
getInitialState: function() {
return getStateFromStores(); shouldComponentUpdate: function(nextProps, nextState) {
return this.isShown && !utils.areStatesEqual(this.state, nextState);
}, },
render: function() { render: function() {
var invite_error = this.state.invite_error ? <label className='has-error control-label'>{this.state.invite_error}</label> : null; var invite_error = this.state.invite_error ? <label className='has-error control-label'>{this.state.invite_error}</label> : null;
@@ -125,22 +109,19 @@ module.exports = React.createClass({
} }
return ( return (
<div className="modal fade" ref="modal" id="channel_invite" role="dialog" aria-hidden="true"> <div className="modal fade" id="channel_invite" tabIndex="-1" role="dialog" aria-hidden="true">
<div className="modal-dialog"> <div className="modal-dialog" role="document">
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 className="modal-title">Add New Members to {this.state.channel_name}</h4> <h4 className="modal-title">Add New Members to {this.state.channel_name}</h4>
</div> </div>
<div className="modal-body"> <div className="modal-body">
{ invite_error } { invite_error }
{ this.state.render_members ?
<MemberList <MemberList
memberList={this.state.nonmember_list} memberList={this.state.nonmembers}
isAdmin={isAdmin} isAdmin={isAdmin}
handleInvite={this.handleInvite} handleInvite={this.handleInvite} />
/>
: "" }
</div> </div>
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button> <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
@@ -151,7 +132,3 @@ module.exports = React.createClass({
); );
} }
}); });

View File

@@ -5,13 +5,17 @@ var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
module.exports = React.createClass({ module.exports = React.createClass({
handleInvite: function() { displayName: 'MemberListItem',
handleInvite: function(e) {
e.preventDefault();
this.props.handleInvite(this.props.member.id); this.props.handleInvite(this.props.member.id);
}, },
handleRemove: function() { handleRemove: function(e) {
e.preventDefault();
this.props.handleRemove(this.props.member.id); this.props.handleRemove(this.props.member.id);
}, },
handleMakeAdmin: function() { handleMakeAdmin: function(e) {
e.preventDefault();
this.props.handleMakeAdmin(this.props.member.id); this.props.handleMakeAdmin(this.props.member.id);
}, },
render: function() { render: function() {
@@ -20,12 +24,6 @@ module.exports = React.createClass({
var isAdmin = this.props.isAdmin; var isAdmin = this.props.isAdmin;
var isMemberAdmin = member.roles.indexOf("admin") > -1; var isMemberAdmin = member.roles.indexOf("admin") > -1;
if (member.roles === '') {
member.roles = 'Member';
} else {
member.roles = member.roles.charAt(0).toUpperCase() + member.roles.slice(1);
}
var invite; var invite;
if (member.invited && this.props.handleInvite) { if (member.invited && this.props.handleInvite) {
invite = <span className="member-role">Added</span>; invite = <span className="member-role">Added</span>;
@@ -36,30 +34,28 @@ module.exports = React.createClass({
invite = ( invite = (
<div className="dropdown member-drop"> <div className="dropdown member-drop">
<a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true"> <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
<span>{member.roles} </span> <span className="text-capitalize">{member.roles || 'Member'} </span>
<span className="caret"></span> <span className="caret"></span>
</a> </a>
<ul className="dropdown-menu member-menu" role="menu" aria-labelledby="channel_header_dropdown"> <ul className="dropdown-menu member-menu" role="menu" aria-labelledby="channel_header_dropdown">
{ this.props.handleMakeAdmin ? { this.props.handleMakeAdmin ?
<li role="presentation"><a role="menuitem" onClick={self.handleMakeAdmin}>Make Admin</a></li> <li role="presentation"><a href="" role="menuitem" onClick={self.handleMakeAdmin}>Make Admin</a></li>
: "" } : null }
{ this.props.handleRemove ? { this.props.handleRemove ?
<li role="presentation"><a role="menuitem" onClick={self.handleRemove}>Remove Member</a></li> <li role="presentation"><a href="" role="menuitem" onClick={self.handleRemove}>Remove Member</a></li>
: "" } : null }
</ul> </ul>
</div> </div>
); );
} else { } else {
invite = <div className="member-drop"><span>{member.roles} </span><span className="caret invisible"></span></div>; invite = <div className="member-role text-capitalize" style={{marginRight: 15}}>{member.roles || 'Member'}</div>;
} }
var email = member.email.length > 0 ? member.email : "";
return ( return (
<div className="row member-div"> <div className="row member-div">
<img className="post-profile-img pull-left" src={"/api/v1/users/" + member.id + "/image"} height="36" width="36" /> <img className="post-profile-img pull-left" src={"/api/v1/users/" + member.id + "/image"} height="36" width="36" />
<span className="member-name">{member.username}</span> <span className="member-name">{member.username}</span>
<span className="member-email">{email}</span> <span className="member-email">{member.email}</span>
{ invite } { invite }
</div> </div>
); );