diff --git a/.changeset/khaki-carpets-matter.md b/.changeset/khaki-carpets-matter.md
new file mode 100644
index 0000000000..aa724f704e
--- /dev/null
+++ b/.changeset/khaki-carpets-matter.md
@@ -0,0 +1,9 @@
+---
+'@openproject/primer-view-components': patch
+---
+
+Change defaults for `Primer::OpenProject::SubHeader` for
+
+* `collapsed_search` to `true`
+* `scheme` of the filter button to `invisible`
+* `label` of the filter button to `All filters`
diff --git a/app/components/primer/open_project/sub_header.html.erb b/app/components/primer/open_project/sub_header.html.erb
index fb1794e405..70764547d7 100644
--- a/app/components/primer/open_project/sub_header.html.erb
+++ b/app/components/primer/open_project/sub_header.html.erb
@@ -7,6 +7,14 @@
<%= render @collapsed_filter_trigger if @collapsed_filter_trigger.present? %>
+ <%= segmented_control %>
+
+ <% if @segmented_control_block.present? %>
+ <%= render(@mobile_segmented_control) do |control| %>
+ <% @segmented_control_block.call(control) %>
+ <% end %>
+ <% end %>
+
<%= quick_sort %>
<%= quick_group %>
<% quick_filters.each do |quick_filter| %>
@@ -15,14 +23,6 @@
<%= render(@mobile_filter_button) if @mobile_filter_button.present? %>
<%= filter_button %>
-
- <%= segmented_control %>
-
- <% if @segmented_control_block.present? %>
- <%= render(@mobile_segmented_control) do |control| %>
- <% @segmented_control_block.call(control) %>
- <% end %>
- <% end %>
diff --git a/app/components/primer/open_project/sub_header.rb b/app/components/primer/open_project/sub_header.rb
index 6446ba4374..026b99a823 100644
--- a/app/components/primer/open_project/sub_header.rb
+++ b/app/components/primer/open_project/sub_header.rb
@@ -131,9 +131,10 @@ class SubHeader < Primer::Component
# To render custom content, call the `with_filter_component` method and pass a block that returns HTML.
renders_one :filter_button, types: {
button: {
- renders: lambda { |icon_only: false, leading_icon: :filter, mobile_label: I18n.t("button_filter"), **kwargs|
+ renders: lambda { |icon_only: false, leading_icon: :filter, mobile_label: I18n.t("button_all_filters"), **kwargs|
kwargs[:mr] ||= 2
kwargs[:icon] = leading_icon
+ kwargs[:scheme] ||= :invisible
kwargs[:aria] ||= merge_aria(
kwargs,
@@ -226,7 +227,7 @@ class SubHeader < Primer::Component
# @param collapsed_search [Boolean] When true, the search bar starts collapsed as an icon button on all screen sizes. Clicking expands it.
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
- def initialize(collapsed_search: false, **system_arguments)
+ def initialize(collapsed_search: true, **system_arguments)
@collapsed_search = collapsed_search
@system_arguments = system_arguments
@system_arguments[:tag] = :"sub-header"
diff --git a/app/components/primer/open_project/sub_header/button.rb b/app/components/primer/open_project/sub_header/button.rb
index 71f6661c62..f81295aa5c 100644
--- a/app/components/primer/open_project/sub_header/button.rb
+++ b/app/components/primer/open_project/sub_header/button.rb
@@ -35,7 +35,7 @@ def before_render
def call
render(@button) do |button|
button.with_leading_visual_icon(icon: @icon)
- content
+ content || I18n.t("button_all_filters")
end
end
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 73a0736907..df8fe6892b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -5,6 +5,7 @@ en:
button_close: "Close"
button_delete: "Delete"
button_delete_permanently: "Delete permanently"
+ button_all_filters: "All filters"
button_filter: "Filter"
button_save: "Save"
diff --git a/previews/primer/open_project/sub_header_preview.rb b/previews/primer/open_project/sub_header_preview.rb
index 4795f0525b..b80fdd1fdc 100644
--- a/previews/primer/open_project/sub_header_preview.rb
+++ b/previews/primer/open_project/sub_header_preview.rb
@@ -22,16 +22,17 @@ def playground(
show_filter_button: true,
show_quick_filter: true,
show_action_button: true,
- collapsed_search: false,
+ collapsed_search: true,
text: nil,
value: nil
)
- render_with_template(locals: {show_filter_input: show_filter_input,
- show_clear_button: show_clear_button,
- show_filter_button: show_filter_button,
- show_quick_filter: show_quick_filter,
- show_action_button: show_action_button,
- collapsed_search: collapsed_search,
+ bool = ActiveModel::Type::Boolean.new
+ render_with_template(locals: {show_filter_input: bool.cast(show_filter_input),
+ show_clear_button: bool.cast(show_clear_button),
+ show_filter_button: bool.cast(show_filter_button),
+ show_quick_filter: bool.cast(show_quick_filter),
+ show_action_button: bool.cast(show_action_button),
+ collapsed_search: bool.cast(collapsed_search),
text: text,
value: value})
end
@@ -42,7 +43,6 @@ def default
component.with_filter_input(name: "filter", label: "Filter")
component.with_filter_button do |button|
button.with_trailing_visual_counter(count: "15")
- "Filter"
end
component.with_action_button(leading_icon: :plus, label: "Create", scheme: :primary) do
@@ -93,11 +93,11 @@ def segmented_control
end
end
- # @label With collapsed search
- def collapsed_search
- render(Primer::OpenProject::SubHeader.new(collapsed_search: true)) do |component|
+ # @label With expanded search
+ def expanded_search
+ render(Primer::OpenProject::SubHeader.new(collapsed_search: false)) do |component|
component.with_filter_input(name: "filter", label: "Filter")
- component.with_filter_button do |button|
+ component.with_filter_button(scheme: :default) do |button|
button.with_trailing_visual_counter(count: "15")
"Filter"
end
diff --git a/previews/primer/open_project/sub_header_preview/quick_filters.html.erb b/previews/primer/open_project/sub_header_preview/quick_filters.html.erb
index 7440769456..f3ca44bc97 100644
--- a/previews/primer/open_project/sub_header_preview/quick_filters.html.erb
+++ b/previews/primer/open_project/sub_header_preview/quick_filters.html.erb
@@ -1,7 +1,7 @@
-<%= render(Primer::OpenProject::SubHeader.new(collapsed_search: true)) do |component|
+<%= render(Primer::OpenProject::SubHeader.new) do |component|
component.with_filter_input(name: "filter", label: "Filter")
- component.with_filter_button(mobile_label: "All filters") do
+ component.with_filter_button do
"All filters"
end
diff --git a/test/components/primer/open_project/sub_header_test.rb b/test/components/primer/open_project/sub_header_test.rb
index 10c3bc3d46..5f4a10b235 100644
--- a/test/components/primer/open_project/sub_header_test.rb
+++ b/test/components/primer/open_project/sub_header_test.rb
@@ -137,6 +137,14 @@ def test_renders_an_icon_filter_button
assert_selector(".SubHeader-leftPane .Button--iconOnly .octicon-filter")
end
+ def test_filter_button_uses_invisible_scheme
+ render_inline(Primer::OpenProject::SubHeader.new) do |component|
+ component.with_filter_button { "Filter" }
+ end
+
+ assert_selector(".SubHeader .Button--invisible")
+ end
+
def test_renders_a_custom_filter_button
render_inline(Primer::OpenProject::SubHeader.new) do |component|
component.with_filter_component do
@@ -229,8 +237,8 @@ def test_does_not_render_input_events_when_show_clear_button_is_not_set
assert_no_selector(".FormControl-input-trailingAction[data-action=\"click:primer-text-field#clearContents\"]")
end
- def test_renders_collapsed_search
- render_inline(Primer::OpenProject::SubHeader.new(collapsed_search: true)) do |component|
+ def test_renders_collapsed_search_per_default
+ render_inline(Primer::OpenProject::SubHeader.new) do |component|
component.with_filter_input(name: "filter", label: "Filter")
end
@@ -243,7 +251,7 @@ def test_renders_collapsed_search
end
def test_collapsed_search_trigger_visible_on_all_screen_sizes
- render_inline(Primer::OpenProject::SubHeader.new(collapsed_search: true)) do |component|
+ render_inline(Primer::OpenProject::SubHeader.new) do |component|
component.with_filter_input(name: "filter", label: "Filter")
end
@@ -252,8 +260,8 @@ def test_collapsed_search_trigger_visible_on_all_screen_sizes
assert_no_selector("[data-action='click:sub-header#expandFilterInput'].d-md-none")
end
- def test_default_search_trigger_hidden_on_desktop
- render_inline(Primer::OpenProject::SubHeader.new) do |component|
+ def test_expanded_search_trigger_hidden_on_desktop
+ render_inline(Primer::OpenProject::SubHeader.new(collapsed_search: false)) do |component|
component.with_filter_input(name: "filter", label: "Filter")
end
@@ -387,4 +395,29 @@ def test_sort_and_group_are_not_hidden_on_mobile_when_multiple_quick_filters
assert_no_selector(".d-none.d-md-flex .SortFilter")
assert_no_selector(".d-none.d-md-flex .GroupFilter")
end
+
+ def test_collapsed_search_auto_enabled_when_quick_filter_present
+ render_inline(Primer::OpenProject::SubHeader.new) do |component|
+ component.with_filter_input(name: "filter", label: "Filter")
+ component.with_quick_filter { "Status".html_safe }
+ end
+
+ # Filter container is hidden (collapsed_search auto-detected as true)
+ assert_selector(".SubHeader-filterContainer.d-none")
+ assert_no_selector(".SubHeader-filterContainer.d-md-flex")
+ # Trigger is visible on all screen sizes (no d-sm-none)
+ assert_no_selector("[data-action='click:sub-header#expandFilterInput'].d-md-none")
+ end
+
+ def test_explicit_collapsed_search_false_overrides_auto_collapse
+ render_inline(Primer::OpenProject::SubHeader.new(collapsed_search: false)) do |component|
+ component.with_filter_input(name: "filter", label: "Filter")
+ component.with_quick_filter { "Status".html_safe }
+ end
+
+ # Filter container is NOT hidden despite quick_filter being present
+ assert_selector(".SubHeader-filterContainer.d-md-flex")
+ # Trigger is mobile-only (d-sm-none) since collapsed_search is false
+ assert_selector("[data-action='click:sub-header#expandFilterInput'].d-md-none")
+ end
end
diff --git a/test/system/open_project/sub_header_test.rb b/test/system/open_project/sub_header_test.rb
index 117a4ae794..5fbae9f0c8 100644
--- a/test/system/open_project/sub_header_test.rb
+++ b/test/system/open_project/sub_header_test.rb
@@ -17,7 +17,7 @@ def render_clear_button_with_an_initial_value
end
def test_clear_button_functionality
- visit_preview(:playground, show_clear_button: true)
+ visit_preview(:playground, show_clear_button: true, collapsed_search: false)
# no clear button with empty value
assert_no_selector("button.FormControl-input-trailingAction")