Skip to content

Commit

Permalink
Fix EuiOutsideClickDetector to work when its nested inside itself (#1039
Browse files Browse the repository at this point in the history
)

* Fix EuiOutsideClickDetector to work when its nested inside itself

* changelog
  • Loading branch information
chandlerprall authored Jul 19, 2018
1 parent 567a9ed commit 8895722
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
**Bug fixes**

- Fixed `EuiContextMenuPanel` calling `ref` after being unmounted ([#1038](https://github.com/elastic/eui/pull/1038))
- `EuiOutsideClickDetector` supports nested detectors in the DOM tree ([#1039](https://github.com/elastic/eui/pull/1039))

**Bug Fixes**

Expand Down
12 changes: 9 additions & 3 deletions src/components/outside_click_detector/outside_click_detector.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class EuiOutsideClickDetector extends Component {
// virtual DOM and executes EuiClickDetector's onClick handler,
// stamping the id even though the event originates outside
// this component's reified DOM tree.
this.id = htmlIdGenerator();
this.id = htmlIdGenerator()();
}

onClickOutside = event => {
Expand All @@ -45,7 +45,7 @@ export class EuiOutsideClickDetector extends Component {
return;
}

if (event.euiGeneratedBy === this.id) {
if (event.euiGeneratedBy && event.euiGeneratedBy.includes(this.id)) {
return;
}

Expand All @@ -61,7 +61,13 @@ export class EuiOutsideClickDetector extends Component {
}

onChildClick = event => {
event.nativeEvent.euiGeneratedBy = this.id;
// to support nested click detectors, build an array
// of detector ids that have been encountered
if (event.nativeEvent.hasOwnProperty('euiGeneratedBy')) {
event.nativeEvent.euiGeneratedBy.push(this.id);
} else {
event.nativeEvent.euiGeneratedBy = [this.id];
}
if (this.props.onClick) this.props.onClick(event);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render } from 'enzyme';
import { render, mount } from 'enzyme';

import { EuiOutsideClickDetector } from './outside_click_detector';

Expand All @@ -14,4 +14,45 @@ describe('EuiOutsideClickDetector', () => {
expect(component)
.toMatchSnapshot();
});

describe('behavior', () => {
test('nested detectors', () => {
const unrelatedDetector = jest.fn();
const parentDetector = jest.fn();
const childDetector = jest.fn();

// enzyme doesn't mount the components into the global jsdom `document`
// but that's where the click detector listener is,
// pass the top-level mounted component's click event on to document
const triggerDocumentClick = e => {
const event = new Event('click');
event.euiGeneratedBy = e.nativeEvent.euiGeneratedBy;
document.dispatchEvent(event);
};

const component = mount(
<div onClick={triggerDocumentClick}>
<div>
<EuiOutsideClickDetector onOutsideClick={parentDetector}>
<div>
<EuiOutsideClickDetector onOutsideClick={childDetector}>
<div data-test-subj="target"/>
</EuiOutsideClickDetector>
</div>
</EuiOutsideClickDetector>
</div>

<EuiOutsideClickDetector onOutsideClick={unrelatedDetector}>
<div/>
</EuiOutsideClickDetector>
</div>
);

component.find('[data-test-subj="target"]').simulate('click');

expect(unrelatedDetector).toHaveBeenCalledTimes(1);
expect(parentDetector).toHaveBeenCalledTimes(0);
expect(childDetector).toHaveBeenCalledTimes(0);
});
});
});

0 comments on commit 8895722

Please sign in to comment.