forked from CGM_Public/pretix_original
[A11y] Improve focus management for widget overlay
This commit is contained in:
committed by
GitHub
parent
6c7041c875
commit
1c8bcca846
@@ -77,6 +77,13 @@ var strings = {
|
|||||||
'FR': django.gettext('Fr'),
|
'FR': django.gettext('Fr'),
|
||||||
'SA': django.gettext('Sa'),
|
'SA': django.gettext('Sa'),
|
||||||
'SU': django.gettext('Su'),
|
'SU': django.gettext('Su'),
|
||||||
|
'MONDAY': django.gettext('Monday'),
|
||||||
|
'TUESDAY': django.gettext('Tuesday'),
|
||||||
|
'WEDNESDAY': django.gettext('Wednesday'),
|
||||||
|
'THURSDAY': django.gettext('Thursday'),
|
||||||
|
'FRIDAY': django.gettext('Friday'),
|
||||||
|
'SATURDAY': django.gettext('Saturday'),
|
||||||
|
'SUNDAY': django.gettext('Sunday'),
|
||||||
},
|
},
|
||||||
'months': {
|
'months': {
|
||||||
'01': django.gettext('January'),
|
'01': django.gettext('January'),
|
||||||
@@ -964,10 +971,10 @@ Vue.component('pretix-widget-event-form', {
|
|||||||
template: ('<div class="pretix-widget-event-form">'
|
template: ('<div class="pretix-widget-event-form">'
|
||||||
// Back navigation
|
// Back navigation
|
||||||
+ '<div class="pretix-widget-event-list-back" v-if="$root.events || $root.weeks || $root.days">'
|
+ '<div class="pretix-widget-event-list-back" v-if="$root.events || $root.weeks || $root.days">'
|
||||||
+ '<a href="#" @click.prevent.stop="back_to_list" v-if="!$root.subevent">‹ '
|
+ '<a href="#" rel="back" @click.prevent.stop="back_to_list" v-if="!$root.subevent">‹ '
|
||||||
+ strings['back_to_list']
|
+ strings['back_to_list']
|
||||||
+ '</a>'
|
+ '</a>'
|
||||||
+ '<a href="#" @click.prevent.stop="back_to_list" v-if="$root.subevent">‹ '
|
+ '<a href="#" rel="back" @click.prevent.stop="back_to_list" v-if="$root.subevent">‹ '
|
||||||
+ strings['back_to_dates']
|
+ strings['back_to_dates']
|
||||||
+ '</a>'
|
+ '</a>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
@@ -1119,6 +1126,11 @@ Vue.component('pretix-widget-event-form', {
|
|||||||
back_to_list: function() {
|
back_to_list: function() {
|
||||||
this.$root.target_url = this.$root.parent_stack.pop();
|
this.$root.target_url = this.$root.parent_stack.pop();
|
||||||
this.$root.error = null;
|
this.$root.error = null;
|
||||||
|
if (!this.$root.subevent) {
|
||||||
|
// reset if we are not in a series
|
||||||
|
this.$root.name = null;
|
||||||
|
this.$root.frontpage_text = null;
|
||||||
|
}
|
||||||
this.$root.subevent = null;
|
this.$root.subevent = null;
|
||||||
this.$root.offset = 0;
|
this.$root.offset = 0;
|
||||||
this.$root.append_events = false;
|
this.$root.append_events = false;
|
||||||
@@ -1130,6 +1142,12 @@ Vue.component('pretix-widget-event-form', {
|
|||||||
} else {
|
} else {
|
||||||
this.$root.view = "weeks";
|
this.$root.view = "weeks";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var $el = this.$root.$el;
|
||||||
|
this.$root.$nextTick(function() {
|
||||||
|
// wait for redraw, then focus content element for better a11y
|
||||||
|
$el.focus();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
calculate_buy_disabled: function() {
|
calculate_buy_disabled: function() {
|
||||||
var i, j, k;
|
var i, j, k;
|
||||||
@@ -1197,7 +1215,7 @@ Vue.component('pretix-widget-event-list-filter-form', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Vue.component('pretix-widget-event-list-entry', {
|
Vue.component('pretix-widget-event-list-entry', {
|
||||||
template: ('<a :class="classObject" @click.prevent.stop="select">'
|
template: ('<a href="#" :class="classObject" @click.prevent.stop="select">'
|
||||||
+ '<div class="pretix-widget-event-list-entry-name">{{ event.name }}</div>'
|
+ '<div class="pretix-widget-event-list-entry-name">{{ event.name }}</div>'
|
||||||
+ '<div class="pretix-widget-event-list-entry-date">{{ event.date_range }}</div>'
|
+ '<div class="pretix-widget-event-list-entry-date">{{ event.date_range }}</div>'
|
||||||
+ '<div class="pretix-widget-event-list-entry-location">{{ location }}</div>' // hidden by css for now, but
|
+ '<div class="pretix-widget-event-list-entry-location">{{ location }}</div>' // hidden by css for now, but
|
||||||
@@ -1237,7 +1255,7 @@ Vue.component('pretix-widget-event-list-entry', {
|
|||||||
Vue.component('pretix-widget-event-list', {
|
Vue.component('pretix-widget-event-list', {
|
||||||
template: ('<div class="pretix-widget-event-list">'
|
template: ('<div class="pretix-widget-event-list">'
|
||||||
+ '<div class="pretix-widget-back" v-if="$root.weeks || $root.parent_stack.length > 0">'
|
+ '<div class="pretix-widget-back" v-if="$root.weeks || $root.parent_stack.length > 0">'
|
||||||
+ '<a href="#" @click.prevent.stop="back_to_calendar" role="button">‹ '
|
+ '<a href="#" rel="prev" @click.prevent.stop="back_to_calendar">‹ '
|
||||||
+ strings['back']
|
+ strings['back']
|
||||||
+ '</a>'
|
+ '</a>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
@@ -1256,6 +1274,10 @@ Vue.component('pretix-widget-event-list', {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
back_to_calendar: function () {
|
back_to_calendar: function () {
|
||||||
|
// make sure to always focus content element
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.$root.$el.focus();
|
||||||
|
});
|
||||||
this.$root.offset = 0;
|
this.$root.offset = 0;
|
||||||
this.$root.append_events = false;
|
this.$root.append_events = false;
|
||||||
if (this.$root.weeks) {
|
if (this.$root.weeks) {
|
||||||
@@ -1280,7 +1302,7 @@ Vue.component('pretix-widget-event-list', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Vue.component('pretix-widget-event-calendar-event', {
|
Vue.component('pretix-widget-event-calendar-event', {
|
||||||
template: ('<a :class="classObject" @click.prevent.stop="select">'
|
template: ('<a href="#" :class="classObject" @click.prevent.stop="select" v-bind:aria-describedby="describedby">'
|
||||||
+ '<strong class="pretix-widget-event-calendar-event-name">'
|
+ '<strong class="pretix-widget-event-calendar-event-name">'
|
||||||
+ '{{ event.name }}'
|
+ '{{ event.name }}'
|
||||||
+ '</strong>'
|
+ '</strong>'
|
||||||
@@ -1288,7 +1310,8 @@ Vue.component('pretix-widget-event-calendar-event', {
|
|||||||
+ '<div class="pretix-widget-event-calendar-event-availability" v-if="!event.continued && event.availability.text">{{ event.availability.text }}</div>'
|
+ '<div class="pretix-widget-event-calendar-event-availability" v-if="!event.continued && event.availability.text">{{ event.availability.text }}</div>'
|
||||||
+ '</a>'),
|
+ '</a>'),
|
||||||
props: {
|
props: {
|
||||||
event: Object
|
event: Object,
|
||||||
|
describedby: String,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
classObject: function () {
|
classObject: function () {
|
||||||
@@ -1316,11 +1339,11 @@ Vue.component('pretix-widget-event-calendar-event', {
|
|||||||
|
|
||||||
Vue.component('pretix-widget-event-week-cell', {
|
Vue.component('pretix-widget-event-week-cell', {
|
||||||
template: ('<div :class="classObject" @click.prevent.stop="selectDay">'
|
template: ('<div :class="classObject" @click.prevent.stop="selectDay">'
|
||||||
+ '<div class="pretix-widget-event-calendar-day" v-if="day">'
|
+ '<div class="pretix-widget-event-calendar-day" v-if="day" :id="id">'
|
||||||
+ '{{ dayhead }}'
|
+ '{{ dayhead }}'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div class="pretix-widget-event-calendar-events" v-if="day">'
|
+ '<div class="pretix-widget-event-calendar-events" v-if="day">'
|
||||||
+ '<pretix-widget-event-calendar-event v-for="e in day.events" :event="e"></pretix-widget-event-calendar-event>'
|
+ '<pretix-widget-event-calendar-event v-for="e in day.events" :event="e" :describedby="id"></pretix-widget-event-calendar-event>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '</div>'),
|
+ '</div>'),
|
||||||
props: {
|
props: {
|
||||||
@@ -1346,6 +1369,9 @@ Vue.component('pretix-widget-event-week-cell', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
id: function () {
|
||||||
|
return this.day ? this.$root.html_id + '-' + this.day.date : '';
|
||||||
|
},
|
||||||
dayhead: function () {
|
dayhead: function () {
|
||||||
if (!this.day) {
|
if (!this.day) {
|
||||||
return;
|
return;
|
||||||
@@ -1381,7 +1407,7 @@ Vue.component('pretix-widget-event-week-cell', {
|
|||||||
|
|
||||||
Vue.component('pretix-widget-event-calendar-cell', {
|
Vue.component('pretix-widget-event-calendar-cell', {
|
||||||
template: ('<td :class="classObject" @click.prevent.stop="selectDay">'
|
template: ('<td :class="classObject" @click.prevent.stop="selectDay">'
|
||||||
+ '<div class="pretix-widget-event-calendar-day" v-if="day">'
|
+ '<div class="pretix-widget-event-calendar-day" v-if="day" v-bind:aria-label="date">'
|
||||||
+ '{{ daynum }}'
|
+ '{{ daynum }}'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div class="pretix-widget-event-calendar-events" v-if="day">'
|
+ '<div class="pretix-widget-event-calendar-events" v-if="day">'
|
||||||
@@ -1417,6 +1443,9 @@ Vue.component('pretix-widget-event-calendar-cell', {
|
|||||||
}
|
}
|
||||||
return this.day.date.substr(8);
|
return this.day.date.substr(8);
|
||||||
},
|
},
|
||||||
|
date: function () {
|
||||||
|
return this.day ? (new Date(this.day.date)).toLocaleDateString() : '';
|
||||||
|
},
|
||||||
classObject: function () {
|
classObject: function () {
|
||||||
var o = {};
|
var o = {};
|
||||||
if (this.day && this.day.events.length > 0) {
|
if (this.day && this.day.events.length > 0) {
|
||||||
@@ -1474,26 +1503,26 @@ Vue.component('pretix-widget-event-calendar', {
|
|||||||
|
|
||||||
// Calendar navigation
|
// Calendar navigation
|
||||||
+ '<div class="pretix-widget-event-calendar-head">'
|
+ '<div class="pretix-widget-event-calendar-head">'
|
||||||
+ '<a class="pretix-widget-event-calendar-previous-month" href="#" @click.prevent.stop="prevmonth" role="button">« '
|
+ '<a class="pretix-widget-event-calendar-previous-month" href="#" @click.prevent.stop="prevmonth">« '
|
||||||
+ strings['previous_month']
|
+ strings['previous_month']
|
||||||
+ '</a> '
|
+ '</a> '
|
||||||
+ '<strong>{{ monthname }}</strong> '
|
+ '<strong>{{ monthname }}</strong> '
|
||||||
+ '<a class="pretix-widget-event-calendar-next-month" href="#" @click.prevent.stop="nextmonth" role="button">'
|
+ '<a class="pretix-widget-event-calendar-next-month" href="#" @click.prevent.stop="nextmonth">'
|
||||||
+ strings['next_month']
|
+ strings['next_month']
|
||||||
+ ' »</a>'
|
+ ' »</a>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
|
|
||||||
// Calendar
|
// Calendar
|
||||||
+ '<table class="pretix-widget-event-calendar-table">'
|
+ '<table class="pretix-widget-event-calendar-table" :id="id" tabindex="0" v-bind:aria-label="monthname">'
|
||||||
+ '<thead>'
|
+ '<thead>'
|
||||||
+ '<tr>'
|
+ '<tr>'
|
||||||
+ '<th>' + strings['days']['MO'] + '</th>'
|
+ '<th aria-label="' + strings['days']['MONDAY'] + '">' + strings['days']['MO'] + '</th>'
|
||||||
+ '<th>' + strings['days']['TU'] + '</th>'
|
+ '<th aria-label="' + strings['days']['TUESDAY'] + '">' + strings['days']['TU'] + '</th>'
|
||||||
+ '<th>' + strings['days']['WE'] + '</th>'
|
+ '<th aria-label="' + strings['days']['WEDNESDAY'] + '">' + strings['days']['WE'] + '</th>'
|
||||||
+ '<th>' + strings['days']['TH'] + '</th>'
|
+ '<th aria-label="' + strings['days']['THURSDAY'] + '">' + strings['days']['TH'] + '</th>'
|
||||||
+ '<th>' + strings['days']['FR'] + '</th>'
|
+ '<th aria-label="' + strings['days']['FRIDAY'] + '">' + strings['days']['FR'] + '</th>'
|
||||||
+ '<th>' + strings['days']['SA'] + '</th>'
|
+ '<th aria-label="' + strings['days']['SATURDAY'] + '">' + strings['days']['SA'] + '</th>'
|
||||||
+ '<th>' + strings['days']['SU'] + '</th>'
|
+ '<th aria-label="' + strings['days']['SUNDAY'] + '">' + strings['days']['SU'] + '</th>'
|
||||||
+ '</tr>'
|
+ '</tr>'
|
||||||
+ '</thead>'
|
+ '</thead>'
|
||||||
+ '<tbody>'
|
+ '<tbody>'
|
||||||
@@ -1507,7 +1536,10 @@ Vue.component('pretix-widget-event-calendar', {
|
|||||||
},
|
},
|
||||||
monthname: function () {
|
monthname: function () {
|
||||||
return strings['months'][this.$root.date.substr(5, 2)] + ' ' + this.$root.date.substr(0, 4);
|
return strings['months'][this.$root.date.substr(5, 2)] + ' ' + this.$root.date.substr(0, 4);
|
||||||
}
|
},
|
||||||
|
id: function () {
|
||||||
|
return this.$root.html_id + "-event-calendar-table";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
back_to_list: function () {
|
back_to_list: function () {
|
||||||
@@ -1526,7 +1558,7 @@ Vue.component('pretix-widget-event-calendar', {
|
|||||||
}
|
}
|
||||||
this.$root.date = String(curYear) + "-" + padNumber(curMonth, 2) + "-01";
|
this.$root.date = String(curYear) + "-" + padNumber(curMonth, 2) + "-01";
|
||||||
this.$root.loading++;
|
this.$root.loading++;
|
||||||
this.$root.reload();
|
this.$root.reload({focus: '#'+this.id});
|
||||||
},
|
},
|
||||||
nextmonth: function () {
|
nextmonth: function () {
|
||||||
var curMonth = parseInt(this.$root.date.substr(5, 2));
|
var curMonth = parseInt(this.$root.date.substr(5, 2));
|
||||||
@@ -1538,7 +1570,7 @@ Vue.component('pretix-widget-event-calendar', {
|
|||||||
}
|
}
|
||||||
this.$root.date = String(curYear) + "-" + padNumber(curMonth, 2) + "-01";
|
this.$root.date = String(curYear) + "-" + padNumber(curMonth, 2) + "-01";
|
||||||
this.$root.loading++;
|
this.$root.loading++;
|
||||||
this.$root.reload();
|
this.$root.reload({focus: '#'+this.id});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1573,7 +1605,7 @@ Vue.component('pretix-widget-event-week-calendar', {
|
|||||||
+ '</div>'
|
+ '</div>'
|
||||||
|
|
||||||
// Actual calendar
|
// Actual calendar
|
||||||
+ '<div class="pretix-widget-event-week-table">'
|
+ '<div class="pretix-widget-event-week-table" :id="id" tabindex="0" v-bind:aria-label="weekname">'
|
||||||
+ '<div class="pretix-widget-event-week-col" v-for="d in $root.days">'
|
+ '<div class="pretix-widget-event-week-col" v-for="d in $root.days">'
|
||||||
+ '<pretix-widget-event-week-cell :day="d">'
|
+ '<pretix-widget-event-week-cell :day="d">'
|
||||||
+ '</pretix-widget-event-week-cell>'
|
+ '</pretix-widget-event-week-cell>'
|
||||||
@@ -1591,6 +1623,9 @@ Vue.component('pretix-widget-event-week-calendar', {
|
|||||||
var curYear = this.$root.week[0];
|
var curYear = this.$root.week[0];
|
||||||
return curWeek + ' / ' + curYear;
|
return curWeek + ' / ' + curYear;
|
||||||
},
|
},
|
||||||
|
id: function () {
|
||||||
|
return this.$root.html_id + "-event-week-table";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
back_to_list: function () {
|
back_to_list: function () {
|
||||||
@@ -1609,7 +1644,7 @@ Vue.component('pretix-widget-event-week-calendar', {
|
|||||||
}
|
}
|
||||||
this.$root.week = [curYear, curWeek];
|
this.$root.week = [curYear, curWeek];
|
||||||
this.$root.loading++;
|
this.$root.loading++;
|
||||||
this.$root.reload();
|
this.$root.reload({focus: '#'+this.id});
|
||||||
},
|
},
|
||||||
nextweek: function () {
|
nextweek: function () {
|
||||||
var curWeek = this.$root.week[1];
|
var curWeek = this.$root.week[1];
|
||||||
@@ -1621,13 +1656,13 @@ Vue.component('pretix-widget-event-week-calendar', {
|
|||||||
}
|
}
|
||||||
this.$root.week = [curYear, curWeek];
|
this.$root.week = [curYear, curWeek];
|
||||||
this.$root.loading++;
|
this.$root.loading++;
|
||||||
this.$root.reload();
|
this.$root.reload({focus: '#'+this.id});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Vue.component('pretix-widget', {
|
Vue.component('pretix-widget', {
|
||||||
template: ('<div class="pretix-widget-wrapper" ref="wrapper">'
|
template: ('<div class="pretix-widget-wrapper" ref="wrapper" tabindex="0" role="article" v-bind:aria-label="$root.name">'
|
||||||
+ '<div :class="classObject">'
|
+ '<div :class="classObject">'
|
||||||
+ shared_loading_fragment
|
+ shared_loading_fragment
|
||||||
+ '<div class="pretix-widget-error-message" v-if="$root.error && $root.view !== \'event\'">{{ $root.error }}</div>'
|
+ '<div class="pretix-widget-error-message" v-if="$root.error && $root.view !== \'event\'">{{ $root.error }}</div>'
|
||||||
@@ -1737,7 +1772,7 @@ var shared_root_methods = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
reload: function () {
|
reload: function (opt = {}) {
|
||||||
var url;
|
var url;
|
||||||
if (this.$root.is_button) {
|
if (this.$root.is_button) {
|
||||||
return;
|
return;
|
||||||
@@ -1854,6 +1889,15 @@ var shared_root_methods = {
|
|||||||
// If we're on desktop and someone selects a seating-only event in a calendar, let's open it right away,
|
// If we're on desktop and someone selects a seating-only event in a calendar, let's open it right away,
|
||||||
// but only if the person didn't close it before.
|
// but only if the person didn't close it before.
|
||||||
root.startseating()
|
root.startseating()
|
||||||
|
} else {
|
||||||
|
// make sure to only move focus to content element when it had focus before the reload/click
|
||||||
|
// this is needed because reload is also called on initial load and we do not want to move focus on initial load
|
||||||
|
if (root.$el.contains(document.activeElement)) {
|
||||||
|
root.$nextTick(function() {
|
||||||
|
// wait for redraw, then focus content element for better a11y
|
||||||
|
(opt.focus ? document.querySelector(opt.focus) : root.$el).focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
root.categories = [];
|
root.categories = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user