From 542deb779be75aa5e0dd53d589f05a57b69abf32 Mon Sep 17 00:00:00 2001 From: maranran Date: Sun, 15 Oct 2017 23:13:10 -0500 Subject: [PATCH] Accessibility for message & message-box & tabs (#7424) * add accessibility for message & message-box & tabs * Update message-box.md --- packages/message-box/src/main.vue | 64 +++++++++++---- packages/message/src/main.vue | 36 +++++++-- packages/radio/src/radio-button.vue | 11 ++- packages/radio/src/radio.vue | 4 +- packages/tabs/src/tab-nav.vue | 57 ++++++++++++-- packages/tabs/src/tab-pane.vue | 12 ++- packages/tabs/src/tabs.vue | 2 + packages/theme-chalk/src/message.scss | 6 ++ packages/theme-chalk/src/radio-button.scss | 14 ++-- packages/theme-chalk/src/radio.scss | 18 ++--- packages/theme-chalk/src/tabs.scss | 4 + src/utils/aria-dialog.js | 90 ++++++++++++++++++++++ src/utils/popup/popup-manager.js | 1 + test/unit/specs/message-box.spec.js | 8 +- 14 files changed, 273 insertions(+), 54 deletions(-) create mode 100644 src/utils/aria-dialog.js diff --git a/packages/message-box/src/main.vue b/packages/message-box/src/main.vue index 3d0260db7..45129988b 100644 --- a/packages/message-box/src/main.vue +++ b/packages/message-box/src/main.vue @@ -1,14 +1,27 @@ @@ -42,7 +44,9 @@ closed: false, timer: null, dangerouslyUseHTMLString: false, - center: false + center: false, + initFocus: null, + originFocus: null }; }, @@ -83,6 +87,7 @@ if (typeof this.onClose === 'function') { this.onClose(this); } + this.originFocus && this.originFocus.focus(); // 键盘焦点回归 }, clearTimer() { @@ -97,11 +102,30 @@ } }, this.duration); } + }, + keydown(e) { + if (e.keyCode === 46 || e.keyCode === 8) { + this.clearTimer(); // detele 取消倒计时 + } else if (e.keyCode === 27) { // esc关闭消息 + if (!this.closed) { + this.close(); + } + } else { + this.startTimer(); // 恢复倒计时 + } } }, - mounted() { this.startTimer(); + this.originFocus = document.activeElement; + this.initFocus = this.showClose ? this.$el.querySelector('.el-icon-close') : this.$el.querySelector('.el-message__content'); + setTimeout(() => { + this.initFocus && this.initFocus.focus(); + }); + document.addEventListener('keydown', this.keydown); + }, + beforeDestroy() { + document.removeEventListener('keydown', this.keydown); } }; diff --git a/packages/radio/src/radio-button.vue b/packages/radio/src/radio-button.vue index cc7be9b58..916409ae2 100644 --- a/packages/radio/src/radio-button.vue +++ b/packages/radio/src/radio-button.vue @@ -4,7 +4,8 @@ :class="[ size ? 'el-radio-button--' + size : '', { 'is-active': value === label }, - { 'is-disabled': isDisabled } + { 'is-disabled': isDisabled }, + { 'is-focus': focus } ]" role="radio" :aria-checked="value === label" @@ -21,6 +22,8 @@ @change="handleChange" :disabled="isDisabled" tabindex="-1" + @focus="focus = true" + @blur="focus = false" > @@ -43,7 +46,11 @@ disabled: Boolean, name: String }, - + data() { + return { + focus: false + }; + }, computed: { value: { get() { diff --git a/packages/radio/src/radio.vue b/packages/radio/src/radio.vue index a84e65858..746513516 100644 --- a/packages/radio/src/radio.vue +++ b/packages/radio/src/radio.vue @@ -4,6 +4,7 @@ :class="[ border && radioSize ? 'el-radio--' + radioSize : '', { 'is-disabled': isDisabled }, + { 'is-focus': focus }, { 'is-bordered': border }, { 'is-checked': model === label } ]" @@ -16,8 +17,7 @@ diff --git a/packages/tabs/src/tab-nav.vue b/packages/tabs/src/tab-nav.vue index 048ab2b98..365476d95 100644 --- a/packages/tabs/src/tab-nav.vue +++ b/packages/tabs/src/tab-nav.vue @@ -34,7 +34,8 @@ data() { return { scrollable: false, - navOffset: 0 + navOffset: 0, + isFocus: false }; }, @@ -119,6 +120,38 @@ this.navOffset = 0; } } + }, + changeTab(e) { + const keyCode = e.keyCode; + let nextIndex; + let currentIndex, tabList; + if ([37, 38, 39, 40].indexOf(keyCode) !== -1) { // 左右上下键更换tab + tabList = e.currentTarget.querySelectorAll('[role=tab]'); + currentIndex = Array.prototype.indexOf.call(tabList, e.target); + } else { + return; + } + if (keyCode === 37 || keyCode === 38) { // left + if (currentIndex === 0) { // first + nextIndex = tabList.length - 1; + } else { + nextIndex = currentIndex - 1; + } + } else { // right + if (currentIndex < tabList.length - 1) { // not last + nextIndex = currentIndex + 1; + } else { + nextIndex = 0; + } + } + tabList[nextIndex].focus(); // 改变焦点元素 + tabList[nextIndex].click(); // 选中下一个tab + }, + setFocus() { + this.isFocus = true; + }, + removeFocus() { + this.isFocus = false; } }, @@ -136,9 +169,11 @@ navStyle, scrollable, scrollNext, - scrollPrev + scrollPrev, + changeTab, + setFocus, + removeFocus } = this; - const scrollBtn = scrollable ? [ , @@ -156,17 +191,27 @@ : null; const tabLabelContent = pane.$slots.label || pane.label; + const tabindex = pane.active ? 0 : -1; return (