From 017cd2a9575149bb87f382f441b9c960b6816c9d Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Wed, 17 May 2017 11:29:41 -0400 Subject: PLT-6406 Migrate new channel modal to be pure and use redux (#6416) * Migrate new channel modal to be pure and use redux * Add component tests --- webapp/components/new_channel_modal/index.js | 21 ++ .../new_channel_modal/new_channel_modal.jsx | 391 +++++++++++++++++++++ 2 files changed, 412 insertions(+) create mode 100644 webapp/components/new_channel_modal/index.js create mode 100644 webapp/components/new_channel_modal/new_channel_modal.jsx (limited to 'webapp/components/new_channel_modal') diff --git a/webapp/components/new_channel_modal/index.js b/webapp/components/new_channel_modal/index.js new file mode 100644 index 000000000..770084fbb --- /dev/null +++ b/webapp/components/new_channel_modal/index.js @@ -0,0 +1,21 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import {connect} from 'react-redux'; +import {getBool} from 'mattermost-redux/selectors/entities/preferences'; +import {isCurrentUserSystemAdmin} from 'mattermost-redux/selectors/entities/users'; +import {isCurrentUserCurrentTeamAdmin} from 'mattermost-redux/selectors/entities/teams'; +import {Preferences} from 'mattermost-redux/constants'; + +import NewChannelModal from './new_channel_modal.jsx'; + +function mapStateToProps(state, ownProps) { + return { + ...ownProps, + ctrlSend: getBool(state, Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter'), + isTeamAdmin: isCurrentUserCurrentTeamAdmin(state), + isSystemAdmin: isCurrentUserSystemAdmin(state) + }; +} + +export default connect(mapStateToProps)(NewChannelModal); diff --git a/webapp/components/new_channel_modal/new_channel_modal.jsx b/webapp/components/new_channel_modal/new_channel_modal.jsx new file mode 100644 index 000000000..48c2ddd15 --- /dev/null +++ b/webapp/components/new_channel_modal/new_channel_modal.jsx @@ -0,0 +1,391 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import $ from 'jquery'; +import ReactDOM from 'react-dom'; + +import {getShortenedURL} from 'utils/url.jsx'; +import * as UserAgent from 'utils/user_agent.jsx'; +import * as Utils from 'utils/utils.jsx'; +import * as ChannelUtils from 'utils/channel_utils.jsx'; +import Constants from 'utils/constants.jsx'; + +import {FormattedMessage} from 'react-intl'; + +import {Modal} from 'react-bootstrap'; + +import React from 'react'; +import PropTypes from 'prop-types'; + +export default class NewChannelModal extends React.PureComponent { + static propTypes = { + + /** + * Set whether to show the modal or not + */ + show: PropTypes.bool.isRequired, + + /** + * The type of channel to create, 'O' or 'P' + */ + channelType: PropTypes.string.isRequired, + + /** + * The data needed to create the channel + */ + channelData: PropTypes.object.isRequired, + + /** + * Set to force form submission on CTRL/CMD + ENTER instead of ENTER + */ + ctrlSend: PropTypes.bool, + + /** + * Set to show options available to team admins + */ + isTeamAdmin: PropTypes.bool, + + /** + * Set to show options available to system admins + */ + isSystemAdmin: PropTypes.bool, + + /** + * Server error from failed channel creation + */ + serverError: PropTypes.node, + + /** + * Function used to submit the channel + */ + onSubmitChannel: PropTypes.func.isRequired, + + /** + * Function to call when modal is dimissed + */ + onModalDismissed: PropTypes.func.isRequired, + + /** + * Function to call when modal has exited + */ + onModalExited: PropTypes.func, + + /** + * Function to call to switch channel type + */ + onTypeSwitched: PropTypes.func.isRequired, + + /** + * Function to call when edit URL button is pressed + */ + onChangeURLPressed: PropTypes.func.isRequired, + + /** + * Function to call when channel data is modified + */ + onDataChanged: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + + this.handleSubmit = this.handleSubmit.bind(this); + this.handleChange = this.handleChange.bind(this); + this.onEnterKeyDown = this.onEnterKeyDown.bind(this); + + this.state = { + displayNameError: '' + }; + } + + componentWillReceiveProps(nextProps) { + if (nextProps.show === true && this.props.show === false) { + this.setState({ + displayNameError: '' + }); + + document.addEventListener('keydown', this.onEnterKeyDown); + } else if (nextProps.show === false && this.props.show === true) { + document.removeEventListener('keydown', this.onEnterKeyDown); + } + } + + componentDidMount() { + // ??? + if (UserAgent.isInternetExplorer()) { + $('body').addClass('browser--ie'); + } + } + + onEnterKeyDown(e) { + if (this.props.ctrlSend && e.keyCode === Constants.KeyCodes.ENTER && e.ctrlKey) { + this.handleSubmit(e); + } else if (!this.props.ctrlSend && e.keyCode === Constants.KeyCodes.ENTER && !e.shiftKey && !e.altKey) { + this.handleSubmit(e); + } + } + + handleSubmit(e) { + e.preventDefault(); + + const displayName = ReactDOM.findDOMNode(this.refs.display_name).value.trim(); + if (displayName.length < Constants.MIN_CHANNELNAME_LENGTH) { + this.setState({displayNameError: true}); + return; + } + + this.props.onSubmitChannel(); + } + + handleChange() { + const newData = { + displayName: this.refs.display_name.value, + header: this.refs.channel_header.value, + purpose: this.refs.channel_purpose.value + }; + this.props.onDataChanged(newData); + } + + render() { + var displayNameError = null; + var serverError = null; + var displayNameClass = 'form-group'; + + if (this.state.displayNameError) { + displayNameError = ( +

+ + {this.state.displayNameError} +

+ ); + displayNameClass += ' has-error'; + } + + if (this.props.serverError) { + serverError =

{this.props.serverError}

; + } + + let createPublicChannelLink = ( + + + + ); + + let createPrivateChannelLink = ( + + + + ); + + const isAdmin = this.props.isTeamAdmin || this.props.isSystemAdmin; + + if (!ChannelUtils.showCreateOption(Constants.OPEN_CHANNEL, isAdmin, this.props.isSystemAdmin)) { + createPublicChannelLink = null; + } + + if (!ChannelUtils.showCreateOption(Constants.PRIVATE_CHANNEL, isAdmin, this.props.isSystemAdmin)) { + createPrivateChannelLink = null; + } + + var channelSwitchText = ''; + switch (this.props.channelType) { + case 'P': + channelSwitchText = ( +
+ + {createPublicChannelLink} +
+ ); + break; + case 'O': + channelSwitchText = ( +
+ + {createPrivateChannelLink} +
+ ); + break; + } + + const prettyTeamURL = getShortenedURL(); + + return ( + + + + + + + +
+ +
+ {channelSwitchText} +
+
+ +
+ + {displayNameError} +

+ {'URL: ' + prettyTeamURL + this.props.channelData.name + ' ('} + + + + {')'} +

+
+
+
+
+ + +
+
+