mirror of
https://github.com/overleaf/overleaf.git
synced 2025-03-19 19:14:02 +00:00
Merge pull request #8029 from overleaf/ta-settings-fix-7
[SettingsPage] Small Fixes 7 GitOrigin-RevId: 2716fe13af3f5f6b56d6bba47505fad75ba1adbf
This commit is contained in:
parent
2c62ba29c7
commit
3580ec6db3
14 changed files with 85 additions and 31 deletions
|
@ -355,7 +355,7 @@
|
|||
"reconnect": "",
|
||||
"redirect_to_editor": "",
|
||||
"reference_error_relink_hint": "",
|
||||
"reference_sync": "",
|
||||
"reference_managers": "",
|
||||
"references_search_hint": "",
|
||||
"refresh": "",
|
||||
"refresh_page_after_linking_dropbox": "",
|
||||
|
|
|
@ -31,7 +31,7 @@ function EmailsSectionContent() {
|
|||
<Trans i18nKey="change_primary_email_address_instructions">
|
||||
<strong />
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
|
||||
<a href="/learn/how-to/Keeping_your_account_secure" />
|
||||
<a href="/learn/how-to/Managing_your_Overleaf_emails" />
|
||||
</Trans>
|
||||
</p>
|
||||
<>
|
||||
|
|
|
@ -114,7 +114,10 @@ function AddEmail() {
|
|||
<label htmlFor="affiliations-email" className="sr-only">
|
||||
{t('email')}
|
||||
</label>
|
||||
<Input onChange={handleEmailChange} />
|
||||
<Input
|
||||
onChange={handleEmailChange}
|
||||
handleAddNewEmail={handleAddNewEmail}
|
||||
/>
|
||||
</Cell>
|
||||
</Col>
|
||||
{isSsoAvailable(newEmailMatchedInstitution) ? (
|
||||
|
|
|
@ -40,9 +40,10 @@ export function clearDomainCache() {
|
|||
|
||||
type InputProps = {
|
||||
onChange: (value: string, institution?: InstitutionInfo) => void
|
||||
handleAddNewEmail: () => void
|
||||
}
|
||||
|
||||
function Input({ onChange }: InputProps) {
|
||||
function Input({ onChange, handleAddNewEmail }: InputProps) {
|
||||
const { signal } = useAbortController()
|
||||
|
||||
const inputRef = useRef<HTMLInputElement | null>(null)
|
||||
|
@ -126,6 +127,11 @@ function Input({ onChange }: InputProps) {
|
|||
|
||||
if (suggestion) {
|
||||
setInputValueAndResetSuggestion()
|
||||
} else {
|
||||
const match = matchLocalAndDomain(inputValue)
|
||||
if (match.local && match.domain) {
|
||||
handleAddNewEmail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +140,7 @@ function Input({ onChange }: InputProps) {
|
|||
setInputValueAndResetSuggestion()
|
||||
}
|
||||
},
|
||||
[suggestion]
|
||||
[inputValue, suggestion, handleAddNewEmail]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
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'
|
||||
|
||||
export function LeaversSurveyAlert() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [expirationDate, setExpirationDate] = usePersistedState(
|
||||
'showInstitutionalLeaversSurveyUntil',
|
||||
0,
|
||||
true
|
||||
)
|
||||
const {
|
||||
showInstitutionalLeaversSurveyUntil,
|
||||
setShowInstitutionalLeaversSurveyUntil,
|
||||
} = useUserEmailsContext()
|
||||
|
||||
const [hide, setHide] = usePersistedState(
|
||||
'hideInstitutionalLeaversSurvey',
|
||||
|
@ -18,11 +18,11 @@ export function LeaversSurveyAlert() {
|
|||
)
|
||||
|
||||
function handleDismiss() {
|
||||
setExpirationDate(0)
|
||||
setShowInstitutionalLeaversSurveyUntil(0)
|
||||
setHide(true)
|
||||
}
|
||||
|
||||
if (Date.now() > expirationDate) {
|
||||
if (Date.now() > showInstitutionalLeaversSurveyUntil) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ function LinkingSection() {
|
|||
{hasReferencesLinkingSection ? (
|
||||
<>
|
||||
<h3 id="references" className="text-capitalize">
|
||||
{t('reference_sync')}
|
||||
{t('reference_managers')}
|
||||
</h3>
|
||||
<div className="settings-widgets-container">
|
||||
{referenceLinkingWidgets.map(
|
||||
|
@ -146,9 +146,9 @@ function SSOLinkingWidgetContainer({
|
|||
break
|
||||
case 'google':
|
||||
case 'twitter':
|
||||
description = t('login_with_service', {
|
||||
description = `${t('login_with_service', {
|
||||
service: subscription.provider.name,
|
||||
})
|
||||
})}.`
|
||||
break
|
||||
case 'orcid':
|
||||
description = t('oauth_orcid_description')
|
||||
|
|
|
@ -203,10 +203,10 @@ const reducer = (state: State, action: Action) => {
|
|||
}
|
||||
|
||||
function useUserEmails() {
|
||||
const [, setExpirationDate] = usePersistedState(
|
||||
'showInstitutionalLeaversSurveyUntil',
|
||||
0
|
||||
)
|
||||
const [
|
||||
showInstitutionalLeaversSurveyUntil,
|
||||
setShowInstitutionalLeaversSurveyUntil,
|
||||
] = usePersistedState('showInstitutionalLeaversSurveyUntil', 0, true)
|
||||
const [state, unsafeDispatch] = useReducer(reducer, initialState)
|
||||
const dispatch = useSafeDispatch(unsafeDispatch)
|
||||
const { data, isLoading, isError, isSuccess, runAsync } =
|
||||
|
@ -237,11 +237,11 @@ function useUserEmails() {
|
|||
userEmail.emailHasInstitutionLicence
|
||||
)
|
||||
if (!stillHasLicenseAccess) {
|
||||
setExpirationDate(Date.now() + ONE_WEEK_IN_MS)
|
||||
setShowInstitutionalLeaversSurveyUntil(Date.now() + ONE_WEEK_IN_MS)
|
||||
}
|
||||
}
|
||||
},
|
||||
[state, setExpirationDate]
|
||||
[state, setShowInstitutionalLeaversSurveyUntil]
|
||||
)
|
||||
|
||||
return {
|
||||
|
@ -250,6 +250,8 @@ function useUserEmails() {
|
|||
isInitializingSuccess: isSuccess,
|
||||
isInitializingError: isError,
|
||||
getEmails,
|
||||
showInstitutionalLeaversSurveyUntil,
|
||||
setShowInstitutionalLeaversSurveyUntil,
|
||||
resetLeaversSurveyExpiration,
|
||||
setLoading: useCallback(
|
||||
(flag: boolean) => dispatch(ActionCreators.setLoading(flag)),
|
||||
|
|
|
@ -97,6 +97,7 @@ export function defaultSetupMocks(fetchMock) {
|
|||
})
|
||||
.get(/\/institutions\/domains\?hostname=a/, fakeInstitutionDomain1)
|
||||
.get(/\/institutions\/domains\?hostname=f/, fakeInstitutionDomain2)
|
||||
.get(/\/institutions\/domains/, [])
|
||||
.post(/\/user\/emails\/*/, 200, {
|
||||
delay: MOCK_DELAY,
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import EmailsSection from '../../js/features/settings/components/emails-section'
|
||||
import { UserEmailsProvider } from '../../js/features/settings/context/user-email-context'
|
||||
import { LeaversSurveyAlert } from '../../js/features/settings/components/leavers-survey-alert'
|
||||
import localStorage from '../../js/infrastructure/local-storage'
|
||||
|
||||
|
@ -7,7 +8,11 @@ export const SurveyAlert = () => {
|
|||
'showInstitutionalLeaversSurveyUntil',
|
||||
Date.now() + 1000 * 60 * 60
|
||||
)
|
||||
return <LeaversSurveyAlert />
|
||||
return (
|
||||
<UserEmailsProvider>
|
||||
<LeaversSurveyAlert />
|
||||
</UserEmailsProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
defaultSetupMocks as defaultSetupLinkingMocks,
|
||||
} from './helpers/linking'
|
||||
import { UserProvider } from '../../js/shared/context/user-context'
|
||||
import { ScopeDecorator } from '../decorators/scope'
|
||||
|
||||
export const Overleaf = args => {
|
||||
setDefaultLeaveMeta()
|
||||
|
@ -70,4 +71,5 @@ export const ServerPro = args => {
|
|||
export default {
|
||||
title: 'Account Settings / Full Page',
|
||||
component: SettingsPageRoot,
|
||||
decorators: [ScopeDecorator],
|
||||
}
|
||||
|
|
|
@ -263,7 +263,7 @@
|
|||
"dropbox_already_linked_error_with_email": "Your Dropbox account cannot be linked as it is already linked with another Overleaf account using email address __otherUsersEmail__.",
|
||||
"github_too_many_files_error": "This repository cannot be imported as it exceeds the maximum number of files allowed",
|
||||
"linked_accounts": "linked accounts",
|
||||
"linked_accounts_explained": "You can link your __appName__ account with other services to enable the features described below",
|
||||
"linked_accounts_explained": "You can link your __appName__ account with other services to enable the features described below.",
|
||||
"oauth_orcid_description": " <a href=\"__link__\">Securely establish your identity by linking your ORCID iD to your __appName__ account</a>. Submissions to participating publishers will automatically include your ORCID iD for improved workflow and visibility. ",
|
||||
"no_existing_password": "Please use the password reset form to set your password",
|
||||
"password_managed_externally": "Password settings are managed externally",
|
||||
|
@ -477,6 +477,7 @@
|
|||
"reference_search": "Advanced reference search",
|
||||
"reference_search_info": "You can always search by citation key, and advanced reference search lets you also search by author, title, year or journal.",
|
||||
"reference_search_info_v2": "It’s easy to find your references - you can search by author, title, year or journal. You can still search by citation key too.",
|
||||
"reference_managers": "Reference managers",
|
||||
"reference_sync": "Reference manager sync",
|
||||
"reference_sync_info": "Manage your reference library in Mendeley and link it directly to a .bib file in Overleaf, so you can easily cite anything in your Mendeley library.",
|
||||
"faq_how_free_trial_works_answer": "You get full access to your chosen __appName__ plan during your __len__-day free trial. There is no obligation to continue beyond the trial. Your card will be charged at the end of your __len__ day trial unless you cancel before then. You can cancel via your subscription settings.",
|
||||
|
|
|
@ -18,6 +18,7 @@ const testInstitutionData = [
|
|||
describe('<AddEmailInput/>', function () {
|
||||
const defaultProps = {
|
||||
onChange: (value: string) => {},
|
||||
handleAddNewEmail: () => {},
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -40,11 +41,19 @@ describe('<AddEmailInput/>', function () {
|
|||
|
||||
describe('when typing text that does not contain any potential domain match', function () {
|
||||
let onChangeStub
|
||||
let handleAddNewEmailStub
|
||||
|
||||
beforeEach(function () {
|
||||
fetchMock.get('express:/institutions/domains', 200)
|
||||
onChangeStub = sinon.stub()
|
||||
render(<Input {...defaultProps} onChange={onChangeStub} />)
|
||||
handleAddNewEmailStub = sinon.stub()
|
||||
render(
|
||||
<Input
|
||||
{...defaultProps}
|
||||
onChange={onChangeStub}
|
||||
handleAddNewEmail={handleAddNewEmailStub}
|
||||
/>
|
||||
)
|
||||
fireEvent.change(screen.getByRole('textbox'), {
|
||||
target: { value: 'user' },
|
||||
})
|
||||
|
@ -66,6 +75,22 @@ describe('<AddEmailInput/>', function () {
|
|||
it('should not make any request for institution domains', function () {
|
||||
expect(fetchMock.called()).to.be.false
|
||||
})
|
||||
|
||||
it('should submit on Enter if email looks valid', async function () {
|
||||
fireEvent.change(screen.getByRole('textbox'), {
|
||||
target: { value: 'user@domain.com' },
|
||||
})
|
||||
fireEvent.keyDown(screen.getByRole('textbox'), { key: 'Enter' })
|
||||
expect(handleAddNewEmailStub.calledWith()).to.equal(true)
|
||||
})
|
||||
|
||||
it('should not submit on Enter if email does not look valid', async function () {
|
||||
fireEvent.change(screen.getByRole('textbox'), {
|
||||
target: { value: 'user@' },
|
||||
})
|
||||
fireEvent.keyDown(screen.getByRole('textbox'), { key: 'Enter' })
|
||||
expect(handleAddNewEmailStub.calledWith()).to.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when typing text that contains a potential domain match', function () {
|
||||
|
@ -73,7 +98,7 @@ describe('<AddEmailInput/>', function () {
|
|||
|
||||
beforeEach(function () {
|
||||
onChangeStub = sinon.stub()
|
||||
render(<Input onChange={onChangeStub} />)
|
||||
render(<Input {...defaultProps} onChange={onChangeStub} />)
|
||||
})
|
||||
|
||||
describe('when there are no matches', function () {
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
import { expect } from 'chai'
|
||||
import { fireEvent, screen, render } from '@testing-library/react'
|
||||
import { UserEmailsProvider } from '../../../../../frontend/js/features/settings/context/user-email-context'
|
||||
import { LeaversSurveyAlert } from '../../../../../frontend/js/features/settings/components/leavers-survey-alert'
|
||||
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
|
||||
|
||||
function renderWithProvider() {
|
||||
render(<LeaversSurveyAlert />, {
|
||||
wrapper: ({ children }) => (
|
||||
<UserEmailsProvider>{children}</UserEmailsProvider>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
describe('<LeaversSurveyAlert/>', function () {
|
||||
it('should render before the expiration date', function () {
|
||||
const tomorrow = Date.now() + 1000 * 60 * 60 * 24
|
||||
localStorage.setItem('showInstitutionalLeaversSurveyUntil', tomorrow)
|
||||
localStorage.setItem('hideInstitutionalLeaversSurvey', false)
|
||||
render(<LeaversSurveyAlert />)
|
||||
renderWithProvider()
|
||||
screen.getByRole('alert')
|
||||
screen.getByText(/Provide some quick feedback/)
|
||||
screen.getByRole('link', { name: 'Take a short survey' })
|
||||
|
@ -18,7 +27,7 @@ describe('<LeaversSurveyAlert/>', function () {
|
|||
const yesterday = Date.now() - 1000 * 60 * 60 * 24
|
||||
localStorage.setItem('showInstitutionalLeaversSurveyUntil', yesterday)
|
||||
localStorage.setItem('hideInstitutionalLeaversSurvey', false)
|
||||
render(<LeaversSurveyAlert />)
|
||||
renderWithProvider()
|
||||
expect(screen.queryByRole('alert')).to.be.null
|
||||
})
|
||||
|
||||
|
@ -26,7 +35,7 @@ describe('<LeaversSurveyAlert/>', function () {
|
|||
const tomorrow = Date.now() + 1000 * 60 * 60 * 24
|
||||
localStorage.setItem('showInstitutionalLeaversSurveyUntil', tomorrow)
|
||||
localStorage.setItem('hideInstitutionalLeaversSurvey', true)
|
||||
render(<LeaversSurveyAlert />)
|
||||
renderWithProvider()
|
||||
expect(screen.queryByRole('alert')).to.be.null
|
||||
})
|
||||
|
||||
|
@ -34,7 +43,7 @@ describe('<LeaversSurveyAlert/>', function () {
|
|||
const tomorrow = Date.now() + 1000 * 60 * 60 * 24
|
||||
localStorage.setItem('showInstitutionalLeaversSurveyUntil', tomorrow)
|
||||
localStorage.setItem('hideInstitutionalLeaversSurvey', false)
|
||||
render(<LeaversSurveyAlert />)
|
||||
renderWithProvider()
|
||||
screen.getByRole('alert')
|
||||
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
|
|
|
@ -65,7 +65,7 @@ describe('<LinkingSection />', function () {
|
|||
|
||||
screen.getByText('Integrations')
|
||||
screen.getByText(
|
||||
'You can link your Overleaf account with other services to enable the features described below'
|
||||
'You can link your Overleaf account with other services to enable the features described below.'
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -74,7 +74,7 @@ describe('<LinkingSection />', function () {
|
|||
screen.getByText('linked accounts')
|
||||
|
||||
screen.getByText('Google')
|
||||
screen.getByText('Log in with Google')
|
||||
screen.getByText('Log in with Google.')
|
||||
screen.getByRole('button', { name: 'Unlink' })
|
||||
|
||||
screen.getByText('Orcid')
|
||||
|
|
Loading…
Reference in a new issue