0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

実務1年目駆け出しエンジニアがLaravel&ReactでWebアプリケーション開発に挑戦してみた!(フロントエンド実装編㉑)~レスポンシブデザイン対応~

0
Posted at

実務1年目駆け出しエンジニアがLaravel&ReactでWebアプリケーション開発に挑戦してみた!(その42)

0. 初めに

駆け出しエンジニアの僕が、ゼロからWebアプリケーション開発に挑戦する様子をお届けしているシリーズです。

ついに、長かったフロントエンド編も今日で最終回です...!!

最後までお付き合いいただきありがとうございます。
今日は、レスポンシブデザインに挑戦したいと思います。

1. ブランチ運用

例によって、developブランチを最新化させて、feature/frontend/responsiveという新規ブランを切って作業しましょう。

2. レスポンシブデザインとは

今日取り扱う、レスポンシブデザインとは何でしょうか?
MDN Web Docsの説明によれば、

レスポンシブウェブデザイン (RWD) は、ユーザビリティを確保しながら、すべての画面サイズと解像度でウェブページをうまく描画するためのウェブデザインの手法です。複数の端末に対応したウェブをデザインする方法です。

ということらしいです。

このシリーズでのこれまでのフロントエンドの実装では、PCで使うことを前提に画面を作ってきました。

しかし、今の世の中ではインターネットを利用するのはPCだけでなく、スマホやタブレットなどもメインとなっています。

そのため、どの画面サイズの端末で見ても見た目が崩れないようなレイアウトを用意することが求められることが多く、そのような対応ができているWebページをレスポンシブ対応と呼んだりします。

理想的には、すべてのページで、PC用、タブレット用、スマホ用とデザインを一つずつ用意します。

しかし...

_人人人人人人人人人_
> 面倒くさい!! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

じゃあ、今回はどうするのかと言いますと...

タブレットはいったんおいておいて、スマホのサイズにしたときに著しく見た目が悪くなる箇所をリストアップして、その個所についてのみスマホ用のデザインを適用するという方針でいきたいと思います...!!

スマホサイズでデザインが崩れる箇所

  • ヘッダーのロゴ
  • ヘッダーのタイトル
  • パンくずリスト
  • 学部一覧
  • 研究室一覧
  • 研究室詳細
  • マイページ

3. ヘッダー修正

3.1 ロゴ

header-mobile

これを、resources/js/Assets/logo/header-mobile.svgのパスで追加します。

また、Header.jsxも修正です。

resources/js/Components/Header.jsx

クリックでコードを見る
\project-root\src\resources\js\Components\Header.jsx
import logoPC from '../Assets/logo/header.svg';
import logoMobile from '../Assets/logo/header-mobile.svg';

      {/* 左:ロゴ */}
      <div className="flex items-center">
        <Link href={route('home')} aria-label="トップページへ">
          <img src={logoPC} alt="App Logo" className="hidden md:block h-9 w-auto" />
          <img src={logoMobile} alt="App Logo" className="md:hidden h-9 w-auto" />
        </Link>
      </div>

こんな感じに「LabChart」という文字がなくなりました。
image.png

ちなみに、どうやってスマホサイズを確認するかというと、検証ツールの「デバイスのツールバーを切り替え」から「サイズ」で例えば「iPhone 12 Pro」とかを選択するとできます。
image.png

タイトル

タイトルの文字が大きすぎて2行、3行...となってしまっていたので小さくするように修正します。

resources/js/Components/Header.jsx

クリックでコードを見る
\project-root\src\resources\js\Components\Header.jsx
      {/* 中央:タイトル */}
      <h1 className="absolute left-1/2 -translate-x-1/2 w-[50%] text-center truncate text-sm md:text-xl font-semibold text-black">
        {title}
      </h1>

image.png

4. パンくずリスト修正

現在、スマホサイズだとパンくずリストが長い時は2行になってしまっています。
image.png

resources/js/Components/Common/Breadcrumb.jsx

クリックでコードを見る
\project-root\src\resources\js\Components\Common\Breadcrumb.jsx
  // スマホ用:現在のページの一つ上の階層を返す
  const getPreviousItem = () => {
    if (lab) {
      if (faculty) return { label: faculty.name, href: route('labs.index', buildParams({ faculty: faculty.id })) };
      if (university) return { label: university.name, href: route('faculties.index', buildParams({ university: university.id })) };
      if (query) return { label: `「${query}」の検索結果`, href: route('universities.index', { query }) };
    }
    if (faculty) {
      if (university) return { label: university.name, href: route('faculties.index', buildParams({ university: university.id })) };
      if (query) return { label: `「${query}」の検索結果`, href: route('universities.index', { query }) };
    }
    if (university) {
      if (query) return { label: `「${query}」の検索結果`, href: route('universities.index', { query }) };
    }
    return null;
  };

  const previousItem = getPreviousItem();

  return (
    <nav className="text-sm text-[#747D8C] mb-4">
      {/* スマホ:一つ前だけ表示 */}
      {previousItem && (
        <div className="md:hidden">
          <Link href={previousItem.href} className="flex items-center gap-1 hover:text-black hover:underline">
            <span></span>
            <span className="truncate">{previousItem.label}</span>
          </Link>
        </div>
      )}

      {/* PC:全項目表示 */}
      <ol className="hidden md:flex items-center gap-2 flex-wrap">

この書き方が可読性が高いのかは少し自信がありませんが...

こんな感じに一つ上の階層(研究室詳細画面なら研究室一覧画面)のみ表示させることで戻れるようにしています。
image.png

5. 学部一覧画面修正

現在の学部一覧画面は、このように学部名が全然収まりきっていません。
image.png

resources/js/Pages/Faculty/Index.jsx

クリックでコードを見る
\project-root\src\resources\js\Pages\Faculty\Index.jsx
        {/* コンテンツ部分 */}
        {hasResults ? (
          <div className="w-full grid grid-cols-1 md:grid-cols-3 gap-6 mt-8 justify-items-center">
            {faculties.map(faculty => (
              <FacultyCard key={faculty.id} faculty={faculty} query={query} />
            ))}
          </div>
        ) : (
          <div className="flex-1 flex items-center justify-center">
            <p className="text-[#747D8C]">まだ学部が登録されていません。</p>
          </div>
        )}

PCサイズの時は、3列ではなく1列表示にしました。
image.png

6. 研究室一覧画面修正

こちらの画面は、かなり崩れてしまっていますね...w
image.png

カードタイトル

タイトルの文字を少し小さくして改行されないようにします。

resources/js/Components/Lab/LabCard.jsx

クリックでコードを見る
\project-root\src\resources\js\Components\Lab\LabCard.jsx
      <div className="ml-6 min-w-0">
        <span className="block text-xl md:text-2xl font-bold text-[#747D8C] truncate">

画面上部の配置

スマホの時は、絞り込みメニューを一つ下に表示させます。

resources/js/Pages/Lab/Index.jsx

クリックでコードを見る
\project-root\src\resources\js\Pages\Lab\Index.jsx
      <div className="flex flex-col items-center min-h-full">
        <div className="w-full flex flex-row items-center justify-between">
          {/* パンくずリスト 左寄せ */}
          <div>
            <Breadcrumb university={faculty.university} faculty={faculty} query={query} />
          </div>
          {/* 研究室件数 + ケバブメニュー 右寄せ+ソート */}
          <div className="flex items-center gap-2">
            <p className="text-[#747D8C]">{labs.total}件の研究室</p>
            <select
              value={sort}
              onChange={handleSortChange}
              className="hidden md:block text-sm text-[#747D8C] bg-[#EEF5F9] border border-[#747D8C] rounded px-3 py-1 pr-8 outline-none focus:outline-none focus:ring-0 focus:border-[#747D8C]"
            >
              {sortOptions.map(option => (
                <option key={option.value} value={option.value}>
                  {option.label}
                </option>
              ))}
            </select>
            {/* ケバブメニュー */}
            <div className="relative" ref={menuRef}>
              <button
                onClick={() => setIsMenuOpen(!isMenuOpen)}
                className="p-2 rounded-full"
              >
                <KebabIcon />
              </button>
              {isMenuOpen && <MenuPopover addLabel="研究室を追加する" onAddClick={handleAddLabClick} onEditClick={handleEditClick} onViewHistoryClick={handleViewHistoryClick} onDeletionRequestClick={handleDeletionRequestClick} />}
            </div>
          </div>
        </div>
        {/* スマホ用ソート:左寄せで下に配置 */}
        <div className="w-full md:hidden mt-2">
          <select
            value={sort}
            onChange={handleSortChange}
            className="text-sm text-[#747D8C] bg-[#EEF5F9] border border-[#747D8C] rounded px-3 py-1 pr-8 outline-none focus:outline-none focus:ring-0 focus:border-[#747D8C]"
          >
            {sortOptions.map(option => (
              <option key={option.value} value={option.value}>
                {option.label}
              </option>
            ))}
          </select>
        </div>

image.png

プルダウンメニュー

絞り込みのプルダウンメニューがでかすぎます...w
image.png

...と思ったら、別のFireFoxっていうブラウザで開くとそんなことはありませんでした。

image.png

なので、これについては修正は必要ありません。

なお、今後はFireFoxでサイズ感を確認したいと思います。

7. 研究室詳細画面修正

男女比のメーターがちょっと右寄りになっていますね。
image.png

...あと、ヘッダーのタイトルが全然収まっていないので再度修正が必要そうですね。

男女比バー

resources/js/Pages/Lab/Show.jsx

クリックでコードを見る
\project-root\src\resources\js\Pages\Lab\Show.jsx
                {lab.gender_ratio_male != null && lab.gender_ratio_female != null ? (
                  <div className="flex w-full h-6 rounded overflow-hidden text-sm text-white font-medium md:ml-4">
                    {lab.gender_ratio_male > 0 && (

今更ですけど、Tailwind CSSを使うとレスポンシブ対応もかなり楽に書けますね。

ヘッダーのタイトル

研究室名の前についていた大学名と学部名を、学部名の前についていた大学名を取って、裸の状態で表示させるように変えます。

  • resources/js/Components/Header.jsx
クリックでコードを見る
\project-root\src\resources\js\Components\Header.jsx
const Header = ({ title, mobileTitle, onOpenSidebar, headerRight }) => {

      {/* 中央:タイトル */}
      <h1 className="absolute left-1/2 -translate-x-1/2 w-[50%] text-center truncate text-sm md:text-xl font-semibold text-black">
        {mobileTitle && (
          <span className="md:hidden">{mobileTitle}</span>
        )}
        <span className={mobileTitle ? 'hidden md:inline' : ''}>{title}</span>
      </h1>
  • resources/js/Layouts/AppLayout.jsx
クリックでコードを見る
\project-root\src\resources\js\Layouts\AppLayout.jsx
const AppLayout = ({ children, title, mobileTitle, mode='default', headerRight }) => {

        <Header title={title} mobileTitle={mobileTitle} onOpenSidebar={() => setIsSidebarOpen(true)} headerRight={headerRight} />

  • resources/js/Pages/Lab/Index.jsx
クリックでコードを見る
\app-lab-review\project-root\src\resources\js\Pages\Lab\Index.jsx
    <AppLayout title={`${faculty.university.name} ${faculty.name}`} mobileTitle={faculty.name}>
  • resources/js/Pages/Lab/Show.jsx
クリックでコードを見る
\project-root\src\resources\js\Pages\Lab\Show.jsx
    <AppLayout title={`${lab.faculty.university.name} ${lab.faculty.name} ${lab.name}`} mobileTitle={lab.name}>

これでどうにか収まるようになりましたね。
image.png

8. マイページ修正

マイページも見た目が崩れてしまっていますね...
image.png

resources/js/Pages/MyPage/Index.jsx

クリックでコードを見る
\project-root\src\resources\js\Pages\MyPage\Index.jsx
				{/* 基本情報 */}
				<div className="flex items-center justify-between border-b border-black pb-2 w-full">
					<h2 className="text-xl font-bold text-black">基本情報</h2>
					<span className="text-sm text-[#747D8C]">
						利用開始日: {user?.created_at ? new Date(user.created_at).toLocaleDateString('ja-JP') : ''}
					</span>
				</div>

				{/* ニックネーム */}
				<div className="mt-6 mb-4 flex flex-col md:flex-row md:items-center">
					<h3 className="text-base md:text-lg font-semibold text-[#747D8C] md:w-40 md:shrink-0">ニックネーム</h3>
					<div className="mt-2 md:mt-0 w-full md:w-96">
						<UserInfoBar value={user?.name} onOpenEditDialog={() => setUserEditModalOpen(true)} />
					</div>
				</div>

				{/* e-Mailアドレス */}
				<div className="mt-6 mb-4 flex flex-col md:flex-row md:items-center">
					<h3 className="text-base md:text-lg font-semibold text-[#747D8C] md:w-40 md:shrink-0">e-Mailアドレス</h3>
					<div className="mt-2 md:mt-0 w-full md:w-96">
						<UserInfoBar value={user?.email} onOpenEditDialog={() => setUserEditModalOpen(true)} />
					</div>
				</div>

				{/* パスワード */}
				<div className="mt-6 mb-4 flex flex-col md:flex-row md:items-center">
					<h3 className="text-base md:text-lg font-semibold text-[#747D8C] md:w-40 md:shrink-0">パスワード</h3>
					<div className="mt-2 md:mt-0 w-full md:w-96">
						<UserInfoBar value="••••••••" onOpenEditDialog={() => setUserEditModalOpen(true)} />
					</div>
				</div>

かなりマシになったのではないでしょうか...!(笑)
image.png

ここまでできたら、コミット・プッシュ、PR作成・マージをお忘れなく!

9.(おまけその17)リファクタリング: 不要なファイルを削除

最後はおまけコーナーでもやって閉めますか。

9.1 ブランチ運用

現在、使い終わったブランチをそのままにしていたのでとんでもないことになっていると思います。

不要なブランチはすべて消しましょうw

ローカルのブランチ整理

project-root/src
$ git branch -D △△

※△△はブランチ名です。
※今チェックアウトしているブランチを削除することはできません。

リモートのブランチ整理

GitHubの「Branches」のゴミ箱アイコンから削除できます。
image.png

両方ともmainブランチとdevelopブランチのみを残して、それ以外をすべて削除してください。

全部で、59ブランチもあってめちゃくちゃ大変かもしれませんがw
(本当は使わなくなった時点で削除しましょう。僕は、本業で同じことをして同僚に笑われてしまいました)

新規ブランチ

ここまで整理出来たら、developブランチをpullして新しいブランチを切って作業をしましょう。

ブランチ名は、refactor/frontend/unnecessary-componentsとかにします。

9.2 不要なコンポーネント削除

使われなくなったコンポーネントを削除します。

  • resources/js/Components/ApplicationLogo.jsx
  • resources/js/Components/Checkbox.jsx
  • resources/js/Components/DangerButton.jsx
  • resources/js/Components/Dropdown.jsx
  • resources/js/Components/InputError.jsx
  • resources/js/Components/InputLabel.jsx
  • resources/js/Components/Modal.jsx
  • resources/js/Components/NavLink.jsx
  • resources/js/Components/PrimaryButton.jsx
  • resources/js/Components/ResponsiveNavLink.jsx
  • resources/js/Components/SecondaryButton.jsx
  • resources/js/Layouts/AuthenticatedLayout.jsx
  • resources/js/Layouts/GuestLayout.jsx
  • resources/js/Pages/Auth/ConfirmPassword.jsx
  • resources/js/Pages/Auth/ForgotPassword.jsx
  • resources/js/Pages/Auth/ResetPassword.jsx
  • resources/js/Pages/Auth/VerifyEmail.jsx
  • resources/js/Pages/Lab/Home.jsx
  • resources/js/Pages/MyPage/Bookmarks.jsx
  • resources/js/Pages/Profile/Edit.jsx
  • resources/js/Pages/Profile/Partials/DeleteUserForm.jsx
  • resources/js/Pages/Profile/Partials/UpdatePasswordForm.jsx
  • resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.jsx

結構たくさん削除してしまったので後でエラーが起きそうで怖いですが...
その時はその時で直しましょうw

コミット・プッシュ、PR作成・マージ、ブランチの削除をしましょう。

10. まとめ・次回予告

本日を持ちまして、かなり長くなってしまっていたフロントエンド実装編もおしまいです!

バックエンド編、ひいては設計編時の考慮が貧弱だったために、その部分の修正をフロントエンド編でしりぬぐいしていた感は否めませんでしたw

改めて、設計の大切さを実感しつつ、自分の成長も感じている昨今です。

次回からは、テスト編が始まります...!!

今の職場では、ほとんどテストケースを書かないので、これまた勉強しながらになるとは思いますが、もう少しだけお付き合いくださればと思います。

これまでの記事一覧

☆要件定義・設計編

☆環境構築編

☆バックエンド実装編

☆フロントエンド実装編

軽く宣伝

YouTubeを始めました(というか始めてました)。
内容としては、Webエンジニアの生活や稼げるようになるまでの成長記録などを発信していく予定です。

現在、まったく再生されておらず、落ち込みそうなので、見てくださる方はぜひ高評価を教えもらえると励みになると思います。"(-""-)"

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?