[MM-56824] Componentize the Mobile and Desktop section headers in the user settings modal. (#26193)

This commit is contained in:
M-ZubairAhmed 2024-02-15 09:56:25 +00:00 committed by GitHub
parent 86c880cf87
commit 114b3dc8d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 705 additions and 1217 deletions

View File

@ -17,7 +17,6 @@ import {emitUserLoggedOutEvent} from 'actions/global_actions';
import ConfirmModal from 'components/confirm_modal'; import ConfirmModal from 'components/confirm_modal';
import SettingItem from 'components/setting_item'; import SettingItem from 'components/setting_item';
import SettingItemMax from 'components/setting_item_max'; import SettingItemMax from 'components/setting_item_max';
import BackIcon from 'components/widgets/icons/fa_back_icon';
import Constants, {AdvancedSections, Preferences} from 'utils/constants'; import Constants, {AdvancedSections, Preferences} from 'utils/constants';
import {t} from 'utils/i18n'; import {t} from 'utils/i18n';
@ -27,6 +26,9 @@ import {a11yFocus, localizeMessage} from 'utils/utils';
import JoinLeaveSection from './join_leave_section'; import JoinLeaveSection from './join_leave_section';
import PerformanceDebuggingSection from './performance_debugging_section'; import PerformanceDebuggingSection from './performance_debugging_section';
import SettingDesktopHeader from '../headers/setting_desktop_header';
import SettingMobileHeader from '../headers/setting_mobile_header';
const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES; const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
type Settings = { type Settings = {
@ -898,38 +900,25 @@ export default class AdvancedSettingsDisplay extends React.PureComponent<Props,
return ( return (
<div> <div>
<div className='modal-header'> <SettingMobileHeader
<button closeModal={this.props.closeModal}
id='closeButton' collapseModal={this.props.collapseModal}
type='button' text={
className='close'
data-dismiss='modal'
aria-label='Close'
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
>
<div className='modal-back'>
<span onClick={this.props.collapseModal}>
<BackIcon/>
</span>
</div>
<FormattedMessage <FormattedMessage
id='user.settings.advance.title' id='user.settings.advance.title'
defaultMessage='Advanced Settings' defaultMessage='Advanced Settings'
/> />
</h4> }
</div> />
<div className='user-settings'> <div className='user-settings'>
<h3 className='tab-header'> <SettingDesktopHeader
<FormattedMessage text={
id='user.settings.advance.title' <FormattedMessage
defaultMessage='Advanced Settings' id='user.settings.advance.title'
/> defaultMessage='Advanced Settings'
</h3> />
}
/>
<div className='divider-dark first'/> <div className='divider-dark first'/>
{ctrlSendSection} {ctrlSendSection}
{formattingSectionDivider} {formattingSectionDivider}

View File

@ -4,53 +4,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -381,53 +356,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -758,53 +708,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -1135,53 +1060,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -1512,53 +1412,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -1816,53 +1691,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -2102,53 +1952,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -2479,53 +2304,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -2874,53 +2674,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -3178,53 +2953,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -3482,53 +3232,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -3786,53 +3511,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -4103,53 +3803,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -4407,53 +4082,28 @@ exports[`components/user_settings/display/UserSettingsDisplay should not show la
<div <div
id="displaySettings" id="displaySettings"
> >
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
id="closeButton"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<span
onClick={[MockFunction]}
>
<BackIcon />
</span>
</div>
<MemoizedFormattedMessage
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header"
id="displaySettingsTitle" id="displaySettingsTitle"
> text={
<MemoizedFormattedMessage <Memo(MemoizedFormattedMessage)
defaultMessage="Display Settings" defaultMessage="Display Settings"
id="user.settings.display.title" id="user.settings.display.title"
/> />
</h3> }
/>
<div <div
className="divider-dark first" className="divider-dark first"
/> />

View File

@ -19,7 +19,6 @@ import {trackEvent} from 'actions/telemetry_actions';
import SettingItem from 'components/setting_item'; import SettingItem from 'components/setting_item';
import SettingItemMax from 'components/setting_item_max'; import SettingItemMax from 'components/setting_item_max';
import ThemeSetting from 'components/user_settings/display/user_settings_theme'; import ThemeSetting from 'components/user_settings/display/user_settings_theme';
import BackIcon from 'components/widgets/icons/fa_back_icon';
import * as I18n from 'i18n/i18n.jsx'; import * as I18n from 'i18n/i18n.jsx';
import Constants from 'utils/constants'; import Constants from 'utils/constants';
@ -30,6 +29,9 @@ import {a11yFocus} from 'utils/utils';
import ManageLanguages from './manage_languages'; import ManageLanguages from './manage_languages';
import ManageTimezones from './manage_timezones'; import ManageTimezones from './manage_timezones';
import SettingDesktopHeader from '../headers/setting_desktop_header';
import SettingMobileHeader from '../headers/setting_mobile_header';
const Preferences = Constants.Preferences; const Preferences = Constants.Preferences;
function getDisplayStateFromProps(props: Props) { function getDisplayStateFromProps(props: Props) {
@ -94,8 +96,8 @@ type Props = {
user: UserProfile; user: UserProfile;
updateSection: (section: string) => void; updateSection: (section: string) => void;
activeSection?: string; activeSection?: string;
closeModal?: () => void; closeModal: () => void;
collapseModal?: () => void; collapseModal: () => void;
setRequireConfirm?: () => void; setRequireConfirm?: () => void;
setEnforceFocus?: () => void; setEnforceFocus?: () => void;
timezones: Timezone[]; timezones: Timezone[];
@ -1100,39 +1102,26 @@ export default class UserSettingsDisplay extends React.PureComponent<Props, Stat
return ( return (
<div id='displaySettings'> <div id='displaySettings'>
<div className='modal-header'> <SettingMobileHeader
<button closeModal={this.props.closeModal}
id='closeButton' collapseModal={this.props.collapseModal}
type='button' text={
className='close'
data-dismiss='modal'
aria-label='Close'
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
<h4 className='modal-title'>
<div className='modal-back'>
<span onClick={this.props.collapseModal}>
<BackIcon/>
</span>
</div>
<FormattedMessage <FormattedMessage
id='user.settings.display.title' id='user.settings.display.title'
defaultMessage='Display Settings' defaultMessage='Display Settings'
/> />
</h4> }
</div> />
<div className='user-settings'> <div className='user-settings'>
<h3 <SettingDesktopHeader
id='displaySettingsTitle' id='displaySettingsTitle'
className='tab-header' text={
> <FormattedMessage
<FormattedMessage id='user.settings.display.title'
id='user.settings.display.title' defaultMessage='Display Settings'
defaultMessage='Display Settings' />
/> }
</h3> />
<div className='divider-dark first'/> <div className='divider-dark first'/>
{themeSection} {themeSection}
{collapsedReplyThreads} {collapsedReplyThreads}

View File

@ -7,7 +7,7 @@ import {setThemeDefaults} from 'mattermost-redux/utils/theme_utils';
import {mountWithIntl, shallowWithIntl} from 'tests/helpers/intl-test-helper'; import {mountWithIntl, shallowWithIntl} from 'tests/helpers/intl-test-helper';
import ImportThemeModal from './import_theme_modal'; import ImportThemeModal from './index';
describe('components/user_settings/ImportThemeModal', () => { describe('components/user_settings/ImportThemeModal', () => {
const props = { const props = {

View File

@ -11,7 +11,7 @@ import ExternalLink from 'components/external_link';
import SettingItemMax from 'components/setting_item_max'; import SettingItemMax from 'components/setting_item_max';
import SettingItemMin from 'components/setting_item_min'; import SettingItemMin from 'components/setting_item_min';
import type SettingItemMinComponent from 'components/setting_item_min'; import type SettingItemMinComponent from 'components/setting_item_min';
import ImportThemeModal from 'components/user_settings/import_theme_modal'; import ImportThemeModal from 'components/user_settings/display/user_settings_theme/import_theme_modal';
import {Constants, ModalIdentifiers} from 'utils/constants'; import {Constants, ModalIdentifiers} from 'utils/constants';
import {applyTheme} from 'utils/utils'; import {applyTheme} from 'utils/utils';

View File

@ -3,7 +3,7 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
import React from 'react'; import React, {PureComponent} from 'react';
import {defineMessages, FormattedDate, FormattedMessage, injectIntl} from 'react-intl'; import {defineMessages, FormattedDate, FormattedMessage, injectIntl} from 'react-intl';
import type {IntlShape} from 'react-intl'; import type {IntlShape} from 'react-intl';
@ -22,6 +22,9 @@ import LoadingWrapper from 'components/widgets/loading/loading_wrapper';
import {AnnouncementBarMessages, AnnouncementBarTypes, AcceptedProfileImageTypes, Constants, ValidationErrors} from 'utils/constants'; import {AnnouncementBarMessages, AnnouncementBarTypes, AcceptedProfileImageTypes, Constants, ValidationErrors} from 'utils/constants';
import * as Utils from 'utils/utils'; import * as Utils from 'utils/utils';
import SettingDesktopHeader from '../headers/setting_desktop_header';
import SettingMobileHeader from '../headers/setting_mobile_header';
const holders = defineMessages({ const holders = defineMessages({
usernameReserved: { usernameReserved: {
id: 'user.settings.general.usernameReserved', id: 'user.settings.general.usernameReserved',
@ -143,7 +146,7 @@ type State = {
emailError?: string; emailError?: string;
} }
export class UserSettingsGeneralTab extends React.Component<Props, State> { export class UserSettingsGeneralTab extends PureComponent<Props, State> {
public submitActive = false; public submitActive = false;
constructor(props: Props) { constructor(props: Props) {
@ -1352,8 +1355,6 @@ export class UserSettingsGeneralTab extends React.Component<Props, State> {
}; };
render() { render() {
const {formatMessage} = this.props.intl;
const nameSection = this.createNameSection(); const nameSection = this.createNameSection();
const nicknameSection = this.createNicknameSection(); const nicknameSection = this.createNicknameSection();
const usernameSection = this.createUsernameSection(); const usernameSection = this.createUsernameSection();
@ -1363,41 +1364,26 @@ export class UserSettingsGeneralTab extends React.Component<Props, State> {
return ( return (
<div id='generalSettings'> <div id='generalSettings'>
<div className='modal-header'> <SettingMobileHeader
<button closeModal={this.props.closeModal}
id='closeUserSettings' collapseModal={this.props.collapseModal}
type='button' text={
className='close'
data-dismiss='modal'
aria-label={formatMessage(holders.close)}
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
<h4 className='modal-title'>
<div className='modal-back'>
<i
className='fa fa-angle-left'
title={this.props.intl.formatMessage({id: 'generic_icons.collapse', defaultMessage: 'Collapse Icon'})}
onClick={this.props.collapseModal}
/>
</div>
<FormattedMessage <FormattedMessage
id='user.settings.modal.profile' id='user.settings.modal.profile'
defaultMessage='Profile' defaultMessage='Profile'
/> />
</h4> }
</div> />
<div className='user-settings'> <div className='user-settings'>
<h3 <SettingDesktopHeader
id='generalSettingsTitle' id='generalSettingsTitle'
className='tab-header' text={
> <FormattedMessage
<FormattedMessage id='user.settings.modal.profile'
id='user.settings.modal.profile' defaultMessage='Profile'
defaultMessage='Profile' />
/> }
</h3> />
<div className='divider-dark first'/> <div className='divider-dark first'/>
{nameSection} {nameSection}
<div className='divider-light'/> <div className='divider-light'/>

View File

@ -0,0 +1,20 @@
.userSettingDesktopHeader {
display: flex;
align-items: center;
justify-content: space-between;
.userSettingDesktopHeaderInfo {
a {
font-size: 12px;
}
svg.circular-border {
width: 18px;
height: 18px;
padding: 2px;
background: rgba(var(--button-bg-rgb), 0.12);
border-radius: 50%;
text-align: center;
}
}
}

View File

@ -11,14 +11,14 @@ import SettingDesktopHeader from './setting_desktop_header';
type Props = ComponentProps<typeof SettingDesktopHeader>; type Props = ComponentProps<typeof SettingDesktopHeader>;
const baseProps: Props = { describe('settings_desktop_header', () => {
text: 'setting header', const baseProps: Props = {
}; text: 'setting section header',
};
describe('plugin tab', () => {
it('properly renders the header', () => { it('properly renders the header', () => {
renderWithContext(<SettingDesktopHeader {...baseProps}/>); renderWithContext(<SettingDesktopHeader {...baseProps}/>);
const header = screen.queryByText('setting header'); const header = screen.queryByText('setting section header');
expect(header).toBeInTheDocument(); expect(header).toBeInTheDocument();
// The className is important for how the modal system work // The className is important for how the modal system work

View File

@ -0,0 +1,27 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {ReactNode} from 'react';
import React from 'react';
import './setting_desktop_header.scss';
interface Props {
id?: string;
text: ReactNode;
info?: ReactNode;
}
export default function SettingDesktopHeader(props: Props) {
return (
<div className='userSettingDesktopHeader'>
<h3
id={props.id}
className='tab-header'
>
{props.text}
</h3>
{props.info && <div className='userSettingDesktopHeaderInfo'>{props.info}</div>}
</div>
);
}

View File

@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import type {ReactNode} from 'react';
import React from 'react'; import React from 'react';
import {useIntl} from 'react-intl'; import {useIntl} from 'react-intl';
type Props = { type Props = {
text: string; text: ReactNode;
closeModal: () => void; closeModal: () => void;
collapseModal: () => void; collapseModal: () => void;
} }

View File

@ -11,7 +11,7 @@ import AdvancedTab from './advanced';
import DisplayTab from './display'; import DisplayTab from './display';
import GeneralTab from './general'; import GeneralTab from './general';
import NotificationsTab from './notifications'; import NotificationsTab from './notifications';
import PluginTab from './plugin/plugin'; import PluginTab from './plugin';
import SecurityTab from './security'; import SecurityTab from './security';
import SidebarTab from './sidebar'; import SidebarTab from './sidebar';
@ -28,96 +28,94 @@ export type Props = {
pluginSettings: {[tabName: string]: PluginConfiguration}; pluginSettings: {[tabName: string]: PluginConfiguration};
}; };
export default class UserSettings extends React.PureComponent<Props> { export default function UserSettings(props: Props) {
render() { if (props.activeTab === 'profile') {
if (this.props.activeTab === 'profile') { return (
return ( <div>
<div> <GeneralTab
<GeneralTab user={props.user}
user={this.props.user} activeSection={props.activeSection}
activeSection={this.props.activeSection} updateSection={props.updateSection}
updateSection={this.props.updateSection} updateTab={props.updateTab}
updateTab={this.props.updateTab} closeModal={props.closeModal}
closeModal={this.props.closeModal} collapseModal={props.collapseModal}
collapseModal={this.props.collapseModal} />
/> </div>
</div> );
); } else if (props.activeTab === 'security') {
} else if (this.props.activeTab === 'security') { return (
return ( <div>
<div> <SecurityTab
<SecurityTab user={props.user}
user={this.props.user} activeSection={props.activeSection}
activeSection={this.props.activeSection} updateSection={props.updateSection}
updateSection={this.props.updateSection} closeModal={props.closeModal}
closeModal={this.props.closeModal} collapseModal={props.collapseModal}
collapseModal={this.props.collapseModal} setRequireConfirm={props.setRequireConfirm}
setRequireConfirm={this.props.setRequireConfirm} />
/> </div>
</div> );
); } else if (props.activeTab === 'notifications') {
} else if (this.props.activeTab === 'notifications') { return (
return ( <div>
<div> <NotificationsTab
<NotificationsTab user={props.user}
user={this.props.user} activeSection={props.activeSection}
activeSection={this.props.activeSection} updateSection={props.updateSection}
updateSection={this.props.updateSection} closeModal={props.closeModal}
closeModal={this.props.closeModal} collapseModal={props.collapseModal}
collapseModal={this.props.collapseModal} />
/> </div>
</div> );
); } else if (props.activeTab === 'display') {
} else if (this.props.activeTab === 'display') { return (
return ( <div>
<div> <DisplayTab
<DisplayTab user={props.user}
user={this.props.user} activeSection={props.activeSection}
activeSection={this.props.activeSection} updateSection={props.updateSection}
updateSection={this.props.updateSection} closeModal={props.closeModal}
closeModal={this.props.closeModal} collapseModal={props.collapseModal}
collapseModal={this.props.collapseModal} setEnforceFocus={props.setEnforceFocus}
setEnforceFocus={this.props.setEnforceFocus} setRequireConfirm={props.setRequireConfirm}
setRequireConfirm={this.props.setRequireConfirm} />
/> </div>
</div> );
); } else if (props.activeTab === 'sidebar') {
} else if (this.props.activeTab === 'sidebar') { return (
return ( <div>
<div> <SidebarTab
<SidebarTab activeSection={props.activeSection}
activeSection={this.props.activeSection} updateSection={props.updateSection}
updateSection={this.props.updateSection} closeModal={props.closeModal}
closeModal={this.props.closeModal} collapseModal={props.collapseModal}
collapseModal={this.props.collapseModal} />
/> </div>
</div> );
); } else if (props.activeTab === 'advanced') {
} else if (this.props.activeTab === 'advanced') { return (
return ( <div>
<div> <AdvancedTab
<AdvancedTab activeSection={props.activeSection}
activeSection={this.props.activeSection} updateSection={props.updateSection}
updateSection={this.props.updateSection} closeModal={props.closeModal}
closeModal={this.props.closeModal} collapseModal={props.collapseModal}
collapseModal={this.props.collapseModal} />
/> </div>
</div> );
); } else if (props.activeTab && props.pluginSettings[props.activeTab]) {
} else if (this.props.activeTab && this.props.pluginSettings[this.props.activeTab]) { return (
return ( <div>
<div> <PluginTab
<PluginTab activeSection={props.activeSection}
activeSection={this.props.activeSection} updateSection={props.updateSection}
updateSection={this.props.updateSection} closeModal={props.closeModal}
closeModal={this.props.closeModal} collapseModal={props.collapseModal}
collapseModal={this.props.collapseModal} settings={props.pluginSettings[props.activeTab]}
settings={this.props.pluginSettings[this.props.activeTab]} />
/> </div>
</div> );
);
}
return <div/>;
} }
return null;
} }

View File

@ -41,7 +41,7 @@ Object {
class="user-settings" class="user-settings"
> >
<div <div
class="notificationSettingsModalHeader" class="userSettingDesktopHeader"
> >
<h3 <h3
class="tab-header" class="tab-header"
@ -49,27 +49,33 @@ Object {
> >
Notifications Notifications
</h3> </h3>
<a <div
href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid=" class="userSettingDesktopHeaderInfo"
rel="noopener noreferrer"
target="_blank"
> >
<svg <a
fill="currentColor" class="btn btn-link"
height="1em" href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid="
version="1.1" rel="noopener noreferrer"
viewBox="0 0 24 24" target="_blank"
width="1em"
xmlns="http://www.w3.org/2000/svg"
> >
<path <svg
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z" class="circular-border"
/> fill="currentColor"
</svg> height="1em"
<span> version="1.1"
Learn more about notifications viewBox="0 0 24 24"
</span> width="1em"
</a> xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z"
/>
</svg>
<span>
Learn more about notifications
</span>
</a>
</div>
</div> </div>
<div <div
class="divider-dark first" class="divider-dark first"
@ -293,7 +299,7 @@ Object {
class="user-settings" class="user-settings"
> >
<div <div
class="notificationSettingsModalHeader" class="userSettingDesktopHeader"
> >
<h3 <h3
class="tab-header" class="tab-header"
@ -301,27 +307,33 @@ Object {
> >
Notifications Notifications
</h3> </h3>
<a <div
href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid=" class="userSettingDesktopHeaderInfo"
rel="noopener noreferrer"
target="_blank"
> >
<svg <a
fill="currentColor" class="btn btn-link"
height="1em" href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid="
version="1.1" rel="noopener noreferrer"
viewBox="0 0 24 24" target="_blank"
width="1em"
xmlns="http://www.w3.org/2000/svg"
> >
<path <svg
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z" class="circular-border"
/> fill="currentColor"
</svg> height="1em"
<span> version="1.1"
Learn more about notifications viewBox="0 0 24 24"
</span> width="1em"
</a> xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z"
/>
</svg>
<span>
Learn more about notifications
</span>
</a>
</div>
</div> </div>
<div <div
class="divider-dark first" class="divider-dark first"
@ -604,7 +616,7 @@ Object {
class="user-settings" class="user-settings"
> >
<div <div
class="notificationSettingsModalHeader" class="userSettingDesktopHeader"
> >
<h3 <h3
class="tab-header" class="tab-header"
@ -612,27 +624,33 @@ Object {
> >
Notifications Notifications
</h3> </h3>
<a <div
href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid=" class="userSettingDesktopHeaderInfo"
rel="noopener noreferrer"
target="_blank"
> >
<svg <a
fill="currentColor" class="btn btn-link"
height="1em" href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid="
version="1.1" rel="noopener noreferrer"
viewBox="0 0 24 24" target="_blank"
width="1em"
xmlns="http://www.w3.org/2000/svg"
> >
<path <svg
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z" class="circular-border"
/> fill="currentColor"
</svg> height="1em"
<span> version="1.1"
Learn more about notifications viewBox="0 0 24 24"
</span> width="1em"
</a> xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z"
/>
</svg>
<span>
Learn more about notifications
</span>
</a>
</div>
</div> </div>
<div <div
class="divider-dark first" class="divider-dark first"
@ -859,7 +877,7 @@ Object {
class="user-settings" class="user-settings"
> >
<div <div
class="notificationSettingsModalHeader" class="userSettingDesktopHeader"
> >
<h3 <h3
class="tab-header" class="tab-header"
@ -867,27 +885,33 @@ Object {
> >
Notifications Notifications
</h3> </h3>
<a <div
href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid=" class="userSettingDesktopHeaderInfo"
rel="noopener noreferrer"
target="_blank"
> >
<svg <a
fill="currentColor" class="btn btn-link"
height="1em" href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid="
version="1.1" rel="noopener noreferrer"
viewBox="0 0 24 24" target="_blank"
width="1em"
xmlns="http://www.w3.org/2000/svg"
> >
<path <svg
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z" class="circular-border"
/> fill="currentColor"
</svg> height="1em"
<span> version="1.1"
Learn more about notifications viewBox="0 0 24 24"
</span> width="1em"
</a> xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z"
/>
</svg>
<span>
Learn more about notifications
</span>
</a>
</div>
</div> </div>
<div <div
class="divider-dark first" class="divider-dark first"
@ -1173,7 +1197,7 @@ Object {
class="user-settings" class="user-settings"
> >
<div <div
class="notificationSettingsModalHeader" class="userSettingDesktopHeader"
> >
<h3 <h3
class="tab-header" class="tab-header"
@ -1181,27 +1205,33 @@ Object {
> >
Notifications Notifications
</h3> </h3>
<a <div
href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid=" class="userSettingDesktopHeaderInfo"
rel="noopener noreferrer"
target="_blank"
> >
<svg <a
fill="currentColor" class="btn btn-link"
height="1em" href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid="
version="1.1" rel="noopener noreferrer"
viewBox="0 0 24 24" target="_blank"
width="1em"
xmlns="http://www.w3.org/2000/svg"
> >
<path <svg
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z" class="circular-border"
/> fill="currentColor"
</svg> height="1em"
<span> version="1.1"
Learn more about notifications viewBox="0 0 24 24"
</span> width="1em"
</a> xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z"
/>
</svg>
<span>
Learn more about notifications
</span>
</a>
</div>
</div> </div>
<div <div
class="divider-dark first" class="divider-dark first"
@ -1390,7 +1420,7 @@ Object {
class="user-settings" class="user-settings"
> >
<div <div
class="notificationSettingsModalHeader" class="userSettingDesktopHeader"
> >
<h3 <h3
class="tab-header" class="tab-header"
@ -1398,27 +1428,33 @@ Object {
> >
Notifications Notifications
</h3> </h3>
<a <div
href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid=" class="userSettingDesktopHeaderInfo"
rel="noopener noreferrer"
target="_blank"
> >
<svg <a
fill="currentColor" class="btn btn-link"
height="1em" href="https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=&uid=&sid="
version="1.1" rel="noopener noreferrer"
viewBox="0 0 24 24" target="_blank"
width="1em"
xmlns="http://www.w3.org/2000/svg"
> >
<path <svg
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z" class="circular-border"
/> fill="currentColor"
</svg> height="1em"
<span> version="1.1"
Learn more about notifications viewBox="0 0 24 24"
</span> width="1em"
</a> xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,2A7,7 0 0,1 19,9C19,11.38 17.81,13.47 16,14.74V17A1,1 0 0,1 15,18H9A1,1 0 0,1 8,17V14.74C6.19,13.47 5,11.38 5,9A7,7 0 0,1 12,2M9,21V20H15V21A1,1 0 0,1 14,22H10A1,1 0 0,1 9,21M12,4A5,5 0 0,0 7,9C7,11.05 8.23,12.81 10,13.58V16H14V13.58C15.77,12.81 17,11.05 17,9A5,5 0 0,0 12,4Z"
/>
</svg>
<span>
Learn more about notifications
</span>
</a>
</div>
</div> </div>
<div <div
class="divider-dark first" class="divider-dark first"

View File

@ -1,50 +0,0 @@
.customKeywordsWithNotificationSubsection {
& .multiInput {
margin-block-start: 10px;
}
}
.notificationSettingsModalHeader {
display: flex;
> span {
align-self: center;
margin-left: auto;
a {
display: flex;
align-items: center;
&:hover,
&:active,
&:focus {
color: var(--button-bg);
text-decoration: none;
}
> i {
font-size: 14.4px;
}
> span {
font-size: 12px;
font-weight: 600;
line-height: 16px;
&:hover {
text-decoration: underline;
}
}
> svg {
width: 18px;
height: 18px;
padding: 2px;
margin-right: 4px;
background: rgba(var(--button-bg-rgb), 0.12);
border-radius: 50%;
text-align: center;
}
}
}
}

View File

@ -4,7 +4,7 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
import React from 'react'; import React from 'react';
import type {ChangeEvent, RefObject} from 'react'; import type {ChangeEvent} from 'react';
import type {WrappedComponentProps} from 'react-intl'; import type {WrappedComponentProps} from 'react-intl';
import {FormattedMessage, injectIntl} from 'react-intl'; import {FormattedMessage, injectIntl} from 'react-intl';
import type {Styles as ReactSelectStyles, ValueType} from 'react-select'; import type {Styles as ReactSelectStyles, ValueType} from 'react-select';
@ -29,8 +29,10 @@ import DesktopNotificationSettings from './desktop_notification_setting/desktop_
import EmailNotificationSetting from './email_notification_setting'; import EmailNotificationSetting from './email_notification_setting';
import ManageAutoResponder from './manage_auto_responder/manage_auto_responder'; import ManageAutoResponder from './manage_auto_responder/manage_auto_responder';
import SettingDesktopHeader from '../headers/setting_desktop_header';
import SettingMobileHeader from '../headers/setting_mobile_header';
import type {PropsFromRedux} from './index'; import type {PropsFromRedux} from './index';
import './user_settings_notifications.scss';
const WHITE_SPACE_REGEX = /\s+/g; const WHITE_SPACE_REGEX = /\s+/g;
const COMMA_REGEX = /,/g; const COMMA_REGEX = /,/g;
@ -213,9 +215,6 @@ function getDefaultStateFromProps(props: Props): State {
} }
class NotificationsTab extends React.PureComponent<Props, State> { class NotificationsTab extends React.PureComponent<Props, State> {
drawerRef: RefObject<HTMLHeadingElement>;
wrapperRef: RefObject<HTMLDivElement>;
static defaultProps = { static defaultProps = {
activeSection: '', activeSection: '',
}; };
@ -224,8 +223,6 @@ class NotificationsTab extends React.PureComponent<Props, State> {
super(props); super(props);
this.state = getDefaultStateFromProps(props); this.state = getDefaultStateFromProps(props);
this.drawerRef = React.createRef();
this.wrapperRef = React.createRef();
} }
handleSubmit = async () => { handleSubmit = async () => {
@ -328,7 +325,9 @@ class NotificationsTab extends React.PureComponent<Props, State> {
a11yFocus(e?.currentTarget as HTMLElement); a11yFocus(e?.currentTarget as HTMLElement);
}; };
handleEmailRadio = (enableEmail: UserNotifyProps['email']): void => this.setState({enableEmail}); handleEmailRadio = (enableEmail: UserNotifyProps['email']): void => {
this.setState({enableEmail});
};
handleChangeForUsernameKeyCheckbox = (event: ChangeEvent<HTMLInputElement>) => { handleChangeForUsernameKeyCheckbox = (event: ChangeEvent<HTMLInputElement>) => {
const {target: {checked}} = event; const {target: {checked}} = event;
@ -873,7 +872,6 @@ class NotificationsTab extends React.PureComponent<Props, State> {
isClearable={false} isClearable={false}
isMulti={true} isMulti={true}
styles={customKeywordsSelectorStyles} styles={customKeywordsSelectorStyles}
className='multiInput'
placeholder='' placeholder=''
components={{ components={{
DropdownIndicator: () => null, DropdownIndicator: () => null,
@ -966,7 +964,6 @@ class NotificationsTab extends React.PureComponent<Props, State> {
isClearable={false} isClearable={false}
isMulti={true} isMulti={true}
styles={customKeywordsSelectorStyles} styles={customKeywordsSelectorStyles}
className='multiInput'
placeholder='' placeholder=''
components={{ components={{
DropdownIndicator: () => null, DropdownIndicator: () => null,
@ -1265,63 +1262,45 @@ class NotificationsTab extends React.PureComponent<Props, State> {
return ( return (
<div id='notificationSettings'> <div id='notificationSettings'>
<div className='modal-header'> <SettingMobileHeader
<button closeModal={this.props.closeModal}
id='closeButton' collapseModal={this.props.collapseModal}
type='button' text={
className='close'
data-dismiss='modal'
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
ref={this.drawerRef}
>
<div className='modal-back'>
<i
className='fa fa-angle-left'
aria-label={this.props.intl.formatMessage({
id: 'generic_icons.collapse',
defaultMessage: 'Collapse Icon',
})}
onClick={this.props.collapseModal}
/>
</div>
<FormattedMessage <FormattedMessage
id='user.settings.notifications.title' id='user.settings.notifications.title'
defaultMessage='Notification Settings' defaultMessage='Notification Settings'
/> />
</h4> }
</div> />
<div <div
ref={this.wrapperRef}
className='user-settings' className='user-settings'
> >
<div className='notificationSettingsModalHeader'> <SettingDesktopHeader
<h3 id='notificationSettingsTitle'
id='notificationSettingsTitle' text={
className='tab-header'
>
<FormattedMessage <FormattedMessage
id='user.settings.notifications.header' id='user.settings.notifications.header'
defaultMessage='Notifications' defaultMessage='Notifications'
/> />
</h3> }
<FormattedMessage info={
id='user.settings.notifications.learnMore' <FormattedMessage
defaultMessage='<a>Learn more about notifications</a>' id='user.settings.notifications.learnMore'
values={{ defaultMessage='<a>Learn more about notifications</a>'
a: (chunks: string) => (( values={{
<ExternalLink href='https://mattermost.com/pl/about-notifications'> a: (chunks: string) => ((
<LightbulbOutlineIcon/> <ExternalLink
<span>{chunks}</span> href='https://mattermost.com/pl/about-notifications'
</ExternalLink> className='btn btn-link'
)), >
}} <LightbulbOutlineIcon className='circular-border'/>
/> <span>{chunks}</span>
</div> </ExternalLink>
)),
}}
/>
}
/>
<div className='divider-dark first'/> <div className='divider-dark first'/>
<DesktopNotificationSettings <DesktopNotificationSettings
active={this.props.activeSection === 'desktop'} active={this.props.activeSection === 'desktop'}
@ -1396,6 +1375,10 @@ class NotificationsTab extends React.PureComponent<Props, State> {
} }
const customKeywordsSelectorStyles: ReactSelectStyles = { const customKeywordsSelectorStyles: ReactSelectStyles = {
container: ((baseStyle) => ({
...baseStyle,
marginBlockStart: '10px',
})),
control: ((baseStyles) => ({ control: ((baseStyles) => ({
...baseStyles, ...baseStyles,
backgroundColor: 'var(--center-channel-bg)', backgroundColor: 'var(--center-channel-bg)',

View File

@ -8,7 +8,7 @@ import React from 'react';
import {renderWithContext} from 'tests/react_testing_utils'; import {renderWithContext} from 'tests/react_testing_utils';
import PluginTab from './plugin'; import PluginTab from './index';
type Props = ComponentProps<typeof PluginTab>; type Props = ComponentProps<typeof PluginTab>;

View File

@ -9,8 +9,8 @@ import type {PluginConfiguration} from 'types/plugins/user_settings';
import PluginAction from './plugin_action'; import PluginAction from './plugin_action';
import PluginSetting from './plugin_setting'; import PluginSetting from './plugin_setting';
import SettingDesktopHeader from '../setting_desktop_header'; import SettingDesktopHeader from '../headers/setting_desktop_header';
import SettingMobileHeader from '../setting_mobile_header'; import SettingMobileHeader from '../headers/setting_mobile_header';
type Props = { type Props = {
updateSection: (section: string) => void; updateSection: (section: string) => void;

View File

@ -2,51 +2,27 @@
exports[`components/user_settings/display/UserSettingsDisplay should match snapshot, enable gitlab 1`] = ` exports[`components/user_settings/display/UserSettingsDisplay should match snapshot, enable gitlab 1`] = `
<div> <div>
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<i
className="fa fa-angle-left"
onClick={[MockFunction]}
title="Collapse Icon"
/>
</div>
<MemoizedFormattedMessage
defaultMessage="Security Settings" defaultMessage="Security Settings"
id="user.settings.security.title" id="user.settings.security.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header" text={
> <Memo(MemoizedFormattedMessage)
<MemoizedFormattedMessage defaultMessage="Security Settings"
defaultMessage="Security Settings" id="user.settings.security.title"
id="user.settings.security.title" />
/> }
</h3> />
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -163,51 +139,27 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
exports[`components/user_settings/display/UserSettingsDisplay should match snapshot, enable google 1`] = ` exports[`components/user_settings/display/UserSettingsDisplay should match snapshot, enable google 1`] = `
<div> <div>
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<i
className="fa fa-angle-left"
onClick={[MockFunction]}
title="Collapse Icon"
/>
</div>
<MemoizedFormattedMessage
defaultMessage="Security Settings" defaultMessage="Security Settings"
id="user.settings.security.title" id="user.settings.security.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header" text={
> <Memo(MemoizedFormattedMessage)
<MemoizedFormattedMessage defaultMessage="Security Settings"
defaultMessage="Security Settings" id="user.settings.security.title"
id="user.settings.security.title" />
/> }
</h3> />
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -324,51 +276,27 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
exports[`components/user_settings/display/UserSettingsDisplay should match snapshot, enable office365 1`] = ` exports[`components/user_settings/display/UserSettingsDisplay should match snapshot, enable office365 1`] = `
<div> <div>
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<i
className="fa fa-angle-left"
onClick={[MockFunction]}
title="Collapse Icon"
/>
</div>
<MemoizedFormattedMessage
defaultMessage="Security Settings" defaultMessage="Security Settings"
id="user.settings.security.title" id="user.settings.security.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header" text={
> <Memo(MemoizedFormattedMessage)
<MemoizedFormattedMessage defaultMessage="Security Settings"
defaultMessage="Security Settings" id="user.settings.security.title"
id="user.settings.security.title" />
/> }
</h3> />
<div <div
className="divider-dark first" className="divider-dark first"
/> />
@ -485,51 +413,27 @@ exports[`components/user_settings/display/UserSettingsDisplay should match snaps
exports[`components/user_settings/display/UserSettingsDisplay should match snapshot, enable openID 1`] = ` exports[`components/user_settings/display/UserSettingsDisplay should match snapshot, enable openID 1`] = `
<div> <div>
<div <SettingMobileHeader
className="modal-header" closeModal={[MockFunction]}
> collapseModal={[MockFunction]}
<button text={
aria-label="Close" <Memo(MemoizedFormattedMessage)
className="close"
data-dismiss="modal"
onClick={[MockFunction]}
type="button"
>
<span
aria-hidden="true"
>
×
</span>
</button>
<h4
className="modal-title"
>
<div
className="modal-back"
>
<i
className="fa fa-angle-left"
onClick={[MockFunction]}
title="Collapse Icon"
/>
</div>
<MemoizedFormattedMessage
defaultMessage="Security Settings" defaultMessage="Security Settings"
id="user.settings.security.title" id="user.settings.security.title"
/> />
</h4> }
</div> />
<div <div
className="user-settings" className="user-settings"
> >
<h3 <SettingDesktopHeader
className="tab-header" text={
> <Memo(MemoizedFormattedMessage)
<MemoizedFormattedMessage defaultMessage="Security Settings"
defaultMessage="Security Settings" id="user.settings.security.title"
id="user.settings.security.title" />
/> }
</h3> />
<div <div
className="divider-dark first" className="divider-dark first"
/> />

View File

@ -27,6 +27,9 @@ import * as Utils from 'utils/utils';
import MfaSection from './mfa_section'; import MfaSection from './mfa_section';
import UserAccessTokenSection from './user_access_token_section'; import UserAccessTokenSection from './user_access_token_section';
import SettingDesktopHeader from '../headers/setting_desktop_header';
import SettingMobileHeader from '../headers/setting_mobile_header';
const SECTION_MFA = 'mfa'; const SECTION_MFA = 'mfa';
const SECTION_PASSWORD = 'password'; const SECTION_PASSWORD = 'password';
const SECTION_SIGNIN = 'signin'; const SECTION_SIGNIN = 'signin';
@ -990,45 +993,25 @@ export class SecurityTab extends React.PureComponent<Props, State> {
return ( return (
<div> <div>
<div className='modal-header'> <SettingMobileHeader
<button closeModal={this.props.closeModal}
type='button' collapseModal={this.props.collapseModal}
className='close' text={
data-dismiss='modal'
aria-label={this.props.intl.formatMessage({
id: 'user.settings.security.close',
defaultMessage: 'Close',
})}
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
>
<div className='modal-back'>
<i
className='fa fa-angle-left'
title={this.props.intl.formatMessage({
id: 'generic_icons.collapse',
defaultMessage: 'Collapse Icon',
})}
onClick={this.props.collapseModal}
/>
</div>
<FormattedMessage <FormattedMessage
id='user.settings.security.title' id='user.settings.security.title'
defaultMessage='Security Settings' defaultMessage='Security Settings'
/> />
</h4> }
</div> />
<div className='user-settings'> <div className='user-settings'>
<h3 className='tab-header'> <SettingDesktopHeader
<FormattedMessage text={
id='user.settings.security.title' <FormattedMessage
defaultMessage='Security Settings' id='user.settings.security.title'
/> defaultMessage='Security Settings'
</h3> />
}
/>
<div className='divider-dark first'/> <div className='divider-dark first'/>
{passwordSection} {passwordSection}
<div className='divider-light'/> <div className='divider-light'/>

View File

@ -1,15 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
type Props = {
text: string;
}
const SettingDesktopHeader = ({text}: Props) => (
<h3 className='tab-header'>
{text}
</h3>
);
export default SettingDesktopHeader;

View File

@ -2,11 +2,14 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react'; import React from 'react';
import {FormattedMessage, useIntl} from 'react-intl'; import {FormattedMessage} from 'react-intl';
import LimitVisibleGMsDMs from './limit_visible_gms_dms'; import LimitVisibleGMsDMs from './limit_visible_gms_dms';
import ShowUnreadsCategory from './show_unreads_category'; import ShowUnreadsCategory from './show_unreads_category';
import SettingDesktopHeader from '../headers/setting_desktop_header';
import SettingMobileHeader from '../headers/setting_mobile_header';
export interface Props { export interface Props {
updateSection: (section: string) => void; updateSection: (section: string) => void;
activeSection: string; activeSection: string;
@ -15,47 +18,31 @@ export interface Props {
} }
export default function UserSettingsSidebar(props: Props): JSX.Element { export default function UserSettingsSidebar(props: Props): JSX.Element {
const {formatMessage} = useIntl();
return ( return (
<div> <div>
<div className='modal-header'> <SettingMobileHeader
<button closeModal={props.closeModal}
id='closeButton' collapseModal={props.collapseModal}
type='button' text={
className='close'
data-dismiss='modal'
aria-label='Close'
onClick={props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
<h4 className='modal-title'>
<div
className='modal-back'
onClick={props.collapseModal}
>
<i
className='fa fa-angle-left'
title={formatMessage({id: 'generic_icons.collapse', defaultMessage: 'Collapse Icon'})}
/>
</div>
<FormattedMessage <FormattedMessage
id='user.settings.sidebar.title' id='user.settings.sidebar.title'
defaultMessage='Sidebar Settings' defaultMessage='Sidebar Settings'
/> />
</h4> }
</div> />
<div <div
id='sidebarTitle' id='sidebarTitle'
className='user-settings' className='user-settings'
> >
<h3 className='tab-header'> <SettingDesktopHeader
<FormattedMessage text={
id='user.settings.sidebar.title' <FormattedMessage
defaultMessage='Sidebar Settings' id='user.settings.sidebar.title'
/> defaultMessage='Sidebar Settings'
</h3> />
}
/>
<div className='divider-dark first'/> <div className='divider-dark first'/>
<ShowUnreadsCategory <ShowUnreadsCategory
active={props.activeSection === 'showUnreadsCategory'} active={props.activeSection === 'showUnreadsCategory'}