Commit 136f5f7e authored by Carlos Tadeu Panato Junior's avatar Carlos Tadeu Panato Junior Committed by Joram Wilander

revert PR #1918 (#2203)

parent 62495f63
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/AddUserToChannelModal should match snapshot 1`] = `
<Modal
animation={true}
autoFocus={true}
backdrop={true}
bsClass="modal"
dialogClassName="modal--overflow"
dialogComponentClass={[Function]}
enforceFocus={true}
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[MockFunction]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
show={true}
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
>
<ModalTitle
bsClass="modal-title"
componentClass="h4"
>
<FormattedMessage
defaultMessage="Add {name} to a channel"
id="add_user_to_channel_modal.title"
values={
Object {
"name": "Fake Person",
}
}
/>
</ModalTitle>
</ModalHeader>
<form
onSubmit={[Function]}
role="form"
>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<div
className="modal__hint"
>
<FormattedMessage
defaultMessage="Type to find a channel. Use ↑↓ to browse, ↵ to select, ESC to dismiss."
id="add_user_to_channel_modal.help"
values={Object {}}
/>
</div>
<SuggestionBox
className="form-control focused"
completeOnTab={false}
containerClass=""
delayInputUpdate={true}
isRHS={false}
listComponent={[Function]}
listStyle="bottom"
listenForMentionKeyClick={false}
maxLength="64"
onChange={[Function]}
onItemSelected={[Function]}
openOnFocus={false}
openWhenEmpty={true}
providers={
Array [
SearchChannelWithPermissionsProvider {
"disableDispatches": false,
"latestComplete": true,
"latestPrefix": "",
"requestStarted": false,
},
]
}
renderDividers={false}
renderNoResults={false}
replaceAllInputOnSelect={false}
requiredCharacters={1}
value=""
/>
<div>
<br />
</div>
</ModalBody>
<ModalFooter
bsClass="modal-footer"
componentClass="div"
>
<button
className="btn btn-link"
onClick={[Function]}
type="button"
>
<FormattedMessage
defaultMessage="Cancel"
id="add_user_to_channel_modal.cancel"
values={Object {}}
/>
</button>
<button
className="btn btn-primary"
disabled={true}
id="add-user-to-channel-modal__add-button"
onClick={[Function]}
type="button"
>
<FormattedMessage
defaultMessage="Add"
id="add_user_to_channel_modal.add"
values={Object {}}
/>
</button>
</ModalFooter>
</form>
</Modal>
`;
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import PropTypes from 'prop-types';
import React from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import {getFullName} from 'mattermost-redux/utils/user_utils';
import SearchChannelWithPermissionsProvider from 'components/suggestion/search_channel_with_permissions_provider.jsx';
import SuggestionBox from 'components/suggestion/suggestion_box.jsx';
import SuggestionList from 'components/suggestion/suggestion_list.jsx';
import {placeCaretAtEnd} from 'utils/utils.jsx';
export default class AddUserToChannelModal extends React.Component {
static propTypes = {
/**
* Function that's called when modal is closed
*/
onHide: PropTypes.func.isRequired,
/**
* The user that is being added to a channel
*/
user: PropTypes.object.isRequired,
/**
* Object used to determine if the user
* is a member of a given channel
*/
channelMembers: PropTypes.object.isRequired,
actions: PropTypes.shape({
/**
* Function to add the user to a channel
*/
addChannelMember: PropTypes.func.isRequired,
/**
* Function to fetch the user's channel membership
*/
getChannelMember: PropTypes.func.isRequired,
}).isRequired,
}
constructor(props) {
super(props);
this.state = {
/**
* Whether or not the modal is visible
*/
show: true,
/**
* Whether or not a request to add the user is in progress
*/
saving: false,
/**
* Whether or not a request to check for the user's channel membership
* is in progress
*/
checkingForMembership: false,
/**
* The user input in the channel search box
*/
text: '',
/**
* The id for the channel that is selected
*/
selectedChannelId: null,
/**
* An error to display when the add request fails
*/
submitError: '',
};
this.suggestionProviders = [new SearchChannelWithPermissionsProvider()];
this.enableChannelProvider();
}
enableChannelProvider = () => {
this.suggestionProviders[0].disableDispatches = false;
}
focusTextbox = () => {
if (this.channelSearchBox == null) {
return;
}
const textbox = this.channelSearchBox.getTextbox();
if (document.activeElement !== textbox) {
textbox.focus();
placeCaretAtEnd(textbox);
}
}
onInputChange = (e) => {
this.setState({text: e.target.value, selectedChannelId: null});
}
onHide = () => {
this.setState({show: false});
this.props.onHide();
}
setSearchBoxRef = (input) => {
this.channelSearchBox = input;
this.focusTextbox();
}
handleSubmitError = (error) => {
if (error) {
this.setState({submitError: error.message, saving: false});
}
}
didSelectChannel = (selection) => {
const channel = selection.channel;
const userId = this.props.user.id;
this.setState({
text: channel.display_name,
selectedChannelId: channel.id,
checkingForMembership: true,
submitError: '',
});
this.props.actions.getChannelMember(channel.id, userId).then(() => {
this.setState({checkingForMembership: false});
});
}
handleSubmit = (e) => {
if (e && e.preventDefault) {
e.preventDefault();
}
const channelId = this.state.selectedChannelId;
const user = this.props.user;
if (!channelId) {
return;
}
if (this.isUserMemberOfChannel(channelId) || this.state.saving) {
return;
}
this.setState({saving: true});
this.props.actions.addChannelMember(channelId, user.id).then(({error}) => {
if (error) {
this.handleSubmitError(error);
} else {
this.onHide();
}
});
}
isUserMemberOfChannel = (channelId) => {
const user = this.props.user;
const memberships = this.props.channelMembers;
if (!channelId) {
return false;
}
if (!memberships[channelId]) {
return false;
}
return Boolean(memberships[channelId][user.id]);
}
render() {
const user = this.props.user;
const channelId = this.state.selectedChannelId;
const targetUserIsMemberOfSelectedChannel = this.isUserMemberOfChannel(channelId);
let name = getFullName(user);
if (!name) {
name = `@${user.username}`;
}
let errorMsg;
if (!this.state.saving) {
if (this.state.submitError) {
errorMsg = (
<label
id='add-user-to-channel-modal__invite-error'
className='modal__error has-error control-label'
>
{this.state.submitError}
</label>
);
} else if (targetUserIsMemberOfSelectedChannel) {
errorMsg = (
<label
id='add-user-to-channel-modal__user-is-member'
className='modal__error has-error control-label'
>
<FormattedMessage
id='add_user_to_channel_modal.membershipExistsError'
defaultMessage='{name} is already a member of that channel'
values={{
name,
}}
/>
</label>
);
}
}
const help = (
<FormattedMessage
id='add_user_to_channel_modal.help'
defaultMessage='Type to find a channel. Use ↑↓ to browse, ↵ to select, ESC to dismiss.'
/>
);
const content = (
<SuggestionBox
ref={this.setSearchBoxRef}
className='form-control focused'
onChange={this.onInputChange}
value={this.state.text}
onKeyDown={this.handleKeyDown}
onItemSelected={this.didSelectChannel}
listComponent={SuggestionList}
maxLength='64'
providers={this.suggestionProviders}
listStyle='bottom'
completeOnTab={false}
renderDividers={false}
delayInputUpdate={true}
openWhenEmpty={true}
/>
);
const shouldDisableAddButton = targetUserIsMemberOfSelectedChannel ||
this.state.checkingForMembership ||
Boolean(!this.state.selectedChannelId) ||
this.state.saving;
return (
<Modal
dialogClassName='modal--overflow'
show={this.state.show}
onHide={this.onHide}
onExited={this.props.onHide}
ref='modal'
enforceFocus={true}
>
<Modal.Header closeButton={true}>
<Modal.Title>
<FormattedMessage
id='add_user_to_channel_modal.title'
defaultMessage='Add {name} to a channel'
values={{
name,
}}
/>
</Modal.Title>
</Modal.Header>
<form
role='form'
onSubmit={this.handleSubmit}
>
<Modal.Body>
<div className='modal__hint'>
{help}
</div>
{content}
<div>
{errorMsg}
<br/>
</div>
</Modal.Body>
<Modal.Footer>
<button
type='button'
className='btn btn-link'
onClick={this.onHide}
>
<FormattedMessage
id='add_user_to_channel_modal.cancel'
defaultMessage='Cancel'
/>
</button>
<button
type='button'
id='add-user-to-channel-modal__add-button'
className='btn btn-primary'
onClick={this.handleSubmit}
disabled={shouldDisableAddButton}
>
<FormattedMessage
id='add_user_to_channel_modal.add'
defaultMessage='Add'
/>
</button>
</Modal.Footer>
</form>
</Modal>
);
}
}
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import AddUserToChannelModal from 'components/add_user_to_channel_modal/add_user_to_channel_modal';
describe('components/AddUserToChannelModal', () => {
const baseProps = {
channelMembers: {},
user: {
id: 'someUserId',
first_name: 'Fake',
last_name: 'Person',
},
onHide: jest.fn(),
actions: {
addChannelMember: jest.fn().mockResolvedValue({}),
getChannelMember: jest.fn().mockResolvedValue({}),
},
};
it('should match snapshot', () => {
const wrapper = shallow(
<AddUserToChannelModal {...baseProps}/>
);
expect(wrapper.find('#add-user-to-channel-modal__add-button').props().disabled).toBe(true);
expect(wrapper.find('#add-user-to-channel-modal__user-is-member').exists()).toBe(false);
expect(wrapper.find('#add-user-to-channel-modal__invite-error').exists()).toBe(false);
expect(wrapper).toMatchSnapshot();
});
it('should enable the add button when a channel is selected', () => {
const wrapper = shallow(
<AddUserToChannelModal {...baseProps}/>
);
wrapper.setState({selectedChannelId: 'someChannelId'});
expect(wrapper.find('#add-user-to-channel-modal__add-button').props().disabled).toBe(false);
expect(wrapper.find('#add-user-to-channel-modal__invite-error').exists()).toBe(false);
});
it('should show invite error when an error message is captured', () => {
const wrapper = shallow(
<AddUserToChannelModal {...baseProps}/>
);
wrapper.setState({submitError: 'some error'});
expect(wrapper.find('#add-user-to-channel-modal__add-button').props().disabled).toBe(true);
expect(wrapper.find('#add-user-to-channel-modal__invite-error').exists()).toBe(true);
});
it('should disable add button when membership is being checked', () => {
const wrapper = shallow(
<AddUserToChannelModal {...baseProps}/>
);
wrapper.setState({
selectedChannelId: 'someChannelId',
checkingForMembership: true,
});
expect(wrapper.find('#add-user-to-channel-modal__add-button').props().disabled).toBe(true);
});
it('should display error message if user is a member of the selected channel', () => {
const props = {...baseProps,
channelMembers: {
someChannelId: {
someUserId: {},
},
},
};
const wrapper = shallow(
<AddUserToChannelModal {...props}/>
);
wrapper.setState({selectedChannelId: 'someChannelId'});
expect(wrapper.find('#add-user-to-channel-modal__add-button').props().disabled).toBe(true);
expect(wrapper.find('#add-user-to-channel-modal__user-is-member').exists()).toBe(true);
});
it('should disable the add button when saving', () => {
const wrapper = shallow(
<AddUserToChannelModal {...baseProps}/>
);
wrapper.setState({
selectedChannelId: 'someChannelId',
saving: true,
});
expect(wrapper.find('#add-user-to-channel-modal__add-button').props().disabled).toBe(true);
});
describe('didSelectChannel', () => {
it('should fetch the selected user\'s membership for the selected channel', () => {
const props = {...baseProps};
const wrapper = shallow(
<AddUserToChannelModal {...props}/>
);
const selection = {channel: {id: 'someChannelId', display_name: 'channelName'}};
wrapper.instance().didSelectChannel(selection);
expect(props.actions.getChannelMember).toBeCalledWith('someChannelId', 'someUserId');
});
it('should match state on selection', async () => {
const promise = Promise.resolve({});
const props = {
...baseProps,
actions: {
...baseProps.actions,
getChannelMember: jest.fn(() => {
return promise;
}),
},
};
const wrapper = shallow(
<AddUserToChannelModal {...props}/>
);
expect(wrapper.state().text).toEqual('');
expect(wrapper.state().checkingForMembership).toEqual(false);
expect(wrapper.state().selectedChannelId).toEqual(null);
expect(wrapper.state().submitError).toEqual('');
const selection = {channel: {id: 'someChannelId', display_name: 'channelName'}};
wrapper.setState({submitError: 'some pre-existing error'});
wrapper.instance().didSelectChannel(selection);
expect(wrapper.state().text).toEqual('channelName');
expect(wrapper.state().checkingForMembership).toEqual(true);
expect(wrapper.state().selectedChannelId).toEqual('someChannelId');
expect(wrapper.state().submitError).toEqual('');
await promise;
expect(wrapper.state().checkingForMembership).toEqual(false);
});
});
describe('handleSubmit', () => {
it('should do nothing if no channel is selected', () => {
const props = {...baseProps};
const wrapper = shallow(
<AddUserToChannelModal {...props}/>
);
wrapper.instance().handleSubmit();
expect(wrapper.state().saving).toBe(false);
expect(props.actions.addChannelMember).not.toBeCalled();
});
it('should do nothing if user is a member of the selected channel', () => {
const props = {...baseProps,
channelMembers: {
someChannelId: {
someUserId: {},
},
},
};
const wrapper = shallow(
<AddUserToChannelModal {...props}/>
);
wrapper.setState({selectedChannelId: 'someChannelId'});
wrapper.instance().handleSubmit();
expect(wrapper.state().saving).toBe(false);
expect(props.actions.addChannelMember).not.toBeCalled();
});
it('should submit if user is not a member of the selected channel', () => {
const props = {...baseProps,
channelMembers: {
someChannelId: {},
},
};
const wrapper = shallow(
<AddUserToChannelModal {...props}/>
);
wrapper.setState({selectedChannelId: 'someChannelId'});
wrapper.instance().handleSubmit();
expect(wrapper.state().saving).toBe(true);
expect(props.actions.addChannelMember).toBeCalled();
});
test('should match state when save is successful', async () => {
const onHide = jest.fn();
const promise = Promise.resolve({});
const props = {
...baseProps,
onHide,
actions: {
...baseProps.actions,
addChannelMember: () => promise,
},
};
const wrapper = shallow(
<AddUserToChannelModal {...props}/>
);
expect(wrapper.state().show).toBe(true);
expect(wrapper.state().saving).toBe(false);
wrapper.setState({selectedChannelId: 'someChannelId'});
wrapper.instance().handleSubmit();
expect(wrapper.state().show).toBe(true);
expect(wrapper.state().saving).toBe(true);
await promise;
expect(wrapper.state().submitError).toEqual('');
expect(wrapper.state().show).toBe(false);
expect(onHide).toHaveBeenCalled();
});
test('should match state when save fails', async () => {
const onHide = jest.fn();
const promise = Promise.resolve({error: new Error('some error')});
const props = {
...baseProps,
onHide,
actions: {
...baseProps.actions,
addChannelMember: () => promise,
},
};
const wrapper = shallow(
<AddUserToChannelModal {...props}/>
);
expect(wrapper.state().show).toBe(true);
wrapper.setState({selectedChannelId: 'someChannelId'});