浏览代码

fix: missing aria attributes + controls on tabset component

NGPixel 3 年之前
父节点
当前提交
63ecb32494
共有 1 个文件被更改,包括 44 次插入1 次删除
  1. 44 1
      client/themes/default/components/tabset.vue

+ 44 - 1
client/themes/default/components/tabset.vue

@@ -1,12 +1,16 @@
 <template lang="pug">
   .tabset.elevation-2
-    ul.tabset-tabs(ref='tabs')
+    ul.tabset-tabs(ref='tabs', role='tablist')
       slot(name='tabs')
     .tabset-content(ref='content')
       slot(name='content')
 </template>
 
 <script>
+import { customAlphabet } from 'nanoid/non-secure'
+
+const nanoid = customAlphabet('1234567890abcdef', 10)
+
 export default {
   data() {
     return {
@@ -23,15 +27,19 @@ export default {
       this.$refs.tabs.childNodes.forEach((node, idx) => {
         if (idx === this.currentTab) {
           node.className = 'is-active'
+          node.setAttribute('aria-selected', 'true')
         } else {
           node.className = ''
+          node.setAttribute('aria-selected', 'false')
         }
       })
       this.$refs.content.childNodes.forEach((node, idx) => {
         if (idx === this.currentTab) {
           node.className = 'tabset-panel is-active'
+          node.removeAttribute('hidden')
         } else {
           node.className = 'tabset-panel'
+          node.setAttribute('hidden', '')
         }
       })
     }
@@ -53,10 +61,43 @@ export default {
 
     this.setActiveTab()
 
+    const tabRefId = nanoid()
+
     this.$refs.tabs.childNodes.forEach((node, idx) => {
+      node.setAttribute('id', `${tabRefId}-${idx}`)
+      node.setAttribute('role', 'tab')
+      node.setAttribute('aria-controls', `${tabRefId}-${idx}-tab`)
+      node.setAttribute('tabindex', '0')
       node.addEventListener('click', ev => {
         this.currentTab = [].indexOf.call(ev.target.parentNode.children, ev.target)
       })
+      node.addEventListener('keydown', ev => {
+        if (ev.key === 'ArrowLeft' && idx > 0) {
+          this.currentTab = idx - 1
+          this.$refs.tabs.childNodes[idx - 1].focus()
+        } else if (ev.key === 'ArrowRight' && idx < this.$refs.tabs.childNodes.length - 1) {
+          this.currentTab = idx + 1
+          this.$refs.tabs.childNodes[idx + 1].focus()
+        } else if (ev.key === 'Enter' || ev.key === ' ') {
+          this.currentTab = idx
+          node.focus()
+        } else if (ev.key === 'Home') {
+          this.currentTab = 0
+          ev.preventDefault()
+          ev.target.parentNode.children[0].focus()
+        } else if (ev.key === 'End') {
+          this.currentTab = this.$refs.tabs.childNodes.length - 1
+          ev.preventDefault()
+          ev.target.parentNode.children[this.$refs.tabs.childNodes.length - 1].focus()
+        }
+      })
+    })
+
+    this.$refs.content.childNodes.forEach((node, idx) => {
+      node.setAttribute('id', `${tabRefId}-${idx}-tab`)
+      node.setAttribute('role', 'tabpanel')
+      node.setAttribute('aria-labelledby', `${tabRefId}-${idx}`)
+      node.setAttribute('tabindex', '0')
     })
   }
 }
@@ -106,7 +147,9 @@ export default {
         background-color: #FFF;
         margin-bottom: 0;
         padding-bottom: 17px;
+        padding-top: 13px;
         color: mc('blue', '700');
+        border-top: 3px solid mc('blue', '700');
 
         @at-root .theme--dark & {
           background-color: #292929;