mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #17806 from overleaf/rd-bootstrap-button2
[web] - Updating the Account Settings page with the Button and Icon Button wrappers GitOrigin-RevId: 135c4ddaa64d009d3ab8cdfef9cff899fd77669c
This commit is contained in:
parent
f6d5152a37
commit
fa3f51fb2e
21 changed files with 211 additions and 151 deletions
|
@ -1,11 +1,5 @@
|
|||
import { useState } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
ControlLabel,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
} from 'react-bootstrap'
|
||||
import { Alert, ControlLabel, FormControl, FormGroup } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
getUserFacingMessage,
|
||||
|
@ -15,6 +9,7 @@ import getMeta from '../../../utils/meta'
|
|||
import { ExposedSettings } from '../../../../../types/exposed-settings'
|
||||
import useAsync from '../../../shared/hooks/use-async'
|
||||
import { useUserContext } from '../../../shared/context/user-context'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
function AccountInfoSection() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -118,14 +113,17 @@ function AccountInfoSection() {
|
|||
</FormGroup>
|
||||
) : null}
|
||||
{canUpdateEmail || canUpdateNames ? (
|
||||
<Button
|
||||
form="account-info-form"
|
||||
<ButtonWrapper
|
||||
type="submit"
|
||||
bsStyle="primary"
|
||||
disabled={isLoading || !isFormValid}
|
||||
variant="primary"
|
||||
form="account-info-form"
|
||||
isLoading={isLoading || !isFormValid}
|
||||
bs3Props={{
|
||||
loading: isLoading ? `${t('saving')}…` : t('update'),
|
||||
}}
|
||||
>
|
||||
{isLoading ? <>{t('saving')}…</> : t('update')}
|
||||
</Button>
|
||||
{t('update')}
|
||||
</ButtonWrapper>
|
||||
) : null}
|
||||
</form>
|
||||
</>
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
import { Button } from 'react-bootstrap'
|
||||
import ButtonWrapper, {
|
||||
ButtonWrapperProps,
|
||||
} from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
function PrimaryButton({ children, disabled, onClick }: Button.ButtonProps) {
|
||||
function PrimaryButton({ children, disabled, onClick }: ButtonWrapperProps) {
|
||||
return (
|
||||
<Button
|
||||
bsSize="small"
|
||||
bsStyle={null}
|
||||
className="btn-secondary-info btn-secondary"
|
||||
<ButtonWrapper
|
||||
size="small"
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
variant="secondary"
|
||||
bs3Props={{ bsStyle: null }}
|
||||
className={bsVersion({
|
||||
bs3: 'btn-secondary btn-secondary-info',
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +1,38 @@
|
|||
import Icon from '../../../../../shared/components/icon'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { UserEmailData } from '../../../../../../../types/user-email'
|
||||
import { useUserEmailsContext } from '../../../context/user-email-context'
|
||||
import { postJSON } from '../../../../../infrastructure/fetch-json'
|
||||
import { UseAsyncReturnType } from '../../../../../shared/hooks/use-async'
|
||||
import TooltipWrapper from '@/features/ui/components/bootstrap-5/wrappers/tooltip-wrapper'
|
||||
import IconButtonWrapper, {
|
||||
IconButtonWrapperProps,
|
||||
} from '@/features/ui/components/bootstrap-5/wrappers/icon-button-wrapper'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
function DeleteButton({ disabled, onClick }: Button.ButtonProps) {
|
||||
type DeleteButtonProps = Pick<
|
||||
IconButtonWrapperProps,
|
||||
'disabled' | 'isLoading' | 'onClick'
|
||||
>
|
||||
|
||||
function DeleteButton({ disabled, isLoading, onClick }: DeleteButtonProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Button
|
||||
bsSize="small"
|
||||
bsStyle="danger"
|
||||
<IconButtonWrapper
|
||||
variant="danger"
|
||||
disabled={disabled}
|
||||
isLoading={isLoading}
|
||||
size="small"
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon type="trash" fw accessibilityLabel={t('remove')} />
|
||||
</Button>
|
||||
accessibilityLabel={t('remove') || ''}
|
||||
icon={
|
||||
bsVersion({
|
||||
bs5: 'delete',
|
||||
bs3: 'trash',
|
||||
}) || 'trash'
|
||||
}
|
||||
bs3Props={{ fw: true }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -49,7 +63,7 @@ function Remove({ userEmailData, deleteEmailAsync }: RemoveProps) {
|
|||
}
|
||||
|
||||
if (deleteEmailAsync.isLoading) {
|
||||
return <DeleteButton disabled />
|
||||
return <DeleteButton isLoading />
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -18,7 +18,7 @@ import getMeta from '../../../../utils/meta'
|
|||
import { ReCaptcha2 } from '../../../../shared/components/recaptcha-2'
|
||||
import { useRecaptcha } from '../../../../shared/hooks/use-recaptcha'
|
||||
import ColWrapper from '@/features/ui/components/bootstrap-5/wrappers/col-wrapper'
|
||||
import { bsClassName } from '@/features/utils/bootstrap-5'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
function AddEmail() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -136,7 +136,7 @@ function AddEmail() {
|
|||
<>
|
||||
<label
|
||||
htmlFor="affiliations-email"
|
||||
className={bsClassName({ bs5: 'visually-hidden', bs3: 'sr-only' })}
|
||||
className={bsVersion({ bs5: 'visually-hidden', bs3: 'sr-only' })}
|
||||
>
|
||||
{t('email')}
|
||||
</label>
|
||||
|
@ -162,7 +162,7 @@ function AddEmail() {
|
|||
</ColWrapper>
|
||||
<ColWrapper md={4}>
|
||||
<Cell
|
||||
className={bsClassName({
|
||||
className={bsVersion({
|
||||
bs5: 'text-md-end',
|
||||
bs3: 'text-md-right',
|
||||
})}
|
||||
|
@ -207,7 +207,7 @@ function AddEmail() {
|
|||
{!isSsoAvailableForDomain ? (
|
||||
<ColWrapper md={4}>
|
||||
<Cell
|
||||
className={bsClassName({
|
||||
className={bsVersion({
|
||||
bs5: 'text-md-end',
|
||||
bs3: 'text-md-right',
|
||||
})}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
|
|||
import { useCombobox } from 'downshift'
|
||||
import classnames from 'classnames'
|
||||
import countries, { CountryCode } from '../../../data/countries-list'
|
||||
import { bsClassName } from '@/features/utils/bootstrap-5'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
type CountryInputProps = {
|
||||
setValue: React.Dispatch<React.SetStateAction<CountryCode | null>>
|
||||
|
@ -58,7 +58,7 @@ function Downshift({ setValue, inputRef }: CountryInputProps) {
|
|||
{/* eslint-disable-next-line jsx-a11y/label-has-for */}
|
||||
<label
|
||||
{...getLabelProps()}
|
||||
className={bsClassName({ bs5: 'visually-hidden', bs3: 'sr-only' })}
|
||||
className={bsVersion({ bs5: 'visually-hidden', bs3: 'sr-only' })}
|
||||
>
|
||||
{t('country')}
|
||||
</label>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useState, useEffect, forwardRef } from 'react'
|
|||
import { useCombobox } from 'downshift'
|
||||
import classnames from 'classnames'
|
||||
import { escapeRegExp } from 'lodash'
|
||||
import { bsClassName } from '@/features/utils/bootstrap-5'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
type DownshiftInputProps = {
|
||||
highlightMatches?: boolean
|
||||
|
@ -92,7 +92,7 @@ function Downshift({
|
|||
className={
|
||||
showLabel
|
||||
? ''
|
||||
: bsClassName({ bs5: 'visually-hidden', bs3: 'sr-only' })
|
||||
: bsVersion({ bs5: 'visually-hidden', bs3: 'sr-only' })
|
||||
}
|
||||
>
|
||||
{label}
|
||||
|
|
|
@ -3,7 +3,7 @@ import EmailCell from './cell'
|
|||
import ColWrapper from '@/features/ui/components/bootstrap-5/wrappers/col-wrapper'
|
||||
import RowWrapper from '@/features/ui/components/bootstrap-5/wrappers/row-wrapper'
|
||||
import classnames from 'classnames'
|
||||
import { bsClassName } from '@/features/utils/bootstrap-5'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
function Header() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -13,7 +13,7 @@ function Header() {
|
|||
<RowWrapper>
|
||||
<ColWrapper
|
||||
md={4}
|
||||
className={bsClassName({
|
||||
className={bsVersion({
|
||||
bs5: 'd-none d-sm-block',
|
||||
bs3: 'hidden-xs',
|
||||
})}
|
||||
|
@ -24,7 +24,7 @@ function Header() {
|
|||
</ColWrapper>
|
||||
<ColWrapper
|
||||
md={8}
|
||||
className={bsClassName({
|
||||
className={bsVersion({
|
||||
bs5: 'd-none d-sm-block',
|
||||
bs3: 'hidden-xs',
|
||||
})}
|
||||
|
@ -36,13 +36,13 @@ function Header() {
|
|||
</RowWrapper>
|
||||
<div
|
||||
className={classnames(
|
||||
bsClassName({ bs5: 'd-none d-sm-block', bs3: 'hidden-xs' }),
|
||||
bsVersion({ bs5: 'd-none d-sm-block', bs3: 'hidden-xs' }),
|
||||
'horizontal-divider'
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={classnames(
|
||||
bsClassName({ bs5: 'd-none d-sm-block', bs3: 'hidden-xs' }),
|
||||
bsVersion({ bs5: 'd-none d-sm-block', bs3: 'hidden-xs' }),
|
||||
'horizontal-divider'
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -15,7 +15,7 @@ import ReconfirmationInfo from './reconfirmation-info'
|
|||
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 { bsClassName } from '@/features/utils/bootstrap-5'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
type EmailsRowProps = {
|
||||
userEmailData: UserEmailData
|
||||
|
@ -44,7 +44,7 @@ function EmailsRow({ userEmailData }: EmailsRowProps) {
|
|||
</ColWrapper>
|
||||
<ColWrapper md={3}>
|
||||
<EmailCell
|
||||
className={bsClassName({
|
||||
className={bsVersion({
|
||||
bs5: 'text-md-end',
|
||||
bs3: 'text-md-right',
|
||||
})}
|
||||
|
@ -154,7 +154,7 @@ function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) {
|
|||
</ColWrapper>
|
||||
<ColWrapper
|
||||
md={3}
|
||||
className={bsClassName({
|
||||
className={bsVersion({
|
||||
bs5: 'text-md-end',
|
||||
bs3: 'text-md-right',
|
||||
})}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
ControlLabel,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
} from 'react-bootstrap'
|
||||
import { Alert, ControlLabel, FormControl, FormGroup } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import {
|
||||
getUserFacingMessage,
|
||||
|
@ -16,6 +10,7 @@ import getMeta from '../../../utils/meta'
|
|||
import { ExposedSettings } from '../../../../../types/exposed-settings'
|
||||
import { PasswordStrengthOptions } from '../../../../../types/password-strength-options'
|
||||
import useAsync from '../../../shared/hooks/use-async'
|
||||
import ButtonWrapper from '@/features/ui/components/bootstrap-5/wrappers/button-wrapper'
|
||||
|
||||
type PasswordUpdateResult = {
|
||||
message?: {
|
||||
|
@ -198,14 +193,17 @@ function PasswordForm() {
|
|||
</Alert>
|
||||
</FormGroup>
|
||||
) : null}
|
||||
<Button
|
||||
<ButtonWrapper
|
||||
form="password-change-form"
|
||||
type="submit"
|
||||
bsStyle="primary"
|
||||
variant="primary"
|
||||
disabled={isLoading || !isFormValid}
|
||||
bs3Props={{
|
||||
loading: isLoading ? `${t('saving')}…` : t('change'),
|
||||
}}
|
||||
>
|
||||
{isLoading ? <>{t('saving')}…</> : t('change')}
|
||||
</Button>
|
||||
{t('change')}
|
||||
</ButtonWrapper>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Button as B5Button, Spinner } from 'react-bootstrap-5'
|
||||
import { Button as BS5Button, Spinner } from 'react-bootstrap-5'
|
||||
import type { ButtonProps } from '@/features/ui/components/types/button-props'
|
||||
import classNames from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
|
||||
const sizeClasses = new Map<ButtonProps['size'], string>([
|
||||
['small', 'btn-sm'],
|
||||
|
@ -12,8 +13,11 @@ const sizeClasses = new Map<ButtonProps['size'], string>([
|
|||
export default function Button({
|
||||
children,
|
||||
className,
|
||||
leadingIcon,
|
||||
isLoading = false,
|
||||
size = 'default',
|
||||
trailingIcon,
|
||||
variant = 'primary',
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
const { t } = useTranslation()
|
||||
|
@ -24,9 +28,10 @@ export default function Button({
|
|||
})
|
||||
const loadingSpinnerClassName =
|
||||
size === 'large' ? 'loading-spinner-large' : 'loading-spinner-small'
|
||||
const materialIconClassName = size === 'large' ? 'icon-large' : 'icon-small'
|
||||
|
||||
return (
|
||||
<B5Button className={buttonClassName} {...props}>
|
||||
<BS5Button className={buttonClassName} variant={variant} {...props}>
|
||||
{isLoading && (
|
||||
<span className="spinner-container">
|
||||
<Spinner
|
||||
|
@ -40,8 +45,14 @@ export default function Button({
|
|||
</span>
|
||||
)}
|
||||
<span className="button-content" aria-hidden={isLoading}>
|
||||
{leadingIcon && (
|
||||
<MaterialIcon type={leadingIcon} className={materialIconClassName} />
|
||||
)}
|
||||
{children}
|
||||
{trailingIcon && (
|
||||
<MaterialIcon type={trailingIcon} className={materialIconClassName} />
|
||||
)}
|
||||
</span>
|
||||
</B5Button>
|
||||
</BS5Button>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
Dropdown as B5Dropdown,
|
||||
DropdownToggle as B5DropdownToggle,
|
||||
DropdownMenu as B5DropdownMenu,
|
||||
DropdownItem as B5DropdownItem,
|
||||
DropdownDivider as B5DropdownDivider,
|
||||
Dropdown as BS5Dropdown,
|
||||
DropdownToggle as BS5DropdownToggle,
|
||||
DropdownMenu as BS5DropdownMenu,
|
||||
DropdownItem as BS5DropdownItem,
|
||||
DropdownDivider as BS5DropdownDivider,
|
||||
} from 'react-bootstrap-5'
|
||||
import type {
|
||||
DropdownProps,
|
||||
|
@ -15,7 +15,7 @@ import type {
|
|||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
|
||||
export function Dropdown({ ...props }: DropdownProps) {
|
||||
return <B5Dropdown {...props} />
|
||||
return <BS5Dropdown {...props} />
|
||||
}
|
||||
|
||||
export function DropdownItem({
|
||||
|
@ -29,7 +29,7 @@ export function DropdownItem({
|
|||
const trailingIconType = active ? 'check' : trailingIcon
|
||||
return (
|
||||
<li>
|
||||
<B5DropdownItem
|
||||
<BS5DropdownItem
|
||||
active={active}
|
||||
className={description ? 'dropdown-item-description-container' : ''}
|
||||
role="menuitem"
|
||||
|
@ -51,19 +51,19 @@ export function DropdownItem({
|
|||
{description && (
|
||||
<span className="dropdown-item-description">{description}</span>
|
||||
)}
|
||||
</B5DropdownItem>
|
||||
</BS5DropdownItem>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export function DropdownToggle({ ...props }: DropdownToggleProps) {
|
||||
return <B5DropdownToggle {...props} />
|
||||
return <BS5DropdownToggle {...props} />
|
||||
}
|
||||
|
||||
export function DropdownMenu({ as = 'ul', ...props }: DropdownMenuProps) {
|
||||
return <B5DropdownMenu as={as} role="menubar" {...props} />
|
||||
return <BS5DropdownMenu as={as} role="menubar" {...props} />
|
||||
}
|
||||
|
||||
export function DropdownDivider() {
|
||||
return <B5DropdownDivider aria-hidden="true" />
|
||||
return <BS5DropdownDivider aria-hidden="true" />
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import classNames from 'classnames'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import Button from './button'
|
||||
import type { IconButtonProps } from '@/features/ui/components/types/icon-button-props'
|
||||
import classNames from 'classnames'
|
||||
|
||||
export default function IconButton({
|
||||
accessibilityLabel,
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import { IconTextButtonProps } from '../types/icon-text-button-props'
|
||||
import Button from './button'
|
||||
|
||||
export default function IconTextButton({
|
||||
children,
|
||||
className,
|
||||
leadingIcon,
|
||||
size = 'default',
|
||||
trailingIcon,
|
||||
...props
|
||||
}: IconTextButtonProps) {
|
||||
const materialIconClassName = size === 'large' ? 'icon-large' : 'icon-small'
|
||||
|
||||
return (
|
||||
<Button size={size} {...props}>
|
||||
{leadingIcon && (
|
||||
<MaterialIcon type={leadingIcon} className={materialIconClassName} />
|
||||
)}
|
||||
{children}
|
||||
{trailingIcon && (
|
||||
<MaterialIcon type={trailingIcon} className={materialIconClassName} />
|
||||
)}
|
||||
</Button>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import BootstrapVersionSwitcher from '../bootstrap-version-switcher'
|
||||
import { Button as BS3Button } from 'react-bootstrap'
|
||||
import type { ButtonProps } from '@/features/ui/components/types/button-props'
|
||||
import type { ButtonProps as BS3ButtonPropsBase } from 'react-bootstrap'
|
||||
import Button from '../button'
|
||||
|
||||
export type ButtonWrapperProps = ButtonProps & {
|
||||
bs3Props?: {
|
||||
bsStyle?: string | null
|
||||
loading?: React.ReactNode
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve type mismatch of the onClick event handler
|
||||
export type BS3ButtonProps = Omit<BS3ButtonPropsBase, 'onClick'> & {
|
||||
onClick?: React.MouseEventHandler<any>
|
||||
}
|
||||
|
||||
// maps Bootstrap 5 sizes to Bootstrap 3 sizes
|
||||
export const mapBsButtonSizes = (
|
||||
size: ButtonProps['size']
|
||||
): 'sm' | 'lg' | undefined =>
|
||||
size === 'small' ? 'sm' : size === 'large' ? 'lg' : undefined
|
||||
|
||||
export default function ButtonWrapper(props: ButtonWrapperProps) {
|
||||
const { bs3Props, ...rest } = props
|
||||
|
||||
const bs3ButtonProps: BS3ButtonProps = {
|
||||
bsStyle: rest.variant,
|
||||
bsSize: mapBsButtonSizes(rest.size),
|
||||
className: rest.className,
|
||||
disabled: rest.isLoading || rest.disabled,
|
||||
form: rest.form,
|
||||
onClick: rest.onClick,
|
||||
type: rest.type,
|
||||
...bs3Props,
|
||||
}
|
||||
|
||||
return (
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={
|
||||
<BS3Button {...bs3ButtonProps}>
|
||||
{bs3Props?.loading || rest.children}
|
||||
</BS3Button>
|
||||
}
|
||||
bs5={<Button {...rest} />}
|
||||
/>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { BS3ButtonProps, mapBsButtonSizes } from './button-wrapper'
|
||||
import { Button as BS3Button } from 'react-bootstrap'
|
||||
import type { IconButtonProps } from '@/features/ui/components/types/icon-button-props'
|
||||
import BootstrapVersionSwitcher from '../bootstrap-version-switcher'
|
||||
import Icon, { IconProps } from '@/shared/components/icon'
|
||||
import IconButton from '../icon-button'
|
||||
|
||||
export type IconButtonWrapperProps = IconButtonProps & {
|
||||
bs3Props?: {
|
||||
loading?: React.ReactNode
|
||||
fw?: IconProps['fw']
|
||||
}
|
||||
}
|
||||
|
||||
export default function IconButtonWrapper(props: IconButtonWrapperProps) {
|
||||
const { bs3Props, ...rest } = props
|
||||
|
||||
const { fw, ...filterBs3Props } = bs3Props || {}
|
||||
|
||||
const bs3ButtonProps: BS3ButtonProps = {
|
||||
bsStyle: rest.variant,
|
||||
bsSize: mapBsButtonSizes(rest.size),
|
||||
disabled: rest.isLoading || rest.disabled,
|
||||
form: rest.form,
|
||||
onClick: rest.onClick,
|
||||
type: rest.type,
|
||||
...filterBs3Props,
|
||||
}
|
||||
|
||||
return (
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={
|
||||
<BS3Button {...bs3ButtonProps}>
|
||||
{bs3Props?.loading}
|
||||
<Icon
|
||||
type={rest.icon}
|
||||
fw={fw}
|
||||
accessibilityLabel={rest.accessibilityLabel}
|
||||
/>
|
||||
</BS3Button>
|
||||
}
|
||||
bs5={<IconButton {...rest} />}
|
||||
/>
|
||||
)
|
||||
}
|
|
@ -4,10 +4,13 @@ export type ButtonProps = {
|
|||
children?: ReactNode
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
form?: string
|
||||
leadingIcon?: string
|
||||
href?: string
|
||||
isLoading?: boolean
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>
|
||||
size?: 'small' | 'default' | 'large'
|
||||
trailingIcon?: string
|
||||
type?: 'button' | 'reset' | 'submit'
|
||||
variant?:
|
||||
| 'primary'
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import { ButtonProps } from './button-props'
|
||||
|
||||
export type IconTextButtonProps = ButtonProps & {
|
||||
leadingIcon?: string
|
||||
trailingIcon?: string
|
||||
}
|
|
@ -2,6 +2,6 @@ import getMeta from '@/utils/meta'
|
|||
|
||||
export const isBootstrap5 = getMeta('ol-bootstrapVersion') === 5
|
||||
|
||||
export const bsClassName = ({ bs5, bs3 }: { bs5: string; bs3: string }) => {
|
||||
export const bsVersion = ({ bs5, bs3 }: { bs5?: string; bs3?: string }) => {
|
||||
return isBootstrap5 ? bs5 : bs3
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'classnames'
|
||||
import { bsClassName } from '@/features/utils/bootstrap-5'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
|
||||
type IconOwnProps = {
|
||||
type: string
|
||||
|
@ -36,9 +36,7 @@ function Icon({
|
|||
<>
|
||||
<i className={iconClassName} aria-hidden="true" {...rest} />
|
||||
{accessibilityLabel && (
|
||||
<span
|
||||
className={bsClassName({ bs5: 'visually-hidden', bs3: 'sr-only' })}
|
||||
>
|
||||
<span className={bsVersion({ bs5: 'visually-hidden', bs3: 'sr-only' })}>
|
||||
{accessibilityLabel}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
@ -7,6 +7,18 @@ export const NewButton = (args: Args) => {
|
|||
return <Button {...args} />
|
||||
}
|
||||
|
||||
export const ButtonWithLeadingIcon = (args: Args) => {
|
||||
return <Button leadingIcon="add" {...args} />
|
||||
}
|
||||
|
||||
export const ButtonWithTrailingIcon = (args: Args) => {
|
||||
return <Button trailingIcon="add" {...args} />
|
||||
}
|
||||
|
||||
export const ButtonWithIcons = (args: Args) => {
|
||||
return <Button trailingIcon="add" leadingIcon="add" {...args} />
|
||||
}
|
||||
|
||||
const meta: Meta<typeof Button> = {
|
||||
title: 'Shared / Components / Bootstrap 5 / Button',
|
||||
component: Button,
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import IconTextButton from '@/features/ui/components/bootstrap-5/icon-text-button'
|
||||
import { Meta } from '@storybook/react'
|
||||
|
||||
type Args = React.ComponentProps<typeof IconTextButton>
|
||||
|
||||
export const IconText = (args: Args) => {
|
||||
return <IconTextButton {...args} />
|
||||
}
|
||||
|
||||
const meta: Meta<typeof IconTextButton> = {
|
||||
title: 'Shared / Components / Bootstrap 5 / IconTextButton',
|
||||
component: IconTextButton,
|
||||
args: {
|
||||
children: 'IconTextButton',
|
||||
disabled: false,
|
||||
isLoading: false,
|
||||
leadingIcon: 'add',
|
||||
trailingIcon: 'expand_more',
|
||||
},
|
||||
argTypes: {
|
||||
size: {
|
||||
control: 'radio',
|
||||
options: ['small', 'default', 'large'],
|
||||
},
|
||||
variant: {
|
||||
control: 'radio',
|
||||
options: [
|
||||
'primary',
|
||||
'secondary',
|
||||
'ghost',
|
||||
'danger',
|
||||
'danger-ghost',
|
||||
'premium',
|
||||
],
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
bootstrap5: true,
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
Loading…
Reference in a new issue