diff --git a/app/components/open_project/common/work_package_card_box_component.rb b/app/components/open_project/common/work_package_card_box_component.rb
index 6183b08c373..244443d25b2 100644
--- a/app/components/open_project/common/work_package_card_box_component.rb
+++ b/app/components/open_project/common/work_package_card_box_component.rb
@@ -92,11 +92,14 @@ module OpenProject
# # `#card` returning a renderable object.
# # @param item_menu_src [String, NilClass] optional menu source for the
# # item's `WorkPackageCardComponent`.
+ # # @param item_metric [Object, NilClass] optional metric content for the
+ # # item's `WorkPackageCardComponent`.
# # @param system_arguments [Hash] forwarded to the item class.
# def with_work_package_item(
# work_package:,
# component_klass: Item,
# item_menu_src: nil,
+ # item_metric: nil,
# **system_arguments,
# &block
# )
@@ -114,8 +117,10 @@ module OpenProject
# end
renders_many :items, types: {
work_package_item: {
- renders: lambda { |work_package:, **system_arguments|
- build_item(work_package:, **system_arguments)
+ renders: lambda { |work_package:, **system_arguments, &block|
+ build_item(work_package:, **system_arguments).tap do |item|
+ capture(item, &block) if block
+ end
},
as: :work_package_item
},
@@ -137,6 +142,7 @@ module OpenProject
:container,
:drag_and_drop,
:item_menu_src,
+ :item_metric,
:params,
:current_user
@@ -154,6 +160,9 @@ module OpenProject
# automatically built items. Procs receive the work package. When set,
# callers are responsible for including any URL params they want in the
# returned source.
+ # @param item_metric [Proc, NilClass] optional metric content for
+ # automatically built items. Procs receive the work package and return
+ # renderable content.
# @param params [Hash] optional URL params passed to work package items
# when deriving row and menu URLs.
# @param current_user [User] passed through to each item for permission
@@ -166,6 +175,7 @@ module OpenProject
work_packages: [],
drag_and_drop: nil,
item_menu_src: nil,
+ item_metric: nil,
params: {},
current_user: User.current,
**system_arguments
@@ -177,6 +187,7 @@ module OpenProject
@container = container
@drag_and_drop = drag_and_drop
@item_menu_src = item_menu_src
+ @item_metric = item_metric
@params = params
@current_user = current_user
@automatic_items = false
@@ -193,6 +204,7 @@ module OpenProject
# so slot calls have already populated `items`.
content
validate_item_menu_src!
+ validate_item_metric!
validate_item_mode!
build_automatic_items if build_automatic_items?
validate_empty_state!
@@ -215,11 +227,13 @@ module OpenProject
# @param item_menu_src [String, NilClass] optional item menu source
# override. When set, callers are responsible for including any URL
# params they want in the source.
+ # @param item_metric [Object, NilClass] optional item metric content.
# @param system_arguments [Hash] forwarded to the item class.
def build_item(
work_package:,
component_klass: Item,
item_menu_src: item_menu_src_for(work_package),
+ item_metric: item_metric_for(work_package),
**system_arguments
)
component_klass.new(
@@ -228,6 +242,7 @@ module OpenProject
container:,
params:,
item_menu_src:,
+ item_metric:,
current_user:,
**system_arguments
)
@@ -265,12 +280,25 @@ module OpenProject
end
end
+ def item_metric_for(work_package)
+ return unless item_metric
+
+ metric = item_metric.call(work_package)
+ metric.respond_to?(:render_in) ? render(metric) : metric
+ end
+
def validate_item_menu_src!
return if item_menu_src.nil? || item_menu_src.is_a?(Proc) || item_menu_src.is_a?(String)
raise ArgumentError, "item_menu_src must be a Proc, String, or nil"
end
+ def validate_item_metric!
+ return if item_metric.nil? || item_metric.is_a?(Proc)
+
+ raise ArgumentError, "item_metric must be a Proc or nil"
+ end
+
def validate_item_mode!
return unless empty_items.any?
diff --git a/app/components/open_project/common/work_package_card_box_component/item.rb b/app/components/open_project/common/work_package_card_box_component/item.rb
index f177257f3ad..b028f449dd3 100644
--- a/app/components/open_project/common/work_package_card_box_component/item.rb
+++ b/app/components/open_project/common/work_package_card_box_component/item.rb
@@ -43,15 +43,19 @@ module OpenProject
:project,
:container,
:item_menu_src,
+ :item_metric,
:params,
:current_user
+ delegate :with_metric, to: :card
+
def initialize(
work_package:,
project:,
container:,
params: {},
item_menu_src: nil,
+ item_metric: nil,
current_user: User.current,
**system_arguments
)
@@ -64,6 +68,7 @@ module OpenProject
@container = container
@params = params
@item_menu_src = item_menu_src
+ @item_metric = item_metric
@current_user = current_user
@system_arguments = system_arguments
end
@@ -81,7 +86,9 @@ module OpenProject
end
def card
- @card ||= WorkPackageCardComponent.new(work_package:, menu_src: item_menu_src)
+ @card ||= WorkPackageCardComponent.new(work_package:, menu_src: item_menu_src).tap do |card|
+ card.with_metric_content(item_metric) if item_metric
+ end
end
def render? = false
diff --git a/app/components/open_project/common/work_package_card_component.html.erb b/app/components/open_project/common/work_package_card_component.html.erb
index fc374aa78f3..cb2bcf0b5ba 100644
--- a/app/components/open_project/common/work_package_card_component.html.erb
+++ b/app/components/open_project/common/work_package_card_component.html.erb
@@ -27,15 +27,14 @@ See COPYRIGHT and LICENSE files for more details.
++# %>
-<%= grid_layout("op-work-package-card", tag: :article) do |grid| %>
+<%= grid_layout(card_classes, tag: :article) do |grid| %>
<% grid.with_area(:info_line) do %>
<%= render(WorkPackages::InfoLineComponent.new(work_package:)) %>
<% end %>
- <% grid.with_area(:points) do %>
- <%= render(Primer::Beta::Text.new(color: :subtle)) do %>
- <%= story_points %>
- <%= t(:"backlogs.points_label", count: story_points) %>
+ <% if metric? %>
+ <% grid.with_area(:metric) do %>
+ <%= metric %>
<% end %>
<% end %>
diff --git a/app/components/open_project/common/work_package_card_component.rb b/app/components/open_project/common/work_package_card_component.rb
index eef7e67c720..bcbea5c8e56 100644
--- a/app/components/open_project/common/work_package_card_component.rb
+++ b/app/components/open_project/common/work_package_card_component.rb
@@ -31,8 +31,11 @@
module OpenProject
module Common
class WorkPackageCardComponent < ApplicationComponent
+ include Primer::ClassNameHelper
include OpPrimer::ComponentHelpers
+ renders_one :metric, Primer::Content
+
attr_reader :work_package, :menu_src
# @param work_package [WorkPackage] the work package this card represents.
@@ -44,10 +47,11 @@ module OpenProject
@menu_src = menu_src
end
- private
-
- def story_points
- work_package.story_points || 0
+ def card_classes
+ class_names(
+ "op-work-package-card",
+ "op-work-package-card_with-metric": metric?
+ )
end
end
end
diff --git a/app/components/open_project/common/work_package_card_component.sass b/app/components/open_project/common/work_package_card_component.sass
index 79624a91b3a..6a1a4a72d15 100644
--- a/app/components/open_project/common/work_package_card_component.sass
+++ b/app/components/open_project/common/work_package_card_component.sass
@@ -28,14 +28,18 @@
.op-work-package-card
display: grid
- grid-template-columns: 1fr minmax(5rem, max-content) auto
+ grid-template-columns: 1fr auto
grid-template-rows: auto auto
- grid-template-areas: "info_line points menu" "subject subject subject"
+ grid-template-areas: "info_line menu" "subject subject"
align-items: center
margin-top: calc(-1 * var(--base-size-4))
margin-bottom: var(--base-size-4)
-.op-work-package-card--points
+.op-work-package-card_with-metric
+ grid-template-columns: 1fr minmax(5rem, max-content) auto
+ grid-template-areas: "info_line metric menu" "subject subject subject"
+
+.op-work-package-card--metric
margin-left: var(--stack-gap-normal)
font-variant-numeric: tabular-nums
@@ -51,8 +55,8 @@
// `backlogsListsContainer` named container is established on
// `.op-backlogs-page`; outside of it these rules are inert.
@container backlogsListsContainer (max-width: 654px)
- .op-work-package-card-points-label
+ .op-work-package-card-metric-label
display: none
- .op-work-package-card
+ .op-work-package-card_with-metric
grid-template-columns: 1fr minmax(2rem, max-content) auto
diff --git a/lookbook/previews/open_project/common/work_package_card_box_component_preview.rb b/lookbook/previews/open_project/common/work_package_card_box_component_preview.rb
index ab7ad4e1fff..d32a65f2d0b 100644
--- a/lookbook/previews/open_project/common/work_package_card_box_component_preview.rb
+++ b/lookbook/previews/open_project/common/work_package_card_box_component_preview.rb
@@ -41,7 +41,9 @@ module OpenProject
work_packages = sprint.work_packages_for(project).limit(3)
render OpenProject::Common::WorkPackageCardBoxComponent.new(
- work_packages:, project:, container: sprint
+ work_packages:,
+ project:,
+ container: sprint
) do |box|
box.with_header(title: sprint.name, count: work_packages.size) do |header|
points = work_packages.sum { |w| w.story_points || 0 }
diff --git a/lookbook/previews/open_project/common/work_package_card_component_preview.rb b/lookbook/previews/open_project/common/work_package_card_component_preview.rb
index 17ab09b5727..023d64a9667 100644
--- a/lookbook/previews/open_project/common/work_package_card_component_preview.rb
+++ b/lookbook/previews/open_project/common/work_package_card_component_preview.rb
@@ -41,6 +41,19 @@ module OpenProject
)
end
+ def with_metric
+ work_package = WorkPackage.first
+ return preview_message("No work packages in the database.") unless work_package
+
+ render OpenProject::Common::WorkPackageCardComponent.new(
+ work_package:
+ ) do |card|
+ card.with_metric do
+ render Backlogs::StoryPointsComponent.new(work_package:)
+ end
+ end
+ end
+
private
def preview_message(text)
diff --git a/modules/backlogs/app/components/backlogs/bucket_component.html.erb b/modules/backlogs/app/components/backlogs/bucket_component.html.erb
index b29e029c8fb..c8c0f26870d 100644
--- a/modules/backlogs/app/components/backlogs/bucket_component.html.erb
+++ b/modules/backlogs/app/components/backlogs/bucket_component.html.erb
@@ -44,6 +44,7 @@ See COPYRIGHT and LICENSE files for more details.
all_backlogs_params
)
end,
+ item_metric: ->(work_package) { Backlogs::StoryPointsComponent.new(work_package:) },
params: all_backlogs_params,
current_user:,
data: { test_selector: "backlog-bucket-#{backlog_bucket.id}" }
diff --git a/modules/backlogs/app/components/backlogs/inbox_component.html.erb b/modules/backlogs/app/components/backlogs/inbox_component.html.erb
index b7123131212..733fce566ad 100644
--- a/modules/backlogs/app/components/backlogs/inbox_component.html.erb
+++ b/modules/backlogs/app/components/backlogs/inbox_component.html.erb
@@ -44,6 +44,7 @@ See COPYRIGHT and LICENSE files for more details.
all_backlogs_params
)
end,
+ item_metric: ->(work_package) { Backlogs::StoryPointsComponent.new(work_package:) },
params: all_backlogs_params,
current_user:,
data: { test_selector: "backlog-inbox" }
diff --git a/modules/backlogs/app/components/backlogs/sprint_component.html.erb b/modules/backlogs/app/components/backlogs/sprint_component.html.erb
index bb95943d9b9..7ea388165d3 100644
--- a/modules/backlogs/app/components/backlogs/sprint_component.html.erb
+++ b/modules/backlogs/app/components/backlogs/sprint_component.html.erb
@@ -45,6 +45,7 @@ See COPYRIGHT and LICENSE files for more details.
all_backlogs_params
)
end,
+ item_metric: ->(work_package) { Backlogs::StoryPointsComponent.new(work_package:) },
params: all_backlogs_params,
current_user:,
data: { test_selector: "sprint-#{sprint.id}" }
diff --git a/modules/backlogs/app/components/backlogs/story_points_component.html.erb b/modules/backlogs/app/components/backlogs/story_points_component.html.erb
new file mode 100644
index 00000000000..842c8ab06e2
--- /dev/null
+++ b/modules/backlogs/app/components/backlogs/story_points_component.html.erb
@@ -0,0 +1,33 @@
+<%# -- 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.
+
+++%>
+
+<%= render(Primer::Beta::Text.new(color: :subtle)) do %>
+ <%= story_points %>
+ <%= t(:"backlogs.points_label", count: story_points) %>
+<% end %>
diff --git a/modules/backlogs/app/components/backlogs/story_points_component.rb b/modules/backlogs/app/components/backlogs/story_points_component.rb
new file mode 100644
index 00000000000..cdf919691c4
--- /dev/null
+++ b/modules/backlogs/app/components/backlogs/story_points_component.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+#-- 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.
+#++
+
+module Backlogs
+ class StoryPointsComponent < ApplicationComponent
+ attr_reader :work_package
+
+ def initialize(work_package:)
+ super()
+
+ @work_package = work_package
+ end
+
+ private
+
+ def story_points
+ work_package.story_points || 0
+ end
+ end
+end
diff --git a/modules/backlogs/spec/components/backlogs/bucket_component_spec.rb b/modules/backlogs/spec/components/backlogs/bucket_component_spec.rb
index 70cc1a8c35d..273e4007a8f 100644
--- a/modules/backlogs/spec/components/backlogs/bucket_component_spec.rb
+++ b/modules/backlogs/spec/components/backlogs/bucket_component_spec.rb
@@ -112,6 +112,10 @@ RSpec.describe Backlogs::BucketComponent, type: :component do
expect(rendered_component).to have_text("##{work_package.id}")
end
+ it "renders story points on the work package card" do
+ expect(rendered_component).to have_text("3 points", normalize_ws: true)
+ end
+
it "wires the bucket drop-target data on the box" do
expect(rendered_component).to have_css(".Box") do |box|
expect(box["data-generic-drag-and-drop-target"]).to eq("container mirrorContainer")
diff --git a/modules/backlogs/spec/components/backlogs/inbox_component_spec.rb b/modules/backlogs/spec/components/backlogs/inbox_component_spec.rb
index 45e28a3e731..2d8d8196d1e 100644
--- a/modules/backlogs/spec/components/backlogs/inbox_component_spec.rb
+++ b/modules/backlogs/spec/components/backlogs/inbox_component_spec.rb
@@ -83,8 +83,8 @@ RSpec.describe Backlogs::InboxComponent, type: :component do
describe "with work packages" do
let(:work_packages) do
[
- create(:work_package, subject: "First item", project:, position: 1),
- create(:work_package, subject: "Second item", project:, position: 2)
+ create(:work_package, subject: "First item", project:, story_points: 2, position: 1),
+ create(:work_package, subject: "Second item", project:, story_points: 4, position: 2)
]
end
@@ -98,6 +98,11 @@ RSpec.describe Backlogs::InboxComponent, type: :component do
# does not show the blankslate
expect(page).to have_no_css("h4", text: "Backlog inbox is empty")
end
+
+ it "renders story points on each work package card" do
+ expect(page).to have_text("2 points", normalize_ws: true)
+ expect(page).to have_text("4 points", normalize_ws: true)
+ end
end
describe "pagination" do
diff --git a/modules/backlogs/spec/components/backlogs/sprint_component_spec.rb b/modules/backlogs/spec/components/backlogs/sprint_component_spec.rb
index 94d1cab83c2..424af198c0d 100644
--- a/modules/backlogs/spec/components/backlogs/sprint_component_spec.rb
+++ b/modules/backlogs/spec/components/backlogs/sprint_component_spec.rb
@@ -79,6 +79,11 @@ RSpec.describe Backlogs::SprintComponent, type: :component do
expect(rendered_component).to have_text("8 points", normalize_ws: true)
end
+ it "renders story points on each work package card" do
+ expect(rendered_component).to have_text("5 points", normalize_ws: true)
+ expect(rendered_component).to have_text("3 points", normalize_ws: true)
+ end
+
it "renders one Box-row per work package" do
expect(rendered_component).to have_css(".Box-row", count: 2)
expect(rendered_component).to have_text(work_package1.subject)
diff --git a/modules/backlogs/spec/components/backlogs/story_points_component_spec.rb b/modules/backlogs/spec/components/backlogs/story_points_component_spec.rb
new file mode 100644
index 00000000000..cd6676601df
--- /dev/null
+++ b/modules/backlogs/spec/components/backlogs/story_points_component_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+#-- 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 "rails_helper"
+
+RSpec.describe Backlogs::StoryPointsComponent, type: :component do
+ shared_let(:project) { create(:project) }
+
+ it "renders the work package story points" do
+ work_package = create(:work_package, project:, story_points: 5)
+
+ render_inline(described_class.new(work_package:))
+
+ expect(page).to have_text("5 points", normalize_ws: true)
+ end
+
+ it "renders zero when story points are unset" do
+ work_package = create(:work_package, project:, story_points: nil)
+
+ render_inline(described_class.new(work_package:))
+
+ expect(page).to have_text("0 points", normalize_ws: true)
+ end
+end
diff --git a/spec/components/open_project/common/work_package_card_box_component/item_spec.rb b/spec/components/open_project/common/work_package_card_box_component/item_spec.rb
index 5c19fefec9a..6488a501b35 100644
--- a/spec/components/open_project/common/work_package_card_box_component/item_spec.rb
+++ b/spec/components/open_project/common/work_package_card_box_component/item_spec.rb
@@ -181,6 +181,12 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent::Item, type: :co
expect(item.card).to equal(item.card)
end
+ it "forwards metric content to the visual card" do
+ item.with_metric { "Forwarded metric" }
+
+ expect(rendered_card).to have_text("Forwarded metric")
+ end
+
context "with a provided menu source" do
let(:item) do
described_class.new(
diff --git a/spec/components/open_project/common/work_package_card_box_component_spec.rb b/spec/components/open_project/common/work_package_card_box_component_spec.rb
index fbc35655124..2720acb07da 100644
--- a/spec/components/open_project/common/work_package_card_box_component_spec.rb
+++ b/spec/components/open_project/common/work_package_card_box_component_spec.rb
@@ -47,6 +47,7 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent, type: :componen
let(:container) { sprint }
let(:drag_and_drop) { nil }
let(:item_menu_src) { nil }
+ let(:item_metric) { nil }
let(:params) { {} }
let(:work_packages) { [] }
let(:system_arguments) { {} }
@@ -64,6 +65,7 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent, type: :componen
container:,
drag_and_drop:,
item_menu_src:,
+ item_metric:,
params:,
current_user: user,
**system_arguments
@@ -306,6 +308,23 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent, type: :componen
expect { rendered_component }.to raise_error(ArgumentError, /item_menu_src/)
end
end
+
+ context "with an item_metric proc" do
+ let(:item_metric) { ->(work_package) { "metric #{work_package.id}" } }
+
+ it "uses the derived metric for automatically built items" do
+ expect(rendered_component).to have_text("metric #{work_packages.first.id}")
+ expect(rendered_component).to have_text("metric #{work_packages.second.id}")
+ end
+ end
+
+ context "with an invalid item_metric" do
+ let(:item_metric) { "static metric" }
+
+ it "raises ArgumentError" do
+ expect { rendered_component }.to raise_error(ArgumentError, /item_metric/)
+ end
+ end
end
describe ":work_package_item slot" do
@@ -329,6 +348,7 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent, type: :componen
container:,
params:,
item_menu_src: nil,
+ item_metric: nil,
current_user: User.current,
**system_arguments
)
@@ -336,6 +356,7 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent, type: :componen
@work_package = work_package
@item_menu_src = item_menu_src
+ @item_metric = item_metric
@params = params
@context = [project, container, current_user]
@system_arguments = system_arguments
@@ -347,6 +368,7 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent, type: :componen
context_size: @context.size
)
data[:item_menu_src] = @item_menu_src if @item_menu_src
+ data[:item_metric] = @item_metric if @item_metric
@system_arguments.merge(
id: "custom_work_package_#{@work_package.id}",
@@ -425,6 +447,19 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent, type: :componen
expect(rendered).to have_element("include-fragment", src: "/manual-menu")
end
+ it "uses caller-provided metric content for manual work package items" do
+ rendered = render_inline(
+ described_class.new(project:, container:, params:, current_user: user)
+ ) do |box|
+ box.with_empty_state(title: "empty", description: "drag here")
+ box.with_work_package_item(work_package: slot_work_package) do |item|
+ item.with_metric { "manual metric" }
+ end
+ end
+
+ expect(rendered).to have_text("manual metric")
+ end
+
it "exposes build_item for building an item without adding it to the box" do
component = described_class.new(work_packages: [], project:, container:, params:, current_user: user)
diff --git a/spec/components/open_project/common/work_package_card_component_spec.rb b/spec/components/open_project/common/work_package_card_component_spec.rb
index 35e42e4a24e..d9034a3ea84 100644
--- a/spec/components/open_project/common/work_package_card_component_spec.rb
+++ b/spec/components/open_project/common/work_package_card_component_spec.rb
@@ -69,8 +69,16 @@ RSpec.describe OpenProject::Common::WorkPackageCardComponent, type: :component d
expect(rendered_component).to have_text("Card subject")
end
- it "renders the story points label" do
- expect(rendered_component).to have_text("5 points", normalize_ws: true)
+ it "does not render story points by default" do
+ expect(rendered_component).to have_no_text("5 points", normalize_ws: true)
+ end
+
+ it "renders the metric slot when provided" do
+ rendered = render_inline(component) do |card|
+ card.with_metric { "Custom metric" }
+ end
+
+ expect(rendered).to have_text("Custom metric")
end
it "renders a WorkPackageCardComponent::Menu kebab" do