Skip to content

Commit 0ff6537

Browse files
Update accessibility features and improve UI elements for both human and agent interactions.
- Added ARIA roles and labels to various buttons and elements for better accessibility. - Enhanced focus styles for interactive elements to improve keyboard navigation. - Updated HTML structure in message manager and index pages to include roles and labels for improved screen reader support. - Introduced live regions for dynamic updates in the message manager to enhance user experience. - Added new configuration attributes for tab navigation in the config page to improve usability. - Updated CSS styles to ensure focus visibility and improve layout consistency across components.
1 parent 73cca69 commit 0ff6537

File tree

12 files changed

+586
-35
lines changed

12 files changed

+586
-35
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Playwright artifacts
2+
playwright-report/
3+
test-results/
4+
blob-report/
5+
.cache/ms-playwright/
16
## Ignore Visual Studio temporary files, build results, and
27
## files generated by popular Visual Studio add-ons.
38

pages/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html>
1+
<!DOCTYPE html>
22

33
<html xmlns="http://www.w3.org/1999/xhtml">
44

@@ -73,7 +73,7 @@
7373
</head>
7474

7575
<body>
76-
<div class="dashOuter picDashboard" data-panel="dashboard">
76+
<div class="dashOuter picDashboard" data-panel="dashboard" role="main" aria-label="Pool Control Dashboard">
7777
<header class="picHeader">
7878
<div class="picController picControlPanel control-panel" style="display:block;"></div>
7979
</header>

pages/messageManager.html

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html>
1+
<!DOCTYPE html>
22

33
<html xmlns="http://www.w3.org/1999/xhtml">
44
<head>
@@ -45,27 +45,28 @@
4545

4646
</head>
4747
<body>
48-
<div class="mmgrOuter picMessageManager" data-panel="messages">
48+
<div class="mmgrOuter picMessageManager" data-panel="messages" role="main" aria-label="Message Manager">
4949
<header>
5050
<div class="picController picControlPanel control-panel" style="display:block;"></div>
5151
</header>
5252
<div class="messageContainer">
5353
<!-- Tab Navigation (Settings-style tabBar widget) -->
5454
<div class="mmgrTabHeader">
55-
<div class="mmgrTabBar"></div>
55+
<div class="mmgrTabBar" id="mmgrTabBar" data-nav-group="message-manager-tabs"></div>
5656
<div class="view-bar-actions">
5757
<span class="view-bar-status" title="Replay load status"></span>
58-
<button type="button" class="view-bar-icon-btn view-bar-clear" title="Clear Messages">
58+
<button type="button" class="view-bar-icon-btn view-bar-clear" title="Clear Messages" data-nav-id="mmgr-clear-messages" aria-label="Clear Messages">
5959
<i class="fas fa-broom"></i>
6060
</button>
61-
<button type="button" class="view-bar-icon-btn view-bar-filter" title="Filter Display">
61+
<button type="button" class="view-bar-icon-btn view-bar-filter" title="Filter Display" data-nav-id="mmgr-filter-display" aria-label="Filter Display">
6262
<i class="fas fa-filter"></i>
6363
</button>
64-
<button type="button" class="upload-btn view-bar-upload" title="Choose Files">
64+
<button type="button" class="upload-btn view-bar-upload" title="Choose Files" data-nav-id="mmgr-choose-files" aria-label="Choose Replay Files">
6565
<i class="fas fa-folder-open"></i> Choose Files
6666
</button>
6767
<input id="universalReplayFileInput" type="file" accept=".json,.zip,.log" multiple style="display:none;" />
6868
</div>
69+
<div class="mmgr-live-region" aria-live="polite" aria-atomic="true"></div>
6970
</div>
7071
<!-- Message List View -->
7172
<div class="view-container active" data-view="messages">
@@ -119,11 +120,6 @@
119120
$mmTabBar.find('div.picTab[data-tabid="tabEntityFlow"] span.picTabText:first')
120121
.html('<i class="fas fa-project-diagram"></i>Entity Flow');
121122

122-
// Move actions into the tab row (right aligned)
123-
var $tabsRow = $mmTabBar.find('div.picTabs:first');
124-
$('<div class="mmgrTabSpacer"></div>').appendTo($tabsRow);
125-
$('.view-bar-actions').appendTo($tabsRow);
126-
127123
// View switching on tab change
128124
$mmTabBar.on('tabchange', function (evt) {
129125
var viewName = 'messages';
@@ -152,6 +148,7 @@
152148
var $replayStatus = $('.view-bar-status');
153149
var $clearBtn = $('.view-bar-clear');
154150
var $filterBtn = $('.view-bar-filter');
151+
var $liveRegion = $('.mmgr-live-region');
155152

156153
$replayBtn.on('click', function(e) {
157154
e.preventDefault();
@@ -161,6 +158,7 @@
161158
// Show status updates emitted from Entity Flow widget
162159
$('div.picEntityFlow').on('replayLoadStatus', function(e) {
163160
$replayStatus.text(e.text || '');
161+
$liveRegion.text(e.text || '');
164162
});
165163

166164
$replayInput.on('change', function(e) {
@@ -183,6 +181,7 @@
183181
var ml = $('div.picMessages:first')[0];
184182
if (ml && ml.clear) ml.clear();
185183
$replayStatus.text('');
184+
$liveRegion.text('Messages cleared');
186185
// Reset upload button text
187186
$replayBtn.html('<i class="fas fa-folder-open"></i> Choose Files');
188187
$replayBtn.attr('title', 'Choose Files');

scripts/configPage.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(function ($) {
1+
(function ($) {
22
$.widget('pic.configPage', {
33
options: {
44
cfg: {},
@@ -110,6 +110,8 @@
110110
var self = this, o = self.options, el = self.element;
111111
var tabs = $('<div class="picTabPanel"></div>');
112112
tabs.appendTo(el);
113+
tabs.attr('id', 'configMainTabBar');
114+
tabs.attr('data-nav-group', 'config-main-tabs');
113115
tabs.tabBar();
114116
tabs.find('div.picTabContents').addClass('picConfigTabContents');
115117
tabs.on('tabchange', function (evt) { self._onTabChanged(evt); });
@@ -168,6 +170,8 @@
168170
if (typeof subTabs !== 'undefined') {
169171
var tabs = $('<div class="picTabPanel"></div>');
170172
tabs.appendTo(contents);
173+
tabs.attr('id', `${attrs.id || 'config'}SubTabBar`);
174+
tabs.attr('data-nav-group', `${attrs.id || 'config'}-sub-tabs`);
171175
tabs.tabBar();
172176
tabs.find('div.picTabContents').addClass('picConfigTabContents');
173177
//tabs.on('tabchange', function (evt) { self._onTabChanged(evt); });

scripts/controller.js

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(function ($) {
1+
(function ($) {
22
$.widget("pic.controller", {
33
options: {},
44
_create: function () {
@@ -208,20 +208,50 @@
208208
row = $('<div class="picPanelMode" data-status="auto"><i class="far fa-pause-circle burst-animated"></i><label></label><span class="service-timeout-remaining"></span><i class="far fa-pause-circle burst-animated"></i></div>');
209209
row.appendTo(el);
210210
$('<div class="picSpaDrain" data-status="off"><i class="fas fa-skull-crossbones burst-animated"></i><label>SPA DRAIN ACTIVE</label><i class="fas fa-skull-crossbones burst-animated"></i></div>').appendTo(el);
211+
// Keyboard + automation semantics for header icon controls.
212+
el.find('div.picModel > i, div.picConfigIcon, div.picLockIcon').attr('role', 'button').attr('tabindex', 0);
213+
el.find('div.picModel > i').attr('aria-label', 'Open Settings').attr('data-nav-id', 'settings-open');
214+
el.find('div.picConfigIcon').attr('aria-label', 'Toggle Configuration View').attr('data-nav-id', 'config-toggle');
215+
el.find('div.picLockIcon').attr('aria-label', 'Lock Or Unlock Settings').attr('data-nav-id', 'security-lock-toggle');
216+
el.on('keydown', 'div.picModel > i, div.picConfigIcon, div.picLockIcon', function (evt) {
217+
if (evt.key === 'Enter' || evt.key === ' ' || evt.key === 'Spacebar') {
218+
evt.preventDefault();
219+
$(evt.currentTarget).trigger('click');
220+
}
221+
});
211222
el.find('div.picModel > i').on('click', function (evt) {
212223
var btn = evt.currentTarget;
213224
self._ensureAdminAccess(function () {
214-
// Open up the settings window.
225+
// Toggle/reuse a single settings popover instance.
226+
if (o.settingsPopover && o.settingsPopover.length && o.settingsPopover.is(':visible')) {
227+
o.settingsPopover[0].close();
228+
o.settingsPopover.remove();
229+
o.settingsPopover = null;
230+
return;
231+
}
232+
// Cleanup any stale popovers left in DOM.
233+
el.parent().find('div.picPopover.picAppSettings').remove();
215234
var divPopover = $('<div class="picAppSettings"></div>');
235+
o.settingsPopover = divPopover;
216236
divPopover.appendTo(el.parent());
217237
divPopover.on('initPopover', function (e) {
238+
// Guard: initialize contents only once.
239+
if (divPopover.attr('data-settings-initialized') === 'true') return;
240+
divPopover.attr('data-settings-initialized', 'true');
218241
let divSettings = $('<div class="picAppSettings"></div>');
219242
divSettings.appendTo(e.contents());
220243
divSettings.settingsPanel();
221-
divSettings.on('loaded', function (e) { divPopover[0].show(btn); });
222244
e.stopImmediatePropagation();
223245
});
224-
divPopover.popover({ title: 'Settings', popoverStyle: 'modal', placement: { target: btn } });
246+
divPopover.on('beforeClose', function () {
247+
o.settingsPopover = null;
248+
});
249+
divPopover.popover({
250+
title: 'Settings',
251+
popoverStyle: 'modal',
252+
autoClose: false,
253+
placement: { target: btn }
254+
});
225255
divPopover[0].show(btn);
226256
});
227257
evt.preventDefault();
@@ -1467,6 +1497,8 @@
14671497
var tabs = $('<div class="picTabPanel"></div>');
14681498
console.log('Building controls');
14691499
tabs.appendTo(el);
1500+
tabs.attr('id', 'settingsTabBar');
1501+
tabs.attr('data-nav-group', 'settings-tabs');
14701502
tabs.tabBar();
14711503
$.getLocalService('/options', null, function (configData, status, xhr) {
14721504
console.log(configData);

scripts/messages/entityFlow/entityFlow.widget.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
});
9393

9494
var collapsedRight = $('<div class="collapsed-header-right"></div>').appendTo(o.collapsedHeader);
95-
var expandBtn = $('<button class="expand-setup-btn" title="Show setup panel"><i class="fas fa-chevron-down"></i></button>').appendTo(collapsedRight);
95+
var expandBtn = $('<button type="button" class="expand-setup-btn" title="Show setup panel" aria-label="Show setup panel"><i class="fas fa-chevron-down"></i></button>').appendTo(collapsedRight);
9696
expandBtn.on('click', function() { self._toggleSetupPanel(true); });
9797

9898
// Collapsed dropdowns sync with expanded ones
@@ -117,7 +117,9 @@
117117
// === STEP 2: Entity Selection ===
118118
o.step2Container = $('<div class="entity-flow-step step-2 step-disabled"></div>').appendTo(o.setupPanel);
119119
var step2Header = $('<div class="step-header"><span class="step-number">2</span><span class="step-title">Select Entity to Analyze</span></div>').appendTo(o.step2Container);
120-
var collapseBtn = $('<button class="collapse-setup-btn" title="Collapse to header"><i class="fas fa-chevron-up"></i></button>').appendTo(step2Header);
120+
var collapseBtn = $('<button type="button" class="collapse-setup-btn" title="Collapse to header" aria-label="Collapse to header"><i class="fas fa-chevron-up"></i></button>').appendTo(step2Header);
121+
o.collapseSetupBtn = collapseBtn;
122+
collapseBtn.prop('disabled', true).attr('aria-disabled', 'true');
121123
collapseBtn.on('click', function() { self._toggleSetupPanel(false); });
122124

123125
var step2Content = $('<div class="step-content"></div>').appendTo(o.step2Container);
@@ -397,6 +399,7 @@
397399

398400
if (allLoaded) {
399401
o.step2Container.removeClass('step-disabled');
402+
if (o.collapseSetupBtn) o.collapseSetupBtn.prop('disabled', false).attr('aria-disabled', 'false');
400403
if (o.modeSelect) o.modeSelect.prop('disabled', false);
401404
if (o.flowMultiBtn) o.flowMultiBtn.prop('disabled', false);
402405
if (o.flowMultiBtnCollapsed) o.flowMultiBtnCollapsed.prop('disabled', false);
@@ -405,6 +408,7 @@
405408
if (o.entitySelect) o.entitySelect.prop('disabled', !hasEntitySource);
406409
} else {
407410
o.step2Container.addClass('step-disabled');
411+
if (o.collapseSetupBtn) o.collapseSetupBtn.prop('disabled', true).attr('aria-disabled', 'true');
408412
if (o.modeSelect) o.modeSelect.prop('disabled', true);
409413
if (o.flowMultiBtn) o.flowMultiBtn.prop('disabled', true);
410414
if (o.flowMultiBtnCollapsed) o.flowMultiBtnCollapsed.prop('disabled', true);

0 commit comments

Comments
 (0)