Skip to content

Commit

Permalink
feat: Filter class 구현, Filter 관련 컴포넌트 ts 적용
Browse files Browse the repository at this point in the history
 - 기존의 상수화된 FILTER에서 Filter 객체를 갖고 있도록 변경 및 패키지 이전
  • Loading branch information
hwookim committed Jan 7, 2021
1 parent 1c51396 commit 4b400d2
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 87 deletions.
2 changes: 1 addition & 1 deletion src/_testUtils/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { filterState } from "../state/filterState";

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

import { FILTER } from "../utils/filter";
import { FILTER } from "../domain/Filter";

export function renderWithRecoil(
ui,
Expand Down
19 changes: 0 additions & 19 deletions src/component/TodoFilter.test.jsx

This file was deleted.

22 changes: 22 additions & 0 deletions src/component/TodoFilter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import { render } from "@testing-library/react";

import TodoFilter, { Props } from "./TodoFilter";
import Filter, { FILTER } from "../domain/Filter";

describe("TodoFilter", () => {
function renderFilter({ selected, onSelect }: Props) {
return render(<TodoFilter selected={selected} onSelect={onSelect} />);
}

it("render all filterState btn", () => {
const { container } = renderFilter({
selected: FILTER.ALL,
onSelect: jest.fn(),
});

Object.values(FILTER).forEach((filter: Filter) =>
expect(container).toHaveTextContent(filter.getText()),
);
});
});
15 changes: 11 additions & 4 deletions src/component/TodoFilter.jsx → src/component/TodoFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@ import React from "react";

import TodoFilterItem from "./TodoFilterItem";

import { FILTER } from "../utils/filter";
import Filter, { FILTER } from "../domain/Filter";

export default function TodoFilter({ selected, onSelect }) {
export interface Props {
selected: Filter;
onSelect: Function;
}

const TodoFilter: React.FC<Props> = ({ selected, onSelect }) => {
return (
<ul className="filters">
{Object.values(FILTER).map((filter) => (
<TodoFilterItem
key={filter.state}
key={filter.getState()}
filter={filter}
isSelected={selected === filter}
onSelect={onSelect}
/>
))}
</ul>
);
}
};

export default TodoFilter;
24 changes: 0 additions & 24 deletions src/component/TodoFilterItem.jsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React from "react";
import { render, fireEvent } from "@testing-library/react";
// @ts-ignore
import context from "jest-plugin-context";

import TodoFilterItem from "./TodoFilterItem";
import { FILTER, findFilterByState } from "../utils/filter";
import Filter, { FILTER } from "../domain/Filter";
import { Props } from "./TodoFilterItem";

describe("TodoFilterItem", () => {
function renderItem({ filter, isSelected, onSelect }) {
function renderItem({ filter, isSelected, onSelect }: Props) {
return render(
<TodoFilterItem
filter={filter}
Expand All @@ -19,25 +21,31 @@ describe("TodoFilterItem", () => {
context("when selected", () => {
const filter = FILTER.ALL;
const isSelected = true;
const onSelect = jest.fn();

it("render filterState btn with selected class", () => {
const { getByText } = renderItem({ filter, isSelected });
const $btn = getByText(filter.text);
const { getByText } = renderItem({
filter,
isSelected,
onSelect,
});
const $btn = getByText(filter.getText());

expect($btn).toHaveClass(filter.state);
expect($btn).toHaveClass(filter.getState());
expect($btn).toHaveClass("selected");
});
});

context("when not selected", () => {
const filter = FILTER.ALL;
const isSelected = false;
const onSelect = jest.fn();

it("render filterState btn without selected class", () => {
const { getByText } = renderItem({ filter, isSelected });
const $btn = getByText(filter.text);
const { getByText } = renderItem({ filter, isSelected, onSelect });
const $btn = getByText(filter.getText());

expect($btn).toHaveClass(filter.state);
expect($btn).toHaveClass(filter.getState());
expect($btn).not.toHaveClass("selected");
});
});
Expand All @@ -47,10 +55,10 @@ describe("TodoFilterItem", () => {
const isSelected = false;
const onSelect = jest.fn();
const { getByText } = renderItem({ filter, isSelected, onSelect });
const $btn = getByText(filter.text);
const $btn = getByText(filter.getText());

fireEvent.click($btn);

expect(onSelect).toBeCalledWith(findFilterByState(filter.state));
expect(onSelect).toBeCalledWith(Filter.findFilter(filter));
});
});
32 changes: 32 additions & 0 deletions src/component/TodoFilterItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";
import Filter from "../domain/Filter";

export interface Props {
filter: Filter;
isSelected: boolean;
onSelect: Function;
}

const TodoFilterItem: React.FC<Props> = ({ filter, isSelected, onSelect }) => {
const className = [filter.getState(), isSelected ? "selected" : ""]
.join(" ")
.trim();

const handleSelectFilter = () => {
const selected = Filter.findFilter(filter);
onSelect(selected);
};

return (
<li>
<a
className={className}
href={filter.getHref()}
onClick={handleSelectFilter}>
{filter.getText()}
</a>
</li>
);
};

export default TodoFilterItem;
6 changes: 3 additions & 3 deletions src/component/TodoListContainer.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import context from "jest-plugin-context";
import { renderWithRecoil } from "../_testUtils/render";

import TodoListContainer from "./TodoListContainer";
import { FILTER } from "../utils/filter";
import { FILTER } from "../domain/Filter";

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

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

fireEvent.click($completedFilterBtn);

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

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

fireEvent.click($activeFilterBtn);

Expand Down
41 changes: 41 additions & 0 deletions src/domain/Filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export default class Filter {
constructor(
private state: string,
private text: string,
private href: string,
) {}

public static findFilter(target: Filter) {
return Object.values(FILTER).find((filter: Filter) =>
filter.isSame(target),
);
}

private isSame(target: Filter) {
return this === target;
}

public getState() {
return this.state;
}

public getText() {
return this.text;
}

public getHref() {
return this.href;
}
}

const ALL: Filter = new Filter("all", "전체보기", "/#");
const ACTIVE: Filter = new Filter("active", "해야할 일", "/#active");
const COMPLETED: Filter = new Filter("completed", "완료한 일", "/#completed");

const FILTER = {
ALL,
ACTIVE,
COMPLETED,
};

export { FILTER };
2 changes: 1 addition & 1 deletion src/state/filterState.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { atom } from "recoil";
import { FILTER } from "../utils/filter";
import { FILTER } from "../domain/Filter";

export const filterState = atom({
key: "filterState",
Expand Down
3 changes: 1 addition & 2 deletions src/state/todoState.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { atom, selector } from "recoil";
import { filterState } from "./filterState";

import { FILTER } from "../utils/filter";
import { FILTER } from "../domain/Filter";

export const todoState = atom({
key: "todoState",
Expand Down
23 changes: 0 additions & 23 deletions src/utils/filter.js

This file was deleted.

0 comments on commit 4b400d2

Please sign in to comment.