Skip to content

Commit

Permalink
[FE] 공통 체크박스 컴포넌트 구현 (#243)
Browse files Browse the repository at this point in the history
* feat: 체크박스 구현을 위한 체크 아이콘 svg 업로드

* feat: 체크박스 크기에 대한 타입 정의

* feat: 체크박스 컴포넌트 구현

* test: 체크박스 컴포넌트에 대한 스토리 작성

* refactor: 공용 체크박스의 size prop에 대해 기본값을 md로 지정
  • Loading branch information
wzrabbit authored Jul 31, 2023
1 parent 6c9fb8a commit d0909e7
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 0 deletions.
1 change: 1 addition & 0 deletions frontend/src/assets/svg/check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/svg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { ReactComponent as DeleteIcon } from './delete.svg';
export { ReactComponent as EditIcon } from './edit.svg';
export { ReactComponent as LogoIcon } from './logo.svg';
export { ReactComponent as PlusIcon } from './plus.svg';
export { ReactComponent as CheckIcon } from './check.svg';
96 changes: 96 additions & 0 deletions frontend/src/components/common/Checkbox/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type { Meta, StoryObj } from '@storybook/react';
import Checkbox from './Checkbox';
import { css } from 'styled-components';

/**
* `Checkbox`는 대부분의 상황에서 사용할 수 있는 공용 체크박스 컴포넌트입니다.
*/
const meta = {
title: 'common/Checkbox',
component: Checkbox,
tags: ['autodocs'],
} satisfies Meta<typeof Checkbox>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Medium: Story = {
args: {
isChecked: true,
onChange: () =>
alert(
'체크박스의 onChange 이벤트가 실행되었습니다! 추후 이 이벤트를 핸들링해 state를 변경한다면, 체크박스도 바뀔 것입니다!',
),
},
};

export const MediumNotChecked: Story = {
args: {
isChecked: false,
},
};

export const Small: Story = {
args: {
size: 'sm',
isChecked: true,
},
};

export const SmallNotChecked: Story = {
args: {
size: 'sm',
isChecked: false,
},
};

export const Large: Story = {
args: {
size: 'lg',
isChecked: true,
},
};

export const LargeNotChecked: Story = {
args: {
size: 'lg',
isChecked: false,
},
};

export const ExtraLarge: Story = {
args: {
size: 'xl',
isChecked: true,
},
};

export const ExtraLargeNotChecked: Story = {
args: {
size: 'xl',
isChecked: false,
},
};

export const CustomColor: Story = {
args: {
size: 'md',
isChecked: true,
color: '#ff8888',
},
};

export const CustomStyle: Story = {
args: {
size: 'xl',
isChecked: true,
css: css`
width: 70px;
height: 50px;
border: none;
background: linear-gradient(45deg, #00ffe5, #2600ff, #ff0ff7);
`,
},
};
113 changes: 113 additions & 0 deletions frontend/src/components/common/Checkbox/Checkbox.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { styled, css } from 'styled-components';
import type { CheckboxSize } from '~/types/size';
import type { CheckboxProps } from './Checkbox';
import type { CSSProp } from 'styled-components';

type CustomCheckboxProps = Pick<CheckboxProps, 'color' | 'css' | 'size'>;

type CheckIconWrapperProps = Pick<CheckboxProps, 'size'>;

const checkboxSizes: Record<CheckboxSize, CSSProp> = {
sm: css`
width: 20px;
height: 20px;
border-radius: 2px;
`,

md: css`
width: 26px;
height: 26px;
border-radius: 3px;
`,

lg: css`
width: 32px;
height: 32px;
border-radius: 4px;
`,

xl: css`
width: 38px;
height: 38px;
border-radius: 5px;
`,
};

const checkIconSizes: Record<CheckboxSize, CSSProp> = {
sm: css`
width: 14px;
height: 14px;
`,

md: css`
width: 20px;
height: 20px;
`,

lg: css`
width: 26px;
height: 26px;
`,

xl: css`
width: 32px;
height: 32px;
`,
};

export const RealCheckbox = styled.input`
appearance: none;
`;

export const CustomCheckbox = styled.span<CustomCheckboxProps>`
display: inline-block;
${({ size = 'md' }) => checkboxSizes[size]}
border: 3px solid
${({ color, theme }) => {
if (color) {
return color;
}
return theme.color.PRIMARY;
}};
background: transparent;
transition: 0.2s;
cursor: pointer;
${RealCheckbox}:checked ~ & {
background-color: ${({ color, theme }) => {
if (color) {
return color;
}
return theme.color.PRIMARY;
}};
}
${RealCheckbox} ~ & svg {
opacity: 0;
transition: 0.2s;
}
${RealCheckbox}:checked ~ & svg {
opacity: 1;
}
${({ css }) => css};
`;

export const CheckIconWrapper = styled.div<CheckIconWrapperProps>`
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
& svg {
${({ size = 'md' }) => checkIconSizes[size]}
}
`;
29 changes: 29 additions & 0 deletions frontend/src/components/common/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as S from './Checkbox.styled';
import type { CSSProp } from 'styled-components';
import type { CheckboxSize } from '~/types/size';
import { CheckIcon } from '~/assets/svg';

export interface CheckboxProps {
isChecked: boolean;
color?: string;
size?: CheckboxSize;
css?: CSSProp;
onChange?: () => void;
}

const Checkbox = (props: CheckboxProps) => {
const { isChecked, onChange, color, size = 'md', css } = props;

return (
<label>
<S.RealCheckbox type="checkbox" checked={isChecked} onChange={onChange} />
<S.CustomCheckbox color={color} css={css} size={size}>
<S.CheckIconWrapper size={size}>
<CheckIcon />
</S.CheckIconWrapper>
</S.CustomCheckbox>
</label>
);
};

export default Checkbox;
1 change: 1 addition & 0 deletions frontend/src/types/size.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export type ThreadSize = Extract<Size, 'sm' | 'md'>;

export type NotificationSize = Extract<Size, 'sm' | 'md'>;

export type CheckboxSize = Extract<Size, 'sm' | 'md' | 'lg' | 'xl'>;

0 comments on commit d0909e7

Please sign in to comment.