Extend the WorkPackageCardComponent with new optionss

This commit is contained in:
Henriette Darge
2026-05-12 14:16:58 +02:00
parent 6a4030f337
commit 91de3310bd
14 changed files with 336 additions and 49 deletions
@@ -0,0 +1,62 @@
The `WorkPackageCard` is a compact representation of a work package, designed for use in list and board views such as sprint and backlog views.
## Overview
<%= embed OpenProject::Common::WorkPackageCardComponentPreview, :default %>
## Anatomy
The card is structured in up to three rows:
**Row 1 — Metadata**
Contains the info line (type, ID, status), followed by optional elements: assignee, metric (e.g. story points), priority, and the actions menu. When `show_drag_handle: true`, a drag handle icon appears on the far left, spanning all rows, aligned to the top (row 1).
**Row 2 — Subject**
The work package title, always visible.
**Row 3 — Footer** *(only rendered when content is present)*
Shows a link to the parent work package (when `show_parent_link: true` and a parent exists) and/or the `with_bottom_line` slot.
## Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| `work_package` | `WorkPackage` | required | The work package to display |
| `menu_src` | `String` | `nil` | Optional lazy-load URL for the actions menu |
| `show_assignee` | `Boolean` | `false` | Show the assignee icon and name |
| `show_priority` | `Boolean` | `false` | Show a priority badge |
| `show_drag_handle` | `Boolean` | `false` | Show a drag handle icon |
| `show_parent_link` | `Boolean` | `false` | Show a link to the parent work package in row 3. Only rendered when `work_package.parent` is present. |
| `status_scheme` | `Symbol` | `:default` | Status label style: `:default` (highlighted) or `:secondary` (neutral label) |
## Slots
| Slot | Description |
|---|---|
| `with_metric` | Numeric value shown in row 1 (e.g. story points) |
| `with_menu` | Custom actions menu, replaces the default lazy menu |
| `with_bottom_line` | Additional content appended to row 3 |
## Variants
<%= embed OpenProject::Common::WorkPackageCardComponentPreview, :playground, panels: %i[params source] %>
## Code structure
```ruby
render OpenProject::Common::WorkPackageCardComponent.new(
work_package: @work_package,
show_assignee: true,
show_priority: true,
show_parent_link: true,
status_scheme: :secondary
) do |card|
card.with_metric { @work_package.story_points }
card.with_menu do |menu|
menu.with_item(label: "Open", href: work_package_path(@work_package))
end
card.with_bottom_line do
# Whatever else you want to show
end
end
```
@@ -32,37 +32,99 @@ module OpenProject
module Common
# @logical_path OpenProject/Common
class WorkPackageCardComponentPreview < ViewComponent::Preview
# See the [component documentation](/lookbook/pages/components/work_packages/card) for more details.
#
# @param show_assignee toggle
# @param show_priority toggle
# @param show_drag_handle toggle
# @param show_parent_link toggle
# @param show_metric toggle
# @param show_menu toggle
# @param show_bottom toggle
# @param status_scheme select [default, secondary]
def playground(show_assignee: false, show_priority: false, show_drag_handle: false,
show_parent_link: false, show_metric: false, show_menu: false,
show_bottom: false, status_scheme: :default)
work_package = WorkPackage.visible.where.not(parent_id: nil).first || WorkPackage.visible.first
return preview_message("No work packages in the database.") unless work_package
render_with_template(template: "open_project/common/work_package_card_component_preview/playground",
locals: {
work_package:,
show_assignee:,
show_priority:,
show_drag_handle:,
show_parent_link:,
show_metric:,
show_menu:,
show_bottom:,
status_scheme:
})
end
# Minimal card showing only the info line, subject and actions menu.
def default
work_package = WorkPackage.first
work_package = WorkPackage.visible.first
return preview_message("No work packages in the database.") unless work_package
render OpenProject::Common::WorkPackageCardComponent.new(work_package:)
end
# Card with a numeric metric (e.g. story points) in the top-right area.
def with_metric
work_package = WorkPackage.visible.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 { (work_package.try(:story_points) || 8).to_s }
end
end
# Card with a custom actions menu.
def with_menu
work_package = WorkPackage.visible.first
return preview_message("No work packages in the database.") unless work_package
render OpenProject::Common::WorkPackageCardComponent.new(work_package:) do |card|
card.with_menu do |menu|
menu.with_item(label: "Open", href: "/work_packages/#{work_package.id}")
menu.with_item(label: "Edit", href: "/work_packages/#{work_package.id}/edit")
menu.with_divider
menu.with_item(label: "Delete", scheme: :danger)
end
end
end
# Card with a drag handle icon for reorderable lists.
def with_drag_handle
work_package = WorkPackage.visible.first
return preview_message("No work packages in the database.") unless work_package
render OpenProject::Common::WorkPackageCardComponent.new(
work_package:
work_package:,
show_drag_handle: true
)
end
def with_metric
work_package = WorkPackage.first
return preview_message("No work packages in the database.") unless work_package
# Card with show_parent_link enabled. Renders a link to the parent work package in row 3.
# Only visible when the work package actually has a parent.
def with_parent_link
work_package = WorkPackage.visible.where.not(parent_id: nil).first
return preview_message("No work packages with a parent found.") unless work_package
render OpenProject::Common::WorkPackageCardComponent.new(
work_package:
) do |card|
card.with_metric_content(10)
end
work_package:,
show_parent_link: true
)
end
def with_menu
work_package = WorkPackage.first
# Card with additional content in the bottom slot (row 3), rendered alongside the parent link.
def with_bottom_line
work_package = WorkPackage.visible.first
return preview_message("No work packages in the database.") unless work_package
render OpenProject::Common::WorkPackageCardComponent.new(
work_package:
) do |card|
card.with_menu do |menu|
menu.with_item(label: "Open", href: "/work_packages/#{work_package.id}")
end
end
render_with_template(template: "open_project/common/work_package_card_component_preview/with_bottom_line",
locals: { work_package: })
end
private
@@ -0,0 +1,25 @@
<%= render OpenProject::Common::WorkPackageCardComponent.new(
work_package:,
show_assignee:,
show_priority:,
show_drag_handle:,
show_parent_link:,
status_scheme: status_scheme.to_sym
) do |card|
card.with_metric { (work_package.try(:story_points) || 5).to_s } if show_metric
if show_menu
card.with_menu do |menu|
menu.with_item(label: "Open", href: "/work_packages/#{work_package.id}")
menu.with_item(label: "Edit", href: "/work_packages/#{work_package.id}/edit")
menu.with_divider
menu.with_item(label: "Delete", scheme: :danger)
end
end
if show_bottom
card.with_bottom_line do
render(Primer::Beta::Text.new(tag: :span, font_size: :small, color: :subtle)) { "Some bottom line" }
end
end
end %>
@@ -0,0 +1,5 @@
<%= render OpenProject::Common::WorkPackageCardComponent.new(work_package:) do |card|
card.with_bottom_line do
render(Primer::Beta::Text.new(tag: :span, font_size: :small, color: :subtle)) { "Some bottom line" }
end
end %>