From 70f7a4592434e5b78ae7f84bd3183e5547f6b514 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Mon, 30 Mar 2026 15:45:30 -0400 Subject: [PATCH 01/10] fix datatables headers on files table --- .../app/javascript/files/data_table.js | 23 +++++++++++++++++-- .../app/views/files/_files_table.html.erb | 7 +++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/apps/dashboard/app/javascript/files/data_table.js b/apps/dashboard/app/javascript/files/data_table.js index 6f167580fc..ea07237e49 100644 --- a/apps/dashboard/app/javascript/files/data_table.js +++ b/apps/dashboard/app/javascript/files/data_table.js @@ -303,8 +303,10 @@ class DataTable { $('#select_all').trigger(); } - $(`${CONTENTID}_caption`).text(`Contents of directory ${data.path}`); - ariaNotify(`navigated to ${data.path}`); + $(`${CONTENTID}_caption`).text(`Contents of directory ${data.path}`); + ariaNotify(`navigated to ${data.path}`); + + this.customizeHeaders(); let result = await Promise.resolve(data); $('td input[type=checkbox]').on('keypress', function(event) { @@ -527,6 +529,23 @@ class DataTable { return btnGroup.outerHTML; } + customizeHeaders() { + $(`${CONTENTID} th.dt-orderable-asc`).each(function(_index, el) { + const sortButton = $(el).find('span.dt-column-order'); + const ariaLabel = sortButton.attr('aria-label'); + const newId = `${el.textContent.replaceAll(' ', '_').toLowerCase()}_label`; + var labelSpan = document.getElementById(newId); + if (labelSpan === null) { + labelSpan = document.createElement('span'); + labelSpan.id = newId; + $('#header_labels').append(labelSpan); + } + labelSpan.textContent = ariaLabel; + sortButton.attr('aria-label', ''); + sortButton.attr('aria-describedby', newId) + }) + } + updateDatatablesStatus() { // from "function info ( api )" of https://cdn.datatables.net/select/1.3.1/js/dataTables.select.js let api = this._table; diff --git a/apps/dashboard/app/views/files/_files_table.html.erb b/apps/dashboard/app/views/files/_files_table.html.erb index bccd1fd57a..fb0e78d93d 100755 --- a/apps/dashboard/app/views/files/_files_table.html.erb +++ b/apps/dashboard/app/views/files/_files_table.html.erb @@ -3,14 +3,15 @@ - - Select All +
+ Select All +
From 5c4f8394fd88dc8a60fb48d4d6e82655ae0d74a8 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Tue, 31 Mar 2026 10:25:32 -0400 Subject: [PATCH 02/10] customize headers on all tables --- apps/dashboard/app/javascript/active_jobs.js | 3 ++- .../app/javascript/files/data_table.js | 24 ++++--------------- .../path_selector/path_selector_data_table.js | 6 ++++- apps/dashboard/app/javascript/utils.js | 19 +++++++++++++++ .../app/views/active_jobs/index.html.erb | 2 ++ .../shared/_path_selector_table.html.erb | 2 ++ 6 files changed, 34 insertions(+), 22 deletions(-) diff --git a/apps/dashboard/app/javascript/active_jobs.js b/apps/dashboard/app/javascript/active_jobs.js index 2468653ad2..a8b8253e0c 100644 --- a/apps/dashboard/app/javascript/active_jobs.js +++ b/apps/dashboard/app/javascript/active_jobs.js @@ -2,7 +2,7 @@ import oboe from 'oboe'; import { supportPath } from './config.js'; -import { cssBadgeForState, capitalizeFirstLetter } from './utils.js' +import { cssBadgeForState, capitalizeFirstLetter, customizeTableHeaders } from './utils.js' window.fetch_table_data = fetch_table_data; window.create_datatable = create_datatable; @@ -165,6 +165,7 @@ function create_datatable(options){ }, processing: true, // Add the "processing" while json is being downloaded. drawCallback: function(settings){ + customizeTableHeaders('#job_status_table'); if(options.drawCallback){ options.drawCallback(settings); } diff --git a/apps/dashboard/app/javascript/files/data_table.js b/apps/dashboard/app/javascript/files/data_table.js index ea07237e49..0b469332d8 100644 --- a/apps/dashboard/app/javascript/files/data_table.js +++ b/apps/dashboard/app/javascript/files/data_table.js @@ -3,7 +3,7 @@ import { getGlobusLink, updateGlobusLink } from './globus.js'; import { downloadEnabled } from '../config.js'; export { CONTENTID, EVENTNAME }; import { OODAlertError } from '../alert.js'; -import { toHumanSize, ariaNotify } from '../utils.js'; +import { toHumanSize, ariaNotify, customizeTableHeaders } from '../utils.js'; const EVENTNAME = { getJsonResponse: 'getJsonResponse', @@ -204,6 +204,9 @@ class DataTable { // if you need to omit more columns, use a "selectable" class on the columns you want to support selection selector: 'td:not(:first-child)' }, + drawCallback: function() { + customizeTableHeaders(CONTENTID); + }, layout: { top1Start: { div: { @@ -306,8 +309,6 @@ class DataTable { $(`${CONTENTID}_caption`).text(`Contents of directory ${data.path}`); ariaNotify(`navigated to ${data.path}`); - this.customizeHeaders(); - let result = await Promise.resolve(data); $('td input[type=checkbox]').on('keypress', function(event) { if (event.which === 13) { @@ -529,23 +530,6 @@ class DataTable { return btnGroup.outerHTML; } - customizeHeaders() { - $(`${CONTENTID} th.dt-orderable-asc`).each(function(_index, el) { - const sortButton = $(el).find('span.dt-column-order'); - const ariaLabel = sortButton.attr('aria-label'); - const newId = `${el.textContent.replaceAll(' ', '_').toLowerCase()}_label`; - var labelSpan = document.getElementById(newId); - if (labelSpan === null) { - labelSpan = document.createElement('span'); - labelSpan.id = newId; - $('#header_labels').append(labelSpan); - } - labelSpan.textContent = ariaLabel; - sortButton.attr('aria-label', ''); - sortButton.attr('aria-describedby', newId) - }) - } - updateDatatablesStatus() { // from "function info ( api )" of https://cdn.datatables.net/select/1.3.1/js/dataTables.select.js let api = this._table; diff --git a/apps/dashboard/app/javascript/path_selector/path_selector_data_table.js b/apps/dashboard/app/javascript/path_selector/path_selector_data_table.js index eee7ddb2e6..dce5c049d1 100644 --- a/apps/dashboard/app/javascript/path_selector/path_selector_data_table.js +++ b/apps/dashboard/app/javascript/path_selector/path_selector_data_table.js @@ -1,5 +1,5 @@ import { OODAlertError } from '../alert'; -import { ariaNotify, hide, show } from "../utils"; +import { ariaNotify, customizeTableHeaders, hide, show } from "../utils"; export class PathSelectorTable { _table = null; @@ -38,6 +38,7 @@ export class PathSelectorTable { } initDataTable() { + const tableId = this.tableId; this._table = $(`#${this.tableId}`).DataTable({ autoWidth: false, language: { @@ -55,6 +56,9 @@ export class PathSelectorTable { // if you need to omit more columns, use a "selectable" class on the columns you want to support selection selector: 'td:not(:first-child)' }, + drawCallback: function() { + customizeTableHeaders(`#${tableId}`); + }, // https://datatables.net/reference/option/dom // dom: '', dataTables_info nowrap // diff --git a/apps/dashboard/app/javascript/utils.js b/apps/dashboard/app/javascript/utils.js index 39790781b4..bddd93e748 100644 --- a/apps/dashboard/app/javascript/utils.js +++ b/apps/dashboard/app/javascript/utils.js @@ -150,6 +150,25 @@ export function pushNotify(message, options = {}) { } } +// rearrange table header labels so button labels are not part of header +export function customizeTableHeaders(tableId) { + $(`${tableId} th.dt-orderable-asc`).each(function(_index, el) { + const sortButton = $(el).find('span.dt-column-order'); + const ariaLabel = sortButton.attr('aria-label'); + const labelId = `${el.textContent.replaceAll(' ', '_').toLowerCase()}_label`; + var labelSpan = document.getElementById(labelId); + if (labelSpan === null) { + labelSpan = document.createElement('span'); + labelSpan.id = labelId; + $('#header_labels').append(labelSpan); + } + labelSpan.textContent = ariaLabel; + sortButton.removeAttr('aria-label'); + sortButton.attr('aria-describedby', labelId); + console.log(sortButton[0]); + }); +} + // Store a boolean value in localStorage export function storeBoolean(key, value) { localStorage.setItem(key, value ? 'true' : 'false'); diff --git a/apps/dashboard/app/views/active_jobs/index.html.erb b/apps/dashboard/app/views/active_jobs/index.html.erb index 10ec4bea9f..6b131e1d3b 100644 --- a/apps/dashboard/app/views/active_jobs/index.html.erb +++ b/apps/dashboard/app/views/active_jobs/index.html.erb @@ -41,6 +41,8 @@

Active Jobs

+
+
<%= "Contents of directory #{@path}" %>
- + Select Type
diff --git a/apps/dashboard/app/views/shared/_path_selector_table.html.erb b/apps/dashboard/app/views/shared/_path_selector_table.html.erb index 80d2704caf..4b2f1b67cb 100644 --- a/apps/dashboard/app/views/shared/_path_selector_table.html.erb +++ b/apps/dashboard/app/views/shared/_path_selector_table.html.erb @@ -59,6 +59,8 @@ Loading... +
+
From e4a0a78524a80f86812c131eeaa6bd2c7c409a3d Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Tue, 31 Mar 2026 11:38:05 -0400 Subject: [PATCH 03/10] Remove console log from utils.js Removed console log statement for sortButton. --- apps/dashboard/app/javascript/utils.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dashboard/app/javascript/utils.js b/apps/dashboard/app/javascript/utils.js index bddd93e748..d81b778cd6 100644 --- a/apps/dashboard/app/javascript/utils.js +++ b/apps/dashboard/app/javascript/utils.js @@ -165,7 +165,6 @@ export function customizeTableHeaders(tableId) { labelSpan.textContent = ariaLabel; sortButton.removeAttr('aria-label'); sortButton.attr('aria-describedby', labelId); - console.log(sortButton[0]); }); } From c16f67f53aebe3f6daac97eacb05fa376869eef3 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Fri, 3 Apr 2026 10:30:15 -0400 Subject: [PATCH 04/10] rename tableId to tableIdSelector --- apps/dashboard/app/javascript/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/app/javascript/utils.js b/apps/dashboard/app/javascript/utils.js index d81b778cd6..2bc74a61a9 100644 --- a/apps/dashboard/app/javascript/utils.js +++ b/apps/dashboard/app/javascript/utils.js @@ -151,8 +151,8 @@ export function pushNotify(message, options = {}) { } // rearrange table header labels so button labels are not part of header -export function customizeTableHeaders(tableId) { - $(`${tableId} th.dt-orderable-asc`).each(function(_index, el) { +export function customizeTableHeaders(tableIdSelector) { + $(`${tableIdSelector} th.dt-orderable-asc`).each(function(_index, el) { const sortButton = $(el).find('span.dt-column-order'); const ariaLabel = sortButton.attr('aria-label'); const labelId = `${el.textContent.replaceAll(' ', '_').toLowerCase()}_label`; From 99b4efa5be26580de31d4db6bf541b7ec90c56f6 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Fri, 3 Apr 2026 16:22:29 -0400 Subject: [PATCH 05/10] use aria-live to communicate sorting changes --- apps/dashboard/app/javascript/utils.js | 1 + apps/dashboard/app/views/active_jobs/index.html.erb | 4 ++-- apps/dashboard/app/views/files/_files_table.html.erb | 6 +++--- .../app/views/shared/_path_selector_table.html.erb | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/dashboard/app/javascript/utils.js b/apps/dashboard/app/javascript/utils.js index 2bc74a61a9..e6ad685de1 100644 --- a/apps/dashboard/app/javascript/utils.js +++ b/apps/dashboard/app/javascript/utils.js @@ -160,6 +160,7 @@ export function customizeTableHeaders(tableIdSelector) { if (labelSpan === null) { labelSpan = document.createElement('span'); labelSpan.id = labelId; + labelSpan.setAttribute('aria-live', 'polite'); $('#header_labels').append(labelSpan); } labelSpan.textContent = ariaLabel; diff --git a/apps/dashboard/app/views/active_jobs/index.html.erb b/apps/dashboard/app/views/active_jobs/index.html.erb index 6b131e1d3b..63b9d15d10 100644 --- a/apps/dashboard/app/views/active_jobs/index.html.erb +++ b/apps/dashboard/app/views/active_jobs/index.html.erb @@ -41,8 +41,6 @@

Active Jobs

-
-
Choose a file or directory to use in the form.
@@ -59,6 +57,8 @@
+
+
diff --git a/apps/dashboard/app/views/files/_files_table.html.erb b/apps/dashboard/app/views/files/_files_table.html.erb index fb0e78d93d..1568cb87f8 100755 --- a/apps/dashboard/app/views/files/_files_table.html.erb +++ b/apps/dashboard/app/views/files/_files_table.html.erb @@ -3,9 +3,6 @@ -
- Select All -
@@ -26,5 +23,8 @@
<%= "Contents of directory #{@path}" %>
+
+ Select All +
<%= render partial: "spinner" %> diff --git a/apps/dashboard/app/views/shared/_path_selector_table.html.erb b/apps/dashboard/app/views/shared/_path_selector_table.html.erb index 4b2f1b67cb..b7ab6351aa 100644 --- a/apps/dashboard/app/views/shared/_path_selector_table.html.erb +++ b/apps/dashboard/app/views/shared/_path_selector_table.html.erb @@ -59,8 +59,6 @@ Loading... -
-
@@ -72,6 +70,8 @@
Choose a file or directory to use in the form.
+
+
From 0523dda340f0f4524686a04f0e95ae510f211cd0 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Fri, 3 Apr 2026 17:37:31 -0400 Subject: [PATCH 06/10] use headerCallback instead of drawCallback --- apps/dashboard/app/javascript/active_jobs.js | 4 +++- apps/dashboard/app/javascript/files/data_table.js | 4 ++-- .../app/javascript/path_selector/path_selector_data_table.js | 5 ++--- apps/dashboard/app/javascript/utils.js | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/dashboard/app/javascript/active_jobs.js b/apps/dashboard/app/javascript/active_jobs.js index a8b8253e0c..c838b42525 100644 --- a/apps/dashboard/app/javascript/active_jobs.js +++ b/apps/dashboard/app/javascript/active_jobs.js @@ -165,11 +165,13 @@ function create_datatable(options){ }, processing: true, // Add the "processing" while json is being downloaded. drawCallback: function(settings){ - customizeTableHeaders('#job_status_table'); if(options.drawCallback){ options.drawCallback(settings); } }, + headerCallback: (thead, _data, _start, _end, _display) => { + customizeTableHeaders(thead); + }, columns: [ { "orderable": false, diff --git a/apps/dashboard/app/javascript/files/data_table.js b/apps/dashboard/app/javascript/files/data_table.js index 0b469332d8..e4a50e0bfb 100644 --- a/apps/dashboard/app/javascript/files/data_table.js +++ b/apps/dashboard/app/javascript/files/data_table.js @@ -204,8 +204,8 @@ class DataTable { // if you need to omit more columns, use a "selectable" class on the columns you want to support selection selector: 'td:not(:first-child)' }, - drawCallback: function() { - customizeTableHeaders(CONTENTID); + headerCallback: (thead, _data, _start, _end, _display) => { + customizeTableHeaders(thead); }, layout: { top1Start: { diff --git a/apps/dashboard/app/javascript/path_selector/path_selector_data_table.js b/apps/dashboard/app/javascript/path_selector/path_selector_data_table.js index dce5c049d1..7719072732 100644 --- a/apps/dashboard/app/javascript/path_selector/path_selector_data_table.js +++ b/apps/dashboard/app/javascript/path_selector/path_selector_data_table.js @@ -38,7 +38,6 @@ export class PathSelectorTable { } initDataTable() { - const tableId = this.tableId; this._table = $(`#${this.tableId}`).DataTable({ autoWidth: false, language: { @@ -56,8 +55,8 @@ export class PathSelectorTable { // if you need to omit more columns, use a "selectable" class on the columns you want to support selection selector: 'td:not(:first-child)' }, - drawCallback: function() { - customizeTableHeaders(`#${tableId}`); + headerCallback: (thead, _data, _start, _end, _display) => { + customizeTableHeaders(thead); }, // https://datatables.net/reference/option/dom // dom: '', dataTables_info nowrap diff --git a/apps/dashboard/app/javascript/utils.js b/apps/dashboard/app/javascript/utils.js index e6ad685de1..c559a2eb23 100644 --- a/apps/dashboard/app/javascript/utils.js +++ b/apps/dashboard/app/javascript/utils.js @@ -151,8 +151,8 @@ export function pushNotify(message, options = {}) { } // rearrange table header labels so button labels are not part of header -export function customizeTableHeaders(tableIdSelector) { - $(`${tableIdSelector} th.dt-orderable-asc`).each(function(_index, el) { +export function customizeTableHeaders(thead) { + $(thead).find('th.dt-orderable-asc').each(function(_index, el) { const sortButton = $(el).find('span.dt-column-order'); const ariaLabel = sortButton.attr('aria-label'); const labelId = `${el.textContent.replaceAll(' ', '_').toLowerCase()}_label`; From db96c450da742360a678d338acaf0b2f0f3f5187 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 23 Apr 2026 14:07:50 -0400 Subject: [PATCH 07/10] simplify rearrangement --- apps/dashboard/app/javascript/utils.js | 21 +++++++------------ .../app/views/active_jobs/index.html.erb | 1 - .../app/views/files/_files_table.html.erb | 4 +--- .../shared/_path_selector_table.html.erb | 1 - 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/apps/dashboard/app/javascript/utils.js b/apps/dashboard/app/javascript/utils.js index c559a2eb23..2a48922276 100644 --- a/apps/dashboard/app/javascript/utils.js +++ b/apps/dashboard/app/javascript/utils.js @@ -153,19 +153,14 @@ export function pushNotify(message, options = {}) { // rearrange table header labels so button labels are not part of header export function customizeTableHeaders(thead) { $(thead).find('th.dt-orderable-asc').each(function(_index, el) { - const sortButton = $(el).find('span.dt-column-order'); - const ariaLabel = sortButton.attr('aria-label'); - const labelId = `${el.textContent.replaceAll(' ', '_').toLowerCase()}_label`; - var labelSpan = document.getElementById(labelId); - if (labelSpan === null) { - labelSpan = document.createElement('span'); - labelSpan.id = labelId; - labelSpan.setAttribute('aria-live', 'polite'); - $('#header_labels').append(labelSpan); - } - labelSpan.textContent = ariaLabel; - sortButton.removeAttr('aria-label'); - sortButton.attr('aria-describedby', labelId); + const sortButton = $(el).find('span.dt-column-order'); + const ariaLabel = sortButton.attr('aria-label'); + const headerText = $(el).find('.dt-column-title').text(); + $(el).attr('aria-label', ariaLabel).attr('tabindex', '0'); + sortButton.removeAttr('aria-label') + .removeAttr('tabindex') + .removeAttr('role') + .attr('aria-hidden', 'true'); }); } diff --git a/apps/dashboard/app/views/active_jobs/index.html.erb b/apps/dashboard/app/views/active_jobs/index.html.erb index 63b9d15d10..94023bc768 100644 --- a/apps/dashboard/app/views/active_jobs/index.html.erb +++ b/apps/dashboard/app/views/active_jobs/index.html.erb @@ -57,7 +57,6 @@ -
diff --git a/apps/dashboard/app/views/files/_files_table.html.erb b/apps/dashboard/app/views/files/_files_table.html.erb index 1568cb87f8..0a9c5147c8 100755 --- a/apps/dashboard/app/views/files/_files_table.html.erb +++ b/apps/dashboard/app/views/files/_files_table.html.erb @@ -3,6 +3,7 @@ + Select All @@ -23,8 +24,5 @@
<%= "Contents of directory #{@path}" %>
-
- Select All -
<%= render partial: "spinner" %> diff --git a/apps/dashboard/app/views/shared/_path_selector_table.html.erb b/apps/dashboard/app/views/shared/_path_selector_table.html.erb index b7ab6351aa..14bd673be3 100644 --- a/apps/dashboard/app/views/shared/_path_selector_table.html.erb +++ b/apps/dashboard/app/views/shared/_path_selector_table.html.erb @@ -70,7 +70,6 @@ -
From a0a6e03656a2cc27cca35c01b4633ac8b66e0873 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 23 Apr 2026 14:08:46 -0400 Subject: [PATCH 08/10] Remove closing div tag from active_jobs index view --- apps/dashboard/app/views/active_jobs/index.html.erb | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dashboard/app/views/active_jobs/index.html.erb b/apps/dashboard/app/views/active_jobs/index.html.erb index 94023bc768..10ec4bea9f 100644 --- a/apps/dashboard/app/views/active_jobs/index.html.erb +++ b/apps/dashboard/app/views/active_jobs/index.html.erb @@ -57,7 +57,6 @@ - From e5b68c3b00e109e82b94d28f117705360c8f4782 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 23 Apr 2026 14:09:29 -0400 Subject: [PATCH 09/10] Update _path_selector_table.html.erb --- apps/dashboard/app/views/shared/_path_selector_table.html.erb | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dashboard/app/views/shared/_path_selector_table.html.erb b/apps/dashboard/app/views/shared/_path_selector_table.html.erb index 14bd673be3..80d2704caf 100644 --- a/apps/dashboard/app/views/shared/_path_selector_table.html.erb +++ b/apps/dashboard/app/views/shared/_path_selector_table.html.erb @@ -70,7 +70,6 @@ - From 5251db28fb48b755137f83bf7fd472c86aab7c68 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 23 Apr 2026 14:16:45 -0400 Subject: [PATCH 10/10] Remove unused variable Remove header text retrieval from customizeTableHeaders function. --- apps/dashboard/app/javascript/utils.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dashboard/app/javascript/utils.js b/apps/dashboard/app/javascript/utils.js index 2a48922276..7936b713a2 100644 --- a/apps/dashboard/app/javascript/utils.js +++ b/apps/dashboard/app/javascript/utils.js @@ -155,7 +155,6 @@ export function customizeTableHeaders(thead) { $(thead).find('th.dt-orderable-asc').each(function(_index, el) { const sortButton = $(el).find('span.dt-column-order'); const ariaLabel = sortButton.attr('aria-label'); - const headerText = $(el).find('.dt-column-title').text(); $(el).attr('aria-label', ariaLabel).attr('tabindex', '0'); sortButton.removeAttr('aria-label') .removeAttr('tabindex')