Skip to content

Commit 6f4e15d

Browse files
committed
🎨 [#1378] Added mobile menu and two desktop menus with notifications
1 parent f6fe153 commit 6f4e15d

File tree

15 files changed

+726
-264
lines changed

15 files changed

+726
-264
lines changed

src/open_inwoner/components/templates/components/Header/Breadcrumbs.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<li class="breadcrumbs__list-item">
77
{% if forloop.first %}
88
<a class="link link--secondary" href="{{url}}" title="{{ name }}" aria-label="{{ name }}">
9-
{% icon icon="apps" %}
9+
{% icon icon="home" outlined=True %}
1010
</a>
1111
{% else %}
1212
{% icon icon="chevron_right" %}

src/open_inwoner/components/templates/components/Header/Header.html

+139-93
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{% load i18n header_tags form_tags icon_tags link_tags string_tags solo_tags menu_tags button_tags %}
2+
3+
{% get_solo "configurations.SiteConfiguration" as config %}
4+
5+
{% if request.user.is_authenticated %}
6+
7+
<nav class="primary-navigation primary-navigation__authenticated" aria-label="Navigatie na inloggen">
8+
<ul class="primary-navigation__list">
9+
<li class="primary-navigation__list-item">
10+
{% button text=_('Welkom ')|addstr:request.user.get_short_name type="button" icon="expand_more" icon_position="after" icon_outlined=True transparent=True extra_classes="primary-navigation--toggle" %}
11+
12+
<ul class="primary-navigation__list subpage-list">
13+
<li class="primary-navigation__list-item">
14+
{% link text=_("Mijn profiel") href='profile:detail' icon="inventory_2" icon_position="before" icon_outlined=True %}
15+
</li>
16+
<li class="primary-navigation__list-item">
17+
{% link text=_('Mijn berichten') href='inbox:index' icon="inbox" icon_position="before" %}
18+
{% with request.user.get_new_messages_total as total_messages %}
19+
{% if total_messages %}
20+
{% with ""|addstr:total_messages|addstr:"" as message_total %}
21+
<span class="indicator">{% link text=message_total href='inbox:index' secondary=True extra_classes="indicator__link" %}<span class="indicator__dot"></span></span>
22+
{% endwith %}
23+
{% endif %}
24+
{% endwith %}
25+
</li>
26+
27+
{% get_solo 'configurations.SiteConfiguration' as config %}
28+
29+
{% if request.user.bsn and config.show_cases %}
30+
<li class="primary-navigation__list-item">
31+
{% link text=_('Mijn aanvragen') href='cases:open_cases' icon="inventory_2" icon_position="before" icon_outlined=True %}
32+
</li>
33+
{% endif %}
34+
{% if show_plans %}
35+
<li class="primary-navigation__list-item">
36+
{% link text=_('Samenwerken') href='collaborate:plan_list' icon="people" icon_outlined=True icon_position="before" %}
37+
{% with request.user.get_plan_contact_new_count as plan_count %}
38+
{% if plan_count %}
39+
{% with ""|addstr:plan_count|addstr:"" as plan_count %}
40+
<span class="indicator">{% link text=plan_count href='collaborate:plan_list' secondary=True %}<span class="indicator__dot"></span></span>
41+
{% endwith %}
42+
{% endif %}
43+
{% endwith %}
44+
</li>
45+
{% endif %}
46+
47+
{% if has_general_faq_questions %}
48+
<li class="primary-navigation__list-item">
49+
{% link text=_('FAQ') href='general_faq' icon="help_outline" icon_position="before" icon_outlined=True %}
50+
</li>
51+
{% endif %}
52+
53+
<li class="header__list-item">
54+
{% trans "Logout" as logout %}
55+
{% link text=logout href=request.user.get_logout_url icon="logout" icon_position="before" primary=True %}
56+
</li>
57+
</ul>
58+
</li>
59+
</ul>
60+
</nav>
61+
{% elif config.login_show %}
62+
<div class="desktop-login">
63+
<span class="desktop-login__link">
64+
{% url 'login' as login_url %}
65+
{% trans "Inloggen" as login %}
66+
{% button text="Inloggen" href=login_url icon="person" icon_position="before" primary=True icon_outlined=True transparent=True %}
67+
</span>
68+
</div>
69+
{% endif %}
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,38 @@
1-
{% load i18n icon_tags link_tags string_tags solo_tags menu_tags %}
1+
{% load i18n header_tags form_tags icon_tags link_tags string_tags solo_tags menu_tags button_tags %}
22

3-
<nav class="primary-navigation" aria-label="Hoofd navigatie">
4-
<ul class="primary-navigation__list">
5-
{% show_menu 0 100 100 100 "cms/menu/primary.html" %}
6-
7-
{# <li class="primary-navigation__list-item">#}
8-
{# {% trans "Home" as link_text %}#}
9-
{# {% url 'root' as link %}#}
10-
{# {% link text=link_text href=link icon="apps" icon_position="before" %}#}
11-
{# </li>#}
12-
{# <li class="primary-navigation__list-item">#}
13-
{# {% link text=_("Onderwerpen") href='products:category_list' icon="description" icon_position="before" icon_outlined=True %}#}
14-
{# {% icon icon="expand_more" %}#}
15-
{##}
16-
{# {% if categories %}#}
17-
{# <ul class="primary-navigation__list subpage-list">#}
18-
{# {% for category in categories %}#}
19-
{# <li class="primary-navigation__list-item">#}
20-
{# {% url 'products:category_detail' slug=category.slug as category_href %}#}
21-
{# {% link text=category.name href=category_href %}#}
22-
{# </li>#}
23-
{# {% endfor %}#}
24-
{# </ul>#}
25-
{# {% endif %}#}
26-
{# </li>#}
27-
{# {% if request.user.is_authenticated %}#}
28-
{# <li class="primary-navigation__list-item">#}
29-
{# {% link text=_("Mijn profiel") href='profile:detail' icon="inventory_2" icon_position="before" icon_outlined=True %}#}
30-
{# </li>#}
31-
{# <li class="primary-navigation__list-item">#}
32-
{# {% link text=_('Mijn berichten') href='inbox:index' icon="inbox" icon_position="before" %}#}
33-
{# {% with request.user.get_new_messages_total as total_messages %}#}
34-
{# {% if total_messages %}#}
35-
{# {% with "("|addstr:total_messages|addstr:")" as message_total %}#}
36-
{# {% link text=message_total href='inbox:index' secondary=True %}#}
37-
{# {% endwith %}#}
38-
{# {% endif %}#}
39-
{# {% endwith %}#}
40-
{# </li>#}
3+
{# * * * Start of DISABLED * * * Django CMS dynamic Desktop menu#}
4+
{#<nav class="primary-navigation" aria-label="Hoofd navigatie">#}
5+
{# <ul class="primary-navigation__list">#}
6+
{# {% show_menu 0 100 100 100 "cms/menu/primary.html" %}#}
417
{##}
42-
{# {% get_solo 'configurations.SiteConfiguration' as config %}#}
43-
{##}
44-
{# {% if request.user.bsn and config.show_cases %}#}
45-
{# <li class="primary-navigation__list-item">#}
46-
{# {% link text=_('Mijn aanvragen') href='cases:open_cases' icon="inventory_2" icon_position="before" icon_outlined=True %}#}
47-
{# </li>#}
48-
{# {% endif %}#}
49-
{# {% if show_plans %}#}
8+
{# {% if has_general_faq_questions %}#}
509
{# <li class="primary-navigation__list-item">#}
51-
{# {% link text=_('Samenwerken') href='collaborate:plan_list' icon="people" icon_outlined=True icon_position="before" %}#}
52-
{# {% with request.user.get_plan_contact_new_count as plan_count %}#}
53-
{# {% if plan_count %}#}
54-
{# {% with "("|addstr:plan_count|addstr:")" as plan_count %}#}
55-
{# {% link text=plan_count href='collaborate:plan_list' secondary=True %}#}
56-
{# {% endwith %}#}
57-
{# {% endif %}#}
58-
{# {% endwith %}#}
10+
{# {% link text=_('FAQ') href='general_faq' icon="help_outline" icon_position="before" icon_outlined=True %}#}
5911
{# </li>#}
60-
{# {% endif %}#}
6112
{# {% endif %}#}
62-
{% if has_general_faq_questions %}
63-
<li class="primary-navigation__list-item">
64-
{% link text=_('FAQ') href='general_faq' icon="help_outline" icon_position="before" icon_outlined=True %}
65-
</li>
66-
{% endif %}
13+
{# </ul>#}
14+
{#</nav>#}
15+
{# * * * End of DISABLED * * * Django CMS dynamic Desktop menu#}
16+
17+
{% get_solo "configurations.SiteConfiguration" as config %}
18+
19+
<nav class="primary-navigation primary-navigation__main" aria-label="Hoofd navigatie">
20+
<ul class="primary-navigation__list">
21+
22+
<li class="primary-navigation__list-item">
23+
{% button text=_('Onderwerpen') type="button" icon="expand_more" icon_position="after" icon_outlined=True transparent=True extra_classes="primary-navigation--toggle" %}
24+
25+
{% if categories %}
26+
<ul class="primary-navigation__list subpage-list">
27+
{% for category in categories %}
28+
<li class="primary-navigation__list-item">
29+
{% url 'products:category_detail' slug=category.slug as category_href %}
30+
{% link text=category.name href=category_href %}
31+
</li>
32+
{% endfor %}
33+
</ul>
34+
{% endif %}
35+
</li>
36+
6737
</ul>
6838
</nav>

src/open_inwoner/components/templatetags/header_tags.py

+23
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,26 @@ def primary_navigation(categories, request, **kwargs):
6666
"categories": categories,
6767
"request": request,
6868
}
69+
70+
71+
@register.inclusion_tag("components/Header/NavigationAuthenticated.html")
72+
def navigation_authenticated(categories, request, **kwargs):
73+
"""
74+
Displaying the desktop navigation when user is authenticated
75+
76+
Usage:
77+
{% navigation_authenticated categories=Category.objects.all request=request %}
78+
79+
Variables:
80+
+ categories: Category[] | The categories that should be displayed in the theme dropdown.
81+
+ request: Request | The django request object.
82+
+ questionnaire: QuestionnaireStep | The default QuestionnaireStep, if any.
83+
- has_general_faq_questions: boolean | If the FAQ menu item should be shown.
84+
- show_plans: boolean | If the Plan item should be shown.
85+
"""
86+
87+
return {
88+
**kwargs,
89+
"categories": categories,
90+
"request": request,
91+
}

src/open_inwoner/js/components/header/primary-navigation.js

+40-30
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import BEM from 'bem.js'
22
import { Component } from '../abstract/component'
33

4-
/** @type {string} The primary navibation block name. */
4+
/** @type {string} The primary navigation block name. */
55
const BLOCK_PRIMARY_NAVIGATION = 'primary-navigation'
66

77
/** @type {NodeList<HTMLElement>} The primary navigation block name. */
@@ -10,75 +10,85 @@ const PRIMARY_NAVIGATIONS = BEM.getBEMNodes(BLOCK_PRIMARY_NAVIGATION)
1010
/** @type {string} The dismissed modifier state, overrides focus/hover. */
1111
const MODIFIER_DISMISSED = 'dismissed'
1212

13+
/** Handler to bypass Safari bug */
14+
const navigationToggle = document.querySelectorAll(
15+
'.primary-navigation--toggle'
16+
)
17+
1318
/**
1419
* Controls the primary navigation and dismissing it using the escape key.
1520
*/
1621
class PrimaryNavigation extends Component {
1722
/**
1823
* Binds events to callbacks.
1924
* Callbacks should trigger `setState` which in turn triggers `render`.
25+
* Focus is split to be handled in Safari
2026
*/
2127
bindEvents() {
22-
this.node.addEventListener('focus', this.onFocus.bind(this))
23-
this.node.addEventListener('mouseenter', this.onHover.bind(this))
28+
this.node.addEventListener('click', this.toggleDesktopNavOpen.bind(this))
29+
this.node.addEventListener('focusin', this.onFocusIn.bind(this))
30+
this.node.addEventListener('focusout', this.onFocusOut.bind(this))
2431
window.addEventListener('keyup', this.onKeyUp.bind(this))
2532
}
2633

2734
/**
28-
* Gets called when `node` receives focus.
35+
* Gets called when `node` receives focus -in or -out events.
2936
* Clears the dismissed state, (prevents overriding focus/hover).
37+
* Focusin and Focusout are used instead of Focus for Safari
3038
*/
31-
onFocus() {
39+
onFocusIn() {
3240
this.setState({ dismissed: false })
41+
this.node.classList.add('primary-navigation--open')
42+
}
43+
onFocusOut() {
44+
this.setState({ dismissed: true })
45+
this.node.blur()
46+
this.node.classList.remove('primary-navigation--open')
3347
}
3448

3549
/**
36-
* Gets called when `node` receives focus.
37-
* Clears the dismissed state, (prevents overriding focus/hover).
50+
* Gets called when `node` is clicked.
51+
* Clears the dismissed state, (prevents overriding focus/toggle).
3852
*/
39-
onHover() {
53+
toggleDesktopNavOpen(event) {
54+
event.stopPropagation()
4055
this.setState({ dismissed: false })
56+
this.node.classList.add('primary-navigation--open')
4157
}
4258

4359
/**
4460
* Gets called when a key is released.
4561
* If key is escape key, explicitly dismiss the menu (overriding focus/hover).
46-
* @param {KeyboardEvent} event
62+
//* @param {KeyboardEvent} event
4763
*/
4864
onKeyUp(event) {
4965
if (event.key === 'Escape') {
50-
if (this.getFocussedChild() || this.getHoveredChild()) {
51-
this.setState({ dismissed: true })
52-
}
66+
this.setState({ dismissed: true })
67+
this.node.blur()
68+
this.node.classList.remove('primary-navigation--open')
69+
// Safari specific
70+
navigationToggle.forEach((elem) => {
71+
elem.blur()
72+
})
5373
}
5474
}
5575

56-
/**
57-
* Returns the child node in focus (if any).
58-
* @return {HTMLElement|null}
59-
*/
60-
getFocussedChild() {
61-
return this.node.querySelector(':focus') || null
62-
}
63-
64-
/**
65-
* Returns the hovered child node (if any).
66-
* @return {HTMLElement|null}
67-
*/
68-
getHoveredChild() {
69-
return this.node.querySelector(':hover') || null
70-
}
71-
7276
/**
7377
* Persists state to DOM.
7478
* Rendering should be one-way traffic and not inspect any current values in DOM.
75-
* @param {Object} state State to render.
79+
//* @param {Object} state State to render.
7680
*/
7781
render(state) {
7882
BEM.toggleModifier(this.node, MODIFIER_DISMISSED, state.dismissed)
7983

8084
if (state.dismissed) {
81-
this.getFocussedChild().blur()
85+
this.node.blur()
86+
// Safari specific
87+
navigationToggle.forEach((elem) => {
88+
elem.blur()
89+
})
90+
91+
return this.node.querySelector('.primary-navigation--toggle')
8292
}
8393
}
8494
}
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
class Subpage {
22
constructor(node) {
33
this.node = node
4-
this.node.addEventListener('click', this.toggleOpen.bind(this))
4+
this.node.addEventListener('click', this.toggleNavOpen.bind(this))
55
}
66

7-
toggleOpen(event) {
8-
event.preventDefault()
7+
toggleNavOpen(event) {
98
this.node.parentElement.classList.toggle('nav__list--open')
109
}
1110
}
1211

1312
/**
1413
* Controls the toggling of subpages if there are any
1514
*/
16-
const filterButtons = document.querySelectorAll(
15+
const toggleSubitems = document.querySelectorAll(
1716
'.dropdown-nav__toggle .link--toggle'
1817
)
19-
;[...filterButtons].forEach((filterButton) => new Subpage(filterButton))
18+
;[...toggleSubitems].forEach((toggleSubitem) => new Subpage(toggleSubitem))

src/open_inwoner/plans/tests/test_views.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def test_plan_detail_contacts(self):
113113

114114
response = self.app.get(self.detail_url, user=self.contact)
115115
self.assertContains(response, self.user.get_full_name())
116-
self.assertContains(response, self.contact.get_full_name())
116+
self.assertContains(response, self.contact.first_name)
117117

118118
# Contact for one user, but not the other
119119
# Check if all users can see eachother in the plan
@@ -128,12 +128,12 @@ def test_plan_detail_contacts(self):
128128

129129
response = self.app.get(self.detail_url, user=self.contact)
130130
self.assertContains(response, self.user.get_full_name())
131-
self.assertContains(response, self.contact.get_full_name())
131+
self.assertContains(response, self.contact.first_name)
132132
self.assertContains(response, new_contact.get_full_name())
133133

134134
response = self.app.get(self.detail_url, user=new_contact)
135135
self.assertContains(response, self.user.get_full_name())
136-
self.assertContains(response, new_contact.get_full_name())
136+
self.assertContains(response, new_contact.first_name)
137137
self.assertContains(response, self.contact.get_full_name())
138138

139139
new_contact.delete()

src/open_inwoner/scss/components/Form/Form.scss

+14
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,17 @@
237237
}
238238
}
239239
}
240+
241+
/// Specific forms
242+
243+
.search__mobile,
244+
.header__submenu {
245+
.form {
246+
// tablet sizes
247+
@media screen and (min-width: 360px) and (max-width: 768px) {
248+
&__control > .label .input {
249+
max-width: 100%;
250+
}
251+
}
252+
}
253+
}

0 commit comments

Comments
 (0)