Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] refactor: Checkbox, CheckboxItem 컴포넌트 역할 분리 #1005

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
Open
59 changes: 0 additions & 59 deletions frontend/src/components/common/Checkbox/index.tsx

This file was deleted.

57 changes: 0 additions & 57 deletions frontend/src/components/common/CheckboxItem/index.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { ChangeEvent } from 'react';

import { Checkbox } from '../../checkboxes';
import { CheckboxItemBaseProps } from '../CheckboxItemBase';
import { CheckboxItemBase } from '../index';

export interface CheckboxItemProps extends CheckboxItemBaseProps {
isDisabled?: boolean;
handleChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
handleKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
}

const CheckboxItem = ({ id, label, isChecked, isDisabled = false, handleChange, ...rest }: CheckboxItemProps) => {
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
Comment on lines +10 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

props에 handleKeyDown은 Checkbox에게 전달되고, 컴포넌트안에서 handleKeyDown을 또 선언하고 있어서 헷갈리네요 🤔

if (event.key === 'Enter' && handleChange) {
handleChange({
currentTarget: {
id: id,
checked: !isChecked,
} as Partial<HTMLInputElement>,
} as ChangeEvent<HTMLInputElement>);
}
};

return (
<CheckboxItemBase
id={`checkbox-item-${id}`}
isChecked={isChecked}
tabIndex={isDisabled ? -1 : 0}
isDisabled={isDisabled}
label={label}
handleKeyDown={handleKeyDown}
>
<Checkbox
id={id}
isChecked={isChecked}
isDisabled={isDisabled}
tabIndex={-1}
handleChange={handleChange}
{...rest}
/>
</CheckboxItemBase>
);
};

export default CheckboxItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { EssentialPropsWithChildren } from '@/types';

import { CheckboxItemProps } from '../CheckboxItem';

import * as S from './styles';

export interface CheckboxItemBaseProps {
id: string;
label: string;
isChecked: boolean;
name?: string;
tabIndex?: number;
}

interface FinalCheckboxItemBaseProps extends CheckboxItemBaseProps, CheckboxItemProps{};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CheckboxItemProps가 이미 CheckboxItemBaseProps를 확장하고 있는데, interface FinalCheckboxItemBaseProps extends CheckboxItemBaseProps, CheckboxItemProps{}; 라고 타입을 지정해야하는 이유가 있나요?


const CheckboxItemBase = ({
label,
isChecked,
tabIndex = 0,
handleKeyDown,
children,
...rest
}: EssentialPropsWithChildren<FinalCheckboxItemBaseProps>) => {
const isCheckedLabel = `${label}, ${isChecked ? '선택됨' : '선택 안 됨'}`;

return (
<S.CheckboxItem
className="checkbox-item"
Comment on lines +28 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존에 구현 사항에 충돌 때문에 클래스명을 checkbox-item이라고 한건가요?

CheckboxItem 내에서 CheckboxItemBase가 사용되고 CheckboxItemBase의 클래스가 checkbox-item인 구조가 헷갈렸어요.

tabIndex={tabIndex}
aria-label={isCheckedLabel}
onKeyDown={handleKeyDown}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CheckboxItemBase가 스타일링을 담당하는 기본 컴포넌트라고 설명이 되어 있지만, 체크 박스나 라벨 선택 시 선택/해제 기능을 위해서 onKeyDown이라는 이벤트를 가지는 건가요?

{...rest}
>
<S.CheckboxLabel>
{children}
{label}
</S.CheckboxLabel>
</S.CheckboxItem>
);
};

export default CheckboxItemBase;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ReadonlyCheckbox } from '../../checkboxes';
import { CheckboxItemBaseProps } from '../CheckboxItemBase';
import { CheckboxItemBase } from '../index';

export interface ReadonlyCheckboxItemProps
extends Omit<CheckboxItemBaseProps, 'tabIndex' | 'isDisabled' | 'handleChange' | 'handleKeyDown'> {}

const ReadonlyCheckboxItem = ({ id, label, isChecked, ...rest }: ReadonlyCheckboxItemProps) => {
return (
<CheckboxItemBase id={id} isChecked={isChecked} tabIndex={-1} label={label}>
<ReadonlyCheckbox id={id} isChecked={isChecked} tabIndex={-1} $isReadonly={true} {...rest} />
</CheckboxItemBase>
);
};

export default ReadonlyCheckboxItem;
3 changes: 3 additions & 0 deletions frontend/src/components/common/checkboxItems/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as CheckboxItem } from './CheckboxItem';
export { default as CheckboxItemBase } from './CheckboxItemBase';
export { default as ReadonlyCheckboxItem } from './ReadonlyCheckboxItem';
22 changes: 22 additions & 0 deletions frontend/src/components/common/checkboxes/Checkbox/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ChangeEvent } from 'react';

import { CheckboxBaseProps } from '../CheckboxBase';
import { CheckboxBase } from '../index';

export interface CheckboxProps extends CheckboxBaseProps {
handleChange?: (event: ChangeEvent<HTMLInputElement>, label?: string) => void;
}

export const Checkbox = ({ isDisabled, isChecked, isTabAccessible = true, handleChange, ...rest }: CheckboxProps) => {
return (
<CheckboxBase
isDisabled={isDisabled}
isChecked={isChecked}
tabIndex={isDisabled || isTabAccessible ? 0 : -1}
handleChange={handleChange}
{...rest}
/>
);
};

export default Checkbox;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import CheckedIcon from '@/assets/checked.svg';
import UncheckedIcon from '@/assets/unchecked.svg';
import UndraggableWrapper from '@/components/common/UndraggableWrapper';

import { CheckboxProps } from '../Checkbox';

import * as S from './styles';

export interface CheckboxStyleProps {
$isReadonly?: boolean;
$style?: React.CSSProperties;
}

export interface CheckboxA11yProps {
isTabAccessible?: boolean; // CheckboxItem을 사용할 때 Checkbox 중복 포커싱 방지용
tabIndex?: number;
}
export interface CheckboxBaseProps extends CheckboxStyleProps, CheckboxA11yProps {
id: string;
isChecked: boolean;
isDisabled?: boolean;
name?: string;
}

interface FinalCheckboxBaseProps extends CheckboxBaseProps, CheckboxProps {}

const CheckboxBase = ({
id,
isChecked,
isDisabled,
tabIndex,
handleChange,
$isReadonly = false,
$style,
...rest
}: FinalCheckboxBaseProps) => {
return (
<UndraggableWrapper>
<S.CheckboxContainer $style={$style} $isReadonly={$isReadonly}>
<S.CheckboxLabel>
<input
id={id}
data-testid={`checkbox-${id}`}
checked={isChecked}
disabled={isDisabled}
onChange={handleChange}
type="checkbox"
tabIndex={-1}
{...rest}
/>
<img
src={isChecked ? CheckedIcon : UncheckedIcon}
tabIndex={tabIndex}
role="checkbox"
aria-checked={isChecked}
alt=""
/>
{$isReadonly && <span className="sr-only">{isChecked ? '선택됨' : '선택 안 됨'}</span>}
</S.CheckboxLabel>
</S.CheckboxContainer>
</UndraggableWrapper>
);
};

export default CheckboxBase;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled from '@emotion/styled';

import { CheckboxStyleProps } from './index';
import { CheckboxStyleProps } from '.';

export const CheckboxContainer = styled.div<CheckboxStyleProps>`
width: 2.7rem;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CheckboxProps } from '../Checkbox';
import { CheckboxBase } from '../index';

interface ReadonlyCheckboxProps extends Omit<CheckboxProps, 'isDisabled' | 'handleChange'> {}

const ReadonlyCheckbox = ({ isChecked, ...rest }: ReadonlyCheckboxProps) => {
return (
<CheckboxBase
isDisabled={true}
isChecked={isChecked}
tabIndex={-1}
aria-readonly={true}
$isReadonly={true}
{...rest}
/>
);
};

export default ReadonlyCheckbox;
3 changes: 3 additions & 0 deletions frontend/src/components/common/checkboxes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as Checkbox } from './Checkbox';
export { default as CheckboxBase } from './CheckboxBase';
export { default as ReadonlyCheckbox } from './ReadonlyCheckbox';
7 changes: 4 additions & 3 deletions frontend/src/components/common/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ export { default as Input } from './Input';
export { default as ReviewDate } from './ReviewDate';
export { default as MultilineTextViewer } from './MultilineTextViewer';
export { default as TopButton } from './TopButton';
export { default as Checkbox } from './Checkbox';
export { default as CheckboxItem } from './CheckboxItem';
export { default as Checkbox } from './checkboxes/Checkbox';
export { default as ReadonlyCheckbox } from './checkboxes/ReadonlyCheckbox';
export { default as CheckboxItem } from './checkboxItems/CheckboxItem';
export { default as ReadonlyCheckboxItem } from './checkboxItems/ReadonlyCheckboxItem';
export { default as EyeButton } from './EyeButton';
export { default as Carousel } from './Carousel';
export { default as Accordion } from './Accordion';
export { default as Dropdown } from './Dropdown';
export { default as Toast } from './Toast';

export { default as OptionSwitch } from './OptionSwitch';
export { default as ReviewEmptySection } from './ReviewEmptySection';
export * from './modals';
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';

import { AlertModal } from '@/components';
import Checkbox from '@/components/common/Checkbox';
import Checkbox from '@/components/common/checkboxes/Checkbox';

import { CopyTextButton } from '../index';

Expand Down
Loading
Loading