feat: finalize button component with tests (#224)
* feat: finalize button component with tests * fix: import * fix: import
This commit is contained in:
parent
999d936f85
commit
ee20c2fde2
133
__tests__/components/Button/Button.test.tsx
Normal file
133
__tests__/components/Button/Button.test.tsx
Normal file
@ -0,0 +1,133 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import {
|
||||
buttonColorClasses,
|
||||
buttonSizeClasses,
|
||||
buttonVariantClasses,
|
||||
focusClasses,
|
||||
} from 'components/Button/constants'
|
||||
|
||||
describe('<Button />', () => {
|
||||
afterAll(() => {
|
||||
jest.resetAllMocks()
|
||||
})
|
||||
|
||||
it('should render', () => {
|
||||
const { container } = render(<Button />)
|
||||
expect(container).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render `children` when its passed', () => {
|
||||
const children = <span data-testid='test-id'>Hello World!</span>
|
||||
const { getByTestId } = render(<Button>{children}</Button>)
|
||||
|
||||
expect(getByTestId('test-id')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle `className` prop correctly', () => {
|
||||
const testClass = 'test-class'
|
||||
const { container } = render(<Button className={testClass} />)
|
||||
|
||||
expect(container.querySelector('button')).toHaveClass(testClass)
|
||||
})
|
||||
|
||||
it('should handle `color` prop correctly', () => {
|
||||
const colors = Object.keys(buttonColorClasses) as [keyof typeof buttonColorClasses]
|
||||
|
||||
colors.forEach((color) => {
|
||||
const { container } = render(<Button color={color} />)
|
||||
|
||||
expect(container.querySelector('button')).toHaveClass(buttonColorClasses[color])
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle `disabled=true` prop correctly', () => {
|
||||
const testFunction = jest.fn()
|
||||
const { container } = render(<Button disabled={true} onClick={testFunction} />)
|
||||
const button = container.querySelector('button')
|
||||
|
||||
button?.click()
|
||||
|
||||
expect(button).toHaveClass('pointer-events-none')
|
||||
expect(testFunction).not.toBeCalled()
|
||||
})
|
||||
|
||||
it('should handle `disabled=false` prop correctly', () => {
|
||||
const testFunction = jest.fn()
|
||||
const { container } = render(<Button disabled={false} onClick={testFunction} />)
|
||||
const button = container.querySelector('button')
|
||||
|
||||
button?.click()
|
||||
|
||||
expect(button).not.toHaveClass('pointer-events-none')
|
||||
expect(testFunction).toBeCalled()
|
||||
})
|
||||
|
||||
it('should show progress indicator when `showProgressIndicator=true`', () => {
|
||||
const { getByTestId } = render(<Button showProgressIndicator={true} />)
|
||||
|
||||
expect(getByTestId('circular-progress-component')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle `size` prop correctly', () => {
|
||||
const sizes = Object.keys(buttonSizeClasses) as [keyof typeof buttonSizeClasses]
|
||||
|
||||
sizes.forEach((size) => {
|
||||
const { container } = render(<Button size={size} />)
|
||||
|
||||
expect(container.querySelector('button')).toHaveClass(buttonSizeClasses[size])
|
||||
})
|
||||
})
|
||||
|
||||
it('should show `text` when its passed', () => {
|
||||
const text = 'Hello!'
|
||||
const { getByText } = render(<Button text={text} />)
|
||||
|
||||
expect(getByText(text)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle `variant` prop correctly', () => {
|
||||
const variants = Object.keys(buttonVariantClasses) as [keyof typeof buttonVariantClasses]
|
||||
|
||||
variants.forEach((variant) => {
|
||||
const { container } = render(<Button variant={variant} />)
|
||||
|
||||
expect(container.querySelector('button')).toHaveClass(buttonVariantClasses[variant])
|
||||
})
|
||||
})
|
||||
|
||||
it('should show left icon when `leftIcon` prop is passed', () => {
|
||||
const icon = <span data-testid='left-icon'>this is the left icon</span>
|
||||
const { getByTestId } = render(<Button leftIcon={icon} />)
|
||||
|
||||
expect(getByTestId('left-icon')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show right icon when `rightIcon` prop is passed', () => {
|
||||
const icon = <span data-testid='right-icon'>this is the right icon</span>
|
||||
const { getByTestId } = render(<Button rightIcon={icon} />)
|
||||
|
||||
expect(getByTestId('right-icon')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle `iconClassName` prop correctly', () => {
|
||||
const icon = <span data-testid='icon'>just an icon</span>
|
||||
const { getByTestId } = render(<Button rightIcon={icon} iconClassName='test-icon-class' />)
|
||||
|
||||
expect(getByTestId('icon').parentElement).toHaveClass('test-icon-class')
|
||||
})
|
||||
|
||||
it('should show submenu indicator when `hasSubmenu=true`', () => {
|
||||
const { getByTestId } = render(<Button hasSubmenu={true} />)
|
||||
|
||||
expect(getByTestId('button-submenu-indicator')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should set focus classes when `hasFocus=true`', () => {
|
||||
const { container } = render(<Button hasFocus={true} color='primary' />)
|
||||
const button = container.querySelector('button')
|
||||
|
||||
expect(button).toHaveClass(focusClasses['primary'])
|
||||
})
|
||||
})
|
@ -9,6 +9,7 @@ module.exports = {
|
||||
'!<rootDir>/.next/**',
|
||||
'!<rootDir>/*.config.js',
|
||||
'!<rootDir>/coverage/**',
|
||||
'!<rootDir>/src/types/**',
|
||||
],
|
||||
moduleNameMapper: {
|
||||
// Handle CSS imports (with CSS modules)
|
||||
|
@ -3,7 +3,7 @@ import { useEffect } from 'react'
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||
|
||||
import AccountStats from 'components/Account/AccountStats'
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import { ArrowCircledTopRight, ArrowDownLine, ArrowUpLine, TrashBin } from 'components/Icons'
|
||||
import Radio from 'components/Radio'
|
||||
|
@ -5,7 +5,7 @@ import { useNavigate, useParams } from 'react-router-dom'
|
||||
import AccountList from 'components/Account/AccountList'
|
||||
import CreateAccount from 'components/Account/CreateAccount'
|
||||
import FundAccount from 'components/Account/FundAccount'
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import { Account, Plus, PlusCircled } from 'components/Icons'
|
||||
import Overlay from 'components/Overlay'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
|
||||
|
@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import { ArrowRight, Cross } from 'components/Icons'
|
||||
import SwitchWithLabel from 'components/SwitchWithLabel'
|
||||
import Text from 'components/Text'
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import useStore from 'store'
|
||||
import { getMarketAssets } from 'utils/assets'
|
||||
|
||||
|
@ -1,205 +0,0 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { LegacyRef, ReactElement, ReactNode } from 'react'
|
||||
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import { ChevronDown } from 'components/Icons'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
children?: string | ReactNode
|
||||
className?: string
|
||||
color?: 'primary' | 'secondary' | 'tertiary' | 'quaternary'
|
||||
disabled?: boolean
|
||||
id?: string
|
||||
showProgressIndicator?: boolean
|
||||
size?: 'small' | 'medium' | 'large'
|
||||
text?: string | ReactNode
|
||||
variant?: 'solid' | 'transparent' | 'round'
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||
leftIcon?: ReactElement
|
||||
rightIcon?: ReactElement
|
||||
iconClassName?: string
|
||||
hasSubmenu?: boolean
|
||||
hasFocus?: boolean
|
||||
}
|
||||
|
||||
export const buttonColorClasses = {
|
||||
primary:
|
||||
'font-bold gradient-primary-to-secondary hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||
secondary:
|
||||
'border border-white/30 bg-transparent hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||
tertiary: 'bg-white/10 hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||
quaternary: 'bg-transparent text-white/60 hover:text-white ctive:text-white',
|
||||
}
|
||||
|
||||
const focusClasses = {
|
||||
primary: 'bg-white/20',
|
||||
secondary: 'bg-white/20',
|
||||
tertiary: 'bg-white/20',
|
||||
quaternary: 'text-white',
|
||||
}
|
||||
|
||||
const buttonBorderClasses =
|
||||
'before:content-[" "] before:absolute before:inset-0 before:rounded-sm before:p-[1px] before:border-glas before:-z-1'
|
||||
|
||||
const buttonGradientClasses = [
|
||||
'before:content-[" "] before:absolute before:inset-0 before:rounded-sm before:-z-1 before:opacity-0',
|
||||
'before:gradient-secondary-to-primary before:transition-opacity before:ease-in',
|
||||
'hover:before:opacity-100',
|
||||
]
|
||||
|
||||
const buttonTransparentColorClasses = {
|
||||
primary: 'hover:text-primary active:text-primary focus:text-primary',
|
||||
secondary: 'hover:text-secondary active:text-secondary focus:text-secondary',
|
||||
tertiary: 'hover:text-white/80 active:text-white/80 focus:text-white/80',
|
||||
quaternary: 'text-white/60 hover:text-white active:text-white',
|
||||
}
|
||||
|
||||
const buttonRoundSizeClasses = {
|
||||
small: 'h-[32px] w-[32px]',
|
||||
medium: 'h-[40px] w-[40px]',
|
||||
large: 'h-[56px] w-[56px]',
|
||||
}
|
||||
|
||||
export const buttonSizeClasses = {
|
||||
small: 'text-sm',
|
||||
medium: 'text-base',
|
||||
large: 'text-lg',
|
||||
}
|
||||
|
||||
export const buttonPaddingClasses = {
|
||||
small: 'px-2.5 py-1.5 min-h-[32px]',
|
||||
medium: 'px-3 py-2 min-h-[40px]',
|
||||
large: 'px-3.5 py-2.5 min-h-[56px]',
|
||||
}
|
||||
|
||||
export const buttonVariantClasses = {
|
||||
solid: 'rounded-sm text-white shadow-button justify-center group',
|
||||
transparent: 'rounded-sm bg-transparent p-0 transition duration-200 ease-in',
|
||||
round: 'rounded-full p-0',
|
||||
}
|
||||
|
||||
function glowElement(enableAnimations: boolean) {
|
||||
return (
|
||||
<svg
|
||||
className={classNames(
|
||||
enableAnimations && 'group-hover:animate-glow group-focus:animate-glow',
|
||||
'glow-container isolate opacity-0',
|
||||
'pointer-events-none absolute inset-0 h-full w-full',
|
||||
)}
|
||||
>
|
||||
<rect
|
||||
pathLength='100'
|
||||
strokeLinecap='round'
|
||||
width='100%'
|
||||
height='100%'
|
||||
rx='4'
|
||||
className='absolute glow-line group-hover:glow-hover group-focus:glow-hover'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const Button = React.forwardRef(function Button(
|
||||
{
|
||||
children,
|
||||
className = '',
|
||||
color = 'primary',
|
||||
disabled,
|
||||
id = '',
|
||||
showProgressIndicator,
|
||||
size = 'small',
|
||||
text,
|
||||
variant = 'solid',
|
||||
onClick,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
iconClassName,
|
||||
hasSubmenu,
|
||||
hasFocus,
|
||||
}: Props,
|
||||
ref,
|
||||
) {
|
||||
const buttonClasses = []
|
||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||
const isDisabled = disabled || showProgressIndicator
|
||||
|
||||
switch (variant) {
|
||||
case 'round':
|
||||
buttonClasses.push(
|
||||
buttonSizeClasses[size],
|
||||
buttonRoundSizeClasses[size],
|
||||
buttonPaddingClasses[size],
|
||||
buttonColorClasses[color],
|
||||
)
|
||||
break
|
||||
|
||||
case 'transparent':
|
||||
buttonClasses.push(buttonSizeClasses[size], buttonTransparentColorClasses[color])
|
||||
break
|
||||
|
||||
case 'solid':
|
||||
buttonClasses.push(
|
||||
buttonSizeClasses[size],
|
||||
buttonPaddingClasses[size],
|
||||
buttonColorClasses[color],
|
||||
)
|
||||
break
|
||||
default:
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classNames(
|
||||
'relative z-1 flex items-center',
|
||||
'cursor-pointer appearance-none break-normal outline-none',
|
||||
'text-white transition-all',
|
||||
enableAnimations && 'transition-color',
|
||||
buttonClasses,
|
||||
buttonVariantClasses[variant],
|
||||
variant === 'solid' && color === 'tertiary' && buttonBorderClasses,
|
||||
variant === 'solid' && color === 'primary' && buttonGradientClasses,
|
||||
isDisabled && 'pointer-events-none opacity-50',
|
||||
hasFocus && focusClasses[color],
|
||||
className,
|
||||
)}
|
||||
id={id}
|
||||
ref={ref as LegacyRef<HTMLButtonElement>}
|
||||
onClick={isDisabled ? () => {} : onClick}
|
||||
>
|
||||
{leftIcon && !showProgressIndicator && (
|
||||
<span
|
||||
className={classNames(
|
||||
'flex items-center justify-center',
|
||||
(text || children) && 'mr-2',
|
||||
iconClassName ?? 'h-4 w-4',
|
||||
)}
|
||||
>
|
||||
{leftIcon}
|
||||
</span>
|
||||
)}
|
||||
{text && !children && !showProgressIndicator && <span>{text}</span>}
|
||||
{children && !showProgressIndicator && children}
|
||||
{rightIcon && !showProgressIndicator && (
|
||||
<span
|
||||
className={classNames(
|
||||
'flex items-center justify-center',
|
||||
(text || children) && 'ml-2',
|
||||
iconClassName ?? 'h-4 w-4',
|
||||
)}
|
||||
>
|
||||
{rightIcon}
|
||||
</span>
|
||||
)}
|
||||
{hasSubmenu && !showProgressIndicator && (
|
||||
<span className='ml-2 inline-block w-2.5'>
|
||||
<ChevronDown />
|
||||
</span>
|
||||
)}
|
||||
{variant === 'solid' && !isDisabled && glowElement(enableAnimations)}
|
||||
{showProgressIndicator && (
|
||||
<CircularProgress size={size === 'small' ? 10 : size === 'medium' ? 12 : 18} />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
})
|
55
src/components/Button/constants.ts
Normal file
55
src/components/Button/constants.ts
Normal file
@ -0,0 +1,55 @@
|
||||
export const buttonColorClasses = {
|
||||
primary:
|
||||
'font-bold gradient-primary-to-secondary hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||
secondary:
|
||||
'border border-white/30 bg-transparent hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||
tertiary: 'bg-white/10 hover:bg-white/20 active:bg-white/40 focus:bg-white/20',
|
||||
quaternary: 'bg-transparent text-white/60 hover:text-white active:text-white',
|
||||
}
|
||||
|
||||
export const focusClasses = {
|
||||
primary: 'bg-white/20',
|
||||
secondary: 'bg-white/20',
|
||||
tertiary: 'bg-white/20',
|
||||
quaternary: 'text-white',
|
||||
}
|
||||
|
||||
export const buttonBorderClasses =
|
||||
'before:content-[" "] before:absolute before:inset-0 before:rounded-sm before:p-[1px] before:border-glas before:-z-1'
|
||||
|
||||
export const buttonGradientClasses = [
|
||||
'before:content-[" "] before:absolute before:inset-0 before:rounded-sm before:-z-1 before:opacity-0',
|
||||
'before:gradient-secondary-to-primary before:transition-opacity before:ease-in',
|
||||
'hover:before:opacity-100',
|
||||
]
|
||||
|
||||
export const buttonTransparentColorClasses = {
|
||||
primary: 'hover:text-primary active:text-primary focus:text-primary',
|
||||
secondary: 'hover:text-secondary active:text-secondary focus:text-secondary',
|
||||
tertiary: 'hover:text-white/80 active:text-white/80 focus:text-white/80',
|
||||
quaternary: 'text-white/60 hover:text-white active:text-white',
|
||||
}
|
||||
|
||||
export const buttonRoundSizeClasses = {
|
||||
small: 'h-[32px] w-[32px]',
|
||||
medium: 'h-[40px] w-[40px]',
|
||||
large: 'h-[56px] w-[56px]',
|
||||
}
|
||||
|
||||
export const buttonSizeClasses = {
|
||||
small: 'text-sm',
|
||||
medium: 'text-base',
|
||||
large: 'text-lg',
|
||||
}
|
||||
|
||||
export const buttonPaddingClasses = {
|
||||
small: 'px-2.5 py-1.5 min-h-[32px]',
|
||||
medium: 'px-3 py-2 min-h-[40px]',
|
||||
large: 'px-3.5 py-2.5 min-h-[56px]',
|
||||
}
|
||||
|
||||
export const buttonVariantClasses = {
|
||||
solid: 'rounded-sm text-white shadow-button justify-center group',
|
||||
transparent: 'rounded-sm bg-transparent p-0 transition duration-200 ease-in',
|
||||
round: 'rounded-full p-0',
|
||||
}
|
127
src/components/Button/index.tsx
Normal file
127
src/components/Button/index.tsx
Normal file
@ -0,0 +1,127 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { LegacyRef, ReactElement, ReactNode, useMemo } from 'react'
|
||||
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import {
|
||||
buttonBorderClasses,
|
||||
buttonColorClasses,
|
||||
buttonGradientClasses,
|
||||
buttonPaddingClasses,
|
||||
buttonRoundSizeClasses,
|
||||
buttonSizeClasses,
|
||||
buttonTransparentColorClasses,
|
||||
buttonVariantClasses,
|
||||
focusClasses,
|
||||
} from 'components/Button/constants'
|
||||
import { glowElement } from 'components/Button/utils'
|
||||
import { ChevronDown } from 'components/Icons'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
children?: string | ReactNode
|
||||
className?: string
|
||||
color?: 'primary' | 'secondary' | 'tertiary' | 'quaternary'
|
||||
disabled?: boolean
|
||||
id?: string
|
||||
showProgressIndicator?: boolean
|
||||
size?: 'small' | 'medium' | 'large'
|
||||
text?: string | ReactNode
|
||||
variant?: 'solid' | 'transparent' | 'round'
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
|
||||
leftIcon?: ReactElement
|
||||
rightIcon?: ReactElement
|
||||
iconClassName?: string
|
||||
hasSubmenu?: boolean
|
||||
hasFocus?: boolean
|
||||
}
|
||||
|
||||
const Button = React.forwardRef(function Button(
|
||||
{
|
||||
children,
|
||||
className = '',
|
||||
color = 'primary',
|
||||
disabled,
|
||||
id = '',
|
||||
showProgressIndicator,
|
||||
size = 'small',
|
||||
text,
|
||||
variant = 'solid',
|
||||
onClick,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
iconClassName,
|
||||
hasSubmenu,
|
||||
hasFocus,
|
||||
}: Props,
|
||||
ref,
|
||||
) {
|
||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||
const isDisabled = disabled || showProgressIndicator
|
||||
const shouldShowText = text && !children
|
||||
const shouldShowGlowElement = variant === 'solid' && !isDisabled
|
||||
|
||||
const buttonClassNames = useMemo(() => {
|
||||
const buttonClasses = [
|
||||
buttonSizeClasses[size],
|
||||
buttonPaddingClasses[size],
|
||||
buttonColorClasses[color],
|
||||
]
|
||||
|
||||
if (variant === 'round') {
|
||||
buttonClasses.push(buttonColorClasses[color], buttonRoundSizeClasses[size])
|
||||
} else if (variant === 'transparent') {
|
||||
buttonClasses.push(buttonTransparentColorClasses[color])
|
||||
}
|
||||
|
||||
return classNames(
|
||||
'relative z-1 flex items-center',
|
||||
'cursor-pointer appearance-none break-normal outline-none',
|
||||
'text-white transition-all',
|
||||
enableAnimations && 'transition-color',
|
||||
buttonClasses,
|
||||
buttonVariantClasses[variant],
|
||||
variant === 'solid' && color === 'tertiary' && buttonBorderClasses,
|
||||
variant === 'solid' && color === 'primary' && buttonGradientClasses,
|
||||
isDisabled && 'pointer-events-none opacity-50',
|
||||
hasFocus && focusClasses[color],
|
||||
className,
|
||||
)
|
||||
}, [className, color, enableAnimations, hasFocus, isDisabled, size, variant])
|
||||
|
||||
const [leftIconClassNames, rightIconClassNames] = useMemo(() => {
|
||||
const hasContent = !!(text || children)
|
||||
const iconClasses = ['flex items-center justify-center', iconClassName ?? 'h-4 w-4']
|
||||
const leftIconClasses = [iconClasses, hasContent && 'mr-2']
|
||||
const rightIconClasses = [iconClasses, hasContent && 'ml-2']
|
||||
|
||||
return [leftIconClasses, rightIconClasses].map(classNames)
|
||||
}, [children, iconClassName, text])
|
||||
|
||||
return (
|
||||
<button
|
||||
className={buttonClassNames}
|
||||
id={id}
|
||||
ref={ref as LegacyRef<HTMLButtonElement>}
|
||||
onClick={isDisabled ? () => {} : onClick}
|
||||
>
|
||||
{showProgressIndicator ? (
|
||||
<CircularProgress size={size === 'small' ? 10 : size === 'medium' ? 12 : 18} />
|
||||
) : (
|
||||
<>
|
||||
{leftIcon && <span className={leftIconClassNames}>{leftIcon}</span>}
|
||||
{shouldShowText && <span>{text}</span>}
|
||||
{children && children}
|
||||
{rightIcon && <span className={rightIconClassNames}>{rightIcon}</span>}
|
||||
{hasSubmenu && (
|
||||
<span data-testid='button-submenu-indicator' className='ml-2 inline-block w-2.5'>
|
||||
<ChevronDown />
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{shouldShowGlowElement && glowElement(enableAnimations)}
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
export default Button
|
22
src/components/Button/utils.tsx
Normal file
22
src/components/Button/utils.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
export function glowElement(enableAnimations: boolean) {
|
||||
return (
|
||||
<svg
|
||||
className={classNames(
|
||||
enableAnimations && 'group-hover:animate-glow group-focus:animate-glow',
|
||||
'glow-container isolate opacity-0',
|
||||
'pointer-events-none absolute inset-0 h-full w-full',
|
||||
)}
|
||||
>
|
||||
<rect
|
||||
pathLength='100'
|
||||
strokeLinecap='round'
|
||||
width='100%'
|
||||
height='100%'
|
||||
rx='4'
|
||||
className='absolute glow-line group-hover:glow-hover group-focus:glow-hover'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
@ -31,7 +31,11 @@ export const CircularProgress = ({ color = '#FFFFFF', size = 20, className }: Pr
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={loaderClasses} style={{ width: `${size}px`, height: `${size}px` }}>
|
||||
<div
|
||||
data-testid='circular-progress-component'
|
||||
className={loaderClasses}
|
||||
style={{ width: `${size}px`, height: `${size}px` }}
|
||||
>
|
||||
<div
|
||||
className={elementClasses}
|
||||
style={{
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import VaultLogo from 'components/Earn/vault/VaultLogo'
|
||||
import Text from 'components/Text'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
import { ReactNode, useEffect, useRef } from 'react'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import { Cross } from 'components/Icons'
|
||||
|
||||
|
@ -2,7 +2,7 @@ import Image from 'next/image'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import AccountSummary from 'components/Account/AccountSummary'
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import Divider from 'components/Divider'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
|
@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'
|
||||
import { useState } from 'react'
|
||||
|
||||
import AccountSummary from 'components/Account/AccountSummary'
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import Divider from 'components/Divider'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import Divider from 'components/Divider'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import { Gear } from 'components/Icons'
|
||||
import Overlay from 'components/Overlay'
|
||||
import Switch from 'components/Switch'
|
||||
|
@ -3,7 +3,7 @@ import { toast as createToast, Slide, ToastContainer } from 'react-toastify'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { mutate } from 'swr'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import { CheckCircled, Cross, CrossCircled } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import useStore from 'store'
|
||||
|
@ -11,7 +11,7 @@ import { ASSETS } from 'constants/assets'
|
||||
import useStore from 'store'
|
||||
import { BN } from 'utils/helpers'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
|
||||
interface Props {
|
||||
amount: BigNumber
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import { Wallet } from 'components/Icons'
|
||||
|
||||
|
@ -9,7 +9,7 @@ import classNames from 'classnames'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import useClipboard from 'react-use-clipboard'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { Check, Copy, ExternalLink, Osmo } from 'components/Icons'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ChainInfoID, WalletManagerProvider } from '@marsprotocol/wallet-connector'
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import Button from 'components/Button'
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import { Cross } from 'components/Icons'
|
||||
import { ENV } from 'constants/env'
|
||||
|
@ -268,10 +268,10 @@ module.exports = {
|
||||
'linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 100%), linear-gradient(0deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05))',
|
||||
},
|
||||
'.gradient-primary-to-secondary': {
|
||||
background: 'linear-gradient(90deg, #FF625E 0%, #FB9562 100%)',
|
||||
background: 'linear-gradient(180deg, #7F78E8 0%, #926AC8 100%)',
|
||||
},
|
||||
'.gradient-secondary-to-primary': {
|
||||
background: 'linear-gradient(90deg, #FB9562 0%, #FF625E 100%)',
|
||||
background: 'linear-gradient(180deg, #926AC8 100%, #7F78E8 0%)',
|
||||
},
|
||||
'.gradient-tooltip': {
|
||||
background:
|
||||
|
Loading…
Reference in New Issue
Block a user