From e372dc089b1d214db51adf39de2a31fe00895e14 Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Thu, 14 May 2026 20:35:40 +0200 Subject: [PATCH] [#72945] Add BorderBoxList transparent scheme Adds `scheme: :transparent` to BorderBoxListComponent with CSS overrides for transparent header background and no separator line. Backlogs callers adopt the transparent scheme. https://community.openproject.org/wp/72945 --- .../common/border_box_list_component.rb | 21 +++++++++-- .../common/border_box_list_component.sass | 9 +++++ .../border_box_list_component_preview.rb | 29 +++++++++++++++ .../backlogs/inbox_component.html.erb | 1 + .../work_package_card_list_component.rb | 1 + .../common/border_box_list_component_spec.rb | 36 +++++++++++++++++++ 6 files changed, 95 insertions(+), 2 deletions(-) diff --git a/app/components/open_project/common/border_box_list_component.rb b/app/components/open_project/common/border_box_list_component.rb index 777263a6da1..aaaa4562f08 100644 --- a/app/components/open_project/common/border_box_list_component.rb +++ b/app/components/open_project/common/border_box_list_component.rb @@ -37,8 +37,12 @@ module OpenProject # header actions, collapsible behavior, and row rendering. class BorderBoxListComponent < ApplicationComponent include OpPrimer::ComponentHelpers + include Primer::FetchOrFallbackHelper - attr_reader :container, :collapsible, :current_user, :header_id, :footer_id + SCHEME_DEFAULT = :default + SCHEME_OPTIONS = [SCHEME_DEFAULT, :transparent].freeze + + attr_reader :container, :scheme, :collapsible, :current_user, :header_id, :footer_id alias_method :collapsible?, :collapsible @@ -163,6 +167,9 @@ module OpenProject # @param container [String, Symbol, Class, Object] value passed to # `dom_target` to derive DOM ids for the list and related controls. + # @param scheme [Symbol] visual scheme. `:default` renders the standard + # BorderBox header. `:transparent` renders a transparent header with no + # separator line. # @param interactive [Boolean] whether dynamic list updates should be # announced politely to assistive technology. This affects the counter # and an explicitly configured empty state; it does not create default @@ -172,8 +179,9 @@ module OpenProject # @param current_user [User] user context passed to work-package items. # @param system_arguments [Hash] forwarded to `Primer::Beta::BorderBox`. # Pass `id:` to set the box id; related ids are derived from it. - def initialize( + def initialize( # rubocop:disable Metrics/AbcSize container:, + scheme: SCHEME_DEFAULT, interactive: false, collapsible: false, current_user: User.current, @@ -182,6 +190,9 @@ module OpenProject super() @container = container + @scheme = ActiveSupport::StringInquirer.new( + fetch_or_fallback(SCHEME_OPTIONS, scheme, SCHEME_DEFAULT).to_s + ) @interactive = interactive @collapsible = collapsible @current_user = current_user @@ -189,6 +200,12 @@ module OpenProject @system_arguments[:id] ||= dom_target(container) @system_arguments[:list_id] = dom_target(@system_arguments[:id], :list) + @system_arguments[:classes] = class_names( + @system_arguments[:classes], + "op-border-box-list", + "op-border-box-list_transparent" => @scheme.transparent? + ) + @header_id = dom_target(@system_arguments[:id], :header) @footer_id = dom_target(@system_arguments[:id], :footer) end diff --git a/app/components/open_project/common/border_box_list_component.sass b/app/components/open_project/common/border_box_list_component.sass index 11a8520a032..ce1c0c165e7 100644 --- a/app/components/open_project/common/border_box_list_component.sass +++ b/app/components/open_project/common/border_box_list_component.sass @@ -8,6 +8,14 @@ // See COPYRIGHT and LICENSE files for more details. //++ +.op-border-box-list_transparent + > .Box-header + background-color: transparent + border-bottom: none + + > ul > .Box-row:first-of-type + border-top: none + .op-border-box-list-header display: grid grid-template-columns: 1fr minmax(5rem, max-content) auto @@ -20,3 +28,4 @@ align-self: flex-start // Unfortunately, the invisible button style bites us here again. margin-top: -6px + diff --git a/lookbook/previews/open_project/common/border_box_list_component_preview.rb b/lookbook/previews/open_project/common/border_box_list_component_preview.rb index 4a406b87055..b1ea48d5087 100644 --- a/lookbook/previews/open_project/common/border_box_list_component_preview.rb +++ b/lookbook/previews/open_project/common/border_box_list_component_preview.rb @@ -61,6 +61,35 @@ module OpenProject end end + # @label Transparent scheme + # @param interactive toggle + # @param collapsible [Boolean] toggle + def transparent(padding: :default, interactive: false, collapsible: false) + render OpenProject::Common::BorderBoxListComponent.new( + container: "border-box-list-transparent-preview", + scheme: :transparent, + interactive: boolean_preview_param(interactive), + collapsible: + ) do |list| + list.with_header(title: "Sprint backlog", count: true) do |header| + header.with_description { "3 points remaining" } + header.with_action_button do |button| + button.with_leading_visual_icon(icon: :rocket) + "Start sprint" + end + header.with_menu(button_aria_label: "Sprint actions") do |menu| + menu.with_item(label: "Edit sprint") do |menu_item| + menu_item.with_leading_visual_icon(icon: :pencil) + end + end + end + + list.with_item { "User authentication stories" } + list.with_item { "Dashboard improvements" } + list.with_item { "API documentation" } + end + end + # @label With work package items # @param interactive toggle # @param collapsible toggle diff --git a/modules/backlogs/app/components/backlogs/inbox_component.html.erb b/modules/backlogs/app/components/backlogs/inbox_component.html.erb index 4d57bd44c6e..5ded201a4a6 100644 --- a/modules/backlogs/app/components/backlogs/inbox_component.html.erb +++ b/modules/backlogs/app/components/backlogs/inbox_component.html.erb @@ -34,6 +34,7 @@ See COPYRIGHT and LICENSE files for more details. container: inbox_container, current_user:, interactive: true, + scheme: :transparent, padding: :condensed, test_selector: "backlog-inbox", data: { diff --git a/modules/backlogs/app/components/backlogs/work_package_card_list_component.rb b/modules/backlogs/app/components/backlogs/work_package_card_list_component.rb index 5122de033ed..36f39a15498 100644 --- a/modules/backlogs/app/components/backlogs/work_package_card_list_component.rb +++ b/modules/backlogs/app/components/backlogs/work_package_card_list_component.rb @@ -68,6 +68,7 @@ module Backlogs container:, current_user:, interactive: true, + scheme: :transparent, **@system_arguments ) end diff --git a/spec/components/open_project/common/border_box_list_component_spec.rb b/spec/components/open_project/common/border_box_list_component_spec.rb index c072dc586cf..76e9ae73825 100644 --- a/spec/components/open_project/common/border_box_list_component_spec.rb +++ b/spec/components/open_project/common/border_box_list_component_spec.rb @@ -767,4 +767,40 @@ RSpec.describe OpenProject::Common::BorderBoxListComponent, type: :component do expect(rendered).to have_css("collapsible-header") end end + + describe "scheme" do + it "defaults to :default" do + rendered = render_inline( + described_class.new(container: "scheme-default") + ) do |list| + list.with_header(title: "Default") + list.with_item { "row" } + end + + expect(rendered).to have_no_css(".op-border-box-list_transparent") + end + + it "applies the transparent CSS class when scheme is :transparent" do + rendered = render_inline( + described_class.new(container: "scheme-transparent", scheme: :transparent) + ) do |list| + list.with_header(title: "Transparent") + list.with_item { "row" } + end + + expect(rendered).to have_css(".Box.op-border-box-list_transparent") + end + + it "keeps collapsible independent of the transparent scheme" do + rendered = render_inline( + described_class.new(container: "transparent-collapse", scheme: :transparent, collapsible: true) + ) do |list| + list.with_header(title: "Transparent collapsible") + list.with_item { "row" } + end + + expect(rendered).to have_css(".Box.op-border-box-list_transparent") + expect(rendered).to have_css("collapsible-header") + end + end end