From 25bf3d9997ae944c73ce3c50e705100072e047e8 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Thu, 16 Feb 2023 16:09:23 -0700 Subject: [PATCH 01/25] Create initial JSX file, change view template filename --- js/NarrativeView.js | 2 +- templates/narrative.jsx | 218 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 templates/narrative.jsx diff --git a/js/NarrativeView.js b/js/NarrativeView.js index 8cb6141..5e84b84 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -346,6 +346,6 @@ class NarrativeView extends ComponentView { } } -NarrativeView.template = 'narrative'; +NarrativeView.template = 'narrative.jsx'; export default NarrativeView; diff --git a/templates/narrative.jsx b/templates/narrative.jsx new file mode 100644 index 0000000..8189878 --- /dev/null +++ b/templates/narrative.jsx @@ -0,0 +1,218 @@ +import Adapt from 'core/js/adapt'; +import React from 'react'; +import { templates, classes } from 'core/js/reactHelpers'; + +export default function Narrative(props) { + + const ariaLabels = Adapt.course.get('_globals')?._accessibility?._ariaLabels; + + const { + _items, + _hasNavigationInTextArea, + _totalWidth + } = props; + + return ( +
+ + + +
+ +
+
+ + {_items.map(({ _index, _isVisited, title, body }) => + +
+ + {title && +
+
+
+ } + + {body && +
+
+
+ } + +
+ + )} + +
+ + + +
+ {_items.map(({ _index, _isVisited }) => + +
+ + )} +
+ + + +
+ +
+
+ +
+ +
+
+ + {_items.map(({ _index, _isVisited, strapline, _itemWidth }) => + + + + )} + +
+
+ +
+ +
+ +
+ + {_items.map(({ _index, _isVisited, _graphic, _itemWidth }) => + +
+ + + + {_graphic.attribution && +
+
+
+ } + +
+ + )} + +
+ + + + + +
+ +
+ + {_items.map(({ _index, _isVisited }) => +
+ )} + +
+ +
+ +
+ ); +} From 5450786d902325b7fdf889fcfa6bb0ff52db1333 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Tue, 21 Feb 2023 14:03:40 -0700 Subject: [PATCH 02/25] Move click events --- js/NarrativeView.js | 9 +++++---- templates/narrative.jsx | 25 ++++++++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/js/NarrativeView.js b/js/NarrativeView.js index 5e84b84..362b2ef 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -10,8 +10,6 @@ class NarrativeView extends ComponentView { events() { return { - 'click .js-narrative-strapline-open-popup': 'openPopup', - 'click .js-narrative-controls-click': 'onNavigationClicked', 'click .js-narrative-progress-click': 'onProgressClicked', 'swipeleft .js-narrative-swipe': 'onSwipeLeft', 'swiperight .js-narrative-swipe': 'onSwipeRight' @@ -22,6 +20,8 @@ class NarrativeView extends ComponentView { super.initialize(...args); this._isInitial = true; + this.onNavigationClicked = this.onNavigationClicked.bind(this); + this.openPopup = this.openPopup.bind(this); } preRender() { @@ -307,9 +307,10 @@ class NarrativeView extends ComponentView { }); } - onNavigationClicked(event) { - const $btn = $(event.currentTarget); + onNavigationClicked(e) { + const $btn = $(e.currentTarget); let index = this.model.getActiveItem().get('_index'); + $btn.data('direction') === 'right' ? index++ : index--; this.model.setActiveItem(index); } diff --git a/templates/narrative.jsx b/templates/narrative.jsx index 8189878..2bab26e 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -1,6 +1,6 @@ import Adapt from 'core/js/adapt'; import React from 'react'; -import { templates, classes } from 'core/js/reactHelpers'; +import { templates, compile, classes } from 'core/js/reactHelpers'; export default function Narrative(props) { @@ -9,6 +9,8 @@ export default function Narrative(props) { const { _items, _hasNavigationInTextArea, + onNavigationClicked, + openPopup, _totalWidth } = props; @@ -41,7 +43,7 @@ export default function Narrative(props) { className="narrative__content-title-inner" role="heading" aria-level="{{a11y_aria_level @root/_id 'componentItem' _ariaLevel}}" - dangerouslySetInnerHTML={{ __html: title }} /> + dangerouslySetInnerHTML={{ __html: compile(title, props) }} />
} @@ -49,7 +51,7 @@ export default function Narrative(props) {
} @@ -62,8 +64,9 @@ export default function Narrative(props) { @@ -84,8 +87,9 @@ export default function Narrative(props) { @@ -107,11 +111,12 @@ export default function Narrative(props) { From c6c13691aaafe3f944f511f16bee0eeef8ca76b5 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Tue, 21 Feb 2023 14:08:21 -0700 Subject: [PATCH 03/25] Fix item width --- templates/narrative.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/narrative.jsx b/templates/narrative.jsx index 2bab26e..27034d6 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -11,6 +11,7 @@ export default function Narrative(props) { _hasNavigationInTextArea, onNavigationClicked, openPopup, + _itemWidth, _totalWidth } = props; @@ -148,7 +149,7 @@ export default function Narrative(props) { style={{ width: _totalWidth + '%' }} > - {_items.map(({ _index, _isVisited, _graphic, _itemWidth }) => + {_items.map(({ _index, _isVisited, _graphic }) =>
From 62db272fdaabcb7d27b9b3bf7c89a22f1141bd72 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Tue, 21 Feb 2023 14:15:30 -0700 Subject: [PATCH 04/25] Fix strapline widths --- templates/narrative.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/narrative.jsx b/templates/narrative.jsx index 27034d6..c92e300 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -108,7 +108,7 @@ export default function Narrative(props) { style={{ width: _totalWidth + '%' }} > - {_items.map(({ _index, _isVisited, strapline, _itemWidth }) => + {_items.map(({ _index, _isVisited, strapline }) =>
- {_items.map(({ _index, _isVisited }) => + {_items.map(({ _index, _isVisited, _isActive }) =>
- {_items.map(({ _index, _isVisited }) => + {_items.map(({ _index, _isVisited, _isActive }) =>
Date: Wed, 22 Feb 2023 11:36:44 -0700 Subject: [PATCH 11/25] Move transform styles from moveSliderToIndex() to JSX --- js/NarrativeView.js | 6 +----- templates/narrative.jsx | 11 +++++++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/js/NarrativeView.js b/js/NarrativeView.js index 89ebe95..e98948f 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -199,12 +199,8 @@ class NarrativeView extends ComponentView { if (Adapt.config.get('_defaultDirection') === 'ltr') { offset *= -1; } - const cssValue = `translateX(${offset}%)`; - const $sliderElm = this.$('.narrative__slider'); - const $straplineHeaderElm = this.$('.narrative__strapline-header-inner'); - $sliderElm.css('transform', cssValue); - $straplineHeaderElm.css('transform', cssValue); + this.model.set('_translateXOffset', offset); } setStage(item) { diff --git a/templates/narrative.jsx b/templates/narrative.jsx index 5e7c479..40d8ba4 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -10,6 +10,7 @@ export default function Narrative(props) { const { _items, + _translateXOffset, _hasNavigationInTextArea, onNavigationClicked, openPopup, @@ -114,7 +115,10 @@ export default function Narrative(props) {
{_items.map(({ _index, _isVisited, strapline }) => @@ -155,7 +159,10 @@ export default function Narrative(props) {
{_items.map(({ _index, _isVisited, _graphic }) => From e0af34fee00e3e71da22bff0d4c5c6a521a0aa06 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Fri, 3 Mar 2023 10:39:07 -0700 Subject: [PATCH 12/25] Button enable/disable, aria-labels --- js/NarrativeView.js | 94 +++++++++++++++++++++++++++-------------- templates/narrative.jsx | 41 ++++++++++++------ 2 files changed, 92 insertions(+), 43 deletions(-) diff --git a/js/NarrativeView.js b/js/NarrativeView.js index e98948f..7d37169 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -5,6 +5,7 @@ import device from 'core/js/device'; import notify from 'core/js/notify'; import ComponentView from 'core/js/views/componentView'; import MODE from './modeEnum'; +import { compile } from 'core/js/reactHelpers'; class NarrativeView extends ComponentView { @@ -40,11 +41,16 @@ class NarrativeView extends ComponentView { onItemsActiveChange(item, _isActive) { if (!_isActive) return; + if (this.isTextBelowImage()) { item.toggleVisited(true); } + + const index = item.get('_index'); + + this.manageBackNextStates(index); this.setStage(item); - this.setFocus(item.get('_index')); + this.setFocus(index); } setFocus(itemIndex) { @@ -139,7 +145,7 @@ class NarrativeView extends ComponentView { const previousMode = this.model.get('_mode'); this.renderMode(); if (previousMode !== this.model.get('_mode')) this.replaceInstructions(); - this.evaluateNavigation(); + this.setupBackNextLabels(); const activeItem = this.model.getActiveItem(); if (activeItem) this.setStage(activeItem); } @@ -225,53 +231,79 @@ class NarrativeView extends ComponentView { a11y.toggleAccessibleEnabled($narrativeStraplineButtons, false); a11y.toggleAccessibleEnabled($narrativeStraplineButtons.filter(indexSelector), true); - this.evaluateNavigation(); + this.setupBackNextLabels(); this.evaluateCompletion(); this.shouldShowInstructionError(); this.moveSliderToIndex(index); } - evaluateNavigation() { - const active = this.model.getActiveItem(); - if (!active) return; + /** + * Controls whether the back and next buttons should be enabled + * + * @param {Number} [index] Item's index value. Defaults to the currently active item. + */ + manageBackNextStates(index = this.model.getActiveItem().get('_index')) { + const totalItems = this.model.getChildren().length; + const canCycleThroughPagination = this.model.get('_canCycleThroughPagination'); - const index = active.get('_index'); - const itemCount = this.model.getChildren().length; + const shouldEnableBack = index > 0 || canCycleThroughPagination; + const shouldEnableNext = index < totalItems - 1 || canCycleThroughPagination; - const isAtStart = index === 0; - const isAtEnd = index === itemCount - 1; + this.model.set('shouldEnableBack', shouldEnableBack); + this.model.set('shouldEnableNext', shouldEnableNext); + } - const $left = this.$('.narrative__controls-left'); - const $right = this.$('.narrative__controls-right'); + /** + * Construct back and next aria labels + * + * @param {Number} [index] Item's index value. + */ + setupBackNextLabels(index = this.model.getActiveItem().get('_index')) { + const totalItems = this.model.getChildren().length; + const canCycleThroughPagination = this.model.get('_canCycleThroughPagination'); - const globals = Adapt.course.get('_globals'); + const isAtStart = index === 0; + const isAtEnd = index === totalItems - 1; - const ariaLabelsGlobals = globals._accessibility._ariaLabels; + const globals = Adapt.course.get('_globals'); const narrativeGlobals = globals._components._narrative; - const ariaLabelPrevious = narrativeGlobals.previous || ariaLabelsGlobals.previous; - const ariaLabelNext = narrativeGlobals.next || ariaLabelsGlobals.next; - - const prevTitle = isAtStart ? '' : this.model.getItem(index - 1).get('title'); - const nextTitle = isAtEnd ? '' : this.model.getItem(index + 1).get('title'); - - a11y.toggleEnabled($left, !isAtStart); - a11y.toggleEnabled($right, !isAtEnd); + let prevTitle = isAtStart ? '' : this.model.getItem(index - 1).get('title'); + let nextTitle = isAtEnd ? '' : this.model.getItem(index + 1).get('title'); + + let backItem = isAtStart ? null : index; + let nextItem = isAtEnd ? null : index + 2; + + if (canCycleThroughPagination) { + if (isAtStart) { + prevTitle = this.model.getItem(totalItems - 1).get('title'); + backItem = totalItems; + } + if (isAtEnd) { + nextTitle = this.model.getItem(0).get('title'); + nextItem = 1; + } + } - $left.attr('aria-label', Handlebars.helpers.compile_a11y_normalize(ariaLabelPrevious, { + const backLabel = compile(narrativeGlobals.previous, { + _globals: globals, title: prevTitle, + itemNumber: backItem, + totalItems + }); + + const nextLabel = compile(narrativeGlobals.next, { _globals: globals, - itemNumber: isAtStart ? null : index, - totalItems: itemCount - })); - $right.attr('aria-label', Handlebars.helpers.compile_a11y_normalize(ariaLabelNext, { title: nextTitle, - _globals: globals, - itemNumber: isAtEnd ? null : index + 2, - totalItems: itemCount - })); + itemNumber: nextItem, + totalItems + }); + + this.model.set('backLabel', backLabel); + this.model.set('nextLabel', nextLabel); } + evaluateCompletion() { if (this.model.areAllItemsCompleted()) { this.trigger('allItems'); diff --git a/templates/narrative.jsx b/templates/narrative.jsx index 40d8ba4..c814b6e 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -1,4 +1,3 @@ -import Adapt from 'core/js/adapt'; import React from 'react'; import a11y from 'core/js/a11y'; import MODE from '../js/modeEnum'; @@ -6,8 +5,6 @@ import { templates, compile, classes } from 'core/js/reactHelpers'; export default function Narrative(props) { - const ariaLabels = Adapt.course.get('_globals')?._accessibility?._ariaLabels; - const { _items, _translateXOffset, @@ -17,7 +14,11 @@ export default function Narrative(props) { _itemWidth, _totalWidth, _mode, - _isTextBelowImageResolved + _isTextBelowImageResolved, + backLabel, + nextLabel, + shouldEnableBack, + shouldEnableNext } = props; return ( @@ -74,8 +75,12 @@ export default function Narrative(props) { - -
- {{#each _items}} -
- {{/each}} -
- - -
- -
-
- -
- -
-
- - {{#each _items}} - - {{/each}} - -
-
- -
- -
- -
- - {{#each _items}} -
- - - - {{#if _graphic.attribution}} -
-
- {{{_graphic.attribution}}} -
-
- {{/if}} - -
- {{/each}} - -
- - - - - -
- -
- - {{#each _items}} -
- {{/each}} - -
- -
-
diff --git a/templates/narrative.jsx b/templates/narrative.jsx index c814b6e..651910d 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -38,13 +38,15 @@ export default function Narrative(props) {
- {_items.map(({ _index, _isVisited, title, body, _ariaLevel }) => + {_items.map(({ _index, _isActive, _isVisited, title, body, _ariaLevel }) =>
@@ -174,15 +176,17 @@ export default function Narrative(props) { }} > - {_items.map(({ _index, _isVisited, _graphic }) => + {_items.map(({ _index, _isActive, _isVisited, _graphic }) =>
From 2dda0477babc16aeca3009f97d01a6125feaad95 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Fri, 3 Mar 2023 10:59:07 -0700 Subject: [PATCH 14/25] Replace user-drag CSS property with draggable HTML property --- less/narrative.less | 5 ----- templates/narrative.jsx | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/less/narrative.less b/less/narrative.less index 8803ba5..1e38e64 100755 --- a/less/narrative.less +++ b/less/narrative.less @@ -96,11 +96,6 @@ &__slider-image { .u-no-select; - -webkit-user-drag: none; - -khtml-user-drag: none; - -moz-user-drag: none; - -o-user-drag: none; - user-drag: none; } // Button controls diff --git a/templates/narrative.jsx b/templates/narrative.jsx index 651910d..f1c2429 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -196,6 +196,7 @@ export default function Narrative(props) { src={_graphic.src} aria-label={a11y.normalize(_graphic.alt) || null} aria-hidden={!_graphic.alt || null} + draggable="false" /> {_graphic.attribution && From 57ecec78961cee9ca1c9f95a2916bcc9da393eb8 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Fri, 3 Mar 2023 11:03:27 -0700 Subject: [PATCH 15/25] Remove onItemsVisitedChange() --- js/NarrativeView.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/js/NarrativeView.js b/js/NarrativeView.js index 5dbd7f7..54ce793 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -32,8 +32,7 @@ class NarrativeView extends ComponentView { this.renderMode(); this.listenTo(this.model.getChildren(), { - 'change:_isActive': this.onItemsActiveChange, - 'change:_isVisited': this.onItemsVisitedChange + 'change:_isActive': this.onItemsActiveChange }); this.calculateWidths(); @@ -75,11 +74,6 @@ class NarrativeView extends ComponentView { a11y.focusFirst($elementToFocus); } - onItemsVisitedChange(item, _isVisited) { - if (!_isVisited) return; - this.$(`[data-index="${item.get('_index')}"]`).addClass('is-visited'); - } - calculateMode() { const mode = device.screenSize === 'large' ? MODE.LARGE : MODE.SMALL; this.model.set('_mode', mode); From 4b3695e5b8438a62c8be8344b76008eea2772c0e Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Fri, 3 Mar 2023 12:56:45 -0700 Subject: [PATCH 16/25] Add visibility hidden to narrative__content-item --- less/narrative.less | 1 + 1 file changed, 1 insertion(+) diff --git a/less/narrative.less b/less/narrative.less index 1e38e64..bb3123b 100755 --- a/less/narrative.less +++ b/less/narrative.less @@ -17,6 +17,7 @@ &__content-item:not(.is-active) { .u-display-none; + .u-visibility-hidden; } // Strapline From 18d45317316ff5df6badd187f8af6d3185a07d56 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Fri, 3 Mar 2023 15:28:19 -0700 Subject: [PATCH 17/25] Focus functionality --- js/NarrativeView.js | 50 ++++++++++++++++++++--------------------- templates/narrative.jsx | 33 +++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 27 deletions(-) diff --git a/js/NarrativeView.js b/js/NarrativeView.js index 54ce793..3b9be12 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -1,6 +1,5 @@ import Adapt from 'core/js/adapt'; import components from 'core/js/components'; -import a11y from 'core/js/a11y'; import device from 'core/js/device'; import notify from 'core/js/notify'; import ComponentView from 'core/js/views/componentView'; @@ -19,7 +18,8 @@ class NarrativeView extends ComponentView { initialize(...args) { super.initialize(...args); - this._isInitial = true; + this.model.set('_isInitial', true); + this.model.set('_activeItemIndex', 0); this.onNavigationClicked = this.onNavigationClicked.bind(this); this.openPopup = this.openPopup.bind(this); } @@ -46,37 +46,37 @@ class NarrativeView extends ComponentView { } const index = item.get('_index'); + this.model.set('_activeItemIndex', index); this.manageBackNextStates(index); this.setStage(item); - this.setFocus(index); } - setFocus(itemIndex) { - if (this._isInitial) return; - const $straplineHeaderElm = this.$('.narrative__strapline-header-inner'); - const hasStraplineTransition = !this.isLargeMode() && ($straplineHeaderElm.css('transitionDuration') !== '0s'); - if (hasStraplineTransition) { - $straplineHeaderElm.one('transitionend', () => { - this.focusOnNarrativeElement(itemIndex); - }); - return; - } - - this.focusOnNarrativeElement(itemIndex); - } - - focusOnNarrativeElement(itemIndex) { - const dataIndexAttr = `[data-index='${itemIndex}']`; - const $elementToFocus = this.isLargeMode() ? - this.$(`.narrative__content-item${dataIndexAttr}`) : - this.$(`.narrative__strapline-btn${dataIndexAttr}`); - a11y.focusFirst($elementToFocus); - } + // setFocus(itemIndex) { + // const $straplineHeaderElm = this.$('.narrative__strapline-header-inner'); + // const hasStraplineTransition = !this.isLargeMode() && ($straplineHeaderElm.css('transitionDuration') !== '0s'); + // if (hasStraplineTransition) { + // $straplineHeaderElm.one('transitionend', () => { + // this.focusOnNarrativeElement(itemIndex); + // }); + // return; + // } + + // this.focusOnNarrativeElement(itemIndex); + // } + + // focusOnNarrativeElement(itemIndex) { + // const dataIndexAttr = `[data-index='${itemIndex}']`; + // const $elementToFocus = this.isLargeMode() ? + // this.$(`.narrative__content-item${dataIndexAttr}`) : + // this.$(`.narrative__strapline-btn${dataIndexAttr}`); + // a11y.focusFirst($elementToFocus); + // } calculateMode() { const mode = device.screenSize === 'large' ? MODE.LARGE : MODE.SMALL; this.model.set('_mode', mode); + this.model.set('_isLargeMode', mode === MODE.LARGE); } renderMode() { @@ -124,7 +124,7 @@ class NarrativeView extends ComponentView { this.replaceInstructions(); } this.setupEventListeners(); - this._isInitial = false; + this.model.set('_isInitial', false); } calculateWidths() { diff --git a/templates/narrative.jsx b/templates/narrative.jsx index f1c2429..ce419a2 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import a11y from 'core/js/a11y'; import MODE from '../js/modeEnum'; import { templates, compile, classes } from 'core/js/reactHelpers'; @@ -18,9 +18,38 @@ export default function Narrative(props) { backLabel, nextLabel, shouldEnableBack, - shouldEnableNext + shouldEnableNext, + _isInitial, + _isLargeMode, + _activeItemIndex } = props; + useEffect(() => { + if (_isInitial || _activeItemIndex === undefined) return; + + const itemIndex = _activeItemIndex; + const $straplineHeaderElm = $('.narrative__strapline-header-inner'); + const hasStraplineTransition = !_isLargeMode && ($straplineHeaderElm.css('transitionDuration') !== '0s'); + if (hasStraplineTransition) { + $straplineHeaderElm.one('transitionend', () => { + focusOnNarrativeElement(itemIndex); + }); + return; + } + + focusOnNarrativeElement(itemIndex); + }); + + const focusOnNarrativeElement = (itemIndex) => { + const dataIndexAttr = `[data-index='${itemIndex}']`; + + const $elementToFocus = _isLargeMode ? + $(`.narrative__content-item${dataIndexAttr}`) : + $(`.narrative__strapline-btn${dataIndexAttr}`); + + a11y.focusFirst($elementToFocus); + }; + return (
Date: Fri, 3 Mar 2023 15:33:31 -0700 Subject: [PATCH 18/25] Remove comments --- js/NarrativeView.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/js/NarrativeView.js b/js/NarrativeView.js index 3b9be12..5b5b6c6 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -52,27 +52,6 @@ class NarrativeView extends ComponentView { this.setStage(item); } - // setFocus(itemIndex) { - // const $straplineHeaderElm = this.$('.narrative__strapline-header-inner'); - // const hasStraplineTransition = !this.isLargeMode() && ($straplineHeaderElm.css('transitionDuration') !== '0s'); - // if (hasStraplineTransition) { - // $straplineHeaderElm.one('transitionend', () => { - // this.focusOnNarrativeElement(itemIndex); - // }); - // return; - // } - - // this.focusOnNarrativeElement(itemIndex); - // } - - // focusOnNarrativeElement(itemIndex) { - // const dataIndexAttr = `[data-index='${itemIndex}']`; - // const $elementToFocus = this.isLargeMode() ? - // this.$(`.narrative__content-item${dataIndexAttr}`) : - // this.$(`.narrative__strapline-btn${dataIndexAttr}`); - // a11y.focusFirst($elementToFocus); - // } - calculateMode() { const mode = device.screenSize === 'large' ? MODE.LARGE : MODE.SMALL; this.model.set('_mode', mode); From 1cc7ee90a49178e982c1888cb419ac2e9569c515 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Fri, 3 Mar 2023 15:44:20 -0700 Subject: [PATCH 19/25] Instruction error styles --- less/narrative.less | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/less/narrative.less b/less/narrative.less index bb3123b..5a5e6c9 100755 --- a/less/narrative.less +++ b/less/narrative.less @@ -1,4 +1,15 @@ .narrative { + &__instruction-inner.instruction-error { + color: @validation-error; + .icon; + .icon-exclamation; + + &::before { + display: inline-block; + vertical-align: middle; + } + } + &__inner:not(.items-are-full-width) &__content { display: none; From b015a5edd796d51c09513ef044e43401f96d0469 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Wed, 8 Mar 2023 15:45:45 -0700 Subject: [PATCH 20/25] Check for element before trying to focus --- templates/narrative.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/narrative.jsx b/templates/narrative.jsx index ce419a2..82f48e6 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -47,7 +47,9 @@ export default function Narrative(props) { $(`.narrative__content-item${dataIndexAttr}`) : $(`.narrative__strapline-btn${dataIndexAttr}`); - a11y.focusFirst($elementToFocus); + if ($elementToFocus.length) { + a11y.focusFirst($elementToFocus); + } }; return ( From 6c456b5e0bb493497914d5014afb446bd9fd3702 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Wed, 8 Mar 2023 15:48:02 -0700 Subject: [PATCH 21/25] Check prefers-reduced-motion preference for transitions --- less/narrative.less | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/less/narrative.less b/less/narrative.less index 5a5e6c9..e3c9859 100755 --- a/less/narrative.less +++ b/less/narrative.less @@ -179,7 +179,9 @@ html:not(.disable-animation) & { &__slider, &__strapline-header-inner { - .transition(transform 400ms ease-in-out); + @media (prefers-reduced-motion: no-preference) { + .transition(transform 400ms ease-in-out); + } } } From c64d2f48a9f812e03f5fefbd7c623d85c81aca9a Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Fri, 10 Mar 2023 08:09:49 -0700 Subject: [PATCH 22/25] Return early from focusOnNarrativeElement() --- templates/narrative.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/narrative.jsx b/templates/narrative.jsx index 82f48e6..5490162 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -47,9 +47,9 @@ export default function Narrative(props) { $(`.narrative__content-item${dataIndexAttr}`) : $(`.narrative__strapline-btn${dataIndexAttr}`); - if ($elementToFocus.length) { - a11y.focusFirst($elementToFocus); - } + if (!$elementToFocus.length) return; + + a11y.focusFirst($elementToFocus); }; return ( From 29051411bf67f7de0adf4a77c741ef5b29f8ba9f Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Mon, 13 Mar 2023 15:39:30 -0600 Subject: [PATCH 23/25] Use ref for focus in focusOnNarrativeElement --- templates/narrative.jsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/templates/narrative.jsx b/templates/narrative.jsx index 5490162..4dea644 100644 --- a/templates/narrative.jsx +++ b/templates/narrative.jsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import a11y from 'core/js/a11y'; import MODE from '../js/modeEnum'; import { templates, compile, classes } from 'core/js/reactHelpers'; @@ -24,6 +24,8 @@ export default function Narrative(props) { _activeItemIndex } = props; + const narrativeWidgetRef = useRef(null); + useEffect(() => { if (_isInitial || _activeItemIndex === undefined) return; @@ -41,11 +43,9 @@ export default function Narrative(props) { }); const focusOnNarrativeElement = (itemIndex) => { - const dataIndexAttr = `[data-index='${itemIndex}']`; - - const $elementToFocus = _isLargeMode ? - $(`.narrative__content-item${dataIndexAttr}`) : - $(`.narrative__strapline-btn${dataIndexAttr}`); + const focusClass = _isLargeMode ? '.narrative__content-item' : '.narrative__strapline-btn'; + const dataAttr = `[data-index='${itemIndex}']`; + const $elementToFocus = $(narrativeWidgetRef.current).find(focusClass + dataAttr); if (!$elementToFocus.length) return; @@ -57,14 +57,17 @@ export default function Narrative(props) { 'component__inner narrative__inner', _mode === MODE.LARGE ? 'mode-large' : 'mode-small', _isTextBelowImageResolved && 'items-are-full-width' - ])}> + ])} + >
+ ])} + ref={narrativeWidgetRef} + >
@@ -79,7 +82,8 @@ export default function Narrative(props) { ])} aria-hidden={!_isActive || null} data-index={_index} - key={_index}> + key={_index} + > {title &&
From 901d019f68020d70b3bc80162f26a9039fb27530 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Mon, 13 Mar 2023 15:54:33 -0600 Subject: [PATCH 24/25] Remove error styles, change class to 'has-error' and move to narrative__instruction --- js/NarrativeView.js | 2 +- less/narrative.less | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/js/NarrativeView.js b/js/NarrativeView.js index 5b5b6c6..b524635 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -306,7 +306,7 @@ class NarrativeView extends ComponentView { shouldShowInstructionError() { const prevItemIndex = this.model.getActiveItem().get('_index') - 1; if (prevItemIndex < 0 || this.model.getItem(prevItemIndex).get('_isVisited')) return; - this.$('.narrative__instruction-inner').addClass('instruction-error'); + this.$('.narrative__instruction').addClass('has-error'); } setupEventListeners() { diff --git a/less/narrative.less b/less/narrative.less index e3c9859..c4062ea 100755 --- a/less/narrative.less +++ b/less/narrative.less @@ -1,15 +1,4 @@ .narrative { - &__instruction-inner.instruction-error { - color: @validation-error; - .icon; - .icon-exclamation; - - &::before { - display: inline-block; - vertical-align: middle; - } - } - &__inner:not(.items-are-full-width) &__content { display: none; From f96a583c3f4c5f686115ccef9ab1dcbdc470c960 Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Mon, 13 Mar 2023 16:14:50 -0600 Subject: [PATCH 25/25] Instruction error fixes --- js/NarrativeView.js | 2 +- less/narrative.less | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/js/NarrativeView.js b/js/NarrativeView.js index b524635..0ca1845 100644 --- a/js/NarrativeView.js +++ b/js/NarrativeView.js @@ -264,7 +264,7 @@ class NarrativeView extends ComponentView { evaluateCompletion() { if (this.model.areAllItemsCompleted()) { this.trigger('allItems'); - this.$('.narrative__instruction-inner').removeClass('instruction-error'); + this.$('.narrative__instruction').removeClass('has-error'); } } diff --git a/less/narrative.less b/less/narrative.less index c4062ea..e8b7717 100755 --- a/less/narrative.less +++ b/less/narrative.less @@ -1,4 +1,13 @@ .narrative { + &__instruction.has-error { + display: flex; + color: @validation-error; + + .icon { + .icon-exclamation; + } + } + &__inner:not(.items-are-full-width) &__content { display: none;