-
Notifications
You must be signed in to change notification settings - Fork 2
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
base: develop
Are you sure you want to change the base?
Changes from all commits
1d7c1fd
7b3f5cf
8469412
e884e07
076a218
ca16399
a8face8
928518f
59f4074
8e74178
b20e291
c382dbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
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>) => { | ||
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{}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CheckboxItemProps가 이미 CheckboxItemBaseProps를 확장하고 있는데, |
||
|
||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
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'; |
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 |
---|---|---|
@@ -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; |
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'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
props에 handleKeyDown은 Checkbox에게 전달되고, 컴포넌트안에서 handleKeyDown을 또 선언하고 있어서 헷갈리네요 🤔