Merge pull request #15946 from overleaf/msm-new-notification-others

[web] new notification style for dashboard alerts

GitOrigin-RevId: cf5bea32a89f35d63ca4bdc2df2619352d421ff3
This commit is contained in:
Miguel Serrano 2024-01-08 14:55:03 +01:00 committed by Copybot
parent a50ca25f3a
commit a04b5c0418
16 changed files with 645 additions and 203 deletions

View file

@ -5,6 +5,7 @@ import Notification from '../notification'
import * as eventTracking from '../../../../../infrastructure/event-tracking'
import { Modal, Button } from 'react-bootstrap'
import AccessibleModal from '../../../../../shared/components/accessible-modal'
import getMeta from '@/utils/meta'
interface VariantContents {
default: string
@ -25,6 +26,10 @@ type INRBannerProps = {
export default function INRBanner({ variant, splitTestName }: INRBannerProps) {
const { t } = useTranslation()
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const [dismissedUntil, setDismissedUntil] = usePersistedState<
Date | undefined
>(`has_dismissed_inr_banner_until`)
@ -107,9 +112,9 @@ export default function INRBanner({ variant, splitTestName }: INRBannerProps) {
}
action={
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
bsSize="sm"
className="pull-right"
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
onClick={handleClick}
>
{t('get_discounted_plan')}
@ -133,9 +138,9 @@ export default function INRBanner({ variant, splitTestName }: INRBannerProps) {
}
action={
<Button
bsStyle="success"
bsStyle={newNotificationStyle ? null : 'success'}
bsSize="sm"
className="pull-right"
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
onClick={handleClick}
>
{t('get_discounted_plan')}

View file

@ -16,6 +16,10 @@ const LATAM_CURRENCIES = {
export default function LATAMBanner() {
const { t } = useTranslation()
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const [dismissedAt, setDismissedAt] = usePersistedState<Date | undefined>(
`has_dismissed_latam_banner`
)
@ -97,9 +101,9 @@ export default function LATAMBanner() {
}
action={
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
bsSize="sm"
className="pull-right"
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
onClick={handleClick}
>
{t('get_discounted_plan')}

View file

@ -20,6 +20,10 @@ export default function GroupsAndEnterpriseBanner() {
)
const groupsAndEnterpriseBannerVariant: GroupsAndEnterpriseBannerVariant =
getMeta('ol-groupsAndEnterpriseBannerVariant')
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const hasDismissedGroupsAndEnterpriseBanner = hasRecentlyDismissedBanner()
@ -68,7 +72,11 @@ export default function GroupsAndEnterpriseBanner() {
body={<BannerContent variant={groupsAndEnterpriseBannerVariant} />}
action={
<a
className="pull-right btn btn-info btn-sm"
className={
newNotificationStyle
? 'btn btn-secondary btn-sm'
: 'pull-right btn btn-info btn-sm'
}
href={contactSalesUrl}
target="_blank"
rel="noreferrer"

View file

@ -25,6 +25,10 @@ function ReconfirmAffiliation({
}: ReconfirmAffiliationProps) {
const { t } = useTranslation()
const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const { error, isLoading, isError, isSuccess, runAsync } = useAsync()
const [hasSent, setHasSent] = useState(false)
const [isPending, setIsPending] = useState(false)
@ -95,7 +99,7 @@ function ReconfirmAffiliation({
return (
<div className="w-100">
<Icon type="warning" />
{!newNotificationStyle && <Icon type="warning" />}
<Button
bsStyle="info"
bsSize="sm"

View file

@ -18,16 +18,15 @@ function ReconfirmationInfo() {
<Notification
key={`reconfirmation-period-email-${userEmail.email}`}
bsStyle="info"
>
<Notification.Body>
body={
<div className="reconfirm-notification">
<ReconfirmAffiliation
email={userEmail.email}
institution={userEmail.affiliation.institution}
/>
</div>
</Notification.Body>
</Notification>
}
/>
) : null
)}
{userEmails.map(userEmail =>
@ -37,13 +36,12 @@ function ReconfirmationInfo() {
key={`samlIdentifier-email-${userEmail.email}`}
bsStyle="info"
onDismiss={() => {}}
>
<Notification.Body>
body={
<ReconfirmationInfoSuccess
institution={userEmail.affiliation?.institution}
/>
</Notification.Body>
</Notification>
}
/>
) : null
)}
</>

View file

@ -38,6 +38,10 @@ function CommonNotification({ notification }: CommonNotificationProps) {
const { t } = useTranslation()
const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings
const user = getMeta('ol-user', []) as Pick<User, 'features'>
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const { isLoading, isSuccess, error, runAsync } = useAsync<
never,
FetchError
@ -90,16 +94,21 @@ function CommonNotification({ notification }: CommonNotificationProps) {
action={
accepted ? (
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
bsSize="sm"
className="pull-right"
className={
newNotificationStyle ? 'btn-secondary' : 'pull-right'
}
href={`/project/${notification.messageOpts.projectId}`}
>
{t('open_project')}
</Button>
) : (
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
className={
newNotificationStyle ? 'btn-secondary' : 'pull-right'
}
bsSize="sm"
disabled={isLoading}
onClick={() => handleAcceptInvite(notification)}
@ -128,9 +137,9 @@ function CommonNotification({ notification }: CommonNotificationProps) {
}
action={
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
bsSize="sm"
className="pull-right"
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
href="https://www.overleaf.com/events/wfh2020"
>
View
@ -189,9 +198,9 @@ function CommonNotification({ notification }: CommonNotificationProps) {
}
action={
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
bsSize="sm"
className="pull-right"
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
href={
notification.messageOpts.ssoEnabled
? `${samlInitPath}?university_id=${notification.messageOpts.institutionId}&auto=/project`
@ -219,9 +228,9 @@ function CommonNotification({ notification }: CommonNotificationProps) {
}
action={
<Button
bsStyle="danger"
bsStyle={newNotificationStyle ? null : 'danger'}
bsSize="sm"
className="pull-right"
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
href="/user/settings"
>
Account Settings

View file

@ -103,8 +103,53 @@ function ConfirmEmailNotification({ userEmail }: { userEmail: UserEmailData }) {
// already have premium features.
if (emailHasLicenceAfterConfirming(userEmail) && isOnFreeOrIndividualPlan()) {
return (
<Notification bsStyle="info">
<Notification.Body data-testid="notification-body">
<Notification
bsStyle="info"
body={
<div data-testid="notification-body">
{isLoading ? (
<>
<Icon type="spinner" spin /> {t('resending_confirmation_email')}
&hellip;
</>
) : isError ? (
<div aria-live="polite">{getUserFacingMessage(error)}</div>
) : (
<>
<Trans
i18nKey="one_step_away_from_professional_features"
components={[<strong />]} // eslint-disable-line react/jsx-key
/>
<button
className="pull-right btn btn-info btn-sm"
onClick={() => handleResendConfirmationEmail(userEmail)}
>
{t('resend_email')}
</button>
<br />
<Trans
i18nKey="institution_has_overleaf_subscription"
values={{
institutionName: userEmail.affiliation?.institution.name,
emailAddress: userEmail.email,
}}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
components={[<strong />]} // eslint-disable-line react/jsx-key
/>
</>
)}
</div>
}
/>
)
}
return (
<Notification
bsStyle="warning"
body={
<div data-testid="pro-notification-body">
{isLoading ? (
<>
<Icon type="spinner" spin /> {t('resending_confirmation_email')}
@ -114,60 +159,21 @@ function ConfirmEmailNotification({ userEmail }: { userEmail: UserEmailData }) {
<div aria-live="polite">{getUserFacingMessage(error)}</div>
) : (
<>
<Trans
i18nKey="one_step_away_from_professional_features"
components={[<strong />]} // eslint-disable-line react/jsx-key
/>
<button
className="pull-right btn btn-info btn-sm"
{t('please_confirm_email', {
emailAddress: userEmail.email,
})}{' '}
<Button
bsStyle="link"
className="btn-inline-link"
onClick={() => handleResendConfirmationEmail(userEmail)}
>
{t('resend_email')}
</button>
<br />
<Trans
i18nKey="institution_has_overleaf_subscription"
values={{
institutionName: userEmail.affiliation?.institution.name,
emailAddress: userEmail.email,
}}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
components={[<strong />]} // eslint-disable-line react/jsx-key
/>
({t('resend_confirmation_email')})
</Button>
</>
)}
</Notification.Body>
</Notification>
)
}
return (
<Notification bsStyle="warning">
<Notification.Body data-testid="pro-notification-body">
{isLoading ? (
<>
<Icon type="spinner" spin /> {t('resending_confirmation_email')}
&hellip;
</>
) : isError ? (
<div aria-live="polite">{getUserFacingMessage(error)}</div>
) : (
<>
{t('please_confirm_email', {
emailAddress: userEmail.email,
})}{' '}
<Button
bsStyle="link"
className="btn-inline-link"
onClick={() => handleResendConfirmationEmail(userEmail)}
>
({t('resend_confirmation_email')})
</Button>
</>
)}
</Notification.Body>
</Notification>
</div>
}
/>
)
}

View file

@ -4,6 +4,7 @@ import type { Dispatch, SetStateAction } from 'react'
import Notification from '../../notification'
import { GroupInvitationStatus } from './hooks/use-group-invitation-notification'
import type { NotificationGroupInvitation } from '../../../../../../../../types/project/dashboard/notification'
import getMeta from '@/utils/meta'
type GroupInvitationCancelIndividualSubscriptionNotificationProps = {
setGroupInvitationStatus: Dispatch<SetStateAction<GroupInvitationStatus>>
@ -19,30 +20,41 @@ export default function GroupInvitationCancelIndividualSubscriptionNotification(
notification,
}: GroupInvitationCancelIndividualSubscriptionNotificationProps) {
const { t } = useTranslation()
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const {
messageOpts: { inviterName },
} = notification
return (
<Notification bsStyle="info" onDismiss={dismissGroupInviteNotification}>
<Notification.Body>
{t('invited_to_group_have_individual_subcription', { inviterName })}
</Notification.Body>
<Notification.Action className="group-invitation-cancel-subscription-notification-buttons">
<Button
bsStyle="info"
bsSize="sm"
className="me-1"
onClick={() =>
setGroupInvitationStatus(GroupInvitationStatus.AskToJoin)
}
>
{t('not_now')}
</Button>
<Button bsStyle="info" bsSize="sm" onClick={cancelPersonalSubscription}>
{t('cancel_my_subscription')}
</Button>
</Notification.Action>
</Notification>
<Notification
bsStyle="info"
onDismiss={dismissGroupInviteNotification}
body={t('invited_to_group_have_individual_subcription', { inviterName })}
action={
<div className="group-invitation-cancel-subscription-notification-buttons">
<Button
bsStyle={newNotificationStyle ? null : 'info'}
bsSize="sm"
className={newNotificationStyle ? 'me-1 btn-secondary' : 'me-1'}
onClick={() =>
setGroupInvitationStatus(GroupInvitationStatus.AskToJoin)
}
>
{t('not_now')}
</Button>
<Button
bsStyle={newNotificationStyle ? null : 'info'}
className={newNotificationStyle ? 'btn-secondary' : ''}
bsSize="sm"
onClick={cancelPersonalSubscription}
>
{t('cancel_my_subscription')}
</Button>
</div>
}
/>
)
}

View file

@ -2,6 +2,7 @@ import { Button } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import Notification from '../../notification'
import type { NotificationGroupInvitation } from '../../../../../../../../types/project/dashboard/notification'
import getMeta from '@/utils/meta'
type GroupInvitationNotificationProps = {
acceptGroupInvite: () => void
@ -17,26 +18,30 @@ export default function GroupInvitationNotificationJoin({
dismissGroupInviteNotification,
}: GroupInvitationNotificationProps) {
const { t } = useTranslation()
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const {
messageOpts: { inviterName },
} = notification
return (
<Notification bsStyle="info" onDismiss={dismissGroupInviteNotification}>
<Notification.Body>
{t('invited_to_group', { inviterName })}
</Notification.Body>
<Notification.Action>
<Notification
bsStyle="info"
onDismiss={dismissGroupInviteNotification}
body={t('invited_to_group', { inviterName })}
action={
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
bsSize="sm"
className="pull-right"
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
onClick={acceptGroupInvite}
disabled={isAcceptingInvitation}
>
{t('join_now')}
</Button>
</Notification.Action>
</Notification>
}
/>
)
}

View file

@ -1,6 +1,7 @@
import Notification from '../../notification'
import { useTranslation } from 'react-i18next'
import Icon from '../../../../../../shared/components/icon'
import getMeta from '@/utils/meta'
type GroupInvitationSuccessfulNotificationProps = {
hideNotification: () => void
@ -10,13 +11,23 @@ export default function GroupInvitationSuccessfulNotification({
hideNotification,
}: GroupInvitationSuccessfulNotificationProps) {
const { t } = useTranslation()
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
return (
<Notification bsStyle="success" onDismiss={hideNotification}>
<Notification.Body>
<Icon type="check-circle" fw aria-hidden="true" className="me-1" />
{t('congratulations_youve_successfully_join_group')}
</Notification.Body>
</Notification>
<Notification
bsStyle="success"
onDismiss={hideNotification}
body={
<>
{!newNotificationStyle && (
<Icon type="check-circle" fw aria-hidden="true" className="me-1" />
)}
{t('congratulations_youve_successfully_join_group')}
</>
}
/>
)
}

View file

@ -17,6 +17,10 @@ function Institution() {
'ol-notificationsInstitution',
[]
) as InstitutionType[]
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const { handleDismiss } = useAsyncDismiss()
if (!notificationsInstitution.length) {
@ -41,47 +45,51 @@ function Institution() {
) => (
<Fragment key={index}>
{templateKey === 'notification_institution_sso_available' && (
<Notification bsStyle="info">
<Notification.Body>
<p>
<Trans
i18nKey="can_link_institution_email_acct_to_institution_acct"
components={{ b: <b /> }}
values={{ appName, email, institutionName }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>
</p>
<div>
<Trans
i18nKey="doing_this_allow_log_in_through_institution"
components={{ b: <b /> }}
values={{ appName }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>{' '}
<a href="/learn/how-to/Institutional_Login">
{t('learn_more')}
</a>
</div>
</Notification.Body>
<Notification.Action>
<Notification
bsStyle="info"
body={
<>
{' '}
<p>
<Trans
i18nKey="can_link_institution_email_acct_to_institution_acct"
components={{ b: <b /> }}
values={{ appName, email, institutionName }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>
</p>
<div>
<Trans
i18nKey="doing_this_allow_log_in_through_institution"
components={{ b: <b /> }}
values={{ appName }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>{' '}
<a href="/learn/how-to/Institutional_Login">
{t('learn_more')}
</a>
</div>
</>
}
action={
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
className={newNotificationStyle ? 'btn-secondary' : ''}
bsSize="sm"
href={`${samlInitPath}?university_id=${institutionId}&auto=/project&email=${email}`}
>
{t('link_account')}
</Button>
</Notification.Action>
</Notification>
}
/>
)}
{templateKey === 'notification_institution_sso_linked' && (
<Notification
bsStyle="info"
onDismiss={() => id && handleDismiss(id)}
>
<Notification.Body>
body={
<Trans
i18nKey="account_has_been_link_to_institution_account"
components={{ b: <b /> }}
@ -89,76 +97,88 @@ function Institution() {
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>
</Notification.Body>
</Notification>
}
/>
)}
{templateKey === 'notification_institution_sso_non_canonical' && (
<Notification
bsStyle="warning"
onDismiss={() => id && handleDismiss(id)}
>
<Notification.Body>
<Icon type="exclamation-triangle" fw />
<Trans
i18nKey="tried_to_log_in_with_email"
components={{ b: <b /> }}
values={{ appName, email: requestedEmail }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>{' '}
<Trans
i18nKey="in_order_to_match_institutional_metadata_associated"
components={{ b: <b /> }}
values={{ email: institutionEmail }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>
</Notification.Body>
</Notification>
body={
<>
{!newNotificationStyle && (
<>
<Icon type="exclamation-triangle" fw />{' '}
</>
)}
<Trans
i18nKey="tried_to_log_in_with_email"
components={{ b: <b /> }}
values={{ appName, email: requestedEmail }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>{' '}
<Trans
i18nKey="in_order_to_match_institutional_metadata_associated"
components={{ b: <b /> }}
values={{ email: institutionEmail }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>
</>
}
/>
)}
{templateKey ===
'notification_institution_sso_already_registered' && (
<Notification
bsStyle="info"
onDismiss={() => id && handleDismiss(id)}
>
<Notification.Body>
<Trans
i18nKey="tried_to_register_with_email"
components={{ b: <b /> }}
values={{ appName, email }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>{' '}
{t('we_logged_you_in')}
</Notification.Body>
<Notification.Action>
body={
<>
<Trans
i18nKey="tried_to_register_with_email"
components={{ b: <b /> }}
values={{ appName, email }}
shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }}
/>{' '}
{t('we_logged_you_in')}
</>
}
action={
<Button
bsStyle="info"
bsStyle={newNotificationStyle ? null : 'info'}
className={newNotificationStyle ? 'btn-secondary' : ''}
bsSize="sm"
href="/learn/how-to/Institutional_Login"
>
{t('find_out_more')}
</Button>
</Notification.Action>
</Notification>
}
/>
)}
{templateKey === 'notification_institution_sso_error' && (
<Notification
bsStyle="danger"
onDismiss={() => id && handleDismiss(id)}
>
<Notification.Body>
<Icon type="exclamation-triangle" fw />{' '}
{t('generic_something_went_wrong')}.
<div>
{error?.translatedMessage
? error?.translatedMessage
: error?.message}
</div>
{error?.tryAgain ? `${t('try_again')}.` : null}
</Notification.Body>
</Notification>
body={
<>
{!newNotificationStyle && (
<>
<Icon type="exclamation-triangle" fw />{' '}
</>
)}
{t('generic_something_went_wrong')}.
<div>
{error?.translatedMessage
? error?.translatedMessage
: error?.message}
</div>
{error?.tryAgain ? `${t('try_again')}.` : null}
</>
}
/>
)}
</Fragment>
)

View file

@ -61,13 +61,15 @@ function Notification({
bsStyle === 'danger' ? 'error' : bsStyle
) as NotificationType
return (
<NewNotification
type={newNotificationType}
isDismissible={onDismiss != null}
onDismiss={handleDismiss}
content={body as React.ReactElement}
action={action}
/>
<li>
<NewNotification
type={newNotificationType}
isDismissible={onDismiss != null}
onDismiss={handleDismiss}
content={body as React.ReactElement}
action={action}
/>
</li>
)
}

View file

@ -13,6 +13,7 @@ import getMeta from '../../../../utils/meta'
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
import customLocalStorage from '../../../../infrastructure/local-storage'
import { sendMB } from '../../../../infrastructure/event-tracking'
import classNames from 'classnames'
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
const isChromium = () =>
@ -34,6 +35,10 @@ const EnrollmentNotification: JSXElementConstructor<{
}> = enrollmentNotificationModule?.import.default
function UserNotifications() {
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const groupSubscriptionsPendingEnrollment: Subscription[] = getMeta(
'ol-groupSubscriptionsPendingEnrollment',
[]
@ -61,7 +66,7 @@ function UserNotifications() {
}
const show =
user.writefull?.enabled === true || // show to any users who have writefull enabled regardless of split test
user?.writefull?.enabled === true || // show to any users who have writefull enabled regardless of split test
(!writefullIntegrationSplitTestEnabled && // show old banner to users who are not in the split test, who are on chrome and havent dismissed
isChromium() &&
getMeta('ol-showWritefullPromoBanner'))
@ -71,7 +76,7 @@ function UserNotifications() {
location: 'dashboard-banner',
page: '/project',
name:
user.writefull?.enabled === true ||
user?.writefull?.enabled === true ||
writefullIntegrationSplitTestEnabled
? 'writefull-premium'
: 'writefull',
@ -83,7 +88,11 @@ function UserNotifications() {
const [dismissedWritefull, setDismissedWritefull] = useState(false)
return (
<div className="user-notifications">
<div
className={classNames('user-notifications', {
'notification-list': newNotificationStyle,
})}
>
<ul className="list-unstyled">
{EnrollmentNotification &&
groupSubscriptionsPendingEnrollment.map(subscription => (
@ -110,7 +119,7 @@ function UserNotifications() {
splitTestName={inrGeoBannerSplitTestName}
/>
) : null}
{writefullIntegrationSplitTestEnabled || user.writefull?.enabled ? (
{writefullIntegrationSplitTestEnabled || user?.writefull?.enabled ? (
<WritefullPremiumPromoBanner
show={showWritefull}
setShow={setShowWritefull}

View file

@ -44,7 +44,7 @@ function WritefullPremiumPromoBanner({
}
action={
<a
className="btn btn-secondary"
className="btn btn-secondary btn-sm"
href="https://my.writefull.com/overleaf-invite?code=OVERLEAF10&redirect=plans"
target="_blank"
rel="noreferrer"

View file

@ -2,6 +2,7 @@ import { memo, useCallback } from 'react'
import Notification from './notification'
import { sendMB } from '../../../../infrastructure/event-tracking'
import customLocalStorage from '../../../../infrastructure/local-storage'
import getMeta from '@/utils/meta'
const eventSegmentation = {
location: 'dashboard-banner',
@ -18,6 +19,10 @@ function WritefullPromoBanner({
setShow: (value: boolean) => void
onDismiss: () => void
}) {
const newNotificationStyle = getMeta(
'ol-newNotificationStyle',
false
) as boolean
const handleOpenLink = useCallback(() => {
sendMB('promo-click', eventSegmentation)
}, [])
@ -47,7 +52,11 @@ function WritefullPromoBanner({
}
action={
<a
className="pull-right btn btn-info btn-sm"
className={
newNotificationStyle
? 'btn btn-secondary btn-sm'
: 'pull-right btn btn-info btn-sm'
}
href="https://my.writefull.com/overleaf-invite?code=OVERLEAF10"
target="_blank"
rel="noreferrer"

View file

@ -0,0 +1,340 @@
import UserNotifications from '../../js/features/project-list/components/notifications/user-notifications'
import { ProjectListProvider } from '../../js/features/project-list/context/project-list-context'
import useFetchMock from '../hooks/use-fetch-mock'
import {
commonSetupMocks,
errorsMocks,
fakeReconfirmationUsersData,
institutionSetupMocks,
reconfirmAffiliationSetupMocks,
reconfirmationSetupMocks,
setCommonMeta,
setInstitutionMeta,
setReconfirmAffiliationMeta,
setReconfirmationMeta,
} from './helpers/emails'
export const ProjectInvite = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
templateKey: 'notification_project_invite',
messageOpts: {
projectId: '123',
projectName: 'Abc Project',
userName: 'fakeUser',
token: 'abcdef',
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const ProjectInviteNetworkError = (args: any) => {
useFetchMock(errorsMocks)
setCommonMeta({
templateKey: 'notification_project_invite',
messageOpts: {
projectId: '123',
projectName: 'Abc Project',
userName: 'fakeUser',
token: 'abcdef',
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const Wfh2020UpgradeOffer = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
_id: 1,
templateKey: 'wfh_2020_upgrade_offer',
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const IPMatchedAffiliationSsoEnabled = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
_id: 1,
templateKey: 'notification_ip_matched_affiliation',
messageOpts: {
university_name: 'Abc University',
institutionId: '456',
ssoEnabled: true,
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const IPMatchedAffiliationSsoDisabled = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
_id: 1,
templateKey: 'notification_ip_matched_affiliation',
messageOpts: {
university_name: 'Abc University',
institutionId: '456',
ssoEnabled: false,
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const TpdsFileLimit = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
_id: 1,
templateKey: 'notification_tpds_file_limit',
messageOpts: {
projectName: 'Abc Project',
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const DropBoxDuplicateProjectNames = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
_id: 1,
templateKey: 'notification_dropbox_duplicate_project_names',
messageOpts: {
projectName: 'Abc Project',
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const DropBoxUnlinkedDueToLapsedReconfirmation = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
_id: 1,
templateKey: 'notification_dropbox_unlinked_due_to_lapsed_reconfirmation',
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const NotificationGroupInvitation = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
_id: 1,
templateKey: 'notification_group_invitation',
messageOpts: {
inviterName: 'John Doe',
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const NotificationGroupInvitationCancelSubscription = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({
_id: 1,
templateKey: 'notification_group_invitation',
messageOpts: {
inviterName: 'John Doe',
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
window.metaAttributesCache.set('ol-hasIndividualRecurlySubscription', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const NonSpecificMessage = (args: any) => {
useFetchMock(commonSetupMocks)
setCommonMeta({ _id: 1, html: 'Non specific message' })
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const InstitutionSsoAvailable = (args: any) => {
useFetchMock(institutionSetupMocks)
setInstitutionMeta({
_id: 1,
templateKey: 'notification_institution_sso_available',
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const InstitutionSsoLinked = (args: any) => {
useFetchMock(institutionSetupMocks)
setInstitutionMeta({
_id: 1,
templateKey: 'notification_institution_sso_linked',
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const InstitutionSsoNonCanonical = (args: any) => {
useFetchMock(institutionSetupMocks)
setInstitutionMeta({
_id: 1,
templateKey: 'notification_institution_sso_non_canonical',
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const InstitutionSsoAlreadyRegistered = (args: any) => {
useFetchMock(institutionSetupMocks)
setInstitutionMeta({
_id: 1,
templateKey: 'notification_institution_sso_already_registered',
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const InstitutionSsoError = (args: any) => {
useFetchMock(institutionSetupMocks)
setInstitutionMeta({
templateKey: 'notification_institution_sso_error',
error: {
message: 'message',
translatedMessage: 'Translated Message',
tryAgain: true,
},
})
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const ResendConfirmationEmail = (args: any) => {
useFetchMock(reconfirmationSetupMocks)
setReconfirmationMeta()
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const ResendConfirmationEmailNetworkError = (args: any) => {
useFetchMock(errorsMocks)
setReconfirmationMeta()
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const ReconfirmAffiliation = (args: any) => {
useFetchMock(reconfirmAffiliationSetupMocks)
setReconfirmAffiliationMeta()
window.metaAttributesCache.set('ol-allInReconfirmNotificationPeriods', [
fakeReconfirmationUsersData,
])
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const ReconfirmAffiliationNetworkError = (args: any) => {
useFetchMock(errorsMocks)
setReconfirmAffiliationMeta()
window.metaAttributesCache.set('ol-allInReconfirmNotificationPeriods', [
fakeReconfirmationUsersData,
])
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export const ReconfirmedAffiliationSuccess = (args: any) => {
useFetchMock(reconfirmAffiliationSetupMocks)
setReconfirmAffiliationMeta()
window.metaAttributesCache.set('ol-userEmails', [fakeReconfirmationUsersData])
window.metaAttributesCache.set('ol-newNotificationStyle', true)
return (
<ProjectListProvider>
<UserNotifications {...args} />
</ProjectListProvider>
)
}
export default {
title: 'Project List / Notifications New Style',
component: UserNotifications,
}