-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(FocusZone): fix EventListener leak (#14031)
* fix(FocusZone): fix EventListener leak * changelog * address PR comments * address PR comments
- Loading branch information
1 parent
2d5932c
commit 042d257
Showing
3 changed files
with
114 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"type": "patch", | ||
"comment": "Fix memory leak in FocusZone", | ||
"packageName": "@fluentui/react-focus", | ||
"email": "mistastn@microsoft.com", | ||
"dependentChangeType": "patch", | ||
"date": "2020-07-14T22:07:46.112Z" | ||
} |
92 changes: 92 additions & 0 deletions
92
packages/react-focus/src/components/FocusZone/FocusZone.EventHandler.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import * as ReactDOM from 'react-dom'; | ||
import * as React from 'react'; | ||
import { FocusZone } from './FocusZone'; | ||
|
||
type EventHandler = { | ||
listener: EventListenerOrEventListenerObject; | ||
options?: boolean | AddEventListenerOptions; | ||
}; | ||
|
||
function optionsEqual( | ||
a: boolean | AddEventListenerOptions | undefined, | ||
b: boolean | AddEventListenerOptions | undefined, | ||
) { | ||
if (typeof a !== 'object' || typeof b !== 'object') { | ||
return a === b; | ||
} | ||
|
||
return a.once === b.once && a.passive === b.passive && a.capture === b.capture; | ||
} | ||
|
||
function handlersEqual(a: EventHandler, b: EventHandler) { | ||
return a.listener === b.listener && optionsEqual(a.options, b.options); | ||
} | ||
|
||
// HEADS UP: this test is intentionally in a separate file aside from the rest of FocusZone tests | ||
// As it is testing ref counting on a global `window` object it would interfere with other FocusZone tests | ||
// which use ReactTestUtils.renderIntoDocument() which renders FocusZone into a detached DOM node and never unmounts. | ||
describe('FocusZone keydown event handler', () => { | ||
let host: HTMLElement; | ||
let keydownEventHandlers: EventHandler[]; | ||
|
||
beforeEach(() => { | ||
host = document.createElement('div'); | ||
keydownEventHandlers = []; | ||
|
||
window.addEventListener = jest.fn((type, listener, options) => { | ||
if (type === 'keydown') { | ||
const eventListener = { listener, options }; | ||
if (!keydownEventHandlers.some(item => handlersEqual(item, eventListener))) { | ||
keydownEventHandlers.push(eventListener); | ||
} | ||
} | ||
}); | ||
|
||
window.removeEventListener = jest.fn((type, listener, options) => { | ||
if (type === 'keydown') { | ||
const eventListener = { listener, options }; | ||
const index = keydownEventHandlers.findIndex(item => handlersEqual(item, eventListener)); | ||
if (index >= 0) { | ||
keydownEventHandlers.splice(index, 1); | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
it('is added on mount/removed on unmount', () => { | ||
ReactDOM.render(<FocusZone />, host); | ||
expect(keydownEventHandlers.length).toBe(1); | ||
|
||
ReactDOM.unmountComponentAtNode(host); | ||
expect(keydownEventHandlers.length).toBe(0); | ||
}); | ||
|
||
it('is added only once for nested focus zones', () => { | ||
ReactDOM.render( | ||
<div> | ||
<FocusZone> | ||
<FocusZone /> | ||
</FocusZone> | ||
</div>, | ||
host, | ||
); | ||
expect(keydownEventHandlers.length).toBe(1); | ||
|
||
ReactDOM.unmountComponentAtNode(host); | ||
expect(keydownEventHandlers.length).toBe(0); | ||
}); | ||
|
||
it('is added only once for sibling focus zones', () => { | ||
ReactDOM.render( | ||
<div> | ||
<FocusZone /> | ||
<FocusZone /> | ||
</div>, | ||
host, | ||
); | ||
expect(keydownEventHandlers.length).toBe(1); | ||
|
||
ReactDOM.unmountComponentAtNode(host); | ||
expect(keydownEventHandlers.length).toBe(0); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters