Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♿ [#2379] Search feedback notification for screenreaders #1280

Merged
merged 3 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/open_inwoner/cms/cases/tests/test_htmx.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ def mock_upload(request, context):
upload_form = page.locator("#document-upload")
file_input = upload_form.get_by_label("Sleep of selecteer bestanden")
submit_button = upload_form.get_by_role("button", name=_("Upload documenten"))
notification_list = page.get_by_role("alert").get_by_role("list")
notification_list = page.locator(".notification").get_by_role("list")
notification_list_items = notification_list.get_by_role("listitem")
file_list = page.get_by_role("list").last
file_list_items = file_list.get_by_role("listitem")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div class="notifications notifications__errors">
<div class="notification notification--warning">
{% icon icon="warning_amber" icon_position="before" outlined=True %}
<div class="notification__content">
<div class="notification__content" role="alert" tabindex="-1">
<p class="utrecht-paragraph utrecht-paragraph--oip utrecht-paragraph--oip-compact">{{ message.message }}</p>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
{% load i18n utils button_tags icon_tags button_tags icon_tags %}
<div class="notification{% if contents %} notification--contents{% endif %}{% if type %} notification--{{ type }}{% endif %}{% if compact %} notification--compact{% endif %} {% if ctx %}notification--{{ ctx }}{% endif %}" role="alert">
<div class="notification{% if contents %} notification--contents{% endif %}{% if type %} notification--{{ type }}{% endif %}{% if compact %} notification--compact{% endif %} {% if ctx %}notification--{{ ctx }}{% endif %}">
{% if not icon == False %}
<div class="notification__icon">
{% icon icon outlined=True %}
</div>
{% endif %}

<div class="notification__content">
<div class="notification__content"
{% if type == "error" %}
role="alert"
{% elif type == "warning" %}
role="alert"
{% else %}
role="status"
{% endif %} tabindex="-1">
{% if title %}<h2 class="utrecht-heading-2">{{ title }}</h2>{% endif %}
{% if notification %}<p class="utrecht-paragraph">{{ notification }}</p>{% endif %}
{% if action %}{% button href=action text=action_text %}{% endif %}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% load notification_tags string_tags %}

<div class="notifications">
<section class="notifications">
{% for message in messages %}
{% with as_markdown=message.extra_tags|is_substring:"as_markdown" %}
{% with local_message=message.extra_tags|is_substring:"local_message" %}
Expand All @@ -14,4 +14,4 @@
{% endwith %}
{% endwith %}
{% endfor %}
</div>
</section>
4 changes: 2 additions & 2 deletions src/open_inwoner/conf/locale/nl/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -6552,9 +6552,9 @@ msgstr "Zoekindex opnieuw opbouwen"
#: open_inwoner/search/tests/test_feedback.py:192
#: open_inwoner/search/views.py:158
msgid ""
"Thank you for your feedback. It will help us to improve our search engine"
"Thank you for your feedback, it will help us improve our search engine."
msgstr ""
"Dank u voor uw feedback, hiermee kunnen wij de omgeving verder verbeteren"
"Dank u voor uw feedback, hiermee kunnen wij de omgeving verder verbeteren."

#: open_inwoner/search/tests/test_logging.py:33 open_inwoner/search/views.py:63
#, python-brace-format
Expand Down
7 changes: 7 additions & 0 deletions src/open_inwoner/js/components/form/LoginForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@ export class LoginFormFocus {
}

hideLoginFormOnLoad() {
const notificationContent = this.loginFormColumn.querySelector(
'.notification__content'
)
if (this.loginFormColumn) {
this.loginFormColumn.classList.add('hide')
}
// Show form on error
if (notificationContent) {
this.loginFormColumn.classList.remove('hide')
}
}

addEmailToggleListener() {
Expand Down
95 changes: 85 additions & 10 deletions src/open_inwoner/js/components/notifications/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
const typeOrder = ['error', 'warning', 'success', 'info']

/**
* Helper function to determine the order index of a notification type.
* @param {HTMLElement} notification - The notification element.
* @returns {number} - Order index of the notification type.
*/
const getTypeOrderIndex = (notification) => {
const type = getTypeFromNotification(notification)
return typeOrder.indexOf(type)
}

/**
* Helper function to get the type of a notification.
* @param {HTMLElement} notification - The notification element.
* @returns {string} - Type of the notification.
*/
const getTypeFromNotification = (notification) => {
let notificationType = ''
notification.classList.forEach((cls) => {
if (cls.startsWith('notification--')) {
notificationType = cls.replace('notification--', '')
}
})
return notificationType
}

/**
* Notification class.
* Single Notification class.
* @class
*/
export class Notification {
Expand All @@ -14,6 +41,7 @@ export class Notification {
this.node = node

this.bindEvents()
this.reorderNotifications()
}

/**
Expand All @@ -25,16 +53,27 @@ export class Notification {
}

/**
* Scrolls to the notification content.
* Scrolls to the notification content and sets focus.
*/
scrollToNotification() {
const notificationContent = document.querySelector('.notification__content')
const notificationContents = Array.from(
this.node.querySelectorAll('.notification__content')
)

if (notificationContents) {
notificationContents.forEach((content) => {
// If errors are present, scroll and trigger the opened state
jiromaykin marked this conversation as resolved.
Show resolved Hide resolved
// The document.querySelectorAll method returns elements in the order they appear in the document,
// so the forEach method will create Notification instances in this same order.
content.scrollIntoView({
block: 'center',
behavior: 'smooth',
})

if (notificationContent) {
// If errors are present, scroll and trigger the opened state
notificationContent.scrollIntoView({
block: 'center',
behavior: 'smooth',
// Add a pause before setting focus for screen readers after DOM load
setTimeout(() => {
content.focus()
}, 100)
})
}
}
Expand All @@ -47,8 +86,6 @@ export class Notification {
e.preventDefault()
this.close()
})

this.scrollToNotification()
}

/**
Expand All @@ -57,6 +94,35 @@ export class Notification {
close() {
this.node.parentElement.removeChild(this.node)
}

/**
* Reorders notifications based on type.
*/
reorderNotifications() {
// Select the parent container, in order to re-index its children
const notificationsContainer = document.querySelector('.notifications')

if (notificationsContainer) {
const notifications = Array.from(
// Get first matching element in the document
notificationsContainer.querySelectorAll(Notification.selector)
)

// Re-indexing the NodeList: Sort notifications (children/siblings) based on type order
notifications.sort((a, b) => {
const typeA = getTypeOrderIndex(a)
const typeB = getTypeOrderIndex(b)
return typeA - typeB
})

// Re-append sorted notifications to parent container
notifications.forEach((notification) =>
notificationsContainer.appendChild(notification)
)
} else {
return
}
}
}

// Start!
Expand All @@ -65,3 +131,12 @@ export class Notification {
document
.querySelectorAll(Notification.selector)
.forEach((notification) => new Notification(notification))

// Scroll to the notifications after reordering
setTimeout(() => {
const firstNotification = document.querySelector(Notification.selector)
if (firstNotification) {
const instance = new Notification(firstNotification)
instance.scrollToNotification()
}
}, 0)
6 changes: 6 additions & 0 deletions src/open_inwoner/scss/components/Grid/Grid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
}
}
}

/// Search grid
&--search {
display: flex;
flex-direction: column;
}
}

&--limit &__sidebar,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@
& &__content {
margin-top: var(--spacing-tiny);

&:focus,
&:focus-visible {
outline: none;
border: none;
}

& * {
margin: 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
top: var(--spacing-extra-large);
display: flex;
flex-direction: column;
gap: var(--spacing-extra-large);
gap: var(--spacing-medium);
z-index: 1002;

/// Multiple errors.
Expand Down
2 changes: 1 addition & 1 deletion src/open_inwoner/search/tests/test_feedback.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,6 @@ def test_feedback_form_not_displayed_after_submit(self):
self.assertEqual(
message.message,
_(
"Thank you for your feedback. It will help us to improve our search engine"
"Thank you for your feedback, it will help us improve our search engine."
),
)
2 changes: 1 addition & 1 deletion src/open_inwoner/search/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def form_valid(self, form):
self.request,
messages.SUCCESS,
_(
"Thank you for your feedback. It will help us to improve our search engine"
"Thank you for your feedback, it will help us improve our search engine."
),
)
redirect = furl(reverse("search:search"))
Expand Down
2 changes: 1 addition & 1 deletion src/open_inwoner/templates/pages/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ <h2 class="utrecht-heading-2">{% trans "Zoekfilters" %}</h2>
{# end search filters #}
</aside>

<div class="grid__main">
<div class="grid__main grid__main--search">
{% if paginator.count %}
<div class="search-results">
<h2 class="utrecht-heading-2 search-results__title">
Expand Down
Loading