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
This commit is contained in:
Rebeka Dekany
2025-03-19 17:51:38 +01:00
committed by Copybot
parent c329149b07
commit 12fd0194ff
36 changed files with 116 additions and 739 deletions

View File

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

View File

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

View File

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

View File

@@ -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"
/>
)
}

View File

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

View File

@@ -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>
<DropdownItem
as="span"
role={undefined}
className={classnames({
active: selectedItem?.name === item.name,
'dropdown-item-highlighted': highlightedIndex === index,
})}
trailingIcon={
selectedItem?.name === item.name ? 'check' : undefined
}
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>
}
/>
>
{item.name}
</DropdownItem>
</li>
))}
</ul>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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