From 1e7053cb8ee534a81a3939ae594c9f7e8bbd5fe7 Mon Sep 17 00:00:00 2001 From: ilkin-overleaf <100852799+ilkin-overleaf@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:25:05 +0300 Subject: [PATCH] Merge pull request #17921 from overleaf/ii-bs5-alert-continue [web] Replace all alerts with notifications in settings page GitOrigin-RevId: a05755c39d2e54b3f57ffa589885e3e96aee00dc --- .../components/account-info-section.tsx | 13 +- .../components/emails/add-email/layout.tsx | 13 +- .../components/emails/reconfirmation-info.tsx | 272 +++++++++++++++--- .../reconfirmation-info-prompt-text.tsx | 51 ++++ .../reconfirmation-info-prompt.tsx | 186 ------------ .../reconfirmation-info-success.tsx | 18 +- .../settings/components/emails/sso-alert.tsx | 119 +++++--- .../components/leave/modal-form-error.tsx | 21 +- .../components/leavers-survey-alert.tsx | 36 ++- .../settings/components/linking-section.tsx | 14 +- .../settings/components/password-section.tsx | 68 ++--- .../bootstrap-5/notification-wrapper.tsx | 2 +- .../js/shared/components/notification.tsx | 2 +- .../stylesheets/app/account-settings.less | 2 + .../bootstrap-5/components/notifications.scss | 8 +- .../emails/reconfirmation-info.test.tsx | 3 + 16 files changed, 473 insertions(+), 355 deletions(-) create mode 100644 services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt-text.tsx delete mode 100644 services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx diff --git a/services/web/frontend/js/features/settings/components/account-info-section.tsx b/services/web/frontend/js/features/settings/components/account-info-section.tsx index ef1726dd86..3a8c0926b0 100644 --- a/services/web/frontend/js/features/settings/components/account-info-section.tsx +++ b/services/web/frontend/js/features/settings/components/account-info-section.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Alert, ControlLabel, FormControl, FormGroup } from 'react-bootstrap' +import { ControlLabel, FormControl, FormGroup } from 'react-bootstrap' import { useTranslation } from 'react-i18next' import { getUserFacingMessage, @@ -10,6 +10,7 @@ import { ExposedSettings } from '../../../../../types/exposed-settings' import useAsync from '../../../shared/hooks/use-async' import { useUserContext } from '../../../shared/context/user-context' import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper' +import NotificationWrapper from '@/features/ui/components/bootstrap-5/notification-wrapper' function AccountInfoSection() { const { t } = useTranslation() @@ -104,12 +105,18 @@ function AccountInfoSection() { /> {isSuccess ? ( - {t('thanks_settings_updated')} + ) : null} {isError ? ( - {getUserFacingMessage(error)} + ) : null} {canUpdateEmail || canUpdateNames ? ( diff --git a/services/web/frontend/js/features/settings/components/emails/add-email/layout.tsx b/services/web/frontend/js/features/settings/components/emails/add-email/layout.tsx index 045b61a775..ed63c7c8d6 100644 --- a/services/web/frontend/js/features/settings/components/emails/add-email/layout.tsx +++ b/services/web/frontend/js/features/settings/components/emails/add-email/layout.tsx @@ -1,8 +1,8 @@ -import { Alert } from 'react-bootstrap' import Icon from '../../../../../shared/components/icon' import { UseAsyncReturnType } from '../../../../../shared/hooks/use-async' import { getUserFacingMessage } from '../../../../../infrastructure/fetch-json' import RowWrapper from '@/features/ui/components/bootstrap-5/wrappers/row-wrapper' +import NotificationWrapper from '@/features/ui/components/bootstrap-5/notification-wrapper' type LayoutProps = { children: React.ReactNode @@ -15,9 +15,14 @@ function Layout({ isError, error, children }: LayoutProps) {
{children} {isError && ( - - {getUserFacingMessage(error)} - + , + className: 'text-center', + }} + /> )}
) diff --git a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info.tsx b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info.tsx index 83c0fade4b..39eb239ec6 100644 --- a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info.tsx +++ b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info.tsx @@ -1,11 +1,23 @@ -import { ReactNode } from 'react' +import { useState, useEffect, useLayoutEffect } from 'react' import { UserEmailData } from '../../../../../../types/user-email' -import classNames from 'classnames' import getMeta from '../../../../utils/meta' import ReconfirmationInfoSuccess from './reconfirmation-info/reconfirmation-info-success' -import ReconfirmationInfoPrompt from './reconfirmation-info/reconfirmation-info-prompt' +import ReconfirmationInfoPromptText from './reconfirmation-info/reconfirmation-info-prompt-text' import RowWrapper from '@/features/ui/components/bootstrap-5/wrappers/row-wrapper' import ColWrapper from '@/features/ui/components/bootstrap-5/wrappers/col-wrapper' +import NotificationWrapper from '@/features/ui/components/bootstrap-5/notification-wrapper' +import { isBootstrap5 } from '@/features/utils/bootstrap-5' +import Icon from '@/shared/components/icon' +import { useUserEmailsContext } from '@/features/settings/context/user-email-context' +import { FetchError, postJSON } from '@/infrastructure/fetch-json' +import { debugConsole } from '@/utils/debugging' +import { ssoAvailableForInstitution } from '@/features/settings/utils/sso' +import { Trans, useTranslation } from 'react-i18next' +import useAsync from '@/shared/hooks/use-async' +import { ExposedSettings } from '../../../../../../types/exposed-settings' +import { useLocation } from '@/shared/hooks/use-location' +import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper' +import classnames from 'classnames' type ReconfirmationInfoProps = { userEmailData: UserEmailData @@ -17,6 +29,48 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) { ) as string const reconfirmedViaSAML = getMeta('ol-reconfirmedViaSAML') as string + const { t } = useTranslation() + const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings + const { error, isLoading, isError, isSuccess, runAsync } = useAsync() + const { state, setLoading: setUserEmailsContextLoading } = + useUserEmailsContext() + const [hasSent, setHasSent] = useState(false) + const [isPending, setIsPending] = useState(false) + const location = useLocation() + const ssoAvailable = Boolean( + ssoAvailableForInstitution(userEmailData.affiliation?.institution ?? null) + ) + + const handleRequestReconfirmation = () => { + if (userEmailData.affiliation?.institution && ssoAvailable) { + setIsPending(true) + location.assign( + `${samlInitPath}?university_id=${userEmailData.affiliation.institution.id}&reconfirm=/user/settings` + ) + } else { + runAsync( + postJSON('/user/emails/send-reconfirmation', { + body: { + email: userEmailData.email, + }, + }) + ).catch(debugConsole.error) + } + } + + useEffect(() => { + setUserEmailsContextLoading(isLoading) + }, [setUserEmailsContextLoading, isLoading]) + + useLayoutEffect(() => { + if (isSuccess) { + setHasSent(true) + } + }, [isSuccess]) + + const rateLimited = + isError && error instanceof FetchError && error.response?.status === 429 + if (!userEmailData.affiliation) { return null } @@ -26,53 +80,189 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) { userEmailData.samlProviderId === reconfirmedViaSAML ) { return ( - - - + + + + } + bs3Props={{ className: 'settings-reconfirm-info small' }} + /> + + ) } if (userEmailData.affiliation.inReconfirmNotificationPeriod) { return ( - - - + + + {isBootstrap5 ? ( + + {hasSent ? ( + ] + } + /> + ) : ( + + )} +
+ {isError && ( +
+ {rateLimited + ? t('too_many_requests') + : t('generic_something_went_wrong')} +
+ )} + + } + action={ + hasSent ? ( + <> + {isLoading ? ( + <> + {t('sending')}... + + ) : ( + + {t('resend_confirmation_email')} + + )} + + ) : ( + + {isLoading ? ( + <> + {t('sending')}... + + ) : ( + t('confirm_affiliation') + )} + + ) + } + /> + ) : ( +
+ {hasSent ? ( +
+ ] + } + />{' '} + {isLoading ? ( + <> + {t('sending')}... + + ) : ( + + {t('resend_confirmation_email')} + + )} +
+ {isError && ( +
+ {rateLimited + ? t('too_many_requests') + : t('generic_something_went_wrong')} +
+ )} +
+ ) : ( + <> +
+ + } + /> +
+
+ + {isLoading ? ( + <> + {t('sending')}... + + ) : ( + t('confirm_affiliation') + )} + +
+ {isError && ( +
+ {rateLimited + ? t('too_many_requests') + : t('generic_something_went_wrong')} +
+ )} +
+ + )} +
+ )} +
+
) } return null } -type ReconfirmationInfoContentWrapperProps = { - asAlertInfo: boolean - children: ReactNode -} - -function ReconfirmationInfoContentWrapper({ - asAlertInfo, - children, -}: ReconfirmationInfoContentWrapperProps) { - return ( - - -
- {children} -
-
-
- ) -} - export default ReconfirmationInfo diff --git a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt-text.tsx b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt-text.tsx new file mode 100644 index 0000000000..4dace050ef --- /dev/null +++ b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt-text.tsx @@ -0,0 +1,51 @@ +import { Trans, useTranslation } from 'react-i18next' +import { Institution } from '../../../../../../../types/institution' + +type ReconfirmationInfoPromptTextProps = { + primary: boolean + institutionName: Institution['name'] + icon?: React.ReactElement // BS3 only +} + +function ReconfirmationInfoPromptText({ + primary, + institutionName, + icon, +}: ReconfirmationInfoPromptTextProps) { + const { t } = useTranslation() + + return ( + <> + {icon} + ] + } + />{' '} + ] + } + />{' '} + + {t('learn_more')} + +
+ {primary ? {t('need_to_add_new_primary_before_remove')} : null} + + ) +} + +export default ReconfirmationInfoPromptText diff --git a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx deleted file mode 100644 index 761bc4ae1b..0000000000 --- a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { useState, useEffect, useLayoutEffect } from 'react' -import useAsync from '../../../../../shared/hooks/use-async' -import { FetchError, postJSON } from '../../../../../infrastructure/fetch-json' -import { Trans, useTranslation } from 'react-i18next' -import { Institution } from '../../../../../../../types/institution' -import { Button } from 'react-bootstrap' -import { useUserEmailsContext } from '../../../context/user-email-context' -import getMeta from '../../../../../utils/meta' -import { ExposedSettings } from '../../../../../../../types/exposed-settings' -import { ssoAvailableForInstitution } from '../../../utils/sso' -import Icon from '../../../../../shared/components/icon' -import { useLocation } from '../../../../../shared/hooks/use-location' -import { debugConsole } from '@/utils/debugging' - -type ReconfirmationInfoPromptProps = { - email: string - primary: boolean - institution: Institution -} - -function ReconfirmationInfoPrompt({ - email, - primary, - institution, -}: ReconfirmationInfoPromptProps) { - const { t } = useTranslation() - const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings - const { error, isLoading, isError, isSuccess, runAsync } = useAsync() - const { state, setLoading: setUserEmailsContextLoading } = - useUserEmailsContext() - const [isPending, setIsPending] = useState(false) - const [hasSent, setHasSent] = useState(false) - const ssoAvailable = Boolean(ssoAvailableForInstitution(institution)) - const location = useLocation() - - useEffect(() => { - setUserEmailsContextLoading(isLoading) - }, [setUserEmailsContextLoading, isLoading]) - - useLayoutEffect(() => { - if (isSuccess) { - setHasSent(true) - } - }, [isSuccess]) - - const handleRequestReconfirmation = () => { - if (ssoAvailable) { - setIsPending(true) - location.assign( - `${samlInitPath}?university_id=${institution.id}&reconfirm=/user/settings` - ) - } else { - runAsync( - postJSON('/user/emails/send-reconfirmation', { - body: { - email, - }, - }) - ).catch(debugConsole.error) - } - } - - const rateLimited = - isError && error instanceof FetchError && error.response?.status === 429 - - if (hasSent) { - return ( -
- ] - } - />{' '} - {isLoading ? ( - <> - {t('sending')}... - - ) : ( - - )} -
- {isError && ( -
- {rateLimited - ? t('too_many_requests') - : t('generic_something_went_wrong')} -
- )} -
- ) - } - - return ( - <> -
- -
-
- -
- {isError && ( -
- {rateLimited - ? t('too_many_requests') - : t('generic_something_went_wrong')} -
- )} -
- - ) -} - -type ReconfirmationInfoPromptTextProps = { - primary: boolean - institutionName: Institution['name'] -} - -function ReconfirmationInfoPromptText({ - primary, - institutionName, -}: ReconfirmationInfoPromptTextProps) { - const { t } = useTranslation() - - return ( -
- - ] - } - />{' '} - ] - } - />{' '} - - {t('learn_more')} - -
- {primary ? {t('need_to_add_new_primary_before_remove')} : null} -
- ) -} - -export default ReconfirmationInfoPrompt diff --git a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-success.tsx b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-success.tsx index eaf9d1e353..b8fe9a0fcc 100644 --- a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-success.tsx +++ b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-success.tsx @@ -14,14 +14,16 @@ function ReconfirmationInfoSuccess({ return (
- ]} // eslint-disable-line react/jsx-key - />{' '} - {t('thank_you_exclamation')} +
+ ]} // eslint-disable-line react/jsx-key + />{' '} + {t('thank_you_exclamation')} +
) } diff --git a/services/web/frontend/js/features/settings/components/emails/sso-alert.tsx b/services/web/frontend/js/features/settings/components/emails/sso-alert.tsx index fb082d420d..9319f81c5e 100644 --- a/services/web/frontend/js/features/settings/components/emails/sso-alert.tsx +++ b/services/web/frontend/js/features/settings/components/emails/sso-alert.tsx @@ -1,8 +1,8 @@ import { useState } from 'react' import { useTranslation, Trans } from 'react-i18next' -import { Alert } from 'react-bootstrap' import Icon from '../../../../shared/components/icon' import getMeta from '../../../../utils/meta' +import NotificationWrapper from '@/features/ui/components/bootstrap-5/notification-wrapper' type InstitutionLink = { universityName: string @@ -36,18 +36,28 @@ export function SSOAlert() { if (samlError) { return !errorClosed ? ( - -

- {' '} - {samlError.translatedMessage - ? samlError.translatedMessage - : samlError.message} -

- {samlError.tryAgain &&

{t('try_again')}

} -
+ + {samlError.translatedMessage + ? samlError.translatedMessage + : samlError.message} + {samlError.tryAgain &&

{t('try_again')}

} + + } + isDismissible + onDismiss={handleErrorClosed} + bs3Props={{ + icon: ( + + ), + className: 'mb-0 text-center', + }} + /> ) : null } @@ -58,40 +68,43 @@ export function SSOAlert() { return ( <> {!infoClosed && ( - -

- ]} // eslint-disable-line react/jsx-key - values={{ institutionName: institutionLinked.universityName }} - shouldUnescape - tOptions={{ interpolation: { escapeValue: true } }} - /> -

- {institutionLinked.hasEntitlement && ( -

- ]} // eslint-disable-line react/jsx-key - values={{ featureType: t('professional') }} - shouldUnescape - tOptions={{ interpolation: { escapeValue: true } }} - /> -

- )} -
+ +

+ ]} // eslint-disable-line react/jsx-key + values={{ institutionName: institutionLinked.universityName }} + shouldUnescape + tOptions={{ interpolation: { escapeValue: true } }} + /> +

+ {institutionLinked.hasEntitlement && ( +

+ ]} // eslint-disable-line react/jsx-key + values={{ featureType: t('professional') }} + shouldUnescape + tOptions={{ interpolation: { escapeValue: true } }} + /> +

+ )} + + } + isDismissible + onDismiss={handleInfoClosed} + bs3Props={{ + className: 'mb-0 text-center', + }} + /> )} {!warningClosed && institutionEmailNonCanonical && ( - -

- {' '} + ]} // eslint-disable-line react/jsx-key @@ -99,8 +112,20 @@ export function SSOAlert() { shouldUnescape tOptions={{ interpolation: { escapeValue: true } }} /> -

-
+ } + isDismissible + onDismiss={handleWarningClosed} + bs3Props={{ + icon: ( + + ), + className: 'text-center', + }} + /> )} ) diff --git a/services/web/frontend/js/features/settings/components/leave/modal-form-error.tsx b/services/web/frontend/js/features/settings/components/leave/modal-form-error.tsx index b28c9be75c..f20032e745 100644 --- a/services/web/frontend/js/features/settings/components/leave/modal-form-error.tsx +++ b/services/web/frontend/js/features/settings/components/leave/modal-form-error.tsx @@ -1,8 +1,8 @@ -import { Alert } from 'react-bootstrap' import { useTranslation, Trans } from 'react-i18next' import getMeta from '../../../../utils/meta' import { FetchError } from '../../../../infrastructure/fetch-json' import { ExposedSettings } from '../../../../../../types/exposed-settings' +import NotificationWrapper from '@/features/ui/components/bootstrap-5/notification-wrapper' type LeaveModalFormErrorProps = { error: FetchError @@ -32,15 +32,20 @@ function LeaveModalFormError({ error }: LeaveModalFormErrorProps) { } return ( - - {errorMessage} - {errorTip ? ( + -
- {errorTip} + {errorMessage} + {errorTip ? ( + <> +
+ {errorTip} + + ) : null} - ) : null} -
+ } + /> ) } diff --git a/services/web/frontend/js/features/settings/components/leavers-survey-alert.tsx b/services/web/frontend/js/features/settings/components/leavers-survey-alert.tsx index cecf90f28e..4789b0d91d 100644 --- a/services/web/frontend/js/features/settings/components/leavers-survey-alert.tsx +++ b/services/web/frontend/js/features/settings/components/leavers-survey-alert.tsx @@ -1,9 +1,9 @@ import { useEffect } from 'react' -import { Alert } from 'react-bootstrap' import { useTranslation } from 'react-i18next' import usePersistedState from '../../../shared/hooks/use-persisted-state' import { useUserEmailsContext } from '../context/user-email-context' import { sendMB } from '../../../infrastructure/event-tracking' +import NotificationWrapper from '@/features/ui/components/bootstrap-5/notification-wrapper' function sendMetrics(segmentation: 'view' | 'click' | 'close') { sendMB('institutional-leavers-survey-notification', { type: segmentation }) @@ -47,19 +47,25 @@ export function LeaversSurveyAlert() { } return ( - -

- {t('limited_offer')} - {`: ${t('institutional_leavers_survey_notification')} `} - - {t('take_short_survey')} - -

-
+ + {t('limited_offer')} + {`: ${t('institutional_leavers_survey_notification')} `} + + {t('take_short_survey')} + + + } + isDismissible + onDismiss={handleDismiss} + className="mb-0" + /> ) } diff --git a/services/web/frontend/js/features/settings/components/linking-section.tsx b/services/web/frontend/js/features/settings/components/linking-section.tsx index c5de89de12..57009525f5 100644 --- a/services/web/frontend/js/features/settings/components/linking-section.tsx +++ b/services/web/frontend/js/features/settings/components/linking-section.tsx @@ -1,5 +1,4 @@ import { useState, ElementType } from 'react' -import { Alert } from 'react-bootstrap' import { useTranslation } from 'react-i18next' import importOverleafModules from '../../../../macros/import-overleaf-module.macro' import { useSSOContext, SSOSubscription } from '../context/sso-context' @@ -7,6 +6,7 @@ import { SSOLinkingWidget } from './linking/sso-widget' import getMeta from '../../../utils/meta' import { useBroadcastUser } from '@/shared/hooks/user-channel/use-broadcast-user' import { useSplitTestContext } from '@/shared/context/split-test-context' +import NotificationWrapper from '@/features/ui/components/bootstrap-5/notification-wrapper' function LinkingSection() { useBroadcastUser() @@ -126,7 +126,10 @@ function LinkingSection() { {t('project_synchronisation')} {projectSyncSuccessMessage ? ( - {projectSyncSuccessMessage} + ) : null}
{allIntegrationLinkingWidgets.map( @@ -167,9 +170,10 @@ function LinkingSection() { {t('linked_accounts')} {ssoErrorMessage ? ( - - {t('sso_link_error')}: {ssoErrorMessage} - + ) : null}
{Object.values(subscriptions).map( diff --git a/services/web/frontend/js/features/settings/components/password-section.tsx b/services/web/frontend/js/features/settings/components/password-section.tsx index c2ae97ca17..2c6c96fae6 100644 --- a/services/web/frontend/js/features/settings/components/password-section.tsx +++ b/services/web/frontend/js/features/settings/components/password-section.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react' -import { Alert, ControlLabel, FormControl, FormGroup } from 'react-bootstrap' +import { ControlLabel, FormControl, FormGroup } from 'react-bootstrap' import { Trans, useTranslation } from 'react-i18next' import { getUserFacingMessage, @@ -11,6 +11,7 @@ import { ExposedSettings } from '../../../../../types/exposed-settings' import { PasswordStrengthOptions } from '../../../../../types/password-strength-options' import useAsync from '../../../shared/hooks/use-async' import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper' +import NotificationWrapper from '@/features/ui/components/bootstrap-5/notification-wrapper' type PasswordUpdateResult = { message?: { @@ -156,41 +157,44 @@ function PasswordForm() { /> {isSuccess && data?.message?.text ? ( - {data.message.text} + ) : null} {isError ? ( - - {getErrorMessageKey(error) === 'password-must-be-strong' ? ( - <> - , - ]} - /> - . {t('use_a_different_password')}. - - ) : getErrorMessageKey(error) === 'password-contains-email' ? ( - <> - {t('invalid_password_contains_email')}.{' '} - {t('use_a_different_password')}. - - ) : getErrorMessageKey(error) === 'password-too-similar' ? ( - <> - {t('invalid_password_too_similar')}.{' '} - {t('use_a_different_password')}. - - ) : ( - getUserFacingMessage(error) - )} - + + , + ]} + /> + . {t('use_a_different_password')}. + + ) : getErrorMessageKey(error) === 'password-contains-email' ? ( + <> + {t('invalid_password_contains_email')}.{' '} + {t('use_a_different_password')}. + + ) : getErrorMessageKey(error) === 'password-too-similar' ? ( + <> + {t('invalid_password_too_similar')}.{' '} + {t('use_a_different_password')}. + + ) : ( + getUserFacingMessage(error) ?? '' + ) + } + /> ) : null} & { bs3Props?: { - icon: React.ReactElement + icon?: React.ReactElement className?: string } } diff --git a/services/web/frontend/js/shared/components/notification.tsx b/services/web/frontend/js/shared/components/notification.tsx index 2c0155c2fa..9e5096152f 100644 --- a/services/web/frontend/js/shared/components/notification.tsx +++ b/services/web/frontend/js/shared/components/notification.tsx @@ -14,7 +14,7 @@ export type NotificationProps = { action?: React.ReactElement ariaLive?: 'polite' | 'off' | 'assertive' className?: string - content: React.ReactElement | string + content: React.ReactNode customIcon?: React.ReactElement disclaimer?: React.ReactElement | string isDismissible?: boolean diff --git a/services/web/frontend/stylesheets/app/account-settings.less b/services/web/frontend/stylesheets/app/account-settings.less index b6fb318591..dbc8635ab9 100644 --- a/services/web/frontend/stylesheets/app/account-settings.less +++ b/services/web/frontend/stylesheets/app/account-settings.less @@ -196,6 +196,7 @@ tbody > tr.affiliations-table-warning-row > td { } } +// Should not be migrated to BS5 .settings-reconfirm-info { display: flex; justify-content: space-between; @@ -213,6 +214,7 @@ tbody > tr.affiliations-table-warning-row > td { } } +// Should not be migrated to BS5 .setting-reconfirm-info-right { white-space: nowrap; } diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/notifications.scss b/services/web/frontend/stylesheets/bootstrap-5/components/notifications.scss index 0b18779748..723b0c5126 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/components/notifications.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/components/notifications.scss @@ -2,7 +2,7 @@ // will be deprecated once notifications moved to use .notification (see below) flex-grow: 1; width: 90%; - @media (min-width: var(--bs-breakpoint-md)) { + @include media-breakpoint-up(md) { width: auto; } } @@ -13,7 +13,7 @@ // will be deprecated once notifications moved to use .notification (see below) margin-top: calc($line-height-computed / 2); // match paragraph padding order: 1; - @media (min-width: var(--bs-breakpoint-md)) { + @include media-breakpoint-up(md) { margin-top: 0; order: 0; padding-left: $spacing-05; @@ -43,7 +43,7 @@ } } - @media (min-width: var(--bs-breakpoint-md)) { + @include media-breakpoint-up(md) { width: auto; } } @@ -167,7 +167,7 @@ } } - @media (min-width: var(--bs-breakpoint-md)) { + @include media-breakpoint-up(md) { &:not(.notification-cta-below-content) { .notification-content-and-cta { flex-wrap: nowrap; diff --git a/services/web/test/frontend/features/settings/components/emails/reconfirmation-info.test.tsx b/services/web/test/frontend/features/settings/components/emails/reconfirmation-info.test.tsx index 2e175f6cec..2b4d74402c 100644 --- a/services/web/test/frontend/features/settings/components/emails/reconfirmation-info.test.tsx +++ b/services/web/test/frontend/features/settings/components/emails/reconfirmation-info.test.tsx @@ -28,6 +28,9 @@ describe('', function () { beforeEach(function () { window.metaAttributesCache = window.metaAttributesCache || new Map() + window.metaAttributesCache.set('ol-ExposedSettings', { + samlInitPath: '/saml', + }) fetchMock.get('/user/emails?ensureAffiliation=true', []) assignStub = sinon.stub() this.locationStub = sinon.stub(useLocationModule, 'useLocation').returns({