-
Notifications
You must be signed in to change notification settings - Fork 1
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
[Feature] Chip 컴포넌트 테스트 작성 #88
Changes from all commits
1b84b6c
eed11c1
9e09ac2
2fda402
8c4e6d9
22b23f6
9c84c45
471b195
489a401
c8bdcc8
4579454
b283e21
ab0dffd
8afd547
fe77716
5b35c13
da739a2
fc75fdc
75d7737
f28bbbb
2297768
c14a875
9429b52
0781e50
1cf5670
3b90049
6f591f4
6e9ad33
f3c1962
c0c47ba
34e36d0
693c414
e3bcfb0
82d9a35
1b3b761
0db9c5e
8d78f90
9eb7022
43fea6b
12a8892
ab4d238
ccb7138
ba3f1c5
45a385b
f6ff146
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { render, type RenderResult, waitFor } from "@testing-library/react"; | ||
import { userEvent } from "@testing-library/user-event"; | ||
|
||
import Chip from "@/components/Chip"; | ||
|
||
describe("Chip rendering Test", () => { | ||
let renderChip: RenderResult; | ||
beforeEach(() => { | ||
renderChip = render(<Chip as="div" label="Chip" />); | ||
}); | ||
|
||
it("should render Chip", () => { | ||
const { getByText } = renderChip; | ||
expect(getByText("Chip")).toBeInTheDocument(); | ||
}); | ||
|
||
it("should render with attributes aria-disabled to be false by default", () => { | ||
const chipComponent = renderChip.container.querySelector("div"); | ||
|
||
expect(chipComponent).toHaveAttribute("aria-disabled", "false"); | ||
}); | ||
}); | ||
|
||
describe("Chip toggle Test", () => { | ||
let renderChip: RenderResult; | ||
beforeEach(() => { | ||
renderChip = render(<Chip as="button" clickable={true} label="Chip" />); | ||
}); | ||
|
||
it("should toggle state when onClick event is fired", async () => { | ||
const chipComponent = renderChip.getByRole("checkbox"); | ||
const user = userEvent.setup(); | ||
|
||
await user.click(chipComponent); | ||
expect(chipComponent).toHaveAttribute("aria-checked", "true"); | ||
await user.click(chipComponent); | ||
expect(chipComponent).toHaveAttribute("aria-checked", "false"); | ||
}); | ||
|
||
it("should toggle state when Enter key is pressed", async () => { | ||
const chipComponent = renderChip.getByRole("checkbox"); | ||
userEvent.type(chipComponent, "{enter}"); | ||
await waitFor(() => { | ||
expect(chipComponent).toHaveAttribute("aria-checked", "true"); | ||
}); | ||
userEvent.type(chipComponent, "{enter}"); | ||
await waitFor(() => { | ||
expect(chipComponent).toHaveAttribute("aria-checked", "false"); | ||
}); | ||
}); | ||
|
||
it("should toggle state when Space key is pressed", async () => { | ||
const chipComponent = renderChip.getByRole("checkbox"); | ||
|
||
await userEvent.type(chipComponent, "{space}"); | ||
expect(chipComponent).toHaveAttribute("aria-checked", "true"); | ||
await userEvent.type(chipComponent, "{space}"); | ||
expect(chipComponent).toHaveAttribute("aria-checked", "false"); | ||
}); | ||
}); | ||
|
||
describe("Chip disabled Test", () => { | ||
let renderChip: RenderResult; | ||
beforeEach(() => { | ||
renderChip = render(<Chip disabled={true} label="Chip" />); | ||
}); | ||
|
||
it("should render with attributes aria-disabled to be true", () => { | ||
const chipComponent = renderChip.container.querySelector("button"); | ||
|
||
expect(chipComponent).toHaveAttribute("aria-disabled", "true"); | ||
}); | ||
|
||
it("should not allow focusing", () => { | ||
const chipComponent = renderChip.container.querySelector("button"); | ||
userEvent.click(chipComponent!!); | ||
|
||
expect(chipComponent).not.toHaveFocus(); | ||
}); | ||
}); | ||
|
||
describe("external control and events", () => { | ||
let renderChip: RenderResult; | ||
|
||
it("should fire external onClick event", async () => { | ||
renderChip = render(<Chip clickable label="Chip" />); | ||
const chipComponent = renderChip.getByRole("checkbox"); | ||
const user = userEvent.setup(); | ||
const onClickHandler = jest.fn(); | ||
chipComponent.onclick = onClickHandler; | ||
|
||
await user.click(chipComponent); | ||
expect(onClickHandler).toHaveBeenCalled(); | ||
}); | ||
|
||
it("should fire external onKeyDown event", async () => { | ||
renderChip = render(<Chip clickable as="button" label="Chip" />); | ||
const user = userEvent.setup(); | ||
const chipComponent = renderChip.getByRole("checkbox"); | ||
const onKeyDownHandler = jest.fn(); | ||
chipComponent.onkeydown = onKeyDownHandler; | ||
|
||
await user.type(chipComponent, "{enter}"); | ||
expect(onKeyDownHandler).toHaveBeenCalled(); | ||
await user.type(chipComponent, "{space}"); | ||
expect(onKeyDownHandler).toHaveBeenCalled(); | ||
}); | ||
|
||
it("should toggle external checked state when onClick event fired", async () => { | ||
const user = userEvent.setup(); | ||
let checked = false; | ||
const handleChange = () => { | ||
checked = !checked; | ||
}; | ||
const rendered = render(<Chip clickable as="button" label="Chip" />); | ||
const chipComponent = rendered.getByRole("checkbox"); | ||
chipComponent.onchange = handleChange; | ||
|
||
await user.click(chipComponent); | ||
|
||
expect(chipComponent).toHaveAttribute("aria-checked", "true"); | ||
expect(chipComponent).toHaveAttribute("aria-disabled", "false"); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ import type { | |
* | ||
* @param {T} [element] 렌더링할 요소 또는 컴포넌트. 기본값은 button이며, Chip의 경우 input으로 사용될 수 있음 | ||
* @param {boolean} [defaultChecked=false] 칩의 토글의 default 활성화 상태 | ||
* @param {boolean} [isChecked=false] 외부에서 제어할 활성 상태. | ||
* @param {boolean} [ischecked=false] 외부에서 제어할 활성 상태. | ||
* @param {string} label 칩 버튼에 들어갈 텍스트 | ||
* @param {boolean} [clickable=true] 클릭할 수 있는 칩인지 여부 판단 | ||
* @param {() => void} [onDelete] 칩 버튼을 삭제했을 때의 동작 | ||
|
@@ -42,20 +42,20 @@ type ChipComponent = <T extends ButtonElementType = "button">( | |
|
||
const ChipLabel = ({ | ||
label, | ||
isChecked, | ||
ischecked, | ||
disabled = true, | ||
}: { | ||
label: string; | ||
isChecked: boolean; | ||
ischecked: boolean; | ||
disabled: boolean; | ||
}) => { | ||
return ( | ||
<styled.div | ||
data-disabled={disabled} | ||
data-selected={isChecked} | ||
data-selected={ischecked} | ||
textStyle="label2" | ||
className={chipLabel({ | ||
type: disabled ? "disabled" : isChecked ? "checked" : "unchecked", | ||
type: disabled ? "disabled" : ischecked ? "checked" : "unchecked", | ||
})} | ||
> | ||
{label} | ||
|
@@ -71,16 +71,15 @@ const Chip: ChipComponent & { displayName?: string } = forwardRef( | |
clickable, | ||
onKeyDown, | ||
onClick, | ||
isChecked: checkedProp = false, | ||
ischecked: checkedProp = false, | ||
defaultChecked = false, | ||
disabled = false, | ||
style, | ||
...rest | ||
}: ChipProps<T>, | ||
ref: any | ||
) => { | ||
const Component = as || "button"; | ||
const [isChecked, setIsChecked] = useState(() => | ||
const Component = (as || "button") as React.ElementType; | ||
const [ischecked, setIsChecked] = useState(() => | ||
checkedProp ? checkedProp : defaultChecked | ||
); | ||
useEffect(() => { | ||
|
@@ -92,25 +91,29 @@ const Chip: ChipComponent & { displayName?: string } = forwardRef( | |
const handleClick = () => { | ||
if (disabled) return; | ||
onClick?.(); | ||
clickable ? setIsChecked((prev) => !prev) : null; | ||
clickable ? setIsChecked((prev: boolean) => !prev) : null; | ||
}; | ||
|
||
const handleKeyDown = (event: any) => { | ||
if (!clickable || disabled) return; | ||
if (event.currentTarget === event.target) { | ||
event.preventDefault(); | ||
if (event.key === "Enter" || event.key === " ") { | ||
setIsChecked((prev) => !prev); | ||
setIsChecked((prev: boolean) => !prev); | ||
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. p4; 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. CI/CD로 test를 진행할 때 타입 추론을 추가해주지 않으면 타입스크립트 에러로 테스트가 터지더라구요.. cry |
||
onKeyDown?.(); | ||
} | ||
} | ||
}; | ||
|
||
return ( | ||
<Component | ||
aria-label={`chip ${isChecked ? "activated" : "inactivated"}`} | ||
data-selected={isChecked} | ||
ischecked | ||
aria-checked={clickable ? ischecked : undefined} | ||
aria-disabled={disabled} | ||
aria-label={`chip ${ischecked ? "activated" : "inactivated"}`} | ||
data-selected={ischecked} | ||
ref={ref} | ||
role={clickable ? "checkbox" : undefined} | ||
style={style} | ||
className={chip({ | ||
clickable: disabled ? false : clickable, | ||
|
@@ -119,7 +122,7 @@ const Chip: ChipComponent & { displayName?: string } = forwardRef( | |
onClick={handleClick} | ||
onKeyDown={handleKeyDown} | ||
> | ||
<ChipLabel disabled={disabled} isChecked={isChecked} label={label} /> | ||
<ChipLabel disabled={disabled} ischecked={ischecked} label={label} /> | ||
</Component> | ||
); | ||
} | ||
|
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.
p5; 여기 수정에 맞게 스토리북쪽에서도 변경 부탁드려용
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.
ask;
ischecked 라는 네이밍으로 바꾼 이유가 있으실까요?! 카멜케이스로 유지하는 게 좋을 것 같아서요!
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.
aria 표준 지키려면 role이 checkbox일때
aria-checked
를 쓰기보다는ischecked
라는 값을 컴포넌트에 그대로 전달해주어야 하더라구요~ 🥹 그동안 진행햇던 test에서 warning이 떠서 고냥 고쳐두었어용