[MM-57081] Spacing, font and cutoff issues on Channel notifications preferences modal (#26478)

This commit is contained in:
M-ZubairAhmed
2024-03-21 10:25:10 +00:00
committed by GitHub
parent e43ed713d8
commit 959e333031
25 changed files with 609 additions and 856 deletions

View File

@@ -53,10 +53,15 @@ describe('CRT Desktop notifications', () => {
cy.visit(testChannelUrl);
cy.uiOpenChannelMenu('Notification Preferences');
cy.get('[data-testid="muteChannel"]').click().then(() => {
cy.get('.AlertBanner--app').should('be.visible');
});
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
// # Click on Mute Channel to Unmute Channel
cy.findByText('Mute channel').should('be.visible').click({force: true});
// * Verify that channel is muted alert is visible
cy.findByText('This channel is muted').should('be.visible');
// # Save the changes
cy.findByText('Save').should('be.visible').click();
// Setup notification spy
spyNotificationAs('notifySpy', 'granted');
@@ -64,8 +69,8 @@ describe('CRT Desktop notifications', () => {
// # Set users notification settings
cy.uiOpenChannelMenu('Notification Preferences');
// # click on Mute Channel to Unmute Channel
cy.get('[data-testid="muteChannel"]').click();
// # Click on Mute Channel to Unmute Channel
cy.findByText('Mute channel').should('be.visible').click({force: true});
// # Click "Desktop Notifications"
cy.findByText('Desktop Notifications').should('be.visible');
@@ -83,15 +88,16 @@ describe('CRT Desktop notifications', () => {
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-none').should('be.visible').click();
cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-none').should('be.checked');
// # click on Save button
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
// # Save the changes
cy.findByText('Save').should('be.visible').click();
// # Set users notification settings
cy.uiOpenChannelMenu('Notification Preferences');
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-none').should('be.checked');
cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-all').scrollIntoView().should('be.visible').click();
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
// # Save the changes
cy.findByText('Save').should('be.visible').click();
// # Post a root message as other user
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {
@@ -137,6 +143,7 @@ describe('CRT Desktop notifications', () => {
it('MM-T4417_2 Click on sameMobileSettingsDesktop and check if additional settings still appears', () => {
cy.visit(testChannelUrl);
cy.uiOpenChannelMenu('Notification Preferences');
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click().then(() => {
cy.get('[data-testid="desktopReplyThreads"]').should('be.visible').click();
});
@@ -157,8 +164,8 @@ describe('CRT Desktop notifications', () => {
cy.get('[data-testid="autoFollowThreads"]').should('be.visible').click();
// # click on Save button
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
// # Save the changes
cy.findByText('Save').should('be.visible').click();
});
it('MM-T4417_3 Trigger notifications only on mention replies when channel setting is unchecked', () => {
@@ -168,7 +175,9 @@ describe('CRT Desktop notifications', () => {
spyNotificationAs('notifySpy', 'granted');
cy.uiOpenChannelMenu('Notification Preferences');
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click();
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
// # Save the changes
cy.findByText('Save').should('be.visible').click();
// # Post a root message as other user
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {

View File

@@ -1,147 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React, {useCallback, useState} from 'react';
import {useIntl} from 'react-intl';
import {
AlertOutlineIcon,
CheckIcon,
CloseIcon,
InformationOutlineIcon,
} from '@mattermost/compass-icons/components';
import OverlayTrigger from 'components/overlay_trigger';
import Tooltip from 'components/tooltip';
import Constants from 'utils/constants';
import './alert_banner.scss';
export type ModeType = 'danger' | 'warning' | 'info' | 'success';
export type AlertBannerProps = {
id?: string;
mode: ModeType;
title?: React.ReactNode;
customIcon?: React.ReactNode;
message?: React.ReactNode;
children?: React.ReactNode;
className?: string;
hideIcon?: boolean;
actionButtonLeft?: React.ReactNode;
actionButtonRight?: React.ReactNode;
footerMessage?: React.ReactNode;
closeBtnTooltip?: React.ReactNode;
onDismiss?: () => void;
variant?: 'sys' | 'app';
}
const AlertBanner = ({
id,
mode,
title,
customIcon,
message,
className,
variant = 'sys',
onDismiss,
actionButtonLeft,
actionButtonRight,
closeBtnTooltip,
footerMessage,
hideIcon,
children,
}: AlertBannerProps) => {
const {formatMessage} = useIntl();
const closeText = formatMessage({id: 'alert_banner.tooltipCloseBtn', defaultMessage: 'Close'});
const [tooltipId] = useState(`alert_banner_close_btn_tooltip_${Math.random()}`);
const bannerIcon = useCallback(() => {
if (customIcon) {
return customIcon;
}
if (mode === 'danger' || mode === 'warning') {
return (
<AlertOutlineIcon
size={24}
/>);
} else if (mode === 'success') {
return (
<CheckIcon
size={24}
/>);
}
return (
<InformationOutlineIcon
size={24}
/>);
}, [mode, customIcon]);
return (
<div
data-testid={id}
className={classNames(
'AlertBanner',
mode,
className,
`AlertBanner--${variant}`,
)}
>
{!hideIcon && (
<div className='AlertBanner__icon'>
{bannerIcon()}
</div>
)}
<div className='AlertBanner__body'>
{title && <div className='AlertBanner__title'>{title}</div>}
{message && (
<div
className={classNames({
AlertBanner__message: Boolean(title),
})}
>
{message}
</div>
)}
{children}
{(actionButtonLeft || actionButtonRight) && (
<div className='AlertBanner__actionButtons'>
{actionButtonLeft}
{actionButtonRight}
</div>
)}
{
footerMessage && (
<div className='AlertBanner__footerMessage'>
{footerMessage}
</div>
)
}
</div>
{onDismiss && (
<OverlayTrigger
trigger={['hover', 'focus']}
delayShow={Constants.OVERLAY_TIME_DELAY}
placement='left'
overlay={closeBtnTooltip || (
<Tooltip id={tooltipId}>{closeText}</Tooltip>
)}
>
<button
aria-label={closeText}
className='AlertBanner__closeButton'
onClick={onDismiss}
>
<CloseIcon
size={18}
/>
</button>
</OverlayTrigger>
)}
</div>
);
};
export default AlertBanner;

View File

@@ -3,7 +3,6 @@
.AlertBanner {
display: flex;
overflow: hidden;
align-items: flex-start;
padding: 14px;
border: 1px solid;

View File

@@ -4,7 +4,7 @@
import {shallow} from 'enzyme';
import React from 'react';
import AlertBanner from './alert_banner';
import AlertBanner from 'components/alert_banner';
describe('Components/AlertBanner', () => {
test('should match snapshot', () => {

View File

@@ -0,0 +1,139 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import classNames from 'classnames';
import type {ReactNode} from 'react';
import React, {useMemo} from 'react';
import {useIntl} from 'react-intl';
import {
AlertOutlineIcon,
CheckIcon,
CloseIcon,
InformationOutlineIcon,
} from '@mattermost/compass-icons/components';
import WithTooltip from 'components/with_tooltip';
import './alert_banner.scss';
export type ModeType = 'danger' | 'warning' | 'info' | 'success';
export type AlertBannerProps = {
id?: string;
mode: ModeType;
title?: ReactNode;
customIcon?: ReactNode;
message?: ReactNode;
children?: ReactNode;
className?: string;
hideIcon?: boolean;
actionButtonLeft?: ReactNode;
actionButtonRight?: ReactNode;
footerMessage?: ReactNode;
closeBtnTooltip?: string;
onDismiss?: () => void;
variant?: 'sys' | 'app';
}
const AlertBanner = ({
id,
mode,
title,
customIcon,
message,
className,
variant = 'sys',
onDismiss,
actionButtonLeft,
actionButtonRight,
closeBtnTooltip,
footerMessage,
hideIcon,
children,
}: AlertBannerProps) => {
const {formatMessage} = useIntl();
const bannerIcon = useMemo(() => {
if (customIcon) {
return customIcon;
}
if (mode === 'danger' || mode === 'warning') {
return <AlertOutlineIcon size={24}/>;
} else if (mode === 'success') {
return <CheckIcon size={24}/>;
}
return <InformationOutlineIcon size={24}/>;
}, [mode, customIcon]);
const dismissButton = useMemo(() => {
return (
<button
className='AlertBanner__closeButton'
aria-label={formatMessage({id: 'alert_banner.tooltipCloseBtn', defaultMessage: 'Close'})}
onClick={onDismiss}
>
<CloseIcon size={18}/>
</button>
);
}, [onDismiss]);
return (
<div
data-testid={id}
className={classNames(
'AlertBanner',
mode,
className,
`AlertBanner--${variant}`,
)}
>
{!hideIcon && (
<div className='AlertBanner__icon'>
{bannerIcon}
</div>
)}
<div className='AlertBanner__body'>
{title &&
<div className='AlertBanner__title'>
{title}
</div>
}
{message && (
<div
className={classNames({AlertBanner__message: Boolean(title)})}
>
{message}
</div>
)}
{children}
{(actionButtonLeft || actionButtonRight) && (
<div className='AlertBanner__actionButtons'>
{actionButtonLeft}
{actionButtonRight}
</div>
)}
{footerMessage && (
<div className='AlertBanner__footerMessage'>
{footerMessage}
</div>
)}
</div>
{onDismiss && closeBtnTooltip && (
<WithTooltip
id={`alertBannerTooltip_${id}`}
title={closeBtnTooltip}
placement='left'
>
{dismissButton}
</WithTooltip>
)}
{onDismiss && !closeBtnTooltip && (
dismissButton
)}
</div>
);
};
export default AlertBanner;

View File

@@ -52,20 +52,11 @@ Object {
class="mm-modal-header__ctr"
>
<button
class="style--none mm-modal-header__close-btn"
class="btn btn-icon"
>
<svg
fill="currentcolor"
height="24"
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
<i
class="icon icon-close"
/>
</button>
</div>
</header>
@@ -76,16 +67,16 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<h4
class="mm-modal-generic-section__title"
class="modalSectionTitle"
>
Mute or ignore
</h4>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -108,14 +99,13 @@ Object {
/>
Mute channel
</label>
<br />
</fieldset>
</div>
<p
class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description"
>
Turns off notifications for this channel. Youll still see badges if youre mentioned.
Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p>
</div>
<div
@@ -139,7 +129,6 @@ Object {
/>
Ignore mentions for @channel, @here and @all
</label>
<br />
</fieldset>
</div>
<p
@@ -158,25 +147,21 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Desktop Notifications
</h4>
</div>
Desktop Notifications
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -244,16 +229,12 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
>
@@ -271,15 +252,15 @@ Object {
</svg>
Reset to default
</button>
</div>
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -302,7 +283,6 @@ Object {
/>
Use the same notification settings as desktop
</label>
<br />
</fieldset>
</div>
</div>
@@ -370,12 +350,12 @@ Object {
>
<button
class="channel-notifications-settings-modal__cancel-btn"
class="btn btn-tertiary btn-md"
>
Cancel
</button>
<button
class="channel-notifications-settings-modal__save-btn"
class="btn btn-primary btn-md"
>
Save
</button>
@@ -496,20 +476,11 @@ Object {
class="mm-modal-header__ctr"
>
<button
class="style--none mm-modal-header__close-btn"
class="btn btn-icon"
>
<svg
fill="currentcolor"
height="24"
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
<i
class="icon icon-close"
/>
</button>
</div>
</header>
@@ -520,16 +491,16 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<h4
class="mm-modal-generic-section__title"
class="modalSectionTitle"
>
Mute or ignore
</h4>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -552,14 +523,13 @@ Object {
/>
Mute channel
</label>
<br />
</fieldset>
</div>
<p
class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description"
>
Turns off notifications for this channel. Youll still see badges if youre mentioned.
Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p>
</div>
<div
@@ -583,7 +553,6 @@ Object {
/>
Ignore mentions for @channel, @here and @all
</label>
<br />
</fieldset>
</div>
<p
@@ -602,16 +571,12 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Desktop Notifications
</h4>
Desktop Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
>
@@ -629,15 +594,15 @@ Object {
</svg>
Reset to default
</button>
</div>
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -705,16 +670,12 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
>
@@ -732,15 +693,15 @@ Object {
</svg>
Reset to default
</button>
</div>
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -763,7 +724,6 @@ Object {
/>
Use the same notification settings as desktop
</label>
<br />
</fieldset>
</div>
</div>
@@ -831,12 +791,12 @@ Object {
>
<button
class="channel-notifications-settings-modal__cancel-btn"
class="btn btn-tertiary btn-md"
>
Cancel
</button>
<button
class="channel-notifications-settings-modal__save-btn"
class="btn btn-primary btn-md"
>
Save
</button>
@@ -957,20 +917,11 @@ Object {
class="mm-modal-header__ctr"
>
<button
class="style--none mm-modal-header__close-btn"
class="btn btn-icon"
>
<svg
fill="currentcolor"
height="24"
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
<i
class="icon icon-close"
/>
</button>
</div>
</header>
@@ -981,16 +932,16 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<h4
class="mm-modal-generic-section__title"
class="modalSectionTitle"
>
Mute or ignore
</h4>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -1013,14 +964,13 @@ Object {
/>
Mute channel
</label>
<br />
</fieldset>
</div>
<p
class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description"
>
Turns off notifications for this channel. Youll still see badges if youre mentioned.
Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p>
</div>
<div
@@ -1044,7 +994,6 @@ Object {
/>
Ignore mentions for @channel, @here and @all
</label>
<br />
</fieldset>
</div>
<p
@@ -1063,25 +1012,21 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Desktop Notifications
</h4>
</div>
Desktop Notifications
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -1149,16 +1094,12 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
>
@@ -1176,15 +1117,15 @@ Object {
</svg>
Reset to default
</button>
</div>
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -1207,7 +1148,6 @@ Object {
/>
Use the same notification settings as desktop
</label>
<br />
</fieldset>
</div>
</div>
@@ -1275,12 +1215,12 @@ Object {
>
<button
class="channel-notifications-settings-modal__cancel-btn"
class="btn btn-tertiary btn-md"
>
Cancel
</button>
<button
class="channel-notifications-settings-modal__save-btn"
class="btn btn-primary btn-md"
>
Save
</button>
@@ -1401,20 +1341,11 @@ Object {
class="mm-modal-header__ctr"
>
<button
class="style--none mm-modal-header__close-btn"
class="btn btn-icon"
>
<svg
fill="currentcolor"
height="24"
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
<i
class="icon icon-close"
/>
</button>
</div>
</header>
@@ -1425,16 +1356,16 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<h4
class="mm-modal-generic-section__title"
class="modalSectionTitle"
>
Mute or ignore
</h4>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -1457,14 +1388,13 @@ Object {
/>
Mute channel
</label>
<br />
</fieldset>
</div>
<p
class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description"
>
Turns off notifications for this channel. Youll still see badges if youre mentioned.
Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p>
</div>
<div
@@ -1488,7 +1418,6 @@ Object {
/>
Ignore mentions for @channel, @here and @all
</label>
<br />
</fieldset>
</div>
<p
@@ -1502,6 +1431,7 @@ Object {
</section>
<div
class="AlertBanner info AlertBanner--app"
data-testid="channelNotificationsMutedBanner"
>
<div
class="AlertBanner__icon"
@@ -1540,12 +1470,12 @@ Object {
>
<button
class="channel-notifications-settings-modal__cancel-btn"
class="btn btn-tertiary btn-md"
>
Cancel
</button>
<button
class="channel-notifications-settings-modal__save-btn"
class="btn btn-primary btn-md"
>
Save
</button>
@@ -1666,20 +1596,11 @@ Object {
class="mm-modal-header__ctr"
>
<button
class="style--none mm-modal-header__close-btn"
class="btn btn-icon"
>
<svg
fill="currentcolor"
height="24"
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
<i
class="icon icon-close"
/>
</button>
</div>
</header>
@@ -1690,16 +1611,16 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<h4
class="mm-modal-generic-section__title"
class="modalSectionTitle"
>
Mute or ignore
</h4>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -1722,14 +1643,13 @@ Object {
/>
Mute channel
</label>
<br />
</fieldset>
</div>
<p
class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description"
>
Turns off notifications for this channel. Youll still see badges if youre mentioned.
Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p>
</div>
<div
@@ -1753,7 +1673,6 @@ Object {
/>
Ignore mentions for @channel, @here and @all
</label>
<br />
</fieldset>
</div>
<p
@@ -1772,25 +1691,21 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Desktop Notifications
</h4>
</div>
Desktop Notifications
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -1858,16 +1773,12 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
>
@@ -1885,15 +1796,15 @@ Object {
</svg>
Reset to default
</button>
</div>
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -1916,7 +1827,6 @@ Object {
/>
Use the same notification settings as desktop
</label>
<br />
</fieldset>
</div>
</div>
@@ -1928,12 +1838,12 @@ Object {
>
<button
class="channel-notifications-settings-modal__cancel-btn"
class="btn btn-tertiary btn-md"
>
Cancel
</button>
<button
class="channel-notifications-settings-modal__save-btn"
class="btn btn-primary btn-md"
>
Save
</button>
@@ -2054,20 +1964,11 @@ Object {
class="mm-modal-header__ctr"
>
<button
class="style--none mm-modal-header__close-btn"
class="btn btn-icon"
>
<svg
fill="currentcolor"
height="24"
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
<i
class="icon icon-close"
/>
</button>
</div>
</header>
@@ -2078,16 +1979,16 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<h4
class="mm-modal-generic-section__title"
class="modalSectionTitle"
>
Mute or ignore
</h4>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -2110,14 +2011,13 @@ Object {
/>
Mute channel
</label>
<br />
</fieldset>
</div>
<p
class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description"
>
Turns off notifications for this channel. Youll still see badges if youre mentioned.
Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p>
</div>
<div
@@ -2141,7 +2041,6 @@ Object {
/>
Ignore mentions for @channel, @here and @all
</label>
<br />
</fieldset>
</div>
<p
@@ -2160,25 +2059,21 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Desktop Notifications
</h4>
</div>
Desktop Notifications
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -2246,16 +2141,12 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<div
class="mm-modal-generic-section__row"
<h4
class="modalSectionTitle"
>
<h4
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
Mobile Notifications
<button
class="channel-notifications-settings-modal__reset-btn"
>
@@ -2273,15 +2164,15 @@ Object {
</svg>
Reset to default
</button>
</div>
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -2304,7 +2195,6 @@ Object {
/>
Use the same notification settings as desktop
</label>
<br />
</fieldset>
</div>
</div>
@@ -2373,21 +2263,21 @@ Object {
class="mm-modal-generic-section"
>
<div
class="mm-modal-generic-section__title-description-ctr"
class="modalSectionHeader"
>
<h4
class="mm-modal-generic-section__title"
class="modalSectionTitle"
>
Follow all threads in this channel
</h4>
<p
class="mm-modal-generic-section__description"
class="modalSectionDescription"
>
When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.
</p>
</div>
<div
class="mm-modal-generic-section__content"
class="modalSectionContent"
>
<div
class="mm-modal-generic-section-item"
@@ -2410,7 +2300,6 @@ Object {
/>
Automatically follow threads in this channel
</label>
<br />
</fieldset>
</div>
</div>
@@ -2422,12 +2311,12 @@ Object {
>
<button
class="channel-notifications-settings-modal__cancel-btn"
class="btn btn-tertiary btn-md"
>
Cancel
</button>
<button
class="channel-notifications-settings-modal__save-btn"
class="btn btn-primary btn-md"
>
Save
</button>

View File

@@ -31,7 +31,7 @@
max-width: 1024px;
min-height: 150px;
flex-direction: column;
padding: 32px;
padding: 28px 32px;
gap: 24px;
overflow-x: hidden;
overflow-y: auto;
@@ -72,48 +72,6 @@
color: var(--error-text);
}
&__save-btn {
padding: 12px 20px 12px 20px;
border: none;
border-radius: 4px;
background: var(--button-bg);
color: var(--button-color);
font-size: 14px;
font-weight: 600;
line-height: 14px;
text-transform: capitalize;
&:hover,
&:active {
background: rgba(var(--button-bg-rgb), 0.12);
background: linear-gradient(0deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08)), var(--button-bg);
}
&:focus {
box-sizing: border-box;
padding: 10px 18px;
border: 2px solid var(--sidebar-text-active-border);
box-shadow: none;
}
}
&__cancel-btn {
padding: 12px 20px 12px 20px;
border: none;
border-radius: 4px;
background: rgba(var(--button-bg-rgb), 0.08);
color: var(--button-bg);
font-size: 14px;
font-weight: 600;
line-height: 14px;
text-transform: capitalize;
&:hover,
&:active {
background: rgba(var(--button-bg-rgb), 0.12);
}
}
&__divider {
border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
}

View File

@@ -43,10 +43,8 @@ type Props = PropsFromRedux & {
};
function getUseSameDesktopSetting(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: ChannelMemberNotifyProps) {
const isSameAsDesktop = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop === channelMemberNotifyProps?.push :
currentUserNotifyProps.push === currentUserNotifyProps.desktop;
const isSameAsDesktopThreads = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop_threads === channelMemberNotifyProps?.push_threads :
currentUserNotifyProps.push_threads === currentUserNotifyProps.desktop_threads;
const isSameAsDesktop = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop === channelMemberNotifyProps?.push : currentUserNotifyProps.push === currentUserNotifyProps.desktop;
const isSameAsDesktopThreads = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop_threads === channelMemberNotifyProps?.push_threads : currentUserNotifyProps.push_threads === currentUserNotifyProps.desktop_threads;
return isSameAsDesktop && isSameAsDesktopThreads;
}
@@ -106,16 +104,34 @@ export default function ChannelNotificationsModal(props: Props) {
setSettings((prevSettings) => ({...prevSettings, push: prevSettings.desktop, push_threads: prevSettings.desktop_threads}));
}, []);
const MuteIgnoreSectionContent = (
const MuteOrIgnoreSectionContent = (
<>
<CheckboxSettingItem
description={utils.MuteChannelDesc}
inputFieldTitle={
<FormattedMessage
id='channel_notifications.muteChannelTitle'
defaultMessage='Mute channel'
/>
}
description={formatMessage({
id: 'channel_notifications.muteChannelDesc',
defaultMessage: 'Turns off notifications for this channel. You\'ll still see badges if you\'re mentioned.',
})}
inputFieldValue={settings.mark_unread === 'mention'}
inputFieldData={utils.MuteChannelInputFieldData}
handleChange={(e) => handleChange({mark_unread: e ? 'mention' : 'all'})}
/>
<CheckboxSettingItem
description={utils.IgnoreMentionsDesc}
inputFieldTitle={
<FormattedMessage
id='channel_notifications.ignoreMentionsTitle'
defaultMessage='Ignore mentions for @channel, @here and @all'
/>
}
description={formatMessage({
id: 'channel_notifications.ignoreMentionsDesc',
defaultMessage: 'When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel',
})}
inputFieldValue={settings.ignore_channel_mentions === 'on'}
inputFieldData={utils.IgnoreMentionsInputFieldData}
handleChange={(e) => handleChange({ignore_channel_mentions: e ? 'on' : 'off'})}
@@ -126,16 +142,28 @@ export default function ChannelNotificationsModal(props: Props) {
const DesktopNotificationsSectionContent = (
<>
<RadioSettingItem
title={utils.NotifyMeTitle}
title={formatMessage({
id: 'channel_notifications.NotifyMeTitle',
defaultMessage: 'Notify me about…',
})}
inputFieldValue={settings.desktop}
inputFieldData={utils.desktopNotificationInputFieldData(props.currentUser.notify_props.desktop)}
handleChange={(e) => handleChange({desktop: e.target.value})}
/>
{props.collapsedReplyThreads && settings.desktop === 'mention' &&
<CheckboxSettingItem
title={utils.ThreadsReplyTitle}
title={formatMessage({
id: 'channel_notifications.ThreadsReplyTitle',
defaultMessage: 'Thread reply notifications',
})}
inputFieldValue={settings.desktop_threads === 'all'}
inputFieldData={utils.DesktopReplyThreadsInputFieldData}
inputFieldTitle={
<FormattedMessage
id='channel_notifications.checkbox.threadsReplyTitle'
defaultMessage="Notify me about replies to threads I\'m following"
/>
}
handleChange={(e) => handleChange({desktop_threads: e ? 'all' : 'mention'})}
/>}
</>
@@ -144,6 +172,12 @@ export default function ChannelNotificationsModal(props: Props) {
const MobileNotificationsSectionContent = (
<>
<CheckboxSettingItem
inputFieldTitle={
<FormattedMessage
id='channel_notifications.checkbox.sameMobileSettingsDesktop'
defaultMessage='Use the same notification settings as desktop'
/>
}
inputFieldValue={mobileSettingsSameAsDesktop}
inputFieldData={utils.sameMobileSettingsDesktopInputFieldData}
handleChange={() => handleMobileSettingsChange()}
@@ -151,14 +185,26 @@ export default function ChannelNotificationsModal(props: Props) {
{!mobileSettingsSameAsDesktop && (
<>
<RadioSettingItem
title={utils.NotifyMeTitle}
title={formatMessage({
id: 'channel_notifications.NotifyMeTitle',
defaultMessage: 'Notify me about…',
})}
inputFieldValue={settings.push}
inputFieldData={utils.mobileNotificationInputFieldData(props.currentUser.notify_props.push)}
handleChange={(e) => handleChange({push: e.target.value})}
/>
{props.collapsedReplyThreads && settings.push === 'mention' &&
<CheckboxSettingItem
title={utils.ThreadsReplyTitle}
title={formatMessage({
id: 'channel_notifications.ThreadsReplyTitle',
defaultMessage: 'Thread reply notifications',
})}
inputFieldTitle={
<FormattedMessage
id='channel_notifications.checkbox.threadsReplyTitle'
defaultMessage="Notify me about replies to threads I\'m following"
/>
}
inputFieldValue={settings.push_threads === 'all'}
inputFieldData={utils.MobileReplyThreadsInputFieldData}
handleChange={(e) => handleChange({push_threads: e ? 'all' : 'mention'})}
@@ -169,13 +215,17 @@ export default function ChannelNotificationsModal(props: Props) {
);
const AutoFollowThreadsSectionContent = (
<>
<CheckboxSettingItem
inputFieldValue={settings.channel_auto_follow_threads === 'on'}
inputFieldData={utils.AutoFollowThreadsInputFieldData}
handleChange={(e) => handleChange({channel_auto_follow_threads: e ? 'on' : 'off'})}
/>
</>
<CheckboxSettingItem
inputFieldTitle={
<FormattedMessage
id='channel_notifications.checkbox.autoFollowThreadsTitle'
defaultMessage='Automatically follow threads in this channel'
/>
}
inputFieldValue={settings.channel_auto_follow_threads === 'on'}
inputFieldData={utils.AutoFollowThreadsInputFieldData}
handleChange={(e) => handleChange({channel_auto_follow_threads: e ? 'on' : 'off'})}
/>
);
function handleSave() {
@@ -229,25 +279,38 @@ export default function ChannelNotificationsModal(props: Props) {
);
}, [props.currentUser, settings]);
const settingsAndAlertBanner = settings.mark_unread === 'all' ? (
const desktopAndMobileNotificationSectionContent = settings.mark_unread === 'all' ? (
<>
<div className='channel-notifications-settings-modal__divider'/>
<ModalSection
title={utils.DesktopNotificationsSectionTitle}
description={utils.DesktopNotificationsSectionDesc}
content={DesktopNotificationsSectionContent}
title={formatMessage({
id: 'channel_notifications.desktopNotificationsTitle',
defaultMessage: 'Desktop Notifications',
})}
titleSuffix={resetToDefaultBtn('desktop')}
description={formatMessage({
id: 'channel_notifications.desktopNotificationsDesc',
defaultMessage: 'Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.',
})}
content={DesktopNotificationsSectionContent}
/>
<div className='channel-notifications-settings-modal__divider'/>
<ModalSection
title={utils.MobileNotificationsSectionTitle}
description={utils.MobileNotificationsSectionDesc}
content={MobileNotificationsSectionContent}
title={formatMessage({
id: 'channel_notifications.mobileNotificationsTitle',
defaultMessage: 'Mobile Notifications',
})}
titleSuffix={resetToDefaultBtn('push')}
description={formatMessage({
id: 'channel_notifications.mobileNotificationsDesc',
defaultMessage: 'Notification alerts are pushed to your mobile device when there is activity in Mattermost.',
})}
content={MobileNotificationsSectionContent}
/>
</>
) : (
<AlertBanner
id='channelNotificationsMutedBanner'
mode='info'
variant='app'
customIcon={
@@ -292,16 +355,25 @@ export default function ChannelNotificationsModal(props: Props) {
/>
<main className='channel-notifications-settings-modal__body'>
<ModalSection
title={utils.MuteAndIgnoreSectionTitle}
content={MuteIgnoreSectionContent}
title={formatMessage({
id: 'channel_notifications.muteAndIgnore',
defaultMessage: 'Mute or ignore',
})}
content={MuteOrIgnoreSectionContent}
/>
{settingsAndAlertBanner}
{desktopAndMobileNotificationSectionContent}
{props.collapsedReplyThreads &&
<>
<div className='channel-notifications-settings-modal__divider'/>
<ModalSection
title={utils.AutoFollowThreadsTitle}
description={utils.AutoFollowThreadsDesc}
title={formatMessage({
id: 'channel_notifications.autoFollowThreadsTitle',
defaultMessage: 'Follow all threads in this channel',
})}
description={formatMessage({
id: 'channel_notifications.autoFollowThreadsDesc',
defaultMessage: 'When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.',
})}
content={AutoFollowThreadsSectionContent}
/>
</>
@@ -314,8 +386,8 @@ export default function ChannelNotificationsModal(props: Props) {
</span>
}
<button
className='btn btn-tertiary btn-md'
onClick={handleHide}
className='channel-notifications-settings-modal__cancel-btn'
>
<FormattedMessage
id='generic_btn.cancel'
@@ -323,7 +395,7 @@ export default function ChannelNotificationsModal(props: Props) {
/>
</button>
<button
className={'channel-notifications-settings-modal__save-btn'}
className='btn btn-primary btn-md'
onClick={handleSave}
>
<FormattedMessage

View File

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React from 'react';
import {FormattedMessage, defineMessages} from 'react-intl';
import {FormattedMessage} from 'react-intl';
import type {ChannelNotifyProps} from '@mattermost/types/channels';
import type {UserNotifyProps} from '@mattermost/types/users';
@@ -14,163 +14,47 @@ import {NotificationLevels} from 'utils/constants';
export type ChannelMemberNotifyProps = Partial<ChannelNotifyProps> & Pick<UserNotifyProps, 'desktop_threads' | 'push_threads'>
const translations = defineMessages({
MuteAndIgnoreSectionTitle: {
id: 'channel_notifications.muteAndIgnore',
defaultMessage: 'Mute or ignore',
},
NotifyMeTitle: {
id: 'channel_notifications.NotifyMeTitle',
defaultMessage: 'Notify me about…',
},
ThreadsReplyTitle: {
id: 'channel_notifications.ThreadsReplyTitle',
defaultMessage: 'Thread reply notifications',
},
DesktopNotificationsSectionTitle: {
id: 'channel_notifications.desktopNotificationsTitle',
defaultMessage: 'Desktop Notifications',
},
DesktopNotificationsSectionDesc: {
id: 'channel_notifications.desktopNotificationsDesc',
defaultMessage: 'Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.',
},
MobileNotificationsSectionTitle: {
id: 'channel_notifications.mobileNotificationsTitle',
defaultMessage: 'Mobile Notifications',
},
MobileNotificationsSectionDesc: {
id: 'channel_notifications.mobileNotificationsDesc',
defaultMessage: 'Notification alerts are pushed to your mobile device when there is activity in Mattermost.',
},
MuteChannelDesc: {
id: 'channel_notifications.muteChannelDesc',
defaultMessage: 'Turns off notifications for this channel. Youll still see badges if youre mentioned.',
},
IgnoreMentionsDesc: {
id: 'channel_notifications.ignoreMentionsDesc',
defaultMessage: 'When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel',
},
MuteChannelInputFieldTitle: {
id: 'channel_notifications.muteChannelTitle',
defaultMessage: 'Mute channel',
},
DesktopReplyThreadsInputFieldTitle: {
id: 'channel_notifications.checkbox.threadsReplyTitle',
defaultMessage: 'Notify me about replies to threads I\'m following',
},
MobileReplyThreadsInputFieldTitle: {
id: 'channel_notifications.checkbox.threadsReplyTitle',
defaultMessage: 'Notify me about replies to threads I\'m following',
},
sameMobileSettingsDesktopInputFieldTitle: {
id: 'channel_notifications.checkbox.sameMobileSettingsDesktop',
defaultMessage: 'Use the same notification settings as desktop',
},
IgnoreMentionsInputFieldTitle: {
id: 'channel_notifications.ignoreMentionsTitle',
defaultMessage: 'Ignore mentions for @channel, @here and @all',
},
AutoFollowThreadsTitle: {
id: 'channel_notifications.autoFollowThreadsTitle',
defaultMessage: 'Follow all threads in this channel',
},
AutoFollowThreadsDesc: {
id: 'channel_notifications.autoFollowThreadsDesc',
defaultMessage: 'When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.',
},
AutoFollowThreadsInputFieldTitle: {
id: 'channel_notifications.checkbox.autoFollowThreadsTitle',
defaultMessage: 'Automatically follow threads in this channel',
},
});
const desktopNotificationInputFieldOptions = defineMessages({
allNewMessages: {
id: 'channel_notifications.desktopNotificationAllLabel',
defaultMessage: 'All new messages',
},
mentions: {
id: 'channel_notifications.desktopNotificationMentionLabel',
defaultMessage: 'Mentions, direct messages, and keywords only',
},
nothing: {
id: 'channel_notifications.desktopNotificationNothingLabel',
defaultMessage: 'Nothing',
},
});
const mobileNotificationInputFieldOptions = defineMessages({
allNewMessages: {
id: 'channel_notifications.MobileNotificationAllLabel',
defaultMessage: 'All new messages',
},
mentions: {
id: 'channel_notifications.MobileNotificationMentionLabel',
defaultMessage: 'Mentions, direct messages, and keywords only',
},
nothing: {
id: 'channel_notifications.MobileNotificationNothingLabel',
defaultMessage: 'Nothing',
},
});
const MuteChannelInputFieldData: FieldsetCheckbox = {
title: translations.MuteChannelInputFieldTitle,
name: 'mute channel',
dataTestId: 'muteChannel',
};
const DesktopReplyThreadsInputFieldData: FieldsetCheckbox = {
title: translations.DesktopReplyThreadsInputFieldTitle,
name: 'desktop reply threads',
dataTestId: 'desktopReplyThreads',
};
const MobileReplyThreadsInputFieldData: FieldsetCheckbox = {
title: translations.MobileReplyThreadsInputFieldTitle,
name: 'mobile reply threads',
dataTestId: 'mobileReplyThreads',
};
const sameMobileSettingsDesktopInputFieldData: FieldsetCheckbox = {
title: translations.sameMobileSettingsDesktopInputFieldTitle,
export const sameMobileSettingsDesktopInputFieldData: FieldsetCheckbox = {
name: 'same mobile settings as Desktop',
dataTestId: 'sameMobileSettingsDesktop',
};
const IgnoreMentionsInputFieldData: FieldsetCheckbox = {
title: translations.IgnoreMentionsInputFieldTitle,
export const IgnoreMentionsInputFieldData: FieldsetCheckbox = {
name: 'ignore mentions',
dataTestId: 'ignoreMentions',
};
const AutoFollowThreadsInputFieldData: FieldsetCheckbox = {
title: translations.AutoFollowThreadsInputFieldTitle,
export const AutoFollowThreadsInputFieldData: FieldsetCheckbox = {
name: 'auto follow threads',
dataTestId: 'autoFollowThreads',
};
const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
export const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
return {
options: [
{
dataTestId: `desktopNotification-${NotificationLevels.ALL}`,
title: desktopNotificationInputFieldOptions.allNewMessages,
title: (
<FormattedMessage
id='channel_notifications.desktopNotificationAllLabel'
defaultMessage='All new messages'
/>
),
name: `desktopNotification-${NotificationLevels.ALL}`,
key: `desktopNotification-${NotificationLevels.ALL}`,
value: NotificationLevels.ALL,
@@ -182,7 +66,12 @@ const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio
},
{
dataTestId: `desktopNotification-${NotificationLevels.MENTION}`,
title: desktopNotificationInputFieldOptions.mentions,
title: (
<FormattedMessage
id='channel_notifications.desktopNotificationMentionLabel'
defaultMessage='Mentions, direct messages, and keywords only'
/>
),
name: `desktopNotification-${NotificationLevels.MENTION}`,
key: `desktopNotification-${NotificationLevels.MENTION}`,
value: NotificationLevels.MENTION,
@@ -194,7 +83,12 @@ const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio
},
{
dataTestId: `desktopNotification-${NotificationLevels.NONE}`,
title: desktopNotificationInputFieldOptions.nothing,
title: (
<FormattedMessage
id='channel_notifications.desktopNotificationNothingLabel'
defaultMessage='Nothing'
/>
),
name: `desktopNotification-${NotificationLevels.NONE}`,
key: `desktopNotification-${NotificationLevels.NONE}`,
value: NotificationLevels.NONE,
@@ -208,12 +102,17 @@ const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio
};
};
const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
export const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
return {
options: [
{
dataTestId: `MobileNotification-${NotificationLevels.ALL}`,
title: mobileNotificationInputFieldOptions.allNewMessages,
title: (
<FormattedMessage
id='channel_notifications.MobileNotificationAllLabel'
defaultMessage='All new messages'
/>
),
name: `MobileNotification-${NotificationLevels.ALL}`,
key: `MobileNotification-${NotificationLevels.ALL}`,
value: NotificationLevels.ALL,
@@ -225,7 +124,12 @@ const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio
},
{
dataTestId: `MobileNotification-${NotificationLevels.MENTION}`,
title: mobileNotificationInputFieldOptions.mentions,
title: (
<FormattedMessage
id='channel_notifications.MobileNotificationMentionLabel'
defaultMessage='Mentions, direct messages, and keywords only'
/>
),
name: `MobileNotification-${NotificationLevels.MENTION}`,
key: `MobileNotification-${NotificationLevels.MENTION}`,
value: NotificationLevels.MENTION,
@@ -237,7 +141,12 @@ const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio
},
{
dataTestId: `MobileNotification-${NotificationLevels.NONE}`,
title: mobileNotificationInputFieldOptions.nothing,
title: (
<FormattedMessage
id='channel_notifications.MobileNotificationNothingLabel'
defaultMessage='Nothing'
/>
),
name: `MobileNotification-${NotificationLevels.NONE}`,
key: `MobileNotification-${NotificationLevels.NONE}`,
value: NotificationLevels.NONE,
@@ -253,22 +162,11 @@ const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio
const utils = {
desktopNotificationInputFieldData,
DesktopNotificationsSectionDesc: translations.DesktopNotificationsSectionDesc,
DesktopNotificationsSectionTitle: translations.DesktopNotificationsSectionTitle,
IgnoreMentionsDesc: translations.IgnoreMentionsDesc,
IgnoreMentionsInputFieldData,
mobileNotificationInputFieldData,
MobileNotificationsSectionDesc: translations.MobileNotificationsSectionDesc,
MobileNotificationsSectionTitle: translations.MobileNotificationsSectionTitle,
MuteAndIgnoreSectionTitle: translations.MuteAndIgnoreSectionTitle,
MuteChannelDesc: translations.MuteChannelDesc,
MuteChannelInputFieldData,
NotifyMeTitle: translations.NotifyMeTitle,
DesktopReplyThreadsInputFieldData,
ThreadsReplyTitle: translations.ThreadsReplyTitle,
MobileReplyThreadsInputFieldData,
AutoFollowThreadsTitle: translations.AutoFollowThreadsTitle,
AutoFollowThreadsDesc: translations.AutoFollowThreadsDesc,
AutoFollowThreadsInputFieldData,
sameMobileSettingsDesktopInputFieldData,
};

View File

@@ -16,7 +16,6 @@ import useGetLimits from 'components/common/hooks/useGetLimits';
import useGetUsage from 'components/common/hooks/useGetUsage';
import useOpenPricingModal from 'components/common/hooks/useOpenPricingModal';
import NotifyAdminCTA from 'components/notify_admin_cta/notify_admin_cta';
import Tooltip from 'components/tooltip';
import {CloudProducts, LicenseSkus, MattermostFeatures, Preferences} from 'utils/constants';
import {asGBString} from 'utils/limits';
@@ -147,19 +146,13 @@ function FileLimitStickyBanner() {
/>
);
const tooltip = (
<Tooltip id='file_limit_banner_snooze'>
{formatMessage({id: 'create_post.file_limit_sticky_banner.snooze_tooltip', defaultMessage: 'Snooze for {snoozeDays} days'}, {snoozeDays: snoozeCoolOffDays})}
</Tooltip>
);
return (
<StyledDiv id='cloud_file_limit_banner'>
<AlertBanner
mode={'warning'}
variant={'app'}
onDismiss={snoozeBanner}
closeBtnTooltip={tooltip}
closeBtnTooltip={formatMessage({id: 'create_post.file_limit_sticky_banner.snooze_tooltip', defaultMessage: 'Snooze for {snoozeDays} days'}, {snoozeDays: snoozeCoolOffDays})}
title={title}
message={isAdmin ? adminMessage : nonAdminMessage}
/>

View File

@@ -2,27 +2,12 @@
// See LICENSE.txt for license information.
import React, {useCallback, useState} from 'react';
import {defineMessages, useIntl} from 'react-intl';
import {FormattedMessage, useIntl} from 'react-intl';
import SelectTextInput, {type SelectTextInputOption} from 'components/common/select_text_input/select_text_input';
import CheckboxSettingItem from 'components/widgets/modals/components/checkbox_setting_item';
import {type SaveChangesPanelState} from 'components/widgets/modals/components/save_changes_panel';
const translations = defineMessages({
AllowedDomainsTitle: {
id: 'general_tab.AllowedDomainsTitle',
defaultMessage: 'Users with a specific email domain',
},
AllowedDomainsInfo: {
id: 'general_tab.AllowedDomainsInfo',
defaultMessage: 'When enabled, users can only join the team if their email matches a specific domain (e.g. "mattermost.org")',
},
AllowedDomains: {
id: 'general_tab.allowedDomains',
defaultMessage: 'Allow only users with a specific email domain to join this team',
},
});
type Props = {
allowedDomains: string[];
setAllowedDomains: (domains: string[]) => void;
@@ -52,15 +37,28 @@ const AllowedDomainsSelect = ({allowedDomains, setAllowedDomains, setHasChanges,
setSaveChangesPanelState('editing');
setAllowedDomains(allowedDomainsOptions?.map((domain) => domain.value) || []);
}, [setAllowedDomains, setHasChanges, setSaveChangesPanelState]);
return (
<>
<CheckboxSettingItem
inputFieldTitle={
<FormattedMessage
id='general_tab.allowedDomains'
defaultMessage='Allow only users with a specific email domain to join this team'
/>
}
data-testid='allowedDomainsCheckbox'
className='access-allowed-domains-section'
title={translations.AllowedDomainsTitle}
description={translations.AllowedDomainsInfo}
title={formatMessage({
id: 'general_tab.AllowedDomainsTitle',
defaultMessage: 'Users with a specific email domain',
})}
description={formatMessage({
id: 'general_tab.AllowedDomainsInfo',
defaultMessage: 'When enabled, users can only join the team if their email matches a specific domain (e.g. "mattermost.org")',
})}
descriptionAboveContent={true}
inputFieldData={{title: translations.AllowedDomains, name: 'name'}}
inputFieldData={{name: 'name'}}
inputFieldValue={showAllowedDomains}
handleChange={handleEnableAllowedDomains}
/>

View File

@@ -24,14 +24,6 @@ const translations = defineMessages({
id: 'team_settings.openInviteDescription.error',
defaultMessage: 'There was an error generating the invite code, please try again',
},
CodeTitle: {
id: 'general_tab.codeTitle',
defaultMessage: 'Invite Code',
},
CodeLongDesc: {
id: 'general_tab.codeLongDesc',
defaultMessage: 'The Invite Code is part of the unique team invitation link which is sent to members youre inviting to this team. Regenerating the code creates a new invitation link and invalidates the previous link.',
},
});
type Props = {
@@ -91,8 +83,15 @@ const InviteSectionInput = ({regenerateTeamInviteId}: Props) => {
return (
<BaseSettingItem
className='access-invite-section'
title={translations.CodeTitle}
description={translations.CodeLongDesc}
title={formatMessage({
id: 'general_tab.codeTitle',
defaultMessage: 'Invite Code',
})}
description={formatMessage({
id: 'general_tab.codeLongDesc',
defaultMessage: 'The Invite Code is part of the unique team invitation link which is sent to members youre inviting to this team. Regenerating the code creates a new invitation link and invalidates the previous link.',
})}
content={inviteSectionInput}
error={inviteIdError}
descriptionAboveContent={true}

View File

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React from 'react';
import {defineMessages, useIntl} from 'react-intl';
import {FormattedMessage, useIntl} from 'react-intl';
import ExternalLink from 'components/external_link';
import BaseSettingItem from 'components/widgets/modals/components/base_setting_item';
@@ -14,21 +14,6 @@ type Props = {
setAllowOpenInvite: (value: boolean) => void;
};
const translations = defineMessages({
OpenInviteText: {
id: 'general_tab.openInviteText',
defaultMessage: 'Users on this server',
},
OpenInviteDesc: {
id: 'general_tab.openInviteDesc',
defaultMessage: 'When enabled, a link to this team will be included on the landing page allowing anyone with an account to join this team. Changing this setting will create a new invitation link and invalidate the previous link.',
},
OpenInviteTitle: {
id: 'general_tab.openInviteTitle',
defaultMessage: 'Allow only users with a specific email domain to join this team',
},
});
const OpenInvite = ({isGroupConstrained, allowOpenInvite, setAllowOpenInvite}: Props) => {
const {formatMessage} = useIntl();
if (isGroupConstrained) {
@@ -52,8 +37,14 @@ const OpenInvite = ({isGroupConstrained, allowOpenInvite, setAllowOpenInvite}: P
return (
<BaseSettingItem
className='access-invite-domains-section'
title={translations.OpenInviteText}
description={translations.OpenInviteDesc}
title={formatMessage({
id: 'general_tab.openInviteText',
defaultMessage: 'Users on this server',
})}
description={formatMessage({
id: 'general_tab.openInviteDesc',
defaultMessage: 'When enabled, a link to this team will be included on the landing page allowing anyone with an account to join this team. Changing this setting will create a new invitation link and invalidate the previous link.',
})}
descriptionAboveContent={true}
content={groupConstrainedContent}
/>
@@ -63,11 +54,23 @@ const OpenInvite = ({isGroupConstrained, allowOpenInvite, setAllowOpenInvite}: P
return (
<CheckboxSettingItem
className='access-invite-domains-section'
inputFieldData={{title: translations.OpenInviteTitle, name: 'name'}}
inputFieldTitle={
<FormattedMessage
id='general_tab.openInviteTitle'
defaultMessage='Allow only users with a specific email domain to join this team'
/>
}
inputFieldData={{name: 'name'}}
inputFieldValue={allowOpenInvite}
handleChange={setAllowOpenInvite}
title={translations.OpenInviteText}
description={translations.OpenInviteDesc}
title={formatMessage({
id: 'general_tab.openInviteText',
defaultMessage: 'Users on this server',
})}
description={formatMessage({
id: 'general_tab.openInviteDesc',
defaultMessage: 'When enabled, a link to this team will be included on the landing page allowing anyone with an account to join this team. Changing this setting will create a new invitation link and invalidate the previous link.',
})}
descriptionAboveContent={true}
/>
);

View File

@@ -3,7 +3,7 @@
import React, {useCallback} from 'react';
import type {ChangeEvent} from 'react';
import {defineMessages, useIntl} from 'react-intl';
import {useIntl} from 'react-intl';
import type {Team} from '@mattermost/types/teams';
@@ -12,13 +12,6 @@ import BaseSettingItem, {type BaseSettingItemProps} from 'components/widgets/mod
import Constants from 'utils/constants';
const translations = defineMessages({
TeamDescriptionInfo: {
id: 'general_tab.teamDescriptionInfo',
defaultMessage: 'Team description provides additional information to help users select the right team. Maximum of 50 characters.',
},
});
type Props = {
handleDescriptionChanges: (name: string) => void;
description: Team['description'];
@@ -47,7 +40,10 @@ const TeamDescriptionSection = ({handleDescriptionChanges, clientError, descript
return (
<BaseSettingItem
description={translations.TeamDescriptionInfo}
description={formatMessage({
id: 'general_tab.teamDescriptionInfo',
defaultMessage: 'Team description provides additional information to help users select the right team. Maximum of 50 characters.',
})}
content={descriptionSectionInput}
className='description-setting-item'
error={clientError}

View File

@@ -3,7 +3,7 @@
import React, {useCallback} from 'react';
import type {ChangeEvent} from 'react';
import {defineMessages, useIntl} from 'react-intl';
import {useIntl} from 'react-intl';
import type {Team} from '@mattermost/types/teams';
@@ -12,17 +12,6 @@ import BaseSettingItem, {type BaseSettingItemProps} from 'components/widgets/mod
import Constants from 'utils/constants';
const translations = defineMessages({
TeamInfo: {
id: 'general_tab.teamInfo',
defaultMessage: 'Team info',
},
TeamNameInfo: {
id: 'general_tab.teamNameInfo',
defaultMessage: 'This name will appear on your sign-in screen and at the top of the left sidebar.',
},
});
type Props = {
handleNameChanges: (name: string) => void;
name: Team['display_name'];
@@ -48,8 +37,14 @@ const TeamNameSection = ({clientError, handleNameChanges, name}: Props) => {
return (
<BaseSettingItem
title={translations.TeamInfo}
description={translations.TeamNameInfo}
title={formatMessage({
id: 'general_tab.teamInfo',
defaultMessage: 'Team info',
})}
description={formatMessage({
id: 'general_tab.teamNameInfo',
defaultMessage: 'This name will appear on your sign-in screen and at the top of the left sidebar.',
})}
content={nameSectionInput}
error={clientError}
/>

View File

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React, {type ChangeEvent, useRef, useState, useEffect, useCallback} from 'react';
import {defineMessages, useIntl} from 'react-intl';
import {useIntl} from 'react-intl';
import {TrashCanOutlineIcon} from '@mattermost/compass-icons/components';
import type {Team} from '@mattermost/types/teams';
@@ -14,19 +14,8 @@ import type {BaseSettingItemProps} from 'components/widgets/modals/components/ba
import Constants from 'utils/constants';
import * as FileUtils from 'utils/file_utils';
import {imageURLForTeam} from 'utils/utils';
import './team_picture_section.scss';
const translations = defineMessages({
Title: {
id: 'setting_picture.title',
defaultMessage: 'Team Icon',
},
Profile: {
id: 'setting_picture.help.profile',
defaultMessage: 'Upload a picture in BMP, JPG, JPEG, or PNG format. Maximum file size: {max}',
values: {max: '50MB'},
},
});
import './team_picture_section.scss';
type Props = {
team: Team;
@@ -168,8 +157,19 @@ const TeamPictureSection = ({team, file, teamName, disabled, onFileChange, onRem
return (
<BaseSettingItem
title={translations.Title}
description={teamImageSource ? undefined : translations.Profile}
title={formatMessage({
id: 'setting_picture.title',
defaultMessage: 'Team Icon',
})}
description={teamImageSource ? undefined : formatMessage(
{
id: 'setting_picture.help.profile',
defaultMessage: 'Upload a picture in BMP, JPG, JPEG, or PNG format. Maximum file size: {max}',
},
{
max: '50MB',
},
)}
content={teamPictureSection}
className='picture-setting-item'
error={clientError}

View File

@@ -9,11 +9,11 @@
padding: 0;
margin: 0 0 8px;
color: var(--center-channel-color);
font-family: 'Metropolis', sans-serif;
font-size: 16px;
font-family: "Open Sans", sans-serif;
font-size: 14px;
font-style: normal;
font-weight: 600;
line-height: 24px;
line-height: 20px;
}
&__description {
@@ -82,6 +82,7 @@
width: fit-content;
flex-direction: row;
align-items: center;
margin-bottom: 0;
cursor: pointer;
font-size: 14px;
font-weight: 400;

View File

@@ -16,8 +16,8 @@ type ExtendedMessageDescriptor = MessageDescriptor & {
};
export type BaseSettingItemProps = {
title?: ExtendedMessageDescriptor;
description?: ExtendedMessageDescriptor;
title?: string;
description?: string;
error?: ExtendedMessageDescriptor;
};
@@ -29,21 +29,22 @@ type Props = BaseSettingItemProps & {
function BaseSettingItem({title, description, content, className, error, descriptionAboveContent = false}: Props): JSX.Element {
const {formatMessage} = useIntl();
const Title = title && (
const titleComponent = title && (
<h4
data-testid='mm-modal-generic-section-item__title'
className='mm-modal-generic-section-item__title'
>
{formatMessage({id: title.id, defaultMessage: title.defaultMessage}, title.values)}
{title}
</h4>
);
const Description = description && (
const descriptionComponent = description && (
<p
data-testid='mm-modal-generic-section-item__description'
className='mm-modal-generic-section-item__description'
>
{formatMessage({id: description.id, defaultMessage: description.defaultMessage}, description.values)}
{description}
</p>
);
@@ -57,19 +58,17 @@ function BaseSettingItem({title, description, content, className, error, descrip
</div>
);
const getClassName = classNames('mm-modal-generic-section-item', className);
return (
<div className={getClassName}>
{Title}
{descriptionAboveContent ? Description : undefined}
<div className={classNames('mm-modal-generic-section-item', className)}>
{titleComponent}
{descriptionAboveContent ? descriptionComponent : undefined}
<div
data-testid='mm-modal-generic-section-item__content'
className='mm-modal-generic-section-item__content'
>
{content}
</div>
{descriptionAboveContent ? undefined : Description}
{descriptionAboveContent ? undefined : descriptionComponent}
{Error}
</div>
);

View File

@@ -1,35 +1,40 @@
// 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 {FormattedMessage} from 'react-intl';
import type {MessageDescriptor} from 'react-intl';
import type {BaseSettingItemProps} from './base_setting_item';
import BaseSettingItem from './base_setting_item';
export type FieldsetCheckbox = {
dataTestId?: string;
title: MessageDescriptor;
name: string;
}
type Props = BaseSettingItemProps & {
inputFieldData: FieldsetCheckbox;
inputFieldValue: boolean;
/**
* The title of the checkbox input field, pass in FormattedMessage component for styling compatibility
*/
inputFieldTitle: ReactNode;
handleChange: (e: boolean) => void;
className?: string;
descriptionAboveContent?: boolean;
}
function CheckboxSettingItem({
export default function CheckboxSettingItem({
title,
description,
inputFieldData,
inputFieldValue,
inputFieldTitle,
handleChange,
className,
descriptionAboveContent = false,
}: Props): JSX.Element {
}: Props) {
const content = (
<fieldset
key={inputFieldData.name}
@@ -44,14 +49,11 @@ function CheckboxSettingItem({
checked={inputFieldValue}
onChange={(e) => handleChange(e.target.checked)}
/>
<FormattedMessage
id={inputFieldData.title.id}
defaultMessage={inputFieldData.title.defaultMessage}
/>
{inputFieldTitle}
</label>
<br/>
</fieldset>
);
return (
<BaseSettingItem
content={content}
@@ -62,5 +64,3 @@ function CheckboxSettingItem({
/>
);
}
export default CheckboxSettingItem;

View File

@@ -15,6 +15,7 @@
font-style: normal;
font-weight: 600;
line-height: 28px;
white-space: nowrap;
}
&__vertical-divider {
@@ -42,28 +43,6 @@
flex: 1 1 auto;
align-items: center;
justify-content: flex-end;
color: rgba(var(--center-channel-color-rgb), 0.56);
gap: 24px;
}
&__close-btn {
display: flex;
overflow: hidden;
width: 40px;
height: 40px;
align-items: center;
justify-content: center;
border: unset;
border-radius: 4px;
margin-left: 8px;
background: transparent;
color: rgba(var(--center-channel-color-rgb), 0.56);
&:hover,
&.active {
background: rgba(var(--center-channel-color-rgb), 0.08);
color: rgba(var(--center-channel-color-rgb), 0.72);
}
}
@media screen and (max-width: 768px) {

View File

@@ -3,7 +3,6 @@
import React from 'react';
import {CloseIcon} from '@mattermost/compass-icons/components';
import './modal_header.scss';
type Props = {
@@ -29,11 +28,8 @@ function ModalHeader({id, title, subtitle, handleClose}: Props) {
className='mm-modal-header__ctr'
onClick={handleClose}
>
<button className='style--none mm-modal-header__close-btn'>
<CloseIcon
size={24}
color={'currentcolor'}
/>
<button className='btn btn-icon'>
<i className='icon icon-close'/>
</button>
</div>
</header>

View File

@@ -2,46 +2,41 @@
display: flex;
flex-direction: column;
&__info-ctr {
.modalSectionHeader {
display: flex;
flex-direction: column;
margin-bottom: 8px;
gap: 8px;
margin-block-end: 24px;
.modalSectionTitle {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: var(--center-channel-color);
font-family: "Metropolis", sans-serif;
font-size: 16px;
font-style: normal;
font-weight: 600;
gap: 8px;
line-height: 24px;
}
.modalSectionDescription {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: rgba(var(--center-channel-color-rgb), 0.64);
font-family: "Open Sans", sans-serif;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
}
&__title {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: var(--center-channel-color);
font-family: 'Metropolis', sans-serif;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 24px;
}
&__row {
display: flex;
align-items: center;
gap: 8px;
}
&__description {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: rgba(var(--center-channel-color-rgb), 0.64);
font-family: 'Open Sans', sans-serif;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
&__content {
.modalSectionContent {
display: flex;
flex-direction: column;
gap: 24px;

View File

@@ -2,14 +2,13 @@
// See LICENSE.txt for license information.
import React from 'react';
import type {MessageDescriptor} from 'react-intl';
import {useIntl} from 'react-intl';
import type {ReactNode} from 'react';
import './modal_section.scss';
type Props = {
title?: MessageDescriptor;
description?: MessageDescriptor;
title?: ReactNode;
description?: ReactNode;
content: JSX.Element;
titleSuffix?: JSX.Element;
};
@@ -19,46 +18,29 @@ function ModalSection({
description,
content,
titleSuffix,
}: Props): JSX.Element {
const {formatMessage} = useIntl();
const titleContent = title ? (
<h4 className='mm-modal-generic-section__title'>
{formatMessage({id: title.id, defaultMessage: title.defaultMessage})}
}: Props) {
const titleComponent = title && (
<h4 className='modalSectionTitle'>
{title}
{titleSuffix}
</h4>
) : undefined;
);
const descriptionContent = description && (
<p className='mm-modal-generic-section__description'>
{formatMessage({id: description.id, defaultMessage: description.defaultMessage})}
const descriptionComponent = description && (
<p className='modalSectionDescription'>
{description}
</p>
);
function titleRow() {
if (titleSuffix) {
return (<div className='mm-modal-generic-section__row'>
{titleContent}
{titleSuffix}
</div>);
}
return titleContent;
}
const titleDescriptionSection = () => {
if (title || description) {
return (
<div className='mm-modal-generic-section__title-description-ctr'>
{titleRow()}
{descriptionContent}
</div>
);
}
return null;
};
return (
<section className='mm-modal-generic-section'>
{titleDescriptionSection()}
<div className='mm-modal-generic-section__content'>
{(title || description) && (
<div className='modalSectionHeader'>
{titleComponent}
{descriptionComponent}
</div>
)}
<div className='modalSectionContent'>
{content}
</div>
</section>

View File

@@ -1,9 +1,8 @@
// 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 type {MessageDescriptor} from 'react-intl';
import {FormattedMessage} from 'react-intl';
import type {BaseSettingItemProps} from './base_setting_item';
import BaseSettingItem from './base_setting_item';
@@ -11,7 +10,7 @@ import BaseSettingItem from './base_setting_item';
export type FieldsetRadio = {
options: Array<{
dataTestId?: string;
title: MessageDescriptor;
title: ReactNode;
name: string;
key: string;
value: string;
@@ -20,13 +19,16 @@ export type FieldsetRadio = {
}
type Props = BaseSettingItemProps & {
className?: string;
inputFieldData: FieldsetRadio;
inputFieldValue: string;
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
function RadioSettingItem({
title,
description,
className,
inputFieldData,
inputFieldValue,
handleChange,
@@ -46,10 +48,7 @@ function RadioSettingItem({
value={option.value}
onChange={handleChange}
/>
<FormattedMessage
id={option.title.id}
defaultMessage={option.title.defaultMessage}
/>
{option.title}
{option.suffix}
</label>
);
@@ -62,6 +61,7 @@ function RadioSettingItem({
);
return (
<BaseSettingItem
className={className}
content={content}
title={title}
description={description}