Skip to content

Commit

Permalink
feat[#50499]: Add back button and breadcrumbs to page header (#45)
Browse files Browse the repository at this point in the history
* feat[#50499]: Add back button and breadcrumbs to page header

* chore[#50499]: Set renders_one BackButton attributes size and icon defaults

* fix[#50499]: Remove I18n from PageHeader

* fix[#50499]: Rename CSS class PageHeader-back_button to PageHeader-backButton

* fix[#50499]: Fix RuboCop errors and warnings

* fix[#50499]: Vertically center back button

* fix[#50499]: Add aria-label in preview for with_back_button

* test[#50499]: Add renders_back_button test

* test[#50499]: Add renders_breadcrumbs test

* fix[#50499]: Fix class names in PageHeader

* chore[#50499]: Remove show_breadcrumb option in PageHeader.with_breadcrumbs

* chore[#50499]: Remove manually added static classes

* feat[#50499]: Support **system_arguments in PageHeader breadcrumbs slot

* fix[#50499]: Readd @Label With actions in PageHeaderPreview

* fix[#50499]: Fix spaces

* fix[#50499]: Change PageHeader align-items to baseline

* fix[#50499]: Add size and icon options and use fetch_or_fallback

* chore[#50499]: Add params options in preview for PageHeader with back button

* chore[#50499]: Add params information in back_button slot

* chore[#50499]: Add params options in preview for PageHeader with breadcrumbs

* chore[#50499]: Add anchor_tag_string helper function

* fix[#50499]: Fix back button and title wrapping on small screen

* chore[#50499]: Remove long testing title from preview

* chore[#50499]: Remove back button in breadcrumb preview

* chore[#50499]: Sort PageHeader with-alternatives alphabetically

* chore[#50499]: Add toggles for back button and breadcrumbs in playground

* chore[#50499]: Add back button size parameter to playground

* Center back button in any cases. Unfortunately, there is a two pixel misalignment which we have to cancel out. This is most likely coming from the arrow being thinner than the font

---------

Co-authored-by: Henriette Darge <[email protected]>
  • Loading branch information
dominic-braeunlein and HDinger authored Nov 9, 2023
1 parent f4903ea commit ac9f727
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/green-bananas-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openproject/primer-view-components': minor
---

Add back button and breadcrumbs support to PageHeader
8 changes: 6 additions & 2 deletions app/components/primer/open_project/page_header.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<%= render Primer::BaseComponent.new(**@system_arguments) do %>
<%= title %>
<%= breadcrumbs %>
<div class="PageHeader-titleBar">
<%= back_button %>
<%= title %>
<%= actions %>
</div>
<%= description %>
<%= actions %>
<% end %>
25 changes: 19 additions & 6 deletions app/components/primer/open_project/page_header.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@
padding-bottom: var(--stack-padding-condensed);
margin-bottom: var(--stack-gap-normal);
border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted);
flex-flow: row wrap;
justify-content: flex-end; /* Keep actions right aligned. */
flex-flow: column;

@media (max-width: 767.98px) {
border-bottom: 0;
}
}

.PageHeader-titleBar {
display: flex;
flex-flow: row;
justify-content: flex-end;
align-items: center; /* Keep back button vertically aligned. */
}

.PageHeader-title {
font-size: 24px;
font-weight: var(--base-text-weight-normal);
flex: 1 1 auto;
order: 0;
}

.PageHeader-title--large {
Expand All @@ -29,17 +34,25 @@
font-size: var(--text-body-size-medium);
color: var(--fgColor-muted);
flex: 1 100%;
order: 2;
}

/* Add 1 or 2 buttons to the right of the heading */
.PageHeader-actions {
margin: var(--base-size-4) 0 var(--base-size-4) var(--base-size-4);
align-self: center;
justify-content: flex-end;
order: 1;

& + .PageHeader-description {
margin-top: var(--base-size-4);
}
}

.PageHeader-breadcrumbs {
display: block;
width: 100%;
margin-bottom: var(--base-size-8);
}

.PageHeader-backButton {
margin-top: 2px; /* to center align with label */
margin-right: var(--base-size-4);
}
70 changes: 70 additions & 0 deletions app/components/primer/open_project/page_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ class PageHeader < Primer::Component
DEFAULT_HEADER_VARIANT
].freeze

DEFAULT_BACK_BUTTON_SIZE = :medium
BACK_BUTTON_SIZE_OPTIONS = [
:small,
DEFAULT_HEADER_VARIANT,
:large
].freeze

DEFAULT_BACK_BUTTON_ICON = "arrow-left"
BACK_BUTTON_ICON_OPTIONS = [
DEFAULT_BACK_BUTTON_ICON,
"chevron-left",
"triangle-left"
].freeze

status :open_project

# The title of the page header
Expand Down Expand Up @@ -50,6 +64,45 @@ class PageHeader < Primer::Component
Primer::BaseComponent.new(**system_arguments)
}

# Optional back button prepend the title
#
# @param size [Symbol] <%= one_of(Primer::OpenProject::PageHeader::BACK_BUTTON_SIZE_OPTIONS) %>
# @param icon [String] <%= one_of(Primer::OpenProject::PageHeader::BACK_BUTTON_ICON_OPTIONS) %>
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
renders_one :back_button, lambda { |
size: DEFAULT_BACK_BUTTON_SIZE,
icon: DEFAULT_BACK_BUTTON_ICON,
**system_arguments
|
deny_tag_argument(**system_arguments)
system_arguments[:tag] = :a
system_arguments[:scheme] = :invisible
system_arguments[:size] = fetch_or_fallback(BACK_BUTTON_SIZE_OPTIONS, size, DEFAULT_BACK_BUTTON_SIZE)
system_arguments[:icon] = fetch_or_fallback(BACK_BUTTON_ICON_OPTIONS, icon, DEFAULT_BACK_BUTTON_ICON)
system_arguments[:classes] = class_names(system_arguments[:classes], "PageHeader-backButton")

Primer::Beta::IconButton.new(**system_arguments)
}

# Optional breadcrumbs above the title row
#
# @param items [Array<String, Hash>] 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, **system_arguments|
system_arguments[:classes] = class_names(system_arguments[:classes], "PageHeader-breadcrumbs")
render(Primer::Beta::Breadcrumbs.new(**system_arguments)) do |breadcrumbs|
items.each do |item|
item = anchor_string_to_object(item) if anchor_tag_string?(item)

if item.is_a?(String)
breadcrumbs.with_item(href: "#") { item }
else
breadcrumbs.with_item(href: item[:href]) { item[:text] }
end
end
end
}

def initialize(**system_arguments)
@system_arguments = deny_tag_argument(**system_arguments)

Expand All @@ -64,6 +117,23 @@ def initialize(**system_arguments)
def render?
title?
end

private

# transform anchor tag strings to {href, text} objects
# e.g "\u003ca href=\"/admin\"\u003eAdministration\u003c/a\u003e"
def anchor_string_to_object(html_string)
# Parse the HTML
doc = Nokogiri::HTML.fragment(html_string)
# Extract href and text
anchor = doc.at("a")
{ href: anchor["href"], text: anchor.text }
end

# Check if the item is an anchor tag string e.g "\u003ca href=\"/admin\"\u003eAdministration\u003c/a\u003e"
def anchor_tag_string?(item)
item.is_a?(String) && item.start_with?("\u003c")
end
end
end
end
40 changes: 39 additions & 1 deletion previews/primer/open_project/page_header_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,24 @@ def default
# @param variant [Symbol] select [medium, large]
# @param title [String] text
# @param description [String] text
def playground(variant: :medium, title: "Hello", description: "Last updated 5 minutes ago by XYZ.")
# @param with_back_button [Boolean]
# @param back_button_size [Symbol] select [small, medium, large]
# @param with_breadcrumbs [Boolean]
def playground(
variant: :medium,
title: "Hello",
description: "Last updated 5 minutes ago by XYZ.",
with_back_button: false,
back_button_size: :medium,
with_breadcrumbs: false
)
breadcrumb_items = [{ href: "/foo", text: "Foo" }, { href: "/bar", text: "Bar" }, "Baz"]

render(Primer::OpenProject::PageHeader.new) do |header|
header.with_title(variant: variant) { title }
header.with_description { description }
header.with_back_button(href: "#", size: back_button_size, 'aria-label': "Back") if with_back_button
header.with_breadcrumbs(breadcrumb_items) if with_breadcrumbs
end
end

Expand All @@ -37,6 +51,30 @@ def large_title
def actions
render_with_template(locals: {})
end

# @label With back button
# @param href [String] text
# @param size [Symbol] select [small, medium, large]
# @param icon [String] select ["arrow-left", "chevron-left", "triangle-left"]
def back_button(href: "#", size: :medium, icon: "arrow-left")
render(Primer::OpenProject::PageHeader.new) do |header|
header.with_title { "Hello" }
header.with_back_button(href: href, size: size, icon: icon, 'aria-label': "Back")
end
end

# @label With breadcrumbs
def breadcrumbs
breadcrumb_items = [
{ href: "/foo", text: "Foo" },
"\u003ca href=\"/foo/bar\"\u003eBar\u003c/a\u003e",
"Baz"
]
render(Primer::OpenProject::PageHeader.new) do |header|
header.with_title { "A title" }
header.with_breadcrumbs(breadcrumb_items)
end
end
end
end
end
33 changes: 33 additions & 0 deletions test/components/primer/open_project/page_header_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,37 @@ def test_renders_actions
assert_text("An action")
assert_selector(".PageHeader-actions")
end

def test_renders_back_button
render_inline(Primer::OpenProject::PageHeader.new) do |header|
header.with_title { "Hello" }
header.with_back_button(href: "/link", 'aria-label': "Back")
end

assert_text("Hello")
assert_selector(".PageHeader-title")
assert_selector(".PageHeader-backButton")
assert_selector(".Button-withTooltip a[href='/link']")
end

def test_renders_breadcrumbs
breadcrumb_items = [
{ href: "/foo", text: "Foo" },
"\u003ca href=\"/foo/bar\"\u003eBar\u003c/a\u003e",
"test"
]

render_inline(Primer::OpenProject::PageHeader.new) do |header|
header.with_title { "Hello" }
header.with_breadcrumbs(breadcrumb_items)
end

assert_text("Hello")
assert_selector(".PageHeader-title")
assert_selector(".PageHeader-breadcrumbs")

assert_selector("nav[aria-label='Breadcrumb'].PageHeader-breadcrumbs .breadcrumb-item a[href='/foo']")
assert_selector("nav[aria-label='Breadcrumb'].PageHeader-breadcrumbs .breadcrumb-item a[href='/foo/bar']")
assert_selector("nav[aria-label='Breadcrumb'].PageHeader-breadcrumbs .breadcrumb-item a[href='#']")
end
end

0 comments on commit ac9f727

Please sign in to comment.