Skip to content

Commit 4b400d2

Browse files
committed
feat: Filter class 구현, Filter 관련 컴포넌트 ts 적용
- 기존의 상수화된 FILTER에서 Filter 객체를 갖고 있도록 변경 및 패키지 이전
1 parent 1c51396 commit 4b400d2

12 files changed

+130
-87
lines changed

Diff for: src/_testUtils/render.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { filterState } from "../state/filterState";
55

66
import { render } from "@testing-library/react";
77

8-
import { FILTER } from "../utils/filter";
8+
import { FILTER } from "../domain/Filter";
99

1010
export function renderWithRecoil(
1111
ui,

Diff for: src/component/TodoFilter.test.jsx

-19
This file was deleted.

Diff for: src/component/TodoFilter.test.tsx

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from "react";
2+
import { render } from "@testing-library/react";
3+
4+
import TodoFilter, { Props } from "./TodoFilter";
5+
import Filter, { FILTER } from "../domain/Filter";
6+
7+
describe("TodoFilter", () => {
8+
function renderFilter({ selected, onSelect }: Props) {
9+
return render(<TodoFilter selected={selected} onSelect={onSelect} />);
10+
}
11+
12+
it("render all filterState btn", () => {
13+
const { container } = renderFilter({
14+
selected: FILTER.ALL,
15+
onSelect: jest.fn(),
16+
});
17+
18+
Object.values(FILTER).forEach((filter: Filter) =>
19+
expect(container).toHaveTextContent(filter.getText()),
20+
);
21+
});
22+
});

Diff for: src/component/TodoFilter.jsx renamed to src/component/TodoFilter.tsx

+11-4
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,26 @@ import React from "react";
22

33
import TodoFilterItem from "./TodoFilterItem";
44

5-
import { FILTER } from "../utils/filter";
5+
import Filter, { FILTER } from "../domain/Filter";
66

7-
export default function TodoFilter({ selected, onSelect }) {
7+
export interface Props {
8+
selected: Filter;
9+
onSelect: Function;
10+
}
11+
12+
const TodoFilter: React.FC<Props> = ({ selected, onSelect }) => {
813
return (
914
<ul className="filters">
1015
{Object.values(FILTER).map((filter) => (
1116
<TodoFilterItem
12-
key={filter.state}
17+
key={filter.getState()}
1318
filter={filter}
1419
isSelected={selected === filter}
1520
onSelect={onSelect}
1621
/>
1722
))}
1823
</ul>
1924
);
20-
}
25+
};
26+
27+
export default TodoFilter;

Diff for: src/component/TodoFilterItem.jsx

-24
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import React from "react";
22
import { render, fireEvent } from "@testing-library/react";
3+
// @ts-ignore
34
import context from "jest-plugin-context";
45

56
import TodoFilterItem from "./TodoFilterItem";
6-
import { FILTER, findFilterByState } from "../utils/filter";
7+
import Filter, { FILTER } from "../domain/Filter";
8+
import { Props } from "./TodoFilterItem";
79

810
describe("TodoFilterItem", () => {
9-
function renderItem({ filter, isSelected, onSelect }) {
11+
function renderItem({ filter, isSelected, onSelect }: Props) {
1012
return render(
1113
<TodoFilterItem
1214
filter={filter}
@@ -19,25 +21,31 @@ describe("TodoFilterItem", () => {
1921
context("when selected", () => {
2022
const filter = FILTER.ALL;
2123
const isSelected = true;
24+
const onSelect = jest.fn();
2225

2326
it("render filterState btn with selected class", () => {
24-
const { getByText } = renderItem({ filter, isSelected });
25-
const $btn = getByText(filter.text);
27+
const { getByText } = renderItem({
28+
filter,
29+
isSelected,
30+
onSelect,
31+
});
32+
const $btn = getByText(filter.getText());
2633

27-
expect($btn).toHaveClass(filter.state);
34+
expect($btn).toHaveClass(filter.getState());
2835
expect($btn).toHaveClass("selected");
2936
});
3037
});
3138

3239
context("when not selected", () => {
3340
const filter = FILTER.ALL;
3441
const isSelected = false;
42+
const onSelect = jest.fn();
3543

3644
it("render filterState btn without selected class", () => {
37-
const { getByText } = renderItem({ filter, isSelected });
38-
const $btn = getByText(filter.text);
45+
const { getByText } = renderItem({ filter, isSelected, onSelect });
46+
const $btn = getByText(filter.getText());
3947

40-
expect($btn).toHaveClass(filter.state);
48+
expect($btn).toHaveClass(filter.getState());
4149
expect($btn).not.toHaveClass("selected");
4250
});
4351
});
@@ -47,10 +55,10 @@ describe("TodoFilterItem", () => {
4755
const isSelected = false;
4856
const onSelect = jest.fn();
4957
const { getByText } = renderItem({ filter, isSelected, onSelect });
50-
const $btn = getByText(filter.text);
58+
const $btn = getByText(filter.getText());
5159

5260
fireEvent.click($btn);
5361

54-
expect(onSelect).toBeCalledWith(findFilterByState(filter.state));
62+
expect(onSelect).toBeCalledWith(Filter.findFilter(filter));
5563
});
5664
});

Diff for: src/component/TodoFilterItem.tsx

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from "react";
2+
import Filter from "../domain/Filter";
3+
4+
export interface Props {
5+
filter: Filter;
6+
isSelected: boolean;
7+
onSelect: Function;
8+
}
9+
10+
const TodoFilterItem: React.FC<Props> = ({ filter, isSelected, onSelect }) => {
11+
const className = [filter.getState(), isSelected ? "selected" : ""]
12+
.join(" ")
13+
.trim();
14+
15+
const handleSelectFilter = () => {
16+
const selected = Filter.findFilter(filter);
17+
onSelect(selected);
18+
};
19+
20+
return (
21+
<li>
22+
<a
23+
className={className}
24+
href={filter.getHref()}
25+
onClick={handleSelectFilter}>
26+
{filter.getText()}
27+
</a>
28+
</li>
29+
);
30+
};
31+
32+
export default TodoFilterItem;

Diff for: src/component/TodoListContainer.test.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import context from "jest-plugin-context";
55
import { renderWithRecoil } from "../_testUtils/render";
66

77
import TodoListContainer from "./TodoListContainer";
8-
import { FILTER } from "../utils/filter";
8+
import { FILTER } from "../domain/Filter";
99

1010
const INCOMPLETE_TODO = { id: 1, content: "incomplete", completed: false };
1111
const COMPLETED_TODO = { id: 2, content: "completed", completed: true };
@@ -87,7 +87,7 @@ describe("TodoListContainer", () => {
8787

8888
it("render only complete todo", () => {
8989
const { container, getByText } = renderContainer({ todos });
90-
const $completedFilterBtn = getByText(filter.text);
90+
const $completedFilterBtn = getByText(filter.getText());
9191

9292
fireEvent.click($completedFilterBtn);
9393

@@ -102,7 +102,7 @@ describe("TodoListContainer", () => {
102102

103103
it("render only complete todo", () => {
104104
const { container, getByText } = renderContainer({ todos });
105-
const $activeFilterBtn = getByText(filter.text);
105+
const $activeFilterBtn = getByText(filter.getText());
106106

107107
fireEvent.click($activeFilterBtn);
108108

Diff for: src/domain/Filter.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export default class Filter {
2+
constructor(
3+
private state: string,
4+
private text: string,
5+
private href: string,
6+
) {}
7+
8+
public static findFilter(target: Filter) {
9+
return Object.values(FILTER).find((filter: Filter) =>
10+
filter.isSame(target),
11+
);
12+
}
13+
14+
private isSame(target: Filter) {
15+
return this === target;
16+
}
17+
18+
public getState() {
19+
return this.state;
20+
}
21+
22+
public getText() {
23+
return this.text;
24+
}
25+
26+
public getHref() {
27+
return this.href;
28+
}
29+
}
30+
31+
const ALL: Filter = new Filter("all", "전체보기", "/#");
32+
const ACTIVE: Filter = new Filter("active", "해야할 일", "/#active");
33+
const COMPLETED: Filter = new Filter("completed", "완료한 일", "/#completed");
34+
35+
const FILTER = {
36+
ALL,
37+
ACTIVE,
38+
COMPLETED,
39+
};
40+
41+
export { FILTER };

Diff for: src/state/filterState.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { atom } from "recoil";
2-
import { FILTER } from "../utils/filter";
2+
import { FILTER } from "../domain/Filter";
33

44
export const filterState = atom({
55
key: "filterState",

Diff for: src/state/todoState.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { atom, selector } from "recoil";
22
import { filterState } from "./filterState";
3-
4-
import { FILTER } from "../utils/filter";
3+
import { FILTER } from "../domain/Filter";
54

65
export const todoState = atom({
76
key: "todoState",

Diff for: src/utils/filter.js

-23
This file was deleted.

0 commit comments

Comments
 (0)