Skip to content

Commit

Permalink
Merge pull request #39 from indiana-university/LMSA-9483
Browse files Browse the repository at this point in the history
LMSA-9483 a11y changes
  • Loading branch information
gjthomas authored Nov 18, 2024
2 parents d1a8926 + a47d574 commit d3e1bc0
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public interface CrosslistConstants {
String LOOKUP_FAILURE_COURSE_NOT_CROSSLISTED_MESSAGE = "This course has not been crosslisted";
String LOOKUP_FAILURE_NOT_FOUND_IN_CANVAS_MESSAGE = "Not found in Canvas";
String LOOKUP_FAILURE_NOT_FOUND_IN_SIS_MESSAGE = "Not found in SIS";
String LOOKUP_FAILURE_MISSING_SIS_ID = "SIS Section ID is required.";

String LOOKUP_SUCCESS_CSS = "rvt-color-green rvt-bg-green-100";
String LOOKUP_FAILURE_CSS = "rvt-orange-green rvt-bg-orange-100";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import uk.ac.ox.ctl.lti13.security.oauth2.client.lti.authentication.OidcAuthenticationToken;

import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

Expand Down Expand Up @@ -102,10 +103,33 @@ public String lookupSearchBySisId(@ModelAttribute FindParentModel findParentMode
String sisIdSearch = findParentModel.getSisIdSearch().trim();
findParentResult = crosslistService.processSisLookup(sisIdSearch);

List<Section> unavailableToDecrosslistSectionsList = new ArrayList<>();
List<Section> availableToDecrosslistSectionsList = new ArrayList<>();

if (findParentResult != null) {
List<Section> fullSectionList = findParentResult.getSectionList();

if (fullSectionList != null) {
for (Section section : fullSectionList) {
if (section.getSis_section_id() == null || section.getNonxlist_course_id() == null) {
unavailableToDecrosslistSectionsList.add(section);
} else {
availableToDecrosslistSectionsList.add(section);
}
}
}

model.addAttribute("unavailableToDecrosslistSectionsList", unavailableToDecrosslistSectionsList);
model.addAttribute("availableToDecrosslistSectionsList", availableToDecrosslistSectionsList);

model.addAttribute("findParentResult", findParentResult);
// add canvasCourseId to be used in audit log purposes later
model.addAttribute("canvasCourseId", findParentResult.getCanvasCourseId());

// If we aren't showing the course info, there must be an error
if (!findParentResult.isShowCourseInfo()){
model.addAttribute("errorMsg", findParentResult.getStatusMessage());
}
}

return "findParentCourse";
Expand Down Expand Up @@ -181,6 +205,23 @@ public String decrossListSections(@RequestParam("section-checkboxes") List<Strin
model.addAttribute("findParentResult", findParentResult);
// add canvasCourseId to be used in audit log purposes later
model.addAttribute("canvasCourseId", findParentResult.getCanvasCourseId());

List<Section> fullSectionList = findParentResult.getSectionList();
List<Section> unavailableToDecrosslistSectionsList = new ArrayList<>();
List<Section> availableToDecrosslistSectionsList = new ArrayList<>();

if (fullSectionList != null) {
for (Section section : fullSectionList) {
if (section.getSis_section_id() == null || section.getNonxlist_course_id() == null) {
unavailableToDecrosslistSectionsList.add(section);
} else {
availableToDecrosslistSectionsList.add(section);
}
}
}

model.addAttribute("unavailableToDecrosslistSectionsList", unavailableToDecrosslistSectionsList);
model.addAttribute("availableToDecrosslistSectionsList", availableToDecrosslistSectionsList);
}

return "findParentCourse";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,7 @@ public FindParentResult processSisLookup(String sisSectionId) {

if (sisSectionId == null || sisSectionId.isEmpty()) {
findParentResult.setShowCourseInfo(false);
findParentResult.setStatusMessage(CrosslistConstants.LOOKUP_FAILURE_NOT_FOUND_IN_SIS_MESSAGE);
findParentResult.setStatusIconCssClasses(CrosslistConstants.LOOKUP_FAILURE_CSS);
findParentResult.setStatusIconName(CrosslistConstants.LOOKUP_FAILURE_ICON_NAME);
findParentResult.setStatusMessage(CrosslistConstants.LOOKUP_FAILURE_MISSING_SIS_ID);
return findParentResult;
}

Expand All @@ -406,8 +404,6 @@ public FindParentResult processSisLookup(String sisSectionId) {
if (section == null) {
findParentResult.setShowCourseInfo(false);
findParentResult.setStatusMessage(CrosslistConstants.LOOKUP_FAILURE_NOT_FOUND_IN_CANVAS_MESSAGE);
findParentResult.setStatusIconCssClasses(CrosslistConstants.LOOKUP_FAILURE_CSS);
findParentResult.setStatusIconName(CrosslistConstants.LOOKUP_FAILURE_ICON_NAME);
return findParentResult;
}

Expand All @@ -416,8 +412,6 @@ public FindParentResult processSisLookup(String sisSectionId) {
if (course == null) {
findParentResult.setShowCourseInfo(false);
findParentResult.setStatusMessage(CrosslistConstants.LOOKUP_FAILURE_NOT_FOUND_IN_CANVAS_MESSAGE);
findParentResult.setStatusIconCssClasses(CrosslistConstants.LOOKUP_FAILURE_CSS);
findParentResult.setStatusIconName(CrosslistConstants.LOOKUP_FAILURE_ICON_NAME);
return findParentResult;
}

Expand All @@ -429,8 +423,6 @@ public FindParentResult processSisLookup(String sisSectionId) {
if (sectionsList == null) {
findParentResult.setShowCourseInfo(false);
findParentResult.setStatusMessage(CrosslistConstants.LOOKUP_FAILURE_NOT_FOUND_IN_CANVAS_MESSAGE);
findParentResult.setStatusIconCssClasses(CrosslistConstants.LOOKUP_FAILURE_CSS);
findParentResult.setStatusIconName(CrosslistConstants.LOOKUP_FAILURE_ICON_NAME);
return findParentResult;
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/static/css/crosslisting.css
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,8 @@
.checkboxColumn {
width: 1%;
line-height: inherit;
}

#results-message:focus {
outline: none;
}
134 changes: 134 additions & 0 deletions src/main/resources/static/js/decrosslisting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*-
* #%L
* lms-lti-crosslist
* %%
* Copyright (C) 2015 - 2022 Indiana University
* %%
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the Indiana University nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* #L%
*/

jQuery(document).ready(function() {
let resultsMessage = document.getElementById('results-message');

if (resultsMessage != null) {
resultsMessage.focus();
}

$("#select-all-button").click(function() {
$('input:checkbox').prop('checked', true);
updateCounter();
});

$("#deselect-all-button").click(function() {
$('input:checkbox').prop('checked', false);
updateCounter();
});

checkboxCounter();

// This will move focus to the invalid search if it fails the server-side validation
$('#sisid-search[aria-invalid="true"]').focus();
});

function checkboxCounter() {
// Get all the selected checkboxes, except the "select-all" one up in the table header
let $checkboxes = $('#decrosslist-form input[type="checkbox"]');

// update the count
$checkboxes.change(function() {
let countCheckedCheckboxes = $checkboxes.filter(':checked').length;
$('#sections-selected').text(countCheckedCheckboxes + ' selected');
});
}

function updateCounter() {
let $checkboxes = $('#decrosslist-form input[type="checkbox"]');
let countCheckedCheckboxes = $checkboxes.filter(':checked').length;
$('#sections-selected').text(countCheckedCheckboxes + ' selected');
}

function submitSisIdForm(button) {
let searchInput = document.getElementById('sisid-search');
if (searchInput.value.trim() === '') {
let validationText = document.getElementById('sisid-error-text');
validationText.innerText = 'SIS Section ID is required.';

let validationMessage = document.getElementById('sisid-error-message');
validationMessage.classList.remove('rvt-display-none');

searchInput.setAttribute('aria-invalid', true);
searchInput.setAttribute('aria-describedby', 'sisid-error-text sisid-search-description');
searchInput.focus();

return false;
} else {
buttonLoading(button);
document.getElementById('sisIdForm').submit();
}
}

function validateCheckboxForm(button) {
// Get all the selected checkboxes, except the "select-all" one up in the table header
let checkboxes = $('#decrosslist-form input[type="checkbox"]').not("#checkbox-select-all").filter(':checked').length;

if (checkboxes > 0) {
buttonLoading(button);
document.getElementById('decrosslist-form').submit();
} else {
let checkboxValidationMessage = document.getElementById('checkbox-validation-message');
checkboxValidationMessage.classList.remove('rvt-display-none');

let firstCheckbox = $('#decrosslist-form input[type="checkbox"]').not("#checkbox-select-all").first();
firstCheckbox.attr('aria-invalid', true);
firstCheckbox.attr('aria-describedby', 'checkbox-validation-message');
firstCheckbox.focus();

return false;
}
}

function buttonLoading(button) {
if (button.dataset.action != null) {
document.getElementById("submitValue").value = button.dataset.action;
}
button.setAttribute("aria-busy", true);
let buttonsToDisable = document.getElementsByTagName('button');
for(let i = 0; i < buttonsToDisable.length; i++) {
buttonsToDisable[i].disabled = true;
}
button.classList.add("rvt-button--loading");

let spinners = button.getElementsByClassName('rvt-loader');
if (spinners && spinners.length > 0) {
spinners[0].classList.remove("rvt-display-none");
}

let srText = button.getElementsByClassName('sr-loader-text');
if (srText && srText.length > 0) {
srText[0].classList.remove("rvt-display-none");
}
}
1 change: 1 addition & 0 deletions src/main/resources/templates/decrosslistlayout.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<script defer th:src="@{/app/jsrivet/rivet-icons.js}"></script>
<script type="module" th:src="@{/app/jsrivet/rivet-icon-element.js}"></script>
<script type="text/javascript" th:src="@{/app/webjars/jquery/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/app/js/decrosslisting.js}"></script>

<script>
Rivet.init();
Expand Down
Loading

0 comments on commit d3e1bc0

Please sign in to comment.