LoginSignup
4
1

【Day 8】TOPページを作成する

Last updated at Posted at 2023-12-07

はじめに

スライド9.PNG


2023年アドベントカレンダー8日目です。

今回から、「ユーザーはトップページを開くことができる」を進めていきます。

image.png

Let's Go!!

以前作成したデザインによると、

image.png

このような画面のようです。

今回は「ユーザーはトップページを開くことができる」なので、ブログの一覧をDBから取得するような処理は描かず、赤枠の部分だけ作成することにします。

image.png

TDD(テスト駆動開発)で進めていきます

ざっくりと今回の進め方を図解しました。

image.png

E2Eテスト

cypress/e2e/top/display.cy.js
/// <reference types="cypress" />

describe('トップ画面の表示', () => {
  beforeEach(() => {
    cy.visit(cy.config('baseUrl'))
  })

  it('h1タグで"Life Sync"が表示されていること', () => {
    cy.get('h1').should('have.text', 'Life Sync')
  })

  it('h2タグで"ブログ一覧"の文字が表示されていること', () => {
    cy.get('h2').should('have.text', 'ブログ一覧')
  })

  it('"新規登作成"のボタンが表示されていること', () => {
    cy.get('button').should('have.text', '新規作成')
  })
})

web
npm run dev
e2e/web
npm run test:e2e
  トップ画面の表示
    1) h1タグで"Life Sync"が表示されていること
    2) h2タグで"ブログ一覧"の文字が表示されていること
    3) "新規登作成"のボタンが表示されていること


  0 passing
  3 failing

何も作成していないので、当然のようにテストは失敗します。

では次はUnit Testと行きたいところですが、今回のストーリーでは、ビジネスロジックを書くつもりはないので、E2Eテストのみで実装へ進みます。

コンポーネント設計

作成したい画面をよく見て、コンポーネント設計をしてみます。

image.png

簡単にこのような形で分解しました。

細かく分けるアトミックデザインのような考え方もありますが、自分は関心ごと単位で分けるくらいに抑えることが多いです。コンポーネントが増えると逆にメンテナンス性が下がることもありますので、、、

(参考)アトミックデザインとは
https://udemy.benesse.co.jp/design/web-design/atomic-design.html

実装

今回のストーリーでは、Header, PrimaryButtonを作っていきます。

まずは最低限テストが通ることを目指します。

components/Header.tsx
import React from 'react'

export const Header = () => {
  return <h1>Life Sync</h1>
}
components/Buttons/Primary.tsx
import React, { ButtonHTMLAttributes, FC, MouseEventHandler } from 'react'

interface Props {
  text: string
  type: ButtonHTMLAttributes<HTMLButtonElement>['type']
  onClick?: MouseEventHandler<HTMLButtonElement>
}

export const Primary: FC<Props> = ({ text, type, onClick }) => {
  const clickHander = onClick ? onClick : () => {}
  return <button type={type} onClick={clickHander}>{text}</button>
}

これをpage.tsxで呼び出してみます。

page.tsx
'use client'

import { Blogs } from '@/components/Blogs'
import { Primary } from '@/components/Buttons/Primary'

export default function Home() {
  return (
    <main className='p-8 flex flex-col gap-4'>
      <div className='flex justify-between'>
        <h2 className='font-bold text-lg'>ブログ一覧</h2>
        <Primary text='新規作成' type='button' onClick={moveBlogNew} />
      </div>
    </main>
  )
}

さあテストを通してみましょう

e2e/web
npm run test:e2e
  トップ画面の表示
    ✓ h1タグで"Life Sync"が表示されていること (380ms)
    ✓ h2タグで"ブログ一覧"の文字が表示されていること (353ms)"新規登作成"のボタンが表示されていること (344ms)


  3 passing (1s)

:ok: 通りました!

次はスタイルをデザイン案を見ながら適用します

image.png

再度E2Eテストを回します。

e2e/web
npm run test:e2e
  トップ画面の表示
    ✓ h1タグで"Life Sync"が表示されていること (380ms)
    ✓ h2タグで"ブログ一覧"の文字が表示されていること (353ms)"新規登作成"のボタンが表示されていること (344ms)


  3 passing (1s)

いい感じですね👍

まず最低限テストが通る実装をして、そのあとにリファクタリングをするようにしましょう

image.png

完成コード

Header.tsx
components/Header.tsx
import React from 'react'

export const Header = () => {
  return (
    <div className='w-full px-6 py-3 border-b-2 border-stone-800'>
      <h1 className='font-bold text-2xl'>Life Sync</h1>
    </div>
  )
}
Buttons/Primary.tsx
components/Buttons/Primary.tsx
import React, { ButtonHTMLAttributes, FC, MouseEventHandler } from 'react'

interface Props {
  text: string
  type: ButtonHTMLAttributes<HTMLButtonElement>['type']
  onClick?: MouseEventHandler<HTMLButtonElement>
}

export const Primary: FC<Props> = ({ text, type, onClick }) => {
  const clickHander = onClick ? onClick : () => {}
  return (
    <button
      type={type}
      className='bg-green-500 py-2 px-4 rounded-lg w-fit text-white hover:bg-green-600'
      onClick={clickHander}
    >
      {text}
    </button>
  )
}
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1