Skip to content

Commit 60ceed8

Browse files
author
Conrad Chan
authored
fix(highlights): Manually apply focus to highlight target (#600)
1 parent 2278b0c commit 60ceed8

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

src/highlight/HighlightTarget.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as ReactRedux from 'react-redux';
33
import classNames from 'classnames';
44
import noop from 'lodash/noop';
55
import { getIsCurrentFileVersion } from '../store';
6+
import { MOUSE_PRIMARY } from '../constants';
67
import { Rect } from '../@types/model';
78
import './HighlightTarget.scss';
89

@@ -20,6 +21,13 @@ const HighlightTarget = (props: Props, ref: React.Ref<HighlightTargetRef>): JSX.
2021
const { annotationId, className, onHover = noop, onSelect = noop, shapes } = props;
2122
const isCurrentFileVersion = ReactRedux.useSelector(getIsCurrentFileVersion);
2223

24+
const handleClick = (event: React.MouseEvent<HighlightTargetRef>): void => {
25+
// These are needed to prevent the anchor link from being followed and updating the url location
26+
event.preventDefault();
27+
event.stopPropagation();
28+
event.nativeEvent.stopImmediatePropagation();
29+
};
30+
2331
const handleFocus = (): void => {
2432
onSelect(annotationId);
2533
};
@@ -32,6 +40,26 @@ const HighlightTarget = (props: Props, ref: React.Ref<HighlightTargetRef>): JSX.
3240
onHover(null);
3341
};
3442

43+
const handleMouseDown = (event: React.MouseEvent<HighlightTargetRef>): void => {
44+
if (event.buttons !== MOUSE_PRIMARY) {
45+
return;
46+
}
47+
const activeElement = document.activeElement as HTMLElement;
48+
49+
onSelect(annotationId);
50+
51+
event.preventDefault(); // Prevents focus from leaving the anchor immediately in some browsers
52+
event.nativeEvent.stopImmediatePropagation(); // Prevents document event handlers from executing
53+
54+
// IE11 won't apply the focus to the SVG anchor, so this workaround attempts to blur the existing
55+
// active element.
56+
if (activeElement && activeElement !== event.currentTarget && activeElement.blur) {
57+
activeElement.blur();
58+
}
59+
60+
event.currentTarget.focus();
61+
};
62+
3563
return (
3664
// eslint-disable-next-line jsx-a11y/anchor-is-valid
3765
<a
@@ -42,7 +70,9 @@ const HighlightTarget = (props: Props, ref: React.Ref<HighlightTargetRef>): JSX.
4270
data-resin-target="highlightText"
4371
data-testid={`ba-AnnotationTarget-${annotationId}`}
4472
href="#"
73+
onClick={handleClick}
4574
onFocus={handleFocus}
75+
onMouseDown={handleMouseDown}
4676
onMouseEnter={handleMouseEnter}
4777
onMouseLeave={handleMouseLeave}
4878
role="button"

src/highlight/__tests__/HighlightTarget-test.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,65 @@ describe('HighlightTarget', () => {
5757
expect(defaults.onSelect).toHaveBeenCalledWith(defaults.annotationId);
5858
});
5959

60+
describe('handleMouseDown()', () => {
61+
const mockEvent = {
62+
buttons: 1,
63+
currentTarget: {
64+
focus: jest.fn(),
65+
},
66+
preventDefault: jest.fn(),
67+
nativeEvent: {
68+
stopImmediatePropagation: jest.fn(),
69+
},
70+
};
71+
72+
test('should do nothing if button is not MOUSE_PRIMARY', () => {
73+
const wrapper = getWrapper();
74+
const anchor = wrapper.find('a');
75+
const event = {
76+
...mockEvent,
77+
buttons: 2,
78+
};
79+
80+
anchor.simulate('mousedown', event);
81+
82+
expect(mockEvent.preventDefault).not.toHaveBeenCalled();
83+
expect(mockEvent.nativeEvent.stopImmediatePropagation).not.toHaveBeenCalled();
84+
});
85+
86+
test('should call onSelect', () => {
87+
const wrapper = getWrapper();
88+
const anchor = wrapper.find('a');
89+
const event = {
90+
...mockEvent,
91+
buttons: 1,
92+
};
93+
94+
anchor.simulate('mousedown', event);
95+
96+
expect(defaults.onSelect).toHaveBeenCalledWith('123');
97+
expect(mockEvent.preventDefault).toHaveBeenCalled();
98+
expect(mockEvent.nativeEvent.stopImmediatePropagation).toHaveBeenCalled();
99+
expect(mockEvent.currentTarget.focus).toHaveBeenCalled();
100+
});
101+
102+
test('should call blur on the document.activeElement', () => {
103+
const blurSpy = jest.spyOn(document.body, 'blur');
104+
const wrapper = getWrapper();
105+
const anchor = wrapper.find('a');
106+
const event = {
107+
...mockEvent,
108+
buttons: 1,
109+
};
110+
111+
expect(document.activeElement).toBe(document.body);
112+
113+
anchor.simulate('mousedown', event);
114+
115+
expect(blurSpy).toHaveBeenCalled();
116+
});
117+
});
118+
60119
describe('handleMouseEnter()', () => {
61120
test('should call onHover with annotationId', () => {
62121
const wrapper = getWrapper();

0 commit comments

Comments
 (0)