Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Then open `http://localhost:8000`.
| blurDelay | Delay before hiding on blur, in seconds. | `number` | - |
| builtinPlacements | Named placement presets. | `BuildInPlacements` | `{}` |
| defaultPopupVisible | Initial uncontrolled visibility. | `boolean` | `false` |
| disabled | Temporarily suppress popup visibility without resetting the current open state. | `boolean` | `false` |
| focusDelay | Delay before showing on focus, in seconds. | `number` | - |
| forceRender | Render popup before it is first shown. | `boolean` | `false` |
| fresh | Keep popup content updated while closed. | `boolean` | - |
Expand Down
1 change: 1 addition & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ npm start
| blurDelay | `blur` 后隐藏前的延迟,单位为秒。 | `number` | - |
| builtinPlacements | 命名位置预设。 | `BuildInPlacements` | `{}` |
| defaultPopupVisible | 非受控初始显示状态。 | `boolean` | `false` |
| disabled | 临时隐藏弹层,但不主动重置当前打开状态。 | `boolean` | `false` |
| focusDelay | `focus` 后显示前的延迟,单位为秒。 | `number` | - |
| forceRender | 首次显示前渲染弹层。 | `boolean` | `false` |
| fresh | 关闭时仍保持弹层内容更新。 | `boolean` | - |
Expand Down
8 changes: 8 additions & 0 deletions docs/demos/disabled.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Disabled
nav:
title: Demo
path: /demo
---

<code src="../examples/disabled.tsx"></code>
38 changes: 38 additions & 0 deletions docs/examples/disabled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Trigger from '@rc-component/trigger';
import React, { useState } from 'react';
import '../../assets/index.less';

const builtinPlacements = {
top: {
points: ['bc', 'tc'],
offset: [0, -8],
},
};

const DisabledDemo = () => {
const [disabled, setDisabled] = useState(false);

return (
<div style={{ padding: 100 }}>
<Trigger
action="hover"
builtinPlacements={builtinPlacements}
disabled={disabled}
popup={<span>Tooltip content</span>}
popupPlacement="top"
popupStyle={{
padding: '6px 8px',
color: '#fff',
background: '#1f1f1f',
borderRadius: 4,
}}
>
<button type="button" onClick={() => setDisabled((value) => !value)}>
{disabled ? 'Enable Tooltip' : 'Disable Tooltip'}
</button>
</Trigger>
</div>
);
};

export default DisabledDemo;
5 changes: 4 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export interface TriggerProps {
action?: ActionType | ActionType[];
showAction?: ActionType[];
hideAction?: ActionType[];
/** Temporarily suppress popup visibility without resetting the current open state. */
disabled?: boolean;

prefixCls?: string;

Expand Down Expand Up @@ -159,6 +161,7 @@ export function generateTrigger(
action = 'hover',
showAction,
hideAction,
disabled = false,

// Open
popupVisible,
Expand Down Expand Up @@ -319,7 +322,7 @@ export function generateTrigger(
popupVisible,
);

const mergedOpen = internalOpen || false;
const mergedOpen = (internalOpen || false) && !disabled;

// ========================== Children ==========================
const child = React.useMemo(() => {
Expand Down
57 changes: 57 additions & 0 deletions tests/basic.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,35 @@ describe('Trigger.Basic', () => {
trigger(document, '.rc-trigger-popup', 'pointerEnter');
expect(isPopupHidden()).toBeFalsy();
});

it('temporarily hides while disabled and restores without mouse leave', () => {
const onOpenChange = jest.fn();
const Demo = ({ disabled = false }) => (
<Trigger
action={['hover']}
disabled={disabled}
onOpenChange={onOpenChange}
popup={<strong>trigger</strong>}
>
<div className="target">hover</div>
</Trigger>
);

const { container, rerender } = render(<Demo />);

trigger(container, '.target', 'mouseEnter');
expect(isPopupHidden()).toBeFalsy();
expect(onOpenChange).toHaveBeenCalledWith(true);
onOpenChange.mockReset();

rerender(<Demo disabled />);
expect(isPopupHidden()).toBeTruthy();
expect(onOpenChange).not.toHaveBeenCalled();

rerender(<Demo />);
expect(isPopupHidden()).toBeFalsy();
expect(onOpenChange).not.toHaveBeenCalled();
});
});

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

it('temporarily hides a controlled popup without changing its open state', () => {
const onOpenChange = jest.fn();
const Demo = ({ disabled = false }) => (
<Trigger
disabled={disabled}
onOpenChange={onOpenChange}
popup={<strong>trigger</strong>}
popupVisible
>
{({ open }) => <div data-open={open} />}
</Trigger>
);

const { container, rerender } = render(<Demo />);

expect(container.firstChild).toHaveAttribute('data-open', 'true');
expect(isPopupHidden()).toBeFalsy();

rerender(<Demo disabled />);
expect(container.firstChild).toHaveAttribute('data-open', 'false');
expect(isPopupHidden()).toBeTruthy();

rerender(<Demo />);
expect(container.firstChild).toHaveAttribute('data-open', 'true');
expect(isPopupHidden()).toBeFalsy();
expect(onOpenChange).not.toHaveBeenCalled();
});

describe('click window to hide', () => {
it('should hide', async () => {
const onOpenChange = jest.fn();
Expand Down
Loading