@samitouri / QOS-React-2 / commits / 4ef269606c

[react-events] Support screen reader virtual clicks (#16584)

Dominic Gannaway committed Aug 27, 2019 at 17:32 UTC 4ef269606c691f9b8c833f18d75342b34ef021db
4 files changed +52 -11
packages/react-events/src/dom/Press.js
+20
@@ -476,6 +476,19 @@ function handleStopPropagation(
476 }
477 }
478
479 +// After some investigation work, screen reader virtual
480 +// clicks (NVDA, Jaws, VoiceOver) do not have co-ords associated with the click
481 +// event and "detail" is always 0 (where normal clicks are > 0)
482 +function isScreenReaderVirtualClick(nativeEvent): boolean {
483 + return (
484 + nativeEvent.detail === 0 &&
485 + nativeEvent.screenX === 0 &&
486 + nativeEvent.screenY === 0 &&
487 + nativeEvent.clientX === 0 &&
488 + nativeEvent.clientY === 0
489 + );
490 +}
491 +
492 function targetIsDocument(target: null | Node): boolean {
493 // When target is null, it is the root
494 return target === null || target.nodeType === 9;
@@ -617,6 +630,13 @@ const pressResponderImpl = {
630 if (state.shouldPreventClick) {
631 nativeEvent.preventDefault();
632 }
633 + const onPress = props.onPress;
634 +
635 + if (isFunction(onPress) && isScreenReaderVirtualClick(nativeEvent)) {
636 + state.pointerType = 'keyboard';
637 + state.pressTarget = event.responderTarget;
638 + dispatchEvent(event, onPress, context, state, 'press', DiscreteEvent);
639 + }
640 break;
641 }
642 }
packages/react-events/src/dom/__tests__/Press-test.internal.js
+12
@@ -420,6 +420,18 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
420 innerTarget.pointerup({pointerType: 'mouse'});
421 expect(onPress).toBeCalled();
422 });
423 +
424 + it('is called once after virtual screen reader "click" event', () => {
425 + const target = createEventTarget(ref.current);
426 + target.virtualclick();
427 + expect(onPress).toHaveBeenCalledTimes(1);
428 + expect(onPress).toHaveBeenCalledWith(
429 + expect.objectContaining({
430 + pointerType: 'keyboard',
431 + type: 'press',
432 + }),
433 + );
434 + });
435 });
436
437 describe('onPressMove', () => {
packages/react-events/src/dom/testing-library/domEvents.js
+17 -11
@@ -170,28 +170,30 @@ function createMouseEvent(
170 x = 0,
171 y = 0,
172 } = {},
173 + virtual = false,
174 ) {
175 const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
176
177 return createEvent(type, {
178 altKey,
179 buttons,
179 - clientX: x,
180 - clientY: y,
180 + clientX: virtual ? 0 : x,
181 + clientY: virtual ? 0 : y,
182 ctrlKey,
183 + detail: virtual ? 0 : 1,
184 getModifierState(keyArg) {
185 createGetModifierState(keyArg, modifierState);
186 },
187 metaKey,
186 - movementX,
187 - movementY,
188 - offsetX,
189 - offsetY,
190 - pageX: pageX || x,
191 - pageY: pageY || y,
188 + movementX: virtual ? 0 : movementX,
189 + movementY: virtual ? 0 : movementY,
190 + offsetX: virtual ? 0 : offsetX,
191 + offsetY: virtual ? 0 : offsetY,
192 + pageX: virtual ? 0 : pageX || x,
193 + pageY: virtual ? 0 : pageY || y,
194 preventDefault,
193 - screenX: x,
194 - screenY: y + defaultBrowserChromeSize,
195 + screenX: virtual ? 0 : x,
196 + screenY: virtual ? 0 : y + defaultBrowserChromeSize,
197 shiftKey,
198 });
199 }
@@ -251,7 +253,11 @@ export function blur({relatedTarget} = {}) {
253 }
254
255 export function click(payload) {
254 - return createMouseEvent('click', payload);
256 + return createMouseEvent('click', payload, false);
257 +}
258 +
259 +export function virtualclick(payload) {
260 + return createMouseEvent('click', payload, true);
261 }
262
263 export function contextmenu(payload) {
packages/react-events/src/dom/testing-library/index.js
+3
@@ -44,6 +44,9 @@ const createEventTarget = node => ({
44 keyup(payload) {
45 node.dispatchEvent(domEvents.keyup(payload));
46 },
47 + virtualclick(payload) {
48 + node.dispatchEvent(domEvents.virtualclick(payload));
49 + },
50 scroll(payload) {
51 node.dispatchEvent(domEvents.scroll(payload));
52 },