Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bugfix]: fix the tooltip on disabled children #1658

Merged
merged 14 commits into from
Feb 26, 2021
6 changes: 5 additions & 1 deletion packages/zent/assets/button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -309,12 +309,16 @@ $btn-warning-active: 1;
);
}

&-disabled-wrapper {
cursor: not-allowed;
}

&-disabled,
&-disabled[disabled] {
@include btn-grey(color);
@include theme-color(background-color, stroke, 8);
@include btn-light-grey(border-color);
cursor: not-allowed;
pointer-events: none;

&:link,
&:visited,
Expand Down
2 changes: 1 addition & 1 deletion packages/zent/assets/checkbox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ $size: 16px;
}

& > input[type='checkbox'] {
cursor: not-allowed;
pointer-events: none;
}
}

Expand Down
6 changes: 5 additions & 1 deletion packages/zent/assets/input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ $large-size: 40px;
.zent-input-close {
@include theme-color(color, stroke, 4);
}

&-disabled {
cursor: not-allowed;
}
}

.zent-textarea-wrapper.zent-input-wrapper {
Expand Down Expand Up @@ -122,7 +126,7 @@ $large-size: 40px;
.zent-input[readonly] {
@include theme-color(background-color, stroke, 8);
@include theme-color(color, stroke, 4);
cursor: not-allowed;
pointer-events: none;
}

.zent-input-addon-before,
Expand Down
5 changes: 3 additions & 2 deletions packages/zent/assets/radio.scss
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ $inner-circle-offset: ($outer-circle-size - $inner-circle-size - 2px) / 2;
}

& > input[type='radio'] {
cursor: not-allowed;
pointer-events: none;
}
}

Expand Down Expand Up @@ -194,9 +194,10 @@ $inner-circle-offset: ($outer-circle-size - $inner-circle-size - 2px) / 2;
&--disabled {
@include theme-color(color, stroke, 4);
@include theme-color(background-color, stroke, 8);
cursor: not-allowed;

> input[type='radio'] {
cursor: not-allowed;
pointer-events: none;
}

&.zent-radio-button--checked {
Expand Down
76 changes: 57 additions & 19 deletions packages/zent/src/button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { Component } from 'react';
import { Omit } from 'utility-types';
import Group from './Group';
import { IButtonDirectiveProps, ButtonDirective } from './Directive';
import {
IPopoverHoverTriggerContext,
PopoverHoverTriggerContext,
} from '../popover';

export interface IButtonProps
extends Omit<
Expand All @@ -28,6 +32,31 @@ export class Button extends Component<IButtonProps> {

static Group = Group;
static Directive = ButtonDirective;
static contextType = PopoverHoverTriggerContext;
context!: IPopoverHoverTriggerContext;

/**
* Why fixTooltipOnDisabledChildren?
* Mouse events don't trigger on disabled button
* https://github.com/react-component/tooltip/issues/18
*
* Workaround
* 1. Wrap the disabled button/input in another element.
* 2. Add {pointer-events: none} style to the disabled button/input.
*/
renderCompatibleChildren(children: React.ReactNode) {
return this.context.fixTooltipOnDisabledChildren ? (
<span
className="zent-btn-disabled-wrapper"
onMouseEnter={(this.props as any).onMouseEnter}
onMouseLeave={(this.props as any).onMouseLeave}
>
{children}
</span>
) : (
children
);
}

render() {
const {
Expand All @@ -48,26 +77,35 @@ export class Button extends Component<IButtonProps> {
} = this.props;

return (
<ButtonDirective
type={type}
size={size}
block={block}
disabled={disabled}
loading={loading}
outline={outline}
bordered={bordered}
icon={icon}
>
{href || target ? (
<a href={href || ''} target={target} download={download} {...props}>
{children}
</a>
) : (
<button type={htmlType} {...props}>
{children}
</button>
<>
{this.renderCompatibleChildren(
<ButtonDirective
type={type}
size={size}
block={block}
disabled={disabled}
loading={loading}
outline={outline}
bordered={bordered}
icon={icon}
>
{href || target ? (
<a
href={href || ''}
target={target}
download={download}
{...props}
>
{children}
</a>
) : (
<button type={htmlType} {...props}>
{children}
</button>
)}
</ButtonDirective>
)}
</ButtonDirective>
</>
);
}
}
Expand Down
5 changes: 4 additions & 1 deletion packages/zent/src/checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import getWidth from '../utils/getWidth';
import GroupContext, { ICheckboxContext } from './GroupContext';
import { DisabledContext, IDisabledContext } from '../disabled';
import CheckboxGroup from './Group';
import omit from '../utils/omit';

export interface ICheckboxEventTarget<Value> extends ICheckboxProps<Value> {
type: 'checkbox';
Expand Down Expand Up @@ -117,11 +118,13 @@ export function Checkbox<Value>(props: ICheckboxProps<Value>) {
style={{
...getWidth(width),
}}
onMouseEnter={(props as any).onMouseEnter}
onMouseLeave={(props as any).onMouseLeave}
>
<span className="zent-checkbox">
<span className="zent-checkbox-inner" />
<input
{...others}
{...omit<any, any>(others, ['onMouseEnter', 'onMouseLeave'])}
type="checkbox"
checked={checked && !indeterminate}
disabled={disabled}
Expand Down
4 changes: 1 addition & 3 deletions packages/zent/src/grid/SelectionCheckbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ class SelectionCheckbox extends PureComponent<
const { checked } = this.state;
return reason && disabled ? (
<Pop content={reason} trigger="hover" position="top-left" centerArrow>
<span>
<Checkbox onChange={onChange} checked={checked} disabled={disabled} />
</span>
<Checkbox onChange={onChange} checked={checked} disabled={disabled} />
</Pop>
) : (
<Checkbox onChange={onChange} checked={checked} disabled={disabled} />
Expand Down
4 changes: 1 addition & 3 deletions packages/zent/src/grid/SelectionRadio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ class SelectionCheckbox extends PureComponent<
const { checked } = this.state;
return reason && disabled ? (
<Pop content={reason} trigger="hover" position="top-left" centerArrow>
<span>
<Radio onChange={onChange} checked={checked} disabled={disabled} />
</span>
<Radio onChange={onChange} checked={checked} disabled={disabled} />
</Pop>
) : (
<Radio onChange={onChange} checked={checked} disabled={disabled} />
Expand Down
8 changes: 7 additions & 1 deletion packages/zent/src/input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,18 @@ export class Input extends Component<IInputProps, IInputState> {
(props as IInputCoreProps).addonBefore),
'zent-input--has-focus': hasFocus,
'zent-input-wrapper-inline': props.inline,
'zent-input-wrapper-disabled': disabled,
},
className
);

return (
<div className={wrapClass} style={wrapperStyle}>
<div
className={wrapClass}
style={wrapperStyle}
onMouseEnter={(props as any).onMouseEnter}
onMouseLeave={(props as any).onMouseLeave}
>
{renderInner ? renderInner(children) : children}
</div>
);
Expand Down
2 changes: 2 additions & 0 deletions packages/zent/src/pop/Pop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface IPopHoverTriggerProps<
trigger: 'hover';
mouseEnterDelay?: number;
mouseLeaveDelay?: number;
fixTooltipOnDisabledChildren?: boolean;
}

export interface IPopFocusTriggerProps<
Expand Down Expand Up @@ -169,6 +170,7 @@ export class Pop extends Component<IPopProps, IPopState> {
showDelay={props.mouseEnterDelay}
hideDelay={props.mouseLeaveDelay}
anchorOnly={props.anchorOnly}
fixTooltipOnDisabledChildren={props.fixTooltipOnDisabledChildren}
>
{props.children}
</Trigger.Hover>
Expand Down
5 changes: 5 additions & 0 deletions packages/zent/src/pop/README_en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ A floating card opened by clicking, hovering or focusing.
| mouseEnterDelay | Hover open delay(in ms) | number | No | `200` |
| mouseLeaveDelay | Hover close delay(in ms) | number | No | `200` |
| anchorOnly | Only use trigger as hot area | boolean | No | `false` |
| fixTooltipOnDisabledChildren | Fix the tooltip on disabled children | boolean | No | `false` |

**PS**

- `fixTooltipOnDisabledChildren` is only effective on Zent Components.

#### None

Expand Down
5 changes: 5 additions & 0 deletions packages/zent/src/pop/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ group: 反馈
| mouseEnterDelay | hover打开的延迟(单位:毫秒) | number | 否 | `200` |
| mouseLeaveDelay | 关闭的的延迟(单位:毫秒) | number | 否 | `200` |
| anchorOnly | 仅考虑 Trigger 作为触发区域 | boolean | 否 | `false` |
| fixTooltipOnDisabledChildren | 被禁用的子节点展示 Tooltip | boolean | 否 | `false` |

**注意**

- `fixTooltipOnDisabledChildren` 仅对 Zent 组件有效。

#### None

Expand Down
54 changes: 54 additions & 0 deletions packages/zent/src/pop/demos/disabled.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
order: 9
zh-CN:
title: 禁用表单元素
content: 内容
en-US:
title: Disabled form inputs
content: content
---

```jsx
import { Button, Pop, Radio, Checkbox, Input } from 'zent';

ReactDOM.render(
<div className="zent-doc-pop-container">
<Pop
centerArrow
trigger="hover"
position="top-left"
content="{i18n.content}"
>
<Radio disabled></Radio>
</Pop>
<Pop
centerArrow
trigger="hover"
position="top-left"
content="{i18n.content}"
>
<Checkbox disabled></Checkbox>
</Pop>
<Pop
centerArrow
trigger="hover"
position="top-left"
content="{i18n.content}"
>
<Input disabled style={{ marginRight: '12px' }} />
</Pop>
<Pop
centerArrow
trigger="hover"
position="top-left"
content="{i18n.content}"
fixTooltipOnDisabledChildren
>
<Button type="primary" disabled>
Button
</Button>
</Pop>
</div>,
mountNode
);
```
30 changes: 26 additions & 4 deletions packages/zent/src/popover/trigger/HoverTrigger.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
import { cloneElement, useContext, useEffect, useMemo, useRef } from 'react';
import {
cloneElement,
createContext,
useContext,
useEffect,
useMemo,
useRef,
} from 'react';
import { Subject, Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import Context from '../Context';
import Anchor, { PopoverAnchorGetElementFn } from '../Anchor';
import { addEventListener } from '../../utils/component/event-handler';
import { isElement } from 'react-is';

interface IHoverTriggerCompatibleProps {
fixTooltipOnDisabledChildren?: boolean;
}

export interface IPopoverHoverTriggerContext
extends IHoverTriggerCompatibleProps {}
export const PopoverHoverTriggerContext = createContext<IPopoverHoverTriggerContext>(
{}
);
export interface IPopoverHoverTriggerChildProps {
onMouseEnter?: (...args: any[]) => void;
onMouseLeave?: (...args: any[]) => void;
}

export interface IPopoverHoverTriggerProps<
ChildProps extends IPopoverHoverTriggerChildProps
> {
> extends IHoverTriggerCompatibleProps {
hideDelay?: number;
showDelay?: number;
anchorOnly?: boolean;
Expand Down Expand Up @@ -66,7 +82,7 @@ export function PopoverHoverTrigger<
return () => $.unsubscribe();
}, [ctx.popover, visible$]);

const { children } = props;
const { children, fixTooltipOnDisabledChildren } = props;
const { portalRef, didMount } = ctx;

didMount(() => {
Expand Down Expand Up @@ -129,7 +145,13 @@ export function PopoverHoverTrigger<
</span>
);
}
return <Anchor getElement={props.getElement}>{child}</Anchor>;
return (
<PopoverHoverTriggerContext.Provider
value={{ fixTooltipOnDisabledChildren }}
>
<Anchor getElement={props.getElement}>{child}</Anchor>
</PopoverHoverTriggerContext.Provider>
);
}

export default PopoverHoverTrigger;
Loading