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

Cleanup Bootstrap 3 code in the Account settings page (#24058)

* Remove the Bootstrap 5 version utilities

* Remove Account settings LESS stylesheet and unused styles

* Prefer using the OLFormText wrapper component instead of FormText

* Remove the Bootstrap 3 version stories

* Replace Font Awesome icons to Material icons

* Fix the heading hierarchy

* Cleanup unused translation

* Restore ellipsis to the text of two loading spinners

* Add loading button tests back and add some button loading labels

---------

Co-authored-by: Tim Down <158919+timdown@users.noreply.github.com>
GitOrigin-RevId: 283a9167c8c78bf0fe5062840ded6917dcd6263b
Rebeka Dekany 1 год назад
Родитель
Сommit
562ef81389
36 измененных файлов с 116 добавлено и 739 удалено
  1. 1 0
      services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx
  2. 3 5
      services/web/frontend/js/features/settings/components/account-info-section.tsx
  3. 3 7
      services/web/frontend/js/features/settings/components/emails-section.tsx
  4. 1 3
      services/web/frontend/js/features/settings/components/emails/actions/remove.tsx
  5. 1 5
      services/web/frontend/js/features/settings/components/emails/add-email.tsx
  6. 18 58
      services/web/frontend/js/features/settings/components/emails/add-email/country-input.tsx
  7. 0 5
      services/web/frontend/js/features/settings/components/emails/add-email/layout.tsx
  8. 2 20
      services/web/frontend/js/features/settings/components/emails/confirm-email-form.tsx
  9. 21 64
      services/web/frontend/js/features/settings/components/emails/downshift-input.tsx
  10. 1 3
      services/web/frontend/js/features/settings/components/emails/email.tsx
  11. 4 27
      services/web/frontend/js/features/settings/components/emails/header.tsx
  12. 0 5
      services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx
  13. 45 137
      services/web/frontend/js/features/settings/components/emails/reconfirmation-info.tsx
  14. 0 23
      services/web/frontend/js/features/settings/components/emails/sso-alert.tsx
  15. 1 7
      services/web/frontend/js/features/settings/components/leave/modal.tsx
  16. 0 3
      services/web/frontend/js/features/settings/components/password-section.tsx
  17. 0 4
      services/web/frontend/stories/settings/account-info.stories.jsx
  18. 0 2
      services/web/frontend/stories/settings/add-email-input.stories.tsx
  19. 0 4
      services/web/frontend/stories/settings/beta-program.stories.jsx
  20. 0 4
      services/web/frontend/stories/settings/emails.stories.jsx
  21. 0 2
      services/web/frontend/stories/settings/leave.stories.jsx
  22. 0 4
      services/web/frontend/stories/settings/leavers-survey-alert.stories.tsx
  23. 0 4
      services/web/frontend/stories/settings/linking.stories.jsx
  24. 0 4
      services/web/frontend/stories/settings/newsletter.stories.jsx
  25. 0 4
      services/web/frontend/stories/settings/page.stories.jsx
  26. 0 4
      services/web/frontend/stories/settings/password.stories.jsx
  27. 0 4
      services/web/frontend/stories/settings/sessions.stories.jsx
  28. 0 4
      services/web/frontend/stories/settings/sso-alert.stories.tsx
  29. 0 305
      services/web/frontend/stylesheets/app/account-settings.less
  30. 0 4
      services/web/frontend/stylesheets/bootstrap-5/pages/account-settings.scss
  31. 0 1
      services/web/frontend/stylesheets/main-style.less
  32. 1 1
      services/web/test/frontend/features/project-list/components/notifications.test.tsx
  33. 1 1
      services/web/test/frontend/features/settings/components/account-info-section.test.tsx
  34. 10 8
      services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx
  35. 2 2
      services/web/test/frontend/features/settings/components/emails/emails-section-institution-and-role.test.tsx
  36. 1 1
      services/web/test/frontend/features/settings/components/emails/reconfirmation-info.test.tsx

+ 1 - 0
services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx

@@ -87,6 +87,7 @@ function ReconfirmAffiliation({
             className="btn-inline-link"
             disabled={isLoading}
             isLoading={isLoading}
+            loadingLabel={t('sending') + '…'}
           >
             {t('resend_confirmation_email')}
           </OLButton>

+ 3 - 5
services/web/frontend/js/features/settings/components/account-info-section.tsx

@@ -12,7 +12,7 @@ import OLNotification from '@/features/ui/components/ol/ol-notification'
 import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
 import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
 import OLFormControl from '@/features/ui/components/ol/ol-form-control'
-import FormText from '@/features/ui/components/bootstrap-5/form/form-text'
+import OLFormText from '@/features/ui/components/ol/ol-form-text'
 
 function AccountInfoSection() {
   const { t } = useTranslation()
@@ -127,9 +127,7 @@ function AccountInfoSection() {
               form="account-info-form"
               disabled={!isFormValid}
               isLoading={isLoading}
-              bs3Props={{
-                loading: isLoading ? `${t('saving')}…` : t('update'),
-              }}
+              loadingLabel={t('saving') + '…'}
             >
               {t('update')}
             </OLButton>
@@ -196,7 +194,7 @@ function ReadOrWriteFormGroup({
         onInvalid={handleInvalid}
       />
       {validationMessage && (
-        <FormText type="error">{validationMessage}</FormText>
+        <OLFormText type="error">{validationMessage}</OLFormText>
       )}
     </OLFormGroup>
   )

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

@@ -8,8 +8,8 @@ import {
 import EmailsHeader from './emails/header'
 import EmailsRow from './emails/row'
 import AddEmail from './emails/add-email'
-import Icon from '../../../shared/components/icon'
 import OLNotification from '@/features/ui/components/ol/ol-notification'
+import OLSpinner from '@/features/ui/components/ol/ol-spinner'
 import { LeaversSurveyAlert } from './leavers-survey-alert'
 
 function EmailsSectionContent() {
@@ -28,7 +28,7 @@ function EmailsSectionContent() {
 
   return (
     <>
-      <h3>{t('emails_and_affiliations_title')}</h3>
+      <h2 className="h3">{t('emails_and_affiliations_title')}</h2>
       <p className="small">{t('emails_and_affiliations_explanation')}</p>
       <p className="small">
         <Trans
@@ -49,7 +49,7 @@ function EmailsSectionContent() {
         {isInitializing ? (
           <div className="affiliations-table-row-highlighted">
             <div className="affiliations-table-cell text-center">
-              <Icon type="refresh" fw spin /> {t('loading')}...
+              <OLSpinner size="sm" /> {t('loading')}...
             </div>
           </div>
         ) : (
@@ -68,10 +68,6 @@ function EmailsSectionContent() {
           <OLNotification
             type="error"
             content={t('error_performing_request')}
-            bs3Props={{
-              icon: <Icon type="exclamation-triangle" fw />,
-              className: 'text-center',
-            }}
           />
         )}
       </>

+ 1 - 3
services/web/frontend/js/features/settings/components/emails/actions/remove.tsx

@@ -7,7 +7,6 @@ import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
 import OLIconButton, {
   OLIconButtonProps,
 } from '@/features/ui/components/ol/ol-icon-button'
-import { bsVersion } from '@/features/utils/bootstrap-5'
 import getMeta from '@/utils/meta'
 
 type DeleteButtonProps = Pick<
@@ -26,8 +25,7 @@ function DeleteButton({ disabled, isLoading, onClick }: DeleteButtonProps) {
       size="sm"
       onClick={onClick}
       accessibilityLabel={t('remove') || ''}
-      icon={bsVersion({ bs5: 'delete', bs3: 'trash' })}
-      bs3Props={{ fw: true }}
+      icon="delete"
     />
   )
 }

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

@@ -18,7 +18,6 @@ import getMeta from '../../../../utils/meta'
 import { ReCaptcha2 } from '../../../../shared/components/recaptcha-2'
 import { useRecaptcha } from '../../../../shared/hooks/use-recaptcha'
 import OLCol from '@/features/ui/components/ol/ol-col'
-import { bsVersion } from '@/features/utils/bootstrap-5'
 import { ConfirmEmailForm } from '@/features/settings/components/emails/confirm-email-form'
 
 function AddEmail() {
@@ -153,10 +152,7 @@ function AddEmail() {
 
   const InputComponent = (
     <>
-      <label
-        htmlFor="affiliations-email"
-        className={bsVersion({ bs5: 'visually-hidden', bs3: 'sr-only' })}
-      >
+      <label htmlFor="affiliations-email" className="visually-hidden">
         {t('email')}
       </label>
       <Input

+ 18 - 58
services/web/frontend/js/features/settings/components/emails/add-email/country-input.tsx

@@ -3,10 +3,8 @@ import { useTranslation } from 'react-i18next'
 import { useCombobox } from 'downshift'
 import classnames from 'classnames'
 import countries, { CountryCode } from '../../../data/countries-list'
-import { bsVersion } from '@/features/utils/bootstrap-5'
 import OLFormControl from '@/features/ui/components/ol/ol-form-control'
 import { DropdownItem } from '@/features/ui/components/bootstrap-5/dropdown-menu'
-import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
 
 type CountryInputProps = {
   setValue: React.Dispatch<React.SetStateAction<CountryCode | null>>
@@ -51,26 +49,10 @@ function Downshift({ setValue, inputRef }: CountryInputProps) {
   const shouldOpen = isOpen && inputItems.length
 
   return (
-    <div
-      className={classnames(
-        'dropdown',
-        bsVersion({
-          bs5: 'd-block',
-          bs3: classnames('ui-select-container ui-select-bootstrap', {
-            open: shouldOpen,
-          }),
-        })
-      )}
-    >
-      <div
-        {...getComboboxProps()}
-        className={bsVersion({ bs3: 'ui-select-toggle' })}
-      >
+    <div className={classnames('dropdown', 'd-block')}>
+      <div {...getComboboxProps()}>
         {/* eslint-disable-next-line jsx-a11y/label-has-for */}
-        <label
-          {...getLabelProps()}
-          className={bsVersion({ bs5: 'visually-hidden', bs3: 'sr-only' })}
-        >
+        <label {...getLabelProps()} className="visually-hidden">
           {t('country')}
         </label>
         <OLFormControl
@@ -91,52 +73,30 @@ function Downshift({ setValue, inputRef }: CountryInputProps) {
       </div>
       <ul
         {...getMenuProps()}
-        className={classnames(
-          'dropdown-menu',
-          bsVersion({
-            bs5: classnames('select-dropdown-menu', { show: shouldOpen }),
-            bs3: 'ui-select-choices ui-select-choices-content ui-select-dropdown',
-          })
-        )}
+        className={classnames('dropdown-menu', 'select-dropdown-menu', {
+          show: shouldOpen,
+        })}
       >
         {inputItems.map((item, index) => (
           // eslint-disable-next-line jsx-a11y/role-supports-aria-props
           <li
-            className={bsVersion({ bs3: 'ui-select-choices-group' })}
             key={`${item.name}-${index}`}
             {...getItemProps({ item, index })}
             aria-selected={selectedItem?.name === item.name}
           >
-            <BootstrapVersionSwitcher
-              bs3={
-                <div
-                  className={classnames('ui-select-choices-row', {
-                    active: selectedItem?.name === item.name,
-                    'ui-select-choices-row--highlighted':
-                      highlightedIndex === index,
-                  })}
-                >
-                  <span className="ui-select-choices-row-inner">
-                    <span>{item.name}</span>
-                  </span>
-                </div>
-              }
-              bs5={
-                <DropdownItem
-                  as="span"
-                  role={undefined}
-                  className={classnames({
-                    active: selectedItem?.name === item.name,
-                    'dropdown-item-highlighted': highlightedIndex === index,
-                  })}
-                  trailingIcon={
-                    selectedItem?.name === item.name ? 'check' : undefined
-                  }
-                >
-                  {item.name}
-                </DropdownItem>
+            <DropdownItem
+              as="span"
+              role={undefined}
+              className={classnames({
+                active: selectedItem?.name === item.name,
+                'dropdown-item-highlighted': highlightedIndex === index,
+              })}
+              trailingIcon={
+                selectedItem?.name === item.name ? 'check' : undefined
               }
-            />
+            >
+              {item.name}
+            </DropdownItem>
           </li>
         ))}
       </ul>

+ 0 - 5
services/web/frontend/js/features/settings/components/emails/add-email/layout.tsx

@@ -1,4 +1,3 @@
-import Icon from '../../../../../shared/components/icon'
 import { UseAsyncReturnType } from '../../../../../shared/hooks/use-async'
 import { getUserFacingMessage } from '../../../../../infrastructure/fetch-json'
 import OLRow from '@/features/ui/components/ol/ol-row'
@@ -18,10 +17,6 @@ function Layout({ isError, error, children }: LayoutProps) {
         <OLNotification
           type="error"
           content={getUserFacingMessage(error) ?? ''}
-          bs3Props={{
-            icon: <Icon type="exclamation-triangle" fw />,
-            className: 'text-center',
-          }}
         />
       )}
     </div>

+ 2 - 20
services/web/frontend/js/features/settings/components/emails/confirm-email-form.tsx

@@ -217,16 +217,7 @@ export function ConfirmEmailForm({
             disabled={isResending}
             type="submit"
             isLoading={isConfirming}
-            bs3Props={{
-              loading: isConfirming ? (
-                <>
-                  {t('confirming')}
-                  <span>&hellip;</span>
-                </>
-              ) : (
-                t('confirm')
-              ),
-            }}
+            loadingLabel={t('confirming')}
           >
             {t('confirm')}
           </OLButton>
@@ -235,16 +226,7 @@ export function ConfirmEmailForm({
             disabled={isConfirming}
             onClick={resendHandler}
             isLoading={isResending}
-            bs3Props={{
-              loading: isResending ? (
-                <>
-                  {t('resending_confirmation_code')}
-                  <span>&hellip;</span>
-                </>
-              ) : (
-                t('resend_confirmation_code')
-              ),
-            }}
+            loadingLabel={t('resending_confirmation_code')}
           >
             {t('resend_confirmation_code')}
           </OLButton>

+ 21 - 64
services/web/frontend/js/features/settings/components/emails/downshift-input.tsx

@@ -2,10 +2,8 @@ import { useState, useEffect, forwardRef } from 'react'
 import { useCombobox } from 'downshift'
 import classnames from 'classnames'
 import { escapeRegExp } from 'lodash'
-import { bsVersion } from '@/features/utils/bootstrap-5'
 import OLFormControl from '@/features/ui/components/ol/ol-form-control'
 import { DropdownItem } from '@/features/ui/components/bootstrap-5/dropdown-menu'
-import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
 import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
 
 type DownshiftInputProps = {
@@ -83,26 +81,12 @@ function Downshift({
   const shouldOpen = isOpen && inputItems.length
 
   return (
-    <div
-      className={classnames(
-        'dropdown',
-        bsVersion({
-          bs5: 'd-block',
-          bs3: classnames('ui-select-container ui-select-bootstrap', {
-            open: shouldOpen,
-          }),
-        })
-      )}
-    >
+    <div className={classnames('dropdown', 'd-block')}>
       <div {...getComboboxProps()}>
         {/* eslint-disable-next-line jsx-a11y/label-has-for */}
         <OLFormLabel
           {...getLabelProps()}
-          className={
-            showLabel
-              ? ''
-              : bsVersion({ bs5: 'visually-hidden', bs3: 'sr-only' })
-          }
+          className={showLabel ? '' : 'visually-hidden'}
         >
           {label}
         </OLFormLabel>
@@ -124,62 +108,35 @@ function Downshift({
       </div>
       <ul
         {...getMenuProps()}
-        className={classnames(
-          'dropdown-menu',
-          bsVersion({
-            bs5: classnames('select-dropdown-menu', { show: shouldOpen }),
-            bs3: 'ui-select-choices ui-select-choices-content ui-select-dropdown',
-          })
-        )}
+        className={classnames('dropdown-menu', 'select-dropdown-menu', {
+          show: shouldOpen,
+        })}
       >
         {showSuggestedText && inputItems.length && (
-          <BootstrapVersionSwitcher
-            bs3={<li className="ui-select-title">{itemsTitle}</li>}
-            bs5={
-              <li>
-                <DropdownItem as="span" role={undefined} disabled>
-                  {itemsTitle}
-                </DropdownItem>
-              </li>
-            }
-          />
+          <li>
+            <DropdownItem as="span" role={undefined} disabled>
+              {itemsTitle}
+            </DropdownItem>
+          </li>
         )}
         {inputItems.map((item, index) => (
           // eslint-disable-next-line jsx-a11y/role-supports-aria-props
           <li
-            className={bsVersion({ bs3: 'ui-select-choices-group' })}
             key={`${item}${index}`}
             {...getItemProps({ item, index })}
             aria-selected={selectedItem === item}
           >
-            <BootstrapVersionSwitcher
-              bs3={
-                <div
-                  className={classnames('ui-select-choices-row', {
-                    active: selectedItem === item,
-                    'ui-select-choices-row--highlighted':
-                      highlightedIndex === index,
-                  })}
-                >
-                  <span className="ui-select-choices-row-inner">
-                    <span>{highlightMatchedCharacters(item, inputValue)}</span>
-                  </span>
-                </div>
-              }
-              bs5={
-                <DropdownItem
-                  as="span"
-                  role={undefined}
-                  className={classnames({
-                    active: selectedItem === item,
-                    'dropdown-item-highlighted': highlightedIndex === index,
-                  })}
-                  trailingIcon={selectedItem === item ? 'check' : undefined}
-                >
-                  {highlightMatchedCharacters(item, inputValue)}
-                </DropdownItem>
-              }
-            />
+            <DropdownItem
+              as="span"
+              role={undefined}
+              className={classnames({
+                active: selectedItem === item,
+                'dropdown-item-highlighted': highlightedIndex === index,
+              })}
+              trailingIcon={selectedItem === item ? 'check' : undefined}
+            >
+              {highlightMatchedCharacters(item, inputValue)}
+            </DropdownItem>
           </li>
         ))}
       </ul>

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

@@ -2,8 +2,6 @@ import { useTranslation } from 'react-i18next'
 import { UserEmailData } from '../../../../../../types/user-email'
 import { ssoAvailableForInstitution } from '../../utils/sso'
 import OLBadge from '@/features/ui/components/ol/ol-badge'
-import { isBootstrap5 } from '@/features/utils/bootstrap-5'
-import classnames from 'classnames'
 import ResendConfirmationCodeModal from '@/features/settings/components/emails/resend-confirmation-code-modal'
 
 type EmailProps = {
@@ -37,7 +35,7 @@ function Email({ userEmailData }: EmailProps) {
         </div>
       )}
       {hasBadges && (
-        <div className={classnames({ small: !isBootstrap5() })}>
+        <div>
           {isPrimary && (
             <>
               <OLBadge bg="info">Primary</OLBadge>{' '}

+ 4 - 27
services/web/frontend/js/features/settings/components/emails/header.tsx

@@ -3,7 +3,6 @@ import EmailCell from './cell'
 import OLCol from '@/features/ui/components/ol/ol-col'
 import OLRow from '@/features/ui/components/ol/ol-row'
 import classnames from 'classnames'
-import { bsVersion } from '@/features/utils/bootstrap-5'
 
 function Header() {
   const { t } = useTranslation()
@@ -11,41 +10,19 @@ function Header() {
   return (
     <>
       <OLRow>
-        <OLCol
-          lg={4}
-          className={bsVersion({
-            bs5: 'd-none d-sm-block',
-            bs3: 'hidden-xs',
-          })}
-        >
+        <OLCol lg={4} className="d-none d-sm-block">
           <EmailCell>
             <strong>{t('email')}</strong>
           </EmailCell>
         </OLCol>
-        <OLCol
-          lg={8}
-          className={bsVersion({
-            bs5: 'd-none d-sm-block',
-            bs3: 'hidden-xs',
-          })}
-        >
+        <OLCol lg={8} className="d-none d-sm-block">
           <EmailCell>
             <strong>{t('institution_and_role')}</strong>
           </EmailCell>
         </OLCol>
       </OLRow>
-      <div
-        className={classnames(
-          bsVersion({ bs5: 'd-none d-sm-block', bs3: 'hidden-xs' }),
-          'horizontal-divider'
-        )}
-      />
-      <div
-        className={classnames(
-          bsVersion({ bs5: 'd-none d-sm-block', bs3: 'hidden-xs' }),
-          'horizontal-divider'
-        )}
-      />
+      <div className={classnames('d-none d-sm-block', 'horizontal-divider')} />
+      <div className={classnames('d-none d-sm-block', 'horizontal-divider')} />
     </>
   )
 }

+ 0 - 5
services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx

@@ -146,11 +146,6 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
               disabled={!role || !department}
               isLoading={isLoading}
               loadingLabel={t('saving')}
-              bs3Props={{
-                loading: isLoading
-                  ? `${t('saving')}…`
-                  : t('save_or_cancel-save'),
-              }}
             >
               {t('save_or_cancel-save')}
             </OLButton>

+ 45 - 137
services/web/frontend/js/features/settings/components/emails/reconfirmation-info.tsx

@@ -6,8 +6,6 @@ import ReconfirmationInfoPromptText from './reconfirmation-info/reconfirmation-i
 import OLRow from '@/features/ui/components/ol/ol-row'
 import OLCol from '@/features/ui/components/ol/ol-col'
 import OLNotification from '@/features/ui/components/ol/ol-notification'
-import { isBootstrap5 } from '@/features/utils/bootstrap-5'
-import Icon from '@/shared/components/icon'
 import { useUserEmailsContext } from '@/features/settings/context/user-email-context'
 import { FetchError, postJSON } from '@/infrastructure/fetch-json'
 import { debugConsole } from '@/utils/debugging'
@@ -16,14 +14,13 @@ import { Trans, useTranslation } from 'react-i18next'
 import useAsync from '@/shared/hooks/use-async'
 import { useLocation } from '@/shared/hooks/use-location'
 import OLButton from '@/features/ui/components/ol/ol-button'
-import classnames from 'classnames'
+import LoadingSpinner from '@/shared/components/loading-spinner'
 
 type ReconfirmationInfoProps = {
   userEmailData: UserEmailData
 }
 
 function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
-  const reconfirmationRemoveEmail = getMeta('ol-reconfirmationRemoveEmail')
   const reconfirmedViaSAML = getMeta('ol-reconfirmedViaSAML')
 
   const { t } = useTranslation()
@@ -86,7 +83,6 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
                 institution={userEmailData.affiliation.institution}
               />
             }
-            bs3Props={{ className: 'settings-reconfirm-info small' }}
           />
         </OLCol>
       </OLRow>
@@ -97,88 +93,11 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
     return (
       <OLRow>
         <OLCol lg={12}>
-          {isBootstrap5() ? (
-            <OLNotification
-              type="info"
-              content={
-                <>
-                  {hasSent ? (
-                    <Trans
-                      i18nKey="please_check_your_inbox_to_confirm"
-                      values={{
-                        institutionName:
-                          userEmailData.affiliation.institution.name,
-                      }}
-                      shouldUnescape
-                      tOptions={{ interpolation: { escapeValue: true } }}
-                      components={
-                        /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
-                        [<strong />]
-                      }
-                    />
-                  ) : (
-                    <ReconfirmationInfoPromptText
-                      institutionName={
-                        userEmailData.affiliation.institution.name
-                      }
-                      primary={userEmailData.default}
-                    />
-                  )}
-                  <br />
-                  {isError && (
-                    <div className="text-danger">
-                      {rateLimited
-                        ? t('too_many_requests')
-                        : t('generic_something_went_wrong')}
-                    </div>
-                  )}
-                </>
-              }
-              action={
-                hasSent ? (
-                  <>
-                    {isLoading ? (
-                      <>
-                        <Icon type="refresh" spin fw /> {t('sending')}...
-                      </>
-                    ) : (
-                      <OLButton
-                        variant="link"
-                        disabled={state.isLoading}
-                        onClick={handleRequestReconfirmation}
-                        className="btn-inline-link"
-                      >
-                        {t('resend_confirmation_email')}
-                      </OLButton>
-                    )}
-                  </>
-                ) : (
-                  <OLButton
-                    variant="secondary"
-                    disabled={isPending}
-                    isLoading={isLoading}
-                    onClick={handleRequestReconfirmation}
-                  >
-                    {isLoading ? (
-                      <>
-                        <Icon type="refresh" spin fw /> {t('sending')}...
-                      </>
-                    ) : (
-                      t('confirm_affiliation')
-                    )}
-                  </OLButton>
-                )
-              }
-            />
-          ) : (
-            <div
-              className={classnames('settings-reconfirm-info', 'small', {
-                'alert alert-info':
-                  reconfirmationRemoveEmail === userEmailData.email,
-              })}
-            >
-              {hasSent ? (
-                <div>
+          <OLNotification
+            type="info"
+            content={
+              <>
+                {hasSent ? (
                   <Trans
                     i18nKey="please_check_your_inbox_to_confirm"
                     values={{
@@ -191,10 +110,29 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
                       /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
                       [<strong />]
                     }
-                  />{' '}
+                  />
+                ) : (
+                  <ReconfirmationInfoPromptText
+                    institutionName={userEmailData.affiliation.institution.name}
+                    primary={userEmailData.default}
+                  />
+                )}
+                <br />
+                {isError && (
+                  <div className="text-danger">
+                    {rateLimited
+                      ? t('too_many_requests')
+                      : t('generic_something_went_wrong')}
+                  </div>
+                )}
+              </>
+            }
+            action={
+              hasSent ? (
+                <>
                   {isLoading ? (
                     <>
-                      <Icon type="refresh" spin fw /> {t('sending')}...
+                      <LoadingSpinner loadingText={`${t('sending')}…`} />
                     </>
                   ) : (
                     <OLButton
@@ -206,55 +144,25 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
                       {t('resend_confirmation_email')}
                     </OLButton>
                   )}
-                  <br />
-                  {isError && (
-                    <div className="text-danger">
-                      {rateLimited
-                        ? t('too_many_requests')
-                        : t('generic_something_went_wrong')}
-                    </div>
-                  )}
-                </div>
-              ) : (
-                <>
-                  <div>
-                    <ReconfirmationInfoPromptText
-                      institutionName={
-                        userEmailData.affiliation.institution.name
-                      }
-                      primary={userEmailData.default}
-                      icon={
-                        <Icon type="warning" className="me-1 icon-warning" />
-                      }
-                    />
-                  </div>
-                  <div className="setting-reconfirm-info-right">
-                    <OLButton
-                      variant="secondary"
-                      disabled={state.isLoading || isPending}
-                      onClick={handleRequestReconfirmation}
-                    >
-                      {isLoading ? (
-                        <>
-                          <Icon type="refresh" spin fw /> {t('sending')}...
-                        </>
-                      ) : (
-                        t('confirm_affiliation')
-                      )}
-                    </OLButton>
-                    <br />
-                    {isError && (
-                      <div className="text-danger">
-                        {rateLimited
-                          ? t('too_many_requests')
-                          : t('generic_something_went_wrong')}
-                      </div>
-                    )}
-                  </div>
                 </>
-              )}
-            </div>
-          )}
+              ) : (
+                <OLButton
+                  variant="secondary"
+                  disabled={isPending}
+                  isLoading={isLoading}
+                  onClick={handleRequestReconfirmation}
+                >
+                  {isLoading ? (
+                    <>
+                      <LoadingSpinner loadingText={`${t('sending')}…`} />
+                    </>
+                  ) : (
+                    t('confirm_affiliation')
+                  )}
+                </OLButton>
+              )
+            }
+          />
         </OLCol>
       </OLRow>
     )

+ 0 - 23
services/web/frontend/js/features/settings/components/emails/sso-alert.tsx

@@ -1,6 +1,5 @@
 import { useState } from 'react'
 import { useTranslation, Trans } from 'react-i18next'
-import Icon from '../../../../shared/components/icon'
 import getMeta from '../../../../utils/meta'
 import OLNotification from '@/features/ui/components/ol/ol-notification'
 
@@ -35,15 +34,6 @@ export function SSOAlert() {
         }
         isDismissible
         onDismiss={handleErrorClosed}
-        bs3Props={{
-          icon: (
-            <Icon
-              type="exclamation-triangle"
-              accessibilityLabel={t('generic_something_went_wrong')}
-            />
-          ),
-          className: 'mb-0 text-center',
-        }}
       />
     ) : null
   }
@@ -83,9 +73,6 @@ export function SSOAlert() {
           }
           isDismissible
           onDismiss={handleInfoClosed}
-          bs3Props={{
-            className: 'mb-0 text-center',
-          }}
         />
       )}
       {!warningClosed && institutionEmailNonCanonical && (
@@ -102,16 +89,6 @@ export function SSOAlert() {
           }
           isDismissible
           onDismiss={handleWarningClosed}
-          bs3Props={{
-            icon: (
-              <Icon
-                type="exclamation-triangle"
-                accessibilityLabel={t('generic_something_went_wrong')}
-                fw
-              />
-            ),
-            className: 'text-center',
-          }}
         />
       )}
     </>

+ 1 - 7
services/web/frontend/js/features/settings/components/leave/modal.tsx

@@ -17,13 +17,7 @@ function LeaveModal({ isOpen, handleClose }: LeaveModalProps) {
   }, [handleClose, inFlight])
 
   return (
-    <OLModal
-      animation
-      show={isOpen}
-      onHide={handleHide}
-      id="leave-modal"
-      bs3Props={{ backdrop: 'static' }}
-    >
+    <OLModal animation show={isOpen} onHide={handleHide} id="leave-modal">
       <LeaveModalContent
         handleHide={handleHide}
         inFlight={inFlight}

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

@@ -204,9 +204,6 @@ function PasswordForm() {
           disabled={!isFormValid}
           isLoading={isLoading}
           loadingLabel={`${t('saving')}…`}
-          bs3Props={{
-            loading: isLoading ? `${t('saving')}…` : t('change'),
-          }}
         >
           {t('change')}
         </OLButton>

+ 0 - 4
services/web/frontend/stories/settings/account-info.stories.jsx

@@ -3,7 +3,6 @@ import AccountInfoSection from '../../js/features/settings/components/account-in
 import { setDefaultMeta, defaultSetupMocks } from './helpers/account-info'
 import { UserProvider } from '../../js/shared/context/user-context'
 import getMeta from '@/utils/meta'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const Success = args => {
   setDefaultMeta()
@@ -56,7 +55,4 @@ export const Error = args => {
 export default {
   title: 'Account Settings / Account Info',
   component: AccountInfoSection,
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 2
services/web/frontend/stories/settings/add-email-input.stories.tsx

@@ -1,6 +1,5 @@
 import useFetchMock from './../hooks/use-fetch-mock'
 import Input from '../../js/features/settings/components/emails/add-email/input'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const EmailInput = (args: any) => {
   useFetchMock(fetchMock =>
@@ -27,6 +26,5 @@ export default {
   component: Input,
   argTypes: {
     onChange: { action: 'change' },
-    ...bsVersionDecorator.argTypes,
   },
 }

+ 0 - 4
services/web/frontend/stories/settings/beta-program.stories.jsx

@@ -1,6 +1,5 @@
 import BetaProgramSection from '../../js/features/settings/components/beta-program-section'
 import { UserProvider } from '../../js/shared/context/user-context'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const SectionNotEnrolled = args => {
   window.metaAttributesCache.set('ol-user', { betaProgram: false })
@@ -25,7 +24,4 @@ export const SectionEnrolled = args => {
 export default {
   title: 'Account Settings / Beta Program',
   component: BetaProgramSection,
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 4
services/web/frontend/stories/settings/emails.stories.jsx

@@ -8,7 +8,6 @@ import {
   errorsMocks,
   emailLimitSetupMocks,
 } from './helpers/emails'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const EmailsList = args => {
   useFetchMock(defaultSetupMocks)
@@ -41,7 +40,4 @@ export const NetworkErrors = args => {
 export default {
   title: 'Account Settings / Emails and Affiliations',
   component: EmailsSection,
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 2
services/web/frontend/stories/settings/leave.stories.jsx

@@ -2,7 +2,6 @@ import useFetchMock from '../hooks/use-fetch-mock'
 import LeaveModal from '../../js/features/settings/components/leave/modal'
 import LeaveSection from '../../js/features/settings/components/leave-section'
 import { setDefaultMeta, defaultSetupMocks } from './helpers/leave'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const Section = args => {
   useFetchMock(defaultSetupMocks)
@@ -68,6 +67,5 @@ export default {
   },
   argTypes: {
     handleClose: { action: 'handleClose' },
-    ...bsVersionDecorator.argTypes,
   },
 }

+ 0 - 4
services/web/frontend/stories/settings/leavers-survey-alert.stories.tsx

@@ -2,7 +2,6 @@ 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 '@/infrastructure/local-storage'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const SurveyAlert = () => {
   localStorage.setItem(
@@ -19,7 +18,4 @@ export const SurveyAlert = () => {
 export default {
   title: 'Account Settings / Survey Alerts',
   component: EmailsSection,
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 4
services/web/frontend/stories/settings/linking.stories.jsx

@@ -6,7 +6,6 @@ import { SSOProvider } from '../../js/features/settings/context/sso-context'
 import { ScopeDecorator } from '../decorators/scope'
 import { useEffect } from 'react'
 import { useMeta } from '../hooks/use-meta'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 const MOCK_DELAY = 1000
 
@@ -94,7 +93,4 @@ export default {
   title: 'Account Settings / Linking',
   component: LinkingSection,
   decorators: [ScopeDecorator],
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 4
services/web/frontend/stories/settings/newsletter.stories.jsx

@@ -1,5 +1,4 @@
 import NewsletterSection from '../../js/features/settings/components/newsletter-section'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const Section = args => {
   return <NewsletterSection {...args} />
@@ -8,7 +7,4 @@ export const Section = args => {
 export default {
   title: 'Account Settings / Newsletter',
   component: NewsletterSection,
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 4
services/web/frontend/stories/settings/page.stories.jsx

@@ -24,7 +24,6 @@ import {
 import { UserProvider } from '../../js/shared/context/user-context'
 import { ScopeDecorator } from '../decorators/scope'
 import getMeta from '@/utils/meta'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const Overleaf = args => {
   setDefaultLeaveMeta()
@@ -79,7 +78,4 @@ export default {
   title: 'Account Settings / Full Page',
   component: SettingsPageRoot,
   decorators: [ScopeDecorator],
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 4
services/web/frontend/stories/settings/password.stories.jsx

@@ -2,7 +2,6 @@ import useFetchMock from '../hooks/use-fetch-mock'
 import PasswordSection from '../../js/features/settings/components/password-section'
 import { setDefaultMeta, defaultSetupMocks } from './helpers/password'
 import getMeta from '@/utils/meta'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const Success = args => {
   setDefaultMeta()
@@ -47,7 +46,4 @@ export const Error = args => {
 export default {
   title: 'Account Settings / Password',
   component: PasswordSection,
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 4
services/web/frontend/stories/settings/sessions.stories.jsx

@@ -1,5 +1,4 @@
 import SessionsSection from '../../js/features/settings/components/sessions-section'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const Section = args => {
   return <SessionsSection {...args} />
@@ -8,7 +7,4 @@ export const Section = args => {
 export default {
   title: 'Account Settings / Sessions',
   component: SessionsSection,
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 4
services/web/frontend/stories/settings/sso-alert.stories.tsx

@@ -1,6 +1,5 @@
 import EmailsSection from '../../js/features/settings/components/emails-section'
 import { SSOAlert } from '../../js/features/settings/components/emails/sso-alert'
-import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
 
 export const Info = () => {
   window.metaAttributesCache.set('ol-institutionLinked', {
@@ -54,7 +53,4 @@ export const ErrorWithTryAgain = () => {
 export default {
   title: 'Account Settings / SSO Alerts',
   component: EmailsSection,
-  argTypes: {
-    ...bsVersionDecorator.argTypes,
-  },
 }

+ 0 - 305
services/web/frontend/stylesheets/app/account-settings.less

@@ -1,305 +0,0 @@
-.account-settings {
-  .alert {
-    margin-bottom: 0;
-  }
-  h3 {
-    margin-top: 0;
-  }
-}
-
-#delete-account-modal {
-  .alert {
-    margin-top: 25px;
-    margin-bottom: 4px;
-  }
-  .confirmation-checkbox-wrapper {
-    padding-top: 8px;
-    input {
-      margin-right: 6px;
-    }
-  }
-}
-
-.affiliations-table {
-  table-layout: fixed;
-}
-.affiliations-table-cell {
-  padding: 0.5rem;
-  overflow-wrap: break-word;
-}
-.affiliations-table-cell-tabbed {
-  margin: @margin-sm 0 0 @margin-md;
-  padding-left: @margin-sm;
-  border-left: 2px solid @table-border-color; // don't migrate this line of style
-}
-.affiliations-table-row-highlighted {
-  background-color: tint(@content-alt-bg-color, 6%);
-}
-.affiliations-table-email {
-  width: 40%;
-}
-.affiliations-table-institution {
-  width: 40%;
-}
-.affiliations-table-inline-actions {
-  padding: 0 !important;
-  text-align: right;
-  word-wrap: break-word;
-  button {
-    margin: @table-cell-padding 0;
-  }
-}
-.affiliations-table-inline-action {
-  text-transform: capitalize;
-}
-.affiliations-table-inline-action-disabled-wrapper {
-  display: inline-block;
-}
-.affiliations-table-highlighted-row {
-  background-color: tint(@content-alt-bg-color, 6%);
-}
-.affiliations-table-error-row {
-  background-color: @alert-danger-bg;
-  color: @alert-danger-text;
-  .btn {
-    margin-top: @table-cell-padding;
-    .button-variant(
-      @btn-danger-color; darken(@btn-danger-bg, 8%) ; @btn-danger-border
-    );
-  }
-  .small {
-    color: @alert-danger-text;
-  }
-}
-.affiliations-table-info-row {
-  background-color: @alert-info-bg;
-  color: @alert-info-text;
-  .small {
-    color: @alert-info-text;
-  }
-}
-.affiliations-table-warning-row {
-  background-color: @alert-warning-bg;
-  color: @alert-warning-text;
-  .small {
-    color: @alert-warning-text;
-  }
-}
-tbody > tr.affiliations-table-saml-row > td:not(.with-border) {
-  border: 0;
-}
-tbody > tr.affiliations-table-info-row > td {
-  border: 0;
-}
-tbody > tr.affiliations-table-warning-row > td {
-  border: 0;
-}
-.affiliations-form-group {
-  margin-top: @table-cell-padding;
-  &:first-child {
-    margin-top: 0;
-  }
-}
-.affiliation-change-container,
-.affiliation-change-actions {
-  margin-top: @table-cell-padding;
-}
-
-.affiliations-table-label {
-  padding-top: 4px;
-}
-
-.btn-link-accounts {
-  margin-bottom: (@line-height-computed / 2) - @table-cell-padding;
-}
-
-.settings-widget-status-icon,
-.dropbox-sync-icon {
-  position: relative;
-  font-size: 1.3em;
-  line-height: 1.3em;
-  vertical-align: top;
-  &.status-error,
-  &.dropbox-sync-icon-error {
-    color: @alert-danger-bg;
-  }
-  &.status-success,
-  &.dropbox-sync-icon-success {
-    color: @alert-success-bg;
-  }
-  &.status-pending,
-  &.dropbox-sync-icon-updating {
-    color: @alert-info-bg;
-    &::after {
-      content: '\f021';
-      position: absolute;
-      top: 0;
-      left: 50%;
-      margin-left: -20%;
-      font-size: 60%;
-      color: #fff;
-      animation: fa-spin 2s infinite linear;
-    }
-  }
-}
-
-.settings-widgets-container {
-  border: 1px solid @gray-lighter;
-
-  hr {
-    margin: 0 10px;
-  }
-}
-
-.settings-widget-container {
-  display: grid;
-  grid-template-columns: 40px 1fr auto;
-  gap: 20px;
-  align-items: center;
-  padding: 10px;
-
-  > div {
-    display: flex;
-    flex-direction: column;
-    padding-right: 20px;
-
-    &:last-child {
-      padding-right: 0px;
-    }
-  }
-
-  img {
-    width: 40px;
-    height: 40px;
-  }
-
-  .dual-logo {
-    display: flex;
-    justify-content: space-evenly;
-    height: 100%;
-  }
-
-  .description-container {
-    flex-grow: 1;
-  }
-
-  .title-row {
-    display: flex;
-    align-items: center;
-    margin-bottom: 10px;
-
-    > h4 {
-      margin: 0;
-      margin-right: 10px;
-    }
-  }
-
-  p {
-    margin-bottom: 10px;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-  }
-}
-
-@media (max-width: @screen-xs-max) {
-  .settings-widget-container {
-    grid-template-columns: 1fr;
-
-    .title-row {
-      display: unset;
-    }
-  }
-}
-
-// Should not be migrated to BS5
-.settings-reconfirm-info {
-  display: flex;
-  justify-content: space-between;
-  margin: 0px auto @margin-sm auto !important;
-  padding: @padding-md;
-  &:not(.alert-info) {
-    background-color: @ol-blue-gray-0;
-    .fa-warning {
-      color: @brand-warning;
-    }
-  }
-
-  > *:not(:last-child) {
-    margin-right: @margin-md;
-  }
-}
-
-// Should not be migrated to BS5
-.setting-reconfirm-info-right {
-  white-space: nowrap;
-}
-
-// Prevents icon from large account linking sections, such as the git bridge,
-// from rendering in the center of the widget, anchoring it to the top
-.linking-icon-fixed-position {
-  align-self: start;
-  padding-top: 10px;
-}
-
-// overrides the default `Col` padding, as the inner `affiliations-table-cell` has its own padding, and
-// the content length of the git-bridge token table is pretty much fixed (tokens and dates)
-.linking-git-bridge-table-cell {
-  padding-right: 0;
-}
-
-.linking-git-bridge-revoke-button {
-  padding: 2px 4px;
-}
-
-.security-row {
-  .line-header > b {
-    color: @ol-blue-gray-6;
-  }
-
-  line-height: 24px;
-  color: @ol-blue-gray-4;
-  display: flex;
-  flex-direction: row;
-  padding: 6px 0;
-
-  .icon {
-    color: @ol-blue-gray-6;
-    display: flex;
-    flex: 1 1 7%;
-    padding: 0 16px;
-    margin-top: 16px;
-  }
-
-  .text {
-    flex: 1 1 93%;
-    display: flex;
-    flex-direction: column;
-    margin-right: 16px;
-  }
-
-  .button-column {
-    display: flex;
-    align-items: center;
-  }
-
-  .status-label {
-    font-size: @font-size-small;
-    border-radius: 4px;
-    padding: 2px 4px;
-    margin-top: 4px;
-    margin-left: 8px;
-    flex-shrink: 0;
-
-    &.status-label-configured {
-      background-color: @ol-green;
-      color: @neutral-10;
-    }
-
-    &.status-label-ready {
-      background-color: @neutral-20;
-      color: @neutral-90;
-    }
-  }
-}

+ 0 - 4
services/web/frontend/stylesheets/bootstrap-5/pages/account-settings.scss

@@ -16,10 +16,6 @@
   background-color: var(--bg-light-secondary);
 }
 
-.affiliations-table-inline-action {
-  text-transform: capitalize;
-}
-
 .affiliation-change-container {
   margin-top: var(--spacing-04);
 }

+ 0 - 1
services/web/frontend/stylesheets/main-style.less

@@ -98,7 +98,6 @@
 
 // Overleaf app classes
 @import 'app/base.less';
-@import 'app/account-settings.less';
 @import 'app/beta-program.less';
 @import 'app/about-page.less';
 @import 'app/project-list.less';

+ 1 - 1
services/web/test/frontend/features/project-list/components/notifications.test.tsx

@@ -875,7 +875,7 @@ describe('<UserNotifications />', function () {
       fireEvent.click(
         screen.getByRole('button', { name: /resend confirmation email/i })
       )
-      await waitForElementToBeRemoved(() => screen.getByText('Loading'))
+      await waitForElementToBeRemoved(() => screen.getByText('Sending…'))
       expect(sendReconfirmationMock.calls()).to.have.lengthOf(2)
     })
 

+ 1 - 1
services/web/test/frontend/features/settings/components/account-info-section.test.tsx

@@ -90,7 +90,7 @@ describe('<AccountInfoSection />', function () {
         name: 'Update',
       })
     )
-    await screen.findByRole('button', { name: 'Loading' })
+    await screen.findByRole('button', { name: /saving/i })
 
     finishUpdateCall(200)
     await screen.findByRole('button', {

+ 10 - 8
services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx

@@ -69,7 +69,7 @@ async function confirmCodeForEmail(email: string) {
   })
   fireEvent.click(submitCodeBtn)
   await waitForElementToBeRemoved(() =>
-    screen.getByRole('button', { name: /Loading/i })
+    screen.getByRole('button', { name: /confirming/i })
   )
 }
 
@@ -221,7 +221,7 @@ describe('<EmailsSection />', function () {
     )
 
     await confirmCodeForEmail(userEmailData.email)
-    screen.getByText(userEmailData.email)
+    await screen.findByText(userEmailData.email)
   })
 
   it('fails to add add new email address', async function () {
@@ -381,8 +381,8 @@ describe('<EmailsSection />', function () {
 
     await confirmCodeForEmail(userEmailData.email)
 
-    screen.getByText(userEmailData.affiliation.role!, { exact: false })
-    screen.getByText(customDepartment, { exact: false })
+    await screen.findByText(userEmailData.affiliation.role!, { exact: false })
+    await screen.findByText(customDepartment, { exact: false })
   })
 
   it('autocompletes institution name', async function () {
@@ -528,10 +528,12 @@ describe('<EmailsSection />', function () {
       department: userEmailData.affiliation?.department,
     })
 
-    screen.getByText(userEmailData.email)
-    screen.getByText(newUniversity)
-    screen.getByText(userEmailData.affiliation.role!, { exact: false })
-    screen.getByText(userEmailData.affiliation.department!, { exact: false })
+    await screen.findByText(userEmailData.email)
+    await screen.findByText(newUniversity)
+    await screen.findByText(userEmailData.affiliation.role!, { exact: false })
+    await screen.findByText(userEmailData.affiliation.department!, {
+      exact: false,
+    })
   })
 
   it('shows country, university, role and department fields based on whether `change` was clicked or not', async function () {

+ 2 - 2
services/web/test/frontend/features/settings/components/emails/emails-section-institution-and-role.test.tsx

@@ -195,7 +195,7 @@ describe('user role and institution', function () {
       screen.getByRole('button', { name: /saving/i })
     )
 
-    screen.getByText(roleValue, { exact: false })
-    screen.getByText(departmentValue, { exact: false })
+    await screen.findByText(roleValue, { exact: false })
+    await screen.findByText(departmentValue, { exact: false })
   })
 })

+ 1 - 1
services/web/test/frontend/features/settings/components/emails/reconfirmation-info.test.tsx

@@ -161,7 +161,7 @@ describe('<ReconfirmationInfo/>', function () {
         // commented out as it's already gone by this point
         // await screen.findByText(/Sending/)
         expect(fetchMock.called()).to.be.true
-        await waitForElementToBeRemoved(() => screen.getByText(/Sending/))
+        await waitForElementToBeRemoved(() => screen.getByText('Sending…'))
         await screen.findByRole('button', {
           name: 'Resend confirmation email',
         })