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")