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

[1단계 - 음식점 목록] 루루(송지은) 미션 제출합니다. #18

Merged
merged 53 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
915bebb
docs: step1 기능 요구 사항 작성
ksone02 Feb 28, 2023
278d6a1
feat: 음식점 인터페이스 구현
ksone02 Feb 28, 2023
32caaa9
feat: 음식점 목록 객체 구현
ksone02 Feb 28, 2023
e9c2659
feat: 음식점 카테고리 필터 기능 구현
ksone02 Feb 28, 2023
d66c335
feat: CustomElement 생성
ksone02 Feb 28, 2023
5c18b7e
feat: 음식점 컴포넌트 구현
ksone02 Feb 28, 2023
b448699
feat: 음식점 목록 컴포넌트 구현
ksone02 Feb 28, 2023
13a337b
feat: 헤더 컴포넌트 구현
ksone02 Feb 28, 2023
889c4ba
feat: 모달 컴포넌트 구현
ksone02 Feb 28, 2023
d5e8df9
feat: 음식점 추가 폼 컴포넌트 구현
ksone02 Feb 28, 2023
6cd4e0c
feat: 버튼 컴포넌트 구현
ksone02 Feb 28, 2023
efb88b0
feat: 모달 컴포넌트 구현
ksone02 Feb 28, 2023
b32e0cc
feat: 셀렉터 컴포넌트 구현
ksone02 Feb 28, 2023
3580bd3
feat: 컴포넌트 조립
ksone02 Feb 28, 2023
75d5da9
docs: 컴포넌트 구현 리스트 업데이트
ksone02 Feb 28, 2023
1de5178
feat: button 컴포넌트 스타일 설정
ksone02 Mar 2, 2023
cf6590a
fix: button 컴포넌트 css 변경
ksone02 Mar 2, 2023
4a40de8
feat: Modal 닫기 기능 구현
ksone02 Mar 2, 2023
bf1c249
feat: Restaurants Store 로 구현(Flux 패턴 적용)
ksone02 Mar 2, 2023
3437d4b
feat: 모달 닫는 기능 구현
ksone02 Mar 2, 2023
4e9054e
feat: 음식점 추가 버튼 이미지 임포트
ksone02 Mar 2, 2023
d3c2373
feat: Dispatcher 구현
ksone02 Mar 2, 2023
c022915
refactor: Restaurant 객체 프로퍼티 네임 변경
ksone02 Mar 2, 2023
b28437a
feat: 레스토랑 추가 후 렌더링 구현
ksone02 Mar 2, 2023
9022853
feat: localStorage 연동 기능 구현
ksone02 Mar 2, 2023
5861de3
fix: Restaurants 컴포넌트 사이 콤마 이슈 제거
ksone02 Mar 2, 2023
494ef13
feat: 아이콘 이미지 적용
ksone02 Mar 2, 2023
4fbc782
feat: 카테고리 정렬 기능 구현
ksone02 Mar 2, 2023
80705cc
feat: header 고정
ksone02 Mar 2, 2023
7c4c15f
feat: 정렬 기능 구현
ksone02 Mar 2, 2023
4b74b3a
fix: 음식점 추가 required 항목 무시 이슈 해결
ksone02 Mar 3, 2023
bd6d296
chore: npm test-unit시 jsdom 환경에서 할 수 있도록
ksone02 Mar 3, 2023
69e0713
test: 모달 렌더링 테스트
ksone02 Mar 3, 2023
cfbfd18
fix: 로컬스토리지 저장 안되는 이슈 해결
ksone02 Mar 3, 2023
0f77410
feat: 음식점 카테고리 아이콘 적용
ksone02 Mar 3, 2023
eff75c9
docs: 요구사항 업데이트
ksone02 Mar 3, 2023
1a55cd2
feat: 버튼 컴포넌트 제거 후 원상복귀
ksone02 Mar 3, 2023
091c83c
fix: restaurant 컴포넌트 속성 렌더링 오류 해결
ksone02 Mar 3, 2023
4197128
feat: submit 후 폼 초기화
ksone02 Mar 3, 2023
fa157ca
fix: form 사용시 페이지 이동 방지
ksone02 Mar 3, 2023
1c9c036
refactor: 폴더 구조 리팩터링
ksone02 Mar 3, 2023
d15f7bf
refactor: 안쓰는 임포트 삭제
ksone02 Mar 3, 2023
ebee1f6
refactor: 상수화
ksone02 Mar 3, 2023
dca2497
refactor: index 주석 추가
ksone02 Mar 3, 2023
6ce1454
docs: 미션페이지 추가
hafnium1923 Mar 3, 2023
97dccc1
refactor: index.html 사용하지 않는 태그 삭제
hafnium1923 Mar 6, 2023
82c0c75
refactor: addEventListener 화살표함수 제거
hafnium1923 Mar 6, 2023
0200681
refactor: addRestaurant역할 분리 및 모달 버튼 관리 위치 변경
hafnium1923 Mar 6, 2023
b3182b0
refator: filter나 sort 호출 시 publish에서만 수행하도록 리팩터링
hafnium1923 Mar 6, 2023
4298a80
fix: 필터 적용 후 음식점 추가 시 다른 정보 없어지는 오류 수정
hafnium1923 Mar 6, 2023
1257559
refactor: switch default 추가
hafnium1923 Mar 6, 2023
36b628b
refactor: 도메인 로직 분리
hafnium1923 Mar 6, 2023
b6726d4
refactor: 종속적이지 않은 dispatcher
hafnium1923 Mar 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
# javascript-lunch

우아한테크코스 레벨1 점심 뭐 먹지 미션

## 미션 페이지

https://hafnium1923.github.io/javascript-lunch/
48 changes: 48 additions & 0 deletions __tests__/render.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
import { screen, fireEvent } from "@testing-library/dom";
import "@testing-library/jest-dom";
import HeaderComponent from "../src/components/HeaderComponent";
import ModalComponent from "../src/components/modal/ModalComponent";

/**
* @jest-environment jsdom
*/

describe("모달 렌더링 테스트", () => {
beforeEach(() => {
document.body.innerHTML = "";
});
test("음식점 추가 버튼 클릭하면 모달 창이 열린다.", () => {
// given
const header = new HeaderComponent();
const modal = new ModalComponent();

document.body.innerHTML = `
<header-element></header-element>
<modal-element></modal-element>
`;

// when
fireEvent.click(screen.getByLabelText("음식점 추가"));

// then
expect(
document.querySelector(".modal").classList.contains("modal--open")
).toBeTruthy();
});

test("취소하기 버튼 클릭하면 모달 창이 닫힌다.", () => {
//given
const header = new HeaderComponent();
const modal = new ModalComponent();
document.body.innerHTML = `
<header-element></header-element>
<modal-element></modal-element>
`;
header.show();

//when
fireEvent.click(screen.getByText("취소하기"));

//then
expect(
document.querySelector(".modal").classList.contains("modal--open")
).toBeFalsy();
});
});
Empty file removed __tests__/unit.test.ts
Empty file.
22 changes: 22 additions & 0 deletions docs/REQUIREMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## 기능 요구 사항 STEP1

- 음식점 목록 객체 Restaurants RestaurantList
- 카테고리 필터 기능
- 이름순 정렬기능, 거리순 정렬기능
- 음식점 추가하는 기능
- 음식점 객체 Restaurant

- 필수: 카테고리, 이름, 거리 옵션: 설명, 참고링크

- 새로고침해도 추가한 음식점 정보 유지 -> local storage

- 컴포넌트 단위로 UI 나누기

## 컴포넌트

[x] 헤더
[x] 필터/정렬하는 셀렉터
[x] 음식점 리스트 컴포넌트
[X] 음식점 컴포넌트
[x] 모달 컴포넌트 - 음식점 추가 컴포넌트
[X] 버튼(모달에 달려있는)
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
28 changes: 19 additions & 9 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>점심 뭐 먹지</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<!-- GNB -->
<header-element></header-element>

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>점심 뭐 먹지</title>
</head>
<main>
<!-- 카테고리/정렬 필터 -->
<select-element></select-element>

<body>

</body>
<!-- 음식점 목록 -->
<section class="restaurant-list-container">
<restaurant-list></restaurant-list>
</section>

<!-- 음식점 추가 모달 -->
<modal-element></modal-element>
</main>
</body>
</html>
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"watch": "webpack --watch",
"start": "webpack serve",
"build": "webpack --mode=production",
"test-unit": "jest --watch",
"test-unit": "jest --watch --env=jsdom",
"test-e2e": "cypress open"
},
"repository": {
Expand Down
16 changes: 16 additions & 0 deletions src/abstracts/CustomElement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class CustomElement extends HTMLElement {
connectedCallback() {
this.render();
this.setEvent();
}

render() {
this.insertAdjacentHTML("beforeend", this.template());
}

template() {}

setEvent() {}
}

export default CustomElement;
25 changes: 25 additions & 0 deletions src/abstracts/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { RestaurantAction } from "./types";

export const RESTAURANT_ACTION: RestaurantAction = {
ADD_RESTAURANT: "add_restaurant",
FILTER_BY_CATEGORY: "filter_by_category",
SORT_RESTAURANTS: "sort_restaurants",
} as const;

export const CATEGORY_IMG = {
한식: "./category-korean.png",
중식: "./category-chinese.png",
일식: "./category-japanese.png",
아시안: "./category-asian.png",
양식: "./category-western.png",
기타: "./category-etc.png",
} as const;

export const RESTAURANTS_STORAGE = "restaurantList";

export const CATEGORY_DEFAULT = "전체";

export const SORT_METHOD = {
NAME: "name",
DISTANCE: "distance",
} as const;
38 changes: 38 additions & 0 deletions src/abstracts/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export type Category =
| "전체"
| "한식"
| "중식"
| "일식"
| "아시안"
| "양식"
| "기타";

export type SortMethod = "name" | "distance";

export interface Restaurant {
category: Category;
name: string;
distance: number;
description?: string;
link?: string;
}

export interface CustomElement extends HTMLElement {
render: () => void;
template: () => string;
setEvent: () => void;
show: () => void;
hide: () => void;
rerender: (data: Restaurant[] | Category | SortMethod) => void;
}

export interface Action {
type: string;
data?: Restaurant | Category | SortMethod;
}

export interface RestaurantAction {
ADD_RESTAURANT: string;
FILTER_BY_CATEGORY: string;
SORT_RESTAURANTS: string;
}
28 changes: 28 additions & 0 deletions src/components/HeaderComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import CustomElement from "../abstracts/CustomElement";

class HeaderComponent extends CustomElement {
show() {
document.querySelector(".modal").classList.add("modal--open");
}
Copy link

Choose a reason for hiding this comment

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

현재 이름만 보면 header components를 show하는 것 같아요. 정확히 어떤걸 show해주는건지 명시해주는 건 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

넵 수정하겠습니다!


setEvent() {
document
.querySelector(".gnb__button")
.addEventListener("click", () => this.show());
}

template() {
return `
<header class="gnb">
<h1 class="gnb__title text-title">점심 뭐 먹지</h1>
<button type="button" class="gnb__button" aria-label="음식점 추가">
<img src="./add-button.png" alt="음식점 추가" />
</button>
</header>
`;
}
}

customElements.define("header-element", HeaderComponent);

export default HeaderComponent;
49 changes: 49 additions & 0 deletions src/components/SelectComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import CustomElement from "../abstracts/CustomElement";
import dispatcher from "../domain/Dispatcher";
import { RESTAURANT_ACTION } from "../abstracts/constants";

class SelectComponent extends CustomElement {
changeCategory() {
const category = document.querySelector("#category-filter").value;
dispatcher(RESTAURANT_ACTION.FILTER_BY_CATEGORY, category);
}

changeSortMethod() {
const sortMethod = document.querySelector("#sorting-filter").value;
dispatcher(RESTAURANT_ACTION.SORT_RESTAURANTS, sortMethod);
}

setEvent() {
document
.querySelector("#category-filter")
.addEventListener("change", () => this.changeCategory());
document
.querySelector("#sorting-filter")
.addEventListener("change", () => this.changeSortMethod());
}

template() {
return `
<section class="restaurant-filter-container">
<select name="category" id="category-filter" class="restaurant-filter">
<option value="전체">전체</option>
<option value="한식">한식</option>
<option value="중식">중식</option>
<option value="일식">일식</option>
<option value="양식">양식</option>
<option value="아시안">아시안</option>
<option value="기타">기타</option>
</select>
<select name="sorting" id="sorting-filter" class="restaurant-filter">
<option value="name">이름순</option>
<option value="distance">거리순</option>
</select>
</section>

`;
}
}

customElements.define("select-element", SelectComponent);

export default SelectComponent;
28 changes: 28 additions & 0 deletions src/components/modal/ModalComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import CustomElement from "../../abstracts/CustomElement";

class ModalComponent extends CustomElement {
hide() {
document.querySelector(".modal").classList.remove("modal--open");
}

setEvent() {
document
.querySelector(".button--secondary")
.addEventListener("click", () => this.hide());
}

template() {
return `
<div class="modal">
<div class="modal-backdrop"></div>
<div class="modal-container">
<restaurant-add-form></restaurant-add-form>
</div>
</div>
`;
}
}

customElements.define("modal-element", ModalComponent);

export default ModalComponent;
Loading