mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-20 06:13:40 +00:00
Merge pull request #17990 from overleaf/rd-button-links
[web] Migrating buttons to Bootstrap 5 on the Account Settings page GitOrigin-RevId: c9dfa9b1dee50f4c0b30abf8ac464e53cf98db95
This commit is contained in:
parent
06f34c71bc
commit
898acab307
25 changed files with 277 additions and 146 deletions
|
@ -124,7 +124,8 @@ function AccountInfoSection() {
|
|||
type="submit"
|
||||
variant="primary"
|
||||
form="account-info-form"
|
||||
isLoading={isLoading || !isFormValid}
|
||||
disabled={!isFormValid}
|
||||
isLoading={isLoading}
|
||||
bs3Props={{
|
||||
loading: isLoading ? `${t('saving')}…` : t('update'),
|
||||
}}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import { Modal, Button } from 'react-bootstrap'
|
||||
import { Modal } from 'react-bootstrap'
|
||||
import AccessibleModal from '../../../../../../shared/components/accessible-modal'
|
||||
import { MergeAndOverride } from '../../../../../../../../types/utils'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
type ConfirmationModalProps = MergeAndOverride<
|
||||
React.ComponentProps<typeof AccessibleModal>,
|
||||
|
@ -40,22 +41,24 @@ function ConfirmationModal({
|
|||
<p className="mb-0">{t('log_in_with_primary_email_address')}</p>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
bsStyle={null}
|
||||
className="btn-secondary-info btn-secondary"
|
||||
<ButtonWrapper
|
||||
variant="secondary"
|
||||
onClick={onHide}
|
||||
bs3Props={{
|
||||
bsStyle: null,
|
||||
className: 'btn-secondary-info btn-secondary',
|
||||
}}
|
||||
>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
bsStyle={null}
|
||||
className="btn-primary"
|
||||
</ButtonWrapper>
|
||||
<ButtonWrapper
|
||||
variant="primary"
|
||||
disabled={isConfirmDisabled}
|
||||
onClick={onConfirm}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-primary' }}
|
||||
>
|
||||
{t('confirm')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
)
|
||||
|
|
|
@ -82,7 +82,7 @@ function MakePrimary({ userEmailData, makePrimaryAsync }: MakePrimaryProps) {
|
|||
return (
|
||||
<>
|
||||
{makePrimaryAsync.isLoading ? (
|
||||
<PrimaryButton disabled>
|
||||
<PrimaryButton disabled isLoading={state.isLoading}>
|
||||
{t('processing_uppercase')}…
|
||||
</PrimaryButton>
|
||||
) : (
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
import ButtonWrapper, {
|
||||
ButtonWrapperProps,
|
||||
} from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
function PrimaryButton({ children, disabled, onClick }: ButtonWrapperProps) {
|
||||
function PrimaryButton({
|
||||
children,
|
||||
disabled,
|
||||
isLoading,
|
||||
onClick,
|
||||
}: ButtonWrapperProps) {
|
||||
return (
|
||||
<ButtonWrapper
|
||||
size="small"
|
||||
disabled={disabled}
|
||||
disabled={disabled && !isLoading}
|
||||
isLoading={isLoading}
|
||||
onClick={onClick}
|
||||
variant="secondary"
|
||||
bs3Props={{ bsStyle: null }}
|
||||
className={bsVersion({
|
||||
bs3: 'btn-secondary btn-secondary-info',
|
||||
})}
|
||||
bs3Props={{
|
||||
bsStyle: null,
|
||||
className: 'btn-secondary btn-secondary-info',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ButtonWrapper>
|
||||
|
|
|
@ -214,7 +214,8 @@ function AddEmail() {
|
|||
>
|
||||
<AddNewEmailBtn
|
||||
email={newEmail}
|
||||
disabled={isLoading || state.isLoading}
|
||||
disabled={state.isLoading}
|
||||
isLoading={isLoading}
|
||||
onClick={handleAddNewEmail}
|
||||
/>
|
||||
</Cell>
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, ButtonProps } from 'react-bootstrap'
|
||||
import ButtonWrapper, {
|
||||
ButtonWrapperProps,
|
||||
} from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
function AddAnotherEmailBtn({ onClick, ...props }: ButtonProps) {
|
||||
function AddAnotherEmailBtn({ onClick, ...props }: ButtonWrapperProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Button
|
||||
className="btn-inline-link"
|
||||
<ButtonWrapper
|
||||
variant="link"
|
||||
onClick={onClick}
|
||||
{...props}
|
||||
bsStyle={null}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-inline-link' }}
|
||||
>
|
||||
{t('add_another_email')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, ButtonProps } from 'react-bootstrap'
|
||||
import ButtonWrapper, {
|
||||
ButtonWrapperProps,
|
||||
} from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
const isValidEmail = (email: string) => {
|
||||
return Boolean(email)
|
||||
|
@ -7,20 +9,26 @@ const isValidEmail = (email: string) => {
|
|||
|
||||
type AddNewEmailColProps = {
|
||||
email: string
|
||||
} & ButtonProps
|
||||
} & ButtonWrapperProps
|
||||
|
||||
function AddNewEmailBtn({ email, disabled, ...props }: AddNewEmailColProps) {
|
||||
function AddNewEmailBtn({
|
||||
email,
|
||||
disabled,
|
||||
isLoading,
|
||||
...props
|
||||
}: AddNewEmailColProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Button
|
||||
bsSize="small"
|
||||
bsStyle="primary"
|
||||
disabled={disabled || !isValidEmail(email)}
|
||||
<ButtonWrapper
|
||||
size="small"
|
||||
variant="primary"
|
||||
disabled={(disabled && !isLoading) || !isValidEmail(email)}
|
||||
isLoading={isLoading}
|
||||
{...props}
|
||||
>
|
||||
{t('add_new_email')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, ButtonProps } from 'react-bootstrap'
|
||||
import ButtonWrapper, {
|
||||
ButtonWrapperProps,
|
||||
} from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
function EmailAffiliatedWithInstitution({ onClick, ...props }: ButtonProps) {
|
||||
function EmailAffiliatedWithInstitution({
|
||||
onClick,
|
||||
...props
|
||||
}: ButtonWrapperProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="mt-1">
|
||||
{t('is_email_affiliated')}
|
||||
<Button
|
||||
className="btn-inline-link"
|
||||
<ButtonWrapper
|
||||
variant="link"
|
||||
onClick={onClick}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-inline-link' }}
|
||||
{...props}
|
||||
bsStyle={null}
|
||||
>
|
||||
{t('let_us_know')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useState } from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { DomainInfo } from './input'
|
||||
import { ExposedSettings } from '../../../../../../../types/exposed-settings'
|
||||
import getMeta from '../../../../../utils/meta'
|
||||
import { useLocation } from '../../../../../shared/hooks/use-location'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
type SSOLinkingInfoProps = {
|
||||
domainInfo: DomainInfo
|
||||
|
@ -54,14 +54,15 @@ function SsoLinkingInfo({ domainInfo, email }: SSOLinkingInfoProps) {
|
|||
{t('find_out_more_about_institution_login')}.
|
||||
</a>
|
||||
</p>
|
||||
<Button
|
||||
bsStyle="primary"
|
||||
className="btn-sm btn-link-accounts"
|
||||
<ButtonWrapper
|
||||
variant="primary"
|
||||
className="btn-link-accounts"
|
||||
size="small"
|
||||
disabled={linkAccountsButtonDisabled}
|
||||
onClick={handleLinkAccountsButtonClick}
|
||||
>
|
||||
{t('link_accounts_and_add_email')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
type UniversityNameProps = {
|
||||
name: string
|
||||
|
@ -14,9 +14,13 @@ function UniversityName({ name, onClick }: UniversityNameProps) {
|
|||
{name}
|
||||
<span className="small">
|
||||
{' '}
|
||||
<Button className="btn-inline-link" onClick={onClick} bsStyle={null}>
|
||||
<ButtonWrapper
|
||||
variant="link"
|
||||
onClick={onClick}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-inline-link' }}
|
||||
>
|
||||
{t('change')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { useState, useEffect, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { UserEmailData } from '../../../../../../types/user-email'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { isChangingAffiliation } from '../../utils/selectors'
|
||||
import { useUserEmailsContext } from '../../context/user-email-context'
|
||||
import DownshiftInput from './downshift-input'
|
||||
|
@ -10,6 +9,7 @@ import { getJSON, postJSON } from '../../../../infrastructure/fetch-json'
|
|||
import defaultRoles from '../../data/roles'
|
||||
import defaultDepartments from '../../data/departments'
|
||||
import { University } from '../../../../../../types/university'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
type InstitutionAndRoleProps = {
|
||||
userEmailData: UserEmailData
|
||||
|
@ -107,11 +107,15 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
|
|||
<br />
|
||||
</>
|
||||
)}
|
||||
<Button className="btn-inline-link" onClick={handleChangeAffiliation}>
|
||||
<ButtonWrapper
|
||||
onClick={handleChangeAffiliation}
|
||||
variant="link"
|
||||
bs3Props={{ className: 'btn-inline-link' }}
|
||||
>
|
||||
{!affiliation.department && !affiliation.role
|
||||
? t('add_role_and_department')
|
||||
: t('change')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</div>
|
||||
) : (
|
||||
<div className="affiliation-change-container small">
|
||||
|
@ -135,23 +139,30 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
|
|||
setValue={setDepartment}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
bsSize="small"
|
||||
bsStyle="primary"
|
||||
<ButtonWrapper
|
||||
size="small"
|
||||
variant="primary"
|
||||
type="submit"
|
||||
disabled={!role || !department || isLoading || state.isLoading}
|
||||
disabled={!role || !department}
|
||||
isLoading={isLoading}
|
||||
bs3Props={{
|
||||
loading: isLoading
|
||||
? `${t('saving')}…`
|
||||
: t('save_or_cancel-save'),
|
||||
}}
|
||||
>
|
||||
{isLoading ? <>{t('saving')}…</> : t('save_or_cancel-save')}
|
||||
</Button>
|
||||
{t('save_or_cancel-save')}
|
||||
</ButtonWrapper>
|
||||
{!isLoading && (
|
||||
<>
|
||||
<span className="mx-1">{t('save_or_cancel-or')}</span>
|
||||
<Button
|
||||
className="btn-inline-link"
|
||||
<ButtonWrapper
|
||||
variant="link"
|
||||
onClick={handleCancelAffiliationChange}
|
||||
bs3Props={{ className: 'btn-inline-link' }}
|
||||
>
|
||||
{t('save_or_cancel-cancel')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
|
|
|
@ -146,9 +146,13 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
|
|||
</>
|
||||
) : (
|
||||
<ButtonWrapper
|
||||
className="btn-inline-link"
|
||||
variant="link"
|
||||
disabled={state.isLoading}
|
||||
onClick={handleRequestReconfirmation}
|
||||
bs3Props={{
|
||||
className: 'btn-inline-link',
|
||||
bsStyle: null,
|
||||
}}
|
||||
>
|
||||
{t('resend_confirmation_email')}
|
||||
</ButtonWrapper>
|
||||
|
@ -157,7 +161,8 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
|
|||
) : (
|
||||
<ButtonWrapper
|
||||
variant="secondary"
|
||||
disabled={state.isLoading || isPending}
|
||||
disabled={isPending}
|
||||
isLoading={isLoading}
|
||||
onClick={handleRequestReconfirmation}
|
||||
bs3Props={{ bsStyle: 'info' }}
|
||||
>
|
||||
|
@ -200,9 +205,10 @@ function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
|
|||
</>
|
||||
) : (
|
||||
<ButtonWrapper
|
||||
className="btn-inline-link"
|
||||
variant="link"
|
||||
disabled={state.isLoading}
|
||||
onClick={handleRequestReconfirmation}
|
||||
bs3Props={{ className: 'btn-inline-link', bsStyle: null }}
|
||||
>
|
||||
{t('resend_confirmation_email')}
|
||||
</ButtonWrapper>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Icon from '../../../../shared/components/icon'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { FetchError, postJSON } from '../../../../infrastructure/fetch-json'
|
||||
import useAsync from '../../../../shared/hooks/use-async'
|
||||
import { UserEmailData } from '../../../../../../types/user-email'
|
||||
import { useUserEmailsContext } from '../../context/user-email-context'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
type ResendConfirmationEmailButtonProps = {
|
||||
email: UserEmailData['email']
|
||||
|
@ -47,14 +47,14 @@ function ResendConfirmationEmailButton({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
className="btn-inline-link"
|
||||
disabled={state.isLoading}
|
||||
<ButtonWrapper
|
||||
variant="link"
|
||||
disabled={state.isLoading || isLoading}
|
||||
onClick={handleResendConfirmationEmail}
|
||||
bsStyle={null}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-inline-link' }}
|
||||
>
|
||||
{t('resend_confirmation_email')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
<br />
|
||||
{isError && (
|
||||
<div className="text-danger">
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { UserEmailData } from '../../../../../../types/user-email'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import Email from './email'
|
||||
import InstitutionAndRole from './institution-and-role'
|
||||
import EmailCell from './cell'
|
||||
|
@ -16,6 +15,7 @@ import { useLocation } from '../../../../shared/hooks/use-location'
|
|||
import RowWrapper from '@/features/ui/components/bootstrap-5/wrappers/row-wrapper'
|
||||
import ColWrapper from '@/features/ui/components/bootstrap-5/wrappers/col-wrapper'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
type EmailsRowProps = {
|
||||
userEmailData: UserEmailData
|
||||
|
@ -160,14 +160,15 @@ function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) {
|
|||
})}
|
||||
>
|
||||
<EmailCell>
|
||||
<Button
|
||||
bsStyle="primary"
|
||||
className="btn-sm btn-link-accounts"
|
||||
<ButtonWrapper
|
||||
variant="primary"
|
||||
className="btn-link-accounts"
|
||||
disabled={linkAccountsButtonDisabled}
|
||||
onClick={handleLinkAccountsButtonClick}
|
||||
size="small"
|
||||
>
|
||||
{t('link_accounts')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</EmailCell>
|
||||
</ColWrapper>
|
||||
</RowWrapper>
|
||||
|
|
|
@ -2,6 +2,8 @@ import { useState, useCallback } from 'react'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import LeaveModal from './leave/modal'
|
||||
import getMeta from '../../../utils/meta'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
function LeaveSection() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -28,9 +30,16 @@ function LeaveSection() {
|
|||
return (
|
||||
<>
|
||||
{t('need_to_leave')}{' '}
|
||||
<button className="btn btn-inline-link btn-danger" onClick={handleOpen}>
|
||||
<ButtonWrapper
|
||||
className={bsVersion({
|
||||
bs3: 'btn btn-inline-link btn-danger',
|
||||
bs5: 'btn-link',
|
||||
})}
|
||||
variant="danger"
|
||||
onClick={handleOpen}
|
||||
>
|
||||
{t('delete_your_account')}
|
||||
</button>
|
||||
</ButtonWrapper>
|
||||
<LeaveModal isOpen={isModalOpen} handleClose={handleClose} />
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useState, Dispatch, SetStateAction } from 'react'
|
||||
import { Modal, Button } from 'react-bootstrap'
|
||||
import { Modal } from 'react-bootstrap'
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import LeaveModalForm, { LeaveModalFormProps } from './modal-form'
|
||||
import { ExposedSettings } from '../../../../../../types/exposed-settings'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
type LeaveModalContentProps = {
|
||||
handleHide: () => void
|
||||
|
@ -68,24 +69,23 @@ function LeaveModalContent({
|
|||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
type="button"
|
||||
<ButtonWrapper
|
||||
disabled={inFlight}
|
||||
onClick={handleHide}
|
||||
bsStyle={null}
|
||||
className="btn-secondary"
|
||||
variant="secondary"
|
||||
bs3Props={{ bsStyle: null, className: 'btn-secondary' }}
|
||||
>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
|
||||
<Button
|
||||
<ButtonWrapper
|
||||
form="leave-form"
|
||||
type="submit"
|
||||
bsStyle="danger"
|
||||
variant="danger"
|
||||
disabled={inFlight || !isFormValid}
|
||||
>
|
||||
{inFlight ? <>{t('deleting')}…</> : t('delete')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</Modal.Footer>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
import BadgeWrapper from '@/features/ui/components/bootstrap-5/wrappers/badge-wrapper'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
function trackUpgradeClick() {
|
||||
sendMB('settings-upgrade-click')
|
||||
|
@ -92,36 +92,39 @@ function ActionButton({
|
|||
const { t } = useTranslation()
|
||||
if (!hasFeature) {
|
||||
return (
|
||||
<Button
|
||||
bsStyle={null}
|
||||
className="btn-primary"
|
||||
<ButtonWrapper
|
||||
variant="primary"
|
||||
href="/user/subscription/plans"
|
||||
onClick={trackUpgradeClick}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-primary' }}
|
||||
>
|
||||
<span className="text-capitalize">{t('upgrade')}</span>
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
} else if (linked) {
|
||||
return (
|
||||
<Button
|
||||
className="btn-danger-ghost"
|
||||
<ButtonWrapper
|
||||
variant="danger-ghost"
|
||||
onClick={handleUnlinkClick}
|
||||
bsStyle={null}
|
||||
disabled={disabled}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-danger-ghost' }}
|
||||
>
|
||||
{t('turn_off')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Button
|
||||
<ButtonWrapper
|
||||
variant="secondary"
|
||||
disabled={disabled}
|
||||
bsStyle={null}
|
||||
onClick={handleLinkClick}
|
||||
className="btn btn-secondary-info btn-secondary text-capitalize"
|
||||
bs3Props={{
|
||||
bsStyle: null,
|
||||
className: 'btn btn-secondary-info btn-secondary',
|
||||
}}
|
||||
>
|
||||
{t('turn_on')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { useCallback, useState, ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AccessibleModal from '../../../../shared/components/accessible-modal'
|
||||
import { Modal } from 'react-bootstrap'
|
||||
import BadgeWrapper from '@/features/ui/components/bootstrap-5/wrappers/badge-wrapper'
|
||||
import { Button, Modal } from 'react-bootstrap'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import { sendMB } from '../../../../infrastructure/event-tracking'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
function trackUpgradeClick() {
|
||||
sendMB('settings-upgrade-click')
|
||||
|
@ -107,43 +109,52 @@ function ActionButton({
|
|||
const { t } = useTranslation()
|
||||
if (!hasFeature) {
|
||||
return (
|
||||
<Button
|
||||
bsStyle={null}
|
||||
className="btn-primary"
|
||||
<ButtonWrapper
|
||||
variant="primary"
|
||||
href="/user/subscription/plans"
|
||||
onClick={trackUpgradeClick}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-primary' }}
|
||||
>
|
||||
<span className="text-capitalize">{t('upgrade')}</span>
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
} else if (linked) {
|
||||
return (
|
||||
<Button
|
||||
className="btn-danger-ghost"
|
||||
<ButtonWrapper
|
||||
variant="danger-ghost"
|
||||
onClick={handleUnlinkClick}
|
||||
bsStyle={null}
|
||||
disabled={disabled}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-danger-ghost' }}
|
||||
>
|
||||
{t('unlink')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{disabled ? (
|
||||
<button
|
||||
<ButtonWrapper
|
||||
disabled
|
||||
className="btn btn-secondary-info btn-secondary text-capitalize"
|
||||
variant="secondary"
|
||||
className={bsVersion({
|
||||
bs3: 'btn btn-secondary-info btn-secondary text-capitalize',
|
||||
bs5: 'text-capitalize',
|
||||
})}
|
||||
>
|
||||
{t('link')}
|
||||
</button>
|
||||
</ButtonWrapper>
|
||||
) : (
|
||||
<a
|
||||
className="btn btn-secondary-info btn-secondary text-capitalize"
|
||||
<ButtonWrapper
|
||||
variant="secondary"
|
||||
href={linkPath}
|
||||
className={bsVersion({
|
||||
bs3: 'btn btn-secondary-info btn-secondary text-capitalize',
|
||||
bs5: 'text-capitalize',
|
||||
})}
|
||||
bs3Props={{ bsStyle: null }}
|
||||
>
|
||||
{t('link')}
|
||||
</a>
|
||||
</ButtonWrapper>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
@ -167,9 +178,7 @@ function UnlinkConfirmationModal({
|
|||
}: UnlinkConfirmModalProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleCancel = (
|
||||
event: React.MouseEvent<HTMLButtonElement & Button>
|
||||
) => {
|
||||
const handleCancel = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault()
|
||||
handleHide()
|
||||
}
|
||||
|
@ -186,15 +195,23 @@ function UnlinkConfirmationModal({
|
|||
<Modal.Footer>
|
||||
<form action={unlinkPath} method="POST" className="form-inline">
|
||||
<input type="hidden" name="_csrf" value={getMeta('ol-csrfToken')} />
|
||||
<Button
|
||||
className="btn-secondary-info btn-secondary"
|
||||
<ButtonWrapper
|
||||
variant="secondary"
|
||||
onClick={handleCancel}
|
||||
bs3Props={{
|
||||
bsStyle: null,
|
||||
className: 'btn-secondary-info btn-secondary',
|
||||
}}
|
||||
>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button type="submit" className="btn-danger-ghost" bsStyle={null}>
|
||||
</ButtonWrapper>
|
||||
<ButtonWrapper
|
||||
type="submit"
|
||||
variant="danger-ghost"
|
||||
bs3Props={{ bsStyle: null, className: 'btn-danger-ghost' }}
|
||||
>
|
||||
{t('unlink')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</form>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, Modal } from 'react-bootstrap'
|
||||
import { Modal } from 'react-bootstrap'
|
||||
import { FetchError } from '../../../../infrastructure/fetch-json'
|
||||
import AccessibleModal from '../../../../shared/components/accessible-modal'
|
||||
import IEEELogo from '../../../../shared/svgs/ieee-logo'
|
||||
import GoogleLogo from '../../../../shared/svgs/google-logo'
|
||||
import OrcidLogo from '../../../../shared/svgs/orcid-logo'
|
||||
import LinkingStatus from './status'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
const providerLogos: { readonly [p: string]: JSX.Element } = {
|
||||
collabratec: <IEEELogo />,
|
||||
|
@ -112,28 +114,37 @@ function ActionButton({
|
|||
const { t } = useTranslation()
|
||||
if (unlinkRequestInflight) {
|
||||
return (
|
||||
<Button className="btn-danger-ghost" bsStyle={null} disabled>
|
||||
<ButtonWrapper
|
||||
variant="danger-ghost"
|
||||
disabled
|
||||
bs3Props={{ bsStyle: null, className: 'btn-danger-ghost' }}
|
||||
>
|
||||
{t('unlinking')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
} else if (accountIsLinked) {
|
||||
return (
|
||||
<Button
|
||||
className="btn-danger-ghost"
|
||||
bsStyle={null}
|
||||
<ButtonWrapper
|
||||
variant="danger-ghost"
|
||||
onClick={onUnlinkClick}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-danger-ghost' }}
|
||||
>
|
||||
{t('unlink')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<a
|
||||
<ButtonWrapper
|
||||
variant="secondary"
|
||||
href={linkPath}
|
||||
className="btn btn-secondary-info btn-secondary text-capitalize"
|
||||
bs3Props={{ bsStyle: null }}
|
||||
className={bsVersion({
|
||||
bs3: 'btn btn-secondary-info btn-secondary text-capitalize',
|
||||
bs5: 'text-capitalize',
|
||||
})}
|
||||
>
|
||||
{t('link')}
|
||||
</a>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -166,20 +177,23 @@ function UnlinkConfirmModal({
|
|||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
bsStyle={null}
|
||||
className="btn-secondary-info btn-secondary"
|
||||
<ButtonWrapper
|
||||
variant="secondary"
|
||||
onClick={handleHide}
|
||||
bs3Props={{
|
||||
bsStyle: null,
|
||||
className: 'btn-secondary-info btn-secondary',
|
||||
}}
|
||||
>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
className="btn-danger-ghost"
|
||||
bsStyle={null}
|
||||
</ButtonWrapper>
|
||||
<ButtonWrapper
|
||||
variant="danger-ghost"
|
||||
onClick={handleConfirmation}
|
||||
bs3Props={{ bsStyle: null, className: 'btn-danger-ghost' }}
|
||||
>
|
||||
{t('unlink')}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
)
|
||||
|
|
|
@ -201,7 +201,8 @@ function PasswordForm() {
|
|||
form="password-change-form"
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={isLoading || !isFormValid}
|
||||
disabled={!isFormValid}
|
||||
isLoading={isLoading}
|
||||
bs3Props={{
|
||||
loading: isLoading ? `${t('saving')}…` : t('change'),
|
||||
}}
|
||||
|
|
|
@ -2,6 +2,7 @@ import MaterialIcon from '@/shared/components/material-icon'
|
|||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { GroupSSOLinkingStatus } from '../../../../../types/subscription/sso'
|
||||
import getMeta from '../../../utils/meta'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
function SecuritySection() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -84,12 +85,13 @@ function SecuritySection() {
|
|||
</div>
|
||||
{linked ? null : (
|
||||
<div className="button-column">
|
||||
<a
|
||||
className="btn btn-primary"
|
||||
<ButtonWrapper
|
||||
variant="primary"
|
||||
bs3Props={{ className: 'btn btn-primary', bsStyle: null }}
|
||||
href={`/subscription/${groupId}/sso_enrollment`}
|
||||
>
|
||||
{t('set_up_sso')}
|
||||
</a>
|
||||
</ButtonWrapper>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,7 @@ import Button from '../button'
|
|||
export type ButtonWrapperProps = ButtonProps & {
|
||||
bs3Props?: {
|
||||
bsStyle?: string | null
|
||||
className?: string
|
||||
loading?: React.ReactNode
|
||||
}
|
||||
}
|
||||
|
@ -26,11 +27,12 @@ export default function ButtonWrapper(props: ButtonWrapperProps) {
|
|||
const { bs3Props, ...rest } = props
|
||||
|
||||
const bs3ButtonProps: BS3ButtonProps = {
|
||||
bsStyle: rest.variant,
|
||||
bsStyle: rest.variant === 'secondary' ? 'default' : rest.variant,
|
||||
bsSize: mapBsButtonSizes(rest.size),
|
||||
className: rest.className,
|
||||
disabled: rest.isLoading || rest.disabled,
|
||||
form: rest.form,
|
||||
href: rest.href,
|
||||
onClick: rest.onClick,
|
||||
type: rest.type,
|
||||
...bs3Props,
|
||||
|
|
|
@ -19,4 +19,5 @@ export type ButtonProps = {
|
|||
| 'danger'
|
||||
| 'danger-ghost'
|
||||
| 'premium'
|
||||
| 'link'
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
// Use CSS variables for link colors to make it easy to override in marketing page
|
||||
:root {
|
||||
--link-color: var(--link-ui-visited);
|
||||
--link-color: var(--link-ui);
|
||||
--link-hover-color: var(--link-ui-hover);
|
||||
--link-visited-color: var(--link-ui-visited);
|
||||
}
|
||||
|
||||
a {
|
||||
a:not([role='button']) {
|
||||
color: var(--link-color);
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -87,10 +87,44 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Link buttons
|
||||
// -------------------------
|
||||
|
||||
// Make a button look and behave like a link
|
||||
.btn-link {
|
||||
color: var(--link-ui);
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
text-decoration: underline;
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
vertical-align: inherit;
|
||||
|
||||
&,
|
||||
&:active,
|
||||
&[disabled],
|
||||
fieldset[disabled] & {
|
||||
background-color: transparent;
|
||||
@include box-shadow(none);
|
||||
}
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--link-ui-hover);
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&.btn-danger {
|
||||
color: var(--content-danger);
|
||||
}
|
||||
}
|
||||
|
||||
.button-loading {
|
||||
align-items: center;
|
||||
display: inline-grid;
|
||||
grid-template-areas: 'container'; // Define a single grid area
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.button-loading > * {
|
||||
|
|
Loading…
Add table
Reference in a new issue