Chapter 14

3-4. ユーザー登録ページのカスタマイズ

Masuyama
Masuyama
2022.10.15に更新

ユーザー登録ページのカスタマイズ

本章ではユーザー登録ページ /users/sign_up で使われる view ファイルを編集し、デザインを調整します。

変更前 view ファイルの確認

devise のコマンドで作成した直後ですと、ユーザー登録用の view ファイルは以下のようになっています。

(変更前): app/views/devise/registrations/new.html.erb

<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <% if @minimum_password_length %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

view の修正

では、ユーザー登録の view を次のように変更してください。

(変更後): app/views/devise/registrations/new.html.erb

<%= form_with scope: resource, as: resource_name, url: registration_path(resource_name), class: "space-y-6 w-3/4 max-w-lg" ,local: true do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

  <label class="block text-xl font-bold text-gray-700">ユーザー登録</label>
  <div class="mt-1">
    <label class="text-gray-700 text-md">
      メールアドレス
    </label>
    <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm placeholder-gray-400 border border-gray-300 rounded-md", placeholder: "test@exeample.com" %>
  </div>
  <div class="mt-1">
    <label class="text-gray-700 text-md">
      ニックネーム
    </label>
    <%= f.text_field :nickname, autocomplete: "nickname", class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm placeholder-gray-400 border border-gray-300 rounded-md", placeholder: "エンジニアの卵" %>
  </div>
  <div class="mt-1">
    <label class="text-gray-700 text-md">
      パスワード (6文字以上)
    </label>
    <%= f.password_field :password, autocomplete: "new-password", class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm placeholder-gray-400 border border-gray-300 rounded-md", placeholder: "pass1234" %>
  </div>
  <div class="mt-1">
    <label class="text-gray-700 text-md">
      確認用パスワード
    </label>
    <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm placeholder-gray-400 border border-gray-300 rounded-md", placeholder: "pass1234" %>
  </div>

  <button type="submit" class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
    <%= f.submit "ユーザー登録" %>
  </button>
<% end %>

画面確認

では開発用サーバを起動し、ブラウザで確認していきましょう。

bin/dev コマンドで開発用サーバを起動します。

$ bin/dev
09:33:09 web.1  | started with pid 7086
09:33:09 css.1  | started with pid 7087
09:33:11 web.1  | => Booting Puma
09:33:11 web.1  | => Rails 7.0.3 application starting in development
09:33:11 web.1  | => Run `bin/rails server --help` for more startup options
09:33:11 web.1  | Puma starting in single mode...
09:33:11 web.1  | * Puma version: 5.6.4 (ruby 3.0.4-p208) ("Birdie's Version")
09:33:11 web.1  | *  Min threads: 5
09:33:11 web.1  | *  Max threads: 5
09:33:11 web.1  | *  Environment: development
09:33:11 web.1  | *          PID: 7086
09:33:11 web.1  | * Listening on http://127.0.0.1:3000
09:33:11 web.1  | * Listening on http://[::1]:3000
09:33:11 web.1  | Use Ctrl-C to stop
09:33:12 css.1  |
09:33:12 css.1  | Rebuilding...
09:33:12 css.1  | Done in 201ms.

サーバを起動できたら、ブラウザで http://localhost:3000/users/sign_up にアクセスします。

<img width="657" alt="image" src="https://user-images.githubusercontent.com/68495563/180105295-4980a292-4cda-4b05-8921-67860adf6af6.png">

このような画面になっていれば OK です。

画面全体のレイアウト調整

ユーザー登録画面を見ると分かると思いますが、全体的にコンテンツがブラウザ内の左側に寄ってしまっていますね。

<img width="1429" alt="image" src="https://user-images.githubusercontent.com/68495563/180105517-cca2e100-83b7-414d-ac78-914d4dcf03a2.png">

TechLog は 1 カラム構成のアプリケーションであるため、コンテンツが真ん中になるように調整しましょう。

修正するのは、各 view を読み込んでいる大元の view である app/views/layouts/application.html.erb ファイルです。

(変更前): app/views/layouts/application.html.erb

...
  <body>
    <main class="container mx-auto mt-28 px-5 flex">
      <%= yield %>
    </main>
  </body>
...

TailwindCSS を指定して rails new コマンドを実行したため、TailwindCSS 仕様のスタイルクラスが最初から適用されていることが分かります。
今回は真ん中に寄せるスタイルを適用したいので、上記の部分を以下のように変更してください。
(ついでに背景色も変更)

(変更後): app/views/layouts/application.html.erb

...
  <body class="h-screen bg-blue-50">
    <main class="container mx-auto mt-20 py-8 px-5 flex items-center justify-center">
      <%= yield %>
    </main>
  </body>
...

変更したら再度ブラウザで http://localhost:3000/users/sign_up にアクセスします。
コンテンツが真ん中に寄ったことが確認できます。

<img width="1199" alt="image" src="https://user-images.githubusercontent.com/68495563/180105922-7d005ec2-1075-4f94-a9d4-be41052d46d7.png">

テスト作成

デザイン調整を行なうにあたり、devise がデフォルトで用意している view ファイルから当然ながら変わっています。
言い換えると、devise の gem 自体でテストされている view とは異なる view を使っております。

そのため、修正した後の画面で想定通りの動作をすることを確認するテストを追加しましょう。

System Spec ファイル作成

トップページ (Home#top) の時と同様、System Spec ファイルをコマンドで生成します。

$ bundle exec rails g rspec:system user
      create  spec/system/users_spec.rb

テスト修正

次は作成されたテストファイル spec/system/users_spec.rb を修正していきましょう。

spec/system/users_spec.rb

require 'rails_helper'

describe 'User', type: :system do
  before { driven_by :selenium_chrome_headless }

  # ユーザー情報入力用の変数
  let(:email) { 'test@example.com' }
  let(:nickname) { 'テスト太郎' }
  let(:password) { 'password' }
  let(:password_confirmation) { password }

  describe 'ユーザー登録機能の検証' do
    before { visit '/users/sign_up' }

    # ユーザー登録を行う一連の操作を subject にまとめる
    subject do
      fill_in 'user_nickname', with: nickname
      fill_in 'user_email', with: email
      fill_in 'user_password', with: password
      fill_in 'user_password_confirmation', with: password_confirmation
      click_button 'ユーザー登録'
    end

    context '正常系' do
      it 'ユーザーを作成できる' do
        expect { subject }.to change(User, :count).by(1) # Userが1つ増える
        expect(current_path).to eq('/') # ユーザー登録後はトップページにリダイレクト
      end
    end
  end
end

ここまでで一旦、テストを実行しておきましょう。

$ bin/rspec spec/system/users_spec.rb
...
User
  ユーザー登録機能の検証
    正常系
DEBUGGER[rspec#59177]: Attaching after process 59169 fork to child process 59177
      ユーザーを作成できる

Finished in 2.66 seconds (files took 0.71727 seconds to load)
1 example, 0 failures

テストに通ることを確認してください。

正常系で問題なかったので、続けて異常系 (メールアドレスが空欄の場合など) に関するテストも追加した全文がこちらです。

require 'rails_helper'

describe 'User', type: :system do
  before { driven_by :selenium_chrome_headless }

  # ユーザー情報入力用の変数
  let(:email) { 'test@example.com' }
  let(:nickname) { 'テスト太郎' }
  let(:password) { 'password' }
  let(:password_confirmation) { password }

  describe 'ユーザー登録機能の検証' do
    before { visit '/users/sign_up' }

    # ユーザー登録を行う一連の操作を subject にまとめる
    subject do
      fill_in 'user_nickname', with: nickname
      fill_in 'user_email', with: email
      fill_in 'user_password', with: password
      fill_in 'user_password_confirmation', with: password_confirmation
      click_button 'ユーザー登録'
    end

    context '正常系' do
      it 'ユーザーを作成できる' do
        expect { subject }.to change(User, :count).by(1) # Userが1つ増える
        expect(current_path).to eq('/') # ユーザー登録後はトップページにリダイレクト
      end
    end

    context '異常系' do
      context 'nicknameが空の場合' do
        let(:nickname) { '' }
        it 'ユーザーを作成せず、エラーメッセージを表示する' do
          expect { subject }.not_to change(User, :count) # Userが増えない
          expect(page).to have_content("Nickname can't be blank") # エラーメッセージのチェック
        end
      end

      context 'nicknameが20文字を超える場合' do
        let(:nickname) { 'あ' * 21 }
        it 'ユーザーを作成せず、エラーメッセージを表示する' do
          expect { subject }.not_to change(User, :count)
          expect(page).to have_content('Nickname is too long (maximum is 20 character')
        end
      end

      context 'emailが空の場合' do
        let(:email) { '' }
        it 'ユーザーを作成せず、エラーメッセージを表示する' do
          expect { subject }.not_to change(User, :count)
          expect(page).to have_content("Email can't be blank")
        end
      end

      context 'passwordが空の場合' do
        let(:password) { '' }
        it 'ユーザーを作成せず、エラーメッセージを表示する' do
          expect { subject }.not_to change(User, :count)
          expect(page).to have_content("Password can't be blank")
        end
      end

      context 'passwordが6文字未満の場合' do
        let(:password) { 'a' * 5 }
        it 'ユーザーを作成せず、エラーメッセージを表示する' do
          expect { subject }.not_to change(User, :count)
          expect(page).to have_content('Password is too short (minimum is 6 characters')
        end
      end

      context 'passwordが128文字を超える場合' do
        let(:password) { 'a' * 129 }
        it 'ユーザーを作成せず、エラーメッセージを表示する' do
          expect { subject }.not_to change(User, :count)
          expect(page).to have_content('Password is too long (maximum is 128 characters)')
        end
      end

      context 'passwordとpassword_confirmationが一致しない場合' do
        let(:password_confirmation) { "#{password}hoge" } # passwordに"hoge"を足した文字列にする
        it 'ユーザーを作成せず、エラーメッセージを表示する' do
          expect { subject }.not_to change(User, :count)
          expect(page).to have_content("Password confirmation doesn't match Password")
        end
      end
    end
  end
end

なお、バリデーションによるエラーメッセージは現在は英語ですが、TechLog は日本語用のアプリですので後に日本語に修正していきます。
その際、このテストも修正することになるので覚えておきましょう。

テスト実行

すべてのテストを実行し、すべて成功することを確認します。

$ bin/rspec
Finished in 5.06 seconds (files took 0.33117 seconds to load)
14 examples, 0 failures

変更をコミット

ここまでの変更をコミットして完了です。

$ git add .
$ git commit -m "ユーザー登録画面の修正"
$ git push