@vaadin/a11y-base 25.0.0-alpha9 → 25.0.0-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/aria-hidden.js +1 -1
- package/src/aria-id-reference.js +1 -3
- package/src/delegate-focus-mixin.js +8 -2
- package/src/focus-mixin.js +15 -0
- package/src/focus-restoration-controller.js +6 -3
- package/src/focus-trap-controller.js +3 -3
- package/src/keyboard-direction-mixin.d.ts +2 -2
- package/src/keyboard-direction-mixin.js +17 -15
- package/src/list-mixin.d.ts +1 -7
- package/src/list-mixin.js +12 -14
- package/src/tabindex-mixin.js +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/a11y-base",
|
|
3
|
-
"version": "25.0.0-
|
|
3
|
+
"version": "25.0.0-beta1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
34
|
-
"@vaadin/component-base": "25.0.0-
|
|
34
|
+
"@vaadin/component-base": "25.0.0-beta1",
|
|
35
35
|
"lit": "^3.0.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@vaadin/chai-plugins": "25.0.0-
|
|
39
|
-
"@vaadin/test-runner-commands": "25.0.0-
|
|
38
|
+
"@vaadin/chai-plugins": "25.0.0-beta1",
|
|
39
|
+
"@vaadin/test-runner-commands": "25.0.0-beta1",
|
|
40
40
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
41
|
-
"sinon": "^
|
|
41
|
+
"sinon": "^21.0.0"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "1d20cf54e582d1f2e209126d4586f8b4c01c50e0"
|
|
44
44
|
}
|
package/src/aria-hidden.js
CHANGED
|
@@ -115,7 +115,7 @@ const applyAttributeToOthers = (originalTarget, parentNode, markerName, controlA
|
|
|
115
115
|
targets.forEach(keep);
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
* @param {?Node}
|
|
118
|
+
* @param {?Node} parent
|
|
119
119
|
*/
|
|
120
120
|
const deep = (parent) => {
|
|
121
121
|
if (!parent || elementsToStop.has(parent)) {
|
package/src/aria-id-reference.js
CHANGED
|
@@ -17,7 +17,7 @@ const attributeToTargets = new Map();
|
|
|
17
17
|
*
|
|
18
18
|
* @param {string} attr the attribute name used as key in the map
|
|
19
19
|
*
|
|
20
|
-
* @
|
|
20
|
+
* @return {WeakMap<HTMLElement, Set<string>>} a weak map with the stored values for the elements being controlled by the helper
|
|
21
21
|
*/
|
|
22
22
|
function getAttrMap(attr) {
|
|
23
23
|
if (!attributeToTargets.has(attr)) {
|
|
@@ -32,8 +32,6 @@ function getAttrMap(attr) {
|
|
|
32
32
|
*
|
|
33
33
|
* @param {HTMLElement} target
|
|
34
34
|
* @param {string} attr the attribute to be cleared
|
|
35
|
-
* @param {boolean} storeValue whether or not the current value of the attribute should be stored on the map
|
|
36
|
-
* @returns
|
|
37
35
|
*/
|
|
38
36
|
function cleanAriaIDReference(target, attr) {
|
|
39
37
|
if (!target) {
|
|
@@ -73,18 +73,24 @@ export const DelegateFocusMixin = dedupeMixin(
|
|
|
73
73
|
if (this.autofocus && !this.disabled) {
|
|
74
74
|
requestAnimationFrame(() => {
|
|
75
75
|
this.focus();
|
|
76
|
-
this.setAttribute('focus-ring', '');
|
|
77
76
|
});
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
/**
|
|
81
|
+
* @param {FocusOptions=} options
|
|
82
82
|
* @protected
|
|
83
83
|
* @override
|
|
84
84
|
*/
|
|
85
|
-
focus() {
|
|
85
|
+
focus(options) {
|
|
86
86
|
if (this.focusElement && !this.disabled) {
|
|
87
87
|
this.focusElement.focus();
|
|
88
|
+
|
|
89
|
+
// Set focus-ring attribute on programmatic focus by default
|
|
90
|
+
// unless explicitly disabled by `{ focusVisible: false }`.
|
|
91
|
+
if (!(options && options.focusVisible === false)) {
|
|
92
|
+
this.setAttribute('focus-ring', '');
|
|
93
|
+
}
|
|
88
94
|
}
|
|
89
95
|
}
|
|
90
96
|
|
package/src/focus-mixin.js
CHANGED
|
@@ -54,6 +54,21 @@ export const FocusMixin = dedupeMixin(
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* @param {FocusOptions=} options
|
|
59
|
+
* @protected
|
|
60
|
+
* @override
|
|
61
|
+
*/
|
|
62
|
+
focus(options) {
|
|
63
|
+
super.focus(options);
|
|
64
|
+
|
|
65
|
+
// Set focus-ring attribute on programmatic focus by default
|
|
66
|
+
// unless explicitly disabled by `{ focusVisible: false }`.
|
|
67
|
+
if (!(options && options.focusVisible === false)) {
|
|
68
|
+
this.setAttribute('focus-ring', '');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
/**
|
|
58
73
|
* Override to change how focused and focus-ring attributes are set.
|
|
59
74
|
*
|
|
@@ -29,16 +29,19 @@ export class FocusRestorationController {
|
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const
|
|
32
|
+
const focusOptions = {
|
|
33
|
+
preventScroll: options ? options.preventScroll : false,
|
|
34
|
+
focusVisible: options ? options.focusVisible : false,
|
|
35
|
+
};
|
|
33
36
|
|
|
34
37
|
if (getDeepActiveElement() === document.body) {
|
|
35
38
|
// In Firefox and Safari, focusing the node synchronously
|
|
36
39
|
// doesn't work as expected when the overlay is closing on outside click.
|
|
37
40
|
// These browsers force focus to move to the body element and retain it
|
|
38
41
|
// there until the next event loop iteration.
|
|
39
|
-
setTimeout(() => focusNode.focus(
|
|
42
|
+
setTimeout(() => focusNode.focus(focusOptions));
|
|
40
43
|
} else {
|
|
41
|
-
focusNode.focus(
|
|
44
|
+
focusNode.focus(focusOptions);
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
this.focusNode = null;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2025 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { getFocusableElements, isElementFocused } from './focus-utils.js';
|
|
6
|
+
import { getFocusableElements, isElementFocused, isKeyboardActive } from './focus-utils.js';
|
|
7
7
|
|
|
8
8
|
const instances = [];
|
|
9
9
|
|
|
@@ -87,7 +87,7 @@ export class FocusTrapController {
|
|
|
87
87
|
instances.push(this);
|
|
88
88
|
|
|
89
89
|
if (this.__focusedElementIndex === -1) {
|
|
90
|
-
this.__focusableElements[0].focus();
|
|
90
|
+
this.__focusableElements[0].focus({ focusVisible: isKeyboardActive() });
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -147,7 +147,7 @@ export class FocusTrapController {
|
|
|
147
147
|
const currentIndex = this.__focusedElementIndex;
|
|
148
148
|
const nextIndex = (focusableElements.length + currentIndex + step) % focusableElements.length;
|
|
149
149
|
const element = focusableElements[nextIndex];
|
|
150
|
-
element.focus();
|
|
150
|
+
element.focus({ focusVisible: true });
|
|
151
151
|
if (element.localName === 'input') {
|
|
152
152
|
element.select();
|
|
153
153
|
}
|
|
@@ -32,12 +32,12 @@ export declare class KeyboardDirectionMixinClass {
|
|
|
32
32
|
/**
|
|
33
33
|
* Focus the item at given index. Override this method to add custom logic.
|
|
34
34
|
*/
|
|
35
|
-
protected _focus(index: number, navigating: boolean): void;
|
|
35
|
+
protected _focus(index: number, options: FocusOptions, navigating: boolean): void;
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Focus the given item. Override this method to add custom logic.
|
|
39
39
|
*/
|
|
40
|
-
protected _focusItem(item: Element, navigating: boolean): void;
|
|
40
|
+
protected _focusItem(item: Element, options: FocusOptions, navigating: boolean): void;
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Returns whether the item is focusable. By default,
|
|
@@ -38,13 +38,17 @@ export const KeyboardDirectionMixin = (superclass) =>
|
|
|
38
38
|
return false;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
41
|
+
/**
|
|
42
|
+
* @param {FocusOptions=} options
|
|
43
|
+
* @protected
|
|
44
|
+
* @override
|
|
45
|
+
*/
|
|
46
|
+
focus(options) {
|
|
43
47
|
const items = this._getItems();
|
|
44
48
|
if (Array.isArray(items)) {
|
|
45
49
|
const idx = this._getAvailableIndex(items, 0, null, (item) => !isElementHidden(item));
|
|
46
50
|
if (idx >= 0) {
|
|
47
|
-
this._focus(idx);
|
|
51
|
+
this._focus(idx, options);
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
}
|
|
@@ -104,17 +108,18 @@ export const KeyboardDirectionMixin = (superclass) =>
|
|
|
104
108
|
if (
|
|
105
109
|
this._tabNavigation &&
|
|
106
110
|
key === 'Tab' &&
|
|
107
|
-
((idx > currentIdx && event.shiftKey) || (idx < currentIdx && !event.shiftKey))
|
|
111
|
+
((idx > currentIdx && event.shiftKey) || (idx < currentIdx && !event.shiftKey) || idx === currentIdx)
|
|
108
112
|
) {
|
|
109
113
|
// Prevent "roving tabindex" logic and let the normal Tab behavior if
|
|
110
114
|
// - currently on the first focusable item and Shift + Tab is pressed,
|
|
111
|
-
// - currently on the last focusable item and Tab is pressed
|
|
115
|
+
// - currently on the last focusable item and Tab is pressed,
|
|
116
|
+
// - currently on the only focusable item and Tab is pressed
|
|
112
117
|
return;
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
if (idx >= 0) {
|
|
116
121
|
event.preventDefault();
|
|
117
|
-
this._focus(idx, true);
|
|
122
|
+
this._focus(idx, { focusVisible: true }, true);
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
|
|
@@ -150,30 +155,27 @@ export const KeyboardDirectionMixin = (superclass) =>
|
|
|
150
155
|
* Focus the item at given index. Override this method to add custom logic.
|
|
151
156
|
*
|
|
152
157
|
* @param {number} index
|
|
158
|
+
* @param {FocusOptions=} options
|
|
153
159
|
* @param {boolean} navigating
|
|
154
160
|
* @protected
|
|
155
161
|
*/
|
|
156
|
-
_focus(index, navigating = false) {
|
|
162
|
+
_focus(index, options, navigating = false) {
|
|
157
163
|
const items = this._getItems();
|
|
158
164
|
|
|
159
|
-
this._focusItem(items[index], navigating);
|
|
165
|
+
this._focusItem(items[index], options, navigating);
|
|
160
166
|
}
|
|
161
167
|
|
|
162
168
|
/**
|
|
163
169
|
* Focus the given item. Override this method to add custom logic.
|
|
164
170
|
*
|
|
165
171
|
* @param {Element} item
|
|
172
|
+
* @param {FocusOptions=} options
|
|
166
173
|
* @param {boolean} navigating
|
|
167
174
|
* @protected
|
|
168
175
|
*/
|
|
169
|
-
_focusItem(item) {
|
|
176
|
+
_focusItem(item, options) {
|
|
170
177
|
if (item) {
|
|
171
|
-
item.focus();
|
|
172
|
-
|
|
173
|
-
// Generally, the items are expected to implement `FocusMixin`
|
|
174
|
-
// that would set this attribute based on the `keydown` event.
|
|
175
|
-
// We set it manually to handle programmatic focus() calls.
|
|
176
|
-
item.setAttribute('focus-ring', '');
|
|
178
|
+
item.focus(options);
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
181
|
|
package/src/list-mixin.d.ts
CHANGED
|
@@ -36,15 +36,9 @@ export declare class ListMixinClass {
|
|
|
36
36
|
orientation: 'horizontal' | 'vertical';
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* A read-only list of items from which a selection can be made.
|
|
40
40
|
* It is populated from the elements passed to the light DOM,
|
|
41
41
|
* and updated dynamically when adding or removing items.
|
|
42
|
-
*
|
|
43
|
-
* The item elements must implement `Vaadin.ItemMixin`.
|
|
44
|
-
*
|
|
45
|
-
* Note: unlike `<vaadin-combo-box>`, this property is read-only,
|
|
46
|
-
* so if you want to provide items by iterating array of data,
|
|
47
|
-
* you have to use `dom-repeat` and place it to the light DOM.
|
|
48
42
|
*/
|
|
49
43
|
readonly items: Element[] | undefined;
|
|
50
44
|
|
package/src/list-mixin.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { timeOut } from '@vaadin/component-base/src/async.js';
|
|
7
7
|
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
8
8
|
import { getNormalizedScrollLeft, setNormalizedScrollLeft } from '@vaadin/component-base/src/dir-utils.js';
|
|
9
|
-
import { getFlattenedElements } from '@vaadin/component-base/src/dom-utils.js';
|
|
10
9
|
import { SlotObserver } from '@vaadin/component-base/src/slot-observer.js';
|
|
11
10
|
import { isElementHidden } from './focus-utils.js';
|
|
12
11
|
import { KeyboardDirectionMixin } from './keyboard-direction-mixin.js';
|
|
@@ -56,15 +55,9 @@ export const ListMixin = (superClass) =>
|
|
|
56
55
|
},
|
|
57
56
|
|
|
58
57
|
/**
|
|
59
|
-
*
|
|
58
|
+
* A read-only list of items from which a selection can be made.
|
|
60
59
|
* It is populated from the elements passed to the light DOM,
|
|
61
60
|
* and updated dynamically when adding or removing items.
|
|
62
|
-
*
|
|
63
|
-
* The item elements must implement `Vaadin.ItemMixin`.
|
|
64
|
-
*
|
|
65
|
-
* Note: unlike `<vaadin-combo-box>`, this property is read-only,
|
|
66
|
-
* so if you want to provide items by iterating array of data,
|
|
67
|
-
* you have to use `dom-repeat` and place it to the light DOM.
|
|
68
61
|
* @type {!Array<!Element> | undefined}
|
|
69
62
|
*/
|
|
70
63
|
items: {
|
|
@@ -114,7 +107,12 @@ export const ListMixin = (superClass) =>
|
|
|
114
107
|
return this.orientation !== 'horizontal';
|
|
115
108
|
}
|
|
116
109
|
|
|
117
|
-
|
|
110
|
+
/**
|
|
111
|
+
* @param {FocusOptions=} options
|
|
112
|
+
* @protected
|
|
113
|
+
* @override
|
|
114
|
+
*/
|
|
115
|
+
focus(options) {
|
|
118
116
|
// In initialization (e.g vaadin-select) observer might not been run yet.
|
|
119
117
|
if (this._observer) {
|
|
120
118
|
this._observer.flush();
|
|
@@ -123,10 +121,10 @@ export const ListMixin = (superClass) =>
|
|
|
123
121
|
const items = Array.isArray(this.items) ? this.items : [];
|
|
124
122
|
const idx = this._getAvailableIndex(items, 0, null, (item) => item.tabIndex === 0 && !isElementHidden(item));
|
|
125
123
|
if (idx >= 0) {
|
|
126
|
-
this._focus(idx);
|
|
124
|
+
this._focus(idx, options);
|
|
127
125
|
} else {
|
|
128
126
|
// Call `KeyboardDirectionMixin` logic to focus first non-disabled item.
|
|
129
|
-
super.focus();
|
|
127
|
+
super.focus(options);
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
130
|
|
|
@@ -138,7 +136,7 @@ export const ListMixin = (superClass) =>
|
|
|
138
136
|
|
|
139
137
|
const slot = this.shadowRoot.querySelector('slot:not([name])');
|
|
140
138
|
this._observer = new SlotObserver(slot, () => {
|
|
141
|
-
this._setItems(this._filterItems(
|
|
139
|
+
this._setItems(this._filterItems([...this.children]));
|
|
142
140
|
});
|
|
143
141
|
}
|
|
144
142
|
|
|
@@ -282,13 +280,13 @@ export const ListMixin = (superClass) =>
|
|
|
282
280
|
* @param {number} idx
|
|
283
281
|
* @protected
|
|
284
282
|
*/
|
|
285
|
-
_focus(idx) {
|
|
283
|
+
_focus(idx, options) {
|
|
286
284
|
this.items.forEach((e, index) => {
|
|
287
285
|
e.focused = index === idx;
|
|
288
286
|
});
|
|
289
287
|
this._setFocusable(idx);
|
|
290
288
|
this._scrollToItem(idx);
|
|
291
|
-
super._focus(idx);
|
|
289
|
+
super._focus(idx, options);
|
|
292
290
|
}
|
|
293
291
|
|
|
294
292
|
/**
|
package/src/tabindex-mixin.js
CHANGED
|
@@ -95,12 +95,13 @@ export const TabindexMixin = (superclass) =>
|
|
|
95
95
|
* `tabindex` to -1 does not prevent the element from being
|
|
96
96
|
* programmatically focusable.
|
|
97
97
|
*
|
|
98
|
+
* @param {FocusOptions=} options
|
|
98
99
|
* @protected
|
|
99
100
|
* @override
|
|
100
101
|
*/
|
|
101
|
-
focus() {
|
|
102
|
+
focus(options) {
|
|
102
103
|
if (!this.disabled || this.__shouldAllowFocusWhenDisabled()) {
|
|
103
|
-
super.focus();
|
|
104
|
+
super.focus(options);
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|