Преглед изворни кода

RadioGroup:add accessibility

maran пре 8 година
родитељ
комит
7a84a3e44a

+ 11 - 1
packages/radio/src/radio-button.vue

@@ -6,6 +6,11 @@
       { 'is-active': value === label },
       { 'is-disabled': isDisabled }
     ]"
+    role="radio"
+    :aria-checked="value === label"
+    :aria-disabled="isDisabled"
+    :tabindex="tabIndex"
+    @keydown.space.stop.prevent="value = label"
   >
     <input
       class="el-radio-button__orig-radio"
@@ -13,7 +18,9 @@
       type="radio"
       v-model="value"
       :name="name"
-      :disabled="isDisabled">
+      :disabled="isDisabled"
+      tabindex="-1"
+    >
     <span class="el-radio-button__inner" :style="value === label ? activeStyle : null">
       <slot></slot>
       <template v-if="!$slots.default">{{label}}</template>
@@ -62,6 +69,9 @@
       },
       isDisabled() {
         return this.disabled || this._radioGroup.disabled;
+      },
+      tabIndex() {
+        return !this.isDisabled ? (this._radioGroup ? (this.value === this.label ? 0 : -1) : 0) : -1;
       }
     }
   };

+ 52 - 1
packages/radio/src/radio-group.vue

@@ -1,11 +1,21 @@
 <template>
-  <div class="el-radio-group">
+  <div
+    class="el-radio-group"
+    role="radiogroup"
+    @keydown="handleKeydown"
+  >
     <slot></slot>
   </div>
 </template>
 <script>
   import Emitter from 'element-ui/src/mixins/emitter';
 
+  const keyCode = Object.freeze({
+    LEFT: 37,
+    UP: 38,
+    RIGHT: 39,
+    DOWN: 40
+  });
   export default {
     name: 'ElRadioGroup',
 
@@ -20,6 +30,47 @@
       textColor: String,
       disabled: Boolean
     },
+    mounted() {
+      // 当radioGroup没有默认选项时,第一个可以选中Tab导航
+      let radios = this.$el.querySelectorAll('[type=radio]');
+      if (![].some.call(radios, radio => radio.checked)) {
+        this.$el.querySelectorAll('[role=radio]')[0].tabIndex = 0;
+      }
+    },
+    methods: {
+      handleKeydown(e) { // 左右上下按键 可以在radio组内切换不同选项
+        const target = e.target;
+        const className = target.nodeName === 'INPUT' ? '[type=radio]' : '[role=radio]';
+        const radios = this.$el.querySelectorAll(className);
+        const length = radios.length;
+        const index = [].indexOf.call(radios, target);
+        const roleRadios = this.$el.querySelectorAll('[role=radio]');
+        switch (e.keyCode) {
+          case keyCode.LEFT:
+          case keyCode.UP:
+            e.stopPropagation();
+            e.preventDefault();
+            if (index === 0) {
+              roleRadios[length - 1].click();
+            } else {
+              roleRadios[index - 1].click();
+            }
+            break;
+          case keyCode.RIGHT:
+          case keyCode.DOWN:
+            if (index === (length - 1)) {
+              e.stopPropagation();
+              e.preventDefault();
+              roleRadios[0].click();
+            } else {
+              roleRadios[index + 1].click();
+            }
+            break;
+          default:
+            break;
+        }
+      }
+    },
     watch: {
       value(value) {
         this.$emit('change', value);

+ 14 - 5
packages/radio/src/radio.vue

@@ -1,5 +1,12 @@
 <template>
-  <label class="el-radio">
+  <label
+    class="el-radio"
+    role="radio"
+    :aria-checked="model === label"
+    :aria-disabled="isDisabled"
+    :tabindex="tabIndex"
+    @keydown.space.stop.prevent="model = label"
+  >
     <span class="el-radio__input"
       :class="{
         'is-disabled': isDisabled,
@@ -16,7 +23,9 @@
         @focus="focus = true"
         @blur="focus = false"
         :name="name"
-        :disabled="isDisabled">
+        :disabled="isDisabled"
+        tabindex="-1"
+      >
     </span>
     <span class="el-radio__label">
       <slot></slot>
@@ -46,7 +55,6 @@
         focus: false
       };
     },
-
     computed: {
       isGroup() {
         let parent = this.$parent;
@@ -60,12 +68,10 @@
         }
         return false;
       },
-
       model: {
         get() {
           return this.isGroup ? this._radioGroup.value : this.value;
         },
-
         set(val) {
           if (this.isGroup) {
             this.dispatch('ElRadioGroup', 'input', [val]);
@@ -79,6 +85,9 @@
         return this.isGroup
           ? this._radioGroup.disabled || this.disabled
           : this.disabled;
+      },
+      tabIndex() {
+        return !this.isDisabled ? (this.isGroup ? (this.model === this.label ? 0 : -1) : 0) : -1;
       }
     }
   };

+ 6 - 0
packages/theme-default/src/radio-button.css

@@ -75,6 +75,12 @@
         box-shadow: none !important;
       }
     }
+    &:focus {
+      outline: none;
+      .el-radio-button__inner { /*获得焦点时 样式提醒*/
+        box-shadow: 0 0 1px 1px var(--radio-button-checked-border-color) !important;
+      }
+    }
     &:last-child {
       .el-radio-button__inner {
         border-radius: 0 var(--border-radius-base) var(--border-radius-base) 0;

+ 7 - 0
packages/theme-default/src/radio.css

@@ -11,6 +11,13 @@
     white-space: nowrap;
     @utils-user-select none;
 
+    &:focus { /*获得焦点时 样式提醒*/
+      outline: none;
+      .el-radio__inner {
+        box-shadow: 0 0 1px 1px var(--radio-input-border-color-hover);
+      }
+    }
+
     & + .el-radio {
       margin-left: 15px;
     }