오늘은 3가지 기본 컴포넌트를 만들 예정
import styled from 'styled-components';
import { ColorKey, HeadingSize } from '../../style/theme';
interface Props {
children: React.ReactNode;
size: HeadingSize;
color?: ColorKey;
}
function Title({children, size, color} : Props) {
return (
<TitleStyle size={size} color={color}>
{children}
</TitleStyle>
)
}
const TitleStyle = styled.h1<Omit<Props, "children">>`
font-size: ${({theme, size}) => theme.heading[size].fontSize};
color: ${({theme, color})=> color ? theme.color[color]: theme.color.primary}
`;
export default Title
테스트 파일
import { render, screen } from '@testing-library/react'
import Title from './Title'
import { BookSotreThemeProvider } from '../../context/themeContext';
describe("Title 컴포넌트 테스트",()=>{
it("렌더를 확인", ()=>{
// 1. 렌더
render(
<BookSotreThemeProvider>
<Title size="large">제목</Title>
</BookSotreThemeProvider>);
// 2. 확인
expect(screen.getByText('제목')).toBeInTheDocument();
});
it('size props 적용', ()=>{
const { container} = render(
<BookSotreThemeProvider>
<Title size="large">제목</Title>
</BookSotreThemeProvider>
);
expect(container?.firstChild).toHaveStyle({ fontSize: "2rem"});
})
it("color props 적용", ()=>{
const { container} = render(
<BookSotreThemeProvider>
<Title size="large">제목</Title>
</BookSotreThemeProvider>
);
expect(container?.firstChild).toHaveStyle({ color: "brown"});
})
});
import styled from "styled-components";
import { ButtonScheme, ButtonSize } from "../../style/theme";
interface Props{
children: React.ReactNode;
size: ButtonSize;
scheme: ButtonScheme;
disabled?: boolean;
isLoading?: boolean;
}
function Button({children, size, scheme, disabled, isLoading}:Props) {
return (
<ButtonStyle size={size} scheme={scheme} disabled={disabled} isLoading={isLoading}>
{children}
</ButtonStyle>
)
}
const ButtonStyle = styled.button<Omit<Props,"children">>`
font-size: ${({theme, size})=>theme.button[size].fontSize};
background-color: ${({theme, scheme})=>theme.buttonScheme[scheme].color};
color: ${({theme, scheme})=> theme.buttonScheme[scheme].color};
border: 0;
border-radius: ${({theme})=> theme.borderRadius.default};
opacity: ${({ disabled })=> (disabled? 0.5: 1)};
pointer-events: ${({disabled})=> (disabled? "none": "auto")};
cursor: ${({ disabled })=> (disabled? 'none': 'pointer')};;
`;
export default Button;
테스트 파일
import { render, screen } from '@testing-library/react'
import Button from './Button';
import { BookSotreThemeProvider } from '../../context/themeContext';
describe("Button 컴포넌트 테스트",()=>{
it("렌더를 확인", ()=>{
// 1. 렌더
render(
<BookSotreThemeProvider>
<Button size="large" scheme="primary">버튼</Button>
</BookSotreThemeProvider>);
// 2. 확인
expect(screen.getByText('버튼')).toBeInTheDocument();
});
it('size props 적용', ()=>{
const { container } = render(
<BookSotreThemeProvider>
<Button size="large" scheme="primary">
버튼
</Button>
</BookSotreThemeProvider>
);
expect(screen.getByRole("button",{name:"버튼"})).toHaveStyle({ fontSize: "1.5rem"});
})
it("color props 적용", ()=>{
const { container} = render(
<BookSotreThemeProvider>
<Button size="large" scheme="primary">버튼</Button>
</BookSotreThemeProvider>
);
expect(container?.firstChild).toHaveStyle({ color: "white"});
})
it("disabled props 적용",()=>{
render(
<BookSotreThemeProvider>
<Button size="large" scheme="primary" disabled>버튼</Button>
</BookSotreThemeProvider>
)
expect(screen.getByRole("button")).toBeDisabled()
})
});
import React, {ForwardedRef} from "react";
import styled from "styled-components";
interface Props{
placeholder?: string;
}
const InputText = React.forwardRef(({ placeholder }: Props, ref: ForwardedRef<HTMLInputElement>)=>{
return <InputTextStyle placeholder={placeholder} ref={ref}/>
});
const InputTextStyle = styled.input.attrs({ type:"text"})`
padding: 0.25rem 0.75rem;
border: 1px solid ${({ theme })=> theme.color.border};
border-radius: ${({ theme})=> theme.borderRadius.default};
font-size: 1rem;
line-height: 1.5;
color: ${({theme})=> theme.color.text};
`
export default InputText;
테스트 파일
import React from 'react';
import { render, screen } from '@testing-library/react'
import InputText from './InputText';
import { BookSotreThemeProvider } from '../../context/themeContext';
describe("InputText 컴포넌트 테스트",()=>{
it("렌더를 확인", ()=>{
// 1. 렌더
render(
<BookSotreThemeProvider>
<InputText placeholder="여기에 입력하세요"/>
</BookSotreThemeProvider>);
// 2. 확인
expect(screen.getByPlaceholderText("여기에 입력하세요")).toBeInTheDocument();
});
it("forwardRef 테스트", ()=>{
const ref = React.createRef<HTMLInputElement>();
render(
<BookSotreThemeProvider>
<InputText placeholder="여기에 입력하세요" ref={ref}/>
</BookSotreThemeProvider>);
expect(ref.current).toBeInstanceOf(HTMLInputElement);
});
});
이제 진짜로 시작한다

해당 사진을 기준으로 헤더와 푸터를 제작하였다.
이걸 볼 사람이 있을지는 모르겠는데, 지금
react-icons@5.5.0버전에서 jsx파일 내에서 사용하면 TS2786 에러가 나온다.
이거 버전만 낮추면 해결되니 참고하길 바란다.
링크