diff --git a/.changeset/thin-terms-shout.md b/.changeset/thin-terms-shout.md new file mode 100644 index 0000000000..8cb23443ce --- /dev/null +++ b/.changeset/thin-terms-shout.md @@ -0,0 +1,5 @@ +--- +"@openproject/primer-view-components": minor +--- + +[67724] keep breadcrumbs slot required but allow nil/empty value diff --git a/app/components/primer/open_project/page_header.pcss b/app/components/primer/open_project/page_header.pcss index c773ea157f..c1638b4834 100644 --- a/app/components/primer/open_project/page_header.pcss +++ b/app/components/primer/open_project/page_header.pcss @@ -83,3 +83,15 @@ .PageHeader-parentLink { flex: 1 1 auto; } + +/* Hide the context bar on desktop when no breadcrumb is visible */ +@media screen and (min-width: 543.98px) { + .PageHeader--noBreadcrumb .PageHeader-contextBar { + display: none; + } +} + +/* Match the title bar height with the toggle menu button for proper vertical alignment */ +.PageHeader--noBreadcrumb .PageHeader-titleBar { + height: var(--control-small-size); +} \ No newline at end of file diff --git a/app/components/primer/open_project/page_header.rb b/app/components/primer/open_project/page_header.rb index 532ffce823..2dc25569c3 100644 --- a/app/components/primer/open_project/page_header.rb +++ b/app/components/primer/open_project/page_header.rb @@ -40,7 +40,6 @@ class PageHeader < Primer::Component "PageHeader-title", "PageHeader-title--#{variant}" ) - Primer::OpenProject::PageHeader::Title.new(state: @state, **system_arguments) } @@ -181,12 +180,17 @@ class PageHeader < Primer::Component Primer::Beta::IconButton.new(icon: icon, **system_arguments) } - # Optional breadcrumbs above the title row - # By default shown on wider screens. Can be overridden with system_argument: display - # + # Using PageHeader without breadcrumbs is only recommended in special cases. + # In doubt, please check the PageHeader component documentation. # @param items [Array] Items is an array of strings, hash {href, text} or an anchor tag string # @param system_arguments [Hash] <%= link_to_system_arguments_docs %> renders_one :breadcrumbs, lambda { |items, selected_item_font_weight: :bold, **system_arguments| + if items.nil? + # No breadcrumbs → mark the PageHeader with a special class + @system_arguments[:classes] = class_names(@system_arguments[:classes], "PageHeader--noBreadcrumb") + return + end + system_arguments[:classes] = class_names(system_arguments[:classes], "PageHeader-breadcrumbs") system_arguments[:display] ||= DEFAULT_BREADCRUMBS_DISPLAY diff --git a/previews/primer/open_project/page_header_preview.rb b/previews/primer/open_project/page_header_preview.rb index 2169804bd4..6eff39986d 100644 --- a/previews/primer/open_project/page_header_preview.rb +++ b/previews/primer/open_project/page_header_preview.rb @@ -191,6 +191,32 @@ def non_bold_breadcrumbs end end + # @label Without breadcrumbs + # A PageHeader example that renders without breadcrumbs. + # This should only be done for *entry pages* (such as overview pages). + # In case of doubt, please check with the Dream team. + def without_breadcrumbs + render(Primer::OpenProject::PageHeader.new) do |header| + header.with_title { "Hello" } + header.with_breadcrumbs(nil) + header.with_description { "This PageHeader does not have any breadcrumbs." } + header.with_action_button(mobile_icon: "star", mobile_label: "Star") do |button| + button.with_leading_visual_icon(icon: "star") + "Star" + end + end + end + + # @label With skipable breadcrumb items + def skip_breadcrumb_item + render(Primer::OpenProject::PageHeader.new) do |component| + component.with_title { "Resize me to mobile screen size" } + component.with_breadcrumbs([{ href: "/foo", text: "Foo" }, + { href: "/bar", text: "Bar", skip_for_mobile: true }, + "Baz"]) + end + end + # @label With tab nav # def tab_nav @@ -257,16 +283,6 @@ def segmented_control_mobile_icons end end - # @label With skipable breadcrumb items - def skip_breadcrumb_item - render(Primer::OpenProject::PageHeader.new) do |component| - component.with_title { "Resize me to mobile screen size" } - component.with_breadcrumbs([{ href: "/foo", text: "Foo" }, - { href: "/bar", text: "Bar", skip_for_mobile: true }, - "Baz"]) - end - end - # @label With a link in the description def description render_with_template(template: "primer/open_project/page_header_preview/description") diff --git a/test/components/primer/open_project/page_header_test.rb b/test/components/primer/open_project/page_header_test.rb index becbcb76a4..e20379cec7 100644 --- a/test/components/primer/open_project/page_header_test.rb +++ b/test/components/primer/open_project/page_header_test.rb @@ -290,6 +290,24 @@ def test_skips_a_breadcrumb_item_for_mobile assert_selector("nav[aria-label='Breadcrumb'].PageHeader-breadcrumbs .breadcrumb-item.text-bold a[href='#']") end + def test_renders_without_breadcrumbs + render_inline(Primer::OpenProject::PageHeader.new) do |header| + header.with_title { "Hello" } + header.with_breadcrumbs(nil) + end + + # Title is rendered + assert_text("Hello") + assert_selector(".PageHeader-title") + + # Breadcrumbs and parent link are not rendered + assert_no_selector(".PageHeader-breadcrumbs") + assert_no_selector(".PageHeader-parentLink") + + # A class should be added to the page header when there is no breadcrumbs + assert_selector(".PageHeader--noBreadcrumb") + end + private def breadcrumb_elements