Skip to content

Commit 1cdbb28

Browse files
committed
feat: support disabled prop
1 parent 8e1a491 commit 1cdbb28

5 files changed

Lines changed: 99 additions & 4 deletions

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` | - |

src/index.tsx

Lines changed: 6 additions & 3 deletions
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(() => {
@@ -374,10 +377,10 @@ export function generateTrigger(
374377
if (mergedOpen) {
375378
uniqueContext.show(getUniqueOptions(mouseEnterDelay), isOpen);
376379
} else {
377-
uniqueContext.hide(mouseLeaveDelay);
380+
uniqueContext.hide(disabled ? 0 : mouseLeaveDelay);
378381
}
379382
}
380-
}, [mergedOpen, targetEle]);
383+
}, [mergedOpen, targetEle, disabled]);
381384

382385
const openRef = React.useRef(mergedOpen);
383386
openRef.current = mergedOpen;

tests/basic.test.jsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,36 @@ 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+
});
211+
182212
});
183213

184214
it('contextMenu works', () => {
@@ -1009,6 +1039,34 @@ describe('Trigger.Basic', () => {
10091039
expect(document.querySelector('.rc-trigger-popup')).toBeTruthy();
10101040
});
10111041

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

tests/unique.test.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,38 @@ describe('Trigger.Unique', () => {
6969
jest.restoreAllMocks();
7070
});
7171

72+
it('ignores leave delay when controlled popup is disabled', async () => {
73+
const Demo = ({ disabled = false }) => (
74+
<UniqueProvider>
75+
<Trigger
76+
disabled={disabled}
77+
mouseLeaveDelay={10}
78+
popup={<strong>trigger</strong>}
79+
popupVisible
80+
unique
81+
>
82+
<div className="target" />
83+
</Trigger>
84+
</UniqueProvider>
85+
);
86+
87+
const { rerender } = render(<Demo />);
88+
await awaitFakeTimer();
89+
expect(document.querySelector('.rc-trigger-popup')).not.toHaveClass(
90+
'rc-trigger-popup-hidden',
91+
);
92+
93+
rerender(<Demo disabled />);
94+
expect(document.querySelector('.rc-trigger-popup')).toHaveClass(
95+
'rc-trigger-popup-hidden',
96+
);
97+
98+
rerender(<Demo />);
99+
expect(document.querySelector('.rc-trigger-popup')).not.toHaveClass(
100+
'rc-trigger-popup-hidden',
101+
);
102+
});
103+
72104
it('moving will not hide the popup', async () => {
73105
const { container } = render(
74106
<UniqueProvider>
@@ -376,7 +408,7 @@ describe('Trigger.Unique', () => {
376408
});
377409

378410
it('esc should close unique popup', async () => {
379-
const { container,baseElement } = render(
411+
const { container, baseElement } = render(
380412
<UniqueProvider>
381413
<Trigger action={['click']} popup={<div>Popup</div>} unique>
382414
<div className="target" />

0 commit comments

Comments
 (0)