【Rails6】Action Text × Bootstrap(4系) Webpacker管理だと相性悪すぎる件

## はじめに
こんにちは！

<font color="red"> **DMM WEBCAMP Advent Calendar 2023** :christmas_tree:</font> **24日目**も、同じく @Keichan_15 が担当させて頂きます！:v:

今回はやはり普段Railsをお教えしている身として、ちゃんとRailsに関する記事を書こうかなと…:thinking:

ところで、皆さんは **Action Text** と呼ばれるRails6から追加された新機能についてご存じでしょうか。
Action Textは**リッチテキストを編集するための便利なエディター** で、通常よりもモダンなフォームを作成することができるエディターです。

こんな感じのヤツ ↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/ae8991fc-fc89-96e0-5717-02aace3b82bf.png)

とても便利な機能なのですが、実はWebpacker側でBootstrapを管理している状態で共存させると… ↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/8ae1bf68-b301-dff0-2fc1-617e3fef8307.png)

~~モダン云々のレベルを超えてる~~ こんな感じで酷過ぎる画面にダウングレードします。おいAction Text！仕事せえ！:zipper_mouth:

そこで本記事では、

- **この現象が発生するパターンってどんな時？**
- **BootstrapをWebpacker側で管理する場合でも、共存して表示できるようにするには？**

上記2つに絞ってご紹介していきたいと思います！:raised_hands:

早速リッチテキストエディタを復活させる旅の出発です！！

## 環境
- Ruby(3.1.2)
- Rails(6.1.7.6)
- Node.js(16.18.0)
- Bootstrap(4.6.2)
- WSL2(Ubuntu 20.04.5 LTS)
- Visual Studio Code

## 事前準備

今回はAction Textを動作させるために、シンプルなタスク管理アプリケーションを作成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/1819d801-3c40-1022-2fa0-e01e4cae4173.png)

今回、Rails6からデフォルトで入っている **Webpackerを用いてBootstrapを管理する手法を取っていた** としましょう。

要するに以下のような形で `app/javascript`配下で`CSS`, `JS`周りを管理しているパターンですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/e4364a3a-0eb7-2303-286f-da76d0fd622a.png)



導入方法について細かく知りたい場合は、以下にて紹介されています！

https://autovice.jp/articles/14

今回はこちらの記事を参考に、若干アレンジを加えて導入しています。

```terminal:terminal
$ yarn add jquery bootstrap@4.6.2 popper.js
```

```javascript:config/webpack/environment.js
const webpack = require('webpack')
environment.plugins.prepend(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery',
    Popper: 'popper.js'
  })
)
```
`application.scss`については別途、新規作成が必要です。
```scss:app/javascript/stylesheets/application.scss
@import '~bootstrap/scss/bootstrap';
```

```javascript:app/javascript/packs/application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"

import "jquery";
import "popper.js";
import "bootstrap";
import "../stylesheets/application"; 

Rails.start()
Turbolinks.start()
ActiveStorage.start()
```

```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>TestApp</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>
```

```ruby:config/routes.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  resources :tasks
end
```

これで最低限、Bootstrapを使用する準備が整いました！:raised_hands:

## Model & Controller & View 作成

では早速`Action Text`を使用してみましょう！

導入手順は[Railsガイド](https://railsguides.jp/action_text_overview.html)を参考にしながら進めていきます！

https://railsguides.jp/action_text_overview.html

まず`Action Text`をインストールします。

```terminal:terminal
$ rails action_text:install
```

インストールが完了すると同時に、`application.js`に以下の記述が追加されていることを確認します。

```diff_javascript:app/javascript/packs/application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"

import "jquery";
import "popper.js";
import "bootstrap";
import "../stylesheets/application"; 

Rails.start()
Turbolinks.start()
ActiveStorage.start()

// この2行が自動で追加されていることを確認！
+ require("trix")
+ require("@rails/actiontext")
```

次に`Model`を作成します。

今回はシンプルなタスク管理のアプリケーションのため、`Task`という命名の`Model`を作成しました。

```terminal:terminal
$ rails g model Task
```

カラムは以下を用意しました。

- `title` : タスクのタイトル

```ruby:db/migrate/hogehoge_create_tasks.rb
class CreateTasks < ActiveRecord::Migration[6.1]
  def change
    create_table :tasks do |t|
      t.string :title
      t.timestamps
    end
  end
end
```

```terminal:terminal
$ rails db:migrate
```

今回はこの`Task`というモデルに、リッチテキストエディタのフィールドを追加します。

`task.rb`に以下の1行を追加します。

```diff_ruby:app/models/task.rb
class Task < ApplicationRecord

+  has_rich_text :content

end
```

次に`View`を作成します。

```erb:app/views/tasks/index.html.erb
<div class="container">
  <div class="row">
    <div class="col-md-12">
    <% @tasks.each do |task| %>
      <table class="table">
        <thead>
          <tr>
            <th>Title</th>
            <th>Body</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><%= task.title %></td>
            <td><%= task.body %></td>
            <td><%= task.content %></td>
          </tr>
        </tbody>
      </table>
    <% end %>
    </div>
  </div>

  <div class="row">
    <div class="col-md-12">
      <h2>New Task</h2>
      
      <%= form_with model: @task do |f| %>
      <div class="form-group">
        <%= f.label :title %>
        <%= f.text_field :title, class: "form-control" %>
      </div>

      <div class="form-group">
        <%= f.label :content %>
        <%= f.rich_text_area :content, class: "form-control" %>
      </div>

      <div class="form-group">
        <%= f.submit 'Create Task', class: "btn btn-success" %>
      </div>
      <% end %>
    </div>
  </div>
</div>
```

最後に`Controller`を作成し、タスク投稿機能を実装していきましょう！

```terminal:terminal
$ rails g controller tasks
```

```rb:app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
    @task = Task.new
  end

  def create
    @task = Task.new(task_params)
    @task.save
    redirect_to tasks_path
  end

  private

  def task_params
    params.require(:task).permit(:title, :content)
  end
end
```

実装内容に問題が無ければ`/tasks`に遷移してみましょう。

一旦以下のような表示になっていればOKです！:ok_hand:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/98d54354-0214-c6b1-37ea-303a9ea69236.png)

## リッチテキストエディタ… どこ？を解決する
実を言うと、この画面は本来以下のような表示になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/6e28c887-e5be-d592-7e8f-1a4ad35a2b98.png)

では何故今こんなにも醜い表示になっているのでしょう…？

類似の条件を漁ってみると、以下のissueで似たような挙動を起こしている方を発見しました。

https://github.com/rails/rails/issues/43441

どうやら`Action Text`と`Webpacker`はあまり相性が良くない(?)らしく、今回のようにWebpackerでCSS類を管理している場合に、競合してリッチテキストエディタの表示が崩れるという現象が起きるみたいです。

ちなみに`Tailwind CSS`を採用した場合にも起きる模様…。ヒデエな（笑）

https://qiita.com/MIDO-ruby7/items/fd2c31ba00d8420cf630

## 解決方法
同issue内で解決方法を提示している神がいました。:sparkles:

https://github.com/rails/rails/issues/43441#issuecomment-1019426236

まず `app/views/layouts/application.html.erb` の`<head>`内に以下の1行を追加します。

```erb:app/views/layouts/application.html.erb
<%= stylesheet_link_tag "actiontext", "data-turbo-track": "reload" %>
```

次に `app/assets/config/manifest.js`に以下の1行を追加します。

```javascript:app/assets/config/manifest.js
//= link actiontext.css
```

それぞれ記述を追加した完成版は以下になります！

```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>TestApp</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "actiontext", "data-turbo-track": "reload" %>
    <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>
```

```javascript:app/assets/config/manifest.js
//= link_tree ../images
//= link_directory ../stylesheets .css
//= link actiontext.css
```

上記修正した所、以下のように画面が正しく表示されるようになりました！:thumbsup:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2798073/6e28c887-e5be-d592-7e8f-1a4ad35a2b98.png)

## 参考
https://github.com/rails/rails/issues/43441

https://qiita.com/MIDO-ruby7/items/fd2c31ba00d8420cf630

## おわりに
いかがでしたでしょうか。

今回は`Action Text`と`Bootstrap`があまりにも相性が悪すぎる問題と、その解決方法についてご紹介しました。

`Action Text`は色々な箇所で使い道がありそうですので、今回の内容を元に更なるカスタマイズを施してみるのも良いかもしれませんね！

さて今年のAdvent Calendarもラスト1日になりました…:disappointed_relieved:

ラストはお馴染みGitHubのREADMEをアレンジしてみた！をまとめます！お楽しみに！