浏览代码

Improve accessibility of Buttons in Webapp (#5432)

* Expose toggle buttons better via ARIA

* Wire up the divs/li as role=button as per ARIA patterns

* Add flow annotations to appease the linter

* For role=button use aria-pressed not aria-checked
master
Michael Telatynski 5 年前
父节点
当前提交
953f838a2a
没有帐户链接到提交者的电子邮件

+ 4
- 3
react/features/base/toolbox/components/AbstractButton.js 查看文件

230
 
230
 
231
     /**
231
     /**
232
      * Helper function to be implemented by subclasses, which must return a
232
      * Helper function to be implemented by subclasses, which must return a
233
-     * {@code boolean} value indicating if this button is toggled or not.
233
+     * {@code boolean} value indicating if this button is toggled or not or
234
+     * undefined if the button is not toggleable.
234
      *
235
      *
235
      * @protected
236
      * @protected
236
-     * @returns {boolean}
237
+     * @returns {?boolean}
237
      */
238
      */
238
     _isToggled() {
239
     _isToggled() {
239
-        return false;
240
+        return undefined;
240
     }
241
     }
241
 
242
 
242
     _onClick: (*) => void;
243
     _onClick: (*) => void;

+ 73
- 2
react/features/base/toolbox/components/ToolboxItem.web.js 查看文件

12
  * Web implementation of {@code AbstractToolboxItem}.
12
  * Web implementation of {@code AbstractToolboxItem}.
13
  */
13
  */
14
 export default class ToolboxItem extends AbstractToolboxItem<Props> {
14
 export default class ToolboxItem extends AbstractToolboxItem<Props> {
15
+    /**
16
+     * Initializes a new {@code ToolboxItem} instance.
17
+     *
18
+     * @inheritdoc
19
+     */
20
+    constructor(props: Props) {
21
+        super(props);
22
+
23
+        this._onKeyDown = this._onKeyDown.bind(this);
24
+        this._onKeyUp = this._onKeyUp.bind(this);
25
+    }
26
+
27
+    _onKeyDown: (Object) => void;
28
+
29
+    /**
30
+     * Handles 'Enter' key on the button to trigger onClick for accessibility.
31
+     *
32
+     * @param {Object} event - The key event.
33
+     * @private
34
+     * @returns {void}
35
+     */
36
+    _onKeyDown(event) {
37
+        // If the event coming to the dialog has been subject to preventDefault
38
+        // we don't handle it here.
39
+        if (event.defaultPrevented) {
40
+            return;
41
+        }
42
+
43
+        if (event.key === 'Enter') {
44
+            event.preventDefault();
45
+            event.stopPropagation();
46
+            this.props.onClick();
47
+        } else if (event.key === ' ') {
48
+            // Space triggers button onKeyUp but we need to prevent PTT here
49
+            event.preventDefault();
50
+            event.stopPropagation();
51
+        }
52
+    }
53
+
54
+    _onKeyUp: (Object) => void;
55
+
56
+    /**
57
+     * Handles ' ' (Space) key on the button to trigger onClick for
58
+     * accessibility.
59
+     *
60
+     * @param {Object} event - The key event.
61
+     * @private
62
+     * @returns {void}
63
+     */
64
+    _onKeyUp(event) {
65
+        // If the event coming to the dialog has been subject to preventDefault
66
+        // we don't handle it here.
67
+        if (event.defaultPrevented) {
68
+            return;
69
+        }
70
+
71
+        if (event.key === ' ') {
72
+            event.preventDefault();
73
+            event.stopPropagation();
74
+            this.props.onClick();
75
+        }
76
+    }
77
+
15
     /**
78
     /**
16
      * Handles rendering of the actual item. If the label is being shown, which
79
      * Handles rendering of the actual item. If the label is being shown, which
17
      * is controlled with the `showLabel` prop, the item is rendered for its
80
      * is controlled with the `showLabel` prop, the item is rendered for its
27
             elementAfter,
90
             elementAfter,
28
             onClick,
91
             onClick,
29
             showLabel,
92
             showLabel,
30
-            tooltipPosition
93
+            tooltipPosition,
94
+            toggled
31
         } = this.props;
95
         } = this.props;
32
         const className = showLabel ? 'overflow-menu-item' : 'toolbox-button';
96
         const className = showLabel ? 'overflow-menu-item' : 'toolbox-button';
33
         const props = {
97
         const props = {
98
+            'aria-pressed': toggled,
99
+            'aria-disabled': disabled,
34
             'aria-label': this.accessibilityLabel,
100
             'aria-label': this.accessibilityLabel,
35
             className: className + (disabled ? ' disabled' : ''),
101
             className: className + (disabled ? ' disabled' : ''),
36
-            onClick: disabled ? undefined : onClick
102
+            onClick: disabled ? undefined : onClick,
103
+            onKeyDown: this._onKeyDown,
104
+            onKeyUp: this._onKeyUp,
105
+            tabIndex: 0,
106
+            role: 'button'
37
         };
107
         };
108
+
38
         const elementType = showLabel ? 'li' : 'div';
109
         const elementType = showLabel ? 'li' : 'div';
39
         const useTooltip = this.tooltip && this.tooltip.length > 0;
110
         const useTooltip = this.tooltip && this.tooltip.length > 0;
40
         let children = (
111
         let children = (

+ 68
- 1
react/features/toolbox/components/web/ToolbarButton.js 查看文件

41
         tooltipPosition: 'top'
41
         tooltipPosition: 'top'
42
     };
42
     };
43
 
43
 
44
+    /**
45
+     * Initializes a new {@code ToolbarButton} instance.
46
+     *
47
+     * @inheritdoc
48
+     */
49
+    constructor(props: Props) {
50
+        super(props);
51
+
52
+        this._onKeyDown = this._onKeyDown.bind(this);
53
+        this._onKeyUp = this._onKeyUp.bind(this);
54
+    }
55
+
56
+    _onKeyDown: (Object) => void;
57
+
58
+    /**
59
+     * Handles 'Enter' key on the button to trigger onClick for accessibility.
60
+     *
61
+     * @param {Object} event - The key event.
62
+     * @private
63
+     * @returns {void}
64
+     */
65
+    _onKeyDown(event) {
66
+        // If the event coming to the dialog has been subject to preventDefault
67
+        // we don't handle it here.
68
+        if (event.defaultPrevented) {
69
+            return;
70
+        }
71
+
72
+        if (event.key === 'Enter') {
73
+            event.preventDefault();
74
+            event.stopPropagation();
75
+            this.props.onClick();
76
+        } else if (event.key === ' ') {
77
+            // Space triggers button onKeyUp but we need to prevent default here
78
+            event.preventDefault();
79
+        }
80
+    }
81
+
82
+    _onKeyUp: (Object) => void;
83
+
84
+    /**
85
+     * Handles ' '(Space) key on the button to trigger onClick for
86
+     * accessibility.
87
+     *
88
+     * @param {Object} event - The key event.
89
+     * @private
90
+     * @returns {void}
91
+     */
92
+    _onKeyUp(event) {
93
+        // If the event coming to the dialog has been subject to preventDefault
94
+        // we don't handle it here.
95
+        if (event.defaultPrevented) {
96
+            return;
97
+        }
98
+
99
+        if (event.key === ' ') {
100
+            event.preventDefault();
101
+            event.stopPropagation();
102
+            this.props.onClick();
103
+        }
104
+    }
105
+
44
     /**
106
     /**
45
      * Renders the button of this {@code ToolbarButton}.
107
      * Renders the button of this {@code ToolbarButton}.
46
      *
108
      *
53
         return (
115
         return (
54
             <div
116
             <div
55
                 aria-label = { this.props.accessibilityLabel }
117
                 aria-label = { this.props.accessibilityLabel }
118
+                aria-pressed = { this.props.toggled }
56
                 className = 'toolbox-button'
119
                 className = 'toolbox-button'
57
-                onClick = { this.props.onClick }>
120
+                onClick = { this.props.onClick }
121
+                onKeyDown = { this._onKeyDown }
122
+                onKeyUp = { this._onKeyUp }
123
+                role = 'button'
124
+                tabIndex = { 0 }>
58
                 { this.props.tooltip
125
                 { this.props.tooltip
59
                     ? <Tooltip
126
                     ? <Tooltip
60
                         content = { this.props.tooltip }
127
                         content = { this.props.tooltip }

正在加载...
取消
保存