From 7a18af0558aba22f40de71bae0913922226496d9 Mon Sep 17 00:00:00 2001 From: Eli Yukelzon Date: Mon, 15 Jul 2019 16:14:50 +0300 Subject: [PATCH] UX screen review fixes for Admin Console Teams/Channels (#3058) Co-authored-by: Martin Kraft --- .../add_groups_to_channel_modal.test.jsx.snap | 24 +++--- .../add_groups_to_channel_modal.jsx | 8 +- .../add_groups_to_team_modal.test.jsx.snap | 24 +++--- .../add_groups_to_team_modal.jsx | 8 +- .../team_channel_settings/abstract_list.jsx | 11 +-- .../channel_details.test.jsx.snap | 6 ++ .../channel_groups.test.jsx.snap | 2 +- .../__snapshots__/channel_modes.test.jsx.snap | 1 + .../channel_profile.test.jsx.snap | 7 ++ .../channel/details/channel_details.jsx | 54 ++++++++----- .../channel/details/channel_details.test.jsx | 7 ++ .../channel/details/channel_groups.jsx | 2 +- .../channel/details/channel_modes.jsx | 18 ++++- .../channel/details/channel_modes.test.jsx | 1 + .../channel/details/channel_profile.jsx | 12 ++- .../channel/details/channel_profile.test.jsx | 1 + .../channel/details/index.js | 11 ++- .../__snapshots__/channel_row.test.jsx.snap | 6 +- .../channel/list/channel_list.jsx | 4 +- .../channel/list/channel_row.jsx | 6 +- .../channel/list/index.js | 9 +-- .../team_channel_settings/errors.jsx | 18 ++++- .../__snapshots__/group_row.test.jsx.snap | 6 +- .../__snapshots__/group_users.test.jsx.snap | 10 +-- .../group_users_row.test.jsx.snap | 4 +- .../group/group_list.jsx | 6 +- .../team_channel_settings/group/group_row.jsx | 6 +- .../group/group_users.jsx | 2 +- .../group/group_users_row.jsx | 2 +- .../team_channel_settings/line_switch.jsx | 13 ++-- .../__snapshots__/team_settings.test.jsx.snap | 2 +- .../__snapshots__/team_image.test.jsx.snap | 2 +- .../__snapshots__/team_profile.test.jsx.snap | 10 ++- .../team/details/index.js | 3 +- .../team/details/team_details.jsx | 40 +++++----- .../team/details/team_image.jsx | 4 +- .../team/details/team_modes.jsx | 8 +- .../team/details/team_profile.jsx | 6 +- .../list/__snapshots__/team_row.test.jsx.snap | 12 +-- .../team/list/team_list.jsx | 4 +- .../team/list/team_row.jsx | 13 ++-- .../team/team_settings.jsx | 2 +- .../users_to_be_removed_modal.jsx | 2 +- components/toggle.jsx | 4 +- i18n/en.json | 14 ++-- sass/components/_groups.scss | 62 +++++++++++++-- sass/components/_modal.scss | 4 + sass/components/_module.scss | 1 + sass/components/_team-channel-settings.scss | 78 +++++++++++++++++++ sass/layout/_team-button.scss | 27 +++++-- 50 files changed, 412 insertions(+), 175 deletions(-) create mode 100644 sass/components/_team-channel-settings.scss diff --git a/components/add_groups_to_channel_modal/__snapshots__/add_groups_to_channel_modal.test.jsx.snap b/components/add_groups_to_channel_modal/__snapshots__/add_groups_to_channel_modal.test.jsx.snap index ab8f53bf7489..66cddac57221 100644 --- a/components/add_groups_to_channel_modal/__snapshots__/add_groups_to_channel_modal.test.jsx.snap +++ b/components/add_groups_to_channel_modal/__snapshots__/add_groups_to_channel_modal.test.jsx.snap @@ -111,10 +111,12 @@ exports[`components/AddGroupsToChannelModal should match when renderOption is ca
- +   - - - +   + - +   - - - +   + - +   - - - +   +
- {option.display_name} {'-'} + {option.display_name} {'-'}  !this.props.excludeGroups.includes(g)); + const hasGroup = (og) => !this.props.excludeGroups.find((g) => g.id === og.id); + groupsToShow = groupsToShow.filter(hasGroup); } if (this.props.includeGroups) { - groupsToShow = [...groupsToShow, ...this.props.includeGroups.filter((g) => !groupsToShow.includes(g))]; + const hasGroup = (og) => this.props.includeGroups.find((g) => g.id === og.id); + groupsToShow = [...groupsToShow, ...this.props.includeGroups.filter(hasGroup)]; } return ( diff --git a/components/add_groups_to_team_modal/__snapshots__/add_groups_to_team_modal.test.jsx.snap b/components/add_groups_to_team_modal/__snapshots__/add_groups_to_team_modal.test.jsx.snap index 52118184b78a..b4cc24a19e21 100644 --- a/components/add_groups_to_team_modal/__snapshots__/add_groups_to_team_modal.test.jsx.snap +++ b/components/add_groups_to_team_modal/__snapshots__/add_groups_to_team_modal.test.jsx.snap @@ -111,10 +111,12 @@ exports[`components/AddGroupsToTeamModal should match when renderOption is calle
- +   - - - +   + - +   - - - +   + - +   - - - +   +
- {option.display_name} {'-'} + {option.display_name} {'-'}  !this.props.excludeGroups.includes(g)); + const hasGroup = (og) => !this.props.excludeGroups.find((g) => g.id === og.id); + groupsToShow = groupsToShow.filter(hasGroup); } if (this.props.includeGroups) { - groupsToShow = [...groupsToShow, ...this.props.includeGroups.filter((g) => !groupsToShow.includes(g))]; + const hasGroup = (og) => this.props.includeGroups.find((g) => g.id === og.id); + groupsToShow = [...groupsToShow, ...this.props.includeGroups.filter(hasGroup)]; } return ( diff --git a/components/admin_console/team_channel_settings/abstract_list.jsx b/components/admin_console/team_channel_settings/abstract_list.jsx index e7db2f0e1fae..ae53fae1ae87 100644 --- a/components/admin_console/team_channel_settings/abstract_list.jsx +++ b/components/admin_console/team_channel_settings/abstract_list.jsx @@ -73,7 +73,8 @@ export default class AbstractList extends React.PureComponent {
); } - return this.props.data.slice(0, PAGE_SIZE).map(this.props.renderRow); + const offset = this.state.page * PAGE_SIZE; + return this.props.data.slice(offset, offset + PAGE_SIZE).map(this.props.renderRow); } performSearch = (page) => { @@ -106,12 +107,12 @@ export default class AbstractList extends React.PureComponent { const lastPage = endCount === total; const firstPage = this.state.page === 0; return ( -
- {this.props.header} +
+ {total > 0 && this.props.header}
{this.renderRows()}
-
+ {total > 0 &&
-
+
}
); } diff --git a/components/admin_console/team_channel_settings/channel/details/__snapshots__/channel_details.test.jsx.snap b/components/admin_console/team_channel_settings/channel/details/__snapshots__/channel_details.test.jsx.snap index ef6cb3fff1d0..8e11a142194b 100644 --- a/components/admin_console/team_channel_settings/channel/details/__snapshots__/channel_details.test.jsx.snap +++ b/components/admin_console/team_channel_settings/channel/details/__snapshots__/channel_details.test.jsx.snap @@ -42,8 +42,14 @@ exports[`admin_console/team_channel_settings/channel/ChannelDetails should match "type": "O", } } + team={ + Object { + "display_name": "test", + } + } />
test +
+ +
+ test
diff --git a/components/admin_console/team_channel_settings/channel/details/channel_details.jsx b/components/admin_console/team_channel_settings/channel/details/channel_details.jsx index 84c907991110..b05a534a3cc2 100644 --- a/components/admin_console/team_channel_settings/channel/details/channel_details.jsx +++ b/components/admin_console/team_channel_settings/channel/details/channel_details.jsx @@ -23,12 +23,14 @@ export default class ChannelDetails extends React.Component { static propTypes = { channelID: PropTypes.string.isRequired, channel: PropTypes.object.isRequired, + team: PropTypes.object.isRequired, groups: PropTypes.arrayOf(PropTypes.object).isRequired, totalGroups: PropTypes.number.isRequired, allGroups: PropTypes.object.isRequired, actions: PropTypes.shape({ getGroups: PropTypes.func.isRequired, linkGroupSyncable: PropTypes.func.isRequired, + convertChannelToPrivate: PropTypes.func.isRequired, unlinkGroupSyncable: PropTypes.func.isRequired, membersMinusGroupMembers: PropTypes.func.isRequired, setNavigationBlocked: PropTypes.func.isRequired, @@ -56,17 +58,21 @@ export default class ChannelDetails extends React.Component { }; } - componentDidUpdate(prevProps) { // TODO: find out how to do this without the lifecycle - if (prevProps.totalGroups !== this.props.totalGroups) { + componentDidUpdate(prevProps) { + if (this.props.channel.id !== prevProps.channel.id) { // eslint-disable-next-line react/no-did-update-set-state - this.setState({totalGroups: this.props.totalGroups}); + this.setState({ + totalGroups: this.props.totalGroups, + isSynced: Boolean(this.props.channel.group_constrained), + isPublic: this.props.channel.type === Constants.OPEN_CHANNEL, + }); } } - componentDidMount() { + async componentDidMount() { const {channelID, actions} = this.props; - actions.getChannel(channelID). - then(() => actions.getGroups(channelID)). + actions.getGroups(channelID). + then(() => actions.getChannel(channelID)). then(() => this.setState({groups: this.props.groups})); } @@ -146,21 +152,23 @@ export default class ChannelDetails extends React.Component { serverError = ; saveNeeded = true; } else { - const {error} = await actions.patchChannel(channel.id, { + const promises = []; + if (!isPublic && channel.type === Constants.OPEN_CHANNEL) { + promises.push(actions.convertChannelToPrivate(channel.id)); + } + promises.push(actions.patchChannel(channel.id, { ...channel, group_constrained: isSynced, type: isPublic ? Constants.OPEN_CHANNEL : Constants.PRIVATE_CHANNEL, - }); - if (error) { - serverError = ; + })); + const unlink = origGroups.filter((g) => !groups.includes(g)).map((g) => actions.unlinkGroupSyncable(g.id, channelID, Groups.SYNCABLE_TYPE_CHANNEL)); + const link = groups.filter((g) => !origGroups.includes(g)).map((g) => actions.linkGroupSyncable(g.id, channelID, Groups.SYNCABLE_TYPE_CHANNEL, {auto_add: true})); + const result = await Promise.all([...promises, ...unlink, ...link]); + const resultWithError = result.find((r) => r.error); + if (resultWithError) { + serverError = ; } else { - const unlink = origGroups.filter((g) => !groups.includes(g)).map((g) => actions.unlinkGroupSyncable(g.id, channelID, Groups.SYNCABLE_TYPE_CHANNEL)); - const link = groups.filter((g) => !origGroups.includes(g)).map((g) => actions.linkGroupSyncable(g.id, channelID, Groups.SYNCABLE_TYPE_CHANNEL)); - const result = await Promise.all([...unlink, ...link]); - const resultWithError = result.find((r) => r.error); - if (resultWithError) { - serverError = ; - } + await actions.getGroups(channelID); } } @@ -170,9 +178,9 @@ export default class ChannelDetails extends React.Component { render = () => { const {totalGroups, saving, saveNeeded, serverError, isSynced, isPublic, groups, showRemoveConfirmation, usersToRemove} = this.state; - const {channel} = this.props; - const removedGroups = this.props.groups.filter((g) => !groups.includes(g)); - + const {channel, team} = this.props; + const missingGroup = (og) => !groups.find((g) => g.id === og.id); + const removedGroups = this.props.groups.filter(missingGroup); return (
@@ -196,11 +204,15 @@ export default class ChannelDetails extends React.Component { onCancel={this.hideRemoveUsersModal} onConfirm={this.handleSubmit} /> - + diff --git a/components/admin_console/team_channel_settings/channel/details/channel_details.test.jsx b/components/admin_console/team_channel_settings/channel/details/channel_details.test.jsx index 540958c11bc0..e5f49b69bc9b 100644 --- a/components/admin_console/team_channel_settings/channel/details/channel_details.test.jsx +++ b/components/admin_console/team_channel_settings/channel/details/channel_details.test.jsx @@ -23,13 +23,20 @@ describe('admin_console/team_channel_settings/channel/ChannelDetails', () => { group_constrained: false, name: 'DN', }; + const team = { + display_name: 'test', + }; + const wrapper = shallow( ( onToggle(!isSynced, isPublic)} title={( ( subTitle={( )} />); @@ -35,11 +36,17 @@ SyncGroupsToggle.propTypes = { onToggle: PropTypes.func.isRequired, }; -const AllowAllToggle = ({isSynced, isPublic, onToggle}) => +const AllowAllToggle = ({isSynced, isOriginallyPrivate, isPublic, onToggle}) => !isSynced && ( onToggle(isSynced, !isPublic)} + last={true} + singleLine={true} + onToggle={() => { + if (!isOriginallyPrivate) { + onToggle(isSynced, !isPublic); + } + }} title={( />); AllowAllToggle.propTypes = { + isOriginallyPrivate: PropTypes.bool.isRequired, isPublic: PropTypes.bool.isRequired, isSynced: PropTypes.bool.isRequired, onToggle: PropTypes.func.isRequired, }; -export const ChannelModes = ({isPublic, isSynced, onToggle}) => ( +export const ChannelModes = ({isOriginallyPrivate, isPublic, isSynced, onToggle}) => ( ( onToggle={onToggle} /> ( ); ChannelModes.propTypes = { + isOriginallyPrivate: PropTypes.bool.isRequired, isPublic: PropTypes.bool.isRequired, isSynced: PropTypes.bool.isRequired, onToggle: PropTypes.func.isRequired, diff --git a/components/admin_console/team_channel_settings/channel/details/channel_modes.test.jsx b/components/admin_console/team_channel_settings/channel/details/channel_modes.test.jsx index dcd7e74daa98..8fb1a6e4ff3c 100644 --- a/components/admin_console/team_channel_settings/channel/details/channel_modes.test.jsx +++ b/components/admin_console/team_channel_settings/channel/details/channel_modes.test.jsx @@ -12,6 +12,7 @@ describe('admin_console/team_channel_settings/channel/ChannelModes', () => { ); diff --git a/components/admin_console/team_channel_settings/channel/details/channel_profile.jsx b/components/admin_console/team_channel_settings/channel/details/channel_profile.jsx index 28c84a02ff2e..cb93b6b7bdff 100644 --- a/components/admin_console/team_channel_settings/channel/details/channel_profile.jsx +++ b/components/admin_console/team_channel_settings/channel/details/channel_profile.jsx @@ -9,7 +9,7 @@ import {t} from 'utils/i18n'; import AdminPanel from 'components/widgets/admin_console/admin_panel.jsx'; import FormattedMarkdownMessage from 'components/formatted_markdown_message.jsx'; -export const ChannelProfile = (props) => ( +export const ChannelProfile = ({team, channel}) => ( ( defaultMessage='**Name**' />
- {props.channel.name} + {channel.name} +
+ +
+ {team.display_name}
@@ -34,4 +41,5 @@ export const ChannelProfile = (props) => ( ChannelProfile.propTypes = { channel: PropTypes.object.isRequired, + team: PropTypes.object.isRequired, }; diff --git a/components/admin_console/team_channel_settings/channel/details/channel_profile.test.jsx b/components/admin_console/team_channel_settings/channel/details/channel_profile.test.jsx index bb64bfe4fd22..a26e432c800c 100644 --- a/components/admin_console/team_channel_settings/channel/details/channel_profile.test.jsx +++ b/components/admin_console/team_channel_settings/channel/details/channel_profile.test.jsx @@ -10,6 +10,7 @@ describe('admin_console/team_channel_settings/channel/ChannelProfile', () => { test('should match snapshot', () => { const wrapper = shallow( ); diff --git a/components/admin_console/team_channel_settings/channel/details/index.js b/components/admin_console/team_channel_settings/channel/details/index.js index 6661884d5ee3..38cfc469e0f8 100644 --- a/components/admin_console/team_channel_settings/channel/details/index.js +++ b/components/admin_console/team_channel_settings/channel/details/index.js @@ -5,7 +5,8 @@ import {bindActionCreators} from 'redux'; import {getChannel} from 'mattermost-redux/selectors/entities/channels'; import {getAllGroups, getGroupsAssociatedToChannel} from 'mattermost-redux/selectors/entities/groups'; -import {getChannel as fetchChannel, membersMinusGroupMembers, patchChannel} from 'mattermost-redux/actions/channels'; +import {convertChannelToPrivate, getChannel as fetchChannel, membersMinusGroupMembers, patchChannel} from 'mattermost-redux/actions/channels'; + import { getGroupsAssociatedToChannel as fetchAssociatedGroups, linkGroupSyncable, @@ -14,6 +15,8 @@ import { import {connect} from 'react-redux'; +import {getTeam} from 'mattermost-redux/selectors/entities/teams'; + import {setNavigationBlocked} from 'actions/admin_actions'; import ChannelDetails from './channel_details'; @@ -21,11 +24,14 @@ import ChannelDetails from './channel_details'; function mapStateToProps(state, props) { const channelID = props.match.params.channel_id; const channel = getChannel(state, channelID) || {}; + const team = channel.team_id ? getTeam(state, channel.team_id) : {}; const groups = getGroupsAssociatedToChannel(state, channelID); + const associatedGroups = state.entities.channels.groupsAssociatedToChannel; const allGroups = getAllGroups(state, channel.team_id); - const totalGroups = state.entities.channels.groupsAssociatedToChannel && state.entities.channels.groupsAssociatedToChannel[channelID] ? state.entities.channels.groupsAssociatedToChannel[channelID].totalCount : 0; + const totalGroups = associatedGroups && associatedGroups[channelID] && associatedGroups[channelID].totalCount ? associatedGroups[channelID].totalCount : 0; return { channel, + team, allGroups, totalGroups, groups, @@ -38,6 +44,7 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ getChannel: fetchChannel, getGroups: fetchAssociatedGroups, + convertChannelToPrivate, linkGroupSyncable, unlinkGroupSyncable, membersMinusGroupMembers, diff --git a/components/admin_console/team_channel_settings/channel/list/__snapshots__/channel_row.test.jsx.snap b/components/admin_console/team_channel_settings/channel/list/__snapshots__/channel_row.test.jsx.snap index de125db910c2..ca0087d3c83f 100644 --- a/components/admin_console/team_channel_settings/channel/list/__snapshots__/channel_row.test.jsx.snap +++ b/components/admin_console/team_channel_settings/channel/list/__snapshots__/channel_row.test.jsx.snap @@ -9,7 +9,7 @@ exports[`admin_console/team_channel_settings/channel/ChannelRow should match sna className="group-row" > team { return (
-
+
{ defaultMessage='Team' />
-
+
- + {channel.type === Constants.PRIVATE_CHANNEL ? : } {channel.name} - + {channel.team_name} - + { - if (a.type === b.type) { - return a.name.localeCompare(b.name); - } - return a.type === Constants.OPEN_CHANNEL ? 1 : -1; -}; +const compareByName = (a, b) => a.name.localeCompare(b.name); const getSortedListOfChannels = createSelector( getAllChannels, (teams) => Object.values(teams). filter((c) => c.type === Constants.OPEN_CHANNEL || c.type === Constants.PRIVATE_CHANNEL). - sort(compareByTypeAndName) + sort(compareByName) ); function mapStateToProps(state) { diff --git a/components/admin_console/team_channel_settings/errors.jsx b/components/admin_console/team_channel_settings/errors.jsx index c02050eca662..eec196cb1412 100644 --- a/components/admin_console/team_channel_settings/errors.jsx +++ b/components/admin_console/team_channel_settings/errors.jsx @@ -12,8 +12,10 @@ import ToggleModalButton from 'components/toggle_modal_button.jsx'; import UsersToBeRemovedModal from './users_to_be_removed_modal'; -export const NeedGroupsError = () => ( +export const NeedGroupsError = ({warning}) => ( ( /> ); +export const NeedDomainsError = () => ( + )} + /> +); + +NeedGroupsError.propTypes = { + warning: PropTypes.bool, +}; + export class UsersWillBeRemovedError extends React.PureComponent { static propTypes = { users: PropTypes.arrayOf(PropTypes.object).isRequired, diff --git a/components/admin_console/team_channel_settings/group/__snapshots__/group_row.test.jsx.snap b/components/admin_console/team_channel_settings/group/__snapshots__/group_row.test.jsx.snap index 181ff21f188c..59b3e1ab7eb5 100644 --- a/components/admin_console/team_channel_settings/group/__snapshots__/group_row.test.jsx.snap +++ b/components/admin_console/team_channel_settings/group/__snapshots__/group_row.test.jsx.snap @@ -2,18 +2,18 @@ exports[`admin_console/team_channel_settings/group/GroupRow should match snapshot 1`] = `
DN
test group @@ -118,7 +118,7 @@ exports[`admin_console/team_channel_settings/group/GroupUsersRow should match sn system_user { return (
-
+
-
+
diff --git a/components/admin_console/team_channel_settings/group/group_row.jsx b/components/admin_console/team_channel_settings/group/group_row.jsx index 737be07082fe..7e2d1d7d2491 100644 --- a/components/admin_console/team_channel_settings/group/group_row.jsx +++ b/components/admin_console/team_channel_settings/group/group_row.jsx @@ -19,13 +19,13 @@ export default class GroupRow extends React.Component { const {group} = this.props; return (
- + {group.display_name || group.name} - + { return ( -
+
{this.renderRolesColumn(user)} {this.renderGroupsColumn(user)}
diff --git a/components/admin_console/team_channel_settings/line_switch.jsx b/components/admin_console/team_channel_settings/line_switch.jsx index 6d7b5bc51c34..fcfa915c38c1 100644 --- a/components/admin_console/team_channel_settings/line_switch.jsx +++ b/components/admin_console/team_channel_settings/line_switch.jsx @@ -9,17 +9,19 @@ import Toggle from 'components/toggle'; export default class LineSwitch extends React.PureComponent { static propTypes = { title: PropTypes.node.isRequired, + last: PropTypes.bool, toggled: PropTypes.bool.isRequired, + singleLine: PropTypes.bool, subTitle: PropTypes.node.isRequired, onToggle: PropTypes.func.isRequired, children: PropTypes.node, - offText: PropTypes.string, - onText: PropTypes.string, + offText: PropTypes.node, + onText: PropTypes.node, }; render() { - const {title, subTitle, toggled, onToggle, children, offText, onText} = this.props; - return (
+ const {title, subTitle, singleLine, toggled, onToggle, children, offText, onText, last} = this.props; + return (
@@ -35,10 +37,11 @@ export default class LineSwitch extends React.PureComponent {
-
{subTitle}
+
{subTitle}
{children} + {!last &&

}
); } } diff --git a/components/admin_console/team_channel_settings/team/__snapshots__/team_settings.test.jsx.snap b/components/admin_console/team_channel_settings/team/__snapshots__/team_settings.test.jsx.snap index 85f3bcc2c237..d3ea49ac6b69 100644 --- a/components/admin_console/team_channel_settings/team/__snapshots__/team_settings.test.jsx.snap +++ b/components/admin_console/team_channel_settings/team/__snapshots__/team_settings.test.jsx.snap @@ -26,7 +26,7 @@ exports[`admin_console/team_channel_settings/team/TeamSettings should match snap

- No team description added. + + No team description added. +
diff --git a/components/admin_console/team_channel_settings/team/details/index.js b/components/admin_console/team_channel_settings/team/details/index.js index 33a4c4804547..9088da7ceec2 100644 --- a/components/admin_console/team_channel_settings/team/details/index.js +++ b/components/admin_console/team_channel_settings/team/details/index.js @@ -25,8 +25,9 @@ function mapStateToProps(state, props) { const teamID = props.match.params.team_id; const team = getTeam(state, teamID); const groups = getGroupsAssociatedToTeam(state, teamID); - const totalGroups = state.entities.teams.groupsAssociatedToTeam && state.entities.teams.groupsAssociatedToTeam[teamID] ? state.entities.teams.groupsAssociatedToTeam[teamID].totalCount : 0; + const associatedGroups = state.entities.teams.groupsAssociatedToTeam; const allGroups = getAllGroups(state, teamID); + const totalGroups = associatedGroups && associatedGroups[teamID] && associatedGroups[teamID].totalCount ? associatedGroups[teamID].totalCount : 0; return { team, groups, diff --git a/components/admin_console/team_channel_settings/team/details/team_details.jsx b/components/admin_console/team_channel_settings/team/details/team_details.jsx index d89812023055..8a7dd8fc14c5 100644 --- a/components/admin_console/team_channel_settings/team/details/team_details.jsx +++ b/components/admin_console/team_channel_settings/team/details/team_details.jsx @@ -12,7 +12,7 @@ import BlockableLink from 'components/admin_console/blockable_link'; import FormError from 'components/form_error'; import RemoveConfirmModal from '../../remove_confirm_modal'; -import {NeedGroupsError, UsersWillBeRemovedError} from '../../errors'; +import {NeedDomainsError, NeedGroupsError, UsersWillBeRemovedError} from '../../errors'; import SaveChangesPanel from '../../save_changes_panel'; @@ -50,7 +50,7 @@ export default class TeamDetails extends React.Component { syncChecked: Boolean(team.group_constrained), allAllowedChecked: team.allow_open_invite, allowedDomainsChecked: team.allowed_domains !== '', - allowedDomains: team.allowed_domains, + allowedDomains: team.allowed_domains || '', saving: false, showRemoveConfirmation: false, usersToRemove: 0, @@ -60,7 +60,7 @@ export default class TeamDetails extends React.Component { }; } - componentDidUpdate(prevProps) { // TODO: find out how to do this without the lifecycle + componentDidUpdate(prevProps) { if (prevProps.totalGroups !== this.props.totalGroups) { // eslint-disable-next-line react/no-did-update-set-state this.setState({totalGroups: this.props.totalGroups}); @@ -82,26 +82,27 @@ export default class TeamDetails extends React.Component { let saveNeeded = false; const {team, groups: origGroups, teamID, actions} = this.props; - if (this.state.groups.length === 0 && syncChecked) { + if (allowedDomainsChecked && allowedDomains.trim().length === 0) { + saveNeeded = true; + serverError = ; + } else if (this.state.groups.length === 0 && syncChecked) { serverError = ; saveNeeded = true; } else { - const {error} = await actions.patchTeam({ + const patchTeamPromise = actions.patchTeam({ ...team, group_constrained: syncChecked, allowed_domains: allowedDomainsChecked ? allowedDomains : '', allow_open_invite: allAllowedChecked, }); - if (error) { - serverError = ; + const unlink = origGroups.filter((g) => !groups.includes(g)).map((g) => actions.unlinkGroupSyncable(g.id, teamID, Groups.SYNCABLE_TYPE_TEAM)); + const link = groups.filter((g) => !origGroups.includes(g)).map((g) => actions.linkGroupSyncable(g.id, teamID, Groups.SYNCABLE_TYPE_TEAM, {auto_add: true})); + const result = await Promise.all([patchTeamPromise, ...unlink, ...link]); + const resultWithError = result.find((r) => r.error); + if (resultWithError) { + serverError = ; } else { - const unlink = origGroups.filter((g) => !groups.includes(g)).map((g) => actions.unlinkGroupSyncable(g.id, teamID, Groups.SYNCABLE_TYPE_TEAM)); - const link = groups.filter((g) => !origGroups.includes(g)).map((g) => actions.linkGroupSyncable(g.id, teamID, Groups.SYNCABLE_TYPE_TEAM)); - const result = await Promise.all([...unlink, ...link]); - const resultWithError = result.find((r) => r.error); - if (resultWithError) { - serverError = ; - } + await actions.getGroups(teamID); } } @@ -116,11 +117,7 @@ export default class TeamDetails extends React.Component { allAllowedChecked: !syncChecked && allAllowedChecked, allowedDomainsChecked: !syncChecked && allowedDomainsChecked, allowedDomains, - }, () => { - if (syncChecked) { - this.processGroupsChange(this.state.groups); - } - }); + }, () => this.processGroupsChange(this.state.groups)); this.props.actions.setNavigationBlocked(true); } @@ -133,7 +130,7 @@ export default class TeamDetails extends React.Component { if (this.state.syncChecked) { try { if (groups.length === 0) { - serverError = ; + serverError = ; } else { const result = await actions.membersMinusGroupMembers(teamID, groups.map((g) => g.id)); usersToRemove = result.data.total_count; @@ -179,7 +176,8 @@ export default class TeamDetails extends React.Component { render = () => { const {team} = this.props; const {totalGroups, saving, saveNeeded, serverError, groups, allAllowedChecked, allowedDomainsChecked, allowedDomains, syncChecked, showRemoveConfirmation, usersToRemove} = this.state; - const removedGroups = this.props.groups.filter((g) => !groups.includes(g)); + const missingGroup = (og) => !groups.find((g) => g.id === og.id); + const removedGroups = this.props.groups.filter(missingGroup); return (
diff --git a/components/admin_console/team_channel_settings/team/details/team_image.jsx b/components/admin_console/team_channel_settings/team/details/team_image.jsx index 65095d00b2fc..9b47ccb6f30c 100644 --- a/components/admin_console/team_channel_settings/team/details/team_image.jsx +++ b/components/admin_console/team_channel_settings/team/details/team_image.jsx @@ -11,7 +11,7 @@ export default class TeamButton extends React.PureComponent { let content; if (teamIconUrl) { content = ( -
+
+
{initials}
diff --git a/components/admin_console/team_channel_settings/team/details/team_modes.jsx b/components/admin_console/team_channel_settings/team/details/team_modes.jsx index b8c80d14a2d2..1527f91bc352 100644 --- a/components/admin_console/team_channel_settings/team/details/team_modes.jsx +++ b/components/admin_console/team_channel_settings/team/details/team_modes.jsx @@ -14,6 +14,7 @@ import LineSwitch from '../../line_switch.jsx'; const SyncGroupsToggle = ({syncChecked, allAllowedChecked, allowedDomainsChecked, allowedDomains, onToggle}) => ( onToggle(!syncChecked, allAllowedChecked, allowedDomainsChecked, allowedDomains)} title={( )} />); @@ -41,6 +42,7 @@ const AllowAllToggle = ({syncChecked, allAllowedChecked, allowedDomainsChecked, !syncChecked && ( onToggle(syncChecked, !allAllowedChecked, allowedDomainsChecked, allowedDomains)} title={( onToggle(syncChecked, allAllowedChecked, !allowedDomainsChecked, allowedDomains)} + singleLine={true} title={( )} > -
+
-
+
-
+

- {team.description || Utils.localizeMessage('admin.team_settings.team_detail.profileNoDescription', 'No team description added.')} + {team.description || {Utils.localizeMessage('admin.team_settings.team_detail.profileNoDescription', 'No team description added.')}}
diff --git a/components/admin_console/team_channel_settings/team/list/__snapshots__/team_row.test.jsx.snap b/components/admin_console/team_channel_settings/team/list/__snapshots__/team_row.test.jsx.snap index 9d5a3b91a103..7a335ad99a8f 100644 --- a/components/admin_console/team_channel_settings/team/list/__snapshots__/team_row.test.jsx.snap +++ b/components/admin_console/team_channel_settings/team/list/__snapshots__/team_row.test.jsx.snap @@ -9,27 +9,23 @@ exports[`admin_console/team_channel_settings/team/TeamRow should match snapshot className="group-row group-row-large" >
-
+
-
+
team
(
-
+
-
+
-
-
+
+
-
+
{team.display_name} {team.description && ( -
+
{team.description}
)} @@ -52,7 +49,7 @@ export default class TeamRow extends React.Component {
- + diff --git a/components/admin_console/team_channel_settings/users_to_be_removed_modal.jsx b/components/admin_console/team_channel_settings/users_to_be_removed_modal.jsx index 89ccdc6b137a..a5a4ed785ee8 100644 --- a/components/admin_console/team_channel_settings/users_to_be_removed_modal.jsx +++ b/components/admin_console/team_channel_settings/users_to_be_removed_modal.jsx @@ -70,7 +70,7 @@ export default class UsersToBeRemovedModal extends React.PureComponent { role='dialog' aria-labelledby='confirmModalLabel' > - + a { + &:hover { + background-color: transparent; + text-decoration: underline; + } + } + } .group-actions { width: 130px; text-align: right; @@ -234,9 +259,13 @@ animation-name: rowhighlight; } &.group-row-large { - height: 50px; + height: 80px; } } + + .channel-icon { + opacity: 0.8; + } } .group-profile { @@ -347,7 +376,7 @@ } .group-teams-and-channels { - padding: 15px; + padding: 2rem; .group-teams-and-channels-loading { text-align: center; padding: 40px; @@ -458,3 +487,24 @@ .group-description.group-user-removal-column { height: 40px; } + +#team_groups .group-row .group-description, +#channel_groups .group-row .group-description { + width: 170px; +} + +.section-separator { + height: 4rem; + margin: 0; + position: relative; + text-align: center; + z-index: 0; + + hr { + border-color: #cccccc; + margin: 0; + position: relative; + top: 2rem; + z-index: 5; + } +} \ No newline at end of file diff --git a/sass/components/_modal.scss b/sass/components/_modal.scss index e02eb0a09d57..2fd0c35871cf 100644 --- a/sass/components/_modal.scss +++ b/sass/components/_modal.scss @@ -714,6 +714,10 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + .more-modal__name_sub { + opacity: 0.5; + font-weight: 500; + } } } diff --git a/sass/components/_module.scss b/sass/components/_module.scss index 0c4e8a1ec2c5..9fc8d5e0bb70 100644 --- a/sass/components/_module.scss +++ b/sass/components/_module.scss @@ -37,3 +37,4 @@ @import 'sidebar-header-dropdown-button'; @import 'sidebar-header'; @import 'toggle'; +@import 'team-channel-settings'; diff --git a/sass/components/_team-channel-settings.scss b/sass/components/_team-channel-settings.scss new file mode 100644 index 000000000000..9af5d668d0af --- /dev/null +++ b/sass/components/_team-channel-settings.scss @@ -0,0 +1,78 @@ +@charset 'UTF-8'; + +span.greyed-out { + @include opacity(.4) +} + +.row-content { + @include opacity(.8) +} +.groups-list-no-padding { + div.group-row { + height: 40px; + padding-left: 20px; + } + div.groups-list--header { + div.group-name.adjusted { + margin-left: 20px; + } + div.group-description.adjusted { + margin-right: 22px; + } + + div.group-description.group-description-adjusted { + margin-right: 22px; + } + + div.group-name.group-name-adjusted { + margin-left: 22px; + } + } + +} +.btn-toggle.btn-lg { + margin-left: 68px; + margin-bottom: -5px; +} + +.help-text-small.help-text-no-padding { + margin-top: 0; +} +.help-text-small.help-text-single-line { + height: 17px; +} +.team-descr-list-column { + max-width: 440px; +} +.large-team-image-col { + max-width: 120px; +} +.center-row > div { + display: inline-block; + vertical-align: middle; + float: none; +} + +.row-bottom-padding { + padding-bottom: 12px; +} + +.csvDomains { + padding-bottom: 4px; + span { + font-size: 12px; + font-weight: 600; + line-height: 17px; + } +} + + +.help-text-small { + height: 34px; + width: 668px; + opacity: 0.5; + color: #000000; + font-family: "Open Sans"; + font-size: 12px; + line-height: 17px; +} diff --git a/sass/layout/_team-button.scss b/sass/layout/_team-button.scss index cedc9c9413b4..5867fbee6650 100644 --- a/sass/layout/_team-button.scss +++ b/sass/layout/_team-button.scss @@ -1,3 +1,7 @@ +.team-container { + margin-right: 10px; +} + .team-btn__content { @include single-transition(all, .25s, ease); @include opacity(.5); @@ -7,12 +11,16 @@ height: 100%; } +.team-btn__content.no-hover { + transition:none; +} + .team-btn__initials { &.team-btn-large__initials { font-size: 3em; } &.team-btn-small__initials { - font-size: 0.6em; + font-size: 14px; } } .team-btn { @@ -29,15 +37,15 @@ width: 44px; &.team-btn-large { - width: 100px; - height: 100px; - line-height: 98px; + width: 94px; + height: 94px; + line-height: 92px; } &.team-btn-small { - width: 30px; - height: 30px; - line-height: 28px; + width: 40px; + height: 40px; + line-height: 38px; } @@ -45,6 +53,9 @@ .team-btn__content { @include opacity(.8); } + .team-btn__content.no-hover { + @include opacity(.5); + } } .team-btn__image { @@ -59,7 +70,7 @@ } &.team-btn-small__image { - width: 29px; + width: 40px; } }