From a8cac953d7ff22991f9456d98919959819c4c521 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Wed, 13 Feb 2019 16:43:38 +0000 Subject: [PATCH 1/6] Deal with lost focus when selection cleared This was missed out of the work on improving focus on the templates page. When you clear the current selection, the 'clear' link disappears so focus needs to be sent somewhere. --- app/assets/javascripts/templateFolderForm.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/templateFolderForm.js b/app/assets/javascripts/templateFolderForm.js index 302c2825f..516085c6c 100644 --- a/app/assets/javascripts/templateFolderForm.js +++ b/app/assets/javascripts/templateFolderForm.js @@ -35,7 +35,7 @@ // first off show the new template / new folder buttons this.currentState = this.$form.data('prev-state') || 'unknown'; if (this.currentState === 'unknown') { - this.selectActionButtons(false); + this.selectActionButtons(); } else { this.render(); } @@ -90,13 +90,14 @@ }; this.addClearButton = function(state) { - + let selector = 'button[value=add-new-template]'; let $clear = this.makeButton('Clear', () => { + // uncheck all templates and folders this.$form.find('input:checkbox').prop('checked', false); // go back to action buttons - this.selectActionButtons(); + this.selectActionButtons(selector); }); state.$el.find('.template-list-selected-counter').append($clear); From 050a513cf5bddb1b5a931319653b64cb39d5e331 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Wed, 13 Feb 2019 17:47:24 +0000 Subject: [PATCH 2/6] Update a11y API when selection count changes Inserts a hidden live region to ensure changes to the count are announced. The live region is hidden because it needs to be in the initial markup of the page. The visual counter is part of a larger region which is inserted/removed from the DOM. See https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions --- app/assets/javascripts/templateFolderForm.js | 18 +++++++++++++++--- app/templates/views/templates/_move_to.html | 3 +++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/templateFolderForm.js b/app/assets/javascripts/templateFolderForm.js index 516085c6c..79db7019e 100644 --- a/app/assets/javascripts/templateFolderForm.js +++ b/app/assets/javascripts/templateFolderForm.js @@ -11,6 +11,7 @@ this.$form.find('button[value=unknown]').remove(); this.$stickyBottom = this.$form.find('#sticky_template_forms'); + this.$liveRegionCounter = this.$form.find('.selection-counter'); this.$stickyBottom.append(this.nothingSelectedButtons); this.$stickyBottom.append(this.itemsSelectedButtons); @@ -135,6 +136,17 @@ this.render(); }; + this.selectionStatus = { + 'default': 'Nothing selected', + 'selected': numSelected => `${numSelected} selected`, + 'update': numSelected => { + let liveRegionHTML = (numSelected > 0) ? this.selectionStatus.selected(numSelected) : this.selectionStatus.default; + + $('.template-list-selected-counter-count').html(numSelected); + this.$liveRegionCounter.html(liveRegionHTML); + } + }; + this.templateFolderCheckboxChanged = function() { let numSelected = this.countSelectedCheckboxes(); @@ -148,7 +160,7 @@ this.render(); - $('.template-list-selected-counter-count').html(numSelected); + this.selectionStatus.update(numSelected); $('.template-list-selected-counter').toggle(this.hasCheckboxes()); @@ -188,7 +200,7 @@
- Nothing selected + ${this.selectionStatus.default}
@@ -200,7 +212,7 @@
- 1 selected + ${this.selectionStatus.selected(1)}
diff --git a/app/templates/views/templates/_move_to.html b/app/templates/views/templates/_move_to.html index 0b28555f5..05334cec0 100644 --- a/app/templates/views/templates/_move_to.html +++ b/app/templates/views/templates/_move_to.html @@ -36,4 +36,7 @@ {{ page_footer('Continue', button_name='operation', button_value='add-new-template') }} +
+ Nothing selected +
From 15104e9d25bc7c721f8b4b36cb6bbbe53b6fb9dd Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Fri, 15 Feb 2019 11:03:22 +0000 Subject: [PATCH 3/6] Re-position live-region to remove duplication Adding a visually hidden live-region creates duplication in the HTML. End result for users of screen readers are that you get the same text read out twice. This adds `aria-hidden` to hide the visible version and re-positions the live-region one next to it. That means the live-region text appears in the same place in the document order as the visible one so things are announced as expected. --- app/assets/javascripts/templateFolderForm.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/templateFolderForm.js b/app/assets/javascripts/templateFolderForm.js index 79db7019e..7fcb4ce6c 100644 --- a/app/assets/javascripts/templateFolderForm.js +++ b/app/assets/javascripts/templateFolderForm.js @@ -10,11 +10,10 @@ // which field is visible. this.$form.find('button[value=unknown]').remove(); - this.$stickyBottom = this.$form.find('#sticky_template_forms'); this.$liveRegionCounter = this.$form.find('.selection-counter'); - this.$stickyBottom.append(this.nothingSelectedButtons); - this.$stickyBottom.append(this.itemsSelectedButtons); + this.$liveRegionCounter.before(this.nothingSelectedButtons); + this.$liveRegionCounter.before(this.itemsSelectedButtons); // all the diff states that we want to show or hide this.states = [ @@ -180,7 +179,7 @@ // detach everything, unless they are the currentState this.states.forEach( - state => (state.key === this.currentState ? this.$stickyBottom.append(state.$el) : state.$el.detach()) + state => (state.key === this.currentState ? this.$liveRegionCounter.before(state.$el) : state.$el.detach()) ); // use dialog mode for states which contain more than one form control @@ -199,7 +198,7 @@
-
+
@@ -211,7 +210,7 @@
-
+
From 2d85469cd0b874e471777052277cb17a5cd76c0c Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Fri, 15 Feb 2019 13:49:49 +0000 Subject: [PATCH 4/6] Move 'Clear' link out of 'aria-hidden' region --- app/assets/javascripts/templateFolderForm.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/templateFolderForm.js b/app/assets/javascripts/templateFolderForm.js index 7fcb4ce6c..d15721af2 100644 --- a/app/assets/javascripts/templateFolderForm.js +++ b/app/assets/javascripts/templateFolderForm.js @@ -139,10 +139,10 @@ 'default': 'Nothing selected', 'selected': numSelected => `${numSelected} selected`, 'update': numSelected => { - let liveRegionHTML = (numSelected > 0) ? this.selectionStatus.selected(numSelected) : this.selectionStatus.default; + let message = (numSelected > 0) ? this.selectionStatus.selected(numSelected) : this.selectionStatus.default; - $('.template-list-selected-counter-count').html(numSelected); - this.$liveRegionCounter.html(liveRegionHTML); + $('.template-list-selected-counter__count').html(message); + this.$liveRegionCounter.html(message); } }; @@ -198,8 +198,10 @@
-
@@ -211,7 +213,9 @@
From 14553dcfd02ce6d38ddd4cf493ea9f0a3d1f41e5 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Fri, 15 Feb 2019 16:10:01 +0000 Subject: [PATCH 5/6] Give context to 'Cancel' and 'Clear' links Links need to work in isolation from their context in the page. This is an attempt at doing that. The one for 'Cancel' is still not ideal but 'Clear selection' gives more information than 'Clear' about what it does. Also adds a 'href' attribute to the link, without which its accessible role isn't recognised. --- app/assets/javascripts/templateFolderForm.js | 62 ++++++++++++-------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/templateFolderForm.js b/app/assets/javascripts/templateFolderForm.js index d15721af2..0db522984 100644 --- a/app/assets/javascripts/templateFolderForm.js +++ b/app/assets/javascripts/templateFolderForm.js @@ -76,45 +76,61 @@ this.addCancelButton = function(state) { let selector = `[value=${state.key}]`; - let $cancel = this.makeButton('Cancel', () => { + let $cancel = this.makeButton('Cancel', { + 'onclick': () => { - // clear existing data - state.$el.find('input:radio').prop('checked', false); - state.$el.find('input:text').val(''); + // clear existing data + state.$el.find('input:radio').prop('checked', false); + state.$el.find('input:text').val(''); - // go back to action buttons - this.selectActionButtons(selector); - }, selector); + // go back to action buttons + this.selectActionButtons(selector); + }, + 'cancelSelector': selector, + 'nonvisualText': "this step" + }); state.$el.find('[type=submit]').after($cancel); }; this.addClearButton = function(state) { let selector = 'button[value=add-new-template]'; - let $clear = this.makeButton('Clear', () => { + let $clear = this.makeButton('Clear', { + 'onclick': () => { - // uncheck all templates and folders - this.$form.find('input:checkbox').prop('checked', false); + // uncheck all templates and folders + this.$form.find('input:checkbox').prop('checked', false); - // go back to action buttons - this.selectActionButtons(selector); + // go back to action buttons + this.selectActionButtons(selector); + }, + 'nonvisualText': "selection" }); state.$el.find('.template-list-selected-counter').append($clear); }; - this.makeButton = (text, fn, cancelSelector) => $('') - .html(text) - .addClass('js-cancel') - .data('target', cancelSelector) // isn't set if cancelSelector is undefined - .attr('tabindex', '0') - .on('click keydown', event => { - // space, enter or no keyCode (must be mouse input) - if ([13, 32, undefined].indexOf(event.keyCode) > -1) { - event.preventDefault(); - fn(); + this.makeButton = (text, opts) => { + let $btn = $('') + .html(text) + .addClass('js-cancel') + // isn't set if cancelSelector is undefined + .data('target', opts.cancelSelector || undefined) + .attr('tabindex', '0') + .on('click keydown', event => { + // space, enter or no keyCode (must be mouse input) + if ([13, 32, undefined].indexOf(event.keyCode) > -1) { + event.preventDefault(); + if (opts.hasOwnProperty('onclick')) { opts.onclick(); } + } + }); + + if (opts.hasOwnProperty('nonvisualText')) { + $btn.append(` ${opts.nonvisualText}`); } - }); + + return $btn; + }; this.selectActionButtons = function (targetSelector) { // If we want to show one of the grey choose actions state, we can pretend we're in the choose actions state, From 9b180472d20f4e4c679c5176fa90a23f289c312d Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Fri, 15 Feb 2019 16:28:09 +0000 Subject: [PATCH 6/6] Fix selection counter on smaller screens --- app/assets/stylesheets/components/message.scss | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/components/message.scss b/app/assets/stylesheets/components/message.scss index 6c592fabc..37d21b2e1 100644 --- a/app/assets/stylesheets/components/message.scss +++ b/app/assets/stylesheets/components/message.scss @@ -131,10 +131,15 @@ } &-selected-counter { - position: absolute; - right: 0; - top: $gutter - 1px; color: $secondary-text-colour; + margin: $gutter-half 0; + + @include media(tablet) { + position: absolute; + right: 0; + top: $gutter - 1px; + margin: 0; + } .content-fixed & { right: $gutter-half;