From 29b98ec383e47f28e7e190434f872072815cb6cb Mon Sep 17 00:00:00 2001 From: 94117nl Date: Tue, 4 Jul 2017 07:58:45 -0500 Subject: PLT-6445 Migrate add_command.jsx to be pure and use Redux (#6804) * Migrate add_command.jsx to be pure and use redux * Add basic test for AddCommand component --- .../integrations/components/add_command.jsx | 614 -------------------- .../components/add_command/add_command.jsx | 615 +++++++++++++++++++++ .../integrations/components/add_command/index.js | 25 + webapp/routes/route_integrations.jsx | 2 +- .../__snapshots__/add_command.test.jsx.snap | 396 +++++++++++++ .../components/integrations/add_command.test.jsx | 30 + 6 files changed, 1067 insertions(+), 615 deletions(-) delete mode 100644 webapp/components/integrations/components/add_command.jsx create mode 100644 webapp/components/integrations/components/add_command/add_command.jsx create mode 100644 webapp/components/integrations/components/add_command/index.js create mode 100644 webapp/tests/components/integrations/__snapshots__/add_command.test.jsx.snap create mode 100644 webapp/tests/components/integrations/add_command.test.jsx (limited to 'webapp') diff --git a/webapp/components/integrations/components/add_command.jsx b/webapp/components/integrations/components/add_command.jsx deleted file mode 100644 index 2141dda4a..000000000 --- a/webapp/components/integrations/components/add_command.jsx +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import React from 'react'; -import PropTypes from 'prop-types'; - -import * as Utils from 'utils/utils.jsx'; - -import {addCommand} from 'actions/integration_actions.jsx'; - -import BackstageHeader from 'components/backstage/components/backstage_header.jsx'; -import {FormattedMessage} from 'react-intl'; -import FormError from 'components/form_error.jsx'; -import {browserHistory, Link} from 'react-router/es6'; -import SpinnerButton from 'components/spinner_button.jsx'; -import Constants from 'utils/constants.jsx'; - -const REQUEST_POST = 'P'; -const REQUEST_GET = 'G'; - -export default class AddCommand extends React.Component { - static get propTypes() { - return { - team: PropTypes.object - }; - } - - constructor(props) { - super(props); - - this.handleSubmit = this.handleSubmit.bind(this); - - this.updateDisplayName = this.updateDisplayName.bind(this); - this.updateDescription = this.updateDescription.bind(this); - this.updateTrigger = this.updateTrigger.bind(this); - this.updateUrl = this.updateUrl.bind(this); - this.updateMethod = this.updateMethod.bind(this); - this.updateUsername = this.updateUsername.bind(this); - this.updateIconUrl = this.updateIconUrl.bind(this); - this.updateAutocomplete = this.updateAutocomplete.bind(this); - this.updateAutocompleteHint = this.updateAutocompleteHint.bind(this); - this.updateAutocompleteDescription = this.updateAutocompleteDescription.bind(this); - - this.state = { - displayName: '', - description: '', - trigger: '', - url: '', - method: REQUEST_POST, - username: '', - iconUrl: '', - autocomplete: false, - autocompleteHint: '', - autocompleteDescription: '', - saving: false, - serverError: '', - clientError: null - }; - } - - handleSubmit(e) { - e.preventDefault(); - - if (this.state.saving) { - return; - } - - this.setState({ - saving: true, - serverError: '', - clientError: '' - }); - - let triggerWord = this.state.trigger.trim().toLowerCase(); - if (triggerWord.indexOf('/') === 0) { - triggerWord = triggerWord.substr(1); - } - - const command = { - display_name: this.state.displayName, - description: this.state.description, - trigger: triggerWord, - url: this.state.url.trim(), - method: this.state.method, - username: this.state.username, - icon_url: this.state.iconUrl, - auto_complete: this.state.autocomplete, - team_id: this.props.team.id - }; - - if (command.auto_complete) { - command.auto_complete_desc = this.state.autocompleteDescription; - command.auto_complete_hint = this.state.autocompleteHint; - } - - if (!command.trigger) { - this.setState({ - saving: false, - clientError: ( - - ) - }); - - return; - } - - if (command.trigger.indexOf('/') === 0) { - this.setState({ - saving: false, - clientError: ( - - ) - }); - - return; - } - - if (command.trigger.indexOf(' ') !== -1) { - this.setState({ - saving: false, - clientError: ( - - ) - }); - return; - } - - if (command.trigger.length < Constants.MIN_TRIGGER_LENGTH || command.trigger.length > Constants.MAX_TRIGGER_LENGTH) { - this.setState({ - saving: false, - clientError: ( - - ) - }); - - return; - } - - if (!command.url) { - this.setState({ - saving: false, - clientError: ( - - ) - }); - - return; - } - - addCommand( - command, - (data) => { - browserHistory.push('/' + this.props.team.name + '/integrations/commands/confirm?type=commands&id=' + data.id); - }, - (err) => { - this.setState({ - saving: false, - serverError: err.message - }); - } - ); - } - - updateDisplayName(e) { - this.setState({ - displayName: e.target.value - }); - } - - updateDescription(e) { - this.setState({ - description: e.target.value - }); - } - - updateTrigger(e) { - this.setState({ - trigger: e.target.value - }); - } - - updateUrl(e) { - this.setState({ - url: e.target.value - }); - } - - updateMethod(e) { - this.setState({ - method: e.target.value - }); - } - - updateUsername(e) { - this.setState({ - username: e.target.value - }); - } - - updateIconUrl(e) { - this.setState({ - iconUrl: e.target.value - }); - } - - updateAutocomplete(e) { - this.setState({ - autocomplete: e.target.checked - }); - } - - updateAutocompleteHint(e) { - this.setState({ - autocompleteHint: e.target.value - }); - } - - updateAutocompleteDescription(e) { - this.setState({ - autocompleteDescription: e.target.value - }); - } - - render() { - let autocompleteFields = null; - if (this.state.autocomplete) { - autocompleteFields = [( -
- -
- -
- -
-
-
- ), - ( -
- -
- -
- -
-
-
- )]; - } - - return ( -
- - - - - - -
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
- -
-
- - - - ) - }} - /> -
-
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
- {autocompleteFields} -
- - - - - - - -
-
-
-
- ); - } -} diff --git a/webapp/components/integrations/components/add_command/add_command.jsx b/webapp/components/integrations/components/add_command/add_command.jsx new file mode 100644 index 000000000..28f6115f9 --- /dev/null +++ b/webapp/components/integrations/components/add_command/add_command.jsx @@ -0,0 +1,615 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; +import PropTypes from 'prop-types'; + +import * as Utils from 'utils/utils.jsx'; + +import BackstageHeader from 'components/backstage/components/backstage_header.jsx'; +import {FormattedMessage} from 'react-intl'; +import FormError from 'components/form_error.jsx'; +import {browserHistory, Link} from 'react-router/es6'; +import SpinnerButton from 'components/spinner_button.jsx'; +import Constants from 'utils/constants.jsx'; + +const REQUEST_POST = 'P'; +const REQUEST_GET = 'G'; + +export default class AddCommand extends React.PureComponent { + static propTypes = { + + /** + * The team data + */ + team: PropTypes.object, + + /** + * The request state for addCommand action. Contains status and error + */ + addCommandRequest: PropTypes.object.isRequired, + + actions: PropTypes.shape({ + + /** + * The function to call to add new command + */ + addCommand: PropTypes.func.isRequired + }).isRequired + } + + constructor(props) { + super(props); + + this.state = { + displayName: '', + description: '', + trigger: '', + url: '', + method: REQUEST_POST, + username: '', + iconUrl: '', + autocomplete: false, + autocompleteHint: '', + autocompleteDescription: '', + saving: false, + serverError: '', + clientError: null + }; + } + + handleSubmit = (e) => { + e.preventDefault(); + + if (this.state.saving) { + return; + } + + this.setState({ + saving: true, + serverError: '', + clientError: '' + }); + + let triggerWord = this.state.trigger.trim().toLowerCase(); + if (triggerWord.indexOf('/') === 0) { + triggerWord = triggerWord.substr(1); + } + + const command = { + display_name: this.state.displayName, + description: this.state.description, + trigger: triggerWord, + url: this.state.url.trim(), + method: this.state.method, + username: this.state.username, + icon_url: this.state.iconUrl, + auto_complete: this.state.autocomplete, + team_id: this.props.team.id + }; + + if (command.auto_complete) { + command.auto_complete_desc = this.state.autocompleteDescription; + command.auto_complete_hint = this.state.autocompleteHint; + } + + if (!command.trigger) { + this.setState({ + saving: false, + clientError: ( + + ) + }); + + return; + } + + if (command.trigger.indexOf('/') === 0) { + this.setState({ + saving: false, + clientError: ( + + ) + }); + + return; + } + + if (command.trigger.indexOf(' ') !== -1) { + this.setState({ + saving: false, + clientError: ( + + ) + }); + return; + } + + if (command.trigger.length < Constants.MIN_TRIGGER_LENGTH || + command.trigger.length > Constants.MAX_TRIGGER_LENGTH) { + this.setState({ + saving: false, + clientError: ( + + ) + }); + + return; + } + + if (!command.url) { + this.setState({ + saving: false, + clientError: ( + + ) + }); + + return; + } + + this.props.actions.addCommand(command).then( + (data) => { + if (data) { + browserHistory.push(`/${this.props.team.name}/integrations/commands/confirm?type=commands&id=${data.id}`); + } else { + this.setState({ + saving: false, + serverError: this.props.addCommandRequest.error.message + }); + } + } + ); + } + + updateDisplayName = (e) => { + this.setState({ + displayName: e.target.value + }); + } + + updateDescription = (e) => { + this.setState({ + description: e.target.value + }); + } + + updateTrigger = (e) => { + this.setState({ + trigger: e.target.value + }); + } + + updateUrl = (e) => { + this.setState({ + url: e.target.value + }); + } + + updateMethod = (e) => { + this.setState({ + method: e.target.value + }); + } + + updateUsername = (e) => { + this.setState({ + username: e.target.value + }); + } + + updateIconUrl = (e) => { + this.setState({ + iconUrl: e.target.value + }); + } + + updateAutocomplete = (e) => { + this.setState({ + autocomplete: e.target.checked + }); + } + + updateAutocompleteHint = (e) => { + this.setState({ + autocompleteHint: e.target.value + }); + } + + updateAutocompleteDescription = (e) => { + this.setState({ + autocompleteDescription: e.target.value + }); + } + + render() { + let autocompleteFields = null; + if (this.state.autocomplete) { + autocompleteFields = [( +
+ +
+ +
+ +
+
+
+ ), + ( +
+ +
+ +
+ +
+
+
+ )]; + } + + return ( +
+ + + + + + +
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+ + + + ) + }} + /> +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+ {autocompleteFields} +
+ + + + + + + +
+
+
+
+ ); + } +} diff --git a/webapp/components/integrations/components/add_command/index.js b/webapp/components/integrations/components/add_command/index.js new file mode 100644 index 000000000..9ac7db220 --- /dev/null +++ b/webapp/components/integrations/components/add_command/index.js @@ -0,0 +1,25 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {addCommand} from 'mattermost-redux/actions/integrations'; + +import AddCommand from './add_command.jsx'; + +function mapStateToProps(state, ownProps) { + return { + ...ownProps, + addCommandRequest: state.requests.integrations.addCommand + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + addCommand + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(AddCommand); diff --git a/webapp/routes/route_integrations.jsx b/webapp/routes/route_integrations.jsx index c7c7497b8..dd3ebe663 100644 --- a/webapp/routes/route_integrations.jsx +++ b/webapp/routes/route_integrations.jsx @@ -74,7 +74,7 @@ export default { { path: 'add', getComponents: (location, callback) => { - System.import('components/integrations/components/add_command.jsx').then(RouteUtils.importComponentSuccess(callback)); + System.import('components/integrations/components/add_command').then(RouteUtils.importComponentSuccess(callback)); } }, { diff --git a/webapp/tests/components/integrations/__snapshots__/add_command.test.jsx.snap b/webapp/tests/components/integrations/__snapshots__/add_command.test.jsx.snap new file mode 100644 index 000000000..99e5d7ad8 --- /dev/null +++ b/webapp/tests/components/integrations/__snapshots__/add_command.test.jsx.snap @@ -0,0 +1,396 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/integrations/AddCommand should match snapshot 1`] = ` +
+ + + + + + +
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+ + + , + } + } + /> +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ + + + + + + +
+
+
+
+`; diff --git a/webapp/tests/components/integrations/add_command.test.jsx b/webapp/tests/components/integrations/add_command.test.jsx new file mode 100644 index 000000000..cf8a31e07 --- /dev/null +++ b/webapp/tests/components/integrations/add_command.test.jsx @@ -0,0 +1,30 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; + +import * as Utils from 'utils/utils.jsx'; +import AddCommand from 'components/integrations/components/add_command/add_command.jsx'; + +describe('components/integrations/AddCommand', () => { + test('should match snapshot', () => { + function emptyFunction() {} //eslint-disable-line no-empty-function + const teamId = Utils.generateId(); + + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + }); +}); -- cgit v1.2.3-1-g7c22