All files / app/src/shared/ui/button button.tsx

100% Statements 76/76
100% Branches 22/22
100% Functions 0/0
100% Lines 76/76

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 1021x                                         1x 1x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x     323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x 323x   323x 323x 323x 323x 323x 323x 323x 323x 323x   323x 323x 323x 323x   323x 1x  
import { ButtonHTMLAttributes, forwardRef } from 'react'
import cn from 'classnames'
import Loading from '@/shared/assets/icons/common/loading.svg'
import { OptionalLinkWrapper } from '@/shared/lib'
 
export type Variant = 'contained' | 'outlined' | 'text' | 'icon' | 'border-icon'
type Color = 'primary' | 'secondary'
export type Size = 'small' | 'medium' | 'large'
 
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: Variant
  size?: Size
  color?: Color
  fullWidth?: boolean
  loading?: boolean
  className?: string
  childrenClassName?: string
  href?: string
  newTab?: boolean
}
 
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      children,
      variant = 'contained',
      color = 'primary',
      size = 'medium',
      fullWidth,
      type = 'button',
      className,
      childrenClassName,
      loading,
      ...rest
    },
    ref
  ) => {
    return (
      <OptionalLinkWrapper {...rest} href={rest.href}>
        <button
          ref={ref}
          className={cn(
            'group relative flex items-center justify-center rounded-base disabled:cursor-not-allowed transition-colors',
            {
              'disabled:bg-white disabled:text-background-primary disabled:border-border': variant !== 'contained',
              'disabled:bg-background-secondary disabled:text-background-primary disabled:border-background-secondary':
                variant === 'contained',
              'bg-main-secondary text-main border border-main hover:bg-main hover:text-white active:bg-red active:border-red':
                color === 'primary' && (variant === 'outlined' || variant === 'border-icon'),
              'bg-background-quaternary text-black border border-background-primary hover:bg-border active:bg-background-primary':
                color === 'secondary' && (variant === 'outlined' || variant === 'border-icon'),
              'bg-main text-white border border-main hover:bg-main-hover hover:border-main-hover active:bg-red active:border-red':
                variant === 'contained',
              'text-main active:text-red': color === 'primary' && variant === 'text',
              'disabled:bg-transparent text-secondary-secondary hover:text-black':
                (color === 'secondary' && variant === 'text') || variant === 'icon',
              'p-px': size === 'small' && variant === 'border-icon',
              'p-3': size === 'medium' && variant === 'border-icon',
              'px-base py-[10.5px]': size === 'medium' && (variant === 'contained' || variant === 'outlined'),
              'px-10 py-[13.5px]': size === 'large' && (variant === 'contained' || variant === 'outlined'),
              'p-0': variant === 'text',
              'w-full': fullWidth,
              'w-fit': !fullWidth,
              'pointer-events-none': loading,
            },
            className
          )}
          type={type}
          {...rest}
        >
          {
            <Loading
              data-testid='loading-button-icon'
              className={cn('absolute animate-spin group-disabled:fill-background-primary', {
                'fill-main': color === 'primary' && variant !== 'contained',
                'fill-white': color === 'primary' && variant === 'contained',
                'fill-black': color === 'secondary' && (variant === 'outlined' || variant === 'border-icon'),
                'fill-secondary-hover': (color === 'secondary' && variant === 'text') || variant === 'icon',
                'h-[29px]': (size === 'large' || size === 'medium') && variant !== 'icon',
                'h-4': size === 'small' || variant === 'icon',
                hidden: !loading,
              })}
            />
          }
          <span
            data-testid='button-children-wrapper'
            className={cn(
              'no-underline transition-opacity duration-100',
              {
                'opacity-0': loading,
              },
              childrenClassName
            )}
          >
            {children}
          </span>
        </button>
      </OptionalLinkWrapper>
    )
  }
)