-
Notifications
You must be signed in to change notification settings - Fork 112
Fix accessibility issues in SGA #87
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -48,13 +48,19 @@ function StaffGradedAssignmentXBlock(runtime, element) { | |||||||
| return; | ||||||||
| } | ||||||||
| } | ||||||||
| var uploadElement = $(content).find(".upload"); | ||||||||
| uploadElement.attr("role", "progressbar"); | ||||||||
| uploadElement.attr("aria-valuemax", "100"); | ||||||||
| uploadElement.attr("aria-valuemin", "0"); | ||||||||
| data.submit(); | ||||||||
| }); | ||||||||
| }, | ||||||||
| progressall: function(e, data) { | ||||||||
| var percent = parseInt(data.loaded / data.total * 100, 10); | ||||||||
| $(content).find('.upload').text( | ||||||||
| 'Uploading... ' + percent + '%'); | ||||||||
| var uploadElement = $(content).find(".upload"); | ||||||||
| uploadElement.text("Uploading... " + percent + "%"); | ||||||||
| uploadElement.attr("aria-valuenow" , percent); | ||||||||
| uploadElement.attr("aria-valuetext" , "Uploading... " + percent + "%"); | ||||||||
| }, | ||||||||
| fail: function(e, data) { | ||||||||
| /** | ||||||||
|
|
@@ -100,8 +106,10 @@ function StaffGradedAssignmentXBlock(runtime, element) { | |||||||
| } | ||||||||
| } | ||||||||
| }); | ||||||||
|
|
||||||||
| updateChangeEvent(fileUpload); | ||||||||
| if (!_.isUndefined(state.error)) { | ||||||||
| $(content).find('p.error').focus(); | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| function renderStaffGrading(data) { | ||||||||
|
|
@@ -198,10 +206,13 @@ function StaffGradedAssignmentXBlock(runtime, element) { | |||||||
| event.preventDefault(); | ||||||||
| if (isNaN(score)) { | ||||||||
| form.find('.error').html('<br/>Grade must be a number.'); | ||||||||
| form.find('.error').focus(); | ||||||||
| } else if (score !== parseInt(score)) { | ||||||||
| form.find('.error').html('<br/>Grade must be an integer.'); | ||||||||
| form.find('.error').focus(); | ||||||||
| } else if (score < 0) { | ||||||||
| form.find('.error').html('<br/>Grade must be positive.'); | ||||||||
| form.find('.error').focus(); | ||||||||
| } else if (score > max_score) { | ||||||||
| form.find('.error').html('<br/>Maximum score is ' + max_score); | ||||||||
|
||||||||
| form.find('.error').html('<br/>Maximum score is ' + max_score); | |
| form.find('.error').html('<br/>Maximum score is ' + max_score); | |
| form.find('.error').focus(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,23 @@ | ||
| {% load i18n %} | ||
|
|
||
| <div id="staff-graded-assignment-edit"> | ||
| <ul class="list-input settings-list"> | ||
| <div id="staff-graded-assignment-edit" role="form"> | ||
|
||
| <div class="list-input settings-list"> | ||
| {% for field, value, validator in fields %} | ||
| <li class="field comp-setting-entry metadata_entry"> | ||
| <div class="field comp-setting-entry metadata_entry"> | ||
| <div class="wrapper-comp-setting"> | ||
| <label class="label setting-label" | ||
| <label class="label setting-label" | ||
| for="{{ field.name }}_input">{{ field.display_name }}</label> | ||
| <input class="input setting-input" type="text" | ||
| id="{{ field.name }}_input" name="{{ field.name}}" | ||
| <input class="input setting-input" type="text" aria-describedby="{{ field.name }}_hint" | ||
| id="{{ field.name }}_input" name="{{ field.name}}" | ||
| value="{{ value }}" data-validator="{{ validator }}"/> | ||
| </div> | ||
| <span class="tip setting-help">{{ field.help }}</span> | ||
| </li> | ||
| <span id="{{ field.name }}_hint" class="tip setting-help">{{ field.help }}</span> | ||
| </div> | ||
|
Comment on lines
+4
to
+15
|
||
| {% endfor %} | ||
| </ul> | ||
| </div> | ||
| </div> | ||
|
|
||
| <script type="application/javascript"> | ||
| $("ul.action-modes").html(' <li class="view-button sga_editor_tab">{% trans "Settings" %}</li>') | ||
| $("ul.action-modes").html(' <li class="view-button sga_editor_tab" aria-label="settings">{% trans "Settings" %}</li>') | ||
| $("h2.modal-window-title").attr("tabindex","0"); | ||
| </script> | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,14 +1,15 @@ | ||||||||||
| {% load i18n %} | ||||||||||
|
|
||||||||||
| <div class="sga-block" data-state="{{ student_state }}" data-max-size="{{ max_file_size }}" | ||||||||||
| data-staff="{{ is_course_staff }}"> | ||||||||||
| data-staff="{{ is_course_staff }}" role="article" aria-describedby="title"> | ||||||||||
|
||||||||||
| <script type="text/template" id="sga-tmpl"> | ||||||||||
| <% if (display_name) { %> | ||||||||||
| <b><%= display_name %></b> | ||||||||||
| <b id="title"><%= display_name %></b> | ||||||||||
| <% } %> | ||||||||||
| <% if (uploaded) { %> | ||||||||||
| <p><b>File uploaded</b> | ||||||||||
| <a href="<%= downloadUrl %>"><%= uploaded.filename %></a></p> | ||||||||||
| <br/> | ||||||||||
| <label for="file_uploaded"><b>File uploaded<span class="sr"> download link</span></b></label> | ||||||||||
| <a href="<%= downloadUrl %>" id="file_uploaded"><%= uploaded.filename %></a> | ||||||||||
|
Comment on lines
+11
to
+12
|
||||||||||
| <label for="file_uploaded"><b>File uploaded<span class="sr"> download link</span></b></label> | |
| <a href="<%= downloadUrl %>" id="file_uploaded"><%= uploaded.filename %></a> | |
| <span><b>File uploaded<span class="sr"> download link</span></b></span> | |
| <a href="<%= downloadUrl %>" id="file_uploaded" aria-label="Download uploaded file: <%= uploaded.filename %>"><%= uploaded.filename %></a> |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The role="alert", aria-live="assertive", and tabindex="-1" attributes are misapplied to this upload container. The role="alert" is meant for important, time-sensitive messages that should interrupt the user, not for static UI containers. The upload div doesn't receive programmatic focus in the JavaScript code (only error elements do), making tabindex="-1" unnecessary. Remove these ARIA attributes from this div - they should only be applied to actual dynamic status/error message elements.
| <div class="upload" aria-describedby="upload_label" role="alert" aria-live="assertive" tabindex="-1"> | |
| <div class="upload" aria-describedby="upload_label"> |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Links should not have role="button". This creates confusion for assistive technology users as the element is semantically a link (using an anchor tag with href). If this needs to behave like a button, consider using an actual <button> element instead. Otherwise, keep it as a link without the role attribute.
| <a class="enter-grade-button button" href="#{{ id }}-enter-grade" role="button"> | |
| <a class="enter-grade-button button" href="#{{ id }}-enter-grade"> |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar issue as line 36: role="alert", aria-live="assertive", and tabindex="-1" are incorrectly applied to this static upload container. These ARIA attributes should only be used for dynamic announcements and status messages, not for the container element itself. Remove these attributes from this div.
| <div class="upload" aria-live="assertive" role="alert" tabindex="-1"> | |
| <div class="upload"> |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The role="banner" attribute is incorrectly used here. The banner role should be used for the main header of the page/site, not for modal headers. Each page should typically have only one banner landmark. This header is within a modal dialog and should not use the banner role. Consider removing this role attribute.
| <header role="banner"><h2>{% trans "Staff Debug" %}</h2></header> | |
| <header><h2>{% trans "Staff Debug" %}</h2></header> |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The table has only one column header but two columns of data (name and field value). The <thead> should have a <th> for each column to properly describe the table structure for screen readers. Add a second <th> for the field value column, or consider restructuring as a definition list if this is meant to be a list of name-value pairs.
| <th>{% trans "Module Fields" %}</th> | |
| <th>{% trans "Field Name" %}</th> | |
| <th>{% trans "Field Value" %}</th> |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Extra whitespace before the class attribute. Should be <section class="modal grade-modal" instead of <section class="modal grade-modal" (note the double space).
| <section class="modal grade-modal" id="{{ id }}-enter-grade"> | |
| <section class="modal grade-modal" id="{{ id }}-enter-grade"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The progressbar role is being set on the upload element, but this element's purpose changes during the upload lifecycle. Initially it's a container for the file input and button, then it becomes a progress indicator. Dynamically changing an element's role can confuse assistive technologies. Consider creating a separate element specifically for the progress indicator with the progressbar role, or ensure this role is only set when actually showing progress and removed afterwards.