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

[DONE] Improve A11y of chapter edition form #938

Merged
merged 8 commits into from
Sep 8, 2023
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 pod/authentication/tests/test_populated.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ def _authenticate_shib_user(self, u):

@override_settings(DEBUG=False)
def test_make_profile(self):
"""Test if user attributes are retreived"""
"""Test if user attributes are retrieved."""
user, shib_meta = self._authenticate_shib_user(
{
"username": "[email protected]",
Expand Down
18 changes: 16 additions & 2 deletions pod/chapter/forms.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""Forms to create/edit and import Esup-Pod video chapter."""
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _

from pod.chapter.models import Chapter
from pod.chapter.utils import vtt_to_chapter
from pod.main.forms_utils import add_placeholder_and_asterisk
from pod.main.forms_utils import add_placeholder_and_asterisk, add_describedby_attr

if getattr(settings, "USE_PODFILE", False):
__FILEPICKER__ = True
Expand All @@ -17,7 +18,10 @@


class ChapterForm(forms.ModelForm):
"""A form to create/edit a video chapter."""

def __init__(self, *args, **kwargs):
"""Initialize fields."""
super(ChapterForm, self).__init__(*args, **kwargs)
self.fields["video"].widget = forms.HiddenInput()
self.fields["time_start"].widget.attrs["min"] = 0
Expand All @@ -28,16 +32,22 @@ def __init__(self, *args, **kwargs):
except Exception:
self.fields["time_start"].widget.attrs["max"] = 36000
self.fields = add_placeholder_and_asterisk(self.fields)
self.fields = add_describedby_attr(self.fields)

class Meta:
"""Form Metadata."""

model = Chapter
fields = "__all__"


class ChapterImportForm(forms.Form):
"""A form to import chapters from VTT file."""

file = forms.ModelChoiceField(queryset=CustomFileModel.objects.all())

def __init__(self, *args, **kwargs):
"""Initialize fields."""
self.user = kwargs.pop("user")
self.video = kwargs.pop("video")
super(ChapterImportForm, self).__init__(*args, **kwargs)
Expand All @@ -48,10 +58,14 @@ def __init__(self, *args, **kwargs):
)
else:
self.fields["file"].queryset = CustomFileModel.objects.all()
# self.fields = add_placeholder_and_asterisk(self.fields)
self.fields = add_describedby_attr(self.fields)
self.fields["file"].label = _("File to import")
self.fields["file"].help_text = _("The file must be in VTT format.")

def clean_file(self):
"""Convert VTT to chapters and return cleaned Data."""
msg = vtt_to_chapter(self.cleaned_data["file"], self.video)
if msg:
raise ValidationError("Error ! {0}".format(msg))
raise ValidationError("Error! {0}".format(msg))
return self.cleaned_data["file"]
1 change: 1 addition & 0 deletions pod/chapter/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def verify_title_items(self):
return list()

def verify_time(self):
"""Check that start time is included inside video duration."""
msg = list()
if (
self.time_start == ""
Expand Down
111 changes: 82 additions & 29 deletions pod/chapter/static/js/chapters.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,39 @@ function show_form(data) {
});
fadeIn(form_chapter);

var describedby_list = [];
let inputStart = document.getElementById("id_time_start");
if (inputStart) {
inputStart.insertAdjacentHTML(
"beforebegin",
"&nbsp;<span class='getfromvideo'><a id='getfromvideo_start' class='btn btn-primary btn-sm'>" +
gettext("Get time from the player") +
"</a><span class='timecode'>&nbsp;</span></span>",
"</a><span class='timecode' id='chapter_time_start'>&nbsp;</span></span>",
);

if(inputStart.getAttribute("aria-describedby")) {
describedby_list = inputStart.getAttribute("aria-describedby").split(" ");
}
if (describedby_list.indexOf("chapter_time_start") === -1){
describedby_list.push("chapter_time_start");
}
inputStart.setAttribute("aria-describedby", describedby_list.join(" "));
}
let inputEnd = document.getElementById("id_time_end");
if (inputEnd) {
inputEnd.insertAdjacentHTML(
"beforebegin",
"&nbsp;<span class='getfromvideo'><a id='getfromvideo_end' class='btn btn-primary btn-sm'>" +
gettext("Get time from the player") +
"</a><span class='timecode'>&nbsp;</span></span>",
"</a><span class='timecode' id='chapter_time_end'>&nbsp;</span></span>",
);
if(inputEnd.getAttribute("aria-describedby")) {
describedby_list = inputEnd.getAttribute("aria-describedby").split(" ");
}
if (describedby_list.indexOf("chapter_time_end") === -1){
describedby_list.push("chapter_time_end");
}
inputEnd.setAttribute("aria-describedby", describedby_list.join(" "));
}
}

Expand Down Expand Up @@ -208,53 +224,90 @@ var sendform = async function (elt, action) {

/*** Verify if value of field respect form field ***/
function verify_start_title_items() {
var ret = true;

// First, check Title field.

let inputTitle = document.getElementById("id_title");
inputTitle.classList.remove("is-invalid");
inputTitle.classList.remove("is-valid");
var describedby_list = [];
if(inputTitle.getAttribute("aria-describedby")) {
describedby_list = inputTitle.getAttribute("aria-describedby").split(" ");
}

var errormsg_id = "lengthErrorMsg";
if (
inputTitle.value === "" ||
inputTitle.value.length < 2 ||
inputTitle.value.length > 100
) {
if (typeof lengthErrorSpan === "undefined") {
lengthErrorSpan = document.createElement("span");
lengthErrorSpan.className = "form-help-inline";
lengthErrorSpan.innerHTML =
"&nbsp;&nbsp;" +
gettext("Please enter a title from 2 to 100 characters.");
inputTitle.insertAdjacentHTML("beforebegin", lengthErrorSpan.outerHTML);
if (typeof lengthErrorMsg === "undefined") {
lengthErrorMsg = document.createElement("div");
lengthErrorMsg.id = errormsg_id;
lengthErrorMsg.className = "invalid-feedback";
lengthErrorMsg.innerHTML = gettext("Please enter a title from 2 to 100 characters.");
inputTitle.insertAdjacentHTML("afterend", lengthErrorMsg.outerHTML);
inputTitle.parentNode.parentNode
.querySelectorAll("div.form-group")
.forEach(function (elt) {
elt.classList.add("has-error");
});
}
return false;
if (describedby_list.indexOf(errormsg_id) === -1){
describedby_list.push(errormsg_id);
}
inputTitle.classList.add("is-invalid");
ret = false;
} else {
describedby_list.pop(errormsg_id);
inputTitle.classList.add("is-valid");
}
inputTitle.setAttribute("aria-describedby", describedby_list.join(" "));

// Then check inputStart field.
let inputStart = document.getElementById("id_time_start");
inputStart.classList.remove("is-invalid");
inputStart.classList.remove("is-valid");

errormsg_id = "timeErrorMsg";
if(inputStart.getAttribute("aria-describedby")) {
describedby_list = inputStart.getAttribute("aria-describedby").split(" ");
} else {
describedby_list = [];
}
if (
inputStart.value === "" ||
inputStart.value < 0 ||
inputStart.value >= video_duration
) {
if (typeof timeErrorSpan === "undefined") {
timeErrorSpan = document.createElement("span");
timeErrorSpan.className = "form-help-inline";
timeErrorSpan.innerHTML =
"&nbsp;&nbsp;" +
if (typeof timeErrorMsg === "undefined") {
timeErrorMsg = document.createElement("div");
timeErrorMsg.id = errormsg_id;
timeErrorMsg.className = "invalid-feedback";
timeErrorMsg.innerHTML =
gettext("Please enter a correct start field between 0 and") +
" " +
(video_duration - 1);
inputStart.insertAdjacentHTML("beforebegin", timeErrorSpan.outerHTML);
" " + (video_duration - 1);
inputStart.insertAdjacentHTML("afterend", timeErrorMsg.outerHTML);
inputStart.setAttribute("aria-describedby", errormsg_id);
inputStart.parentNode.parentNode
.querySelectorAll("div.form-group")
.forEach(function (elt) {
elt.classList.add("has-error");
});
}
return false;
inputStart.classList.add("is-invalid");
if (describedby_list.indexOf(errormsg_id) === -1){
describedby_list.push(errormsg_id);
}
ret = false;
} else {
inputStart.classList.add("is-valid");
describedby_list.pop(errormsg_id);
}
timeErrorSpan = undefined;
lengthErrorSpan = undefined;
return true;
inputStart.setAttribute("aria-describedby", describedby_list.join(" "));

return ret;
}

function overlaptest() {
Expand Down Expand Up @@ -352,10 +405,10 @@ var updateDom = function (data) {

var manageSave = function () {
let player = window.videojs.players.podvideoplayer;
if (player.usingPlugin("videoJsChapters")) {
if (player.usingPlugin("podVideoJsChapters")) {
player.main();
} else {
player.videoJsChapters();
player.podVideoJsChapters();
}
};

Expand All @@ -366,25 +419,25 @@ var manageDelete = function () {
player.main();
} else {
player.controlBar.chapters.dispose();
player.videoJsChapters().dispose();
player.podVideoJsChapters().dispose();
}
};

var manageImport = function () {
let player = window.videojs.players.podvideoplayer;
let n = document.querySelector("div.chapters-list");
if (n != null) {
if (player.usingPlugin("videoJsChapters")) {
if (player.usingPlugin("podVideoJsChapters")) {
player.main();
} else {
player.videoJsChapters();
player.podVideoJsChapters();
}
} else {
if (typeof player.controlBar.chapters != "undefined") {
player.controlBar.chapters.dispose();
}
if (player.usingPlugin("videoJsChapters")) {
player.videoJsChapters().dispose();
if (player.usingPlugin("podVideoJsChapters")) {
player.podVideoJsChapters().dispose();
}
}
};
37 changes: 18 additions & 19 deletions pod/chapter/static/js/videojs-chapters.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
}

(function (window, videojs) {
var videoJsChapters,
defaults = {
var defaults = {
ui: true,
};

Expand All @@ -29,24 +28,24 @@
videojs.dom.addClass(span, "vjs-chapters-icon");
this.el().appendChild(span);
}
};
ChapterMenuButton.prototype.handleClick = function (event) {
MenuButton.prototype.handleClick.call(this, event);
if (document.querySelectorAll(".chapters-list.inactive li").length > 0) {
document
.querySelector(".chapters-list.inactive")
.setAttribute("class", "chapters-list active");
document.querySelector(".vjs-chapters-button button").style =
"text-shadow: 0 0 1em #fff";
} else {
document
.querySelector(".chapters-list.active")
.setAttribute("class", "chapters-list inactive");

document.querySelector(".vjs-chapters-button button").style =
"text-shadow: '' ";
handleClick(event) {
MenuButton.prototype.handleClick.call(this, event);
if (document.querySelectorAll(".chapters-list.inactive li").length > 0) {
document
.querySelector(".chapters-list.inactive")
.setAttribute("class", "chapters-list active");
document.querySelector(".vjs-chapters-button button").style =
"text-shadow: 0 0 1em #fff";
} else {
document
.querySelector(".chapters-list.active")
.setAttribute("class", "chapters-list inactive");

document.querySelector(".vjs-chapters-button button").style =
"text-shadow: '' ";
}
}
};
}
MenuButton.registerComponent("ChapterMenuButton", ChapterMenuButton);

/**
Expand Down
Loading