[Webkit-unassigned] [Bug 250380] New: AX: Voiceover incorrectly changes value of input type="text" with role="spinbutton" when swipe up/swipe down gesture used, value change announces numeric value inserted between percentage value and percent itself
bugzilla-daemon at webkit.org
bugzilla-daemon at webkit.org
Tue Jan 10 06:41:49 PST 2023
https://bugs.webkit.org/show_bug.cgi?id=250380
Bug ID: 250380
Summary: AX: Voiceover incorrectly changes value of input
type="text" with role="spinbutton" when swipe up/swipe
down gesture used, value change announces numeric
value inserted between percentage value and percent
itself
Product: WebKit
Version: Safari 16
Hardware: iPhone / iPad
OS: iOS 16
Status: NEW
Severity: Normal
Priority: P2
Component: Accessibility
Assignee: webkit-unassigned at lists.webkit.org
Reporter: jartik at gmail.com
CC: andresg_22 at apple.com,
webkit-bug-importer at group.apple.com
Created attachment 464441
--> https://bugs.webkit.org/attachment.cgi?id=464441&action=review
Sample code
Steps to Reproduce:
1). Please use the following code snippet:
============= Styles =============
h2 {
margin-top: 0;
}
.spinbutton {
display: flex;
flex-direction: row;
align-items: center;
}
.spinbutton button {
width: 48px;
height: 48px;
cursor: pointer;
}
[role="spinbutton"] {
width: 60px;
text-align: center;
}
input[role="spinbutton"] {
margin: 0 5px;
}
============= END of Styles =============
============= Code =============
<h2>Role "spinbutton" on div</h2>
<div class="spinbutton">
<button class="decrease" type="button" tabindex="-1" aria-hidden>-</button>
<div role="spinbutton" tabindex="0" aria-valuenow="1" aria-valuetext="1" aria-valuemin="0" aria-valuemax="200"
aria-label="Quantity div spinbutton">1</div>
<button class="increase" type="button" tabindex="-1">+</button>
</div>
<hr />
<h2>Role "spinbutton" on input type text</h2>
<div class="spinbutton">
<button class="decrease" type="button" class="decrease" tabindex="-1" aria-hidden>-</button>
<input type="text" role="spinbutton" aria-valuenow="1" aria-valuetext="1" aria-valuemin="0" aria-valuemax="200"
value="1" aria-label="Quantity input type text spinbutton" />
<button class="increase" type="button" tabindex="-1">+</button>
</div>
<hr />
<h2>Role "spinbutton" on input type number</h2>
<div class="spinbutton">
<button class="decrease" type="button" class="decrease" tabindex="-1" aria-hidden>-</button>
<input type="number" step="1" min="0" max="200" role="spinbutton" aria-valuenow="1" aria-valuetext="1" aria-valuemin="0"
aria-valuemax="200" value="1" aria-label="Quantity input type number spinbutton" />
<button class="increase" type="button" tabindex="-1">+</button>
</div>
<script>
window.addEventListener('load', _ => {
const containers = document.querySelectorAll('.spinbutton');
[...containers].forEach(c => {
const spinButton = c.querySelector('[role="spinbutton"]');
const decreaseBtn = c.querySelector('.decrease');
const increaseBtn = c.querySelector('.increase');
const minValue = parseInt(spinButton.getAttribute('aria-valuemin'), 10);
const maxValue = parseInt(spinButton.getAttribute('aria-valuemax'), 10);
decreaseBtn.addEventListener('click', _ => decreaseValue(spinButton, minValue));
increaseBtn.addEventListener('click', _ => increaseValue(spinButton, maxValue));
spinButton.addEventListener('keydown', e => {
console.log('::Keydown event:: key:', e.key);
if (isInput(e.currentTarget) && e.currentTarget.type === 'number')
return;
if (e.key === 'ArrowUp') {
increaseValue(e.currentTarget, maxValue);
} else if (e.key === 'ArrowDown') {
decreaseValue(e.currentTarget, minValue);
}
});
if (isInput(spinButton)) {
spinButton.addEventListener('input', e => {
console.log('::Input event:: inputType:', e.inputType);
console.log('::Input event:: data:', e.data);
console.log('::Input event:: value:', e.currentTarget.value);
});
spinButton.addEventListener('change', e => {
console.log('::Change event:: value:', e.currentTarget.value);
e.currentTarget.setAttribute('aria-valuenow', e.currentTarget.value);
e.currentTarget.setAttribute('aria-valuetext', e.currentTarget.value);
const value = parseInt(e.currentTarget.value, 10);
if (isNaN(value) || value < minValue || value > maxValue) {
e.currentTarget.setAttribute('aria-invalid', 'true');
} else {
e.currentTarget.removeAttribute('aria-invalid');
}
});
}
});
});
function getCurrentValue(spinButton) {
return parseInt(spinButton.getAttribute('aria-valuenow'), 10)
}
function increaseValue(spinButton, maxValue) {
const currentValue = getCurrentValue(spinButton);
if (isNaN(currentValue)) {
setNewValue(spinButton, 0);
return;
}
const newValue = currentValue + 1;
if (newValue > maxValue)
return;
setNewValue(spinButton, newValue);
}
function decreaseValue(spinButton, minValue) {
const currentValue = getCurrentValue(spinButton);
if (isNaN(currentValue)) {
setNewValue(spinButton, 0);
return;
}
const newValue = currentValue - 1;
if (newValue < minValue)
return;
setNewValue(spinButton, newValue);
}
function setNewValue(spinButton, value) {
spinButton.setAttribute('aria-valuenow', value);
spinButton.setAttribute('aria-valuetext', value);
if (isInput(spinButton)) {
spinButton.removeAttribute('aria-invalid');
spinButton.value = value;
} else {
spinButton.innerHTML = value;
}
}
function isInput(spinButton) {
return spinButton.tagName === 'INPUT';
}
</script>
============= END of Code =============
2). Open page with provided code snippet on iPhone/iPad with Voiceover enabled. Swipe left/right or tap directly on input type="text" with role="spinbutton". Try to change value by swiping up and/or down.
Actual Results: Instead of "keydown" event two input events are fired - first one with inputType of "deleteContent" which cleans input value, second one of "insertText" which adds text with value which seems like equal to 5% of the range between aria-valuemin and aria-valuemax values (this was determined by number of tests with different min/max values) is been added or substracted from the original value depending on operation type triggered (increment or decrement). Swiping up/down continuously changes applies added or substracted from the original value, change is applied only after input loses focus. On value change it seems like selected value as percentage from the range is been announced, mostly with some number, source of which I can not determine (but is seems like it should be somehow relative to the value change step), announced between percentage value and percentage itself.
Expected Results: Keyboard "keydown" event is fired with "ArrowUp" as a key for increment and "ArrowDown" for decrement AT (assistive technology) event. Value is changed as per code in "keydown" event listener.
Build Date & Hardware: iPhone 11, iOS 16.2
Additional Information:
As per https://github.com/WICG/aom/blob/gh-pages/explainer.md#user-action-events-from-assistive-technology increment and decrement AT event should trigger Keyboard events simulating pressing of "ArrowUp" and "ArrowDown" keys respectively. This works for general "div" element with role="spinbutton" (present in the provided code snippet) - value changes correctly and been announced, but after that again percentage value, undetermined numeric value and 'percentage' word are announced just as in 'Actual Results' section. "keydown" listener does not run at all on input type="text", value change step as was mentioned seems like equal to 5% of the range between aria-valuemin and aria-valuemax values, but it is totally unclear why and if it is always has this value. If role="spinbutton" added to input type="number" with step attribute present, step value is been honored, but still "keydown" listener is not fired and general behavior is the same as for input type="text".
There is also a bug https://bugs.webkit.org/show_bug.cgi?id=221102 already filled for percentage announcement which is still reproduced in iOS 16.2 (original bug was reported for iOS 14).
--
You are receiving this mail because:
You are the assignee for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.webkit.org/pipermail/webkit-unassigned/attachments/20230110/3d063d57/attachment-0001.htm>
More information about the webkit-unassigned
mailing list