[A11y] Fix sneak-peek for cart (#5076)

This commit is contained in:
Richard Schreiber
2025-05-09 08:38:34 +02:00
committed by GitHub
parent 2b735bec0b
commit 7472564c26
3 changed files with 65 additions and 40 deletions

View File

@@ -13,7 +13,7 @@
{% endblock %}
{% block content %}
<aside aria-label="{% trans "Your cart" %}">
<details class="panel panel-default cart{% if "open_cart" not in request.GET %} sneak-peek{% endif %}" {% if "open_cart" in request.GET %}open{% endif %}>
<details class="panel panel-default cart sneak-peek-container" open>
<summary class="panel-heading">
<h2 class="panel-title">
<span>
@@ -33,11 +33,15 @@
</summary>
{% if "open_cart" not in request.GET %}
<p class="sneak-peek-trigger">
<button type="button" class="btn btn-default">{% trans "Show full cart" %}</button>
<button type="button" class="btn btn-default" aria-controls="cart-foldable-container">{% trans "Show full cart" %}</button>
</p>
{% endif %}
<div>
{% if "open_cart" not in request.GET %}
<div class="panel-body sneak-peek-content" id="cart-foldable-container">
{% else %}
<div class="panel-body">
{% endif %}
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event %}
</div>
</div>

View File

@@ -2,39 +2,56 @@
setup_collapsible_details = function (el) {
el.find('details.sneak-peek:not([open])').each(function() {
this.open = true;
var $elements = $("> :not(summary)", this).show().filter(':not(.sneak-peek-trigger)');
var container = this;
if (Array.prototype.reduce.call($elements, function (h, e) {
return h + $(e).outerHeight();
}, 0) < 200) {
$(".sneak-peek-trigger", this).remove();
$(container).removeClass('sneak-peek');
container.style.removeProperty('height');
el.find('.sneak-peek-trigger').each(function() {
var trigger = this;
var button = this.querySelector('button');
var content = document.getElementById(button.getAttribute('aria-controls'));
if (content.scrollHeight < 200) {
trigger.remove();
content.classList.remove('sneak-peek-content');
return;
}
content.setAttribute('aria-hidden', 'true');
button.setAttribute('aria-expanded', 'false');
button.addEventListener('click', function (e) {
button.setAttribute('aria-expanded', 'true');
content.setAttribute('aria-hidden', 'false');
$elements.attr('aria-hidden', 'true');
var trigger = $('summary, .sneak-peek-trigger button', container);
function onclick(e) {
e.preventDefault();
container.addEventListener('transitionend', function() {
$(container).removeClass('sneak-peek');
container.style.removeProperty('height');
content.addEventListener('transitionend', function() {
content.classList.remove('sneak-peek-content');
content.style.removeProperty('height');
// we need to keep the trigger/button in the DOM to not irritate screenreaders toggling visibility
trigger.classList.add('sr-only');
}, {once: true});
container.style.height = container.scrollHeight + 'px';
$('.sneak-peek-trigger', container).fadeOut(function() {
$(this).remove();
});
$elements.removeAttr('aria-hidden');
content.style.height = content.scrollHeight + 'px';
trigger.off('click', onclick);
button.addEventListener('click', function (e) {
// this will be called by screenreader users if they kept focus on the button after expanding
// we need to keep the trigger/button in the DOM to not irritate screenreaders toggling visibility
var expanded = button.getAttribute('aria-expanded') == 'true';
button.setAttribute('aria-expanded', !expanded);
content.setAttribute('aria-hidden', expanded);
});
button.addEventListener('blur', function (e) {
// if content is visible and the user leaves the button, we can safely remove the trigger/button
if (button.getAttribute('aria-expanded') == 'true') {
trigger.remove();
}
});
}, { once: true });
var container = this.closest('details.sneak-peek-container');
if (container) {
function removeSneekPeakWhenClosed(e) {
if (e.newState == "closed") {
container.removeEventListener("toggle", removeSneekPeakWhenClosed);
trigger.remove();
content.removeAttribute('aria-hidden');
content.classList.remove('sneak-peek-content');
}
}
container.addEventListener("toggle", removeSneekPeakWhenClosed);
}
trigger.on('click', onclick);
});
var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
@@ -43,10 +60,6 @@ setup_collapsible_details = function (el) {
return true;
}
var $details = $(this).closest("details");
if ($details.hasClass('sneak-peek')) {
// if sneak-peek is active, needs to be handled differently
return true;
}
var isOpen = $details.prop("open");
var $detailsNotSummary = $details.children(':not(summary)');
if ($detailsNotSummary.is(':animated')) {
@@ -70,11 +83,6 @@ setup_collapsible_details = function (el) {
if (32 == event.keyCode || (13 == event.keyCode && !isOpera)) {
// Space or Enter is pressed — trigger the `click` event on the `summary` element
// Opera already seems to trigger the `click` event when Enter is pressed
var $details = $(this).closest("details");
if ($details.hasClass('sneak-peek')) {
// if sneak-peek is active, needs to be handled differently
return true;
}
event.preventDefault();
$(this).click();
}

View File

@@ -521,12 +521,17 @@ details summary {
transform: rotate(-90deg);
}
details.sneak-peek {
.sneak-peek-container {
position: relative;
height: 11em;
}
.sneak-peek-content {
height: 8em;
overflow: hidden;
transition: height .5s;
}
.nojs .sneak-peek-content {
height: auto;
}
.sneak-peek-trigger {
display: grid;
justify-content: center;
@@ -540,6 +545,14 @@ details.sneak-peek {
bottom: 0;
left: 0;
z-index: 10;
opacity: 1;
transition: opacity .5s;
}
.nojs .sneak-peek-trigger {
display: none;
}
.sneak-peek-trigger:has(button[aria-expanded="true"]) {
opacity: 0;
}
form.download-btn-form {