mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #7697 from overleaf/msm-sso-flow-alerts
[Settings] SSO Flow Alerts GitOrigin-RevId: fc89c86a6681b27e86bb6bf12f8bee51eb25aa8d
This commit is contained in:
parent
146a207fd1
commit
bb59627db3
5 changed files with 270 additions and 0 deletions
|
@ -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": "",
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
62
services/web/frontend/stories/settings/sso-alert.stories.tsx
Normal file
62
services/web/frontend/stories/settings/sso-alert.stories.tsx
Normal 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,
|
||||
}
|
|
@ -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": "You’ve tried to login with <b>__email__</b>.",
|
||||
"tried_to_register_with_email": "You’ve 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, we’ve linked your account using <b>__email__</b>.",
|
||||
"in_order_to_match_institutional_metadata_2": "In order to match your institutional metadata, we’ve 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",
|
||||
|
|
|
@ -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
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue