Просмотр исходного кода

Merge pull request #8029 from overleaf/ta-settings-fix-7

[SettingsPage] Small Fixes 7

GitOrigin-RevId: 2716fe13af3f5f6b56d6bba47505fad75ba1adbf
Timothée Alby 4 лет назад
Родитель
Сommit
3580ec6db3

+ 1 - 1
services/web/frontend/extracted-translations.json

@@ -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": "",

+ 1 - 1
services/web/frontend/js/features/settings/components/emails-section.tsx

@@ -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>
       <>

+ 4 - 1
services/web/frontend/js/features/settings/components/emails/add-email.tsx

@@ -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) ? (

+ 8 - 2
services/web/frontend/js/features/settings/components/emails/add-email/input.tsx

@@ -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(() => {

+ 7 - 7
services/web/frontend/js/features/settings/components/leavers-survey-alert.tsx

@@ -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
   }
 

+ 3 - 3
services/web/frontend/js/features/settings/components/linking-section.tsx

@@ -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')

+ 8 - 6
services/web/frontend/js/features/settings/context/user-email-context.tsx

@@ -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)),

+ 1 - 0
services/web/frontend/stories/settings/helpers/emails.js

@@ -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,
     })

+ 6 - 1
services/web/frontend/stories/settings/leavers-survey-alert.stories.tsx

@@ -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 {

+ 2 - 0
services/web/frontend/stories/settings/page.stories.js

@@ -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],
 }

+ 2 - 1
services/web/locales/en.json

@@ -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.",

+ 27 - 2
services/web/test/frontend/features/settings/components/emails/add-email-input.test.tsx

@@ -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 () {

+ 13 - 4
services/web/test/frontend/features/settings/components/leavers-survey-alert.test.tsx

@@ -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'))

+ 2 - 2
services/web/test/frontend/features/settings/components/linking-section.test.tsx

@@ -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')