Merge pull request #7697 from overleaf/msm-sso-flow-alerts

[Settings] SSO Flow Alerts

GitOrigin-RevId: fc89c86a6681b27e86bb6bf12f8bee51eb25aa8d
This commit is contained in:
Timothée Alby 2022-04-25 13:04:55 +02:00 committed by Copybot
parent 146a207fd1
commit bb59627db3
5 changed files with 270 additions and 0 deletions

View file

@ -194,6 +194,8 @@
"imported_from_the_output_of_another_project_at_date": "",
"imported_from_zotero_at_date": "",
"importing_and_merging_changes_in_github": "",
"in_order_to_match_institutional_metadata_2": "",
"institution_acct_successfully_linked_2": "",
"institution_and_role": "",
"integrations": "",
"invalid_email": "",
@ -414,6 +416,7 @@
"template_approved_by_publisher": "",
"terminated": "",
"thanks_settings_updated": "",
"this_grants_access_to_features_2": "",
"this_project_is_public": "",
"this_project_is_public_read_only": "",
"this_project_will_appear_in_your_dropbox_folder_at": "",
@ -426,6 +429,7 @@
"too_many_requests": "",
"too_recently_compiled": "",
"total_words": "",
"try_again": "",
"try_it_for_free": "",
"try_premium_for_free": "",
"try_recompile_project": "",

View file

@ -0,0 +1,101 @@
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'
type InstitutionLink = {
universityName: string
hasEntitlement?: boolean
}
type SAMLError = {
translatedMessage?: string
message?: string
tryAgain?: boolean
}
export function SSOAlert() {
const { t } = useTranslation()
const institutionLinked: InstitutionLink | undefined = getMeta(
'ol-institutionLinked'
)
const institutionEmailNonCanonical: string | undefined = getMeta(
'ol-institutionEmailNonCanonical'
)
const samlError: SAMLError | undefined = getMeta('ol-samlError')
const [infoClosed, setInfoClosed] = useState(false)
const [warningClosed, setWarningClosed] = useState(false)
const [errorClosed, setErrorClosed] = useState(false)
const handleInfoClosed = () => setInfoClosed(true)
const handleWarningClosed = () => setWarningClosed(true)
const handleErrorClosed = () => setErrorClosed(true)
if (samlError) {
return (
!errorClosed && (
<Alert bsStyle="danger" onDismiss={handleErrorClosed}>
<p className="text-center">
<Icon
type="exclamation-triangle"
accessibilityLabel={t('generic_something_went_wrong')}
/>{' '}
{samlError.translatedMessage
? samlError.translatedMessage
: samlError.message}
</p>
{samlError.tryAgain && (
<p className="text-center">{t('try_again')}</p>
)}
</Alert>
)
)
}
if (!institutionLinked) {
return null
}
return (
<>
{!infoClosed && (
<Alert bsStyle="info" onDismiss={handleInfoClosed}>
<p className="text-center">
<Trans
i18nKey="institution_acct_successfully_linked_2"
components={[<strong />]} // eslint-disable-line react/jsx-key
values={{ institutionName: institutionLinked.universityName }}
/>
</p>
{institutionLinked.hasEntitlement && (
<p className="text-center">
<Trans
i18nKey="this_grants_access_to_features_2"
components={[<strong />]} // eslint-disable-line react/jsx-key
values={{ featureType: t('professional') }}
/>
</p>
)}
</Alert>
)}
{!warningClosed && institutionEmailNonCanonical && (
<Alert bsStyle="warning" onDismiss={handleWarningClosed}>
<p className="text-center">
<Icon
type="exclamation-triangle"
accessibilityLabel={t('generic_something_went_wrong')}
/>{' '}
<Trans
i18nKey="in_order_to_match_institutional_metadata_2"
components={[<strong />]} // eslint-disable-line react/jsx-key
values={{ email: institutionEmailNonCanonical }}
/>
</p>
</Alert>
)}
</>
)
}

View file

@ -0,0 +1,62 @@
import EmailsSection from '../../js/features/settings/components/emails-section'
import { SSOAlert } from '../../js/features/settings/components/emails/sso-alert'
export const Info = args => {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-institutionLinked', {
universityName: 'Overleaf University',
})
return <SSOAlert />
}
export const InfoWithEntitlement = args => {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-institutionLinked', {
universityName: 'Overleaf University',
hasEntitlement: true,
})
return <SSOAlert />
}
export const NonCanonicalEmail = args => {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-institutionLinked', {
universityName: 'Overleaf University',
})
window.metaAttributesCache.set(
'ol-institutionEmailNonCanonical',
'user@example.com'
)
return <SSOAlert />
}
export const Error = args => {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-samlError', {
translatedMessage: 'There was an Error',
})
return <SSOAlert />
}
export const ErrorTranslated = args => {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-samlError', {
translatedMessage: 'Translated Error Message',
message: 'There was an Error',
})
return <SSOAlert />
}
export const ErrorWithTryAgain = args => {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-samlError', {
message: 'There was an Error',
tryAgain: true,
})
return <SSOAlert />
}
export default {
title: 'Account Settings / SSO Alerts',
component: EmailsSection,
}

View file

@ -167,6 +167,7 @@
"register_with_email_provided": "<a href=\"__link__\">Register with __appName__</a> using the email and password you provided.",
"security_reasons_linked_accts": "For security reasons, as your institutional email is already associated with the <b>__email__</b> <b>__appName__</b> account, we can only allow account linking with that specific account.",
"this_grants_access_to_features": "This grants you access to <b>__appName__</b> <b>__featureType__</b> features.",
"this_grants_access_to_features_2": "This grants you access to <0>__appName__</0> <0>__featureType__</0> features.",
"to_add_email_accounts_need_to_be_linked": "To add this email, your <b>__appName__</b> and <b>__institutionName__</b> accounts will need to be linked.",
"tried_to_log_in_with_email": "Youve tried to login with <b>__email__</b>.",
"tried_to_register_with_email": "Youve tried to register with <b>__email__</b>, which is already registered with <b>__appName__</b> as an institutional account.",
@ -194,12 +195,14 @@
"if_owner_can_link": "If you own the <b>__appName__</b> account with <b>__email__</b>, you will be allowed to link it to your <b>__institutionName__</b> institutional account.",
"ignore_and_continue_institution_linking": "You can also ignore this and <a href=\"__link__\">continue to __appName__ with your <b>__email__</b> account</a>.",
"in_order_to_match_institutional_metadata": "In order to match your institutional metadata, weve linked your account using <b>__email__</b>.",
"in_order_to_match_institutional_metadata_2": "In order to match your institutional metadata, weve linked your account using <0>__email__</0>.",
"in_order_to_match_institutional_metadata_associated": "In order to match your institutional metadata, your account is associated with the email <b>__email__</b>.",
"institution_account_tried_to_add_already_registered": "The email/institution account you tried to add is <b>already registered</b> with __appName__.",
"institution_account_tried_to_add_already_linked": "This institution is <b>already linked</b> with your account via another email address.",
"institution_account_tried_to_add_not_affiliated": "This email is <b>already associated</b> with your account but not affiliated with this institution.",
"institution_account_tried_to_add_affiliated_with_another_institution": "This email is <b>already associated</b> with your account but affiliated with another institution.",
"institution_acct_successfully_linked": "Your <b>__appName__</b> account was successfully linked to your <b>__institutionName__</b> institutional account.",
"institution_acct_successfully_linked_2": "Your <0>__appName__</0> account was successfully linked to your <0>__institutionName__</0> institutional account.",
"institution_account_tried_to_confirm_saml": "This email cannot be confirmed. Please remove the email from your account and try adding it again.",
"institution_email_new_to_app": "Your <b>__institutionName__</b> email (<b>__email__</b>) is new to __appName__.",
"institutional": "Institutional",

View file

@ -0,0 +1,100 @@
import { render, screen, fireEvent } from '@testing-library/react'
import { expect } from 'chai'
import { SSOAlert } from '../../../../../../frontend/js/features/settings/components/emails/sso-alert'
describe('<SSOAlert/>', function () {
beforeEach(function () {
window.metaAttributesCache = new Map()
})
describe('when thereis no institutional linking information', function () {
it('should be empty', function () {
render(<SSOAlert />)
expect(screen.queryByRole('alert')).to.be.null
})
})
describe('when there is institutional linking information', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-institutionLinked', {
universityName: 'Overleaf University',
})
})
it('should render an information alert with the university name', function () {
render(<SSOAlert />)
screen.getByRole('alert')
screen.getByText('account was successfully linked', { exact: false })
screen.getByText('Overleaf University', { exact: false })
})
it('when entitled, it should render access granted to "professional" features', function () {
window.metaAttributesCache.get('ol-institutionLinked').hasEntitlement =
true
render(<SSOAlert />)
screen.getByText('this grants you access', { exact: false })
screen.getByText('Professional')
})
it('when the email is not canonical it should also render a warning alert', function () {
window.metaAttributesCache.set(
'ol-institutionEmailNonCanonical',
'user@example.com'
)
render(<SSOAlert />)
const alerts = screen.getAllByRole('alert')
expect(alerts.length).to.equal(2)
})
it('the alerts should be closeable', function () {
window.metaAttributesCache.set(
'ol-institutionEmailNonCanonical',
'user@example.com'
)
render(<SSOAlert />)
const closeButtons = screen.getAllByRole('button', {
name: 'Close alert',
})
fireEvent.click(closeButtons[0])
fireEvent.click(closeButtons[1])
expect(screen.queryByRole('button', { name: 'Close alert' })).to.be.null
})
})
describe('when there is a SAML Error', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-samlError', {
message: 'there was an error',
})
})
it('should render an error alert', function () {
render(<SSOAlert />)
screen.getByRole('alert')
screen.getByText('there was an error')
})
it('should render translated error if available', function () {
window.metaAttributesCache.get('ol-samlError').translatedMessage =
'translated error'
render(<SSOAlert />)
screen.getByText('translated error')
expect(screen.queryByText('there was an error')).to.be.null
})
it('should render a "try again" label when requested by the error payload', function () {
window.metaAttributesCache.get('ol-samlError').tryAgain = true
render(<SSOAlert />)
screen.getByText('Please try again')
})
it('the alert should be closeable', function () {
render(<SSOAlert />)
const closeButton = screen.getByRole('button', {
name: 'Close alert',
})
fireEvent.click(closeButton)
expect(screen.queryByRole('button', { name: 'Close alert' })).to.be.null
})
})
})