diff --git a/app/actions/views/command.js b/app/actions/views/command.js
index cad181e88..a51266e25 100644
--- a/app/actions/views/command.js
+++ b/app/actions/views/command.js
@@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import {IntegrationTypes} from 'mattermost-redux/action_types';
import {executeCommand as executeCommandService} from 'mattermost-redux/actions/integrations';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
@@ -27,6 +28,12 @@ export function executeCommand(message, channelId, rootId) {
const cmd = msg.substring(0, cmdLength).toLowerCase();
msg = cmd + msg.substring(cmdLength, msg.length);
- return executeCommandService(msg, args)(dispatch, getState);
+ const {data, error} = await dispatch(executeCommandService(msg, args));
+
+ if (data.trigger_id) {
+ dispatch({type: IntegrationTypes.RECEIVED_DIALOG_TRIGGER_ID, data: data.trigger_id});
+ }
+
+ return {data, error};
};
}
diff --git a/app/actions/views/post.js b/app/actions/views/post.js
index 1d7dc9827..b840c8337 100644
--- a/app/actions/views/post.js
+++ b/app/actions/views/post.js
@@ -41,7 +41,7 @@ export function sendAddToChannelEphemeralPost(user, addedUsername, message, chan
};
}
-export function setMenuActionSelector(dataSource, onSelect, options) {
+export function setAutocompleteSelector(dataSource, onSelect, options) {
return {
type: ViewTypes.SELECTED_ACTION_MENU,
data: {
@@ -52,14 +52,14 @@ export function setMenuActionSelector(dataSource, onSelect, options) {
};
}
-export function selectAttachmentMenuAction(postId, actionId, displayText, value) {
+export function selectAttachmentMenuAction(postId, actionId, text, value) {
return (dispatch) => {
dispatch({
type: ViewTypes.SUBMIT_ATTACHMENT_MENU_ACTION,
postId,
data: {
[actionId]: {
- displayText,
+ text,
value,
},
},
diff --git a/app/components/autocomplete_selector/autocomplete_selector.js b/app/components/autocomplete_selector/autocomplete_selector.js
new file mode 100644
index 000000000..49cbc44d6
--- /dev/null
+++ b/app/components/autocomplete_selector/autocomplete_selector.js
@@ -0,0 +1,289 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {PureComponent} from 'react';
+import {Text, TouchableOpacity, View} from 'react-native';
+import PropTypes from 'prop-types';
+import {intlShape} from 'react-intl';
+import Icon from 'react-native-vector-icons/FontAwesome';
+
+import {displayUsername} from 'mattermost-redux/utils/user_utils';
+
+import FormattedText from 'app/components/formatted_text';
+import {preventDoubleTap} from 'app/utils/tap';
+import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
+import {ViewTypes} from 'app/constants';
+
+export default class AutocompleteSelector extends PureComponent {
+ static propTypes = {
+ actions: PropTypes.shape({
+ setAutocompleteSelector: PropTypes.func.isRequired,
+ }).isRequired,
+ label: PropTypes.string,
+ placeholder: PropTypes.string.isRequired,
+ dataSource: PropTypes.string,
+ options: PropTypes.arrayOf(PropTypes.object),
+ selected: PropTypes.object,
+ optional: PropTypes.bool,
+ showRequiredAsterisk: PropTypes.bool,
+ teammateNameDisplay: PropTypes.string,
+ theme: PropTypes.object.isRequired,
+ navigator: PropTypes.object,
+ onSelected: PropTypes.func,
+ helpText: PropTypes.node,
+ errorText: PropTypes.node,
+ roundedBorders: PropTypes.bool,
+ };
+
+ static contextTypes = {
+ intl: intlShape,
+ };
+
+ static defaultProps = {
+ optional: false,
+ showRequiredAsterisk: false,
+ roundedBorders: true,
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ selectedText: null,
+ };
+ }
+
+ static getDerivedStateFromProps(props, state) {
+ if (props.selected && props.selected !== state.selected) {
+ return {
+ selectedText: props.selected.text,
+ selected: props.selected,
+ };
+ }
+
+ return null;
+ }
+
+ handleSelect = (selected) => {
+ if (!selected) {
+ return;
+ }
+
+ const {
+ dataSource,
+ teammateNameDisplay,
+ } = this.props;
+
+ let selectedText;
+ let selectedValue;
+ if (dataSource === ViewTypes.DATA_SOURCE_USERS) {
+ selectedText = displayUsername(selected, teammateNameDisplay);
+ selectedValue = selected.id;
+ } else if (dataSource === ViewTypes.DATA_SOURCE_CHANNELS) {
+ selectedText = selected.display_name;
+ selectedValue = selected.id;
+ } else {
+ selectedText = selected.text;
+ selectedValue = selected.value;
+ }
+
+ this.setState({selectedText});
+
+ if (this.props.onSelected) {
+ this.props.onSelected({text: selectedText, value: selectedValue});
+ }
+ };
+
+ goToSelectorScreen = preventDoubleTap(() => {
+ const {formatMessage} = this.context.intl;
+ const {navigator, theme, actions, dataSource, options, placeholder} = this.props;
+
+ actions.setAutocompleteSelector(dataSource, this.handleSelect, options);
+
+ navigator.push({
+ backButtonTitle: '',
+ screen: 'SelectorScreen',
+ title: placeholder || formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'}),
+ animated: true,
+ navigatorStyle: {
+ navBarTextColor: theme.sidebarHeaderTextColor,
+ navBarBackgroundColor: theme.sidebarHeaderBg,
+ navBarButtonColor: theme.sidebarHeaderTextColor,
+ screenBackgroundColor: theme.centerChannelBg,
+ },
+ });
+ });
+
+ render() {
+ const {intl} = this.context;
+ const {
+ placeholder,
+ theme,
+ label,
+ helpText,
+ errorText,
+ optional,
+ showRequiredAsterisk,
+ roundedBorders,
+ } = this.props;
+ const {selectedText} = this.state;
+ const style = getStyleSheet(theme);
+
+ let text = placeholder || intl.formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'});
+ let selectedStyle = style.dropdownPlaceholder;
+
+ if (selectedText) {
+ text = selectedText;
+ selectedStyle = style.dropdownSelected;
+ }
+
+ let inputStyle = style.input;
+ if (roundedBorders) {
+ inputStyle = style.roundedInput;
+ }
+
+ let optionalContent;
+ let asterisk;
+ if (optional) {
+ optionalContent = (
+
+ );
+ } else if (showRequiredAsterisk) {
+ asterisk = {' *'};
+ }
+
+ let labelContent;
+ if (label) {
+ labelContent = (
+
+
+ {label}
+
+ {asterisk}
+ {optionalContent}
+
+
+ );
+ }
+
+ let helpTextContent;
+ if (helpText) {
+ helpTextContent = (
+
+ {helpText}
+
+ );
+ }
+
+ let errorTextContent;
+ if (errorText) {
+ errorTextContent = (
+
+ {errorText}
+
+ );
+ }
+
+ return (
+
+ {labelContent}
+
+
+
+ {text}
+
+
+
+
+ {helpTextContent}
+ {errorTextContent}
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ const input = {
+ borderWidth: 1,
+ borderColor: changeOpacity(theme.centerChannelColor, 0.1),
+ backgroundColor: changeOpacity(theme.centerChannelBg, 0.9),
+ paddingLeft: 10,
+ paddingRight: 30,
+ paddingVertical: 7,
+ height: 40,
+ };
+
+ return {
+ container: {
+ width: '100%',
+ marginBottom: 2,
+ marginRight: 8,
+ marginTop: 10,
+ },
+ roundedInput: {
+ ...input,
+ borderRadius: 5,
+ },
+ input,
+ dropdownPlaceholder: {
+ top: 3,
+ marginLeft: 5,
+ color: changeOpacity(theme.centerChannelColor, 0.5),
+ },
+ dropdownSelected: {
+ top: 3,
+ marginLeft: 5,
+ color: theme.centerChannelColor,
+ },
+ icon: {
+ position: 'absolute',
+ top: 13,
+ right: 12,
+ },
+ labelContainer: {
+ flexDirection: 'row',
+ marginTop: 15,
+ marginBottom: 10,
+ },
+ label: {
+ fontSize: 14,
+ color: theme.centerChannelColor,
+ marginLeft: 15,
+ },
+ optional: {
+ color: changeOpacity(theme.centerChannelColor, 0.5),
+ fontSize: 14,
+ marginLeft: 5,
+ },
+ helpText: {
+ fontSize: 12,
+ color: changeOpacity(theme.centerChannelColor, 0.5),
+ marginHorizontal: 15,
+ marginVertical: 10,
+ },
+ errorText: {
+ fontSize: 12,
+ color: theme.errorTextColor,
+ marginHorizontal: 15,
+ marginVertical: 10,
+ },
+ asterisk: {
+ color: theme.errorTextColor,
+ fontSize: 14,
+ },
+ };
+});
diff --git a/app/components/autocomplete_selector/index.js b/app/components/autocomplete_selector/index.js
new file mode 100644
index 000000000..dc77afea9
--- /dev/null
+++ b/app/components/autocomplete_selector/index.js
@@ -0,0 +1,28 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {bindActionCreators} from 'redux';
+import {connect} from 'react-redux';
+
+import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
+
+import {setAutocompleteSelector} from 'app/actions/views/post';
+
+import AutocompleteSelector from './autocomplete_selector';
+
+function mapStateToProps(state) {
+ return {
+ teammateNameDisplay: getTeammateNameDisplaySetting(state),
+ theme: getTheme(state),
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ setAutocompleteSelector,
+ }, dispatch),
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(AutocompleteSelector);
diff --git a/app/components/interactive_dialog_controller/index.js b/app/components/interactive_dialog_controller/index.js
new file mode 100644
index 000000000..10d93d094
--- /dev/null
+++ b/app/components/interactive_dialog_controller/index.js
@@ -0,0 +1,15 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {connect} from 'react-redux';
+
+import InteractiveDialogController from './interactive_dialog_controller';
+
+function mapStateToProps(state) {
+ return {
+ triggerId: state.entities.integrations.dialogTriggerId,
+ dialog: state.entities.integrations.dialog || {},
+ };
+}
+
+export default connect(mapStateToProps)(InteractiveDialogController);
diff --git a/app/components/interactive_dialog_controller/interactive_dialog_controller.js b/app/components/interactive_dialog_controller/interactive_dialog_controller.js
new file mode 100644
index 000000000..fcba9b347
--- /dev/null
+++ b/app/components/interactive_dialog_controller/interactive_dialog_controller.js
@@ -0,0 +1,76 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
+
+export default class InteractiveDialogController extends PureComponent {
+ static propTypes = {
+ triggerId: PropTypes.string,
+ dialog: PropTypes.object,
+ navigator: PropTypes.object,
+ theme: PropTypes.object,
+ };
+
+ constructor(props) {
+ super(props);
+
+ MaterialIcon.getImageSource('close', 20, props.theme.sidebarHeaderTextColor).then((source) => {
+ this.closeButton = source;
+ });
+ }
+
+ componentDidUpdate(prevProps) {
+ const triggerId = this.props.triggerId;
+ if (!triggerId) {
+ return;
+ }
+
+ const dialogData = this.props.dialog || {};
+ const prevDialogData = prevProps.dialog || {};
+ if (prevProps.triggerId === triggerId && dialogData.trigger_id === prevDialogData.trigger_id) {
+ return;
+ }
+
+ if (dialogData.trigger_id !== triggerId) {
+ return;
+ }
+
+ if (!dialogData.trigger_id || !dialogData.dialog) {
+ return;
+ }
+
+ const theme = this.props.theme;
+
+ this.props.navigator.showModal({
+ backButtonTitle: '',
+ screen: 'InteractiveDialog',
+ title: dialogData.dialog.title,
+ animated: true,
+ navigatorStyle: {
+ navBarTextColor: theme.sidebarHeaderTextColor,
+ navBarBackgroundColor: theme.sidebarHeaderBg,
+ navBarButtonColor: theme.sidebarHeaderTextColor,
+ screenBackgroundColor: theme.centerChannelBg,
+ },
+ navigatorButtons: {
+ leftButtons: [{
+ id: 'close-dialog',
+ icon: this.closeButton,
+ }],
+ rightButtons: [
+ {
+ id: 'submit-dialog',
+ showAsAction: 'always',
+ title: dialogData.dialog.submit_label,
+ },
+ ],
+ },
+ });
+ }
+
+ render() {
+ return null;
+ }
+}
diff --git a/app/components/message_attachments/action_menu/action_menu.js b/app/components/message_attachments/action_menu/action_menu.js
index 57f0313fc..8a0b1d461 100644
--- a/app/components/message_attachments/action_menu/action_menu.js
+++ b/app/components/message_attachments/action_menu/action_menu.js
@@ -2,23 +2,14 @@
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
-import {Text, TouchableOpacity, View} from 'react-native';
import PropTypes from 'prop-types';
-import {intlShape} from 'react-intl';
-import Icon from 'react-native-vector-icons/FontAwesome';
-import {displayUsername} from 'mattermost-redux/utils/user_utils';
-
-import FormattedText from 'app/components/formatted_text';
-import {preventDoubleTap} from 'app/utils/tap';
-import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
-import {ViewTypes} from 'app/constants';
+import AutocompleteSelector from 'app/components/autocomplete_selector';
export default class ActionMenu extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
selectAttachmentMenuAction: PropTypes.func.isRequired,
- setMenuActionSelector: PropTypes.func.isRequired,
}).isRequired,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
@@ -26,34 +17,9 @@ export default class ActionMenu extends PureComponent {
options: PropTypes.arrayOf(PropTypes.object),
postId: PropTypes.string.isRequired,
selected: PropTypes.object,
- teammateNameDisplay: PropTypes.string,
- theme: PropTypes.object.isRequired,
navigator: PropTypes.object,
};
- static contextTypes = {
- intl: intlShape,
- };
-
- constructor(props) {
- super(props);
-
- this.state = {
- selectedText: null,
- };
- }
-
- static getDerivedStateFromProps(props, state) {
- if (props.selected && props.selected !== state.selected) {
- return {
- selectedText: props.selected.displayText,
- selected: props.selected,
- };
- }
-
- return null;
- }
-
handleSelect = (selected) => {
if (!selected) {
return;
@@ -61,157 +27,31 @@ export default class ActionMenu extends PureComponent {
const {
actions,
- dataSource,
id,
postId,
- teammateNameDisplay,
} = this.props;
- let selectedText;
- let selectedValue;
- if (dataSource === ViewTypes.DATA_SOURCE_USERS) {
- selectedText = displayUsername(selected, teammateNameDisplay);
- selectedValue = selected.id;
- } else if (dataSource === ViewTypes.DATA_SOURCE_CHANNELS) {
- selectedText = selected.display_name;
- selectedValue = selected.id;
- } else {
- selectedText = selected.text;
- selectedValue = selected.value;
- }
-
- this.setState({selectedText});
-
- actions.selectAttachmentMenuAction(postId, id, selectedText, selectedValue);
+ actions.selectAttachmentMenuAction(postId, id, selected.text, selected.value);
};
- goToMenuActionSelector = preventDoubleTap(() => {
- const {formatMessage} = this.context.intl;
- const {navigator, theme, actions, dataSource, options, name} = this.props;
-
- actions.setMenuActionSelector(dataSource, this.handleSelect, options);
-
- navigator.push({
- backButtonTitle: '',
- screen: 'MenuActionSelector',
- title: name || formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'}),
- animated: true,
- navigatorStyle: {
- navBarTextColor: theme.sidebarHeaderTextColor,
- navBarBackgroundColor: theme.sidebarHeaderBg,
- navBarButtonColor: theme.sidebarHeaderTextColor,
- screenBackgroundColor: theme.centerChannelBg,
- },
- });
- });
-
render() {
- const {intl} = this.context;
- const {name, theme, id} = this.props;
- const {selectedText} = this.state;
- const style = getStyleSheet(theme);
-
- let text = name || intl.formatMessage({id: 'mobile.action_menu.select', defaultMessage: 'Select an option'});
- let selectedStyle = style.dropdownPlaceholder;
- let submitted;
- if (selectedText) {
- text = selectedText;
- selectedStyle = style.dropdownSelected;
- submitted = (
-
-
-
-
- );
- } else {
- submitted = ;
- }
+ const {
+ name,
+ dataSource,
+ selected,
+ options,
+ navigator,
+ } = this.props;
return (
-
-
-
-
- {text}
-
-
-
-
- {submitted}
-
+
);
}
-}
-
-const getStyleSheet = makeStyleSheetFromTheme((theme) => {
- return {
- flex: {
- flex: 1,
- },
- container: {
- width: '100%',
- flex: 1,
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- },
- input: {
- flex: 1,
- position: 'relative',
- borderWidth: 1,
- borderRadius: 5,
- borderColor: changeOpacity(theme.centerChannelColor, 0.1),
- backgroundColor: changeOpacity(theme.centerChannelBg, 0.9),
- marginBottom: 2,
- marginRight: 8,
- marginTop: 10,
- paddingLeft: 10,
- paddingRight: 30,
- paddingVertical: 7,
- },
- dropdownPlaceholder: {
- color: changeOpacity(theme.centerChannelColor, 0.5),
- },
- dropdownSelected: {
- color: theme.centerChannelColor,
- },
- icon: {
- position: 'absolute',
- top: 10,
- right: 12,
- },
- blankSubmittedContainer: {
- width: 80,
- height: 23,
- },
- submittedContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- marginTop: 10,
- marginBottom: 2,
- },
- submittedText: {
- marginLeft: 5,
- color: '#287B39',
- },
- };
-});
+}
\ No newline at end of file
diff --git a/app/components/message_attachments/action_menu/index.js b/app/components/message_attachments/action_menu/index.js
index 0a0ee77c5..913d338fb 100644
--- a/app/components/message_attachments/action_menu/index.js
+++ b/app/components/message_attachments/action_menu/index.js
@@ -4,9 +4,7 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
-
-import {setMenuActionSelector, selectAttachmentMenuAction} from 'app/actions/views/post';
+import {selectAttachmentMenuAction} from 'app/actions/views/post';
import ActionMenu from './action_menu';
@@ -16,8 +14,6 @@ function mapStateToProps(state, ownProps) {
return {
selected,
- teammateNameDisplay: getTeammateNameDisplaySetting(state),
- theme: getTheme(state),
};
}
@@ -25,7 +21,6 @@ function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
selectAttachmentMenuAction,
- setMenuActionSelector,
}, dispatch),
};
}
diff --git a/app/components/message_attachments/attachment_actions.js b/app/components/message_attachments/attachment_actions.js
index 186c95b54..4c9aa6195 100644
--- a/app/components/message_attachments/attachment_actions.js
+++ b/app/components/message_attachments/attachment_actions.js
@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
-import {StyleSheet, View} from 'react-native';
import PropTypes from 'prop-types';
import ActionMenu from './action_menu';
@@ -61,16 +60,6 @@ export default class AttachmentActions extends PureComponent {
}
});
- return (
-
- {content}
-
- );
+ return content;
}
-}
-
-const style = StyleSheet.create({
- container: {
- flex: 1,
- },
-});
+}
\ No newline at end of file
diff --git a/app/components/widgets/settings/text_setting.js b/app/components/widgets/settings/text_setting.js
new file mode 100644
index 000000000..f2bf76df0
--- /dev/null
+++ b/app/components/widgets/settings/text_setting.js
@@ -0,0 +1,235 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {
+ View,
+ Text,
+ TextInput,
+ Platform,
+} from 'react-native';
+
+import FormattedText from 'app/components/formatted_text';
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+
+export default class TextSetting extends PureComponent {
+ static propTypes = {
+ id: PropTypes.string.isRequired,
+ label: PropTypes.oneOfType([
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ defaultMessage: PropTypes.string.isRequired,
+ }),
+ PropTypes.string,
+ ]),
+ placeholder: PropTypes.string,
+ helpText: PropTypes.node,
+ errorText: PropTypes.node,
+ disabled: PropTypes.bool,
+ disabledText: PropTypes.string,
+ maxLength: PropTypes.number,
+ optional: PropTypes.bool,
+ theme: PropTypes.object.isRequired,
+ onChange: PropTypes.func.isRequired,
+ value: PropTypes.string.isRequired,
+ multiline: PropTypes.bool,
+ showRequiredAsterisk: PropTypes.bool,
+ keyboardType: PropTypes.oneOf([
+ 'default',
+ 'number-pad',
+ 'decimal-pad',
+ 'numeric',
+ 'email-address',
+ 'phone-pad',
+ 'url',
+ ]),
+ };
+
+ static defaultProps = {
+ optional: false,
+ disabled: false,
+ multiline: false,
+ showRequiredAsterisk: false,
+ keyboardType: 'default',
+ };
+
+ onChangeText = (value) => {
+ const {id, onChange} = this.props;
+ onChange(id, value);
+ };
+
+ render() {
+ const {
+ theme,
+ label,
+ placeholder,
+ helpText,
+ optional,
+ disabled,
+ disabledText,
+ errorText,
+ value,
+ multiline,
+ showRequiredAsterisk,
+ } = this.props;
+ const style = getStyleSheet(theme);
+
+ let labelContent = label;
+ if (label && label.defaultMessage) {
+ labelContent = (
+
+ );
+ } else if (typeof label === 'string') {
+ labelContent = {label};
+ }
+
+ let optionalContent;
+ let asterisk;
+ if (optional) {
+ optionalContent = (
+
+ );
+ } else if (showRequiredAsterisk) {
+ asterisk = {' *'};
+ }
+
+ let {keyboardType} = this.props;
+ if (Platform.OS === 'android' && keyboardType === 'url') {
+ keyboardType = 'default';
+ }
+
+ let inputStyle = style.input;
+ if (multiline) {
+ inputStyle = style.multiline;
+ }
+
+ let helpTextContent;
+ if (helpText) {
+ helpTextContent = (
+
+ {helpText}
+
+ );
+ }
+
+ let errorTextContent;
+ if (errorText) {
+ errorTextContent = (
+
+ {errorText}
+
+ );
+ }
+
+ let disabledTextContent;
+ if (disabled && disabledText) {
+ disabledTextContent = (
+
+ {disabledText}
+
+ );
+ }
+
+ return (
+
+
+ {labelContent}
+ {asterisk}
+ {optionalContent}
+
+
+
+
+
+
+ {disabledTextContent}
+ {helpTextContent}
+ {errorTextContent}
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ const input = {
+ color: theme.centerChannelColor,
+ fontSize: 14,
+ paddingHorizontal: 15,
+ };
+
+ return {
+ inputContainer: {
+ borderTopWidth: 1,
+ borderBottomWidth: 1,
+ borderTopColor: changeOpacity(theme.centerChannelColor, 0.1),
+ borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1),
+ backgroundColor: '#fff',
+ },
+ input: {
+ ...input,
+ height: 40,
+ },
+ multiline: {
+ ...input,
+ paddingTop: 10,
+ paddingBottom: 13,
+ height: 125,
+ },
+ disabled: {
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
+ },
+ title: {
+ fontSize: 14,
+ color: theme.centerChannelColor,
+ marginLeft: 15,
+ },
+ titleContainer: {
+ flexDirection: 'row',
+ marginTop: 15,
+ marginBottom: 10,
+ },
+ optional: {
+ color: changeOpacity(theme.centerChannelColor, 0.5),
+ fontSize: 14,
+ marginLeft: 5,
+ },
+ helpText: {
+ fontSize: 12,
+ color: changeOpacity(theme.centerChannelColor, 0.5),
+ marginHorizontal: 15,
+ marginTop: 10,
+ },
+ errorText: {
+ fontSize: 12,
+ color: theme.errorTextColor,
+ marginHorizontal: 15,
+ marginTop: 10,
+ },
+ asterisk: {
+ color: theme.errorTextColor,
+ fontSize: 14,
+ },
+ };
+});
diff --git a/app/components/widgets/settings/text_setting.test.js b/app/components/widgets/settings/text_setting.test.js
new file mode 100644
index 000000000..1cb293c3a
--- /dev/null
+++ b/app/components/widgets/settings/text_setting.test.js
@@ -0,0 +1,29 @@
+// 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 TextSetting from './text_setting.js';
+
+describe('components/widgets/settings/TextSetting', () => {
+ const theme = Preferences.THEMES.default;
+ test('onChange', () => {
+ const onChange = jest.fn();
+ const wrapper = shallow(
+
+ );
+
+ wrapper.instance().onChangeText('somenewvalue');
+
+ expect(onChange).toHaveBeenCalledTimes(1);
+ expect(onChange).toHaveBeenCalledWith('string.id', 'somenewvalue');
+ });
+});
diff --git a/app/screens/channel/channel.js b/app/screens/channel/channel.js
index 6e2ec37e2..09196d527 100644
--- a/app/screens/channel/channel.js
+++ b/app/screens/channel/channel.js
@@ -14,6 +14,7 @@ import {
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
+import InteractiveDialogController from 'app/components/interactive_dialog_controller';
import EmptyToolbar from 'app/components/start/empty_toolbar';
import ChannelLoader from 'app/components/channel_loader';
import MainSidebar from 'app/components/sidebars/main';
@@ -336,6 +337,10 @@ export default class Channel extends PureComponent {
{LocalConfig.EnableMobileClientUpgrade && }
+
);
}
diff --git a/app/screens/edit_profile/edit_profile.js b/app/screens/edit_profile/edit_profile.js
index ff40c1e3c..8c38d2e32 100644
--- a/app/screens/edit_profile/edit_profile.js
+++ b/app/screens/edit_profile/edit_profile.js
@@ -16,6 +16,7 @@ import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
+import TextSetting from 'app/components/widgets/settings/text_setting';
import Loading from 'app/components/loading';
import ErrorText from 'app/components/error_text';
import StatusBar from 'app/components/status_bar/index';
@@ -24,8 +25,6 @@ import AttachmentButton from 'app/components/attachment_button';
import mattermostBucket from 'app/mattermost_bucket';
import LocalConfig from 'assets/config';
-import EditProfileItem from './edit_profile_item';
-
const holders = {
firstName: {
id: t('user.settings.general.firstName'),
@@ -221,7 +220,8 @@ export default class EditProfile extends PureComponent {
return RNFetchBlob.config(options).fetch('POST', `${Client4.getUserRoute(currentUser.id)}/image`, headers, [fileInfo]);
};
- updateField = (field) => {
+ updateField = (id, name) => {
+ const field = {[id]: name};
this.setState(field, () => {
this.emitCanUpdateAccount(this.canUpdate(field));
});
@@ -250,15 +250,15 @@ export default class EditProfile extends PureComponent {
(service === 'saml' && config.SamlFirstNameAttributeSet === 'true');
return (
-
@@ -276,15 +276,15 @@ export default class EditProfile extends PureComponent {
return (
-
@@ -299,16 +299,16 @@ export default class EditProfile extends PureComponent {
const disabled = currentUser.auth_service !== '';
return (
-
@@ -368,12 +368,12 @@ export default class EditProfile extends PureComponent {
return (
-
@@ -391,16 +391,16 @@ export default class EditProfile extends PureComponent {
(service === 'saml' && config.SamlNicknameAttributeSet === 'true');
return (
-
@@ -416,16 +416,16 @@ export default class EditProfile extends PureComponent {
const disabled = (service === 'ldap' || service === 'saml') && config.PositionAttribute === 'true';
return (
-
diff --git a/app/screens/edit_profile/edit_profile_item.js b/app/screens/edit_profile/edit_profile_item.js
deleted file mode 100644
index 656833be1..000000000
--- a/app/screens/edit_profile/edit_profile_item.js
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import React, {PureComponent} from 'react';
-import PropTypes from 'prop-types';
-import {
- View,
- Text,
- TextInput,
-} from 'react-native';
-
-import FormattedText from 'app/components/formatted_text';
-import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-
-export default class AccountSettingsItem extends PureComponent {
- static propTypes = {
- disabled: PropTypes.bool,
- field: PropTypes.string.isRequired,
- format: PropTypes.shape({
- id: PropTypes.string.isRequired,
- defaultMessage: PropTypes.string.isRequired,
- }),
- helpText: PropTypes.string,
- maxLength: PropTypes.number,
- optional: PropTypes.bool,
- theme: PropTypes.object.isRequired,
- updateValue: PropTypes.func.isRequired,
- value: PropTypes.string.isRequired,
- };
-
- static defaultProps = {
- optional: false,
- disabled: false,
- };
-
- onChangeText = (value) => {
- const {field, updateValue} = this.props;
- updateValue({[field]: value});
- };
-
- render() {
- const {
- theme,
- format,
- helpText,
- optional,
- disabled,
- value,
- } = this.props;
- const style = getStyleSheet(theme);
-
- return (
-
-
-
- {optional && (
-
- )}
-
-
-
-
-
- {disabled &&
-
- {helpText}
-
- }
-
-
- );
- }
-}
-
-const getStyleSheet = makeStyleSheetFromTheme((theme) => {
- return {
- inputContainer: {
- borderTopWidth: 1,
- borderBottomWidth: 1,
- borderTopColor: changeOpacity(theme.centerChannelColor, 0.1),
- borderBottomColor: changeOpacity(theme.centerChannelColor, 0.1),
- backgroundColor: theme.centerChannelBg,
- },
- input: {
- color: theme.centerChannelColor,
- fontSize: 14,
- height: 40,
- paddingHorizontal: 15,
- },
- disabled: {
- backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
- },
- title: {
- fontSize: 14,
- color: theme.centerChannelColor,
- marginLeft: 15,
- },
- titleContainer15: {
- flexDirection: 'row',
- marginTop: 15,
- },
- optional: {
- color: changeOpacity(theme.centerChannelColor, 0.5),
- fontSize: 14,
- marginLeft: 5,
- },
- helpText: {
- fontSize: 12,
- color: changeOpacity(theme.centerChannelColor, 0.5),
- marginHorizontal: 15,
- marginVertical: 10,
- },
- };
-});
diff --git a/app/screens/index.js b/app/screens/index.js
index a6b3986bc..5722c3155 100644
--- a/app/screens/index.js
+++ b/app/screens/index.js
@@ -30,10 +30,10 @@ export function registerScreens(store, Provider) {
Navigation.registerComponent('FlaggedPosts', () => wrapWithContextProvider(require('app/screens/flagged_posts').default), store, Provider);
Navigation.registerComponent('ForgotPassword', () => wrapWithContextProvider(require('app/screens/forgot_password').default), store, Provider);
Navigation.registerComponent('ImagePreview', () => wrapWithContextProvider(require('app/screens/image_preview').default), store, Provider);
+ Navigation.registerComponent('InteractiveDialog', () => wrapWithContextProvider(require('app/screens/interactive_dialog').default), store, Provider);
Navigation.registerComponent('Login', () => wrapWithContextProvider(require('app/screens/login').default), store, Provider);
Navigation.registerComponent('LoginOptions', () => wrapWithContextProvider(require('app/screens/login_options').default), store, Provider);
Navigation.registerComponent('LongPost', () => wrapWithContextProvider(require('app/screens/long_post').default), store, Provider);
- Navigation.registerComponent('MenuActionSelector', () => wrapWithContextProvider(require('app/screens/menu_action_selector').default), store, Provider);
Navigation.registerComponent('MFA', () => wrapWithContextProvider(require('app/screens/mfa').default), store, Provider);
Navigation.registerComponent('MoreChannels', () => wrapWithContextProvider(require('app/screens/more_channels').default), store, Provider);
Navigation.registerComponent('MoreDirectMessages', () => wrapWithContextProvider(require('app/screens/more_dms').default), store, Provider);
@@ -52,6 +52,7 @@ export function registerScreens(store, Provider) {
Navigation.registerComponent('RecentMentions', () => wrapWithContextProvider(require('app/screens/recent_mentions').default), store, Provider);
Navigation.registerComponent('Root', () => require('app/screens/root').default, store, Provider);
Navigation.registerComponent('Search', () => wrapWithContextProvider(require('app/screens/search').default), store, Provider);
+ Navigation.registerComponent('SelectorScreen', () => wrapWithContextProvider(require('app/screens/selector_screen').default), store, Provider);
Navigation.registerComponent('SelectServer', () => wrapWithContextProvider(SelectServer), store, Provider);
Navigation.registerComponent('SelectTeam', () => wrapWithContextProvider(require('app/screens/select_team').default), store, Provider);
Navigation.registerComponent('SelectTimezone', () => wrapWithContextProvider(require('app/screens/timezone/select_timezone').default), store, Provider);
diff --git a/app/screens/interactive_dialog/dialog_element.js b/app/screens/interactive_dialog/dialog_element.js
new file mode 100644
index 000000000..e675bcf78
--- /dev/null
+++ b/app/screens/interactive_dialog/dialog_element.js
@@ -0,0 +1,139 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+
+import TextSetting from 'app/components/widgets/settings/text_setting';
+import AutocompleteSelector from 'app/components/autocomplete_selector';
+
+const TEXT_DEFAULT_MAX_LENGTH = 150;
+const TEXTAREA_DEFAULT_MAX_LENGTH = 3000;
+
+export default class DialogElement extends PureComponent {
+ static propTypes = {
+ displayName: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ type: PropTypes.string.isRequired,
+ subtype: PropTypes.string,
+ placeholder: PropTypes.string,
+ helpText: PropTypes.string,
+ errorText: PropTypes.node,
+ maxLength: PropTypes.number,
+ dataSource: PropTypes.string,
+ optional: PropTypes.bool,
+ options: PropTypes.arrayOf(PropTypes.object),
+ value: PropTypes.any,
+ onChange: PropTypes.func,
+ navigator: PropTypes.object,
+ theme: PropTypes.object,
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ selected: null,
+ };
+ }
+
+ onChange = (name, value) => {
+ const {type, subtype, onChange} = this.props;
+ let newValue = value;
+ if (type === 'text' && subtype === 'number') {
+ newValue = parseInt(value, 10);
+ }
+ onChange(name, newValue);
+ }
+
+ handleAutocompleteSelect = (selected) => {
+ if (!selected) {
+ return;
+ }
+
+ this.setState({selected});
+
+ const {name, onChange} = this.props;
+ onChange(name, selected.value);
+ }
+
+ render() {
+ const {
+ name,
+ type,
+ subtype,
+ displayName,
+ value,
+ placeholder,
+ helpText,
+ errorText,
+ optional,
+ theme,
+ dataSource,
+ options,
+ navigator,
+ } = this.props;
+
+ let {maxLength} = this.props;
+
+ if (type === 'text' || type === 'textarea') {
+ let keyboardType = 'default';
+ let multiline = false;
+ if (type === 'text') {
+ maxLength = maxLength || TEXT_DEFAULT_MAX_LENGTH;
+
+ if (subtype === 'email') {
+ keyboardType = 'email-address';
+ } else if (subtype === 'number') {
+ keyboardType = 'numeric';
+ } else if (subtype === 'tel') {
+ keyboardType = 'phone-pad';
+ } else if (subtype === 'url') {
+ keyboardType = 'url';
+ }
+ } else {
+ multiline = true;
+ maxLength = maxLength || TEXTAREA_DEFAULT_MAX_LENGTH;
+ }
+
+ return (
+
+ );
+ } else if (type === 'select') {
+ return (
+
+ );
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/app/screens/interactive_dialog/index.js b/app/screens/interactive_dialog/index.js
new file mode 100644
index 000000000..e68b387ea
--- /dev/null
+++ b/app/screens/interactive_dialog/index.js
@@ -0,0 +1,36 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {bindActionCreators} from 'redux';
+import {connect} from 'react-redux';
+
+import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
+import {submitInteractiveDialog} from 'mattermost-redux/actions/integrations';
+
+import InteractiveDialog from './interactive_dialog';
+
+function mapStateToProps(state) {
+ const data = state.entities.integrations.dialog || {dialog: {}};
+
+ return {
+ url: data.url,
+ callbackId: data.dialog.callback_id,
+ elements: data.dialog.elements,
+ title: data.dialog.title,
+ iconUrl: data.dialog.icon_url,
+ submitLabel: data.dialog.submit_label,
+ notifyOnCancel: data.dialog.notify_on_cancel,
+ state: data.dialog.state,
+ theme: getTheme(state),
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ submitInteractiveDialog,
+ }, dispatch),
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(InteractiveDialog);
diff --git a/app/screens/interactive_dialog/interactive_dialog.js b/app/screens/interactive_dialog/interactive_dialog.js
new file mode 100644
index 000000000..8f181a89d
--- /dev/null
+++ b/app/screens/interactive_dialog/interactive_dialog.js
@@ -0,0 +1,195 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {ScrollView, View} from 'react-native';
+
+import {checkDialogElementForError, checkIfErrorsMatchElements} from 'mattermost-redux/utils/integration_utils';
+
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+
+import StatusBar from 'app/components/status_bar';
+import FormattedText from 'app/components/formatted_text';
+
+import DialogElement from './dialog_element.js';
+
+export default class InteractiveDialog extends PureComponent {
+ static propTypes = {
+ url: PropTypes.string.isRequired,
+ callbackId: PropTypes.string,
+ elements: PropTypes.arrayOf(PropTypes.object).isRequired,
+ notifyOnCancel: PropTypes.bool,
+ state: PropTypes.string,
+ navigator: PropTypes.object,
+ theme: PropTypes.object,
+ actions: PropTypes.shape({
+ submitInteractiveDialog: PropTypes.func.isRequired,
+ }).isRequired,
+ };
+
+ static defaultProps = {
+ url: '',
+ elements: [],
+ };
+
+ constructor(props) {
+ super(props);
+
+ props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
+
+ const values = {};
+ props.elements.forEach((e) => {
+ values[e.name] = e.default || null;
+ });
+
+ this.state = {
+ values,
+ errors: {},
+ submitting: false,
+ };
+ }
+
+ onNavigatorEvent = (event) => {
+ if (event.type === 'NavBarButtonPress') {
+ switch (event.id) {
+ case 'submit-dialog':
+ this.handleSubmit();
+ break;
+ case 'close-dialog':
+ this.notifyOnCancelIfNeeded();
+ this.handleHide();
+ break;
+ }
+ }
+ };
+
+ handleSubmit = async () => {
+ const {elements} = this.props;
+ const values = this.state.values;
+ const errors = {};
+ elements.forEach((elem) => {
+ const error = checkDialogElementForError(elem, values[elem.name]);
+ if (error) {
+ errors[elem.name] = (
+
+ );
+ }
+ });
+
+ this.setState({errors});
+
+ if (Object.keys(errors).length !== 0) {
+ return;
+ }
+
+ const {url, callbackId, state} = this.props;
+
+ const dialog = {
+ url,
+ callback_id: callbackId,
+ state,
+ submission: values,
+ };
+
+ const {data} = await this.props.actions.submitInteractiveDialog(dialog);
+
+ this.submitted = true;
+
+ if (!data || !data.errors || Object.keys(data.errors).length === 0) {
+ this.handleHide();
+ return;
+ }
+
+ if (checkIfErrorsMatchElements(data.errors, elements)) {
+ this.setState({errors: data.errors});
+ return;
+ }
+
+ this.handleHide();
+ }
+
+ notifyOnCancelIfNeeded = () => {
+ if (this.submitted) {
+ return;
+ }
+
+ const {url, callbackId, state, notifyOnCancel} = this.props;
+
+ if (!notifyOnCancel) {
+ return;
+ }
+
+ const dialog = {
+ url,
+ callback_id: callbackId,
+ state,
+ cancelled: true,
+ };
+
+ this.props.actions.submitInteractiveDialog(dialog);
+ }
+
+ handleHide = () => {
+ this.props.navigator.dismissModal({
+ animationType: 'slide-down',
+ });
+ }
+
+ onChange = (name, value) => {
+ const values = {...this.state.values, [name]: value};
+ this.setState({values});
+ }
+
+ render() {
+ const {elements, theme, navigator} = this.props;
+ const style = getStyleFromTheme(theme);
+
+ return (
+
+
+
+ {elements.map((e) => {
+ return (
+
+ );
+ })}
+
+
+ );
+ }
+}
+
+const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
+ return {
+ container: {
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
+ },
+ scrollView: {
+ marginBottom: 20,
+ marginTop: 10,
+ },
+ };
+});
\ No newline at end of file
diff --git a/app/screens/interactive_dialog/interactive_dialog.test.js b/app/screens/interactive_dialog/interactive_dialog.test.js
new file mode 100644
index 000000000..7f36358bb
--- /dev/null
+++ b/app/screens/interactive_dialog/interactive_dialog.test.js
@@ -0,0 +1,95 @@
+// 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 InteractiveDialog from './interactive_dialog';
+
+describe('InteractiveDialog', () => {
+ const baseProps = {
+ url: 'http://mattermost.com',
+ callbackId: 'someid',
+ elements: [
+ {type: 'text', name: 'name1', default: 'defaulttext', display_name: 'Name1'},
+ ],
+ notifyOnCancel: false,
+ state: 'somestate',
+ theme: Preferences.THEMES.default,
+ actions: {
+ submitInteractiveDialog: jest.fn(),
+ },
+ navigator: {
+ setOnNavigatorEvent: jest.fn(),
+ dismissModal: jest.fn(),
+ },
+ };
+
+ test('should set default values', async () => {
+ const wrapper = shallow(
+ ,
+ );
+
+ expect(wrapper.state().values.name1).toBe('defaulttext');
+ });
+
+ test('should submit dialog', async () => {
+ const submitInteractiveDialog = jest.fn();
+ const wrapper = shallow(
+ ,
+ );
+
+ const dialog = {
+ url: baseProps.url,
+ callback_id: baseProps.callbackId,
+ state: baseProps.state,
+ submission: {name1: 'defaulttext'},
+ };
+
+ wrapper.instance().handleSubmit();
+ expect(submitInteractiveDialog).toHaveBeenCalledTimes(1);
+ expect(submitInteractiveDialog).toHaveBeenCalledWith(dialog);
+ });
+
+ test('should submit dialog on cancel', async () => {
+ const submitInteractiveDialog = jest.fn();
+ const wrapper = shallow(
+ ,
+ );
+
+ const dialog = {
+ url: baseProps.url,
+ callback_id: baseProps.callbackId,
+ state: baseProps.state,
+ cancelled: true,
+ };
+
+ wrapper.instance().onNavigatorEvent({type: 'NavBarButtonPress', id: 'close-dialog'});
+ expect(submitInteractiveDialog).toHaveBeenCalledTimes(1);
+ expect(submitInteractiveDialog).toHaveBeenCalledWith(dialog);
+ });
+
+ test('should not submit dialog on cancel', async () => {
+ const submitInteractiveDialog = jest.fn();
+ const wrapper = shallow(
+ ,
+ );
+
+ wrapper.instance().onNavigatorEvent({type: 'NavBarButtonPress', id: 'close-dialog'});
+ expect(submitInteractiveDialog).not.toHaveBeenCalled();
+ });
+});
diff --git a/app/screens/menu_action_selector/__snapshots__/menu_action_selector.test.js.snap b/app/screens/selector_screen/__snapshots__/selector_screen.test.js.snap
similarity index 96%
rename from app/screens/menu_action_selector/__snapshots__/menu_action_selector.test.js.snap
rename to app/screens/selector_screen/__snapshots__/selector_screen.test.js.snap
index 94127201f..b5e94b672 100644
--- a/app/screens/menu_action_selector/__snapshots__/menu_action_selector.test.js.snap
+++ b/app/screens/selector_screen/__snapshots__/selector_screen.test.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`MenuActionSelector should match snapshot for channels 1`] = `
+exports[`SelectorScreen should match snapshot for channels 1`] = `
`;
-exports[`MenuActionSelector should match snapshot for channels 2`] = `
+exports[`SelectorScreen should match snapshot for channels 2`] = `
`;
-exports[`MenuActionSelector should match snapshot for explicit options 1`] = `
+exports[`SelectorScreen should match snapshot for explicit options 1`] = `
`;
-exports[`MenuActionSelector should match snapshot for searching 1`] = `
+exports[`SelectorScreen should match snapshot for searching 1`] = `
`;
-exports[`MenuActionSelector should match snapshot for users 1`] = `
+exports[`SelectorScreen should match snapshot for users 1`] = `
`;
-exports[`MenuActionSelector should match snapshot for users 2`] = `
+exports[`SelectorScreen should match snapshot for users 2`] = `
({
fs: {
@@ -61,7 +61,7 @@ const searchChannels = async () => {
const intlProvider = new IntlProvider({locale: 'en'}, {});
const {intl} = intlProvider.getChildContext();
-describe('MenuActionSelector', () => {
+describe('SelectorScreen', () => {
const actions = {
getProfiles,
getChannels,
@@ -83,7 +83,7 @@ describe('MenuActionSelector', () => {
test('should match snapshot for explicit options', async () => {
const wrapper = shallow(
- ,
+ ,
{context: {intl}},
);
expect(wrapper.getElement()).toMatchSnapshot();
@@ -97,7 +97,7 @@ describe('MenuActionSelector', () => {
};
const wrapper = shallow(
- ,
+ ,
{context: {intl}},
);
expect(wrapper.getElement()).toMatchSnapshot();
@@ -114,7 +114,7 @@ describe('MenuActionSelector', () => {
};
const wrapper = shallow(
- ,
+ ,
{context: {intl}},
);
expect(wrapper.getElement()).toMatchSnapshot();
@@ -131,7 +131,7 @@ describe('MenuActionSelector', () => {
};
const wrapper = shallow(
- ,
+ ,
{context: {intl}},
);
wrapper.setState({isLoading: false, searching: true, term: 'name2'});
diff --git a/package-lock.json b/package-lock.json
index 88d346850..5bd14cfc7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2806,7 +2806,7 @@
},
"colors": {
"version": "0.5.1",
- "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
"integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=",
"dev": true
},
@@ -12143,7 +12143,7 @@
"dependencies": {
"jsesc": {
"version": "0.5.0",
- "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="
}
}