Skip to content

Commit 4f90505

Browse files
1587315093jzh
and
jzh
authored
fix: contextMenu close all (#407)
* fix: contextMenu close all * test: add all closed test cases * style: delete note --------- Co-authored-by: jzh <[email protected]>
1 parent 0583fd7 commit 4f90505

File tree

4 files changed

+213
-8
lines changed

4 files changed

+213
-8
lines changed

docs/examples/body-overflow.tsx

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import '../../assets/index.less';
55

66
export default () => {
77
const [open, setOpen] = React.useState(false);
8-
8+
const [open1, setOpen1] = React.useState(false);
9+
const [open2, setOpen2] = React.useState(false);
10+
const [open3, setOpen3] = React.useState(false);
911
return (
1012
<React.StrictMode>
1113
<style
@@ -72,6 +74,154 @@ export default () => {
7274
Target
7375
</span>
7476
</Trigger>
77+
78+
<Trigger
79+
arrow
80+
action="click"
81+
popupVisible={open1}
82+
onPopupVisibleChange={(next) => {
83+
console.log('Visible Change:', next);
84+
setOpen1(next);
85+
}}
86+
popupTransitionName="rc-trigger-popup-zoom"
87+
popup={
88+
<div
89+
style={{
90+
background: 'yellow',
91+
border: '1px solid blue',
92+
width: 200,
93+
height: 60,
94+
opacity: 0.9,
95+
}}
96+
>
97+
<button
98+
onClick={() => {
99+
setOpen1(false);
100+
}}
101+
>
102+
Close
103+
</button>
104+
</div>
105+
}
106+
// popupVisible
107+
popupStyle={{ boxShadow: '0 0 5px red' }}
108+
popupAlign={{
109+
points: ['tc', 'bc'],
110+
overflow: {
111+
shiftX: 50,
112+
adjustY: true,
113+
},
114+
htmlRegion: 'scroll',
115+
}}
116+
>
117+
<span
118+
style={{
119+
background: 'green',
120+
color: '#FFF',
121+
paddingBlock: 30,
122+
paddingInline: 70,
123+
opacity: 0.9,
124+
transform: 'scale(0.6)',
125+
display: 'inline-block',
126+
}}
127+
>
128+
Target Click
129+
</span>
130+
</Trigger>
131+
132+
<Trigger
133+
arrow
134+
action="contextMenu"
135+
popupVisible={open2}
136+
onPopupVisibleChange={(next) => {
137+
console.log('Visible Change:', next);
138+
setOpen2(next);
139+
}}
140+
popupTransitionName="rc-trigger-popup-zoom"
141+
popup={
142+
<div
143+
style={{
144+
background: 'yellow',
145+
border: '1px solid blue',
146+
width: 200,
147+
height: 60,
148+
opacity: 0.9,
149+
}}
150+
>
151+
Target ContextMenu1
152+
</div>
153+
}
154+
popupStyle={{ boxShadow: '0 0 5px red' }}
155+
popupAlign={{
156+
points: ['tc', 'bc'],
157+
overflow: {
158+
shiftX: 50,
159+
adjustY: true,
160+
},
161+
htmlRegion: 'scroll',
162+
}}
163+
>
164+
<span
165+
style={{
166+
background: 'blue',
167+
color: '#FFF',
168+
paddingBlock: 30,
169+
paddingInline: 70,
170+
opacity: 0.9,
171+
transform: 'scale(0.6)',
172+
display: 'inline-block',
173+
}}
174+
>
175+
Target ContextMenu1
176+
</span>
177+
</Trigger>
178+
179+
<Trigger
180+
arrow
181+
action="contextMenu"
182+
popupVisible={open3}
183+
onPopupVisibleChange={(next) => {
184+
console.log('Visible Change:', next);
185+
setOpen3(next);
186+
}}
187+
popupTransitionName="rc-trigger-popup-zoom"
188+
popup={
189+
<div
190+
style={{
191+
background: 'yellow',
192+
border: '1px solid blue',
193+
width: 200,
194+
height: 60,
195+
opacity: 0.9,
196+
}}
197+
>
198+
Target ContextMenu2
199+
</div>
200+
}
201+
popupStyle={{ boxShadow: '0 0 5px red' }}
202+
popupAlign={{
203+
points: ['tc', 'bc'],
204+
overflow: {
205+
shiftX: 50,
206+
adjustY: true,
207+
},
208+
htmlRegion: 'scroll',
209+
}}
210+
>
211+
<span
212+
style={{
213+
background: 'blue',
214+
color: '#FFF',
215+
paddingBlock: 30,
216+
paddingInline: 70,
217+
opacity: 0.9,
218+
transform: 'scale(0.6)',
219+
display: 'inline-block',
220+
}}
221+
>
222+
Target ContextMenu2
223+
</span>
224+
</Trigger>
75225
</React.StrictMode>
76226
);
77227
};

src/hooks/useWinClick.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,14 @@ export default function useWinClick(
6767

6868
win.addEventListener('mousedown', onWinMouseDown);
6969
win.addEventListener('click', onWinClick);
70+
win.addEventListener('contextmenu', onWinClick);
7071

7172
// shadow root
7273
const targetShadowRoot = getShadowRoot(targetEle);
7374
if (targetShadowRoot) {
7475
targetShadowRoot.addEventListener('mousedown', onShadowMouseDown);
7576
targetShadowRoot.addEventListener('click', onShadowClick);
77+
targetShadowRoot.addEventListener('contextmenu', onShadowClick);
7678
}
7779

7880
// Warning if target and popup not in same root
@@ -89,10 +91,12 @@ export default function useWinClick(
8991
return () => {
9092
win.removeEventListener('mousedown', onWinMouseDown);
9193
win.removeEventListener('click', onWinClick);
94+
win.removeEventListener('contextmenu', onWinClick);
9295

9396
if (targetShadowRoot) {
9497
targetShadowRoot.removeEventListener('mousedown', onShadowMouseDown);
9598
targetShadowRoot.removeEventListener('click', onShadowClick);
99+
targetShadowRoot.removeEventListener('contextmenu', onShadowClick);
96100
}
97101
};
98102
}

src/index.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -553,8 +553,13 @@ export function generateTrigger(
553553
// ==================== Action: ContextMenu =====================
554554
if (showActions.has('contextMenu')) {
555555
cloneProps.onContextMenu = (event: React.MouseEvent, ...args: any[]) => {
556-
setMousePosByEvent(event);
557-
triggerOpen(true);
556+
if (openRef.current && hideActions.has('contextMenu')) {
557+
triggerOpen(false);
558+
} else {
559+
setMousePosByEvent(event);
560+
triggerOpen(true);
561+
}
562+
558563
event.preventDefault();
559564

560565
// Pass to origin
@@ -608,9 +613,9 @@ export function generateTrigger(
608613

609614
const innerArrow: ArrowTypeOuter = arrow
610615
? {
611-
// true and Object likely
612-
...(arrow !== true ? arrow : {}),
613-
}
616+
// true and Object likely
617+
...(arrow !== true ? arrow : {}),
618+
}
614619
: null;
615620

616621
// Render

tests/basic.test.jsx

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,16 @@ describe('Trigger.Basic', () => {
3535
.querySelector('.rc-trigger-popup')
3636
.className.includes('-hidden');
3737
}
38-
38+
function isPopupClassHidden(name) {
39+
return document
40+
.querySelector(name).className.includes('-hidden')
41+
}
42+
function isPopupAllHidden() {
43+
const popupArr = document
44+
.querySelectorAll('.rc-trigger-popup')
45+
46+
return Array.from(popupArr).every(item => item.className.includes('-hidden'))
47+
}
3948
describe('getPopupContainer', () => {
4049
it('defaults to document.body', () => {
4150
const { container } = render(
@@ -160,7 +169,44 @@ describe('Trigger.Basic', () => {
160169

161170
expect(isPopupHidden()).toBeTruthy();
162171
});
172+
it('contextMenu all close ', () => {
173+
const triggerRef1 = createRef();
174+
const triggerRef2 = createRef();
175+
const { container } = render(
176+
<>
177+
<Trigger
178+
ref={triggerRef1}
179+
popupClassName='trigger-popup1'
180+
action={['contextMenu']}
181+
popupAlign={placementAlignMap.left}
182+
popup={<strong>trigger1</strong>}
183+
>
184+
<div className="target1">contextMenu 1</div>
185+
</Trigger>
186+
<Trigger
187+
ref={triggerRef2}
188+
action={['contextMenu']}
189+
popupClassName='trigger-popup2'
190+
popupAlign={placementAlignMap.right}
191+
popup={<strong>trigger2</strong>}
192+
>
193+
<div className="target2">contextMenu 2</div>
194+
</Trigger>
195+
</>,
196+
);
197+
198+
trigger(container, '.target1', 'contextMenu');
199+
trigger(container, '.target2', 'contextMenu');
200+
expect(isPopupClassHidden('.trigger-popup1')).toBeTruthy();
201+
expect(isPopupClassHidden('.trigger-popup2')).toBeFalsy();
202+
203+
trigger(container, '.target1', 'contextMenu');
204+
expect(isPopupClassHidden('.trigger-popup1')).toBeFalsy();
205+
expect(isPopupClassHidden('.trigger-popup2')).toBeTruthy();
163206

207+
fireEvent.click(document.body);
208+
expect(isPopupAllHidden()).toBeTruthy();
209+
});
164210
describe('afterPopupVisibleChange can be triggered', () => {
165211
it('uncontrolled', async () => {
166212
let triggered = 0;
@@ -836,7 +882,7 @@ describe('Trigger.Basic', () => {
836882
});
837883

838884
it('find real dom node if children not support `forwardRef`', () => {
839-
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
885+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
840886
const Node = () => <p />;
841887

842888
render(

0 commit comments

Comments
 (0)