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:
parent
f0c9aa75fc
commit
1b5d76712a
8 changed files with 215 additions and 222 deletions
|
|
@ -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),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
195
app/components/widgets/settings/bool_setting.tsx
Normal file
195
app/components/widgets/settings/bool_setting.tsx
Normal 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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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[]>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue