diff --git a/js/PageLevelProgressIndicatorView.js b/js/PageLevelProgressIndicatorView.js index cb8902d..604f188 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,8 @@ 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/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; } diff --git a/less/pageLevelProgressIndicator.less b/less/pageLevelProgressIndicator.less index 51f8938..f2e29c2 100644 --- a/less/pageLevelProgressIndicator.less +++ b/less/pageLevelProgressIndicator.less @@ -1,6 +1,18 @@ // Global indicator // -------------------------------------------------- .pagelevelprogress { + &__indicator-outer { + display: flex; + flex-direction: column; + align-items: flex-start; + } + + &__indicator-group { + display: flex; + flex-direction: column; + align-items: center; + } + &__indicator { display: flex; width: 2rem; @@ -28,7 +40,12 @@ width: var(--adapt-pagelevelprogress-percentage); } - &__indicator .js-indicator-aria-label { - top: 0; + &__indicator-label { + font-size: 0.5rem; + font-weight: normal; + text-align: center; + margin-top: 0.125rem; + line-height: 1; + white-space: nowrap; } } diff --git a/templates/pageLevelProgressIndicator.jsx b/templates/pageLevelProgressIndicator.jsx index 3701672..2b1bcc8 100644 --- a/templates/pageLevelProgressIndicator.jsx +++ b/templates/pageLevelProgressIndicator.jsx @@ -3,9 +3,40 @@ 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 optionalLabel = props._globals?._accessibility?._ariaLabels?.optional || 'Optional'; + const fullAriaLabel = _isOptional ? `${optionalLabel}. ${compiledAriaLabel}` : compiledAriaLabel; + + // Only render wrapper group when optional label exists + if (_isOptional) { + return ( + + + + + + + {ariaLabel && + + {fullAriaLabel} + + } + + + + + + + ); + } + return ( @@ -14,7 +45,7 @@ export default function PageLevelProgressIndicator (props) { {ariaLabel && - {compile(ariaLabel, props)} + {fullAriaLabel} }