Skip to content

Commit

Permalink
Vuex 사용하기 (#45)
Browse files Browse the repository at this point in the history
* ✨ vuex 설치 및 적용
 - App에 연결
* 🚚 파일 정리
  - 스크립트 파일 /scripts로 이동
* 🧪 store 관련 테스트 파일 추가
  - 테마 변경 테스트
  - test script 경로 루트로 변경
* ✨ theme 관련 state 작업
  - theme icon 변경, theme 적용을 store를 통해 설정되도록 수정
* ✨ heatmap 색상 Theme에 따라 변경되도록 수정
  • Loading branch information
padosum committed Feb 2, 2023
1 parent f26466c commit 991972b
Show file tree
Hide file tree
Showing 20 changed files with 167 additions and 66 deletions.
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -11,7 +11,7 @@
"dev": "vite",
"build": "run-p type-check build-only",
"preview": "vite preview",
"test:unit": "vitest --environment jsdom --root src/",
"test:unit": "vitest --environment jsdom --root .",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
Expand All @@ -25,6 +25,7 @@
"vue": "^3.2.45",
"vue-router": "^4.1.6",
"vue3-calendar-heatmap": "^2.0.0",
"vuex": "^4.0.2",
"yaml-front-matter": "^3.4.0"
},
"devDependencies": {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
14 changes: 7 additions & 7 deletions src/App.vue
@@ -1,15 +1,10 @@
<template>
<div>
<div :class="themeClass">
<NavBar :sections="sections"></NavBar>
<a @click="topClick" class="scrolltop" id="scroll-top">
<i class="bx bx-chevron-up scrolltop__icon"></i>
</a>
<Suspense>
<template #default>
<router-view :key="$route.fullPath" />
</template>
<template #fallback> </template>
</Suspense>
<router-view :key="$route.fullPath" />
<AppFooter />
</div>
</template>
Expand All @@ -26,6 +21,11 @@ export default defineComponent({
NavBar,
AppFooter,
},
computed: {
themeClass() {
return this.$store.state.darkTheme ? "dark-theme" : "";
},
},
setup() {
const sections: PostIndex[] = inject("sections", []);
return { sections };
Expand Down
2 changes: 1 addition & 1 deletion src/assets/style/styles.css
Expand Up @@ -69,7 +69,7 @@ body {
}

/* variable dark theme */
body.dark-theme {
body:has(.dark-theme) {
--title-color: #f1f3f2;
--text-color: #c7d1cc;
--body-color: #2e353c;
Expand Down
69 changes: 15 additions & 54 deletions src/components/NavBar.vue
Expand Up @@ -22,10 +22,11 @@
</li>
<li class="tools">
<i
class="bx bx-moon change-theme"
class="bx change-theme"
id="theme-button"
v-on:click="changeTheme"
ref="themeButton"
:class="themeIcon"
></i>
</li>
</ul>
Expand All @@ -39,18 +40,28 @@
</template>

<script lang="ts">
import { defineComponent, inject, type HtmlHTMLAttributes } from "vue";
import { defineComponent, inject } from "vue";
import { showMenu, linkAction } from "@/utils/menu";
import { scrollHeader } from "@/utils/scroll";
import type { PostIndex } from "@/types/PostIndex";
import type { PropType } from "vue";
import { MutationTypes } from "@/store/mutations";
import { setTheme } from "@/utils/theme";
export default defineComponent({
props: {
sections: {
type: Array as PropType<PostIndex[]>,
},
},
computed: {
themeIcon() {
return this.$store.state.darkTheme ? "bx-sun" : "bx-moon";
},
theme() {
return this.$store.state.darkTheme ? "dark" : "light";
},
},
methods: {
routeTagsPage() {
this.$router.push(`/tags`);
Expand All @@ -62,41 +73,8 @@ export default defineComponent({
this.$router.push(`/${section}/${id}`);
},
changeTheme() {
const themeButton = this.$refs.themeButton as HTMLElement;
const darkTheme: string = "dark-theme";
const iconTheme: string = "bx-sun";
document.body.classList.toggle(darkTheme);
themeButton.classList.toggle(iconTheme);
// change utteraces theme
const commentTheme =
localStorage.getItem("selected-theme") === "dark"
? "boxy-light"
: "dark-blue";
const message = {
type: "set-theme",
theme: commentTheme,
};
const commentFrame: any = document.querySelector(
"iframe.utterances-frame"
);
if (commentFrame !== null)
commentFrame.contentWindow.postMessage(message, "https://utteranc.es");
localStorage.setItem("selected-theme", this.getCurrentTheme(darkTheme));
localStorage.setItem(
"selected-icon",
this.getCurrentIcon(iconTheme, themeButton)
);
},
getCurrentTheme(darkTheme: string) {
return document.body.classList.contains(darkTheme) ? "dark" : "light";
},
getCurrentIcon(iconTheme: string, themeButton: HTMLElement) {
return themeButton.classList.contains(iconTheme) ? "bx-moon" : "bx-sun";
this.$store.commit(MutationTypes.TOGGLE_THEME);
setTheme(this.theme);
},
},
mounted() {
Expand All @@ -110,23 +88,6 @@ export default defineComponent({
tools.forEach((n) => n.addEventListener("click", linkAction));
window.addEventListener("scroll", scrollHeader);
// previously selected theme
const themeButton = document.getElementById("theme-button");
const darkTheme = "dark-theme";
const iconTheme = "bx-sun";
const selectedTheme = localStorage.getItem("selected-theme");
const selectedIcon = localStorage.getItem("selected-icon");
if (selectedTheme) {
document.body.classList[selectedTheme == "dark" ? "add" : "remove"](
darkTheme
);
themeButton?.classList[selectedIcon == "bx-moon" ? "add" : "remove"](
iconTheme
);
}
},
setup() {
const postsIndex: PostIndex[] = inject<PostIndex[]>("postsIndex", []);
Expand Down
34 changes: 34 additions & 0 deletions src/components/__test__/NavBar.spec.ts
@@ -0,0 +1,34 @@
import NavBar from "@/components/NavBar.vue";
import { describe, test, expect } from "vitest";
import { shallowMount } from "@vue/test-utils";
import { store } from "@/store";

describe("NavBar.vue theme 변경", () => {
test("theme 변경 버튼을 클릭하면 버튼의 theme icon이 변경된다.", () => {
const wrapper = shallowMount(NavBar, {
global: {
plugins: [store],
},
});

const themeButton = wrapper.find("#theme-button");
const beforeTheme = themeButton.attributes("class");
themeButton.trigger("click");

expect(themeButton.classes()).not.toBe(beforeTheme);
});

test("theme 변경 버튼을 클릭하면 store state 값이 반대로 변경된다.", () => {
const wrapper = shallowMount(NavBar, {
global: {
plugins: [store],
},
});

const beforeState = store.state.darkTheme;
const themeButton = wrapper.find("#theme-button");
themeButton.trigger("click");

expect(store.state.darkTheme).not.toEqual(beforeState);
});
});
4 changes: 3 additions & 1 deletion src/main.ts
@@ -1,6 +1,7 @@
import { createApp } from "vue";
import App from "@/App.vue";
import router from "@/router/index";
import router from "@/router";
import { store } from "@/store";
import axios from "redaxios";
import type { PostIndex } from "@/types/PostIndex";

Expand All @@ -16,6 +17,7 @@ const loadApp = async () => {
sections.sort((a, b) => a.localeCompare(b));
createApp(App)
.use(router)
.use(store)
.provide<PostIndex[]>("postsIndex", postsIndex)
.provide("sections", sections)
.mount("#app");
Expand Down
10 changes: 10 additions & 0 deletions src/store/index.ts
@@ -0,0 +1,10 @@
import { createStore } from "vuex";
import { state } from "./state";
import { mutations } from "./mutations";

export const store = createStore({
state() {
return state;
},
mutations,
});
13 changes: 13 additions & 0 deletions src/store/mutations.ts
@@ -0,0 +1,13 @@
import type { RootState } from "./state";

export enum MutationTypes {
TOGGLE_THEME = "TOGGLE_THEME",
}

export const mutations = {
[MutationTypes.TOGGLE_THEME](state: RootState) {
state.darkTheme = !state.darkTheme;
},
};

export type Mutations = typeof mutations;
7 changes: 7 additions & 0 deletions src/store/state.ts
@@ -0,0 +1,7 @@
import { getTheme } from "@/utils/theme";

export const state = {
darkTheme: getTheme() === "dark" || false,
};

export type RootState = typeof state;
9 changes: 9 additions & 0 deletions src/utils/theme.ts
@@ -0,0 +1,9 @@
const getTheme = () => {
return localStorage.getItem("selected-theme");
};

const setTheme = (theme: string) => {
localStorage.setItem("selected-theme", theme);
};

export { getTheme, setTheme };
24 changes: 23 additions & 1 deletion src/views/ArchivePage.vue
Expand Up @@ -14,6 +14,7 @@
:max="5"
tooltip-unit="read"
@day-click="handleDayClick"
:range-color="heatmapRangeColor"
>
</CalendarHeatmap>
</div>
Expand Down Expand Up @@ -105,6 +106,22 @@ export default defineComponent({
return {
selectedDate: "",
selectedList: [] as PostIndex[],
lightColors: [
"rgb(235, 237, 240)",
"rgb(218, 226, 239)",
"rgb(192, 221, 249)",
"rgb(115, 179, 243)",
"rgb(56, 134, 225)",
"rgb(23, 69, 158)",
],
darkColors: [
"#1f1f22",
"#1e334a",
"#1d466c",
"#1d5689",
"#1d69ac",
"#1B95D1",
],
};
},
computed: {
Expand All @@ -118,6 +135,12 @@ export default defineComponent({
const [date] = dateOffset.toISOString().split("T");
return date;
},
darkMode() {
return this.$store.state.darkTheme;
},
heatmapRangeColor() {
return this.darkMode ? this.darkColors : this.lightColors;
},
},
methods: {
handleDayClick(day: heatmapDate) {
Expand Down Expand Up @@ -154,7 +177,6 @@ export default defineComponent({
},
[]
);
console.log(heatMapData.filter((item) => item.date === "2022-05-23"));
const pageStatus = computed(() => {
const isHome = !props.section && !props.tag;
Expand Down
8 changes: 8 additions & 0 deletions src/vue.d.ts
@@ -0,0 +1,8 @@
import { Store } from "vuex";
import { RootState } from "./store/state";
declare module "@vue/runtime-core" {
// provide typings for `this.$store`
interface ComponentCustomProperties {
$store: Store<RootState>;
}
}
13 changes: 13 additions & 0 deletions tests/store.spec.ts
@@ -0,0 +1,13 @@
import { describe, expect, test } from "vitest";
import { mutations, MutationTypes } from "@/store/mutations";

describe("mutations", () => {
test("TOGGLE_THEME", () => {
const state = {
darkTheme: false,
};

mutations[MutationTypes.TOGGLE_THEME](state);
expect(state.darkTheme).toBeTruthy();
});
});
2 changes: 1 addition & 1 deletion tsconfig.app.json
@@ -1,6 +1,6 @@
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "scripts/*"],
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "scripts/*", "test"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
Expand Down
1 change: 1 addition & 0 deletions tsconfig.vitest.json
@@ -1,5 +1,6 @@
{
"extends": "./tsconfig.app.json",
"include": ["tests/**/*", "src/**/*"],
"exclude": [],
"compilerOptions": {
"composite": true,
Expand Down

0 comments on commit 991972b

Please sign in to comment.