Skip to content

Commit

Permalink
fix[Op#40437]: Update reactions text
Browse files Browse the repository at this point in the history
 * ARIA-label, show names and emoji type: "{Name of reaction} by {user A}, {user B} and {user C}".
 * Visually, show just names: "{user A}, {user B} and {user C}"
  • Loading branch information
akabiru committed Oct 22, 2024
1 parent b29c277 commit 5f57d6f
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
<%=
component_wrapper do
if journal.detailed_grouped_emoji_reactions.any?
flex_layout(test_selector: 'emoji-reactions') do |reactions_container|
journal.detailed_grouped_emoji_reactions.each do |emoji, data|
reactions_container.with_column(mr: 2) do
render(Primer::Beta::Button.new(
scheme: button_scheme(data[:users]),
color: :default,
bg: counter_color(data[:users]),
id: "#{journal.id}-#{data[:reaction]}",
test_selector: "reaction-#{data[:reaction]}",
tag: :a,
href: href(reaction: data[:reaction]),
data: { turbo_stream: true, turbo_method: :put },
disabled: current_user_cannot_react?,
classes: 'op-reactions-button'
)) do |button|
button.with_tooltip(text: tooltip_text(data[:reaction], data[:users]))
"#{emoji} #{data[:count]}"
flex_layout(test_selector: "emoji-reactions") do |reactions_container|
journal.detailed_grouped_emoji_reactions.each do |emoji, data|
reactions_container.with_column(mr: 2) do
render(Primer::Beta::Button.new(
scheme: button_scheme(data[:users]),
color: :default,
bg: counter_color(data[:users]),
id: "#{journal.id}-#{data[:reaction]}",
test_selector: "reaction-#{data[:reaction]}",
tag: :a,
href: href(reaction: data[:reaction]),
data: { turbo_stream: true, turbo_method: :put },
aria: { label: aria_label_text(data[:reaction], data[:users]) },
disabled: current_user_cannot_react?,
classes: "op-reactions-button"
)) do |button|
button.with_tooltip(text: number_of_user_reactions_text(data[:users]),
test_selector: "reaction-tooltip-#{data[:reaction]}") do
button.with_icon(emoji, size: :small)
end
"#{emoji} #{data[:count]}"
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ module WorkPackages
module ActivitiesTab
module Journals
class ItemComponent::Reactions < ApplicationComponent
MAX_DISPLAYED_USERS_COUNT = 5

include ApplicationHelper
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable
Expand All @@ -40,20 +42,17 @@ def initialize(journal:)
@journal = journal
end

def render?
journal.detailed_grouped_emoji_reactions.any?
end

private

attr_reader :journal

def wrapper_uniq_by
journal.id
end

def wrapper_uniq_by = journal.id
def work_package = journal.journable

def reacted_by_current_user?(users)
users.any? { |u| u[:id] == User.current.id }
end

def counter_color(users)
reacted_by_current_user?(users) ? :accent : nil
end
Expand All @@ -62,25 +61,30 @@ def button_scheme(users)
reacted_by_current_user?(users) ? :default : :invisible
end

def tooltip_text(reaction, users)
max_displayed_users_count = 5
def reacted_by_current_user?(users)
users.any? { |u| u[:id] == User.current.id }
end

# ARIA-label, show names and emoji type: "{Name of reaction} by {user A}, {user B} and {user C}".
def aria_label_text(reaction, users)
"#{I18n.t('reactions.reaction_by', reaction: reaction.tr('_', ' '))} #{number_of_user_reactions_text(users)}"
end

# Visually, show just names: "{user A}, {user B} and {user C}"
def number_of_user_reactions_text(users, max_displayed_users_count: 5)
user_count = users.length
displayed_users = users.take(max_displayed_users_count).pluck(:name)

result = if user_count <= max_displayed_users_count
displayed_users.join(", ")
elsif user_count == max_displayed_users_count + 1
"#{displayed_users.join(', ')} #{I18n.t('reactions.and_n_others_singular', n: 1)}"
else
"#{displayed_users.join(', ')} #{I18n.t('reactions.and_n_others_plural',
n: user_count - max_displayed_users_count)}"
end

result += " "
return displayed_users.first if user_count == 1

result += I18n.t("reactions.reacted_with", reaction: reaction.tr("_", " "))
result
if user_count <= max_displayed_users_count
"#{displayed_users[0..-2].join(', ')} #{I18n.t('reactions.and_user', user: displayed_users.last)}"
elsif user_count == max_displayed_users_count + 1
"#{displayed_users.join(', ')} #{I18n.t('reactions.and_n_others_singular', n: 1)}"
else
"#{displayed_users.join(', ')} #{I18n.t('reactions.and_n_others_plural',
n: user_count - max_displayed_users_count)}"
end
end

def href(reaction:)
Expand Down
3 changes: 2 additions & 1 deletion config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -550,9 +550,10 @@ en:
action_title: "React"
add_reaction: "Add reaction"
react_with: "React with %{reaction}"
and_user: "and %{user}"
and_n_others_singular: "and %{n} other"
and_n_others_plural: "and %{n} others"
reacted_with: "reacted with %{reaction}"
reaction_by: "%{reaction} by"

reportings:
index:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
#
require "spec_helper"

RSpec.describe WorkPackages::ActivitiesTab::Journals::ItemComponent::Reactions, type: :component do
let(:journal) { build_stubbed(:work_package_journal) }

context "with reactions" do
before do
allow(journal).to receive(:detailed_grouped_emoji_reactions).and_return(mock_detailed_grouped_emoji_reactions)
end

it "renders the reactions" do
render_inline(described_class.new(journal:))

{
thumbs_up: {
count: 3, tooltip_text: "Bob Bobbit, Bob Bobbit and Bob Bobbit",
aria_label: "thumbs up by Bob Bobbit, Bob Bobbit and Bob Bobbit"
},
thumbs_down: {
count: 1, tooltip_text: "Bob Bobbit", aria_label: "thumbs down by Bob Bobbit"
},
eyes: {
count: 20, tooltip_text: "Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit and 15 others",
aria_label: "eyes by Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit and 15 others"
},
rocket: {
count: 5, tooltip_text: "Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit and Bob Bobbit",
aria_label: "rocket by Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit and Bob Bobbit"
},
confused_face: {
count: 6,
tooltip_text: "Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit and 1 other",
aria_label: "confused face by Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit, Bob Bobbit and 1 other"
}
}.each { |reaction, details| expect_emoji_reaction(reaction:, **details) }
end
end

context "with no reactions" do
before do
allow(journal).to receive(:detailed_grouped_emoji_reactions).and_return([])
end

it "does not render" do
render_inline(described_class.new(journal:))

expect(page.text).to be_empty
end
end

def expect_emoji_reaction(reaction:, count:, tooltip_text:, aria_label:)
expect(page).to have_test_selector("reaction-#{reaction}", text: "#{EmojiReaction.emoji(reaction)} #{count}",
aria: { label: aria_label })
expect(page).to have_test_selector("reaction-tooltip-#{reaction}", text: tooltip_text)
end

def mock_detailed_grouped_emoji_reactions
users = build_stubbed_list(:user, 20).map { |user| { id: user.id, name: user.name } }

{
EmojiReaction.emoji(:thumbs_up) => { reaction: "thumbs_up", users: users.sample(3), count: 3 },
EmojiReaction.emoji(:thumbs_down) => { reaction: "thumbs_down", users: users.sample(1), count: 1 },
EmojiReaction.emoji(:eyes) => { reaction: "eyes", users: users, count: 20 },
EmojiReaction.emoji(:rocket) => { reaction: "rocket", users: users.sample(5), count: 5 },
EmojiReaction.emoji(:confused_face) => { reaction: "confused_face", users: users.sample(6), count: 6 }
}
end
end

0 comments on commit 5f57d6f

Please sign in to comment.