Skip to content

Commit

Permalink
Highlight active section in navigation (#13)
Browse files Browse the repository at this point in the history
Highlight, in the navigation bar, the section of the app that the user is
currently using.
  • Loading branch information
mcantelon committed Oct 22, 2023
1 parent 112d31b commit fd4417d
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 20 deletions.
19 changes: 19 additions & 0 deletions AIPscan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from flask_sqlalchemy import SQLAlchemy

from AIPscan.celery import configure_celery
from AIPscan.navbar import NavBar
from config import CONFIGS

db = SQLAlchemy()
Expand Down Expand Up @@ -34,6 +35,24 @@ def create_app(config_name="default"):

db.create_all()

# Define navigation bar sections and route-to-section mapping (needed
# given that the "AIPs" and "Reports" sections are in the same Blueprint)
navbar = NavBar()
navbar.add_section(
"Archivematica Storage Services", "aggregator.storage_services"
)
navbar.add_section("AIPs", "reporter.view_aips")
navbar.add_section("Reports", "reporter.reports")

navbar.map_route("reporter.view_aip", "AIPs")
navbar.map_route("reporter.view_file", "AIPs")

# Inject navigation bar into templates
@app.context_processor
def inject_navbar():
return dict(navbar=navbar)

# Set up 404 handling
@app.errorhandler(404)
def page_not_found(e):
return render_template("error/404.html"), 404
Expand Down
65 changes: 65 additions & 0 deletions AIPscan/navbar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from flask import url_for


class NavBar:
sections: list
route_map: dict

def __init__(self):
self.sections = []
self.route_map = {}

def add_section(self, label, route):
self.sections.append(NavBarSection(label, route, self))

def map_route(self, route, label):
self.route_map[route] = label


class NavBarSection:
label: str
route: str
nav_bar: NavBar

def __init__(self, label, route, nav_bar):
self.label = label # Navigation link text
self.route = route # Route ("transfer.index" for example)
self.nav_bar = nav_bar # Parent NavBar (Needed to access route map)

def get_url(self):
return url_for(self.route)

Check warning on line 30 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L30

Added line #L30 was not covered by tests

def is_active(self, request):
# Don't check if request isn't for a static asset
request_rule = request.url_rule

Check warning on line 34 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L34

Added line #L34 was not covered by tests

if request_rule is None:
return False

Check warning on line 37 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L37

Added line #L37 was not covered by tests

# Check for arbitrary mapping of request route to nav bar section
if request_rule.endpoint in self.nav_bar.route_map:
return self.nav_bar.route_map[request_rule.endpoint] == self.label

Check warning on line 41 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L41

Added line #L41 was not covered by tests

request_blueprint = self.blueprint_of(request_rule.endpoint)

Check warning on line 43 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L43

Added line #L43 was not covered by tests

if request_rule.endpoint == self.route:
# A section's default route has been requested
return True

Check warning on line 47 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L47

Added line #L47 was not covered by tests
elif not self.blueprint_has_mapped_route(request_blueprint):
# A route within a section representing an entire Blueprint may have been requested
section_route_blueprint = self.blueprint_of(self.route)

Check warning on line 50 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L50

Added line #L50 was not covered by tests

# Found Blueprint-level match between section and request
return request_blueprint == section_route_blueprint

Check warning on line 53 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L53

Added line #L53 was not covered by tests

def blueprint_has_mapped_route(self, blueprint):
for mapped_route in self.nav_bar.route_map:
route_map_blueprint = self.blueprint_of(mapped_route)

Check warning on line 57 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L57

Added line #L57 was not covered by tests

if route_map_blueprint == blueprint:
return True

Check warning on line 60 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L60

Added line #L60 was not covered by tests

return False

Check warning on line 62 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L62

Added line #L62 was not covered by tests

def blueprint_of(self, route):
return route.split(".")[0]

Check warning on line 65 in AIPscan/navbar.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/navbar.py#L65

Added line #L65 was not covered by tests
21 changes: 1 addition & 20 deletions AIPscan/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,7 @@

</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="/">AIPscan</a>
<div class="navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('aggregator.storage_services') }}">Archivematica Storage Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('reporter.view_aips') }}">AIPs</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('reporter.reports') }}">Reports</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('api.doc') }}" target="apiframe">API</a>
</li>
</ul>
</div>
<span style="font-style: italic" class="navbar-text">Crawl Archivematica AIPs to provide repository-wide reporting</span>
</nav>
{% include "partials/navbar.html" %}

<div class="container-fluid" style="margin: 20px 0;">

Expand Down
18 changes: 18 additions & 0 deletions AIPscan/templates/partials/navbar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="/">AIPscan</a>
<div class="navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">

{% for section in navbar.sections %}
<li class="nav-item">
<a class="nav-link {{ 'active fw-bold' if section.is_active(request) else '' }}" href="{{ section.get_url() }}">{{ section.label }}</a>
</li>
{% endfor %}

<li class="nav-item">
<a class="nav-link" href="{{ url_for('api.doc') }}" target="apiframe">API</a>
</li>
</ul>
</div>
<span style="font-style: italic" class="navbar-text">Crawl Archivematica AIPs to provide repository-wide reporting</span>
</nav>

0 comments on commit fd4417d

Please sign in to comment.