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

Feat: Footer Component #46

Merged
merged 5 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions src/components/Certificate/Certificate.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,9 @@ test("render LinkedIn link", () => {
`https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&name=${username}&organizationId=104834174&certUrl=${location.href}`,
);
});

test("render footer", () => {
render(<Certificate />);

expect(screen.getByRole("contentinfo"));
});
6 changes: 4 additions & 2 deletions src/components/Certificate/Certificate.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styles from "./Certificate.module.css";
import Header from "../Header/Header";
import Footer from "../Footer/Footer";

export default function Certificate() {
const member = new URL(location.href).searchParams.get("member");
Expand All @@ -20,10 +21,11 @@ export default function Certificate() {
<p>귀하는 어쩌구 저쩌구</p>
</div>
</section>
<footer>
<section>
<button onClick={() => window.print()}>출력</button>
<a href={linkedInURL}>링크드인에 공유하기</a>
</footer>
</section>
<Footer />
DaleSeo marked this conversation as resolved.
Show resolved Hide resolved
</main>
);
}
62 changes: 62 additions & 0 deletions src/components/Footer/Footer.module.css
Copy link
Member

Choose a reason for hiding this comment

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

그리고 지금 Footer.module.css 의 구성을 봤을 때, 모든 스타일이 footer 클래스 아래에 다 들어가 있는데요.
depth가 4개는 넘어보이고, 아래쪽 스타일을 읽다보면, 어디 단위의 p 태그를 말하는지 헷갈립니다😅
이런 방식의 스타일 작성은 일반 css 파일 하나 작성한 다음, Footer 컴포넌트 상위에 불러오는 것과 무엇이 다른 것인지 모르겠습니다. 적당한 단위로 스타일을 나눠 depth를 줄이고, CSS Modules의 특징을 활용해보는 것은 어떨까요..?
아래 예시는 참고용입니다.

<footer className={styles.footer}>
  <section className={styles.section}>
    <ul className={styles["left-links"]}>
      {leftMenu.map(({ label, link }) => (
        <li key={link}>
          <a href={link} target="_blank" aria-label={label}>
            {label}
          </a>
        </li>
      ))}
    </ul>
    <ul className={styles["right-links"]}>
      {rightMenu.map(({ label, link, component }) => (
        <li key={link}>
          <a href={link} target="_blank" aria-label={label}>
            {component}
          </a>
        </li>
      ))}
    </ul>
  </section>
  <p>© 2024 DaleStudy. All rights reserved.</p>
</footer>

Copy link
Contributor

@DaleSeo DaleSeo Nov 7, 2024

Choose a reason for hiding this comment

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

아마도 @Sunjae95 님께서 #50 (comment) 을 읽으시고 CSS Nesting을 활용하시려다보니 이렇게 스타일이 나오지 않으셨나 추측되네요.

저는 HTML과 CSS 간의 depth 일치해서 나란히 두고 코드를 읽으면 편하던데 개발자마다 다르게 느낄 수 있는 것 같습니다. 저는 @sounmind 님께서 제안하신 것처럼 적당한 지점에서 스타일을 나누는 방식에도 이견은 없습니다. 단, 한 가지 고려할 점은 우리가 지금 Top-down 방향으로 컴포넌트화를 시키고 있기 때문에, 나중에 이 컴포넌트의 많은 부분이 자식 컴포넌트로 스타일과 함께 빠져나갈 수 있다는 겁니다. 그러면 자연스럽게 depth는 줄 것입니다. 따라서 depth가 지나치게 많아지는 것을 컴포넌트 화가 필요한 신호로 볼 수도 있을 것 같습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sounmind @DaleSeo 엇 제가 코멘트에 답변달려고 고민하던 사이에 많은일이 있었군요. PR담당자인데 사라진점 죄송합니다😅
저의 의도는 css nesting을 적극 사용하는것이고, 작성하면서 @sounmind 님과 같이 가독성이 떨어지는구나 생각도 들었습니다.
textlink같은 태그는 컴포넌트화하여 분리하면 depth가 줄여질텐데 그걸 말로 표현하기가 어려워서 늦어졌는데 @DaleSeo 님께서 잘 표현해주셨습니다.

제 생각엔 가독성이 힘들었던 부분은 media query가 들어가게되어 블록이 추가되고 child을 읽기가 힘들어졌다고 예상돼요.
또한 현재 PR에서는 앞으로 textLink가 분리되기에 depth가 더 깊어질일은 없다고 생각하구요.

.footer {
  background-color: var(--text-900);
  padding: 80px 27px;

  > section {
    display: flex;
    flex-direction: column;
    gap: 40px;

    > ul:nth-child(1) {
      display: flex;
      flex-direction: column;
      row-gap: 10px;
      line-height: 20px;
      /* component화 할 것*/
      > li > a {
        font-size: 16px;
        color: var(--bg-100);
      }
    }

    > ul:nth-child(2) {
      display: flex;
      align-items: center;
      column-gap: 40px;
    }
  }

  > p {
    font-size: 14px;
    color: rgba(253, 253, 255, 0.75);
    border-top: 1px solid rgba(253, 253, 255, 0.2);
    margin-top: 26px;
    padding-top: 20px;
    line-height: 18px;
  }
}

@media (min-width: 768px) {
  .footer {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: 180px;
    padding-bottom: 100px;

    & > section {
      justify-content: space-between;
      flex-direction: row;
      width: 80%;

      & > ul:nth-child(1) {
        flex-direction: row;
        column-gap: 80px;
      }
    }

    & > p {
      width: 80%;
      margin-top: 50px;
      padding-top: 48px;
    }
  }
}

위는 예시코드인데 depth가 4 depth까지가는걸 방지하고 media query를 분리하여 작성하면 가독성면에서 괜찮을까요??

Copy link
Member

@sounmind sounmind Nov 8, 2024

Choose a reason for hiding this comment

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

같이 통화하면서 작성했던 예제 코드

.footer {
  background-color: var(--text-900);
  padding: 80px 27px;

  @media (min-width: 768px) {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: 180px;
    padding-bottom: 100px;
  }

  section {
    display: flex;
    flex-direction: column;
    gap: 40px;

    @media (min-width: 768px) {
      justify-content: space-between;
      flex-direction: row;
      width: 80%;
    }
  }

  p {
    font-size: 14px;
    color: rgba(253, 253, 255, 0.75);
    border-top: 1px solid rgba(253, 253, 255, 0.2);
    margin-top: 26px;
    padding-top: 20px;
    line-height: 18px;

    @media (min-width: 768px) {
      width: 80%;
      margin-top: 50px;
      padding-top: 48px;
    }
  }
}

.leftMenu {
  display: flex;
  flex-direction: column;
  row-gap: 10px;
  line-height: 20px;

  @media (min-width: 768px) {
    flex-direction: row;
    column-gap: 80px;
  }

  > li > a {
    font-size: 16px;
    color: var(--bg-100);
  }
}

.rightMenu {
  display: flex;
  align-items: center;
  column-gap: 40px;
}
import styles from "./Footer.module.css";

export default function Footer() {
  return (
    <footer className={styles.footer}>
      <section>
        <ul className={styles.leftMenu}>
          {leftMenu.map(({ label, link }) => (
            <li key={link}>
              <a href={link} target="_blank" aria-label={label}>
                {label}
              </a>
            </li>
          ))}
        </ul>

        <ul className={styles.rightMenu}>
          {rightMenu.map(({ label, link, component }) => (
            <li key={link}>
              <a href={link} target="_blank" aria-label={label}>
                {component}
              </a>
            </li>
          ))}
        </ul>
      </section>

      <p>© 2024 DaleStudy. All rights reserved.</p>
    </footer>
  );
}

Copy link
Contributor

Choose a reason for hiding this comment

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

@sounmind @Sunjae95 대화를 통해 좋은 타협안을 찾으신 것 같군요 :) 미디어 쿼리가 안으로 들어가서 스타일이 읽기 편한 것 같습니다 💯

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.footer {
background-color: var(--text-900);
padding: 80px 27px;

@media (min-width: 768px) {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 180px;
padding-bottom: 100px;
}

section {
display: flex;
flex-direction: column;
gap: 40px;

@media (min-width: 768px) {
justify-content: space-between;
flex-direction: row;
width: 80%;
}
}

p {
font-size: 14px;
color: rgba(253, 253, 255, 0.75);
border-top: 1px solid rgba(253, 253, 255, 0.2);
margin-top: 26px;
padding-top: 20px;
line-height: 18px;

@media (min-width: 768px) {
width: 80%;
margin-top: 50px;
padding-top: 48px;
}
}
}

.leftMenu {
display: flex;
flex-direction: column;
row-gap: 10px;
line-height: 20px;

@media (min-width: 768px) {
flex-direction: row;
column-gap: 80px;
}

> li > a {
font-size: 16px;
color: var(--bg-100);
}
}

.rightMenu {
display: flex;
align-items: center;
column-gap: 40px;
}
10 changes: 10 additions & 0 deletions src/components/Footer/Footer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { Meta, StoryObj } from "@storybook/react";
import Footer from "./Footer";

const meta = {
component: Footer,
} satisfies Meta<typeof Footer>;

export default meta;

export const Default: StoryObj<typeof meta> = {};
58 changes: 58 additions & 0 deletions src/components/Footer/Footer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { render, screen } from "@testing-library/react";
import { expect, test } from "vitest";
import Footer from "./Footer";

test("render left menu", () => {
render(<Footer />);

[
{
label: "FAQ",
link: "https://github.com/DaleStudy/leetcode-study/discussions",
},
{
label: "Apply",
link: "https://github.com/DaleStudy/leetcode-study/discussions/209",
},
{
label: "Guide",
link: "https://github.com/DaleStudy/leetcode-study/blob/main/CONTRIBUTING.md",
},
].forEach(({ label, link }) => {
const childMenu = screen.getByRole("link", { name: label });
expect(childMenu).toHaveAttribute("href", link);
});
});

test("render right menu", () => {
render(<Footer />);

[
{
label: "Blog",
link: "https://www.algodale.com",
},
{
label: "LinkedIn",
link: "https://www.linkedin.com/in/daleseo",
},
{
label: "Github",
link: "https://github.com/DaleStudy/leetcode-study",
},
{
label: "Youtube",
link: "https://www.youtube.com/@DaleSeo",
},
].forEach(({ label, link }) => {
const childMenu = screen.getByRole("link", { name: label });
expect(childMenu).toHaveAttribute("href", link);
});
});

test("render copyright", () => {
render(<Footer />);

const copyright = screen.getByText("© 2024 DaleStudy. All rights reserved.");
expect(copyright).toBeInTheDocument();
});
123 changes: 123 additions & 0 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import styles from "./Footer.module.css";

export default function Footer() {
const leftMenu = [
{
label: "FAQ",
link: "https://github.com/DaleStudy/leetcode-study/discussions",
},
{
label: "Apply",
link: "https://github.com/DaleStudy/leetcode-study/discussions/209",
},
{
label: "Guide",
link: "https://github.com/DaleStudy/leetcode-study/blob/main/CONTRIBUTING.md",
},
];

const rightMenu = [
{
label: "Blog",
component: (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.3456 24C20.5301 24 23.9291 20.5912 23.9528 16.4296L24 10.3021L23.9291 9.9688L23.728 9.54933L23.388 9.2864C22.9467 8.94 20.7091 9.30987 20.1067 8.76267C19.6792 8.37227 19.6125 7.66693 19.4832 6.71093C19.2429 4.85947 19.0909 4.76293 18.8008 4.1352C17.748 1.9064 14.8893 0.231467 12.9256 0H7.60613C3.42133 0 0 3.41387 0 7.58347V16.4296C0 20.5912 3.42133 24 7.60613 24H16.3456ZM7.70293 6.19547H11.9203C12.7256 6.19547 13.3776 6.84907 13.3776 7.64347C13.3776 8.43467 12.7256 9.0936 11.9203 9.0936H7.70293C6.8976 9.0936 6.24693 8.4344 6.24693 7.64347C6.24693 6.84907 6.8976 6.19547 7.70293 6.19547ZM6.24693 16.3176C6.24693 15.5235 6.8976 14.8747 7.70293 14.8747H16.2723C17.0723 14.8747 17.7219 15.5235 17.7219 16.3176C17.7219 17.1011 17.0723 17.7603 16.2723 17.7603H7.70293C6.8976 17.7603 6.24693 17.1013 6.24693 16.3176Z"
fill="#FDFDFF"
/>
</svg>
),
link: "https://www.algodale.com",
},
{
label: "LinkedIn",
component: (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M21.3333 0C22.0406 0 22.7189 0.280951 23.219 0.781048C23.719 1.28115 24 1.95942 24 2.66667V21.3333C24 22.0406 23.719 22.7189 23.219 23.219C22.7189 23.719 22.0406 24 21.3333 24H2.66667C1.95942 24 1.28115 23.719 0.781048 23.219C0.280951 22.7189 0 22.0406 0 21.3333V2.66667C0 1.95942 0.280951 1.28115 0.781048 0.781048C1.28115 0.280951 1.95942 0 2.66667 0H21.3333ZM20.6667 20.6667V13.6C20.6667 12.4472 20.2087 11.3416 19.3936 10.5264C18.5784 9.71128 17.4728 9.25333 16.32 9.25333C15.1867 9.25333 13.8667 9.94667 13.2267 10.9867V9.50667H9.50667V20.6667H13.2267V14.0933C13.2267 13.0667 14.0533 12.2267 15.08 12.2267C15.5751 12.2267 16.0499 12.4233 16.3999 12.7734C16.75 13.1235 16.9467 13.5983 16.9467 14.0933V20.6667H20.6667ZM5.17333 7.41333C5.76742 7.41333 6.33717 7.17733 6.75725 6.75725C7.17733 6.33717 7.41333 5.76742 7.41333 5.17333C7.41333 3.93333 6.41333 2.92 5.17333 2.92C4.57571 2.92 4.00257 3.1574 3.57999 3.57999C3.1574 4.00257 2.92 4.57571 2.92 5.17333C2.92 6.41333 3.93333 7.41333 5.17333 7.41333ZM7.02667 20.6667V9.50667H3.33333V20.6667H7.02667Z"
fill="#FDFDFF"
/>
</svg>
),
link: "https://www.linkedin.com/in/daleseo",
},
{
label: "Github",
component: (
<svg
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.2979 0C10.6829 0 9.08376 0.318095 7.59171 0.936124C6.09966 1.55415 4.74395 2.46001 3.60198 3.60198C1.29567 5.90829 0 9.03631 0 12.2979C0 17.7336 3.5295 22.3453 8.41178 23.981C9.02668 24.0793 9.22344 23.6981 9.22344 23.3661C9.22344 23.0832 9.22344 22.3084 9.22344 21.2877C5.81692 22.0256 5.09134 19.6398 5.09134 19.6398C4.52564 18.2132 3.72627 17.832 3.72627 17.832C2.60716 17.0695 3.81236 17.0941 3.81236 17.0941C5.04215 17.1802 5.69394 18.3608 5.69394 18.3608C6.76386 20.2301 8.57165 19.6767 9.27263 19.3815C9.38332 18.5822 9.70306 18.0411 10.0474 17.7336C7.31726 17.4262 4.45185 16.3685 4.45185 11.683C4.45185 10.318 4.91917 9.22344 5.71853 8.35029C5.59556 8.04284 5.16513 6.76386 5.84151 5.10364C5.84151 5.10364 6.87454 4.77159 9.22344 6.35803C10.195 6.08747 11.2526 5.9522 12.2979 5.9522C13.3432 5.9522 14.4009 6.08747 15.3724 6.35803C17.7213 4.77159 18.7543 5.10364 18.7543 5.10364C19.4307 6.76386 19.0003 8.04284 18.8773 8.35029C19.6767 9.22344 20.144 10.318 20.144 11.683C20.144 16.3808 17.2663 17.4139 14.5238 17.7213C14.9666 18.1025 15.3724 18.8527 15.3724 19.9964C15.3724 21.6443 15.3724 22.9725 15.3724 23.3661C15.3724 23.6981 15.5692 24.0916 16.1964 23.981C21.0786 22.333 24.5958 17.7336 24.5958 12.2979C24.5958 10.6829 24.2778 9.08376 23.6597 7.59171C23.0417 6.09966 22.1358 4.74395 20.9939 3.60198C19.8519 2.46001 18.4962 1.55415 17.0041 0.936124C15.5121 0.318095 13.9129 0 12.2979 0Z"
fill="#FDFDFF"
/>
</svg>
),
link: "https://github.com/DaleStudy/leetcode-study",
},
{
label: "Youtube",
component: (
<svg
width="25"
height="18"
viewBox="0 0 25 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M23.4017 1.76818C24.121 2.5016 24.3557 4.16708 24.3557 4.16708C24.3557 4.16708 24.5957 6.12336 24.5957 8.07889V9.9128C24.5957 11.8691 24.3557 13.8246 24.3557 13.8246C24.3557 13.8246 24.121 15.4901 23.4017 16.2235C22.5794 17.0911 21.6676 17.1808 21.1522 17.2314C21.0955 17.237 21.0436 17.2421 20.9972 17.2477C17.6387 17.4924 12.5957 17.5 12.5957 17.5C12.5957 17.5 6.3557 17.4426 4.4357 17.2568C4.34473 17.2397 4.23446 17.2264 4.10958 17.2113C3.5014 17.1378 2.54683 17.0225 1.78895 16.2235C1.0697 15.4901 0.835703 13.8246 0.835703 13.8246C0.835703 13.8246 0.595703 11.8691 0.595703 9.9128V8.07889C0.595703 6.12336 0.835703 4.16708 0.835703 4.16708C0.835703 4.16708 1.0697 2.5016 1.78895 1.76818C2.6131 0.899349 3.52576 0.810675 4.04118 0.760597C4.09707 0.755167 4.14828 0.750191 4.1942 0.744724C7.5527 0.5 12.5905 0.5 12.5905 0.5H12.601C12.601 0.5 17.6387 0.5 20.9972 0.744724C21.0431 0.750199 21.0944 0.755182 21.1503 0.760621C21.6653 0.810704 22.5784 0.89949 23.4017 1.76818ZM10.117 5.34387L10.1177 12.135L16.6015 8.75112L10.117 5.34387Z"
fill="#FDFDFF"
/>
</svg>
),
link: "https://www.youtube.com/@DaleSeo",
},
];

return (
<footer className={styles.footer}>
<section>
<ul className={styles.leftMenu}>
{leftMenu.map(({ label, link }) => (
<li key={link}>
<a href={link} target="_blank" aria-label={label}>
{label}
</a>
</li>
))}
</ul>

<ul className={styles.rightMenu}>
{rightMenu.map(({ label, link, component }) => (
<li key={link}>
<a href={link} target="_blank" aria-label={label}>
Copy link
Contributor

Choose a reason for hiding this comment

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

a11y 👍

{component}
</a>
</li>
))}
</ul>
</section>

<p>© 2024 DaleStudy. All rights reserved.</p>
</footer>
);
}
10 changes: 8 additions & 2 deletions src/components/Leaderboard/Leaderboard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it } from "vitest";
import { render, screen } from "@testing-library/react";
import { render, screen, within } from "@testing-library/react";
import Leaderboard from "./Leaderboard";

describe("<Leaderboard/>", () => {
Expand Down Expand Up @@ -31,7 +31,9 @@ describe("<Leaderboard/>", () => {
{ name: "SamTheKorean", solved: 60, rank: "나무" },
];

const memberItems = screen.getAllByRole("listitem");
const memberItems = within(
screen.getByRole("article", { name: /members list/i }),
).getAllByRole("listitem");

expect(memberItems).toHaveLength(members.length);

Expand Down Expand Up @@ -71,4 +73,8 @@ describe("<Leaderboard/>", () => {
);
});
});

it("renders footer", () => {
expect(screen.getByRole("contentinfo"));
});
});
3 changes: 3 additions & 0 deletions src/components/Leaderboard/Leaderboard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import styles from "./Leaderboard.module.css";
import Header from "../Header/Header";
import Footer from "../Footer/Footer";

export default function Leaderboard() {
const members = [
{ name: "DaleSeo", solved: 71, rank: "새싹" },
Expand Down Expand Up @@ -34,6 +36,7 @@ export default function Leaderboard() {
))}
</ul>
</article>
<Footer />
</main>
);
}
10 changes: 8 additions & 2 deletions src/components/Progress/Progress.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it } from "vitest";
import { render, screen } from "@testing-library/react";
import { render, screen, within } from "@testing-library/react";
import Progress from "./Progress"; // Adjust the import path as needed

describe("<Progress/>", () => {
Expand Down Expand Up @@ -31,7 +31,9 @@ describe("<Progress/>", () => {
});

it("renders the task list", () => {
const taskItems = screen.getAllByRole("listitem");
const taskItems = within(
screen.getByRole("region", { name: "Task List" }),
).getAllByRole("listitem");
expect(taskItems).toHaveLength(4); // Since there are 4 tasks defined

const expectedTasks = [
Expand All @@ -47,4 +49,8 @@ describe("<Progress/>", () => {
expect(taskItem).toHaveTextContent(task.difficulty);
});
});

it("renders footer", () => {
expect(screen.getByRole("contentinfo"));
});
});
2 changes: 2 additions & 0 deletions src/components/Progress/Progress.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styles from "./Progress.module.css";
import Header from "../Header/Header";
import Footer from "../Footer/Footer";

export default function Progress() {
const tasks = [
Expand Down Expand Up @@ -36,6 +37,7 @@ export default function Progress() {
))}
</ul>
</section>
<Footer />
</main>
);
}