mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
[#74684] Extract BorderBoxListComponent
Introduces a shared BorderBox-backed list component and moves the Backlogs-specific work package card list into the Backlogs module. Wires Backlogs callers to the new component API, keeps specialized card rendering behind an item factory hook, and replaces old OpPrimer work-package card list coverage with focused component specs. https://community.openproject.org/wp/74684
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# 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 OpenProject
|
||||
module Common
|
||||
# @logical_path OpenProject/Common
|
||||
class BorderBoxListComponentPreview < ViewComponent::Preview
|
||||
# @label Default
|
||||
def default
|
||||
render OpenProject::Common::BorderBoxListComponent.new(
|
||||
container: "border-box-list-preview"
|
||||
) do |list|
|
||||
list.with_header(title: "Things we're building", count: true) do |header|
|
||||
header.with_description { "There's lots to look forward to" }
|
||||
header.with_action_button(scheme: :invisible) do |button|
|
||||
button.with_leading_visual_icon(icon: :pencil)
|
||||
"Edit"
|
||||
end
|
||||
header.with_menu(button_aria_label: "List actions") do |menu|
|
||||
menu.with_item(label: "Configure") do |menu_item|
|
||||
menu_item.with_leading_visual_icon(icon: :gear)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
list.with_item { "Prioritized project launch" }
|
||||
list.with_item { "Updated status reporting" }
|
||||
list.with_item { "Shared team calendar" }
|
||||
list.with_footer { "Next launch window: October" }
|
||||
end
|
||||
end
|
||||
|
||||
# @label With work package items
|
||||
def with_work_package_items
|
||||
work_packages = WorkPackage.includes(:project).limit(2).to_a
|
||||
return preview_message("No work packages in the database.") if work_packages.empty?
|
||||
|
||||
render OpenProject::Common::BorderBoxListComponent.new(
|
||||
container: "border-box-list-work-package-preview"
|
||||
) do |list|
|
||||
list.with_header(title: "Work packages", count: true)
|
||||
|
||||
work_packages.each do |work_package|
|
||||
list.with_work_package_item(work_package:) do |item|
|
||||
item.with_menu(button_aria_label: "Work package actions") do |menu|
|
||||
menu.with_item(label: "Open", href: "/work_packages/#{work_package.id}") do |menu_item|
|
||||
menu_item.with_leading_visual_icon(icon: :link)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# @label Playground
|
||||
# @param title_tag [Symbol] select [h2, h3, h4, h5]
|
||||
# @param count [Symbol] select [inferred, hidden, explicit, zero]
|
||||
# @param count_scheme [Symbol] select [primary, secondary]
|
||||
# @param hide_zero_count toggle
|
||||
def playground(
|
||||
title_tag: :h4,
|
||||
count: :inferred,
|
||||
count_scheme: :primary,
|
||||
hide_zero_count: true
|
||||
)
|
||||
render OpenProject::Common::BorderBoxListComponent.new(
|
||||
container: "border-box-list-playground-preview"
|
||||
) do |list|
|
||||
list.with_header(
|
||||
title: "Playground list",
|
||||
title_tag: title_tag.to_sym,
|
||||
count: preview_count(count),
|
||||
count_arguments: {
|
||||
scheme: count_scheme.to_sym,
|
||||
hide_if_zero: boolean_preview_param(hide_zero_count),
|
||||
aria: { label: "Visible list item count", live: "polite" }
|
||||
}
|
||||
) do |header|
|
||||
header.with_description { "Advanced header options" }
|
||||
end
|
||||
|
||||
list.with_item { "First item" }
|
||||
list.with_item { "Second item" }
|
||||
list.with_footer { "Footer content" }
|
||||
end
|
||||
end
|
||||
|
||||
# @label Empty state
|
||||
# List with a header and an empty state (Blankslate), no items.
|
||||
def empty_state
|
||||
render OpenProject::Common::BorderBoxListComponent.new(
|
||||
container: "border-box-list-empty-preview"
|
||||
) do |list|
|
||||
list.with_header(title: "Empty list", count: 0)
|
||||
list.with_empty_state(
|
||||
title: "No items yet",
|
||||
description: "There is nothing to show."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def preview_count(count)
|
||||
case count.to_sym
|
||||
when :inferred
|
||||
true
|
||||
when :hidden
|
||||
false
|
||||
when :explicit
|
||||
7
|
||||
when :zero
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def boolean_preview_param(value)
|
||||
ActiveModel::Type::Boolean.new.cast(value)
|
||||
end
|
||||
|
||||
def preview_message(text)
|
||||
render(Primer::Beta::Blankslate.new) do |blankslate|
|
||||
blankslate.with_heading(tag: :h4).with_content(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,114 +0,0 @@
|
||||
# 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 OpenProject
|
||||
module Common
|
||||
# @logical_path OpenProject/Common
|
||||
class WorkPackageCardListComponentPreview < ViewComponent::Preview
|
||||
include ActionView::RecordIdentifier
|
||||
|
||||
def sprint_with_cards
|
||||
sprint = Sprint.first
|
||||
project = sprint&.project
|
||||
return preview_message("No sprints in the database.") unless sprint && project
|
||||
|
||||
work_packages = sprint.work_packages_for(project).limit(3)
|
||||
render OpenProject::Common::WorkPackageCardListComponent.new(
|
||||
work_packages:,
|
||||
project:,
|
||||
container: sprint
|
||||
) do |list|
|
||||
list.with_header(title: sprint.name, count: work_packages.size) do |header|
|
||||
points = work_packages.sum { |w| w.story_points || 0 }
|
||||
header.with_description { "#{points} points" }
|
||||
end
|
||||
list.with_empty_state(title: "Sprint is empty", description: "Drag work packages here")
|
||||
end
|
||||
end
|
||||
|
||||
def empty_sprint
|
||||
sprint = Sprint.first
|
||||
project = sprint&.project
|
||||
return preview_message("No sprints in the database.") unless sprint && project
|
||||
|
||||
render OpenProject::Common::WorkPackageCardListComponent.new(
|
||||
work_packages: [], project:, container: sprint
|
||||
) do |list|
|
||||
list.with_header(title: sprint.name, count: 0) do |header|
|
||||
header.with_description { "0 points" }
|
||||
end
|
||||
list.with_empty_state(title: "Sprint is empty", description: "Drag work packages here")
|
||||
end
|
||||
end
|
||||
|
||||
def inbox
|
||||
project = Project.first
|
||||
return preview_message("No project in the database.") unless project
|
||||
|
||||
render OpenProject::Common::WorkPackageCardListComponent.new(
|
||||
work_packages: [],
|
||||
project:,
|
||||
container: dom_target(:inbox, project)
|
||||
) do |list|
|
||||
list.with_empty_state(title: "Inbox is empty", description: "All caught up",
|
||||
icon: :"op-backlogs")
|
||||
end
|
||||
end
|
||||
|
||||
def manual_item
|
||||
work_package = WorkPackage.first
|
||||
project = work_package&.project
|
||||
return preview_message("No work packages in the database.") unless work_package && project
|
||||
|
||||
render OpenProject::Common::WorkPackageCardListComponent.new(
|
||||
project:,
|
||||
container: :manual_item_demo
|
||||
) do |list|
|
||||
list.with_empty_state(title: "No items", description: "Manual items can be added by callers")
|
||||
list.with_work_package_item(work_package:)
|
||||
list.with_item(scheme: :neutral) { "Caller-provided item" }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# ViewComponent's `Preview.render_args` expects each preview method to
|
||||
# return a Hash (it does `result[:template] = …`), so plain string
|
||||
# returns fail with "no implicit conversion of Symbol into Integer".
|
||||
# Wrap fallback messages in a Blankslate render so they go through the
|
||||
# standard hash path.
|
||||
def preview_message(text)
|
||||
render(Primer::Beta::Blankslate.new) do |b|
|
||||
b.with_heading(tag: :h4).with_content(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user