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

[2단계 - 자주 가는 음식점] 해리(최현웅) 미션 제출합니다. #155

Merged
merged 70 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
6f0d55a
docs(README.md): 스텝2에서 추가된 요구사항 작성
hwinkr Mar 14, 2024
13e87fa
docs(README.md): 스텝2에서 추가된 요구사항 작성
hwinkr Mar 14, 2024
5eb7e3e
chore(delete js file): 사용하지 않는 js 파일 삭제
hwinkr Mar 15, 2024
ec3f055
feat(Restauran): 추가된 도메인 요구사항 구현
hwinkr Mar 15, 2024
72a62ee
refactor(BaseComponent): TS로 마이그레이션
hwinkr Mar 15, 2024
c09a809
refactor(MenuApp): UI 로직 TS로 마이그레이션
hwinkr Mar 15, 2024
db3dea7
feat(Restaurants): 음식점 도메인 기능 구현 완료
hwinkr Mar 17, 2024
46653ca
feat(style.css): 스타일 추가
hwinkr Mar 17, 2024
41f1b08
feat(RestaurantTab): 모든 음식점, 자주 가는 음식점 구분 컴포넌트 구현
hwinkr Mar 17, 2024
a3b60ad
feat(CategoryImage): 카테고리 이미지 컴포넌트 구현
hwinkr Mar 17, 2024
6d9f200
feat(RestaurantDetail): 음식점 상세 정보 컴포넌트 구현
hwinkr Mar 17, 2024
ecadfc6
docs(README.md): 추가된 기능 요구 사항 구현 완료
hwinkr Mar 17, 2024
b7329e5
chore(assets): 자주 가는 음식점 별 아이콘 이미지 추가
hwinkr Mar 17, 2024
188b413
chore(delete unused files): 폴더 구조 변경으로 인한 사용하지 않는 파일 삭제
hwinkr Mar 17, 2024
f9f8200
refactor(BaseComponent): 점심 뭐 먹지 애플리케이션에서 발생하는 이벤트 타입 네이밍 수정
hwinkr Mar 17, 2024
77b8110
refactor(MenuApp): html 구조 수정
hwinkr Mar 17, 2024
12ebc28
feat(FavoriteIcon): 자주 가는 식당을 추가할 수 있는 별 모양 아이콘 컴포넌트 추가 분리
hwinkr Mar 17, 2024
2d2b05e
feat(Modal): 재사용할 수 있는 모달 컴포넌트 구현
hwinkr Mar 17, 2024
5e184f6
refactor(RestaurantAddForm): 식당 추가 폼 UI TS로 마이그레이션
hwinkr Mar 17, 2024
dbd4317
chore(change directory): 폴더 구조 변경
hwinkr Mar 17, 2024
794955c
refactor(OptionSelector): 식당 목록 필터링, 정렬 컴포넌트 로직 수정
hwinkr Mar 17, 2024
4774765
chore(event constants): 점심 뭐 먹지에서 발생하는 커스텀 이벤트 상수화
hwinkr Mar 17, 2024
6651777
chore(menu constants): 카테고리, 정렬 기준, 탭 메뉴 문자 상수화
hwinkr Mar 17, 2024
d0caaf6
refactor(index.js): 변경된 폴더 구조 반영
hwinkr Mar 17, 2024
5435219
feat(type checker): 타입을 확인하는 유틸 함수 구현
hwinkr Mar 17, 2024
dafd4c6
feat(dom utils): DOM 노드를 찾는 유틸 함수 구현
hwinkr Mar 17, 2024
75ea25d
refactor(OptionSelector): 필터링, 정렬 값의 타입 확인하는 함수 적용
hwinkr Mar 17, 2024
e37988e
feat(Header): 점심 뭐 먹지 헤더 컴포넌트 TS 마이그레이션
hwinkr Mar 17, 2024
a1e1a89
config(types): 점심 뭐 먹지 이벤트, 상수 타입 설정
hwinkr Mar 17, 2024
003fa7c
chore(index.js): 파일명 오타 수정
hwinkr Mar 17, 2024
c0158c9
config(cypress): cypress 테스트 환경 설정
hwinkr Mar 17, 2024
0279431
test(RestaurantAddForm): 음식점 추가 폼 테스트
hwinkr Mar 17, 2024
8ecc48e
test(RestaurantList): 음식점 목록 테스트
hwinkr Mar 17, 2024
e52abc1
test(RestaurantDetail): 음식점 상세 정보 테스트
hwinkr Mar 17, 2024
842dc67
refactor(OptionSelector): 필터링, 정렬 옵션 컴포넌트 리팩터링
hwinkr Mar 17, 2024
f634e1c
chore(RestaurantAddForm): 에러 객체의 메시지 프로퍼티를 alert 함수로 전달하도록 수정
hwinkr Mar 17, 2024
05cbb0a
chore(RestaurantDetail): 상세 정보 컴포넌트 내부 버튼 type 제거
hwinkr Mar 17, 2024
6660602
chore(constants): 음식점 디폴트 데이터 순서 수정
hwinkr Mar 17, 2024
2660380
chore(type): 사용하지 않는 의존성 제거
hwinkr Mar 17, 2024
a695f12
chore(delete unused file): 사용하지 않는 파일 삭제
hwinkr Mar 17, 2024
b7852b8
chore(change file name): 컴포넌트 이름 변경으로 인한 파일 이름 변경 반영
hwinkr Mar 17, 2024
bcb84ad
refactor(RestaurantValidator): 음식점 추가 폼 입력값에 대한 유효성 검증 TS로 마이그레이션
hwinkr Mar 17, 2024
af74fab
refactor(Restaurants): 카테고리, 도보 이동 거리 유효성 검증 로직 수정
hwinkr Mar 17, 2024
b14dedd
refactor(RestaurantAddForm): 음식점 추가 폼 컴포넌트 리팩터링
hwinkr Mar 17, 2024
b4ca733
refactor(ResturantOption): 음식점 옵션 선택 컴포넌트 커스텀 이벤트 이름 수정
hwinkr Mar 17, 2024
eaf0992
feat(RestaurantTextInput): 재사용 가능한 입력 컴포넌트 구현
hwinkr Mar 17, 2024
d73df87
test(RestaurantAddTest): 변경된 id 이름 반영
hwinkr Mar 17, 2024
309dcca
feat(text utils): 문자열 사이의 모든 공백을 제거하는 유틸 함수 분리
hwinkr Mar 18, 2024
fb86be6
chore(change method name): 모달을 여는 이벤트인지 확인하는 메서드 네이밍 수정
hwinkr Mar 18, 2024
c73d982
chore(RestaurantAddForm): restaurant-option 컴포넌트에 전달하는 어트리뷰트 순서 통일
hwinkr Mar 18, 2024
cd60e5f
chore(add dependency): 문자열 사이 모든 공백을 제거하는 함수 의존성 추가
hwinkr Mar 18, 2024
97ceeea
chore(change file name): 파일이름 오타 수정
hwinkr Mar 19, 2024
e58c478
change(change directory): OptionSelectorContainer 파일 경로 수정
hwinkr Mar 19, 2024
7501413
refactor(dom utils): type asseriton 제거
hwinkr Mar 19, 2024
3fb961a
refactor(type utils): 여러 상황에서 범용적으로 사용할 수 있도록 함수명 수정
hwinkr Mar 19, 2024
c3612a2
test(RestaurantDetail): 음식점 상세 모달 테스트 수정
hwinkr Mar 19, 2024
2f2cf84
test(RestaurantList): 음식점 목록 테스트 수정
hwinkr Mar 19, 2024
323add7
test(RestaurantAddTest): 유효하지 않는 상황을 주석으로 설명 추가
hwinkr Mar 19, 2024
8ec47cf
refactor(CategoryImage): 추상화 된 타입 가드 함수를 사용하는 것으로 수정
hwinkr Mar 19, 2024
17691a0
refactor(MenuApp): 모달 컴포넌트에서 사용하지 않는 어트리뷰트 삭제
hwinkr Mar 19, 2024
42d0917
refactor(Modal): 모달 컴포넌트 내부에서 id 어트리뷰트를 사용해서 이벤트를 결정하는 것으로 수정
hwinkr Mar 19, 2024
9e4b00f
refactor(OptionSelector): 추상화된 타입 가드 함수를 사용하는 것으로 수정
hwinkr Mar 19, 2024
1cb1d02
refactor(RestaurantAddForm): 객체 타입 가드 함수 이름 변경 반영
hwinkr Mar 19, 2024
4d0c2f3
refactor(RestaurantDetail): 객체 타입 가드 함수 이름 변경 반영
hwinkr Mar 19, 2024
4599c6c
chore(RestaurantList): 라인 구분 수정
hwinkr Mar 19, 2024
c701028
config: img 파일 타입 설정
hwinkr Mar 19, 2024
c5ade21
chore(change file): 이미지 파일을 관리하는 asset 파일 TS로 확장자 변경
hwinkr Mar 19, 2024
2bebec1
refactor(FavoriteIcon): isFavorite(boolean) 상태 값 결정 로직 삼항 연산자에서 비교 연산…
hwinkr Mar 19, 2024
191f542
chore(Modal): 사용하지 않는 메서드 제거
hwinkr Mar 19, 2024
c818b2d
chore(RestaurantDetail): 참고 링크가 존재하지 않을 경우 보여주는 텍스트 색 수정
hwinkr Mar 19, 2024
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
122 changes: 93 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,114 @@

# 기능 요구사항

## 1. UI
# Domain

### header
- [x] 음식점을 카테고리별로 필터링 한다.
- [x] 음식점을 이름순, 거리순으로 정렬한다.
- [x] 음식점 등록 폼을 제출하면 추가한다.
- [x] 추가하기 버튼을 클릭할 경우 입력에 대한 유효성 검증을 한다
- 음식점 이름은 10글자 이하여야한다.
- 음식점 설명은 300자 이하여야한다.
- 유효한 참고 링크인지 검증한다.
- [x] 음식점 이름으로 식당의 정보를 찾을 수 있도록 한다.
- [x] 유효한 입력일 경우 localStorage에 추가한다.
- [x] 자주 가는 음식점인지 아닌지에 대해 상태를 변경(토글)한다.
- [x] 자주 가는 음식점 목록을 필터링한다.
- [x] 음식점 목록에 있는 음식점을 삭제한다.

```ts
addFavoriteRestaurant(restaurantName ; string) : boolean
getFavoriteRestaurant() : Restaurant[]
delete(restaurantName : string) : boolean
```

# UI

- [x] 점심 뭐 먹지 타이틀
- [x] 음식점 추가 버튼
## header

#### event
- UI
- [x] 점심 뭐 먹지 타이틀
- [x] 음식점 추가 버튼
- Event
- [x] 음식점 추가 버튼을 클릭하면 음식점 추가 폼 모달이 보여져야한다.

- [x] 음식점 추가 버튼을 클릭하면 음식점 추가 폼 모달이 보여져야한다.
## option-selector

### option-selector
- UI

- [x] 카테고리를 필터링할 수 있는 옵션 버튼
- [x] 이름, 거리순을 정렬할 수 있는 옵션 버튼

#### event
- Event

- [x] 카테코리를 선택하면 선택된 카테고리에 해당되는 레스토랑 리스트를 보여준다
- [x] 이름, 거리순을 정렬하는 버튼을 클릭하면 정렬된 레스토랑 리스트를 보여준다.

### restaurant-list
## restaurant-list

- UI

- [x] restaurant-item

- 음식점 목록을 확인할 수 있다.
- 카테고리
- 레스토랑 이름
- (캠퍼스로 부터의) 거리
- 설명, 설명은 2줄 이상이 넘어가면 ...으로 내용을 숨긴다

- Event

- [x] 카테고리별 필터링, 정렬 옵션이 변경되는 이벤트가 발생하면 그에 맞춰 보여줘야 할 음식점 목록을 변경한다.
- [x] 모든 음식점, 자주 가는 음식점을 클릭할 경우 그에 맞춰 보여줘야 할 음식점 목록을 변경한다.

## favorite-icon

- UI

- [x] 별 모양 아이콘

- Event

- [x] 아이콘을 클릭할 때마다, 아이콘 이미지가 토글되어야 한다. (filled <-> lined)
- [x] 아이콘을 클릭할 때마다, 식당 아이템의 isFavorite 상태가 토글되어야 한다. (true <-> false)
- [x] 아이콘을 클릭할 때마다, 식당 목록 컴포넌트가 렌더링 되어 변경된 상태를 보여줄 수 있어야한다.

## category-image

- [x] 선택된 옵션에 따라 레스토랑 리스트를 보여준다
- UI

- 음식점 목록을 확인할 수 있다.
- 카테고리
- 레스토랑 이름
- (캠퍼스로 부터의) 거리
- 설명
- 설명은 2줄 이상이 넘어가면 ...으로 내용을 숨긴다
- [x] 전체, 한식, 중식,,, 등 카테고리에 따라 다른 이미지를 보여준다.

### restaurant-add-form
## restaurant-detail

- UI
- 음식점 목록 중 하나를 클릭하면 해당 음식점에 대한 세부 정보를 나타낸다.

- [x] 음식점 이름, 카테고리, 캠퍼스로부터의 거리, 자주 가는 음식점인지의 여부
- [x] 음식점 상세 설명, ...으로 표시하지 않고 모든 내용을 보여준다,
- [x] 참고링크

- Event

- [x] 음식점 세부 정보의 바깥 영역을 클릭하거나, 닫기 버튼을 클릭하면 `restaurant-detail` 컴포넌트는 사라져야한다.
- [x] 음식점 세부 정보에서 자주 가는 음식점인지의 여부를 변경하면, 변경이 반영되어야한다.
- [x] 참고 링크를 클릭하면 해당 링크로 이동시킨다.
- [x] 삭제하기 버튼을 클릭할 경우, 해당 음식점을 삭제한 후 `restaurant-detail` 컴포넌트는 사라져야한다.

## restaurant-tab

- UI

- [x] 식당 목록 선택 옵션 (모든 음식점, 자주 가는 음식점)
- [x] 클릭한 버튼에 액티브 스타일이 적용되어야 하며, 이전에 클릭한 버튼의 액티브 스타일은 사라져야한다.

- Event

- [x] 특정 버튼을 클릭할 때마다, 보여줘야 할 음식점 목록의 변경을 트리거한다.

## restaurant-add-form

- UI

- [x] 새로운 음식점 타이틀
- [x] 카테코리 선택 옵션 (한식, 중식, 일식, 양식, 아시안, 기타)
Expand All @@ -46,19 +121,8 @@
- [x] 취소, 추가 버튼
- [ ] 유효하지 않은 입력값이 들어온 경우 해당 입력값 아래에 빨간색 에러 메시지를 보여준다.

#### event
- Event

- [x] 취소 버튼을 클릭하면 추가 폼을 리셋하고 닫는다.
- [x] 추가하기 버튼을 클릭하면 추가 폼을 닫고, 추가된 레스토랑을 레스토랑 리스트에 반영한다.
- [x] 추가 폼 외부를 클릭 시 폼을 닫는다. 외부를 클릭할 경우 입력한 값들을 리셋하지 않고 닫기만 한다.

## 2. Domain

- [x] 음식점을 카테고리별로 필터링 한다.
- [x] 음식점을 이름순, 거리순으로 정렬한다.
- [x] 음식점 등록 폼을 제출하면 추가한다.
- [ ] 추가하기 버튼을 클릭할 경우 입력에 대한 유효성 검증을 한다
- 음식점 이름은 10글자 이하여야한다.
- 음식점 설명은 300자 이하여야한다.
- 유효한 참고 링크인지 검증한다.
- [ ] 유효한 입력일 경우 localStorage에 추가한다.
8 changes: 5 additions & 3 deletions src/assets/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export { default as addButton } from "../../templates/add-button.png";
export { default as categoryKorean } from "../../templates/category-korean.png";
export { default as categoryAsian } from "../../templates/category-asian.png";
export { default as categoryChinese } from "../../templates/category-chinese.png";
export { default as categoryEtc } from "../../templates/category-etc.png";
export { default as categoryJapanese } from "../../templates/category-japanese.png";
export { default as categoryKorean } from "../../templates/category-korean.png";
export { default as categoryChinese } from "../../templates/category-chinese.png";
export { default as categoryWestern } from "../../templates/category-western.png";
export { default as categoryEtc } from "../../templates/category-etc.png";
export { default as linedFavoriteIcon } from "../../templates/favorite-icon-lined.png";
export { default as filledFavoriteIcon } from "../../templates/favorite-icon-filled.png";
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { TotalMenuAppEvent } from "../types/event";

export default class BaseComponent extends HTMLElement {
constructor() {
super();
}

connectedCallback() {
protected connectedCallback() {
this.render();
this.setEvent();
}

disconnectedCallback() {}
protected disconnectedCallback() {}

render() {}
protected render() {}

setEvent() {}
protected setEvent() {}

emitEvent(event, data) {
protected emitEvent<T>(event: TotalMenuAppEvent, data?: T) {
this.dispatchEvent(
new CustomEvent(event, {
bubbles: true,
Expand Down
52 changes: 52 additions & 0 deletions src/components/CategoryImage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import BaseComponent from "../BaseComponent";
import { CATEGORIES } from "../../constants/menu";
import { CategoryStringWithoutAll } from "../../types/menu";
import {
categoryKorean,
categoryAsian,
categoryChinese,
categoryJapanese,
categoryWestern,
categoryEtc,
} from "../../assets";

class CategoryImage extends BaseComponent {
private isCategoryType(category: string): category is CategoryStringWithoutAll {
return ["한식", "아시안", "일식", "중식", "양식", "기타"].includes(category);
Copy link

Choose a reason for hiding this comment

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

새로운 음식 카테고리가 추가되면 여기만 누락될 여지가 있어보여요. 정의해둔 CATEGORIES 상수를 활용할 수 있을까요~?

Copy link
Author

Choose a reason for hiding this comment

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

저도 정의해 둔 상수를 활용하는 것을 고려해봤었는데,

  private isCategoryType(category: string): category is CategoryStringWithoutAll {
    return (Object.values(CATEGORIES)).includes(category);
  }

이 방식으로 사용할 경우 Object.values 메서드가 반환하는 데이터의 타입이
image
위 사진과 같이 추론되어 string을 인자로 받는 isCategoryType 메서드에서

Argument of type 'string' is not assignable to parameter of type '"전체" | "한식" | "아시안" | "일식" | "중식" | "양식" | "기타"'.

위와 같은 에러가 발생하는 것을 확인할 수 있었습니다. type assertion을 지양하는 것이 좋다고는 하지만 새로운 카테고리가 추가될 때 누락되는 상황이 발생하는것 보다 type assertion을 사용하더라도 변경에 유연하게 대처되는 코드가 더 좋을 것 같다는 생각이 드네요. Object.values 메서드를 활용하는 것으로 수정해보았습니다!

Copy link
Author

@hwinkr hwinkr Mar 19, 2024

Choose a reason for hiding this comment

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

---- 추가 코멘트 ----

export const isArrayElement = <T extends string>(options: string[], target: string): target is T => {
  return options.includes(target);
};

// category-image
!isArrayElement<CategoryStringWithoutAll>(
        Object.values(CATEGORIES),
        category
)

위와 같이 사용하니 type assertion을 사용하지 않아도 돼서 제거했습니다! 추상화 방법 추천 감사드립니다. 🙇‍♂️

}

private categoryToImg(category: CategoryStringWithoutAll) {
if (!category) return;

switch (category) {
case CATEGORIES.korean:
return categoryKorean;
case CATEGORIES.asian:
return categoryAsian;
case CATEGORIES.japanese:
return categoryJapanese;
case CATEGORIES.chinese:
return categoryChinese;
case CATEGORIES.western:
return categoryWestern;
case CATEGORIES.etc:
return categoryEtc;
default:
break;
}
}

render() {
const category = this.getAttribute("category") ?? "";
if (!this.isCategoryType(category)) return;

const categoryImage = this.categoryToImg(category);
this.innerHTML = /*html*/ `
<div class="restaurant__category">
<img src=${categoryImage} alt=${category} class="category-icon">
</div>
`;
}
}

customElements.define("category-image", CategoryImage);
43 changes: 43 additions & 0 deletions src/components/FavoriteIcon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import BaseComponent from "../BaseComponent";
import { MENU_APP_EVENTS } from "../../constants/event";
import { toggleFavoriteStateByName } from "../../domains/Restaurants";
import { filledFavoriteIcon, linedFavoriteIcon } from "../../assets/index";

class FavoriteIcon extends BaseComponent {
protected render() {
const isFavorite = this.getAttribute("is-favorite") === "true" ? true : false;
Copy link

Choose a reason for hiding this comment

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

간결하게 줄일 수 있을 것 같아요 :)

Suggested change
const isFavorite = this.getAttribute("is-favorite") === "true" ? true : false;
const isFavorite = this.getAttribute("is-favorite") === "true";


this.innerHTML = /*html*/ `
Copy link

Choose a reason for hiding this comment

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

이 주석은 특별한 용도가 있나요?

Copy link
Author

Choose a reason for hiding this comment

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

이번 미션에서 주로 innerHTML을 사용했는데, 문자열로 전달하다보니 코드를 작성할 때 태그, 어트리뷰트 등 모두 색깔이 같아 구분하기 힘들다는 문제가 있었습니다. vsCode의 es6-string-html 익스텐션을 설치하고 /*html*/ 주석을 사용하면 색깔이 구분되어서 해당 주석을 사용하게 되었습니다.
image

Copy link

Choose a reason for hiding this comment

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

오 저도 설치했습니다 감사합니다 :)

<img alt="favorite-icon" class="favorite-icon"
src=${isFavorite ? filledFavoriteIcon : linedFavoriteIcon}></img>
`;
}

private toggleFavoriteIcon(iconElement: HTMLImageElement) {
Copy link

Choose a reason for hiding this comment

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

아이콘을 클릭한 순간 filled 이미지가 불러와져서, 최초에 깜빡임이 있는데, 개선해볼 수 있을까요~?


Copy link
Author

Choose a reason for hiding this comment

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

// FavoriteIcon
import { filledFavoriteIcon, linedFavoriteIcon } from "../../assets/index";

// index.js
import "./components/FavoriteIcon/index";

import를 사용해서 이미지 파일을 잘 가져온 줄 알았는데,

image
제일 처음 파일들을 다운 받을 때, filled 이미지는 가져오지를 않네요...😅 이유를 찾아보고 개선 해보려했는데 방법을 잘 모르겠네요. 😭

Copy link

Choose a reason for hiding this comment

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

img.src 값으로 지정된 순간 filled 이미지가 로드되고 있어, 이 시점을 앞당겨주는 방향으로 말씀드려보았습니다.

filled 이미지를

  • 숨김 처리한 <img>태그의 src로 지정한다.
  • new Image() 의 src로 지정한다.
  • <link>의 preload/prefetch 를 활용한다.

등의 방법이 있을 것 같아요 :)

const prevIcon = iconElement?.getAttribute("src");
iconElement?.setAttribute("src", prevIcon === linedFavoriteIcon ? filledFavoriteIcon : linedFavoriteIcon);
}

private toggleFavoriteState() {
const restaurantName = this.getAttribute("restaurant-name") ?? "";
toggleFavoriteStateByName(restaurantName);
}

private handleToggleFavoriteIcon(element: HTMLElement) {
const iconElement = element.closest("img");
if (!(iconElement instanceof HTMLImageElement)) return;

this.toggleFavoriteIcon(iconElement);
this.toggleFavoriteState();
this.emitEvent(MENU_APP_EVENTS.renderRestaurants);
}

protected setEvent() {
this.addEventListener("click", (event) => {
Copy link

Choose a reason for hiding this comment

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

사용자와 상호작용하는 요소들은 HTML에 잘 정의가 되어있어요. (button, input, ... 등)
그래서 앞으로 img에 onclick 핸들러를 지정하실 일은 거의 없을 거예요.

이 토글 기능의 경우, 버튼 또는 label 태그와 함께 input의 type="checkbox"로 구현하는 것이 좋아보입니다. 말 그대로 토글 기능이니까요 :)

시간 여유가 있다면 input의 type="checkbox"로 구현을 해주시면 좋겠습니다. 없다면 그렇구나 하고 넘어가주세요 :)

Copy link
Author

Choose a reason for hiding this comment

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

아하 그렇군요...! img는 사용자와 상호작용하는 태그로 사용하기보다는 사용자에게 시각적 요소를 보여주는 태그로 사용하는게 맞겠네요. 하루를 통해서 정말 많은 팁들을 얻어가는 것 같아요! 감사해요 하루 🙇‍♂️

if (!(event.target instanceof HTMLElement)) return;
this.handleToggleFavoriteIcon(event.target);
});
}
}

customElements.define("favorite-icon", FavoriteIcon);
28 changes: 0 additions & 28 deletions src/components/Header.js

This file was deleted.

32 changes: 0 additions & 32 deletions src/components/MenuApp.js

This file was deleted.

33 changes: 33 additions & 0 deletions src/components/MenuApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import BaseComponent from "./BaseComponent";
import { MENU_APP_EVENTS } from "../constants/event";
import { initRestaurantStorage } from "../domains/Restaurants";

class MenuApp extends BaseComponent {
constructor() {
initRestaurantStorage();
super();
}

render() {
this.innerHTML = /*html*/ `
<app-header></app-header>
<restaurant-tab-container class="flex justify-between tab-container"></restaurant-tab-container>
<main>
<option-selector-container></option-selector-container>
<restaurant-list></restaurant-list>
</main>
<modal-wrapper
open-type=${MENU_APP_EVENTS.openAddForm}
id='add-form'>
<restaurant-add-form></restaurant-add-form>
</modal-wrapper>
<modal-wrapper
open-type=${MENU_APP_EVENTS.openRestaurantDetail}
id="restaurant-detail" >
<restaurant-detail></restaurant-detail>
</modal-wrapper>
`;
}
}

customElements.define("menu-app", MenuApp);
Loading