Skip to content

Commit f6e0c74

Browse files
authored
feat: support disabled prop (#638)
1 parent 8e1a491 commit f6e0c74

6 files changed

Lines changed: 109 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Then open `http://localhost:8000`.
7575
| blurDelay | Delay before hiding on blur, in seconds. | `number` | - |
7676
| builtinPlacements | Named placement presets. | `BuildInPlacements` | `{}` |
7777
| defaultPopupVisible | Initial uncontrolled visibility. | `boolean` | `false` |
78+
| disabled | Temporarily suppress popup visibility without resetting the current open state. | `boolean` | `false` |
7879
| focusDelay | Delay before showing on focus, in seconds. | `number` | - |
7980
| forceRender | Render popup before it is first shown. | `boolean` | `false` |
8081
| fresh | Keep popup content updated while closed. | `boolean` | - |

README.zh-CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ npm start
7575
| blurDelay | `blur` 后隐藏前的延迟,单位为秒。 | `number` | - |
7676
| builtinPlacements | 命名位置预设。 | `BuildInPlacements` | `{}` |
7777
| defaultPopupVisible | 非受控初始显示状态。 | `boolean` | `false` |
78+
| disabled | 临时隐藏弹层,但不主动重置当前打开状态。 | `boolean` | `false` |
7879
| focusDelay | `focus` 后显示前的延迟,单位为秒。 | `number` | - |
7980
| forceRender | 首次显示前渲染弹层。 | `boolean` | `false` |
8081
| fresh | 关闭时仍保持弹层内容更新。 | `boolean` | - |

docs/demos/disabled.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Disabled
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/disabled.tsx"></code>

docs/examples/disabled.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Trigger from '@rc-component/trigger';
2+
import React, { useState } from 'react';
3+
import '../../assets/index.less';
4+
5+
const builtinPlacements = {
6+
top: {
7+
points: ['bc', 'tc'],
8+
offset: [0, -8],
9+
},
10+
};
11+
12+
const DisabledDemo = () => {
13+
const [disabled, setDisabled] = useState(false);
14+
15+
return (
16+
<div style={{ padding: 100 }}>
17+
<Trigger
18+
action="hover"
19+
builtinPlacements={builtinPlacements}
20+
disabled={disabled}
21+
popup={<span>Tooltip content</span>}
22+
popupPlacement="top"
23+
popupStyle={{
24+
padding: '6px 8px',
25+
color: '#fff',
26+
background: '#1f1f1f',
27+
borderRadius: 4,
28+
}}
29+
>
30+
<button type="button" onClick={() => setDisabled((value) => !value)}>
31+
{disabled ? 'Enable Tooltip' : 'Disable Tooltip'}
32+
</button>
33+
</Trigger>
34+
</div>
35+
);
36+
};
37+
38+
export default DisabledDemo;

src/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export interface TriggerProps {
6565
action?: ActionType | ActionType[];
6666
showAction?: ActionType[];
6767
hideAction?: ActionType[];
68+
/** Temporarily suppress popup visibility without resetting the current open state. */
69+
disabled?: boolean;
6870

6971
prefixCls?: string;
7072

@@ -159,6 +161,7 @@ export function generateTrigger(
159161
action = 'hover',
160162
showAction,
161163
hideAction,
164+
disabled = false,
162165

163166
// Open
164167
popupVisible,
@@ -319,7 +322,7 @@ export function generateTrigger(
319322
popupVisible,
320323
);
321324

322-
const mergedOpen = internalOpen || false;
325+
const mergedOpen = (internalOpen || false) && !disabled;
323326

324327
// ========================== Children ==========================
325328
const child = React.useMemo(() => {

tests/basic.test.jsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,35 @@ describe('Trigger.Basic', () => {
179179
trigger(document, '.rc-trigger-popup', 'pointerEnter');
180180
expect(isPopupHidden()).toBeFalsy();
181181
});
182+
183+
it('temporarily hides while disabled and restores without mouse leave', () => {
184+
const onOpenChange = jest.fn();
185+
const Demo = ({ disabled = false }) => (
186+
<Trigger
187+
action={['hover']}
188+
disabled={disabled}
189+
onOpenChange={onOpenChange}
190+
popup={<strong>trigger</strong>}
191+
>
192+
<div className="target">hover</div>
193+
</Trigger>
194+
);
195+
196+
const { container, rerender } = render(<Demo />);
197+
198+
trigger(container, '.target', 'mouseEnter');
199+
expect(isPopupHidden()).toBeFalsy();
200+
expect(onOpenChange).toHaveBeenCalledWith(true);
201+
onOpenChange.mockReset();
202+
203+
rerender(<Demo disabled />);
204+
expect(isPopupHidden()).toBeTruthy();
205+
expect(onOpenChange).not.toHaveBeenCalled();
206+
207+
rerender(<Demo />);
208+
expect(isPopupHidden()).toBeFalsy();
209+
expect(onOpenChange).not.toHaveBeenCalled();
210+
});
182211
});
183212

184213
it('contextMenu works', () => {
@@ -1009,6 +1038,34 @@ describe('Trigger.Basic', () => {
10091038
expect(document.querySelector('.rc-trigger-popup')).toBeTruthy();
10101039
});
10111040

1041+
it('temporarily hides a controlled popup without changing its open state', () => {
1042+
const onOpenChange = jest.fn();
1043+
const Demo = ({ disabled = false }) => (
1044+
<Trigger
1045+
disabled={disabled}
1046+
onOpenChange={onOpenChange}
1047+
popup={<strong>trigger</strong>}
1048+
popupVisible
1049+
>
1050+
{({ open }) => <div data-open={open} />}
1051+
</Trigger>
1052+
);
1053+
1054+
const { container, rerender } = render(<Demo />);
1055+
1056+
expect(container.firstChild).toHaveAttribute('data-open', 'true');
1057+
expect(isPopupHidden()).toBeFalsy();
1058+
1059+
rerender(<Demo disabled />);
1060+
expect(container.firstChild).toHaveAttribute('data-open', 'false');
1061+
expect(isPopupHidden()).toBeTruthy();
1062+
1063+
rerender(<Demo />);
1064+
expect(container.firstChild).toHaveAttribute('data-open', 'true');
1065+
expect(isPopupHidden()).toBeFalsy();
1066+
expect(onOpenChange).not.toHaveBeenCalled();
1067+
});
1068+
10121069
describe('click window to hide', () => {
10131070
it('should hide', async () => {
10141071
const onOpenChange = jest.fn();

0 commit comments

Comments
 (0)