Fixed keyboard/mouse events inside of extension by using ref.ownerDocument
Brian Vaughn committed
Mar 18, 2019 at 15:11 UTC
2fc03eb151758917c0666c971ab89861fdcc9755
4 files changed
+52
-19
src/devtools/views/Elements/SearchInput.js
+12
-3
@@ -52,19 +52,28 @@ export default function SearchInput(props: Props) {
52
53
// Auto-focus search input
54
useEffect(() => {
55
- const handleWindowKeyDown = event => {
55
+ if (inputRef.current === null) {
56
+ return () => {};
57
+ }
58
+
59
+ const handleWindowKey = (event: KeyboardEvent) => {
60
const { key, metaKey } = event;
61
if (key === 'f' && metaKey) {
62
if (inputRef.current !== null) {
63
inputRef.current.focus();
64
event.preventDefault();
65
+ event.stopPropagation();
66
}
67
}
68
};
69
65
- window.addEventListener('keydown', handleWindowKeyDown);
70
+ // It's important to listen to the ownerDocument to support the browser extension.
71
+ // Here we use portals to render individual tabs (e.g. Profiler),
72
+ // and the root document might belong to a different window.
73
+ const ownerDocument = inputRef.current.ownerDocument;
74
+ ownerDocument.addEventListener('keydown', handleWindowKey);
75
67
- return () => window.removeEventListener('keydown', handleWindowKeyDown);
76
+ return () => ownerDocument.removeEventListener('keydown', handleWindowKey);
77
}, [inputRef]);
78
79
return (
src/devtools/views/Elements/Tree.js
+13
-4
@@ -32,6 +32,7 @@ export default function Tree(props: Props) {
32
selectPreviousElementInTree,
33
} = useContext(TreeContext);
34
const listRef = useRef<FixedSizeList<any> | null>(null);
35
+ const treeRef = useRef<HTMLDivElement | null>(null);
36
37
const { lineHeight } = useContext(SettingsContext);
38
@@ -45,7 +46,11 @@ export default function Tree(props: Props) {
46
47
// Navigate the tree with up/down arrow keys.
48
useEffect(() => {
48
- const handleKeyDown = event => {
49
+ if (treeRef.current === null) {
50
+ return () => {};
51
+ }
52
+
53
+ const handleKeyDown = (event: KeyboardEvent) => {
54
// eslint-disable-next-line default-case
55
switch (event.key) {
56
case 'ArrowDown':
@@ -67,10 +72,14 @@ export default function Tree(props: Props) {
72
}
73
};
74
70
- window.addEventListener('keydown', handleKeyDown);
75
+ // It's important to listen to the ownerDocument to support the browser extension.
76
+ // Here we use portals to render individual tabs (e.g. Profiler),
77
+ // and the root document might belong to a different window.
78
+ const ownerDocument = treeRef.current.ownerDocument;
79
+ ownerDocument.addEventListener('keydown', handleKeyDown);
80
81
return () => {
73
- window.removeEventListener('keydown', handleKeyDown);
82
+ ownerDocument.removeEventListener('keydown', handleKeyDown);
83
};
84
}, [
85
selectNextElementInTree,
@@ -90,7 +99,7 @@ export default function Tree(props: Props) {
99
);
100
101
return (
93
- <div className={styles.Tree}>
102
+ <div className={styles.Tree} ref={treeRef}>
103
<div className={styles.SearchInput}>
104
{ownerStack.length > 0 ? <OwnersStack /> : <SearchInput />}
105
<InspectHostNodesToggle />
src/devtools/views/Profiler/SnapshotCommitList.js
+13
-5
@@ -80,6 +80,7 @@ function List({
80
width,
81
}: ListProps) {
82
const listRef = useRef<FixedSizeList<ItemData> | null>(null);
83
+ const divRef = useRef<HTMLDivElement | null>(null);
84
const prevCommitIndexRef = useRef<number | null>(null);
85
86
// Make sure a newly selected snapshot is fully visible within the list.
@@ -103,11 +104,17 @@ function List({
104
setIsMouseDown(false);
105
}, []);
106
useEffect(() => {
106
- window.addEventListener('mouseup', handleMouseUp);
107
- return () => {
108
- window.removeEventListener('mouseup', handleMouseUp);
109
- };
110
- }, [handleMouseUp]);
107
+ if (divRef.current === null) {
108
+ return () => {};
109
+ }
110
+
111
+ // It's important to listen to the ownerDocument to support the browser extension.
112
+ // Here we use portals to render individual tabs (e.g. Profiler),
113
+ // and the root document might belong to a different window.
114
+ const ownerDocument = divRef.current.ownerDocument;
115
+ ownerDocument.addEventListener('mouseup', handleMouseUp);
116
+ return () => ownerDocument.removeEventListener('mouseup', handleMouseUp);
117
+ }, [divRef, handleMouseUp]);
118
119
const itemSize = useMemo(
120
() => Math.max(minBarWidth, width / filteredCommitIndices.length),
@@ -150,6 +157,7 @@ function List({
157
<div
158
onMouseDown={handleMouseDown}
159
onMouseUp={handleMouseUp}
160
+ ref={divRef}
161
style={{ height, width }}
162
>
163
<FixedSizeList
src/devtools/views/hooks.js
+14
-7
@@ -58,6 +58,10 @@ export function useModalDismissSignal(
58
dismissCallback: Function
59
): void {
60
useEffect(() => {
61
+ if (modalRef.current === null) {
62
+ return () => {};
63
+ }
64
+
65
const handleKeyDown = ({ key }: any) => {
66
if (key === 'Escape') {
67
dismissCallback();
@@ -71,15 +75,18 @@ export function useModalDismissSignal(
75
}
76
};
77
74
- const body = ((document.body: any): HTMLBodyElement);
75
- body.addEventListener('keydown', handleKeyDown);
76
- body.addEventListener('mousedown', handleMouseOrTouch);
77
- body.addEventListener('touchstart', handleMouseOrTouch);
78
+ // It's important to listen to the ownerDocument to support the browser extension.
79
+ // Here we use portals to render individual tabs (e.g. Profiler),
80
+ // and the root document might belong to a different window.
81
+ const ownerDocument = modalRef.current.ownerDocument;
82
+ ownerDocument.addEventListener('keydown', handleKeyDown);
83
+ ownerDocument.addEventListener('mousedown', handleMouseOrTouch);
84
+ ownerDocument.addEventListener('touchstart', handleMouseOrTouch);
85
86
return () => {
80
- body.removeEventListener('keydown', handleKeyDown);
81
- body.removeEventListener('mousedown', handleMouseOrTouch);
82
- body.removeEventListener('touchstart', handleMouseOrTouch);
87
+ ownerDocument.removeEventListener('keydown', handleKeyDown);
88
+ ownerDocument.removeEventListener('mousedown', handleMouseOrTouch);
89
+ ownerDocument.removeEventListener('touchstart', handleMouseOrTouch);
90
};
91
}, [modalRef, dismissCallback]);
92
}