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

Q&AページのQuestion部分を非Vue化した #7439

Merged
merged 26 commits into from
Mar 9, 2024

Conversation

junohm410
Copy link
Contributor

@junohm410 junohm410 commented Feb 26, 2024

Issue

概要

個別のQ&Aページ(https://bootcamp.fjord.jp/questions/{id})のうち、答え部分を除いた質問本体部分を、Vueコンポーネント(app/javascript/components/question-page.vueの一部)による実装ではなく、Railsのviewテンプレートによる実装に変更しました。

これにより、質問の更新時にhttps://bootcamp.fjord.jp/questions/{id}/editへの画面遷移が発生するようになっています。

質問ページ下部の答え部分(AIの答え+投稿された答え)は別Issueで非Vue化するとのことなので、こちらは既存の実装を残しております。
app/javascript/components/question-page.vueの質問部分を省いたコードをリネームし、単体で動くように修正)

また、副次的に以下の点が変更されています。

  • 質問作成・更新時のフラッシュメッセージの整理
  • 質問作成時にも「プラクティス選択なし」を選べるようにする
  • 「プラクティス選択なし」の状態かつ質問本文でユーザーがメンションをされると、メンション通知のタイトルで使われるプラクティス名部分は「プラクティス選択なし」になる

質問作成・更新時のフラッシュメッセージの整理

以下のように整理されています。
過去の状態・詳しい議論の経緯は以下のコメントをご参照ください。
#7356 (comment)

# 新規作成
新規作成(公開) -> 「質問を作成しました」
新規作成(WIP) -> 「質問をWIPとして保存しました」

# WIP -> 公開
「質問を更新しました」

# 公開 -> WIP
「質問をWIPとして保存しました」

# 公開 -> 公開(内容のみ変更)
「質問を更新しました」

# WIP -> WIP(内容のみ変更)
「質問をWIPとして保存しました」

質問作成時にも「プラクティス選択なし」を選べるようにする

質問更新時に選べるようになっていた「プラクティス選択なし」を、質問作成時でも選べるようにしました。
非Vue化で、作成・更新ともに同じ_formパーシャルを使うようになったためです。
過去の状態・詳しい議論の経緯は以下のコメントをご参照ください。
#7356 (comment)
image

「プラクティス選択なし」の状態かつ質問本文でユーザーがメンションをされると、メンション通知のタイトルで使われるプラクティス名部分は「プラクティス選択なし」になる

現在の本番環境では、上記の条件で質問が作成・更新されると、存在しない関連プラクティスのタイトルを通知タイトルに使おうとするため、エラーが起こります。
プラクティスが選択されていない質問でのメンション通知は、「プラクティス選択なし」をタイトルとして扱うように変更し、上記のバグを修正しています。
エラーの状態・詳しい議論の経緯は以下のコメントをご参照ください。
#7356 (comment)
image

作業内容のおおまかなイメージ

変更前のQ&AページのQuestion部分の構成

image
# Vue・質問+答え全体

- QuestionPage(app/javascript/components/question-page.vue、大元の親コンポーネント)
    - QuestionEdit (質問本文・編集画面、Vue)
        - UserIcon (ユーザーアイコン、Vue)
        - WatchToggle (Watchボタン、Vue)
        - BookmarkButton (ブックマークボタン、Vue)
        - tags (タグ機能、Vue)
        - reaction (絵文字によるリアクション機能、Vue)
    - AIAnswer (メンターのみのAI回答機能、Vue)
    - Answers (投稿された回答一覧、Vue)
        - Answer (個別の回答、Vue)

変更後のQ&AページのQuestion部分の構成

image
# Railsのview・質問部分

- questions/show
    - render 'question_header'
        - render 'users/icon' (ユーザーアイコンは非同期処理の必要がないため、既存のパーシャルに差し替え)
        - WatchToggle (同じVueコンポーネントをrailsのviewから読み込み)
        - BookmarkButton (同じVueコンポーネントをrailsのviewから読み込み)
        - tags (React版のタグ機能がすでに実装済みのため、そちらをrailsのviewから読み込み)
    - render 'question_body'
        - render 'reactions/reactions' (VueではなくpureなJSで実装されたリアクション機能を持つ既存のパーシャルがあるため、そちらに差し替え)

# Question部分を切り取ったVue・答え部分

- QuestionAnswers(app/javascript/components/question-answers.vue、大元の親コンポーネントをリネームし、単独で動くよう修正)
    - AIAnswer
    - Answers
        - Answer

変更後の非同期の部品をRailsのパーシャルに差し替えるのか、Reactに差し替えるのかなどの選択については、非Vueでscaffoldライクに実装されているpagesreportsと合わせることを基準としております。

変更確認方法

画面遷移を伴った更新(通常の更新、公開 -> WIP、WIP→公開)・フラッシュメッセージ・「プラクティス選択なし」の追加について、下記の方法でご確認いただけます。

  1. chore/convert-question-section-from-vue-to-slimをローカルに取り込む
  2. サーバーを立ち上げる。
  3. komagataでログインし、http://localhost:3000/questions/newにアクセスする。
  4. プラクティス入力部分が「プラクティス選択なし」であることを確認する。プラクティス・タイトル・本文・タグを入力(内容はなんでもOK)し、「登録する」ボタンを押す。
  5. 「質問を作成しました。」というフラッシュメッセージが出て、遷移先の質問ページで質問が作成されたことを確認し、そのまま「内容修正」ボタンを押す。
  6. http://localhost:3000/questions/{id}/editに遷移したことと、4. で入力した内容がセットされていることを確認する。
  7. プラクティス・タイトル・本文・タグを編集し(内容はなんでもOK)、「更新する」ボタンを押す。
  8. 「質問を更新しました。」というフラッシュメッセージが出て、遷移先の質問ページで編集内容が反映されたことを確認し、そのまま「内容修正」ボタンを押す。
  9. 遷移先で、「WIP」ボタンを押す。
  10. 「質問をWIPとして保存しました。」というフラッシュメッセージが出たことを確認する。
  11. 4.で作成した質問のページにアクセスする(1. 〜10. の操作の直後の場合、http://localhost:3000/questionsの一番上にリンクがあると思います)。
  12. 質問タイトルの左横に「WIP」マークがあることを確認し、「内容修正」ボタンを押す。
  13. 遷移先で、「質問を公開」ボタンを押す。
  14. 「質問を更新しました。」というフラッシュメッセージが出て、遷移先の質問ページで、質問タイトルの左横に「未解決」マークがあること(=WIPでない)を確認する。

「プラクティス選択なし」かつメンションをした際にバグが起きないことは以下の方法でご確認いただけます。

  1. kimuraでログインし、質問作成画面にアクセス。
  2. 「プラクティス選択なし」の状態のまま、タイトルを入力。本文では@hatsunoと入力する(hatsunoにメンションを飛ばす)。
  3. 「登録する」をクリックする。
  4. 質問が登録されたことを確認したら、hatsunoでログインする。
  5. 右上の通知ベルを開き、「kimuraさんのQ&A「プラクティス選択なし」でkimuraさんからメンションがきました。」という通知があること確認する。

Screenshot

変更前

質問更新時に画面遷移が起こらず、同じページ上で質問本文部分が編集UIに書き換わる。
KhzK1iyHkA

変更後

質問更新時にhttps://bootcamp.fjord.jp/questions/{id}/editへの遷移が起こる(通常のRailsのscaffoldライクな動き)。
78Zuj8m1qU

Copy link
Contributor Author

@junohm410 junohm410 left a comment

Choose a reason for hiding this comment

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

変更部分の説明です。

@@ -41,6 +40,6 @@ def update
private

def question_params
params.require(:question).permit(:title, :description, :practice_id, :tag_list, :wip)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

非Vue化に伴い、APIを叩いて質問を更新するシナリオは「質問個別ページ上からのタグの編集」のみになりました。
なので、tag_list以外のパラメータは受け付けないようにしています。

またupdateアクションから、Newspaperによる通知の処理を削除しました。
この処理は更新によりwip状態が変更された場合に起こる処理でしたが、そもそもこの変更によりAPIからの更新でwipの状態が変わることがなくなったため、削除しています。

Comment on lines 16 to 15
questions =
case params[:target]
when 'solved'
Question.solved
when 'not_solved'
Question.not_solved.not_wip
else
Question.all
end
questions = Question.by_target(params[:target])
Copy link
Contributor Author

Choose a reason for hiding this comment

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

【行数オーバーのコントローラのリファクタリング】
ここだけではありませんが、showupdateアクションをコントローラに追加したため、クラスの行数が100行を超えRubocopに指摘されるようになったため、既存の処理の一部をリファクタリングし、行数を減らしています。

※厳密にはこのIssueの趣旨に関連する変更ではありませんが、Rubocopに新たな除外項目を増やすのはNGという方針のため、このようなリファクタリングも当PRに含めています。

コントローラ上でcase whenの条件分岐のロジックまで担当するのは責務的に重いのと、solvedなどのtargetによる絞り込みは汎用性があると考えられるので、この部分はモデルのスコープに移しています。

if @question.save
Newspaper.publish(:question_create, { question: @question })
redirect_to @question, notice: notice_message(@question)
redirect_to Redirection.determin_url(self, @question), notice: @question.generate_notice_message(:create)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Redirection.determin_urlは新規作成時・更新時のリダイレクト先を切り替えるクラスメソッドです。
下記のPRでIssueで実装されています。
#6741

Comment on lines +65 to +74
def update
set_wip
if @question.update(question_params)
Newspaper.publish(:question_update, { question: @question }) if @question.saved_change_to_wip?
redirect_to Redirection.determin_url(self, @question), notice: @question.generate_notice_message(:update)
else
render :edit
end
end

Copy link
Contributor Author

@junohm410 junohm410 Feb 27, 2024

Choose a reason for hiding this comment

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

APIで行っていた更新処理を、通常のRailsのコントローラに移しています。
機能的にはAPIでやっていたものと変わりません。

Comment on lines -100 to -109
def questions_property
case params[:target]
when 'solved'
QuestionsProperty.new('解決済みのQ&A', '解決済みのQ&Aはありません。')
when 'not_solved'
QuestionsProperty.new('未解決のQ&A', '未解決のQ&Aはありません。')
else
QuestionsProperty.new('全てのQ&A', 'Q&Aはありません。')
end
end
Copy link
Contributor Author

Choose a reason for hiding this comment

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

【行数オーバーのコントローラのリファクタリング】
特定のQuestionモデルのインスタンスに紐づく処理はないけれど、質問という関心ごとには確実に関連している処理のため、Questionのインスタンスメソッドではなくクラスメソッドに移しました。

Comment on lines -25 to +28
div(data-vue="QuestionPage" data-vue-current-user-id:number="#{current_user.id}" data-vue-question-id="#{@question.id}")
.question.page-content
= render 'question_header', question: @question
= render 'question_body', question: @question
div(data-vue="QuestionAnswers" data-vue-current-user-id:number="#{current_user.id}" data-vue-question-id="#{@question.id}")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

非Vue化で、質問本文の部分のうち、下の機能を各パーシャルに移しています。

  • ユーザーアイコン・Watchボタン・ブックマークボタン・タグ -> question_header
  • 質問本文・編集ページへのリンク(「内容修正」)・絵文字リアクション -> question_body

残った答え部分はQuestionAnsewersというVueコンポーネントにリネームし、ここから繋げています。

Comment on lines -31 to +36
[
@question.user.login_name,
@non_editable_user_login_name,
User.find_by(admin: true).login_name
].each do |name|
changed_title = "#{name} changed"
token = create_token(name, 'testtest')
patch @path,
params: { question: { title: changed_title } },
headers: { 'Authorization' => "Bearer #{token}" }

assert_response :ok
assert_equal changed_title, @question.reload.title
end
token = create_token('hajime', 'testtest')
patch @path,
params: { question: { tag_list: '新規タグ1,新規タグ2' } },
headers: { 'Authorization' => "Bearer #{token}" }
assert_response :ok
assert_equal %w[新規タグ1 新規タグ2], @question.reload.tag_list
Copy link
Contributor Author

@junohm410 junohm410 Feb 27, 2024

Choose a reason for hiding this comment

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

質問編集APIのコントローラテストの部分です。
本PRにより、APIのupdateアクションにはタグを編集する機能だけが残るため、テストもそのように修正しています。

Comment on lines +40 to +52

test '.by_target' do
solved_question = questions(:question3)
not_solved_question = questions(:question1)
assert_includes Question.by_target('solved'), solved_question
assert_not_includes Question.by_target('solved'), not_solved_question

assert_includes Question.by_target('not_solved'), not_solved_question
assert_not_includes Question.by_target('not_solved'), solved_question

assert_includes Question.by_target(nil), solved_question
assert_includes Question.by_target(nil), not_solved_question
end
Copy link
Contributor Author

Choose a reason for hiding this comment

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

【行数オーバーのコントローラのリファクタリング】
コントローラからモデルに移したスコープ・メソッドのテストを追加しています。

click_button '内容修正'
click_link '内容修正'
Copy link
Contributor Author

@junohm410 junohm410 Feb 27, 2024

Choose a reason for hiding this comment

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

非Vue化により、質問を編集する「内容修正」ボタンでRailsのlink_toを使用するようになったため、タグがbuttonではなくaタグに変わっています。
pagesreportsのページでも「内容修正」ボタンはaタグで実装されており、それにあわせています)

この変更により、このボタンをクリックする多くのシステムテストが落ちるようになったため修正しています。

Comment on lines -507 to -518

test 'using file uploading by file selection dialogue in textarea at editing question' do
question = questions(:question3)
visit_with_auth "/questions/#{question.id}", 'komagata'
click_button '内容修正'

element = first('.a-file-insert')
within element do
assert_selector 'input.js-question-file-input', visible: false
end
assert_equal '.js-question-file-input', find('textarea.a-text-input')['data-input']
end
Copy link
Contributor Author

@junohm410 junohm410 Feb 27, 2024

Choose a reason for hiding this comment

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

このテストは、Vueでの質問編集画面上のテキストエリアでファイルアップロードが機能することを確かめる内容になっています。

  • Vueによる質問編集画面がなくなったこと
  • Railsのformパーシャル上のテキストエリアにおけるこの機能のテストは、一つ上の'using file uploading by file selection dialogue in textarea'テストで行われていること

この2点を考慮し、本テストは削除しました。

assert_text '質問を更新しました'
assert_text '質問をWIPとして保存しました。'
Copy link
Contributor Author

@junohm410 junohm410 Feb 27, 2024

Choose a reason for hiding this comment

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

フラッシュメッセージについて、JSではなくRailsのみで一貫して処理することになったので、その都合で一部のシナリオのメッセージの文言が変わりました。

フラッシュメッセージを使っているテストが落ちるようになったものは、このような形で修正しています。
文言変更の詳細については、Issueのやりとりをご確認ください。
#7356 (comment)

@junohm410 junohm410 marked this pull request as ready for review February 27, 2024 08:02
@junohm410 junohm410 requested a review from SuzukaHori February 27, 2024 08:03
@junohm410
Copy link
Contributor Author

@SuzukaHori
お疲れ様です。こちらのレビューをお願いしたくご連絡させていただきました🙏
FileChangedの数が多くて申し訳ないのですが、そのぶん事前にコードの説明を多めにコメントさせていただきましたので、そちらを参照していただければ幸いです🙇‍♂️
Discordの通話でペアレビューとかも全然大丈夫なので、必要であればおっしゃっていただけるとありがたいです。
お手数をおかけしますが、可能であればよろしくおねがいいたします🙇‍♂️

@SuzukaHori
Copy link
Contributor

@junohm410
こんにちは、レビュー承知しました👍
明日から取り組みますが、vueの実装をやったことがないのでかなりお時間いただくかもしれません🙇‍♀️
わからない際には質問させていただきます。よろしくお願いします!

@junohm410 junohm410 self-assigned this Feb 28, 2024
Copy link
Contributor

@SuzukaHori SuzukaHori left a comment

Choose a reason for hiding this comment

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

お疲れさまです!
大変待たせいたしました🙇‍♀️

  • 質問更新・フラッシュの動作を確認しました。バッチリでした👍
    その他QA一覧・コメント・タグ・回答数の表示などで一連の操作を確認しましたが、正常に動作しているように見えました。
  • 全体的な設計やコードについて
    問題ないように見えました!
    丁寧にコメントをつけていただき、大変助かりました。ありがとうございます🙏

以下は感想です:

  • question-edit.vueの置き換えについて
    question/show内の構造について、日報ページなどと一致してDRYになっており、良いと思いました。
    question/editのフラッシュメッセージについても、適切に条件分岐されているとように見えました。
  • Answer部分を単独で動かすための修正について
    vueに詳しくないので自信がないのですが、3回読んでも特に問題はなさそうに見えました。
  • ファットコントローラー解消のためのリファクタリングについて
    コントローラー→モデルへの移行がいい感じで行われており、テストも追加されていて良いと思いました。

とても細かい部分で、2点ほどコメントしておりますのでご確認をお願いします!(うち一点は単純な疑問です🙇‍♀️)


.page-content-header__row
.page-content-header__tags
= react_component('Tags/Tags', { tagsInitialValue: question.tag_list.join(',').to_s, tagsParamName: 'question[tag_list]', tagsInputId: 'question_tag_list', tagsType: 'Question', tagsTypeId: question.id.to_s })
Copy link
Contributor

Choose a reason for hiding this comment

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

  • 1つ目のto_sが不要に思えたのですが、いかがでしょうか??
    joinの時点で文字列になっているように見えます👀
irb(main):018:0> Question.last.tag_list.join(',')
=>  "ruby,rail,Linux" 
irb(main):018:0> Question.last.tag_list.join(',').class
=> String   
  • 2つ目のto_sについても、idを文字列に変換しているのを不思議に感じたので、理由を伺いたいです🙇‍♀️

Copy link
Contributor Author

@junohm410 junohm410 Mar 2, 2024

Choose a reason for hiding this comment

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

ありがとうございます!こちらの引数の渡し方についてはpagesの実装を真似ていたところだったので、細かく見られていないところでした🙇‍♂️お恥ずかしい限りです。

Reactコンポーネントの部分を含めて、あらためて確認しました。

1つ目のto_sが不要に思えたのですが、いかがでしょうか??
joinの時点で文字列になっているように見えます👀

こちらはおっしゃる通りだと思います。
tagsInitialValueはその先のコンポーネントでも文字列であることを期待されていますが、Rubyのメソッドチェーンのjoinの時点で確実に文字列が返ってくるので、このto_sは不要です。

2つ目のto_sについても、idを文字列に変換しているのを不思議に感じたので、理由を伺いたいです🙇‍♀️

結論的には、こちらのto_sはあってもいいと考えました。

fetch(`/api/${tagsType.toLowerCase()}s/${tagsTypeId}`, {

ここのtagsTypeIdpropsは、Tags/Tagsコンポーネントの中で、APIのエンドポイントを指定するのに使われています。
ここでtagsTypeIdが数値型だった場合、暗黙の型変換による文字列結合が起こります。

数値型でもエラーにはならないですが、文字列のエンドポイントを生成する用途と決まっているのであれば、propsで渡すタイミングで確実にto_sで文字列にしておいてもいいのかなと考えました👀

なので、tagsInitialValueの方のto_sのみ外す、という修正がいいかなと考えていますが、いかがでしょうか?
その他の修正の確認含め、ご確認いただけるとありがたいです🙏

Copy link
Contributor

Choose a reason for hiding this comment

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

ご説明ありがとうございます!

tagsTypeIdに関して、コンポーネント内で文字列型以外で使う可能性がないのなら、こちらで変換を行なって良いと思いました👍
tagsInitialValueの方のto_sのみ外す方向で、修正をお願いします🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ご確認ありがとうございます🙏tagsInitialValueの方のto_sのみ外す修正を行いました。

within 'form[name=question]' do
fill_in 'question[title]', with: 'テストの質問(修正)'
fill_in 'question[description]', with: 'テストの質問です。(修正)'
find('.choices__inner').click
find('#choices--js-choices-single-select-item-choice-12', text: 'sshdでパスワード認証を禁止にする').click
find('#choices--js-choices-single-select-item-choice-11', text: 'sshdでパスワード認証を禁止にする').click
Copy link
Contributor

Choose a reason for hiding this comment

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

元の「12」だとテストが失敗&「11」にすると成功するのは確認したのですが、なぜこうなるのかわからなかったため、教えていただきたいです🙇‍♀️

test/fixtures/practices.ymlで、「sshdでパスワード認証を禁止にする」は12番目のデータに見えるので、不思議に思いました。(見る場所が違っていたらすみません🙇‍♀️)

Copy link
Contributor Author

@junohm410 junohm410 Mar 2, 2024

Choose a reason for hiding this comment

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

こちらの説明が足りていませんでした、すみませんでした🙇‍♂️

もともと11に修正していたのは、更新時のフォームから「プラクティス選択なし」という選択肢がなくなったからでした。
(既存の_formにその選択肢がなく、Vue上の編集画面にのみ独自でその選択肢が実装されていた)

先頭の要素'#choices--js-choices-single-select-item-choice-1'がこの「プラクティス選択なし」で、それが消えたので、12から11に順番が変わった、というイメージです。

ただ、そもそも「更新画面から『プラクティス選択なし』を消す」のではなく、「新規画面にも同じ選択肢を追加する」という方向がいいような気がしたので、machidaさんに提案の上、実装の方針を変更しました。
#7356 (comment)
PRの概要欄にも説明を足しております。

よってこの部分はもとの「12」に戻しています🙇‍♂️
代わりに他のテストがまた影響を受けているのですが、その部分は別途コメントを残しておきます。

test/fixtures/practices.ymlで、「sshdでパスワード認証を禁止にする」は12番目のデータに見えるので、不思議に思いました。(見る場所が違っていたらすみません🙇‍♀️)

こちらですが、プラクティスの並びは必ずしもtest/fixtures/practices.ymlの並びとは一致しない感じのようですね👀
kimuraの所属するコース「Railsプログラマー」には「Mac OS X」などのカテゴリがあり、コースの中のカテゴリの順番がまず決まっていて、その中で個別のプラクティスが並んでいく、のような構造になっています。

たとえばtest/fixtures/practices.ymlの7・8番目にある「vi」に関するプラクティスですが、これは「Vim」というカテゴリに所属し、このカテゴリは「Railsプログラマー」の中ではそこそこ後ろの方のため、「sshdでパスワード認証を禁止にする」よりも後に並びます。

なので、fixturesの上から12番目にあった「sshdでパスワード認証を禁止にする」がプラクティス並びでも12番目にあったのは、上記のようなことが積み重なった上でのたまたまということになりそうです😅

Copy link
Contributor

Choose a reason for hiding this comment

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

テストについて

ご説明ありがとうございます🙏🙏

kimuraの所属するコース「Railsプログラマー」には「Mac OS X」などのカテゴリがあり、コースの中のカテゴリの順番がまず決まっていて、その中で個別のプラクティスが並んでいく、のような構造になっています。

なるほど、並び順はカテゴリの順番の影響を受けていて、practices.ymlとは一致しないのですね😳
ご丁寧にありがとうございます、大変勉強になりました!!

「プラクティス選択なし」の追加について

修正ありがとうございます!
新規作成時・更新時ともに「プラクティス選択なし」を選べることを確認しました👍

Docsの「関連プラクティスなし」にはデザインが入っているように見えるので、一応町田さんに確認すると良いかもと思いました👀

スクリーンショット 2024-03-05 15 29 14

Copy link
Contributor Author

Choose a reason for hiding this comment

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

細かいところまでチェックいただきありがとうございます!

デザインの入り方の違いですが、使用するセレクトボックス拡張のライブラリのデフォルト状態をそのまま使うかどうか、という問題な気がしたので、そのような方向でmachidaさんに相談しました👍
#7439 (comment)

結論としては現状のままでOK(Docsの方で使うライブラリをQ&Aに合わせる)ということになりましたので、こちらへの修正は特に加えていません🙏

@junohm410 junohm410 force-pushed the chore/convert-question-section-from-vue-to-slim branch from 3310ec0 to 9b8fbe4 Compare March 2, 2024 04:30
Copy link
Contributor Author

@junohm410 junohm410 left a comment

Choose a reason for hiding this comment

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

1度目のレビュー後の修正部分の補足コメントです。

Comment on lines 17 to 23
@questions = Question
.by_target(params[:target])
.by_practice_id(params[:practice_id])
.by_tag(params[:tag])
.with_avatar
.includes(:practice, :answers, :tags, :correct_answer)
.order(updated_at: :desc, id: :desc)
.recent
Copy link
Contributor Author

@junohm410 junohm410 Mar 2, 2024

Choose a reason for hiding this comment

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

【QuestionsControllerのリファクタリング】
by_target以外にも、paramsから受け取るpractice_id・タグ名に基づく条件分岐を伴った検索や、直近の質問を並べるクエリメソッドをモデルのスコープに移しました。

また、既存のコードは@questionsをセットする処理の途中でタグの取得などを行なっていたりしましたが、このスコープへの切り出しにより、@questionsのセットを一つのメソッドチェーンにまとめられるようになりました。

移したモデルのスコープのテストも追加しております。

Comment on lines 24 to 27
when Question
"#{user.login_name}さんのQ&A「#{practice[:title]}」"
practice_title = practice ? practice[:title] : 'プラクティス選択なし'
"#{user.login_name}さんのQ&A「#{practice_title}」"
end
Copy link
Contributor Author

Choose a reason for hiding this comment

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

「プラクティスを選択しない状態かつ、他のユーザーにメンションをした状態でQ&Aを新規作成・更新するとエラーになる」現象への対応です。
#7356 (comment)

Comment on lines -11 to +14
= f.select(:practice_id, practice_options_within_course, { selected: params[:practice_id] }, { id: 'js-choices-single-select' })
- if question.new_record?
= f.select(:practice_id, practice_options_within_course, { selected: params[:practice_id], include_blank: 'プラクティス選択なし' }, { id: 'js-choices-single-select' })
- else
= f.select(:practice_id, practice_options_within_course, { include_blank: 'プラクティス選択なし' }, { id: 'js-choices-single-select' })
Copy link
Contributor Author

@junohm410 junohm410 Mar 2, 2024

Choose a reason for hiding this comment

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

question.new_record?で分岐しているのは、「プラクティスページの『質問する』ボタンから/questions/new?practice_id=...に飛んだ時に、自動でプラクティスが選択されている」という機能を維持するためです。

image

当該機能のテストは以下です。

test 'select practice title when push question button on practice page' do
visit_with_auth "/practices/#{practices(:practice23).id}", 'hatsuno'
click_on '質問する'
assert_text 'rubyをインストールする'
end

フォームに紐づくモデルオブジェクトが新規のものである場合は、pracrtice_idクエリパラメータがあればそれをもとにプラクティスがセットされ、そうでなければ「プラクティス選択なし」がセットされるようになっています。

更新時は上記のことをケアすることがないため、「プラクティス選択なし」が選択肢の中に用意されることのみ設定しています。
#7356 (comment)

Comment on lines -426 to +427
find('#choices--js-choices-single-select-item-choice-6', text: 'Linuxのファイル操作の基礎を覚える').click
find('#choices--js-choices-single-select-item-choice-7', text: 'Linuxのファイル操作の基礎を覚える').click
Copy link
Contributor Author

Choose a reason for hiding this comment

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

新規作成フォームのプラクティス選択部分の先頭に「プラクティス選択なし」を追加したことにより、既存のプラクティスの並びが一つ後ろにずれました。そのことによる修正です。

@junohm410 junohm410 force-pushed the chore/convert-question-section-from-vue-to-slim branch from 9b8fbe4 to fe9cf3e Compare March 2, 2024 07:51
@SuzukaHori SuzukaHori self-requested a review March 3, 2024 02:06
@junohm410
Copy link
Contributor Author

junohm410 commented Mar 3, 2024

@SuzukaHori
すみません、昨日先に色々とコメントしましたが、思ったよりCIがなかなか通らない状況です。
通り次第改めてご連絡しますので、それまでは見ていただかなくて大丈夫です(いくつか追加対応した部分を概要に書いていますので、そこだけ見てもらっているとあとでコードを見ていただく時にスムーズかもですが)🙏
レビューをセルフアサインいただいていたので、念のためご連絡です〜🙇‍♂️

@SuzukaHori
Copy link
Contributor

SuzukaHori commented Mar 3, 2024

@junohm410
ご連絡ありがとうございます🙏CIの件承知しました!
まだきちんとコードを見れていない状態なので、全く問題ありません😅(Fjord Choiceにレビュー状態がうまく反映されていなかったので、self-requestedしました)

ご連絡お待ちしております!

@junohm410
Copy link
Contributor Author

@SuzukaHori
CIが通りましたので、また明日以降にお手隙のタイミングでご確認いただけると幸いです🙏
ご不明点があればお気軽に聞いていただければと思います🙏よろしくお願いいたします🙇‍♂️

Copy link
Contributor

@SuzukaHori SuzukaHori left a comment

Choose a reason for hiding this comment

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

修正お疲れさまです、お待たせしました!

再度コードと動作を確認し、「プラクティス選択なし」が選択できること、「プラクティスを選択しない状態かつメンション時」にバグが発生しないことを確認しました👍

3箇所コメントを追加しましたので、お手隙の際にご確認ください🙇‍♀️
今回も丁寧にコメントをつけていただき、とても助かりました😭🙏

@@ -31,7 +31,7 @@ def show
.where(practice: @question.practice)
.where.not(id: @question.id)
.includes(:correct_answer)
.order(updated_at: :desc, id: :desc)
.recent
Copy link
Contributor

Choose a reason for hiding this comment

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

recentの名前を見た時、このサイトのような「最新○件のデータを持ってくる」メソッドを想像しました。

リファクタリングに影響がないのならコントローラーにそのまま書くか、メソッド名を具体的に修正するといいかなと思ったのですが、いかがでしょうか??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

おっしゃる通りで、recentだと検索&取得のスコープと勘違いしますね😅勉強になります。
取得ではなく並び替えのスコープであることと、更新順であることの2点を具体化させるべきだと思いましたので、latest_update_orderに変えました。
(indexとshowで全く同じ並び替えをしているので、DRY原則に則ってメソッドに切り出す方針はそのままにしました)


.page-content-header__row
.page-content-header__tags
= react_component('Tags/Tags', { tagsInitialValue: question.tag_list.join(',').to_s, tagsParamName: 'question[tag_list]', tagsInputId: 'question_tag_list', tagsType: 'Question', tagsTypeId: question.id.to_s })
Copy link
Contributor

Choose a reason for hiding this comment

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

ご説明ありがとうございます!

tagsTypeIdに関して、コンポーネント内で文字列型以外で使う可能性がないのなら、こちらで変換を行なって良いと思いました👍
tagsInitialValueの方のto_sのみ外す方向で、修正をお願いします🙏

within 'form[name=question]' do
fill_in 'question[title]', with: 'テストの質問(修正)'
fill_in 'question[description]', with: 'テストの質問です。(修正)'
find('.choices__inner').click
find('#choices--js-choices-single-select-item-choice-12', text: 'sshdでパスワード認証を禁止にする').click
find('#choices--js-choices-single-select-item-choice-11', text: 'sshdでパスワード認証を禁止にする').click
Copy link
Contributor

Choose a reason for hiding this comment

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

テストについて

ご説明ありがとうございます🙏🙏

kimuraの所属するコース「Railsプログラマー」には「Mac OS X」などのカテゴリがあり、コースの中のカテゴリの順番がまず決まっていて、その中で個別のプラクティスが並んでいく、のような構造になっています。

なるほど、並び順はカテゴリの順番の影響を受けていて、practices.ymlとは一致しないのですね😳
ご丁寧にありがとうございます、大変勉強になりました!!

「プラクティス選択なし」の追加について

修正ありがとうございます!
新規作成時・更新時ともに「プラクティス選択なし」を選べることを確認しました👍

Docsの「関連プラクティスなし」にはデザインが入っているように見えるので、一応町田さんに確認すると良いかもと思いました👀

スクリーンショット 2024-03-05 15 29 14

@junohm410
Copy link
Contributor Author

junohm410 commented Mar 5, 2024

@machida
お疲れ様です。
質問作成において「プラクティス選択なし」を選択できるようにする件で、一点ご相談させてください。

レビューでご指摘いただいた問題

「選択なし」の選択肢のスタイルが、DocsとQ&Aで異なる。
#7439 (comment)

詳細

現在同様の機能を持っているDocsでは、「関連プラクティスを指定しない」の選択肢にスタイルが当たっています。

  • テキストカラーが他の選択肢と同様の黒
  • 現在選ばれている場合、別の選択肢を選んでいる間も灰色でハイライトされている

image

今回実装したQ&Aでの「プラクティス選択なし」では、以下のようなスタイルになっています。

  • テキストカラーはうっすら灰色
  • 現在選ばれている場合でも、別の選択肢に写ろうとするとハイライトされなくなる(なおこれは「プラクティス選択なし」以外の他の選択肢でも同じで、現在の本番環境でもそのような挙動を取る)
    image

DocsとQ&Aで、「選択なし」状態を実装するロジックは同じです。
Railsのformヘルパーで、include_blankというオプションを設定することで、無選択の場合の選択肢とそのラベルを用意しています。

# Docs
= f.select(:practice_id, practice_options(categories), { include_blank: '関連プラクティスを指定しない' }, { class: 'js-select2' })

# Q&A
= f.select(:practice_id, practice_options_within_course, { include_blank: 'プラクティス選択なし' }, { id: 'js-choices-single-select' })

原因

DocsとQ&Aで異なるのは、セレクトボックスを拡張するライブラリの違いです。
Docsはselect2、Q&AはChoices-jsを使用しています。

Q&Aはもともとselect2で実装されていたましたが、下記のPRでChoices-jsに置き換えられたようです。

試しにQ&Aの方でもselect2で実装したところ、スタイルの当たり方はDocと全く同じなりました。
Docsのようにスタイルがあたるのは、select2Choices-jsでデフォルトで用意されるクラスにあたるスタイルの差だと思われます。
Choices-jsで無選択の場合の選択肢を用意するのが今回初めてなので、まだスタイルが用意されてない?)

対応について、私としての意見

対応ですが、私個人として今の状態でもわかりやすいかな?とは思っています。
(またDocsに合わせるべきというよりは、Docsだけが古いライブラリを使っていることがそもそもの問題なような気もします)

個別にスタイルを当てることも可能だとは思いますので、まずは上記をご確認いただき、もし「プラクティス選択なし」にスタイルを当てる必要があれば、そのご対応をお願いしたく思います🙏

ご確認のほど、何卒よろしくお願いいたします🙏

@machida
Copy link
Member

machida commented Mar 5, 2024

@junohm410 なるほどー、今回はchoices.jsの見た目のままでお願いします🙏select2をchoices.jsに移行するというのも別Issueで対応したいと思います。こちらのIssue作成をお願いしてもいいでしょうか?

junohm410 added 15 commits March 6, 2024 09:58
@junohm410
Copy link
Contributor Author

@machida
早々にご確認いただき、ありがとうございました🙇‍♂️
それでは、本PRではChoices.jsのデフォルトのままでいきたいと思います。

select2をchoices.jsに移行するというのも別Issueで対応したいと思います。こちらのIssue作成をお願いしてもいいでしょうか?

Issueを作成しておきました👍

@junohm410
Copy link
Contributor Author

@SuzukaHori
お疲れ様です。レビューいただきありがとうございました🙏
修正・コメント返信させていただきましたので、改めてご確認のほど、よろしくお願いいたします🙏

@junohm410 junohm410 requested a review from SuzukaHori March 6, 2024 10:05
Copy link
Contributor

@SuzukaHori SuzukaHori left a comment

Choose a reason for hiding this comment

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

お疲れさまです!

修正内容確認しました😊 Issueの作成含め、ありがとうございました🙏
問題ないと思いますので、私からはApproveさせていただきます✨

レビュー中色々と教えていただき、ありがとうございました!shodanさんの調査方法や、コミュニケーションの取り方など、大変勉強になりました🙇‍♀️🙇‍♀️

@junohm410
Copy link
Contributor Author

@SuzukaHori
ご確認いただき、ありがとうございました🙏
こちらこそ、細かいところまでチェックいただき大変勉強になりました🙇‍♂️感謝です。

@komagata
チームメンバーからApproveいただきましたので、レビューのほどよろしくお願いいたします🙏

@junohm410 junohm410 requested a review from komagata March 6, 2024 11:48
Copy link
Member

@komagata komagata left a comment

Choose a reason for hiding this comment

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

確認させて頂きました。OKです〜🙆‍♂️

@komagata komagata merged commit ac2b8a2 into main Mar 9, 2024
8 checks passed
@komagata komagata deleted the chore/convert-question-section-from-vue-to-slim branch March 9, 2024 17:06
@github-actions github-actions bot mentioned this pull request Mar 9, 2024
29 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants