From 59a7845ac4153345d5158cec45f8c69130dba561 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Tue, 28 Jan 2025 11:04:38 +0100 Subject: [PATCH] [A11y] Improve focus handling for widget overlay * move iframe after close-button to follow tab-order * add missing prevActiveElement * prepare focus-handling for error_message * iframe.src through prop instead of directly accessing it * do not change close button HTML-element for compatability * make all overlay elements role=dialog and modals * fix close button * fix re-opening of iframe * make error-message read out when shown * Improve handling of frame_src with frame_loading * manually focus continue or close button in alert-box * fix btn-focus in transition * Improve quantity group --- .../static/pretixpresale/js/widget/widget.js | 86 ++++++++++++------- 1 file changed, 56 insertions(+), 30 deletions(-) diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js index bd737a25e8..d3f99c803f 100644 --- a/src/pretix/static/pretixpresale/js/widget/widget.js +++ b/src/pretix/static/pretixpresale/js/widget/widget.js @@ -230,7 +230,7 @@ Vue.component('availbox', { + ' v-bind:aria-label="label_select_item"' + '>' + '' - + '
' + + '
' + '' + '' + '
' + '
' + '' + '
' + '
' + + '' + '' - + '' + '
' + '
' ); var shared_alert_fragment = ( - '
' - + '' + '
' + + '' + '
' + '

{{ $root.error_message }}

' + '

' @@ -966,8 +959,7 @@ Vue.component('pretix-overlay', { window.open(this.$root.error_url_after); return; } - var iframe = this.$refs['frame-container'].children[0]; - iframe.src = this.$root.error_url_after; + this.$root.overlay.frame_src = this.$root.error_url_after; this.$root.frame_loading = true; this.$root.error_message = null; this.$root.error_url_after = null; @@ -975,15 +967,22 @@ Vue.component('pretix-overlay', { close: function () { this.$root.frame_shown = false; this.$root.parent.frame_dismissed = true; + this.$root.frame_src = ""; this.$root.parent.reload(); this.$root.parent.trigger_close_callback(); }, iframeLoaded: function () { if (this.$root.frame_loading) { this.$root.frame_loading = false; - this.$root.frame_shown = true; + if (this.$root.frame_src) { + this.$root.frame_shown = true; + } } - } + }, + focusButton: function () { + this.$el.querySelector(".pretix-widget-alert-box button").focus(); + }, + } }); @@ -1738,8 +1737,7 @@ var shared_root_methods = { } else { url += '?iframe=1'; } - this.$root.overlay.$children[0].$refs['frame-container'].children[0].src = url; - this.$root.overlay.frame_loading = true; + this.$root.overlay.frame_src = url; } else { event.target.href = url; return; @@ -1902,9 +1900,7 @@ var shared_root_methods = { redirect_url += '&' + this.$root.additionalURLParams; } if (this.$root.useIframe) { - var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0]; - this.$root.overlay.frame_loading = true; - iframe.src = redirect_url; + this.$root.overlay.frame_src = redirect_url; } else { window.open(redirect_url); } @@ -1928,9 +1924,7 @@ var shared_root_methods = { redirect_url += '&' + this.$root.additionalURLParams; } if (this.$root.useIframe) { - var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0]; - this.$root.overlay.frame_loading = true; - iframe.src = redirect_url; + this.$root.overlay.frame_src = redirect_url; } else { window.open(redirect_url); } @@ -2067,9 +2061,41 @@ var create_overlay = function (app) { error_url_after_new_tab: true, error_message: null, lightbox: null, + prevActiveElement: null, } }, + props: { + frame_src: String, + }, methods: { + }, + watch: { + frame_src: function (newValue, oldValue) { + // show loading spinner only when previously no frame_src was set + if (newValue && !oldValue) { + this.frame_loading = true; + } + // to close and unload the iframe, frame_src can be empty -> make it valid HTML with about:blank + this.$el.querySelector("iframe").src = newValue || "about:blank"; + }, + frame_shown: function (newValue) { + if (newValue) { + this.prevActiveElement = document.activeElement; + var btn = this.$el?.querySelector(".pretix-widget-frame-close a"); + this.$nextTick(function () { + btn?.focus(); + }); + } else { + this.prevActiveElement?.focus(); + } + }, + error_message: function (newValue) { + if (newValue) { + this.prevActiveElement = document.activeElement; + } else { + this.prevActiveElement?.focus(); + } + }, } }); app.$root.overlay = framechild;