From 1216e56a09d1cfdd368991888b800303471deb3a Mon Sep 17 00:00:00 2001 From: Joseph Replin Date: Tue, 28 Oct 2025 16:50:31 -0500 Subject: [PATCH 1/8] page level progress indicator styles --- less/pageLevelProgressIndicator.less | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/less/pageLevelProgressIndicator.less b/less/pageLevelProgressIndicator.less index 51f8938..e1e443f 100644 --- a/less/pageLevelProgressIndicator.less +++ b/less/pageLevelProgressIndicator.less @@ -1,6 +1,53 @@ // Global indicator // -------------------------------------------------- .pagelevelprogress { + &__indicator-outer { + display: flex; + flex-direction: column; + /* left-align the group within the outer container */ + align-items: flex-start; + } + + &__indicator-group { + display: flex; + flex-direction: column; + /* center the indicator and label horizontally as a group */ + align-items: center; + } + + // Override core .aria-label (which is visually-hidden) for our + // indicator group so the message can be presented visually and + // styled similar to a header/title. Keep it lightweight. + &__indicator-group .aria-label { + position: static; + display: block; + width: auto; + height: auto; + margin: 0; + padding: 0; + overflow: visible; + clip: auto; + white-space: normal; + pointer-events: none; + font-size: 0.875rem; + font-weight: 600; + line-height: 1.1; + color: @black; + } + + /* Screen-reader-only text (accessible but not visible) */ + .sr-only { + position: absolute !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; + } + &__indicator { display: flex; width: 2rem; @@ -28,6 +75,15 @@ width: var(--adapt-pagelevelprogress-percentage); } + &__indicator-label { + font-size: 0.5rem; + font-weight: normal; + text-align: center; + margin-top: 0.125rem; + line-height: 1; + white-space: nowrap; + } + &__indicator .js-indicator-aria-label { top: 0; } From eb481e89c53e11c6d4a4c7b6c99ebbe86e9be43c Mon Sep 17 00:00:00 2001 From: Joseph Replin Date: Tue, 28 Oct 2025 16:51:22 -0500 Subject: [PATCH 2/8] Add optional aria-label support to progress indicator --- templates/pageLevelProgressIndicator.jsx | 31 +++++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/templates/pageLevelProgressIndicator.jsx b/templates/pageLevelProgressIndicator.jsx index 3701672..4c52e7b 100644 --- a/templates/pageLevelProgressIndicator.jsx +++ b/templates/pageLevelProgressIndicator.jsx @@ -3,22 +3,35 @@ import { compile } from 'core/js/reactHelpers'; export default function PageLevelProgressIndicator (props) { const { - ariaLabel + ariaLabel, + _isOptional } = props; + // Build aria-label with optional prefix if needed + const compiledAriaLabel = ariaLabel ? compile(ariaLabel, props) : ''; + const fullAriaLabel = _isOptional ? `Optional. ${compiledAriaLabel}` : compiledAriaLabel; + return ( - - + + + + + - + {ariaLabel && + + {fullAriaLabel} + + } - {ariaLabel && - - {compile(ariaLabel, props)} - } + + {_isOptional && + - + } + ); }; From 3ba92f234a269a0da8bf703e972ed6b27fc22765 Mon Sep 17 00:00:00 2001 From: Joseph Replin Date: Tue, 28 Oct 2025 16:51:43 -0500 Subject: [PATCH 3/8] Update pageLevelProgressIndicator.jsx From 4905a2f509de4e18dc98893c5b1c0647b51ff877 Mon Sep 17 00:00:00 2001 From: Joseph Replin Date: Tue, 28 Oct 2025 16:52:01 -0500 Subject: [PATCH 4/8] Fix division by zero and add optional content check Handle division by zero in progress calculation and add optional content check. --- js/PageLevelProgressIndicatorView.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/js/PageLevelProgressIndicatorView.js b/js/PageLevelProgressIndicatorView.js index cb8902d..f706a09 100644 --- a/js/PageLevelProgressIndicatorView.js +++ b/js/PageLevelProgressIndicatorView.js @@ -57,7 +57,8 @@ class PageLevelProgressIndicatorView extends Backbone.View { if (isPresentationComponentWithItems) { const children = this.model.getChildren(); const visited = children.filter(child => child.get('_isVisited')); - return Math.round(visited.length / children.length * 100); + // Handle division by zero when component has no children + return children.length === 0 ? 0 : Math.round(visited.length / children.length * 100); } return 0; } @@ -98,6 +99,14 @@ class PageLevelProgressIndicatorView extends Backbone.View { const data = this.model.toJSON(); data.ariaLabel = this.ariaLabel; data.type = this.type; + + // Check if content is optional (set by diagnostic extension) + data._isOptional = this.model.get('_isOptional') || false; + + // Note: do not generate a combined screen-reader string here. Visible labels + // will remain present and any aria labeling should be handled by the template + // or higher-level components to avoid duplicating responsibilities. + return data; } From 00e19c8070dab60644ab9a91f9cfc45779dd4e03 Mon Sep 17 00:00:00 2001 From: Joseph Replin Date: Tue, 28 Oct 2025 16:52:15 -0500 Subject: [PATCH 5/8] Handle division by zero in percentage calculation Added a check for division by zero when calculating completion percentage. --- js/completionCalculations.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/completionCalculations.js b/js/completionCalculations.js index 1218b63..6c8d539 100644 --- a/js/completionCalculations.js +++ b/js/completionCalculations.js @@ -132,7 +132,8 @@ class Completion extends Backbone.Controller { // this allows the user to see if assessments have been passed, if assessment components can be retaken, and all other component's completion const completed = completionObject.nonAssessmentCompleted + completionObject.assessmentCompleted + completionObject.subProgressCompleted; const total = completionObject.nonAssessmentTotal + completionObject.assessmentTotal + completionObject.subProgressTotal; - const percentageComplete = Math.floor((completed / total) * 100); + // Handle division by zero when page contains only optional content + const percentageComplete = total === 0 ? 0 : Math.floor((completed / total) * 100); return percentageComplete; } From b4b22fd7f2a842940795c796d76006151d93a8f8 Mon Sep 17 00:00:00 2001 From: Joseph Replin Date: Tue, 28 Oct 2025 17:07:57 -0500 Subject: [PATCH 6/8] Clean up getRenderData method comments Removed comments about optional content and aria labeling. --- js/PageLevelProgressIndicatorView.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/js/PageLevelProgressIndicatorView.js b/js/PageLevelProgressIndicatorView.js index f706a09..b3d51c7 100644 --- a/js/PageLevelProgressIndicatorView.js +++ b/js/PageLevelProgressIndicatorView.js @@ -99,14 +99,7 @@ class PageLevelProgressIndicatorView extends Backbone.View { const data = this.model.toJSON(); data.ariaLabel = this.ariaLabel; data.type = this.type; - - // Check if content is optional (set by diagnostic extension) data._isOptional = this.model.get('_isOptional') || false; - - // Note: do not generate a combined screen-reader string here. Visible labels - // will remain present and any aria labeling should be handled by the template - // or higher-level components to avoid duplicating responsibilities. - return data; } From 29db73434c1b9d818ea976887b32b32843e69e3f Mon Sep 17 00:00:00 2001 From: Joseph Replin Date: Tue, 28 Oct 2025 17:10:03 -0500 Subject: [PATCH 7/8] Update pageLevelProgressIndicator.less --- less/pageLevelProgressIndicator.less | 39 ---------------------------- 1 file changed, 39 deletions(-) diff --git a/less/pageLevelProgressIndicator.less b/less/pageLevelProgressIndicator.less index e1e443f..f2e29c2 100644 --- a/less/pageLevelProgressIndicator.less +++ b/less/pageLevelProgressIndicator.less @@ -4,50 +4,15 @@ &__indicator-outer { display: flex; flex-direction: column; - /* left-align the group within the outer container */ align-items: flex-start; } &__indicator-group { display: flex; flex-direction: column; - /* center the indicator and label horizontally as a group */ align-items: center; } - // Override core .aria-label (which is visually-hidden) for our - // indicator group so the message can be presented visually and - // styled similar to a header/title. Keep it lightweight. - &__indicator-group .aria-label { - position: static; - display: block; - width: auto; - height: auto; - margin: 0; - padding: 0; - overflow: visible; - clip: auto; - white-space: normal; - pointer-events: none; - font-size: 0.875rem; - font-weight: 600; - line-height: 1.1; - color: @black; - } - - /* Screen-reader-only text (accessible but not visible) */ - .sr-only { - position: absolute !important; - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - white-space: nowrap !important; - border: 0 !important; - } - &__indicator { display: flex; width: 2rem; @@ -83,8 +48,4 @@ line-height: 1; white-space: nowrap; } - - &__indicator .js-indicator-aria-label { - top: 0; - } } From 0b1e7954afa74dfdd4567c55fc64d3b708ef6d97 Mon Sep 17 00:00:00 2001 From: Joseph Replin Date: Tue, 11 Nov 2025 14:49:51 -0600 Subject: [PATCH 8/8] Added translatable label --- js/PageLevelProgressIndicatorView.js | 1 + templates/pageLevelProgressIndicator.jsx | 48 ++++++++++++++++-------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/js/PageLevelProgressIndicatorView.js b/js/PageLevelProgressIndicatorView.js index b3d51c7..604f188 100644 --- a/js/PageLevelProgressIndicatorView.js +++ b/js/PageLevelProgressIndicatorView.js @@ -99,6 +99,7 @@ class PageLevelProgressIndicatorView extends Backbone.View { const data = this.model.toJSON(); data.ariaLabel = this.ariaLabel; data.type = this.type; + data._globals = Adapt.course.get('_globals'); data._isOptional = this.model.get('_isOptional') || false; return data; } diff --git a/templates/pageLevelProgressIndicator.jsx b/templates/pageLevelProgressIndicator.jsx index 4c52e7b..2b1bcc8 100644 --- a/templates/pageLevelProgressIndicator.jsx +++ b/templates/pageLevelProgressIndicator.jsx @@ -9,29 +9,47 @@ export default function PageLevelProgressIndicator (props) { // Build aria-label with optional prefix if needed const compiledAriaLabel = ariaLabel ? compile(ariaLabel, props) : ''; - const fullAriaLabel = _isOptional ? `Optional. ${compiledAriaLabel}` : compiledAriaLabel; + const optionalLabel = props._globals?._accessibility?._ariaLabels?.optional || 'Optional'; + const fullAriaLabel = _isOptional ? `${optionalLabel}. ${compiledAriaLabel}` : compiledAriaLabel; - return ( - - - + // Only render wrapper group when optional label exists + if (_isOptional) { + return ( + + + + + - + {ariaLabel && + + {fullAriaLabel} + + } - {ariaLabel && - - {fullAriaLabel} - } + + + ); + } + + return ( + + + + + + {ariaLabel && + + {fullAriaLabel} + + } - {_isOptional && - - } - + ); };