From 0b4bcb5d3c2f7475c2d38994457b36a29ea1d05a Mon Sep 17 00:00:00 2001 From: Alexander Brandon Coles Date: Fri, 29 May 2026 18:50:27 +0200 Subject: [PATCH] Set accessible names on table column headers capybara_accessible_selectors 0.16 resolves role selectors such as `:columnheader` by the computed accessible name. In a browser that name folds in the CSS `text-transform: uppercase` styling and the column action-menu trigger text, so `have_columnheader("Subject")` no longer matches a header whose accessible name resolves to "SUBJECT Open menu". Sets an explicit `aria-label` equal to the plain caption on each header cell, in both the server-rendered tables and the work package table, so the accessible name is the column title alone. This also improves screen reader output, which previously announced the uppercased text and the menu label. --- app/components/table_component.html.erb | 2 +- app/helpers/sort_helper.rb | 8 +++++++- app/views/workflows/summaries/show.html.erb | 2 +- .../components/wp-table/wp-table.component.html | 1 + spec/helpers/sort_helper_spec.rb | 12 ++++++------ 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app/components/table_component.html.erb b/app/components/table_component.html.erb index 17e2a3410b9..27c53b728d6 100644 --- a/app/components/table_component.html.erb +++ b/app/components/table_component.html.erb @@ -42,7 +42,7 @@ See COPYRIGHT and LICENSE files for more details. <% if sortable_column?(name) %> <%= helpers.sort_header_tag(name, **options) %> <% else %> - +
diff --git a/app/helpers/sort_helper.rb b/app/helpers/sort_helper.rb index 3fd55d4e29a..982b0524c57 100644 --- a/app/helpers/sort_helper.rb +++ b/app/helpers/sort_helper.rb @@ -338,7 +338,7 @@ module SortHelper # Extracts the given `options` and provides them to a block. # See #sort_header_tag and #sort_header_with_action_menu for usage examples. - def with_sort_header_options(column, allowed_params: nil, with_title: false, **options) + def with_sort_header_options(column, allowed_params: nil, with_title: false, **options) # rubocop:disable Metrics/AbcSize caption = get_caption(column, options) default_order = options.delete(:default_order) || "asc" @@ -349,6 +349,12 @@ module SortHelper options[:title] = sort_header_title(column, caption, options) if with_title options[:icon_only_header] = column == :favorited + # Give the header cell an explicit accessible name equal to the plain caption. + # Without it, the column header's accessible name is derived from its contents, + # which can include the action menu trigger text and is uppercased by the + # `text-transform` styling, neither of which should be part of the name. + options[:"aria-label"] = caption if caption.present? + within_sort_header_tag_hierarchy(options, sort_class(column)) do yield(column, caption, default_order, allowed_params:, param:, lang:, title: options[:title], sortable: options.fetch(:sortable, false), data:) diff --git a/app/views/workflows/summaries/show.html.erb b/app/views/workflows/summaries/show.html.erb index f5ed8bcff9e..3add4663a34 100644 --- a/app/views/workflows/summaries/show.html.erb +++ b/app/views/workflows/summaries/show.html.erb @@ -45,7 +45,7 @@ See COPYRIGHT and LICENSE files for more details.
<% @roles.each do |role| %> - +
<%= content_tag(role.builtin? ? "em" : "span", h(role.name)) %> diff --git a/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.html b/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.html index ddd77c1e302..f2e3fa4ac1f 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.html +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.html @@ -49,6 +49,7 @@ } @for (column of columns; track column.href) { +
@@ -165,7 +165,7 @@ RSpec.describe SortHelper do it "adds the sort class" do expect(output).to be_html_eql(<<-HTML) - +
@@ -187,7 +187,7 @@ RSpec.describe SortHelper do it "adds the sort class" do expect(output).to be_html_eql(<<-HTML) - +
@@ -219,7 +219,7 @@ RSpec.describe SortHelper do context "when not given allowed parameters" do it "copies default ones to the link" do expect(output).to be_html_eql(<<-HTML) - +
@@ -240,7 +240,7 @@ RSpec.describe SortHelper do it "copies them to the link" do expect(output).to be_html_eql(<<-HTML) - +
@@ -262,7 +262,7 @@ RSpec.describe SortHelper do it "includes the passed data param in the link" do expect(output).to be_html_eql(<<~HTML) - +