chore: initial commit
This commit is contained in:
17
components/design/Avatar.tsx
Normal file
17
components/design/Avatar.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import Image, { ImageProps } from 'next/image'
|
||||
|
||||
export const Avatar: React.FC<ImageProps> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<Image {...props} className='avatar-image' />
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
:global(.avatar-image) {
|
||||
border-radius: 50%;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
40
components/design/Button.tsx
Normal file
40
components/design/Button.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { forwardRef } from 'react'
|
||||
|
||||
interface ButtonProps extends React.ComponentPropsWithRef<'button'> {}
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(props, ref) => {
|
||||
const { children, ...rest } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<button ref={ref} {...rest} className='button'>
|
||||
{children}
|
||||
</button>
|
||||
|
||||
<style jsx>{`
|
||||
.button {
|
||||
cursor: pointer;
|
||||
font-size: var(--default-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.8px;
|
||||
padding: 1rem 2rem;
|
||||
transform: translateY(-3px);
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--color-primary);
|
||||
border-radius: 10px;
|
||||
transition: all 0.3s ease-in;
|
||||
color: var(--color-primary);
|
||||
outline: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
||||
)
|
24
components/design/Container.tsx
Normal file
24
components/design/Container.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
interface ContainerProps extends React.ComponentPropsWithRef<'div'> {}
|
||||
|
||||
export const Container: React.FC<ContainerProps> = (props) => {
|
||||
const { children, className } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`container ${className ?? ''}`}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.container {
|
||||
height: calc(100vh - 110px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
38
components/design/Divider.tsx
Normal file
38
components/design/Divider.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
interface DividerProps {
|
||||
content: string
|
||||
}
|
||||
|
||||
export const Divider: React.FC<DividerProps> = (props) => {
|
||||
const { content } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='text-divider'>{content}</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.text-divider {
|
||||
--text-divider-gap: 1rem;
|
||||
--color-divider: #414141;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
letter-spacing: 0.1em;
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
height: 1px;
|
||||
background-color: var(--color-divider);
|
||||
flex-grow: 1;
|
||||
}
|
||||
&::before {
|
||||
margin-right: var(--text-divider-gap);
|
||||
}
|
||||
&::after {
|
||||
margin-left: var(--text-divider-gap);
|
||||
}
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
65
components/design/IconButton.tsx
Normal file
65
components/design/IconButton.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import { forwardRef, useMemo } from 'react'
|
||||
|
||||
export const icons = [
|
||||
'add',
|
||||
'delete',
|
||||
'edit',
|
||||
'emoji',
|
||||
'send',
|
||||
'settings',
|
||||
'more',
|
||||
'download'
|
||||
] as const
|
||||
|
||||
export type Icon = typeof icons[number]
|
||||
|
||||
interface IconButtonProps extends React.ComponentPropsWithRef<'button'> {
|
||||
icon: Icon
|
||||
hasBackground?: boolean
|
||||
size?: number
|
||||
}
|
||||
|
||||
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
||||
(props, ref) => {
|
||||
const { icon, hasBackground = false, size = 60, ...rest } = props
|
||||
|
||||
const imageSize = useMemo(() => {
|
||||
return size / 2.6
|
||||
}, [size])
|
||||
|
||||
return (
|
||||
<>
|
||||
<button ref={ref} className='button' {...rest}>
|
||||
<img src={`/images/svg/icons/${icon}.svg`} alt={icon} />
|
||||
</button>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.button {
|
||||
background: ${hasBackground
|
||||
? 'var(--color-background-secondary)'
|
||||
: 'none'};
|
||||
border-radius: ${hasBackground ? '50%' : '0'};
|
||||
width: ${hasBackground ? `${size}px` : '100%'};
|
||||
height: ${hasBackground ? `${size}px` : '100%'};
|
||||
border: none;
|
||||
outline: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.button > img {
|
||||
width: ${imageSize}px;
|
||||
height: ${imageSize}px;
|
||||
display: block;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
||||
)
|
128
components/design/Input.tsx
Normal file
128
components/design/Input.tsx
Normal file
@ -0,0 +1,128 @@
|
||||
import { forwardRef, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { ErrorMessage } from '../Authentication/ErrorMessage'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
interface InputProps extends React.ComponentPropsWithRef<'input'> {
|
||||
label: string
|
||||
errors?: string[]
|
||||
showForgotPassword?: boolean
|
||||
}
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
|
||||
const {
|
||||
label,
|
||||
name,
|
||||
type = 'text',
|
||||
errors = [],
|
||||
showForgotPassword = false,
|
||||
...rest
|
||||
} = props
|
||||
const { t } = useTranslation()
|
||||
const [inputType, setInputType] = useState(type)
|
||||
|
||||
const handlePassword = (): void => {
|
||||
const oppositeType = inputType === 'password' ? 'text' : 'password'
|
||||
setInputType(oppositeType)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='container'>
|
||||
<div className='input-with-label'>
|
||||
<div className='label-container'>
|
||||
<label className='label' htmlFor={name}>
|
||||
{label}
|
||||
</label>
|
||||
{type === 'password' && showForgotPassword ? (
|
||||
<Link href='/authentication/forgot-password'>
|
||||
<a className='label-forgot-password'>
|
||||
{t('authentication:forgot-password')}
|
||||
</a>
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
<div className='input-container'>
|
||||
<input
|
||||
data-testid='input'
|
||||
className='input'
|
||||
{...rest}
|
||||
ref={ref}
|
||||
id={name}
|
||||
name={name}
|
||||
type={inputType}
|
||||
/>
|
||||
{type === 'password' && (
|
||||
<div
|
||||
data-testid='password-eye'
|
||||
onClick={handlePassword}
|
||||
className='password-eye'
|
||||
/>
|
||||
)}
|
||||
<ErrorMessage errors={errors} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.input-container {
|
||||
margin-top: 0;
|
||||
position: relative;
|
||||
}
|
||||
.input-with-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.label-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 5px 0;
|
||||
}
|
||||
.label-forgot-password {
|
||||
font-size: 12px;
|
||||
}
|
||||
.label {
|
||||
color: var(--color-secondary);
|
||||
font-size: 16px;
|
||||
font-family: 'Poppins', 'Arial', 'sans-serif';
|
||||
padding-left: 3px;
|
||||
}
|
||||
.input {
|
||||
background-color: #f1f1f1;
|
||||
font-family: 'Roboto', 'Arial', 'sans-serif';
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
padding: 0 20px;
|
||||
color: #2a2a2a;
|
||||
border: 0;
|
||||
box-shadow: ${errors.length >= 1
|
||||
? '0 0 0 2px var(--color-error)'
|
||||
: 'none'};
|
||||
border-radius: 10px;
|
||||
}
|
||||
.input:focus {
|
||||
outline: 0;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 2px var(--color-primary);
|
||||
}
|
||||
.password-eye {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 16px;
|
||||
z-index: 1;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-image: url(/images/svg/icons/input/${inputType}.svg);
|
||||
background-size: cover;
|
||||
cursor: pointer;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
})
|
80
components/design/Loader.tsx
Normal file
80
components/design/Loader.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
export interface LoaderProps {
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
|
||||
export const Loader: React.FC<LoaderProps> = (props) => {
|
||||
const { width = 50, height = 50 } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<div data-testid='progress-spinner' className='progress-spinner'>
|
||||
<svg className='progress-spinner-svg' viewBox='25 25 50 50'>
|
||||
<circle
|
||||
className='progress-spinner-circle'
|
||||
cx='50'
|
||||
cy='50'
|
||||
r='20'
|
||||
fill='none'
|
||||
strokeWidth='2'
|
||||
strokeMiterlimit='10'
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
.progress-spinner {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: ${width}px;
|
||||
height: ${height}px;
|
||||
display: inline-block;
|
||||
}
|
||||
.progress-spinner::before {
|
||||
content: '';
|
||||
display: block;
|
||||
padding-top: 100%;
|
||||
}
|
||||
.progress-spinner-svg {
|
||||
animation: progress-spinner-rotate 2s linear infinite;
|
||||
height: 100%;
|
||||
transform-origin: center center;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
}
|
||||
.progress-spinner-circle {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: 0;
|
||||
stroke: var(--color-primary);
|
||||
animation: progress-spinner-dash 1.5s ease-in-out infinite;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
@keyframes progress-spinner-rotate {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes progress-spinner-dash {
|
||||
0% {
|
||||
stroke-dasharray: 1, 200;
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
50% {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: -35px;
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: -124px;
|
||||
}
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
80
components/design/SocialMediaButton.tsx
Normal file
80
components/design/SocialMediaButton.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import { forwardRef, useMemo } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
export type SocialMedia = 'Discord' | 'GitHub' | 'Google'
|
||||
|
||||
type SocialMediaColors = {
|
||||
[key in SocialMedia]: string
|
||||
}
|
||||
|
||||
interface SocialMediaButtonProps extends React.ComponentPropsWithRef<'button'> {
|
||||
socialMedia: SocialMedia
|
||||
}
|
||||
|
||||
const socialMediaColors: SocialMediaColors = {
|
||||
Discord: '#7289DA',
|
||||
GitHub: '#24292E',
|
||||
Google: '#FCFCFC'
|
||||
}
|
||||
|
||||
export const SocialMediaButton = forwardRef<
|
||||
HTMLButtonElement,
|
||||
SocialMediaButtonProps
|
||||
>((props, ref) => {
|
||||
const { socialMedia, className, ...rest } = props
|
||||
|
||||
const socialMediaColor = useMemo(() => {
|
||||
return socialMediaColors[socialMedia]
|
||||
}, [socialMedia])
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
data-testid='button'
|
||||
ref={ref}
|
||||
{...rest}
|
||||
className={`button ${className ?? ''}`}
|
||||
>
|
||||
<Image
|
||||
width={20}
|
||||
height={20}
|
||||
src={`/images/svg/web/${socialMedia}.svg`}
|
||||
alt={socialMedia}
|
||||
/>
|
||||
<span className='social-media'>{socialMedia}</span>
|
||||
</button>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
outline: none;
|
||||
font-size: var(--default-font-size);
|
||||
font-family: 'Roboto', 'Arial', 'sans-serif';
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0.8px;
|
||||
padding: 0.9rem 2.4rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.2);
|
||||
background: ${socialMediaColor};
|
||||
color: ${socialMedia === 'Google' ? '#000' : '#fff'};
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
.button:hover {
|
||||
opacity: 0.85;
|
||||
transition: all 0.3s ease-in;
|
||||
}
|
||||
.button:before {
|
||||
display: none;
|
||||
}
|
||||
.social-media {
|
||||
margin-left: 0.7rem;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
})
|
106
components/design/Tooltip.tsx
Normal file
106
components/design/Tooltip.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import { forwardRef } from 'react'
|
||||
|
||||
interface TooltipProps extends React.ComponentPropsWithRef<'div'> {
|
||||
content: string
|
||||
direction?: 'top' | 'bottom' | 'right' | 'left'
|
||||
}
|
||||
|
||||
export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
|
||||
(props, ref) => {
|
||||
const { direction = 'bottom', children, content, ...rest } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={ref} {...rest} className='tooltip-wrapper'>
|
||||
{children}
|
||||
<div className={`tooltip ${direction}`}>{content}</div>
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.tooltip-wrapper {
|
||||
--tooltip-text-color: white;
|
||||
--tooltip-background-color: black;
|
||||
--tooltip-margin: 50px;
|
||||
--tooltip-arrow-size: 6px;
|
||||
}
|
||||
.tooltip-wrapper {
|
||||
display: inline-block;
|
||||
|
||||
}
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 10px;
|
||||
color: var(--tooltip-text-color);
|
||||
background: var(--tooltip-background-color);
|
||||
font-size: 15px;
|
||||
font-family: sans-serif;
|
||||
line-height: 1;
|
||||
z-index: 100;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.15s ease-in;
|
||||
}
|
||||
.tooltip-wrapper ~ .tooltip-wrapper:hover .tooltip,
|
||||
.tooltip-wrapper:first-child:hover .tooltip {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: all 0.35s ease-out;
|
||||
margin: 0;
|
||||
}
|
||||
.tooltip::before {
|
||||
content: ' ';
|
||||
left: 50%;
|
||||
border: solid transparent;
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-width: var(--tooltip-arrow-size);
|
||||
margin-left: calc(var(--tooltip-arrow-size) * -1);
|
||||
}
|
||||
.tooltip.top {
|
||||
top: calc(var(--tooltip-margin) * -1);
|
||||
}
|
||||
.tooltip.top::before {
|
||||
top: 100%;
|
||||
border-top-color: var(--tooltip-background-color);
|
||||
}
|
||||
.tooltip.right {
|
||||
left: calc(100% + var(--tooltip-margin));
|
||||
}
|
||||
.tooltip.right::before {
|
||||
left: calc(var(--tooltip-arrow-size) * -1);
|
||||
border-right-color: var(--tooltip-background-color);
|
||||
}
|
||||
.tooltip.bottom {
|
||||
bottom: calc(var(--tooltip-margin) * -1);
|
||||
}
|
||||
.tooltip.bottom::before {
|
||||
bottom: 100%;
|
||||
border-bottom-color: var(--tooltip-background-color);
|
||||
}
|
||||
.tooltip.left {
|
||||
left: auto;
|
||||
right: calc(100% + var(--tooltip-margin));
|
||||
top: 50%;
|
||||
transform: translateX(0) translateY(-50%);
|
||||
}
|
||||
.tooltip.left::before {
|
||||
left: auto;
|
||||
right: calc(var(--tooltip-arrow-size) * -2);
|
||||
top: 50%;
|
||||
transform: translateX(0) translateY(-50%);
|
||||
border-left-color: var(--tooltip-background-color);
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
||||
)
|
13
components/design/__test__/Avatar.test.tsx
Normal file
13
components/design/__test__/Avatar.test.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { Avatar } from '../Avatar'
|
||||
|
||||
describe('<Avatar />', () => {
|
||||
it('should render', async () => {
|
||||
const altAttribute = 'avatar'
|
||||
const { getByAltText } = render(
|
||||
<Avatar width={50} height={50} src='/avatar.png' alt={altAttribute} />
|
||||
)
|
||||
expect(getByAltText(altAttribute)).toBeInTheDocument()
|
||||
})
|
||||
})
|
10
components/design/__test__/Button.test.tsx
Normal file
10
components/design/__test__/Button.test.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { Button } from '../Button'
|
||||
|
||||
describe('<Button />', () => {
|
||||
it('should render', async () => {
|
||||
const { getByText } = render(<Button>Submit</Button>)
|
||||
expect(getByText('Submit')).toBeInTheDocument()
|
||||
})
|
||||
})
|
10
components/design/__test__/Container.test.tsx
Normal file
10
components/design/__test__/Container.test.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { Container } from '../Container'
|
||||
|
||||
describe('<Container />', () => {
|
||||
it('should render', async () => {
|
||||
const { getByText } = render(<Container>Content</Container>)
|
||||
expect(getByText('Content')).toBeInTheDocument()
|
||||
})
|
||||
})
|
11
components/design/__test__/Divider.test.tsx
Normal file
11
components/design/__test__/Divider.test.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { Divider } from '../Divider'
|
||||
|
||||
describe('<Divider />', () => {
|
||||
it('should render with the content', async () => {
|
||||
const content = 'divider'
|
||||
const { getByText } = render(<Divider content={content} />)
|
||||
expect(getByText(content)).toBeInTheDocument()
|
||||
})
|
||||
})
|
13
components/design/__test__/IconButton.test.tsx
Normal file
13
components/design/__test__/IconButton.test.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { Icon, IconButton } from '../IconButton'
|
||||
|
||||
describe('<IconButton />', () => {
|
||||
it('should render with the icon', async () => {
|
||||
const icon: Icon = 'add'
|
||||
const { getByAltText } = render(<IconButton icon={icon} />)
|
||||
const iconImage = getByAltText(icon)
|
||||
expect(iconImage).toBeInTheDocument()
|
||||
expect(iconImage).toHaveAttribute('src', `/images/svg/icons/${icon}.svg`)
|
||||
})
|
||||
})
|
26
components/design/__test__/Input.test.tsx
Normal file
26
components/design/__test__/Input.test.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { render, fireEvent } from '@testing-library/react'
|
||||
|
||||
import { Input } from '../Input'
|
||||
|
||||
describe('<Input />', () => {
|
||||
it('should render the label', async () => {
|
||||
const labelContent = 'label content'
|
||||
const { getByText } = render(<Input label={labelContent} />)
|
||||
expect(getByText(labelContent)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render the eye icon if the input is not of type "password"', async () => {
|
||||
const { queryByTestId } = render(<Input type='text' label='content' />)
|
||||
const passwordEye = queryByTestId('password-eye')
|
||||
expect(passwordEye).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handlePassword with eye icon', async () => {
|
||||
const { findByTestId } = render(<Input type='password' label='content' />)
|
||||
const passwordEye = await findByTestId('password-eye')
|
||||
const input = await findByTestId('input')
|
||||
expect(input).toHaveAttribute('type', 'password')
|
||||
fireEvent.click(passwordEye)
|
||||
expect(input).toHaveAttribute('type', 'text')
|
||||
})
|
||||
})
|
20
components/design/__test__/Loader.test.tsx
Normal file
20
components/design/__test__/Loader.test.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { Loader } from '../Loader'
|
||||
|
||||
describe('<Loader />', () => {
|
||||
it('should render with correct width and height', async () => {
|
||||
const size = 20
|
||||
const { findByTestId } = render(<Loader width={size} height={size} />)
|
||||
const progressSpinner = await findByTestId('progress-spinner')
|
||||
expect(progressSpinner).toHaveStyle(`width: ${size}px`)
|
||||
expect(progressSpinner).toHaveStyle(`height: ${size}px`)
|
||||
})
|
||||
|
||||
it('should render with default width and height', async () => {
|
||||
const { findByTestId } = render(<Loader />)
|
||||
const progressSpinner = await findByTestId('progress-spinner')
|
||||
expect(progressSpinner).toHaveStyle('width: 50px')
|
||||
expect(progressSpinner).toHaveStyle('height: 50px')
|
||||
})
|
||||
})
|
23
components/design/__test__/SocialMediaButton.test.tsx
Normal file
23
components/design/__test__/SocialMediaButton.test.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { SocialMedia, SocialMediaButton } from '../SocialMediaButton'
|
||||
|
||||
describe('<SocialMediaButton />', () => {
|
||||
it('should render the social media', async () => {
|
||||
const socialMedia: SocialMedia = 'Discord'
|
||||
const { findByAltText } = render(
|
||||
<SocialMediaButton socialMedia={socialMedia} />
|
||||
)
|
||||
const socialMediaButton = await findByAltText(socialMedia)
|
||||
expect(socialMediaButton).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render with a black text color with Google social media', async () => {
|
||||
const socialMedia: SocialMedia = 'Google'
|
||||
const { findByTestId } = render(
|
||||
<SocialMediaButton socialMedia={socialMedia} />
|
||||
)
|
||||
const button = await findByTestId('button')
|
||||
expect(button).toHaveStyle('color: #000')
|
||||
})
|
||||
})
|
11
components/design/__test__/Tooltip.test.tsx
Normal file
11
components/design/__test__/Tooltip.test.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { Tooltip } from '../Tooltip'
|
||||
|
||||
describe('<Tooltip />', () => {
|
||||
it('should render with content', async () => {
|
||||
const content = 'tooltip content'
|
||||
const { getByText } = render(<Tooltip content={content} />)
|
||||
expect(getByText(content)).toBeInTheDocument()
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user