MM-18464 Interactive dialog renders out of safe area view on landscape orientation (#3335)
* MM-18464 Updated Dialog Items to support isLandscape for SafeArea View * MM-18464 Updated SafeAreaView Updated Autoselector Component * MM-18464 Resolved Issues Resolved issues for MM-18464 * MM-18464 Resolved Snapshot Resolved snapshots
This commit is contained in:
parent
3b3a696a55
commit
2009999090
15 changed files with 93 additions and 38 deletions
|
|
@ -10,6 +10,7 @@ import Icon from 'react-native-vector-icons/FontAwesome';
|
|||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
|
||||
import TouchableWithFeedback from 'app/components/touchable_with_feedback';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
|
||||
|
|
@ -34,6 +35,7 @@ export default class AutocompleteSelector extends PureComponent {
|
|||
helpText: PropTypes.node,
|
||||
errorText: PropTypes.node,
|
||||
roundedBorders: PropTypes.bool,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
|
|
@ -116,6 +118,7 @@ export default class AutocompleteSelector extends PureComponent {
|
|||
optional,
|
||||
showRequiredAsterisk,
|
||||
roundedBorders,
|
||||
isLandscape,
|
||||
} = this.props;
|
||||
const {selectedText} = this.state;
|
||||
const style = getStyleSheet(theme);
|
||||
|
|
@ -181,7 +184,9 @@ export default class AutocompleteSelector extends PureComponent {
|
|||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
{labelContent}
|
||||
<View style={padding(isLandscape)}>
|
||||
{labelContent}
|
||||
</View>
|
||||
<TouchableWithFeedback
|
||||
style={style.flex}
|
||||
onPress={this.goToSelectorScreen}
|
||||
|
|
@ -189,7 +194,7 @@ export default class AutocompleteSelector extends PureComponent {
|
|||
>
|
||||
<View style={inputStyle}>
|
||||
<Text
|
||||
style={selectedStyle}
|
||||
style={[selectedStyle, padding(isLandscape)]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{text}
|
||||
|
|
@ -197,12 +202,14 @@ export default class AutocompleteSelector extends PureComponent {
|
|||
<Icon
|
||||
name='chevron-down'
|
||||
color={changeOpacity(theme.centerChannelColor, 0.5)}
|
||||
style={style.icon}
|
||||
style={[style.icon, padding(isLandscape)]}
|
||||
/>
|
||||
</View>
|
||||
</TouchableWithFeedback>
|
||||
{helpTextContent}
|
||||
{errorTextContent}
|
||||
<View style={padding(isLandscape)}>
|
||||
{helpTextContent}
|
||||
{errorTextContent}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@ import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selector
|
|||
import {setAutocompleteSelector} from 'app/actions/views/post';
|
||||
|
||||
import AutocompleteSelector from './autocomplete_selector';
|
||||
import {isLandscape} from 'app/selectors/device';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
teammateNameDisplay: getTeammateNameDisplaySetting(state),
|
||||
theme: getTheme(state),
|
||||
isLandscape: isLandscape(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {isLandscape} from 'app/selectors/device';
|
||||
import OptionListRow from './option_list_row';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
isLandscape: isLandscape(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,12 +17,17 @@ export default class OptionListRow extends React.PureComponent {
|
|||
id: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
...CustomListRow.propTypes,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isLandscape: false,
|
||||
};
|
||||
|
||||
onPress = () => {
|
||||
if (this.props.onPress) {
|
||||
this.props.onPress(this.props.id, this.props.item);
|
||||
|
|
@ -36,6 +41,7 @@ export default class OptionListRow extends React.PureComponent {
|
|||
selected,
|
||||
theme,
|
||||
item,
|
||||
isLandscape,
|
||||
} = this.props;
|
||||
|
||||
const {text, value} = item;
|
||||
|
|
@ -48,6 +54,7 @@ export default class OptionListRow extends React.PureComponent {
|
|||
enabled={enabled}
|
||||
selectable={selectable}
|
||||
selected={selected}
|
||||
isLandscape={isLandscape}
|
||||
>
|
||||
<View style={style.textContainer}>
|
||||
<View>
|
||||
|
|
|
|||
|
|
@ -104,25 +104,27 @@ export default class BoolSetting extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<View style={padding(isLandscape)}>
|
||||
{labelContent}
|
||||
<React.Fragment>
|
||||
<View style={padding(isLandscape)}>
|
||||
{labelContent}
|
||||
</View>
|
||||
<View style={style.separator}/>
|
||||
<View style={[style.inputContainer]}>
|
||||
<Text style={style.placeholderText}>
|
||||
<View style={style.inputContainer}>
|
||||
<Text style={[style.placeholderText, padding(isLandscape)]}>
|
||||
{placeholder}
|
||||
</Text>
|
||||
<Switch
|
||||
onValueChange={this.handleChange}
|
||||
value={value}
|
||||
style={style.inputSwitch}
|
||||
style={[style.inputSwitch, padding(isLandscape)]}
|
||||
/>
|
||||
</View>
|
||||
<View style={style.separator}/>
|
||||
<View>
|
||||
<View style={padding(isLandscape)}>
|
||||
{helpTextContent}
|
||||
{errorTextContent}
|
||||
</View>
|
||||
</View>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,12 @@ exports[`DialogIntroductionText should not render the component with an empty va
|
|||
exports[`DialogIntroductionText should render the introduction text correctly 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginHorizontal": 15,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"marginHorizontal": 15,
|
||||
},
|
||||
null,
|
||||
]
|
||||
}
|
||||
>
|
||||
<Connect(Markdown)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ exports[`InteractiveDialog should display introduction text if present 1`] = `
|
|||
>
|
||||
<Connect(StatusBar) />
|
||||
<DialogIntroductionText
|
||||
isLandscape={false}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ export default class DialogElement extends PureComponent {
|
|||
multiline={multiline}
|
||||
keyboardType={keyboardType}
|
||||
secureTextEntry={secureTextEntry}
|
||||
isLandscape={isLandscape}
|
||||
/>
|
||||
);
|
||||
} else if (type === 'select') {
|
||||
|
|
@ -135,6 +136,7 @@ export default class DialogElement extends PureComponent {
|
|||
showRequiredAsterisk={true}
|
||||
selected={this.state.selected}
|
||||
roundedBorders={false}
|
||||
isLandscape={isLandscape}
|
||||
/>
|
||||
);
|
||||
} else if (type === 'radio') {
|
||||
|
|
@ -148,6 +150,7 @@ export default class DialogElement extends PureComponent {
|
|||
theme={theme}
|
||||
default={value}
|
||||
onChange={this.onChange}
|
||||
isLandscape={isLandscape}
|
||||
/>
|
||||
);
|
||||
} else if (type === 'bool') {
|
||||
|
|
|
|||
|
|
@ -9,17 +9,20 @@ import Markdown from 'app/components/markdown';
|
|||
|
||||
import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
|
||||
|
||||
export default class DialogIntroductionText extends PureComponent {
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
value,
|
||||
theme,
|
||||
isLandscape,
|
||||
} = this.props;
|
||||
|
||||
if (value) {
|
||||
|
|
@ -28,7 +31,7 @@ export default class DialogIntroductionText extends PureComponent {
|
|||
const textStyles = getMarkdownTextStyles(theme);
|
||||
|
||||
return (
|
||||
<View style={style.introductionTextView}>
|
||||
<View style={[style.introductionTextView, padding(isLandscape)]}>
|
||||
<Markdown
|
||||
baseTextStyle={style.introductionText}
|
||||
textStyles={textStyles}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ describe('DialogIntroductionText', () => {
|
|||
const baseProps = {
|
||||
theme: Preferences.THEMES.default,
|
||||
value: '**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)',
|
||||
isLandscape: false,
|
||||
};
|
||||
|
||||
test('should render the introduction text correctly', () => {
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ export default class InteractiveDialog extends PureComponent {
|
|||
<DialogIntroductionText
|
||||
value={introductionText}
|
||||
theme={theme}
|
||||
isLandscape={isLandscape}
|
||||
/>
|
||||
}
|
||||
{elements && elements.map((e) => {
|
||||
|
|
|
|||
|
|
@ -12,9 +12,12 @@ exports[`SelectorScreen should match snapshot for channels 1`] = `
|
|||
<Connect(StatusBar) />
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
},
|
||||
null,
|
||||
]
|
||||
}
|
||||
>
|
||||
<SearchBarIos
|
||||
|
|
@ -104,9 +107,12 @@ exports[`SelectorScreen should match snapshot for channels 2`] = `
|
|||
<Connect(StatusBar) />
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
},
|
||||
null,
|
||||
]
|
||||
}
|
||||
>
|
||||
<SearchBarIos
|
||||
|
|
@ -196,9 +202,12 @@ exports[`SelectorScreen should match snapshot for explicit options 1`] = `
|
|||
<Connect(StatusBar) />
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
},
|
||||
null,
|
||||
]
|
||||
}
|
||||
>
|
||||
<SearchBarIos
|
||||
|
|
@ -295,9 +304,12 @@ exports[`SelectorScreen should match snapshot for searching 1`] = `
|
|||
<Connect(StatusBar) />
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
},
|
||||
null,
|
||||
]
|
||||
}
|
||||
>
|
||||
<SearchBarIos
|
||||
|
|
@ -387,9 +399,12 @@ exports[`SelectorScreen should match snapshot for users 1`] = `
|
|||
<Connect(StatusBar) />
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
},
|
||||
null,
|
||||
]
|
||||
}
|
||||
>
|
||||
<SearchBarIos
|
||||
|
|
@ -479,9 +494,12 @@ exports[`SelectorScreen should match snapshot for users 2`] = `
|
|||
<Connect(StatusBar) />
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"marginVertical": 5,
|
||||
},
|
||||
null,
|
||||
]
|
||||
}
|
||||
>
|
||||
<SearchBarIos
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
|||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getProfiles, searchProfiles} from 'mattermost-redux/actions/users';
|
||||
import {getChannels, searchChannels} from 'mattermost-redux/actions/channels';
|
||||
|
||||
import {isLandscape} from 'app/selectors/device';
|
||||
import SelectorScreen from './selector_screen';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -22,6 +22,7 @@ function mapStateToProps(state) {
|
|||
dataSource: menuAction.dataSource,
|
||||
onSelect: menuAction.onSelect,
|
||||
theme: getTheme(state),
|
||||
isLandscape: isLandscape(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ import {
|
|||
import {t} from 'app/utils/i18n';
|
||||
import {popTopScreen} from 'app/actions/navigation';
|
||||
|
||||
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
|
||||
|
||||
export default class SelectorScreen extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
|
|
@ -47,6 +49,7 @@ export default class SelectorScreen extends PureComponent {
|
|||
dataSource: PropTypes.string,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
|
|
@ -293,7 +296,7 @@ export default class SelectorScreen extends PureComponent {
|
|||
|
||||
render() {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const {theme, dataSource} = this.props;
|
||||
const {theme, dataSource, isLandscape} = this.props;
|
||||
const {loading, term} = this.state;
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
|
|
@ -322,7 +325,7 @@ export default class SelectorScreen extends PureComponent {
|
|||
return (
|
||||
<View style={style.container}>
|
||||
<StatusBar/>
|
||||
<View style={style.searchBar}>
|
||||
<View style={[style.searchBar, padding(isLandscape)]}>
|
||||
<SearchBar
|
||||
ref='search_bar'
|
||||
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
|
||||
|
|
@ -353,6 +356,7 @@ export default class SelectorScreen extends PureComponent {
|
|||
onRowPress={this.handleSelectItem}
|
||||
renderItem={rowComponent}
|
||||
theme={theme}
|
||||
isLandscape={isLandscape}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ describe('SelectorScreen', () => {
|
|||
data: [{text: 'text', value: 'value'}],
|
||||
dataSource: null,
|
||||
theme: Preferences.THEMES.default,
|
||||
isLandscape: false,
|
||||
};
|
||||
|
||||
test('should match snapshot for explicit options', async () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue