mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
[#73968] Move card metrics to Backlogs
Remove the story-points display from the shared work package card and expose a generic metric slot instead. Backlogs now opts into that slot with a small story-points component when rendering sprint, bucket, and inbox cards. https://community.openproject.org/wp/73968
This commit is contained in:
@@ -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?
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 %>
|
||||
<span class="op-work-package-card-points-label"> <%= t(:"backlogs.points_label", count: story_points) %></span>
|
||||
<% if metric? %>
|
||||
<% grid.with_area(:metric) do %>
|
||||
<%= metric %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}" }
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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}" }
|
||||
|
||||
@@ -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 %>
|
||||
<span class="op-work-package-card-metric-label"> <%= t(:"backlogs.points_label", count: story_points) %></span>
|
||||
<% end %>
|
||||
@@ -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
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user