Skip to content
Merged
5 changes: 4 additions & 1 deletion core/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from enum import StrEnum

from django.conf import settings
from django.template.defaultfilters import capfirst
from django.urls import NoReverseMatch, reverse

from libraries.constants import LATEST_RELEASE_URL_PATH_STR
Expand Down Expand Up @@ -109,7 +110,9 @@ def selected_version(request):
version = header_data.most_recent

is_non_latest = _is_non_latest_version(url_version_slug, cookie_slug)
label = version.display_name if (is_non_latest and version) else "Latest"
# capfirst so the "master"/"develop" branch names render as "Master"/"Develop";
# numeric release names are unaffected.
label = capfirst(version.display_name) if (is_non_latest and version) else "Latest"

latest_href = ""
if is_url_driven and resolver_match and resolver_match.view_name:
Expand Down
24 changes: 24 additions & 0 deletions core/tests/test_context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest
from django.test import RequestFactory
from django.urls import ResolverMatch
from model_bakery import baker

from core.context_processors import (
active_nav_item,
Expand Down Expand Up @@ -147,3 +148,26 @@ def test_selected_version_no_resolver_match(rf):
ctx = selected_version(request)
assert ctx["selected_version_is_url_driven"] is False
assert ctx["selected_version_label"] == "Latest"


@pytest.mark.parametrize(
"branch,expected_label",
[("master", "Master"), ("develop", "Develop")],
)
def test_selected_version_label_capitalizes_branch_names(
db, rf, branch, expected_label
):
"""Branch versions are excluded from the header dropdown options, so the
label comes from the DB fallback — it must still read "Master"/"Develop"."""
baker.make("versions.Version", name=branch, slug=branch, fully_imported=True)
request = rf.get(f"/library/{branch}/foo/")
_stub_header_data(request)
_attach_resolver(
request,
route="library/<boostversionslug:version_slug>/<slug:library_slug>/",
kwargs={"version_slug": branch, "library_slug": "foo"},
view_name="library-detail",
)
ctx = selected_version(request)
assert ctx["selected_version_is_non_latest"] is True
assert ctx["selected_version_label"] == expected_label
3 changes: 1 addition & 2 deletions libraries/templatetags/branch_url_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@

@register.simple_tag()
def branch_url_tag(view: str, branch: str, kwargs: dict):
kwargs["version_slug"] = branch
return reverse(view, kwargs=kwargs)
return reverse(view, kwargs={**kwargs, "version_slug": branch})
86 changes: 86 additions & 0 deletions static/css/v3/branch-buttons.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
Branch Buttons — small Master ("M") / Develop ("D") version-switch links.
Depends on: foundations.css, semantics.css, spacing.css, border-radius.css
*/

.branch-buttons {
display: flex;
align-items: center;
gap: var(--space-s);
}

.branch-button {
position: relative;
display: inline-block;
flex-shrink: 0;
width: 29px;
height: 28px;
color: var(--color-text-primary);
text-decoration: none;
}

.branch-button__square {
position: absolute;
top: 4px;
left: 5px;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
background-color: var(--color-surface-weak);
border: 1px solid var(--color-stroke-weak);
border-radius: var(--border-radius-s);
transition: border-color 0.15s ease;
}

.branch-button__icon {
width: 16px;
height: 16px;
color: var(--color-text-primary);
}

.branch-button__badge {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
min-width: 12px;
padding: 2px;
border-radius: 60px;
background-color: var(--color-button-accent);
color: var(--color-text-on-accent);
font-size: 8px;
line-height: 1;
letter-spacing: -0.08px;
text-align: center;
}

.branch-button:hover .branch-button__square {
border-color: var(--color-stroke-brand-accent);
}

.branch-button:focus-visible {
outline: 2px solid var(--color-stroke-brand-accent);
outline-offset: 2px;
border-radius: var(--border-radius-s);
}

/* Sub-page hero: far right, bottom-aligned with the actions row; right edge
inset to line up with the content cards below. */
.hero__block > .branch-buttons {
align-self: flex-end;
flex-shrink: 0;
margin-right: var(--space-large);
}

/* Mobile: stacks below the actions row, left-aligned with the cards. */
@media (max-width: 767px) {
.hero__block > .branch-buttons {
align-self: flex-start;
margin-right: 0;
margin-top: var(--space-large);
}
}
1 change: 1 addition & 0 deletions static/css/v3/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
@import "./user-card.css";
@import "./post-filter.css";
@import "./library-filter.css";
@import "./branch-buttons.css";
@import "./contributors-list.css";
@import "./card-group.css";
@import "./post-card.css";
Expand Down
17 changes: 17 additions & 0 deletions static/css/v3/library-page.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@
padding: 0 var(--space-large);
}

/* Filter toolbar row: filters fill the row, branch buttons sit at the far right,
bottom-aligned with the filter dropdowns. */
.library-page__filter-row {
display: flex;
align-items: flex-end;
gap: var(--space-medium);
}

.library-page__filter-row .library-filter {
flex: 1 1 auto;
min-width: 0;
}

.library-page__filter-row .branch-buttons {
flex: 0 0 auto;
}

/* === Hero === */

.library-page-hero {
Expand Down
2 changes: 2 additions & 0 deletions templates/includes/icon.html
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@
<path d="M12 1.33334H10.6667V2.66668H9.33337V4.00001H8.00004V5.33334H6.66671V6.66668H5.33337V8.00001H4.00004V9.33334H2.66671V10.6667H1.33337V14.6667H5.33337V13.3333H6.66671V12H8.00004V10.6667H9.33337V9.33334H10.6667V8.00001H12V6.66668H13.3334V5.33334H14.6667V4.00001H13.3334V2.66668H12V1.33334ZM12 6.66668H10.6667V8.00001H9.33337V9.33334H8.00004V10.6667H6.66671V12H5.33337V10.6667H4.00004V9.33334H5.33337V8.00001H6.66671V6.66668H8.00004V5.33334H9.33337V4.00001H10.6667V5.33334H12V6.66668ZM4.00004 10.6667H2.66671V13.3333H5.33337V12H4.00004V10.6667Z" />
{% elif icon_name == "pixel-share" %}
<path d="M2.66671 4H7.33337V5.33333H2.66671V10.6667H7.33337V12H1.33337V4H2.66671ZM13.3334 4H8.66671V5.33333H13.3334V10.6667H8.66671V12H14.6667V4H13.3334ZM11.3334 7.33333H4.66671V8.66667H11.3334V7.33333Z" />
{% elif icon_name == "git-branch" %}
<path d="M2.4 0H4V9.6H6.4V12H12V6.4H9.6V0H16V6.4H13.6V13.6H6.4V16H0V9.6H2.4V0ZM14.4 4.8V1.6H11.2V4.8H14.4ZM4.8 13.6V11.2H1.6V14.4H4.8V13.6Z" />
{% elif icon_name == "file-multiple" %}
<path d="M14 12H4.66667V1.33337H10V2.66671H11.3333V4.00004H10V5.33337H11.3333V4.00004H12.6667V5.33337H14V12ZM6 2.66671V10.6667H12.6667V6.66671H8.66667V2.66671H6ZM2 4.00004H3.33333V13.3334H11.3333V14.6667H2V4.00004Z" />
{% elif icon_name == "rss" %}
Expand Down
23 changes: 23 additions & 0 deletions templates/v3/includes/_branch_buttons.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% load branch_url_tag %}
{% comment %}
V3 Branch Buttons

Links that switch the current page to the Master / Develop Boost version.
URLs are derived from the current view via branch_url_tag, which swaps the
version_slug — so this partial is page-agnostic and takes no variables.
Requires the `request` context processor (project default).
{% endcomment %}
<div class="branch-buttons" role="group" aria-label="Switch branch">
<a class="branch-button" href="{% branch_url_tag request.resolver_match.view_name branch='master' kwargs=request.resolver_match.kwargs %}" title="Master" aria-label="Master">
<span class="branch-button__square">
{% include "includes/icon.html" with icon_name="git-branch" icon_class="branch-button__icon" icon_size=16 icon_viewbox="0 0 16 16" %}
</span>
<span class="branch-button__badge" aria-hidden="true">M</span>
</a>
<a class="branch-button" href="{% branch_url_tag request.resolver_match.view_name branch='develop' kwargs=request.resolver_match.kwargs %}" title="Develop" aria-label="Develop">
<span class="branch-button__square">
{% include "includes/icon.html" with icon_name="git-branch" icon_class="branch-button__icon" icon_size=16 icon_viewbox="0 0 16 16" %}
</span>
<span class="branch-button__badge" aria-hidden="true">D</span>
</a>
</div>
4 changes: 4 additions & 0 deletions templates/v3/includes/_hero_library.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
hero_background_image_url (optional) — Background image URL for the hero container.
extra_classes (optional) — Extra modifier classes for the hero <section>, e.g. "hero--community" (community) or "hero--release" (release detail).
is_flagship_lib (optional) — Boolean value stating whether or not this is the hero for a flagship library. If it isn't, styles are a bit different.
show_branch_buttons (optional) — Boolean. If truthy, renders the small Master/Develop version-switch buttons (v3/includes/_branch_buttons.html) at the far right of the actions/links row. Off by default so the buttons only appear on library sub-pages, not other heroes (community, release, examples).

If none of category_tags/version_tag/first_boost_version are passed, the tags
row is hidden. If none of doc_url/source_url/slack_url/github_url are passed,
Expand Down Expand Up @@ -97,6 +98,9 @@ <h1 class="hero__title{% if is_flagship_lib %} hero__title--flagship{% endif %}"
</div>
{% endwith %}
{% endif %}
{% if show_branch_buttons %}
{% include "v3/includes/_branch_buttons.html" %}
{% endif %}
</div>
{% include "v3/includes/_version_alert.html" %}
</section>
4 changes: 2 additions & 2 deletions templates/v3/libraries/library-subpage.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ <h1 class="library-no-version__title">No library records available for this vers
{% else %}
{% with cpp_ver=library_version.get_cpp_standard_minimum_display|default:"C++03" %}
{% if is_flagship_lib %}
{% include "v3/includes/_hero_library.html" with title=object.display_name description=library_version.description|default:object.description category_tags=category_tags_v3 version_tag=cpp_ver first_boost_version=object.first_boost_version.display_name doc_url=documentation_url source_url=github_url slack_url=slack_url github_url=object.github_issues_url is_flagship_lib=True hero_image_url_light=library_hero_image_url_light hero_image_url_dark=library_hero_image_url_dark %}
{% include "v3/includes/_hero_library.html" with title=object.display_name description=library_version.description|default:object.description category_tags=category_tags_v3 version_tag=cpp_ver first_boost_version=object.first_boost_version.display_name doc_url=documentation_url source_url=github_url slack_url=slack_url github_url=object.github_issues_url is_flagship_lib=True hero_image_url_light=library_hero_image_url_light hero_image_url_dark=library_hero_image_url_dark show_branch_buttons=True %}
{% else %}
{% include "v3/includes/_hero_library.html" with title=object.display_name description=library_version.description|default:object.description category_tags=category_tags_v3 version_tag=cpp_ver first_boost_version=object.first_boost_version.display_name doc_url=documentation_url source_url=github_url slack_url=slack_url github_url=object.github_issues_url %}
{% include "v3/includes/_hero_library.html" with title=object.display_name description=library_version.description|default:object.description category_tags=category_tags_v3 version_tag=cpp_ver first_boost_version=object.first_boost_version.display_name doc_url=documentation_url source_url=github_url slack_url=slack_url github_url=object.github_issues_url show_branch_buttons=True %}
{% endif %}
{% endwith %}

Expand Down
3 changes: 2 additions & 1 deletion templates/v3/library_page.html
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,9 @@ <h1 class="hero-title">Find a battle-tested library for the problem you're solvi
</div>
</section>
<form action="" method="get">
<div id="library-page-filter-container" class="mb-xl">
<div id="library-page-filter-container" class="mb-xl library-page__filter-row">
{% include 'v3/includes/_library_filter.html' with filter_id='library-page-filter' fields=library_filter_fields clear_url=library_filter_clear_url %}
{% include 'v3/includes/_branch_buttons.html' %}
</div>
</form>
{% if library_view_str == 'list' %}
Expand Down
Loading