Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
MM-23816: Group Mentions: Add ability to rename group names (#5362)
Browse files Browse the repository at this point in the history
* MM-23816: Group Mentions: Add ability to rename group names

Co-authored-by: Catalin Tomai <[email protected]>
  • Loading branch information
catalintomai and catalintomai committed May 12, 2020
1 parent e5637e5 commit 44f496d
Show file tree
Hide file tree
Showing 13 changed files with 251 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ exports[`components/admin_console/group_settings/group_details/GroupDetails shou
<GroupProfileAndSettings
allowReference={false}
displayname="Group"
name="Group"
mentionname="Group"
onChange={[Function]}
onToggle={[Function]}
/>
<AdminPanel
Expand Down Expand Up @@ -167,6 +168,13 @@ exports[`components/admin_console/group_settings/group_details/GroupDetails shou
</AdminPanel>
</div>
</div>
<SaveChangesPanel
cancelLink="/admin_console/user_management/groups"
onClick={[Function]}
saveNeeded={false}
saving={false}
serverError={null}
/>
</div>
`;

Expand Down Expand Up @@ -211,7 +219,8 @@ exports[`components/admin_console/group_settings/group_details/GroupDetails shou
<GroupProfileAndSettings
allowReference={false}
displayname="Group"
name="Group"
mentionname="Group"
onChange={[Function]}
onToggle={[Function]}
/>
<AdminPanel
Expand Down Expand Up @@ -336,6 +345,13 @@ exports[`components/admin_console/group_settings/group_details/GroupDetails shou
</AdminPanel>
</div>
</div>
<SaveChangesPanel
cancelLink="/admin_console/user_management/groups"
onClick={[Function]}
saveNeeded={false}
saving={false}
serverError={null}
/>
</div>
`;

Expand Down Expand Up @@ -380,7 +396,8 @@ exports[`components/admin_console/group_settings/group_details/GroupDetails shou
<GroupProfileAndSettings
allowReference={false}
displayname="Group"
name="Group"
mentionname="Group"
onChange={[Function]}
onToggle={[Function]}
/>
<AdminPanel
Expand Down Expand Up @@ -494,6 +511,13 @@ exports[`components/admin_console/group_settings/group_details/GroupDetails shou
</AdminPanel>
</div>
</div>
<SaveChangesPanel
cancelLink="/admin_console/user_management/groups"
onClick={[Function]}
saveNeeded={false}
saving={false}
serverError={null}
/>
</div>
`;

Expand Down Expand Up @@ -538,7 +562,8 @@ exports[`components/admin_console/group_settings/group_details/GroupDetails shou
<GroupProfileAndSettings
allowReference={false}
displayname="Group"
name="Group"
mentionname="Group"
onChange={[Function]}
onToggle={[Function]}
/>
<AdminPanel
Expand Down Expand Up @@ -652,5 +677,12 @@ exports[`components/admin_console/group_settings/group_details/GroupDetails shou
</AdminPanel>
</div>
</div>
<SaveChangesPanel
cancelLink="/admin_console/user_management/groups"
onClick={[Function]}
saveNeeded={false}
saving={false}
serverError={null}
/>
</div>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@ exports[`components/admin_console/group_settings/group_details/GroupProfile shou
<div
className="col-sm-8"
>
<div
className="icon-over-input"
>
<MentionsIcon
aria-hidden="true"
className="icon icon__mentions"
/>
</div>
<input
className="form-control"
disabled={true}
className="form-control group_at_mention_input"
disabled={false}
type="text"
value="Test"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ exports[`components/admin_console/group_settings/group_details/GroupProfileAndSe
>
<GroupProfile
customID="GroupDisplayName"
isDisabled={true}
name="GroupProfileAndSettings"
showAtMention={false}
title="admin.group_settings.group_details.group_profile.name"
titleDefault="Name:"
/>
Expand Down Expand Up @@ -49,7 +51,9 @@ exports[`components/admin_console/group_settings/group_details/GroupProfileAndSe
>
<GroupProfile
customID="GroupDisplayName"
isDisabled={true}
name="GroupProfileAndSettings"
showAtMention={false}
title="admin.group_settings.group_details.group_profile.name"
titleDefault="Name:"
/>
Expand All @@ -75,7 +79,10 @@ exports[`components/admin_console/group_settings/group_details/GroupProfileAndSe
</div>
<GroupProfile
customID="GroupMention"
name="@GroupProfileAndSettings"
isDisabled={false}
name="GroupProfileAndSettings"
onChange={[MockFunction]}
showAtMention={true}
title="admin.group_settings.group_details.group_mention.name"
titleDefault="Group Mention:"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,23 @@ import {Groups} from 'mattermost-redux/constants';

import {t} from 'utils/i18n';
import {localizeMessage} from 'utils/utils.jsx';

import FormError from 'components/form_error';
import {GroupProfileAndSettings} from 'components/admin_console/group_settings/group_details/group_profile_and_settings';
import GroupTeamsAndChannels from 'components/admin_console/group_settings/group_details/group_teams_and_channels';
import GroupUsers from 'components/admin_console/group_settings/group_details/group_users';
import AdminPanel from 'components/widgets/admin_console/admin_panel';
import BlockableLink from 'components/admin_console/blockable_link';

import SaveChangesPanel from 'components/admin_console/team_channel_settings/save_changes_panel';

import TeamSelectorModal from 'components/team_selector_modal';
import ChannelSelectorModal from 'components/channel_selector_modal';
import Menu from 'components/widgets/menu/menu';
import MenuWrapper from 'components/widgets/menu/menu_wrapper';

import {GroupProfileAndSettings} from './group_profile_and_settings';
import {GroupNameIsTakenError, InvalidOrReservedGroupNameError, NeedGroupNameError} from 'components/admin_console/group_settings/group_details/group_details_errors';

export default class GroupDetails extends React.PureComponent {
static propTypes = {
groupID: PropTypes.string.isRequired,
Expand All @@ -35,6 +41,7 @@ export default class GroupDetails extends React.PureComponent {
unlink: PropTypes.func.isRequired,
patchGroupSyncable: PropTypes.func.isRequired,
patchGroup: PropTypes.func.isRequired,
setNavigationBlocked: PropTypes.func.isRequired,
}).isRequired,
};

Expand All @@ -53,6 +60,12 @@ export default class GroupDetails extends React.PureComponent {
addTeamOpen: false,
addChannelOpen: false,
allowReference: Boolean(props.group.allow_reference),
groupMentionName: String(props.group.name),
saving: false,
saveNeeded: false,
serverError: null,
hasAllowReferenceChanged: false,
hasGroupMentionNameChanged: false,
};
}

Expand All @@ -64,7 +77,12 @@ export default class GroupDetails extends React.PureComponent {
actions.getGroupSyncables(groupID, Groups.SYNCABLE_TYPE_TEAM),
actions.getGroupSyncables(groupID, Groups.SYNCABLE_TYPE_CHANNEL),
]).then(() => {
this.setState({loadingTeamsAndChannels: false, group, allowReference: Boolean(this.props.group.allow_reference)});
this.setState({
loadingTeamsAndChannels: false,
group,
allowReference: Boolean(this.props.group.allow_reference),
groupMentionName: String(this.props.group.name),
});
});
}

Expand Down Expand Up @@ -115,14 +133,78 @@ export default class GroupDetails extends React.PureComponent {
this.setState({loadingTeamsAndChannels: false});
}

onToggle = async (allowReference) => {
this.setState({allowReference});
this.props.actions.patchGroup(this.props.groupID, {allow_reference: allowReference});
onMentionToggle = async (allowReference) => {
const {group} = this.props;
const originalAllowReference = group.allow_reference;
const saveNeeded = true;

this.setState({
saveNeeded,
allowReference,
hasAllowReferenceChanged: allowReference !== originalAllowReference}
);
this.props.actions.setNavigationBlocked(saveNeeded);
}

onMentionChange = async (e) => {
const {group} = this.props;
const originalGroupMentionName = group.name;
const groupMentionName = e.target.value;
const saveNeeded = true;

this.setState({
saveNeeded,
groupMentionName,
hasGroupMentionNameChanged: groupMentionName !== originalGroupMentionName
});
this.props.actions.setNavigationBlocked(saveNeeded);
}

handleSubmit = async () => {
this.setState({saving: true});
const {allowReference, groupMentionName, hasAllowReferenceChanged, hasGroupMentionNameChanged} = this.state;

let serverError = null;
let saveNeeded = false;

if (!groupMentionName) {
saveNeeded = true;
serverError = <NeedGroupNameError/>;
this.setState({allowReference, serverError, saving: false, saveNeeded});
} else if (hasAllowReferenceChanged || hasGroupMentionNameChanged) {
saveNeeded = false;
serverError = null;

let lcGroupMentionName;
if (allowReference) {
lcGroupMentionName = groupMentionName.toLowerCase();
}

const result = await this.props.actions.patchGroup(this.props.groupID, {allow_reference: allowReference, name: lcGroupMentionName});
if (result.error) {
saveNeeded = true;
if (result.error.server_error_id === 'store.sql_group.unique_constraint') {
serverError = <GroupNameIsTakenError/>;
} else if (result.error.server_error_id === 'model.group.name.invalid_chars.app_error') {
serverError = <InvalidOrReservedGroupNameError/>;
} else if (result.error.server_error_id === 'api.ldap_groups.existing_reserved_name_error' ||
result.error.server_error_id === 'api.ldap_groups.existing_user_name_error' ||
result.error.server_error_id === 'api.ldap_groups.existing_group_name_error') {
serverError = <GroupNameIsTakenError/>;
} else {
serverError = <FormError error={result.error.message}/>;
}
}
this.setState({allowReference, groupMentionName: lcGroupMentionName, serverError, saving: false, saveNeeded});
} else {
this.setState({saving: false, saveNeeded});
}
this.props.actions.setNavigationBlocked(saveNeeded);
}

render = () => {
const {group, members, groupTeams, groupChannels, memberCount} = this.props;
const {allowReference} = this.state;
const {allowReference, groupMentionName, saving, saveNeeded, serverError} = this.state;

return (
<div className='wrapper--fixed'>
Expand Down Expand Up @@ -152,9 +234,10 @@ export default class GroupDetails extends React.PureComponent {

<GroupProfileAndSettings
displayname={group.display_name}
name={group.name}
mentionname={groupMentionName}
allowReference={allowReference}
onToggle={this.onToggle}
onToggle={this.onMentionToggle}
onChange={this.onMentionChange}
/>

<AdminPanel
Expand Down Expand Up @@ -235,6 +318,13 @@ export default class GroupDetails extends React.PureComponent {
</div>
</div>

<SaveChangesPanel
saving={saving}
cancelLink='/admin_console/user_management/groups'
saveNeeded={saveNeeded}
onClick={this.handleSubmit}
serverError={serverError}
/>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe('components/admin_console/group_settings/group_details/GroupDetails', (
unlink: jest.fn(),
patchGroup: jest.fn(),
patchGroupSyncable: jest.fn(),
setNavigationBlocked: jest.fn(),
},
};

Expand Down Expand Up @@ -76,6 +77,7 @@ describe('components/admin_console/group_settings/group_details/GroupDetails', (
unlink: jest.fn(),
patchGroup: jest.fn(),
patchGroupSyncable: jest.fn(),
setNavigationBlocked: jest.fn(),
};
shallow(
<GroupDetails
Expand All @@ -98,6 +100,7 @@ describe('components/admin_console/group_settings/group_details/GroupDetails', (
unlink: jest.fn().mockReturnValue(Promise.resolve()),
patchGroup: jest.fn(),
patchGroupSyncable: jest.fn(),
setNavigationBlocked: jest.fn(),
};
const wrapper = shallow(
<GroupDetails
Expand All @@ -123,6 +126,7 @@ describe('components/admin_console/group_settings/group_details/GroupDetails', (
unlink: jest.fn().mockReturnValue(Promise.resolve()),
patchGroup: jest.fn(),
patchGroupSyncable: jest.fn(),
setNavigationBlocked: jest.fn(),
};
const wrapper = shallow(
<GroupDetails
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';

import {FormattedMessage} from 'react-intl';

import FormError from 'components/form_error';

export const NeedGroupNameError = () => (
<FormError
iconClassName={'fa-exclamation-triangle'}
textClassName={'has-error'}
error={(
<FormattedMessage
id='admin.group_settings.need_groupname'
defaultMessage='You must specify a group mention.'
/>)}
/>
);

export const GroupNameIsTakenError = () => (
<FormError
iconClassName={'fa-exclamation-triangle'}
textClassName={'has-error'}
error={(
<FormattedMessage
id='admin.group_settings.group_detail.duplicateMentionNameError'
defaultMessage='Group mention is already taken.'
/>)}
/>
);

export const InvalidOrReservedGroupNameError = () => (
<FormError
iconClassName={'fa-exclamation-triangle'}
textClassName={'has-error'}
error={(
<FormattedMessage
id='admin.group_settings.group_detail.invalidOrReservedMentionNameError'
defaultMessage='Only letters (a-z), numbers(0-9), periods, dashes and underscores are allowed.'
/>)}
/>
);
Loading

0 comments on commit 44f496d

Please sign in to comment.