From c1bb691d2b607982e77ad9f95fc758e6456bfd8b Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Wed, 20 May 2026 17:51:17 +0200 Subject: [PATCH] [#72945] Teach Border Box List title_arguments Allows headers to receive title arguments so callers can pass system arguments (including semantic attributes) to rendered headings. https://community.openproject.org/wp/72945 --- .../border_box_list_component/header.html.erb | 4 +- .../border_box_list_component/header.rb | 9 ++++ .../common/border_box_list_component_spec.rb | 44 +++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/app/components/open_project/common/border_box_list_component/header.html.erb b/app/components/open_project/common/border_box_list_component/header.html.erb index 8247c66bfbb..559ac7d2b19 100644 --- a/app/components/open_project/common/border_box_list_component/header.html.erb +++ b/app/components/open_project/common/border_box_list_component/header.html.erb @@ -43,7 +43,7 @@ See COPYRIGHT and LICENSE files for more details. ) ) do |collapsible| %> - <% collapsible.with_title(tag: title_tag) { title } %> + <% collapsible.with_title(**title_arguments, tag: title_tag) { title } %> <% if render_count? %> <% collapsible.with_count(**counter_arguments) %> <% end %> @@ -57,7 +57,7 @@ See COPYRIGHT and LICENSE files for more details. <% else %> <% grid.with_area(:heading) do %> <%= render(Primer::BaseComponent.new(tag: :div, classes: "op-border-box-list-header--heading-line")) do %> - <%= render(Primer::Beta::Truncate.new(tag: title_tag, classes: "Box-title")) { title } %> + <%= render(Primer::Beta::Truncate.new(**title_arguments, tag: title_tag, classes: title_classes)) { title } %> <% if render_count? %> <%= render(Primer::Beta::Counter.new(**counter_arguments)) %> <% end %> diff --git a/app/components/open_project/common/border_box_list_component/header.rb b/app/components/open_project/common/border_box_list_component/header.rb index 17fd8d237a8..a93d7fd0087 100644 --- a/app/components/open_project/common/border_box_list_component/header.rb +++ b/app/components/open_project/common/border_box_list_component/header.rb @@ -88,6 +88,7 @@ module OpenProject :count_label, :count_arguments, :title_tag, + :title_arguments, :list_id, :interactive, :collapsed, @@ -105,6 +106,7 @@ module OpenProject # @param count_arguments [Hash] forwarded to `Primer::Beta::Counter`. # Values are merged over the default counter arguments. # @param title_tag [Symbol] tag used for the title heading. + # @param title_arguments [Hash] forwarded to the title heading. # @param list_id [String, nil] id of the collapsible list body. # @param interactive [Boolean] whether counter updates should be # announced politely to assistive technology. @@ -119,6 +121,7 @@ module OpenProject count_label: nil, count_arguments: {}, title_tag: :h4, + title_arguments: {}, list_id: nil, interactive: false, collapsed: false, @@ -132,6 +135,7 @@ module OpenProject @count_label = count_label @count_arguments = count_arguments @title_tag = title_tag + @title_arguments = title_arguments.except(:tag) @list_id = list_id @interactive = interactive @collapsible_id = list_id @@ -176,6 +180,11 @@ module OpenProject merged end + # @return [String] classes forwarded to the non-collapsible title. + def title_classes + class_names("Box-title", title_arguments[:classes]) + end + # @return [String, nil] ids controlled by the collapsible header. def collapsible_id @collapsible_id.presence 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 260b8c39cb9..fea303c9654 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 @@ -408,6 +408,31 @@ RSpec.describe OpenProject::Common::BorderBoxListComponent, type: :component do expect(rendered).to have_heading("Custom title", level: 3) end + + it "forwards title arguments" do + rendered = render_inline( + described_class.new(container: "hdr-title-args") + ) do |list| + list.with_header( + title: "Described title", + title_tag: :h4, + title_arguments: { + tag: :h2, + id: "custom-title", + classes: "custom-title-class", + data: { title: "custom" }, + aria: { describedby: "goal-text" } + } + ) + list.with_item { "row" } + end + + expect(rendered).to have_css( + "h4#custom-title.Box-title.custom-title-class[data-title='custom']", + text: "Described title", + aria: { describedby: "goal-text" } + ) + end end describe "header collapsible behavior" do @@ -440,6 +465,25 @@ RSpec.describe OpenProject::Common::BorderBoxListComponent, type: :component do "[aria-controls='collapse-no-footer_list']" ) end + + it "forwards title arguments to the collapsible title" do + rendered = render_inline( + described_class.new(container: "collapse-title-args", collapsible: true) + ) do |list| + list.with_header( + title: "Collapsible", + title_arguments: { aria: { describedby: "collapsible-help" } } + ) + list.with_item(id: "collapsible-help") { "Helpful row" } + end + + expect(rendered).to have_heading( + "Collapsible", + level: 4, + accessible_description: "Helpful row" + ) + expect(rendered).to have_css("h4", text: "Collapsible", aria: { describedby: "collapsible-help" }) + end end describe "generic items" do