From 4368e760b6f0ca8f7d83b0fc33ac311447f1fe96 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 19 Apr 2017 12:03:32 -0300 Subject: [PATCH 01/14] dropdown item Keyboard navigation for ARIA More closely aligns with proposed Bootstrap V4 dropdown keyboard navigation. (similar to how native select elements work) When dropdown opens, the dropdown menu is focused. Keyboard up/down will navigate the dropdown list. tabbing out of the dropdown will close the dropdown (and focus the next element based on normal tab flow). hitting ESC will also close the dropdown and refocus either the button or toggle button (depending on if the button is split or not). Also made the .sr-only "Toggle Dropdown" text a prop (toggleText) to allow for different languages. --- lib/components/dropdown.vue | 60 +++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/lib/components/dropdown.vue b/lib/components/dropdown.vue index 11e094b98bf..15cb67c0d68 100755 --- a/lib/components/dropdown.vue +++ b/lib/components/dropdown.vue @@ -2,6 +2,7 @@
- Toggle Dropdown + {{toggleText}} -
+
@@ -72,6 +81,14 @@ right: { type: Boolean, default: false + }, + closeOnEsc: { + type: Boolean, + default: true + }, + toggletext: { + type: String, + default: 'Toggle Dropdown' } }, created() { @@ -97,6 +114,10 @@ methods: { toggle() { this.visible = !this.visible; + if (this.visible) { + // focus the dropdown-menu + this.$refs.menu.focus(); + } }, clickOutListener() { this.visible = false; @@ -109,6 +130,41 @@ this.toggle(); } } + onEsc(e) { + if (!this.visible || !this.closeOnEsc) { + return; + } + this.visble = false; + // Return focus to button/toggle + (this.split ? this.$refs.toggle : this.$refs.button).focus(); + // In case dropdown inside Modal + e.stopPropagation(); + }, + onKeyMove(e, up) { + if (!this.visible || this.disabled) { + return; + } + // get current list of enabled dropdown-items + // if ES6 then items = [...this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')] + var items = [].slice.call(this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')); + if (!items.length) { + return; + } + items.forEach(function(i) { + // ensure dropdown-items are not in tab sequence, but still focusable + i.setAttribute('tabindex','-1'); + } + var index = items.indexOf(e.target); + if (up) { + index--; + } else if (index < items.length) { + index++; + } + if (index < 0) { + index = 0; + } + items[index].focus(); + } } }; From 8d9498e1c7c045771822e7e57ed190aa87595046 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 19 Apr 2017 12:18:47 -0300 Subject: [PATCH 02/14] Changed tabs to spaces --- lib/components/dropdown.vue | 78 ++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/lib/components/dropdown.vue b/lib/components/dropdown.vue index 15cb67c0d68..121b036403a 100755 --- a/lib/components/dropdown.vue +++ b/lib/components/dropdown.vue @@ -114,10 +114,10 @@ methods: { toggle() { this.visible = !this.visible; - if (this.visible) { - // focus the dropdown-menu - this.$refs.menu.focus(); - } + if (this.visible) { + // focus the dropdown-menu + this.$refs.menu.focus(); + } }, clickOutListener() { this.visible = false; @@ -129,42 +129,42 @@ } else { this.toggle(); } - } - onEsc(e) { - if (!this.visible || !this.closeOnEsc) { - return; - } - this.visble = false; - // Return focus to button/toggle - (this.split ? this.$refs.toggle : this.$refs.button).focus(); - // In case dropdown inside Modal - e.stopPropagation(); - }, - onKeyMove(e, up) { - if (!this.visible || this.disabled) { - return; - } - // get current list of enabled dropdown-items - // if ES6 then items = [...this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')] - var items = [].slice.call(this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')); - if (!items.length) { - return; - } - items.forEach(function(i) { - // ensure dropdown-items are not in tab sequence, but still focusable - i.setAttribute('tabindex','-1'); - } - var index = items.indexOf(e.target); - if (up) { - index--; - } else if (index < items.length) { - index++; - } - if (index < 0) { - index = 0; + }, + onEsc(e) { + if (!this.visible || !this.closeOnEsc) { + return; + } + this.visble = false; + // Return focus to button/toggle + (this.split ? this.$refs.toggle : this.$refs.button).focus(); + // In case dropdown inside Modal + e.stopPropagation(); + }, + onKeyMove(e, up) { + if (!this.visible || this.disabled) { + return; + } + // get current list of enabled dropdown-items + // if ES6 then items = [...this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')] + var items = [].slice.call(this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')); + if (!items.length) { + return; + } + items.forEach(function(i) { + // ensure dropdown-items are not in tab sequence, but still focusable + i.setAttribute('tabindex','-1'); + } + var index = items.indexOf(e.target); + if (up) { + index--; + } else if (index < items.length) { + index++; + } + if (index < 0) { + index = 0; } - items[index].focus(); - } + items[index].focus(); + } } }; From 1a7dc5737daa0476b233d841f5c2c5ed7f9bd104 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 19 Apr 2017 12:20:35 -0300 Subject: [PATCH 03/14] Changed tabs to spaces --- lib/components/dropdown.vue | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/components/dropdown.vue b/lib/components/dropdown.vue index 121b036403a..a1ebcaae6b7 100755 --- a/lib/components/dropdown.vue +++ b/lib/components/dropdown.vue @@ -26,10 +26,10 @@
@@ -162,7 +162,7 @@ } if (index < 0) { index = 0; - } + } items[index].focus(); } } From 31194f20f6159a5bf95b831124a2311ac172ce94 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 19 Apr 2017 12:30:21 -0300 Subject: [PATCH 04/14] fixed typo on toggleText prop --- lib/components/dropdown.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/dropdown.vue b/lib/components/dropdown.vue index a1ebcaae6b7..df92136bbaa 100755 --- a/lib/components/dropdown.vue +++ b/lib/components/dropdown.vue @@ -86,7 +86,7 @@ type: Boolean, default: true }, - toggletext: { + toggleText: { type: String, default: 'Toggle Dropdown' } From 4085d1153e75ed88416bed5fb5d1d48d0a2ef436 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 19 Apr 2017 12:34:23 -0300 Subject: [PATCH 05/14] Typo --- lib/components/dropdown.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/dropdown.vue b/lib/components/dropdown.vue index df92136bbaa..27d5eebf592 100755 --- a/lib/components/dropdown.vue +++ b/lib/components/dropdown.vue @@ -153,7 +153,7 @@ items.forEach(function(i) { // ensure dropdown-items are not in tab sequence, but still focusable i.setAttribute('tabindex','-1'); - } + }); var index = items.indexOf(e.target); if (up) { index--; From dae544961468341fe8e97732dc95f0cbae495dc2 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 19 Apr 2017 12:52:28 -0300 Subject: [PATCH 06/14] Better ES6 syntax --- lib/components/dropdown.vue | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/components/dropdown.vue b/lib/components/dropdown.vue index 27d5eebf592..27c613c7a0e 100755 --- a/lib/components/dropdown.vue +++ b/lib/components/dropdown.vue @@ -115,7 +115,7 @@ toggle() { this.visible = !this.visible; if (this.visible) { - // focus the dropdown-menu + // Focus the dropdown-menu this.$refs.menu.focus(); } }, @@ -144,17 +144,16 @@ if (!this.visible || this.disabled) { return; } - // get current list of enabled dropdown-items - // if ES6 then items = [...this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')] - var items = [].slice.call(this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')); - if (!items.length) { + // Get current list of enabled dropdown-items + const items = [...this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')]; + if (items.length > 0) { return; } - items.forEach(function(i) { - // ensure dropdown-items are not in tab sequence, but still focusable - i.setAttribute('tabindex','-1'); + items.forEach( (i) => { + // Ensure dropdown-items are not in tab sequence, but still focusable + i.setAttribute('tabindex', '-1'); }); - var index = items.indexOf(e.target); + let index = items.indexOf(e.target); if (up) { index--; } else if (index < items.length) { From 7097b5f24138da737badf609cb7a63c408592f85 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 19 Apr 2017 13:18:48 -0300 Subject: [PATCH 07/14] Update dropdown.vue --- lib/components/dropdown.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/dropdown.vue b/lib/components/dropdown.vue index 27c613c7a0e..43898d701c3 100755 --- a/lib/components/dropdown.vue +++ b/lib/components/dropdown.vue @@ -149,7 +149,7 @@ if (items.length > 0) { return; } - items.forEach( (i) => { + items.forEach(i => { // Ensure dropdown-items are not in tab sequence, but still focusable i.setAttribute('tabindex', '-1'); }); From 601550bdbd522a9bd74ddf2245f7782308fac4d7 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 19 Apr 2017 15:44:45 -0300 Subject: [PATCH 08/14] Revert for now --- lib/components/dropdown.vue | 59 ++----------------------------------- 1 file changed, 2 insertions(+), 57 deletions(-) diff --git a/lib/components/dropdown.vue b/lib/components/dropdown.vue index 43898d701c3..11e094b98bf 100755 --- a/lib/components/dropdown.vue +++ b/lib/components/dropdown.vue @@ -2,7 +2,6 @@
- {{toggleText}} + Toggle Dropdown -
+
@@ -81,14 +72,6 @@ right: { type: Boolean, default: false - }, - closeOnEsc: { - type: Boolean, - default: true - }, - toggleText: { - type: String, - default: 'Toggle Dropdown' } }, created() { @@ -114,10 +97,6 @@ methods: { toggle() { this.visible = !this.visible; - if (this.visible) { - // Focus the dropdown-menu - this.$refs.menu.focus(); - } }, clickOutListener() { this.visible = false; @@ -129,40 +108,6 @@ } else { this.toggle(); } - }, - onEsc(e) { - if (!this.visible || !this.closeOnEsc) { - return; - } - this.visble = false; - // Return focus to button/toggle - (this.split ? this.$refs.toggle : this.$refs.button).focus(); - // In case dropdown inside Modal - e.stopPropagation(); - }, - onKeyMove(e, up) { - if (!this.visible || this.disabled) { - return; - } - // Get current list of enabled dropdown-items - const items = [...this.$refs.menu.querySelectorAll('.dropdown-item:not(.disabled)')]; - if (items.length > 0) { - return; - } - items.forEach(i => { - // Ensure dropdown-items are not in tab sequence, but still focusable - i.setAttribute('tabindex', '-1'); - }); - let index = items.indexOf(e.target); - if (up) { - index--; - } else if (index < items.length) { - index++; - } - if (index < 0) { - index = 0; - } - items[index].focus(); } } }; From db36d4c53173708855d887547f29f7d10f401ca5 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 20 Apr 2017 14:26:46 -0300 Subject: [PATCH 09/14] Fixed feedback class, & change choosing target ID Changed feedback text block from class 'form-text' to 'form-control-feedback' to convey state. Added ARIA role="alert" to feedback div. Changed the target select from first child of content to querySelector() of first input element in content ref. --- lib/components/form-fieldset.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/components/form-fieldset.vue b/lib/components/form-fieldset.vue index 5db29694c91..a2b8af1d8c4 100644 --- a/lib/components/form-fieldset.vue +++ b/lib/components/form-fieldset.vue @@ -3,7 +3,7 @@
-
+
@@ -32,7 +32,7 @@ if (!content) { return; } - this.target = content.children[0].id; + this.target = content.querySelector(this.inputSelector).id || false; }, props: { state: { @@ -58,6 +58,10 @@ feedback: { type: String, default: null + }, + inputSelector: { + type: String, + default: 'input, select, textarea' } } }; From 94fc62927ada7f62ec352916d05dd1eeace6fad6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 20 Apr 2017 19:02:39 -0300 Subject: [PATCH 10/14] Fixed form-option disabled bug Option `disabled` wasn't being factored into `formOptions()` computed result. --- lib/mixins/form-options.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/mixins/form-options.js b/lib/mixins/form-options.js index fdf9063b032..9faa54a3f67 100755 --- a/lib/mixins/form-options.js +++ b/lib/mixins/form-options.js @@ -9,7 +9,8 @@ export default { if (typeof option === 'object') { return { value: option[this.valueField], - text: option[this.textField] + text: option[this.textField], + disabled: option.disabled }; } From b5ace13c5aa50590191d4abf1cee3898d89ff1fd Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 20 Apr 2017 19:07:14 -0300 Subject: [PATCH 11/14] Fixed form-option disabled bug Option disabled wasn't being factored into formOptions() Return disabled = false if "falsey" --- lib/mixins/form-options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mixins/form-options.js b/lib/mixins/form-options.js index 9faa54a3f67..e194e5f2787 100755 --- a/lib/mixins/form-options.js +++ b/lib/mixins/form-options.js @@ -10,7 +10,7 @@ export default { return { value: option[this.valueField], text: option[this.textField], - disabled: option.disabled + disabled: option.disabled || false }; } From b8350641dafccbc1f46f374b0a56fe82fd75967b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 20 Apr 2017 19:44:54 -0300 Subject: [PATCH 12/14] Merging latest master commits (#6) * Fix validation on 'offset' prop in Popover * Add 'targetOffset' prop to Popover * Fix lint tests - comment edit just included to permit commit --- lib/components/popover.vue | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/components/popover.vue b/lib/components/popover.vue index cba9906eacd..336448b1811 100755 --- a/lib/components/popover.vue +++ b/lib/components/popover.vue @@ -89,7 +89,8 @@ type: String, default: '0 0', validator(value) { - return /^(\d+\s\d+)$/.test(value); + // Regex test for a pair of units, either 0 exactly, px, or percentage + return /^((0\s?)|([+-]?[0-9]+(px|%)\s?)){2}$/.test(value); } }, placement: { @@ -107,6 +108,14 @@ type: Boolean, default: null }, + targetOffset: { + type: String, + default: '0 0', + validator(value) { + // Regex test for a pair of units, either 0 exactly, px, or percentage + return /^((0\s?)|([+-]?[0-9]+(px|%)\s?)){2}$/.test(value); + } + }, title: { type: String, default: '' @@ -299,10 +308,11 @@ return { element: this._popover, target: this._trigger, - offset: this.offset, constraints: this.constraints, attachment: PLACEMENT_PARAMS[this.placement].attachment, - targetAttachment: PLACEMENT_PARAMS[this.placement].targetAttachment + targetAttachment: PLACEMENT_PARAMS[this.placement].targetAttachment, + offset: this.offset, + targetOffset: this.targetOffset }; }, From 32225137baeacc8dff547595675c114b23a06c18 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 20 Apr 2017 22:46:56 -0300 Subject: [PATCH 13/14] Latest from master fork (#7) * Fix validation on 'offset' prop in Popover * Add 'targetOffset' prop to Popover * Fix lint tests - comment edit just included to permit commit From 897d759caf45bcaefd513cdcc5d2da6945cc8b4d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 20 Apr 2017 22:52:24 -0300 Subject: [PATCH 14/14] Revert "Latest from master fork (#7)" (#8) This reverts commit 32225137baeacc8dff547595675c114b23a06c18.