From be4bddc52a04da33f8ab8305c5d7bb879f9182b4 Mon Sep 17 00:00:00 2001 From: Paulo Bittencourt Date: Wed, 2 Oct 2019 10:47:24 -0400 Subject: [PATCH] MM-15220 Support generic error message in interactive dialog (#3259) --- .../interactive_dialog/interactive_dialog.js | 53 +++++++++++++++---- .../interactive_dialog.test.js | 39 +++++++++++++- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/app/screens/interactive_dialog/interactive_dialog.js b/app/screens/interactive_dialog/interactive_dialog.js index 03f70ea44..079b3a6dd 100644 --- a/app/screens/interactive_dialog/interactive_dialog.js +++ b/app/screens/interactive_dialog/interactive_dialog.js @@ -8,6 +8,7 @@ import {Navigation} from 'react-native-navigation'; import {checkDialogElementForError, checkIfErrorsMatchElements} from 'mattermost-redux/utils/integration_utils'; +import ErrorText from 'app/components/error_text'; import StatusBar from 'app/components/status_bar'; import FormattedText from 'app/components/formatted_text'; @@ -48,10 +49,13 @@ export default class InteractiveDialog extends PureComponent { this.state = { values, + error: null, errors: {}, isLandscape: this.isLandscape(), submitting: false, }; + + this.scrollView = React.createRef(); } componentDidMount() { @@ -123,17 +127,29 @@ export default class InteractiveDialog extends PureComponent { this.submitted = true; - if (!data || !data.errors || Object.keys(data.errors).length === 0) { + let hasErrors = false; + + if (data) { + if (data.errors && + Object.keys(data.errors).length >= 0 && + checkIfErrorsMatchElements(data.errors, elements) + ) { + hasErrors = true; + this.setState({errors: data.errors}); + } + + if (data.error) { + hasErrors = true; + this.setState({error: data.error}); + if (this.scrollView?.current) { + this.scrollView.current.scrollTo({x: 0, y: 0}); + } + } + } + + if (!hasErrors) { this.handleHide(); - return; } - - if (checkIfErrorsMatchElements(data.errors, elements)) { - this.setState({errors: data.errors}); - return; - } - - this.handleHide(); } notifyOnCancelIfNeeded = () => { @@ -168,13 +184,22 @@ export default class InteractiveDialog extends PureComponent { render() { const {introductionText, elements, theme} = this.props; - const {errors, isLandscape, values} = this.state; + const {error, errors, isLandscape, values} = this.state; const style = getStyleFromTheme(theme); return ( - + + {error && ( + + )} {Boolean(introductionText) && { container: { backgroundColor: changeOpacity(theme.centerChannelColor, 0.03), }, + errorContainer: { + marginTop: 15, + marginLeft: 15, + fontSize: 14, + fontWeight: 'bold', + }, scrollView: { marginBottom: 20, marginTop: 10, diff --git a/app/screens/interactive_dialog/interactive_dialog.test.js b/app/screens/interactive_dialog/interactive_dialog.test.js index dafcd2ec3..62ecb0273 100644 --- a/app/screens/interactive_dialog/interactive_dialog.test.js +++ b/app/screens/interactive_dialog/interactive_dialog.test.js @@ -7,6 +7,8 @@ import Preferences from 'mattermost-redux/constants/preferences'; import InteractiveDialog from './interactive_dialog'; +import ErrorText from 'app/components/error_text'; + describe('InteractiveDialog', () => { const baseProps = { url: 'http://mattermost.com', @@ -18,7 +20,8 @@ describe('InteractiveDialog', () => { state: 'somestate', theme: Preferences.THEMES.default, actions: { - submitInteractiveDialog: jest.fn(), + submitInteractiveDialog: jest.fn(() => ({})), + dismissModal: jest.fn(), }, componentId: 'component-id', }; @@ -140,4 +143,38 @@ describe('InteractiveDialog', () => { expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledTimes(1); expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledWith(dialog); }); + + describe('generic error message', () => { + test('should show error when submit returns an error', async () => { + const props = { + ...baseProps, + actions: { + ...baseProps.actions, + submitInteractiveDialog: async () => ({ + data: {error: 'This is an error message.'}, + }), + }, + }; + + const wrapper = shallow( + + ); + wrapper.instance().scrollView = {current: {scrollTo: jest.fn()}}; + + await wrapper.instance().handleSubmit(); + expect(wrapper.find(ErrorText).find({error: 'This is an error message.'})).toHaveLength(1); + expect(wrapper.instance().scrollView.current.scrollTo).toHaveBeenCalledWith({x: 0, y: 0}); + }); + + test('should show no error when submit does not return an error', async () => { + const wrapper = shallow( + + ); + wrapper.instance().scrollView = {current: {scrollTo: jest.fn()}}; + + await wrapper.instance().handleSubmit(); + expect(wrapper.find(ErrorText)).not.toExist(); + expect(wrapper.instance().scrollView.current.scrollTo).not.toHaveBeenCalled(); + }); + }); });