MM-35935 Respect disabled for bool settings (#5405)

* Respect disabled for bool settings

* Fix test

* Mirate to functional component

* Fix test
This commit is contained in:
Daniel Espino García 2021-07-09 11:16:06 +02:00 committed by GitHub
parent f0c9aa75fc
commit 1b5d76712a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 215 additions and 222 deletions

View file

@ -198,16 +198,17 @@ export default class AutocompleteSelector extends PureComponent {
);
}
const noediting = disabled ? style.disabled : null;
return (
<View style={style.container}>
{labelContent}
<TouchableWithFeedback
style={disabled ? style.disabled : null}
onPress={this.goToSelectorScreen}
type={'opacity'}
disabled={disabled}
>
<View style={inputStyle}>
<View style={[inputStyle, noediting]}>
<Text
style={selectedStyle}
numberOfLines={1}
@ -302,7 +303,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
fontSize: 14,
},
disabled: {
opacity: 0.5,
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
},
};
});

View file

@ -1,197 +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,
Switch,
} from 'react-native';
import FormattedText from '@components/formatted_text';
import Markdown from '@components/markdown';
import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
import {
changeOpacity,
makeStyleSheetFromTheme,
} from '@utils/theme';
export default class BoolSetting extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.oneOfType([
PropTypes.shape({
id: PropTypes.string.isRequired,
defaultMessage: PropTypes.string.isRequired,
}),
PropTypes.string,
]),
value: PropTypes.bool.isRequired,
placeholder: PropTypes.string,
helpText: PropTypes.node,
errorText: PropTypes.node,
optional: PropTypes.bool,
theme: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
};
handleChange = (value) => {
this.props.onChange(this.props.id, Boolean(value));
};
render() {
const {
label,
value,
placeholder,
helpText,
errorText,
optional,
theme,
} = this.props;
const style = getStyleSheet(theme);
const textStyles = getMarkdownTextStyles(theme);
const blockStyles = getMarkdownBlockStyles(theme);
let optionalContent;
let asterisk;
if (optional) {
optionalContent = (
<FormattedText
style={style.optional}
id='channel_modal.optional'
defaultMessage='(optional)'
/>
);
} else {
asterisk = <Text style={style.asterisk}>{' *'}</Text>;
}
let labelContent;
if (label) {
labelContent = (
<View style={style.labelContainer}>
<Text style={style.label}>
{label}
</Text>
{asterisk}
{optionalContent}
</View>
);
}
let helpTextContent;
if (helpText) {
helpTextContent = (
<View style={style.helpTextContainer} >
<Markdown
baseTextStyle={style.helpText}
textStyles={textStyles}
blockStyles={blockStyles}
value={helpText}
/>
</View>
);
}
let errorTextContent;
if (errorText) {
errorTextContent = (
<View style={style.errorTextContainer} >
<Markdown
baseTextStyle={style.errorText}
textStyles={textStyles}
blockStyles={blockStyles}
value={errorText}
/>
</View>
);
}
return (
<>
<View>
{labelContent}
</View>
<View style={style.separator}/>
<View style={style.inputContainer}>
<Text style={style.placeholderText}>
{placeholder}
</Text>
<Switch
onValueChange={this.handleChange}
value={value}
style={style.inputSwitch}
/>
</View>
<View style={style.separator}/>
<View>
{helpTextContent}
{errorTextContent}
</View>
</>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
labelContainer: {
flexDirection: 'row',
marginTop: 15,
marginBottom: 10,
},
label: {
fontSize: 14,
color: theme.centerChannelColor,
marginLeft: 15,
},
inputContainer: {
backgroundColor: theme.centerChannelBg,
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 15,
height: 40,
},
placeholderText: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 15,
},
inputSwitch: {
position: 'absolute',
right: 12,
},
separator: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
height: 1,
width: '100%',
},
optional: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 14,
marginLeft: 5,
},
helpTextContainer: {
marginHorizontal: 15,
marginTop: 10,
},
helpText: {
fontSize: 12,
color: changeOpacity(theme.centerChannelColor, 0.5),
},
errorTextContainer: {
marginHorizontal: 15,
marginVertical: 10,
},
errorText: {
fontSize: 12,
color: theme.errorTextColor,
},
asterisk: {
color: theme.errorTextColor,
fontSize: 14,
},
};
});

View file

@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {Switch} from 'react-native';
import {shallow} from 'enzyme';
import Preferences from '@mm-redux/constants/preferences';
import BoolSetting from './bool_setting.js';
import BoolSetting from './bool_setting';
describe('components/widgets/settings/TextSetting', () => {
const theme = Preferences.THEMES.default;
@ -23,11 +24,11 @@ describe('components/widgets/settings/TextSetting', () => {
/>,
);
wrapper.instance().handleChange(false);
wrapper.find<Switch>(Switch).simulate('valueChange', false);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('elementid', false);
wrapper.instance().handleChange(true);
wrapper.find<Switch>(Switch).simulate('valueChange', true);
expect(onChange).toHaveBeenCalledTimes(2);
expect(onChange).toHaveBeenCalledWith('elementid', true);
});

View file

@ -0,0 +1,195 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {
View,
Text,
Switch,
} from 'react-native';
import FormattedText from '@components/formatted_text';
import Markdown from '@components/markdown';
import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
import {
changeOpacity,
makeStyleSheetFromTheme,
} from '@utils/theme';
import {Theme} from '@mm-redux/types/preferences';
type Props = {
id: string;
label?: string | {id: string, defaultMessage: string};
value: boolean;
placeholder?: string;
helpText?: string;
errorText?: string;
optional?: boolean;
disabled?: boolean;
theme: Theme;
onChange: (name: string, value: boolean) => void;
}
export default function BoolSetting(props: Props) {
const {
id,
label,
value,
placeholder,
helpText,
errorText,
optional,
theme,
disabled,
onChange,
} = props;
const style = getStyleSheet(theme);
const textStyles = getMarkdownTextStyles(theme);
const blockStyles = getMarkdownBlockStyles(theme);
let optionalContent;
let asterisk;
if (optional) {
optionalContent = (
<FormattedText
style={style.optional}
id='channel_modal.optional'
defaultMessage='(optional)'
/>
);
} else {
asterisk = <Text style={style.asterisk}>{' *'}</Text>;
}
let labelContent;
if (label) {
labelContent = (
<View style={style.labelContainer}>
<Text style={style.label}>
{label}
</Text>
{asterisk}
{optionalContent}
</View>
);
}
let helpTextContent;
if (helpText) {
helpTextContent = (
<View style={style.helpTextContainer} >
<Markdown
baseTextStyle={style.helpText}
textStyles={textStyles}
blockStyles={blockStyles}
value={helpText}
/>
</View>
);
}
let errorTextContent;
if (errorText) {
errorTextContent = (
<View style={style.errorTextContainer} >
<Markdown
baseTextStyle={style.errorText}
textStyles={textStyles}
blockStyles={blockStyles}
value={errorText}
/>
</View>
);
}
const noediting = disabled ? style.disabled : null;
return (
<>
<View>
{labelContent}
</View>
<View style={style.separator}/>
<View style={[style.inputContainer, noediting]}>
<Text style={style.placeholderText}>
{placeholder}
</Text>
<Switch
onValueChange={(newValue: boolean) => onChange(id, newValue)}
value={value}
style={style.inputSwitch}
disabled={disabled}
/>
</View>
<View style={style.separator}/>
<View>
{helpTextContent}
{errorTextContent}
</View>
</>
);
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
labelContainer: {
flexDirection: 'row',
marginTop: 15,
marginBottom: 10,
},
label: {
fontSize: 14,
color: theme.centerChannelColor,
marginLeft: 15,
},
inputContainer: {
backgroundColor: theme.centerChannelBg,
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 15,
height: 40,
},
disabled: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
},
placeholderText: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 15,
},
inputSwitch: {
position: 'absolute',
right: 12,
},
separator: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.1),
height: 1,
width: '100%',
},
optional: {
color: changeOpacity(theme.centerChannelColor, 0.5),
fontSize: 14,
marginLeft: 5,
},
helpTextContainer: {
marginHorizontal: 15,
marginTop: 10,
},
helpText: {
fontSize: 12,
color: changeOpacity(theme.centerChannelColor, 0.5),
},
errorTextContainer: {
marginHorizontal: 15,
marginVertical: 10,
},
errorText: {
fontSize: 12,
color: theme.errorTextColor,
},
asterisk: {
color: theme.errorTextColor,
fontSize: 14,
},
};
});

View file

@ -15,7 +15,6 @@ import {Theme} from '@mm-redux/types/preferences';
import {checkDialogElementForError, checkIfErrorsMatchElements} from '@mm-redux/utils/integration_utils';
import StatusBar from '@components/status_bar';
import FormattedText from '@components/formatted_text';
import Markdown from '@components/markdown';
import {dismissModal} from '@actions/navigation';
@ -47,7 +46,7 @@ export type Props = {
type State = {
values: {[name: string]: string};
formError: string | null;
fieldErrors: {[name: string]: React.ReactNode};
fieldErrors: {[name: string]: string};
form: AppForm;
}
@ -119,7 +118,7 @@ export default class AppsFormComponent extends PureComponent<Props, State> {
const {fields} = this.props.form;
const values = this.state.values;
const fieldErrors: {[name: string]: React.ReactNode} = {};
const fieldErrors: {[name: string]: string} = {};
const elements = fieldsAsElements(fields);
elements?.forEach((element) => {
@ -128,13 +127,7 @@ export default class AppsFormComponent extends PureComponent<Props, State> {
values[element.name],
);
if (error) {
fieldErrors[element.name] = (
<FormattedText
id={error.id}
defaultMessage={error.defaultMessage}
values={error.values}
/>
);
fieldErrors[element.name] = this.context.intl.formatMessage(error.id, error.defaultMessage, error.values);
}
});

View file

@ -24,11 +24,11 @@ const TEXTAREA_DEFAULT_MAX_LENGTH = 3000;
export type Props = {
field: AppField;
name: string;
errorText?: React.ReactNode;
errorText?: string;
theme: Theme;
value: AppFormValue;
onChange: (name: string, value: string | AppSelectOption) => void;
onChange: (name: string, value: string | AppSelectOption | boolean) => void;
performLookup: (name: string, userInput: string) => Promise<AppSelectOption[]>;
}

View file

@ -4,10 +4,10 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import BoolSetting from 'app/components/widgets/settings/bool_setting';
import TextSetting from 'app/components/widgets/settings/text_setting';
import AutocompleteSelector from 'app/components/autocomplete_selector';
import RadioSetting from 'app/components/widgets/settings/radio_setting';
import BoolSetting from '@components/widgets/settings/bool_setting';
import TextSetting from '@components/widgets/settings/text_setting';
import AutocompleteSelector from '@components/autocomplete_selector';
import RadioSetting from '@components/widgets/settings/radio_setting';
const TEXT_DEFAULT_MAX_LENGTH = 150;
const TEXTAREA_DEFAULT_MAX_LENGTH = 3000;

View file

@ -5,9 +5,9 @@ import React from 'react';
import {shallow} from 'enzyme';
import Preferences from '@mm-redux/constants/preferences';
import RadioSetting from 'app/components/widgets/settings/radio_setting';
import BoolSetting from 'app/components/widgets/settings/bool_setting';
import AutocompleteSelector from 'app/components/autocomplete_selector';
import RadioSetting from '@components/widgets/settings/radio_setting';
import BoolSetting from '@components/widgets/settings/bool_setting';
import AutocompleteSelector from '@components/autocomplete_selector';
import DialogElement from './dialog_element.js';
describe('DialogElement', () => {