diff options
Diffstat (limited to 'webapp')
-rw-r--r-- | webapp/components/create_team/components/display_name.jsx | 2 | ||||
-rw-r--r-- | webapp/components/select_team/components/select_team_item.jsx | 24 | ||||
-rw-r--r-- | webapp/components/sidebar.jsx | 1 | ||||
-rw-r--r-- | webapp/components/sidebar_header.jsx | 31 | ||||
-rw-r--r-- | webapp/components/team_general_tab.jsx | 126 | ||||
-rw-r--r-- | webapp/i18n/en.json | 5 | ||||
-rw-r--r-- | webapp/sass/routes/_signup.scss | 12 | ||||
-rw-r--r-- | webapp/tests/client_team.test.jsx | 18 | ||||
-rw-r--r-- | webapp/utils/constants.jsx | 1 |
9 files changed, 208 insertions, 12 deletions
diff --git a/webapp/components/create_team/components/display_name.jsx b/webapp/components/create_team/components/display_name.jsx index a557a48c5..67805a040 100644 --- a/webapp/components/create_team/components/display_name.jsx +++ b/webapp/components/create_team/components/display_name.jsx @@ -38,7 +38,7 @@ export default class TeamSignupDisplayNamePage extends React.Component { this.setState({nameError: ( <FormattedMessage id='create_team.display_name.charLength' - defaultMessage='Name must be {min} or more characters up to a maximum of {max}' + defaultMessage='Name must be {min} or more characters up to a maximum of {max}. You can add a longer team description later.' values={{ min: Constants.MIN_TEAMNAME_LENGTH, max: Constants.MAX_TEAMNAME_LENGTH diff --git a/webapp/components/select_team/components/select_team_item.jsx b/webapp/components/select_team/components/select_team_item.jsx index b29c4b1c4..825afdd69 100644 --- a/webapp/components/select_team/components/select_team_item.jsx +++ b/webapp/components/select_team/components/select_team_item.jsx @@ -4,6 +4,7 @@ import React from 'react'; import {Link} from 'react-router/es6'; +import {Tooltip, OverlayTrigger} from 'react-bootstrap'; export default class SelectTeamItem extends React.Component { static propTypes = { @@ -35,8 +36,31 @@ export default class SelectTeamItem extends React.Component { ); } + var descriptionTooltip = ''; + var showDescriptionTooltip = ''; + if (this.props.team.description) { + descriptionTooltip = ( + <Tooltip id='team-description__tooltip'> + {this.props.team.description} + </Tooltip> + ); + + showDescriptionTooltip = ( + <OverlayTrigger + trigger={['hover', 'focus', 'click']} + delayShow={1000} + placement='left' + overlay={descriptionTooltip} + ref='descriptionOverlay' + > + <span className='fa fa-info-circle signup-team__icon'/> + </OverlayTrigger> + ); + } + return ( <div className='signup-team-dir'> + {showDescriptionTooltip} <Link to={this.props.url} onClick={this.handleTeamClick} diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx index 5c6645833..85d39c9e7 100644 --- a/webapp/components/sidebar.jsx +++ b/webapp/components/sidebar.jsx @@ -736,6 +736,7 @@ export default class Sidebar extends React.Component { <SidebarHeader teamDisplayName={this.state.currentTeam.display_name} + teamDescription={this.state.currentTeam.description} teamName={this.state.currentTeam.name} teamType={this.state.currentTeam.type} currentUser={this.state.currentUser} diff --git a/webapp/components/sidebar_header.jsx b/webapp/components/sidebar_header.jsx index cd41cfb84..a5fbd2659 100644 --- a/webapp/components/sidebar_header.jsx +++ b/webapp/components/sidebar_header.jsx @@ -69,6 +69,25 @@ export default class SidebarHeader extends React.Component { tutorialTip = createMenuTip(this.toggleDropdown); } + let teamNameWithToolTip = null; + if (this.props.teamDescription === '') { + teamNameWithToolTip = ( + <div className='team__name'>{this.props.teamDisplayName}</div> + ); + } else { + teamNameWithToolTip = ( + <OverlayTrigger + trigger={['hover', 'focus']} + delayShow={1000} + placement='bottom' + overlay={<Tooltip id='team-name__tooltip'>{this.props.teamDescription}</Tooltip>} + ref='descriptionOverlay' + > + <div className='team__name'>{this.props.teamDisplayName}</div> + </OverlayTrigger> + ); + } + return ( <div className='team__header theme'> {tutorialTip} @@ -79,15 +98,7 @@ export default class SidebarHeader extends React.Component { {profilePicture} <div className='header__info'> <div className='user__name'>{'@' + me.username}</div> - <OverlayTrigger - trigger={['hover', 'focus']} - delayShow={1000} - placement='bottom' - overlay={<Tooltip id='team-name__tooltip'>{this.props.teamDisplayName}</Tooltip>} - ref='descriptionOverlay' - > - <div className='team__name'>{this.props.teamDisplayName}</div> - </OverlayTrigger> + {teamNameWithToolTip} </div> </a> <SidebarHeaderDropdown @@ -104,10 +115,12 @@ export default class SidebarHeader extends React.Component { SidebarHeader.defaultProps = { teamDisplayName: '', + teamDescription: '', teamType: '' }; SidebarHeader.propTypes = { teamDisplayName: React.PropTypes.string, + teamDescription: React.PropTypes.string, teamName: React.PropTypes.string, teamType: React.PropTypes.string, currentUser: React.PropTypes.object diff --git a/webapp/components/team_general_tab.jsx b/webapp/components/team_general_tab.jsx index a5281d238..955a71ac5 100644 --- a/webapp/components/team_general_tab.jsx +++ b/webapp/components/team_general_tab.jsx @@ -55,6 +55,10 @@ const holders = defineMessages({ teamNameInfo: { id: 'general_tab.teamNameInfo', defaultMessage: 'Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.' + }, + teamDescriptionInfo: { + id: 'general_tab.teamDescriptionInfo', + defaultMessage: 'Team description provides additional information to help users select the right team. Maximum of 50 characters.' } }); @@ -68,9 +72,12 @@ class GeneralTab extends React.Component { this.handleNameSubmit = this.handleNameSubmit.bind(this); this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this); this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this); + this.handleDescriptionSubmit = this.handleDescriptionSubmit.bind(this); this.handleClose = this.handleClose.bind(this); this.onUpdateNameSection = this.onUpdateNameSection.bind(this); this.updateName = this.updateName.bind(this); + this.updateDescription = this.updateDescription.bind(this); + this.onUpdateDescriptionSection = this.onUpdateDescriptionSection.bind(this); this.onUpdateInviteIdSection = this.onUpdateInviteIdSection.bind(this); this.updateInviteId = this.updateInviteId.bind(this); this.onUpdateOpenInviteSection = this.onUpdateOpenInviteSection.bind(this); @@ -95,6 +102,7 @@ class GeneralTab extends React.Component { name: team.display_name, invite_id: team.invite_id, allow_open_invite: team.allow_open_invite, + description: team.description, serverError: '', clientError: '' }; @@ -103,6 +111,7 @@ class GeneralTab extends React.Component { componentWillReceiveProps(nextProps) { this.setState({ name: nextProps.team.display_name, + description: nextProps.team.description, invite_id: nextProps.team.invite_id, allow_open_invite: nextProps.team.allow_open_invite }); @@ -215,6 +224,40 @@ class GeneralTab extends React.Component { this.updateSection(''); } + handleDescriptionSubmit(e) { + e.preventDefault(); + + var state = {serverError: '', clientError: ''}; + let valid = true; + + const {formatMessage} = this.props.intl; + const description = this.state.description.trim(); + if (description === this.props.team.description) { + state.clientError = formatMessage(holders.chooseName); + valid = false; + } else { + state.clientError = ''; + } + + this.setState(state); + + if (!valid) { + return; + } + + var data = this.props.team; + data.description = this.state.description; + updateTeam(data, + () => { + this.updateSection(''); + }, + (err) => { + state.serverError = err.message; + this.setState(state); + } + ); + } + componentDidMount() { $('#team_settings').on('hidden.bs.modal', this.handleClose); } @@ -232,6 +275,15 @@ class GeneralTab extends React.Component { } } + onUpdateDescriptionSection(e) { + e.preventDefault(); + if (this.props.activeSection === 'description') { + this.updateSection(''); + } else { + this.updateSection('description'); + } + } + onUpdateInviteIdSection(e) { e.preventDefault(); if (this.props.activeSection === 'invite_id') { @@ -254,6 +306,10 @@ class GeneralTab extends React.Component { this.setState({name: e.target.value}); } + updateDescription(e) { + this.setState({description: e.target.value}); + } + updateInviteId(e) { this.setState({invite_id: e.target.value}); } @@ -457,6 +513,74 @@ class GeneralTab extends React.Component { ); } + let descriptionSection; + + if (this.props.activeSection === 'description') { + const inputs = []; + + let teamDescriptionLabel = ( + <FormattedMessage + id='general_tab.teamDescription' + defaultMessage='Team Description' + /> + ); + if (Utils.isMobile()) { + teamDescriptionLabel = ''; + } + + inputs.push( + <div + key='teamDescriptionSetting' + className='form-group' + > + <label className='col-sm-5 control-label'>{teamDescriptionLabel}</label> + <div className='col-sm-7'> + <input + className='form-control' + type='text' + maxLength={Constants.MAX_TEAMDESCRIPTION_LENGTH.toString()} + onChange={this.updateDescription} + value={this.state.description} + /> + </div> + </div> + ); + + const descriptionExtraInfo = <span>{formatMessage(holders.teamDescriptionInfo)}</span>; + + descriptionSection = ( + <SettingItemMax + title={formatMessage({id: 'general_tab.teamDescription'})} + inputs={inputs} + submit={this.handleDescriptionSubmit} + server_error={serverError} + client_error={clientError} + updateSection={this.onUpdateDescriptionSection} + extraInfo={descriptionExtraInfo} + /> + ); + } else { + let describemsg = ''; + if (this.state.description) { + describemsg = this.state.description; + } else { + describemsg = ( + <FormattedMessage + id='general_tab.emptyDescription' + defaultMessage="Click 'Edit' to add a team description." + /> + ); + } + + descriptionSection = ( + <SettingItemMin + title={formatMessage({id: 'general_tab.teamDescription'})} + describe={describemsg} + updateSection={this.onUpdateDescriptionSection} + /> + ); + } + return ( <div> <div className='modal-header'> @@ -496,6 +620,8 @@ class GeneralTab extends React.Component { <div className='divider-dark first'/> {nameSection} <div className='divider-light'/> + {descriptionSection} + <div className='divider-light'/> {openInviteSection} <div className='divider-light'/> {inviteSection} diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index 3b87d76c2..1334c7358 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -1145,7 +1145,7 @@ "create_post.write": "Write a message...", "create_team.agreement": "By proceeding to create your account and use {siteName}, you agree to our <a href={TermsOfServiceLink}>Terms of Service</a> and <a href={PrivacyPolicyLink}>Privacy Policy</a>. If you do not agree, you cannot use {siteName}.", "create_team.display_name.back": "Back to previous step", - "create_team.display_name.charLength": "Name must be 2 or more characters up to a maximum of 15", + "create_team.display_name.charLength": "Name must be {min} or more characters up to a maximum of {max}. You can add a longer team description later.", "create_team.display_name.nameHelp": "Name your team in any language. Your team name shows in menus and headings.", "create_team.display_name.next": "Next", "create_team.display_name.required": "This field is required", @@ -1272,8 +1272,11 @@ "general_tab.required": "This field is required", "general_tab.teamName": "Team Name", "general_tab.teamNameInfo": "Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.", + "general_tab.teamDescription": "Team Description", + "general_tab.teamDescriptionInfo": "Team description provides additional information to help users select the right team. Maximum of 50 characters.", "general_tab.title": "General Settings", "general_tab.yes": "Yes", + "general_tab.emptyDescription": "Click 'Edit' to add a team description.", "get_app.alreadyHaveIt": "Already have it?", "get_app.androidAppName": "Mattermost for Android", "get_app.androidHeader": "Mattermost works best if you switch to our Android app", diff --git a/webapp/sass/routes/_signup.scss b/webapp/sass/routes/_signup.scss index 30e80cccb..cbf6f1571 100644 --- a/webapp/sass/routes/_signup.scss +++ b/webapp/sass/routes/_signup.scss @@ -481,7 +481,8 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - width: 90%; + width: calc(100% - 50px); + } .signup-team__icon { @@ -497,6 +498,15 @@ right: -2px; top: 16px; } + + &.fa-info-circle { + float: left; + line-height: 1.5em; + margin-right: .3em; + padding-left: .5em; + font-size: 1.5em; + top: 11px; + } } } diff --git a/webapp/tests/client_team.test.jsx b/webapp/tests/client_team.test.jsx index ab2e625bf..13b2802d2 100644 --- a/webapp/tests/client_team.test.jsx +++ b/webapp/tests/client_team.test.jsx @@ -237,6 +237,24 @@ describe('Client.Team', function() { }); }); + it('updateTeamDescription', function(done) { + TestHelper.initBasic(() => { + var team = TestHelper.basicTeam(); + team.description = 'test_updated'; + + TestHelper.basicClient().updateTeam( + team, + function(data) { + assert.equal(data.description, 'test_updated'); + done(); + }, + function(err) { + done(new Error(err.message)); + } + ); + }); + }); + it('addUserToTeam', function(done) { TestHelper.initBasic(() => { TestHelper.basicClient().createUser( diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index f94461ec7..2c881e024 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -829,6 +829,7 @@ export const Constants = { DEFAULT_MAX_CHANNELS_PER_TEAM: 2000, DEFAULT_MAX_NOTIFICATIONS_PER_CHANNEL: 1000, MAX_TEAMNAME_LENGTH: 15, + MAX_TEAMDESCRIPTION_LENGTH: 50, MIN_USERNAME_LENGTH: 3, MAX_USERNAME_LENGTH: 22, MAX_NICKNAME_LENGTH: 22, |