Skip to content

Commit 6b6d80d

Browse files
authored
fix: Click outside should always trigger close (#438)
1 parent d3acf63 commit 6b6d80d

File tree

4 files changed

+76
-3
lines changed

4 files changed

+76
-3
lines changed

examples/single.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,14 @@ class Test extends React.Component {
4848

4949
return (
5050
<div style={{ margin: 20 }}>
51-
<div style={{ height: 150 }} />
51+
<div
52+
style={{ height: 150, background: 'rgba(0, 255, 0, 0.1)' }}
53+
onMouseDown={e => {
54+
e.preventDefault();
55+
}}
56+
>
57+
Prevent Default
58+
</div>
5259

5360
<h2>Single Select</h2>
5461

src/generate.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import useLock from './hooks/useLock';
4141
import useDelayReset from './hooks/useDelayReset';
4242
import useLayoutEffect from './hooks/useLayoutEffect';
4343
import { getSeparatedContent } from './utils/valueUtil';
44+
import useSelectTriggerControl from './hooks/useSelectTriggerControl';
4445

4546
const DEFAULT_OMIT_PROPS = [
4647
'removeIcon',
@@ -332,6 +333,8 @@ export default function generateSelector<
332333
delete domProps[prop];
333334
});
334335

336+
const containerRef = React.useRef<HTMLDivElement>(null);
337+
const triggerRef = React.useRef<RefTriggerProps>(null);
335338
const selectorRef = React.useRef<RefSelectorProps>(null);
336339
const listRef = React.useRef<RefOptionListProps>(null);
337340

@@ -634,6 +637,15 @@ export default function generateSelector<
634637
}
635638
};
636639

640+
useSelectTriggerControl(
641+
[
642+
containerRef.current,
643+
triggerRef.current && triggerRef.current.getPopupElement(),
644+
],
645+
triggerOpen,
646+
onToggleOpen,
647+
);
648+
637649
// ============================= Search =============================
638650
const triggerSearch = (searchText: string, fromTyping: boolean = true) => {
639651
let ret = true;
@@ -764,7 +776,6 @@ export default function generateSelector<
764776
};
765777

766778
// ========================== Focus / Blur ==========================
767-
const triggerRef = React.useRef<RefTriggerProps>(null);
768779
/** Record real focus status */
769780
const focusRef = React.useRef<boolean>(false);
770781

@@ -856,7 +867,6 @@ export default function generateSelector<
856867
};
857868

858869
// ============================= Popup ==============================
859-
const containerRef = React.useRef<HTMLDivElement>(null);
860870
const [containerWidth, setContainerWidth] = React.useState(null);
861871

862872
useLayoutEffect(() => {

src/hooks/useSelectTriggerControl.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as React from 'react';
2+
3+
export default function useSelectTriggerControl(
4+
elements: (HTMLElement | undefined)[],
5+
open: boolean,
6+
triggerOpen: (open: boolean) => void,
7+
) {
8+
const propsRef = React.useRef(null);
9+
propsRef.current = {
10+
elements: elements.filter(e => e),
11+
open,
12+
triggerOpen,
13+
};
14+
15+
React.useEffect(() => {
16+
function onGlobalMouseDown(event: MouseEvent) {
17+
const target = event.target as HTMLElement;
18+
if (
19+
propsRef.current.open &&
20+
propsRef.current.elements.every(
21+
element => !element.contains(target) && element !== target,
22+
)
23+
) {
24+
// Should trigger close
25+
propsRef.current.triggerOpen(false);
26+
}
27+
}
28+
29+
window.addEventListener('mousedown', onGlobalMouseDown);
30+
return () => window.removeEventListener('mousedown', onGlobalMouseDown);
31+
}, []);
32+
}

tests/Select.test.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { mount, render } from 'enzyme';
22
import KeyCode from 'rc-util/lib/KeyCode';
33
import React from 'react';
4+
import { act } from 'react-dom/test-utils';
45
import { resetWarned } from 'rc-util/lib/warning';
56
import Select, { OptGroup, Option, SelectProps } from '../src';
67
import focusTest from './shared/focusTest';
@@ -1411,4 +1412,27 @@ describe('Select.Basic', () => {
14111412
expect(wrapper.find('SelectTrigger').props().visible).toBeFalsy();
14121413
expect(wrapper.find('Input').props().editable).toBeTruthy();
14131414
});
1415+
1416+
it('click outside to close select', () => {
1417+
const wrapper = mount(
1418+
<Select>
1419+
<Option value="1">One</Option>
1420+
</Select>,
1421+
);
1422+
1423+
toggleOpen(wrapper);
1424+
1425+
const clickEvent = new Event('mousedown');
1426+
Object.defineProperty(clickEvent, 'target', {
1427+
get: () => document.body,
1428+
});
1429+
act(() => {
1430+
window.dispatchEvent(clickEvent);
1431+
});
1432+
wrapper.update();
1433+
1434+
expectOpen(wrapper, false);
1435+
1436+
wrapper.unmount();
1437+
});
14141438
});

0 commit comments

Comments
 (0)