From 30f91a3814a2595dcad9a6e86bde37ac5d1957fe Mon Sep 17 00:00:00 2001 From: Theo Gkourasas Date: Mon, 16 Sep 2019 12:22:59 -0400 Subject: [PATCH] MM-15219 Added support for introduction text in interactive dialogs (#3222) * MM-15219 Added support for optional introduction text in interactive dialogs * Fixed margins in introduction text for interactive dialogs * Created DialogIntroductionText component to render the introduction text. Changed text color * Changed DialogIntroductionText component to only render if a value is supplied --- .../interactive_dialog_controller.test.js | 3 +- .../dialog_introduction_text.test.js.snap | 125 ++++++++++++++++++ .../interactive_dialog.test.js.snap | 53 ++++++++ .../dialog_introduction_text.js | 58 ++++++++ .../dialog_introduction_text.test.js | 38 ++++++ app/screens/interactive_dialog/index.js | 1 + .../interactive_dialog/interactive_dialog.js | 10 +- .../interactive_dialog.test.js | 35 +++++ 8 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 app/screens/interactive_dialog/__snapshots__/dialog_introduction_text.test.js.snap create mode 100644 app/screens/interactive_dialog/__snapshots__/interactive_dialog.test.js.snap create mode 100644 app/screens/interactive_dialog/dialog_introduction_text.js create mode 100644 app/screens/interactive_dialog/dialog_introduction_text.test.js diff --git a/app/components/interactive_dialog_controller/interactive_dialog_controller.test.js b/app/components/interactive_dialog_controller/interactive_dialog_controller.test.js index 29bd8a573..d048b5732 100644 --- a/app/components/interactive_dialog_controller/interactive_dialog_controller.test.js +++ b/app/components/interactive_dialog_controller/interactive_dialog_controller.test.js @@ -55,7 +55,7 @@ describe('InteractiveDialogController', () => { }); }); -function getBaseProps(triggerId, elements) { +function getBaseProps(triggerId, elements, introductionText) { const dialogData = { dialog: { callback_id: 'somecallbackid', @@ -65,6 +65,7 @@ function getBaseProps(triggerId, elements) { state: 'somestate', submit_label: 'Submit Test', title: 'Dialog Test', + introductionText, }, trigger_id: triggerId, url: 'https://localhost:8065/dialog_submit', diff --git a/app/screens/interactive_dialog/__snapshots__/dialog_introduction_text.test.js.snap b/app/screens/interactive_dialog/__snapshots__/dialog_introduction_text.test.js.snap new file mode 100644 index 000000000..4ecbe6655 --- /dev/null +++ b/app/screens/interactive_dialog/__snapshots__/dialog_introduction_text.test.js.snap @@ -0,0 +1,125 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DialogIntroductionText should not render the component with an empty value 1`] = `null`; + +exports[`DialogIntroductionText should render the introduction text correctly 1`] = ` + + + +`; diff --git a/app/screens/interactive_dialog/__snapshots__/interactive_dialog.test.js.snap b/app/screens/interactive_dialog/__snapshots__/interactive_dialog.test.js.snap new file mode 100644 index 000000000..a2753b402 --- /dev/null +++ b/app/screens/interactive_dialog/__snapshots__/interactive_dialog.test.js.snap @@ -0,0 +1,53 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InteractiveDialog should display introduction text if present 1`] = ` + + + + + + +`; diff --git a/app/screens/interactive_dialog/dialog_introduction_text.js b/app/screens/interactive_dialog/dialog_introduction_text.js new file mode 100644 index 000000000..db54b075f --- /dev/null +++ b/app/screens/interactive_dialog/dialog_introduction_text.js @@ -0,0 +1,58 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {PureComponent} from 'react'; +import {View} from 'react-native'; +import PropTypes from 'prop-types'; + +import Markdown from 'app/components/markdown'; + +import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown'; +import {makeStyleSheetFromTheme} from 'app/utils/theme'; + +export default class DialogIntroductionText extends PureComponent { + static propTypes = { + value: PropTypes.string.isRequired, + theme: PropTypes.object.isRequired, + }; + + render() { + const { + value, + theme, + } = this.props; + + if (value) { + const style = getStyleFromTheme(theme); + const blockStyles = getMarkdownBlockStyles(theme); + const textStyles = getMarkdownTextStyles(theme); + + return ( + + + + ); + } + + return null; + } +} + +const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { + return { + introductionTextView: { + marginHorizontal: 15, + }, + introductionText: { + color: theme.centerChannelColor, + }, + }; +}); diff --git a/app/screens/interactive_dialog/dialog_introduction_text.test.js b/app/screens/interactive_dialog/dialog_introduction_text.test.js new file mode 100644 index 000000000..bf54f5353 --- /dev/null +++ b/app/screens/interactive_dialog/dialog_introduction_text.test.js @@ -0,0 +1,38 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; + +import Preferences from 'mattermost-redux/constants/preferences'; + +import DialogIntroductionText from './dialog_introduction_text.js'; + +describe('DialogIntroductionText', () => { + const baseProps = { + theme: Preferences.THEMES.default, + value: '**bold** *italic* [link](https://mattermost.com/)
[link target blank](!https://mattermost.com/)', + }; + + test('should render the introduction text correctly', () => { + const wrapper = shallow( + + ); + + expect(wrapper.getElement()).toMatchSnapshot(); + }); + + test('should not render the component with an empty value', () => { + baseProps.value = ''; + + const wrapper = shallow( + + ); + + expect(wrapper.getElement()).toMatchSnapshot(); + }); +}); diff --git a/app/screens/interactive_dialog/index.js b/app/screens/interactive_dialog/index.js index 6170b7b6e..6b4fa1672 100644 --- a/app/screens/interactive_dialog/index.js +++ b/app/screens/interactive_dialog/index.js @@ -19,6 +19,7 @@ function mapStateToProps(state) { callbackId: data.dialog.callback_id, elements: data.dialog.elements, title: data.dialog.title, + introductionText: data.dialog.introduction_text, iconUrl: data.dialog.icon_url, submitLabel: data.dialog.submit_label, notifyOnCancel: data.dialog.notify_on_cancel, diff --git a/app/screens/interactive_dialog/interactive_dialog.js b/app/screens/interactive_dialog/interactive_dialog.js index a682c1648..e6bd696bb 100644 --- a/app/screens/interactive_dialog/interactive_dialog.js +++ b/app/screens/interactive_dialog/interactive_dialog.js @@ -14,11 +14,13 @@ import StatusBar from 'app/components/status_bar'; import FormattedText from 'app/components/formatted_text'; import DialogElement from './dialog_element.js'; +import DialogIntroductionText from './dialog_introduction_text.js'; export default class InteractiveDialog extends PureComponent { static propTypes = { url: PropTypes.string.isRequired, callbackId: PropTypes.string, + introductionText: PropTypes.string, elements: PropTypes.arrayOf(PropTypes.object), notifyOnCancel: PropTypes.bool, state: PropTypes.string, @@ -150,13 +152,19 @@ export default class InteractiveDialog extends PureComponent { } render() { - const {elements, theme} = this.props; + const {introductionText, elements, theme} = this.props; const style = getStyleFromTheme(theme); return ( + {Boolean(introductionText) && + + } {elements && elements.map((e) => { return ( { expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledTimes(1); expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledWith(dialog); }); + + test('should display introduction text if present', async () => { + baseProps.introductionText = '**Some** _introduction_ text'; + + const wrapper = shallow( + , + ); + + expect(wrapper.getElement()).toMatchSnapshot(); + }); + + test('introduction text should not affect submission', async () => { + baseProps.introductionText = '**Some** _introduction_ text'; + + const wrapper = shallow( + , + ); + + const dialog = { + url: baseProps.url, + callback_id: baseProps.callbackId, + state: baseProps.state, + submission: {}, + }; + + wrapper.instance().handleSubmit(); + expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledTimes(1); + expect(baseProps.actions.submitInteractiveDialog).toHaveBeenCalledWith(dialog); + }); });