1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

tooltip refactoring -- part 1

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-07-28 16:57:24 +03:00
parent 4087cf025e
commit f2803abcb6
13 changed files with 228 additions and 272 deletions

View File

@ -2,18 +2,21 @@
--size: 37px; --size: 37px;
position: relative; position: relative;
opacity: .55;
border-radius: $radius; border-radius: $radius;
padding: $radius; padding: $radius;
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
&.active, &.interactive:hover { &.active, &.interactive:hover {
opacity: 1;
background-color: #fff; background-color: #fff;
img {
opacity: 1;
}
} }
> img { img {
opacity: .55;
width: var(--size); width: var(--size);
height: var(--size); height: var(--size);
} }

View File

@ -7,7 +7,7 @@ import { Hashicon } from "@emeraldpay/hashicon-react";
import { Cluster } from "../../../main/cluster"; import { Cluster } from "../../../main/cluster";
import { cssNames, IClassName } from "../../utils"; import { cssNames, IClassName } from "../../utils";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { Tooltip, TooltipContent } from "../tooltip"; import { Tooltip } from "../tooltip";
interface Props extends DOMAttributes<HTMLElement> { interface Props extends DOMAttributes<HTMLElement> {
cluster: Cluster; cluster: Cluster;
@ -44,9 +44,7 @@ export class ClusterIcon extends React.Component<Props> {
return ( return (
<div {...elemProps} className={className} id={clusterIconId}> <div {...elemProps} className={className} id={clusterIconId}>
{showTooltip && ( {showTooltip && (
<Tooltip htmlFor={clusterIconId} position={{ right: true }}> <Tooltip targetId={clusterIconId}>{clusterName}</Tooltip>
<TooltipContent nowrap>{clusterName}</TooltipContent>
</Tooltip>
)} )}
{icon && <img src={icon} alt={clusterName}/>} {icon && <img src={icon} alt={clusterName}/>}
{!icon && <Hashicon value={clusterName} options={options}/>} {!icon && <Hashicon value={clusterName} options={options}/>}

View File

@ -1,13 +1,13 @@
import "./events.scss"; import "./events.scss";
import React from "react"; import React, { Fragment } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { MainLayout } from "../layout/main-layout"; import { MainLayout } from "../layout/main-layout";
import { eventStore } from "./event.store"; import { eventStore } from "./event.store";
import { KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object"; import { KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object";
import { Trans } from "@lingui/macro"; import { Trans } from "@lingui/macro";
import { KubeEvent } from "../../api/endpoints/events.api"; import { KubeEvent } from "../../api/endpoints/events.api";
import { Tooltip, TooltipContent } from "../tooltip"; import { Tooltip } from "../tooltip";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { cssNames, IClassName, stopPropagation } from "../../utils"; import { cssNames, IClassName, stopPropagation } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
@ -90,18 +90,14 @@ export class Events extends React.Component<Props> {
const detailsUrl = getDetailsUrl(lookupApiLink(involvedObject, event)); const detailsUrl = getDetailsUrl(lookupApiLink(involvedObject, event));
return [ return [
{ {
className: { className: { warning: isWarning },
warning: isWarning
},
title: ( title: (
<> <Fragment>
<span id={tooltipId}>{message}</span> <span id={tooltipId}>{message}</span>
<Tooltip htmlFor={tooltipId} following> <Tooltip targetId={tooltipId} formatters={{ narrow: true, warning: isWarning }}>
<TooltipContent narrow warning={isWarning}>
{message} {message}
</TooltipContent>
</Tooltip> </Tooltip>
</> </Fragment>
) )
}, },
event.getNs(), event.getNs(),

View File

@ -2,7 +2,6 @@ import "./kube-event-icon.scss";
import React from "react"; import React from "react";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { TooltipContent } from "../tooltip";
import { KubeObject } from "../../api/kube-object"; import { KubeObject } from "../../api/kube-object";
import { eventStore } from "./event.store"; import { eventStore } from "./event.store";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
@ -34,15 +33,17 @@ export class KubeEventIcon extends React.Component<Props> {
<Icon <Icon
material="warning" material="warning"
className={cssNames("KubeEventIcon", { warning: event.isWarning() })} className={cssNames("KubeEventIcon", { warning: event.isWarning() })}
tooltip={( tooltip={{
<TooltipContent className="KubeEventTooltip"> children: (
{event.message} <div className="KubeEventTooltip">
<div className="msg">{event.message}</div>
<div className="age"> <div className="age">
<Icon material="access_time"/> <Icon material="access_time"/>
{event.getAge(undefined, undefined, true)} {event.getAge(undefined, undefined, true)}
</div> </div>
</TooltipContent> </div>
)} )
}}
/> />
) )
} }

View File

@ -7,7 +7,6 @@ import { disposeOnUnmount, observer } from "mobx-react";
import { Trans } from "@lingui/macro"; import { Trans } from "@lingui/macro";
import { DrawerItem, DrawerItemLabels } from "../drawer"; import { DrawerItem, DrawerItemLabels } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { TooltipContent } from "../tooltip";
import { nodesStore } from "./nodes.store"; import { nodesStore } from "./nodes.store";
import { ResourceMetrics } from "../resource-metrics"; import { ResourceMetrics } from "../resource-metrics";
import { podsStore } from "../+workloads-pods/pods.store"; import { podsStore } from "../+workloads-pods/pods.store";
@ -127,16 +126,17 @@ export class NodeDetails extends React.Component<Props> {
key={type} key={type}
label={type} label={type}
className={kebabCase(type)} className={kebabCase(type)}
tooltip={ tooltip={{
<TooltipContent tableView> formatters: {
{Object.entries(condition).map(([key, value]) => tableView: true,
},
children: Object.entries(condition).map(([key, value]) =>
<div key={key} className="flex gaps align-center"> <div key={key} className="flex gaps align-center">
<div className="name">{upperFirst(key)}</div> <div className="name">{upperFirst(key)}</div>
<div className="value">{value}</div> <div className="value">{value}</div>
</div> </div>
)} )
</TooltipContent> }}
}
/> />
) )
}) })

View File

@ -15,7 +15,7 @@ import { NodeMenu } from "./node-menu";
import { LineProgress } from "../line-progress"; import { LineProgress } from "../line-progress";
import { _i18n } from "../../i18n"; import { _i18n } from "../../i18n";
import { bytesToUnits } from "../../utils/convertMemory"; import { bytesToUnits } from "../../utils/convertMemory";
import { Tooltip, TooltipContent } from "../tooltip"; import { Tooltip } from "../tooltip";
import kebabCase from "lodash/kebabCase"; import kebabCase from "lodash/kebabCase";
import upperFirst from "lodash/upperFirst"; import upperFirst from "lodash/upperFirst";
import { apiManager } from "../../api/api-manager"; import { apiManager } from "../../api/api-manager";
@ -101,15 +101,13 @@ export class Nodes extends React.Component<Props> {
return ( return (
<div key={type} id={tooltipId} className={cssNames("condition", kebabCase(type))}> <div key={type} id={tooltipId} className={cssNames("condition", kebabCase(type))}>
{type} {type}
<Tooltip htmlFor={tooltipId} following> <Tooltip targetId={tooltipId} formatters={{ tableView: true }}>
<TooltipContent tableView>
{Object.entries(condition).map(([key, value]) => {Object.entries(condition).map(([key, value]) =>
<div key={key} className="flex gaps align-center"> <div key={key} className="flex gaps align-center">
<div className="name">{upperFirst(key)}</div> <div className="name">{upperFirst(key)}</div>
<div className="value">{value}</div> <div className="value">{value}</div>
</div> </div>
)} )}
</TooltipContent>
</Tooltip> </Tooltip>
</div>) </div>)
}) })
@ -162,7 +160,7 @@ export class Nodes extends React.Component<Props> {
this.renderDiskUsage(node), this.renderDiskUsage(node),
<> <>
<span id={tooltipId}>{node.getTaints().length}</span> <span id={tooltipId}>{node.getTaints().length}</span>
<Tooltip htmlFor={tooltipId} style={{ whiteSpace: "pre-line" }}> <Tooltip targetId={tooltipId} style={{ whiteSpace: "pre-line" }}>
{node.getTaints().map(({ key, effect }) => `${key}: ${effect}`).join("\n")} {node.getTaints().map(({ key, effect }) => `${key}: ${effect}`).join("\n")}
</Tooltip> </Tooltip>
</>, </>,

View File

@ -14,7 +14,6 @@ import { Pod, podsApi } from "../../api/endpoints";
import { PodMenu } from "./pod-menu"; import { PodMenu } from "./pod-menu";
import { StatusBrick } from "../status-brick"; import { StatusBrick } from "../status-brick";
import { cssNames, stopPropagation } from "../../utils"; import { cssNames, stopPropagation } from "../../utils";
import { TooltipContent } from "../tooltip";
import { KubeEventIcon } from "../+events/kube-event-icon"; import { KubeEventIcon } from "../+events/kube-event-icon";
import { getDetailsUrl } from "../../navigation"; import { getDetailsUrl } from "../../navigation";
import toPairs from "lodash/toPairs"; import toPairs from "lodash/toPairs";
@ -42,9 +41,15 @@ export class Pods extends React.Component<Props> {
renderContainersStatus(pod: Pod) { renderContainersStatus(pod: Pod) {
return pod.getContainerStatuses().map(containerStatus => { return pod.getContainerStatuses().map(containerStatus => {
const { name, state, ready } = containerStatus; const { name, state, ready } = containerStatus;
const tooltip = ( return (
<TooltipContent tableView> <Fragment key={name}>
{Object.keys(state).map(status => ( <StatusBrick
className={cssNames(state, { ready })}
tooltip={{
formatters: {
tableView: true
},
children: Object.keys(state).map(status => (
<Fragment key={status}> <Fragment key={status}>
<div className="title"> <div className="title">
{name} <span className="text-secondary">({status}{ready ? ", ready" : ""})</span> {name} <span className="text-secondary">({status}{ready ? ", ready" : ""})</span>
@ -56,12 +61,9 @@ export class Pods extends React.Component<Props> {
</div> </div>
))} ))}
</Fragment> </Fragment>
))} ))
</TooltipContent> }}
); />
return (
<Fragment key={name}>
<StatusBrick className={cssNames(state, { ready })} tooltip={tooltip}/>
</Fragment> </Fragment>
) )
}); });

View File

@ -9,7 +9,6 @@ export type AnimateName = "opacity" | "slide-right" | "opacity-scale" | string;
export interface AnimateProps { export interface AnimateProps {
name?: AnimateName; // predefined names in css name?: AnimateName; // predefined names in css
enter?: boolean; enter?: boolean;
enabled?: boolean;
onEnter?: () => void; onEnter?: () => void;
onLeave?: () => void; onLeave?: () => void;
} }
@ -21,7 +20,6 @@ export class Animate extends React.Component<AnimateProps> {
static defaultProps: AnimateProps = { static defaultProps: AnimateProps = {
name: "opacity", name: "opacity",
enter: true, enter: true,
enabled: true,
onEnter: noop, onEnter: noop,
onLeave: noop, onLeave: noop,
}; };
@ -80,10 +78,7 @@ export class Animate extends React.Component<AnimateProps> {
} }
render() { render() {
const { name, enabled, children } = this.props; const { name } = this.props;
if (!enabled) {
return children;
}
const contentElem = this.contentElem; const contentElem = this.contentElem;
return React.cloneElement(contentElem, { return React.cloneElement(contentElem, {
className: cssNames("Animate", name, contentElem.props.className, this.statusClassName), className: cssNames("Animate", name, contentElem.props.className, this.statusClassName),

View File

@ -17,7 +17,7 @@ import { navigate, navigation } from "../../navigation";
import { addClusterURL } from "../+add-cluster"; import { addClusterURL } from "../+add-cluster";
import { clusterSettingsURL } from "../+cluster-settings"; import { clusterSettingsURL } from "../+cluster-settings";
import { landingURL } from "../+landing-page"; import { landingURL } from "../+landing-page";
import { Tooltip, TooltipContent } from "../tooltip"; import { Tooltip } from "../tooltip";
import { ConfirmDialog } from "../confirm-dialog"; import { ConfirmDialog } from "../confirm-dialog";
import { clusterIpc } from "../../../common/cluster-ipc"; import { clusterIpc } from "../../../common/cluster-ipc";
import { clusterStatusURL } from "./cluster-status.route"; import { clusterStatusURL } from "./cluster-status.route";
@ -118,10 +118,8 @@ export class ClustersMenu extends React.Component<Props> {
})} })}
</div> </div>
<div className="add-cluster" onClick={this.addCluster}> <div className="add-cluster" onClick={this.addCluster}>
<Tooltip htmlFor="add-cluster-icon" position={{ right: true }}> <Tooltip targetId="add-cluster-icon">
<TooltipContent nowrap>
<Trans>Add Cluster</Trans> <Trans>Add Cluster</Trans>
</TooltipContent>
</Tooltip> </Tooltip>
<Icon big material="add" id="add-cluster-icon"/> <Icon big material="add" id="add-cluster-icon"/>
{newContexts.size > 0 && ( {newContexts.size > 0 && (

View File

@ -28,7 +28,6 @@ import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resourc
import { CustomResources } from "../+custom-resources/custom-resources"; import { CustomResources } from "../+custom-resources/custom-resources";
import { navigation } from "../../navigation"; import { navigation } from "../../navigation";
import { isAllowedResource } from "../../api/rbac" import { isAllowedResource } from "../../api/rbac"
import { TooltipContent } from "../tooltip";
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false }); const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
type SidebarContextValue = { type SidebarContextValue = {
@ -83,17 +82,9 @@ export class Sidebar extends React.Component<Props> {
</NavLink> </NavLink>
<Icon <Icon
className="pin-icon" className="pin-icon"
tooltip={<Trans>Compact view</Trans>}
material={isPinned ? "keyboard_arrow_left" : "keyboard_arrow_right"} material={isPinned ? "keyboard_arrow_left" : "keyboard_arrow_right"}
onClick={toggle} onClick={toggle}
tooltip={{
following: false,
position: { right: true },
children: (
<TooltipContent nowrap>
<Trans>Compact view</Trans>
</TooltipContent>
)
}}
focusable={false} focusable={false}
/> />
</div> </div>

View File

@ -1,58 +1,32 @@
.Tooltip { .Tooltip {
--tooltip-bgc: #{$contentColor}; // use positioning relative to viewport (window)
--tooltip-color: #{$textColorSecondary}; // https://developer.mozilla.org/en-US/docs/Web/CSS/position
--tooltip-margin: #{$padding / 2 $padding / 3}; position: fixed;
margin: 0 !important;
position: absolute; background: $contentColor;
background: var(--tooltip-bgc);
font-size: small; font-size: small;
font-weight: normal; font-weight: normal;
border: 1px solid $borderColor; border: 1px solid $borderColor;
border-radius: $radius; border-radius: $radius;
color: var(--tooltip-color); color: $textColorSecondary;
margin: var(--tooltip-margin);
white-space: normal; white-space: normal;
padding: .5em; padding: .5em;
text-align: center; text-align: center;
transition-delay: 100ms; // delay for .Animate
pointer-events: none; pointer-events: none;
z-index: 1000; z-index: 1000;
transition: opacity 150ms 25ms ease-in-out;
* { &.hidden {
white-space: normal; opacity: 0;
visibility: hidden;
} }
&:empty { &:empty {
display: none; display: none;
} }
&.following { &.formatter {
position: fixed;
}
&:not(.following) {
margin: #{$margin};
&.left {
right: 100%;
}
&.right {
left: 100%;
}
&.top {
bottom: 100%;
}
&.bottom {
top: 100%;
}
}
}
.TooltipContent {
&.nowrap { &.nowrap {
&, * { &, * {
white-space: nowrap; white-space: nowrap;
@ -99,3 +73,4 @@
} }
} }
} }
}

View File

@ -2,109 +2,141 @@ import './tooltip.scss'
import React from "react" import React from "react"
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { autobind, cssNames, IClassName } from "../../utils";
import { observable } from "mobx"; import { observable } from "mobx";
import { createPortal } from "react-dom"
import { autobind, cssNames } from "../../utils";
import { Animate } from "../animate";
// fixme: refactor -- better positioning + remove "flying effect" export type TooltipPosition = "top" | "left" | "right" | "bottom";
// todo: always render outside of parent element ("overflow: auto" should not affect tooltip)
export interface TooltipProps { export interface TooltipProps {
htmlFor: string; targetId: string; // "id" of target html-element to bind
className?: string; visible?: boolean;
position?: Position; offset?: number; // px
useAnimation?: boolean; position?: TooltipPosition;
following?: boolean; // tooltip is following mouse position className?: IClassName;
formatters?: TooltipContentFormatters;
style?: React.CSSProperties; style?: React.CSSProperties;
children?: React.ReactNode; children?: React.ReactNode;
} }
interface Position { export interface TooltipContentFormatters {
left?: boolean; narrow?: boolean; // max-width
right?: boolean; warning?: boolean; // color
top?: boolean; small?: boolean; // font-size
bottom?: boolean; nowrap?: boolean; // white-space
tableView?: boolean;
} }
const defaultProps: Partial<TooltipProps> = { const defaultProps: Partial<TooltipProps> = {
useAnimation: true, offset: 10,
position: {
bottom: true,
} }
};
@observer @observer
export class Tooltip extends React.Component<TooltipProps> { export class Tooltip extends React.Component<TooltipProps> {
static defaultProps = defaultProps as object; static defaultProps = defaultProps as object;
public anchor: HTMLElement; @observable.ref elem: HTMLElement;
public elem: HTMLElement; @observable activePosition: TooltipPosition;
@observable isVisible = !!this.props.visible;
@observable isVisible = false; get targetElem(): HTMLElement {
return document.getElementById(this.props.targetId)
}
componentDidMount() { componentDidMount() {
const { htmlFor } = this.props; this.targetElem.addEventListener("mouseenter", this.onEnterTarget)
this.anchor = htmlFor ? document.getElementById(htmlFor) : this.elem.parentElement; this.targetElem.addEventListener("mouseleave", this.onLeaveTarget)
if (this.anchor) {
if (window.getComputedStyle(this.anchor).position === "static") {
this.anchor.style.position = "relative"
}
this.anchor.addEventListener("mouseenter", this.onMouseEnter);
this.anchor.addEventListener("mouseleave", this.onMouseLeave);
this.anchor.addEventListener("mousemove", this.onMouseMove);
}
} }
componentWillUnmount() { componentWillUnmount() {
if (this.anchor) { this.targetElem.removeEventListener("mouseenter", this.onEnterTarget)
this.anchor.removeEventListener("mouseenter", this.onMouseEnter); this.targetElem.removeEventListener("mouseleave", this.onLeaveTarget)
this.anchor.removeEventListener("mouseleave", this.onMouseLeave);
this.anchor.removeEventListener("mousemove", this.onMouseMove);
}
} }
@autobind() @autobind()
onMouseEnter(evt: MouseEvent) { protected onEnterTarget(evt: MouseEvent) {
this.isVisible = true; this.isVisible = true;
this.onMouseMove(evt); this.refreshPosition();
} }
@autobind() @autobind()
onMouseLeave(evt: MouseEvent) { protected onLeaveTarget(evt: MouseEvent) {
this.isVisible = false; this.isVisible = false;
} }
@autobind() @autobind()
onMouseMove(evt: MouseEvent) { refreshPosition() {
if (!this.props.following) { const { position } = this.props;
return; const { elem, targetElem } = this;
let allPositions: TooltipPosition[] = ["right", "bottom", "top", "left"];
if (allPositions.includes(position)) {
allPositions = [
position, // put first as priority side for positioning
...allPositions.filter(pos => pos !== position),
];
} }
const offsetX = -10; // reset position first and get all possible client-rect area for tooltip element
const offsetY = 10; this.setPosition({ left: 0, top: 0 });
const { clientX, clientY } = evt;
const { innerWidth, innerHeight } = window;
const initialPos: Partial<CSSStyleDeclaration> = { const selfBounds = elem.getBoundingClientRect();
top: "auto", const targetBounds = targetElem.getBoundingClientRect();
left: "auto", const { innerWidth: viewportWidth, innerHeight: viewportHeight } = window;
right: (innerWidth - clientX + offsetX) + "px",
bottom: (innerHeight - clientY + offsetY) + "px", // find proper position
this.activePosition = null;
for (const pos of allPositions) {
const { left, top, right, bottom } = this.getPosition(pos, selfBounds, targetBounds)
const fitsToWindow = left >= 0 && top >= 0 && right <= viewportWidth && bottom <= viewportHeight;
if (fitsToWindow) {
this.activePosition = pos;
this.setPosition({ top, left });
break;
}
}
if (!this.activePosition) {
const { left, top } = this.getPosition(allPositions[0], selfBounds, targetBounds)
this.activePosition = allPositions[0];
this.setPosition({ left, top });
}
} }
Object.assign(this.elem.style, initialPos); protected setPosition(pos: { left: number, top: number }) {
const elemStyle = this.elem.style;
elemStyle.left = pos.left + "px"
elemStyle.top = pos.top + "px"
}
// correct position if not fits to viewport protected getPosition(position: TooltipPosition, selfBounds: DOMRect, targetBounds: DOMRect) {
const { left, top } = this.elem.getBoundingClientRect(); let left: number
if (left < 0) { let top: number
this.elem.style.left = clientX + offsetX + "px"; const offset = this.props.offset;
this.elem.style.right = "auto" const horizontalCenter = targetBounds.left + (targetBounds.width - selfBounds.width) / 2;
} const verticalCenter = targetBounds.top + (targetBounds.height - selfBounds.height) / 2;
if (top < 0) { switch (position) {
this.elem.style.top = clientY + offsetY + "px"; case "top":
this.elem.style.bottom = "auto" left = horizontalCenter;
top = targetBounds.top - selfBounds.height - offset;
break;
case "bottom":
left = horizontalCenter;
top = targetBounds.bottom + offset;
break;
case "left":
top = verticalCenter;
left = targetBounds.left - selfBounds.width - offset;
break;
case "right":
top = verticalCenter;
left = targetBounds.right + offset;
break;
} }
return {
left,
top,
right: left + selfBounds.width,
bottom: top + selfBounds.height,
};
} }
@autobind() @autobind()
@ -113,40 +145,15 @@ export class Tooltip extends React.Component<TooltipProps> {
} }
render() { render() {
const { isVisible } = this; const { style, formatters, position, children } = this.props;
const { useAnimation, position, following, style, children } = this.props; const className = cssNames("Tooltip", this.props.className, formatters, this.activePosition, {
let { className } = this.props; hidden: !this.isVisible,
className = cssNames('Tooltip', position, { following }, className); formatter: !!formatters,
const tooltip = ( });
<Animate enter={isVisible} enabled={useAnimation}>
<div className={className} ref={this.bindRef} style={style}>
{children}
</div>
</Animate>
);
if (following) {
return createPortal(tooltip, document.body);
}
return tooltip;
}
}
interface TooltipContentProps {
className?: string;
narrow?: boolean; // max-width
warning?: boolean; // color
small?: boolean; // font-size
nowrap?: boolean; // white-space
tableView?: boolean;
}
export class TooltipContent extends React.Component<TooltipContentProps> {
render() {
const { className, children, ...modifiers } = this.props;
return ( return (
<div className={cssNames("TooltipContent", className, modifiers)}> <div className={className} style={style} ref={this.bindRef}>
{children} {children}
</div> </div>
) );
} }
} }

View File

@ -7,7 +7,7 @@ import { isReactNode } from "../../utils/isReactNode";
import uniqueId from "lodash/uniqueId" import uniqueId from "lodash/uniqueId"
export interface TooltipDecoratorProps { export interface TooltipDecoratorProps {
tooltip?: ReactNode | Omit<TooltipProps, "htmlFor">; tooltip?: ReactNode | Omit<TooltipProps, "targetId">;
} }
export function withTooltip<T extends React.ComponentType<any>>(Target: T): T { export function withTooltip<T extends React.ComponentType<any>>(Target: T): T {
@ -18,20 +18,12 @@ export function withTooltip<T extends React.ComponentType<any>>(Target: T): T {
render() { render() {
const { tooltip, ...targetProps } = this.props; const { tooltip, ...targetProps } = this.props;
if (tooltip) { if (tooltip) {
const tooltipId = targetProps.id || this.tooltipId; const tooltipId = targetProps.id || this.tooltipId;
const tooltipProps: TooltipProps = { const tooltipProps: TooltipProps = {
htmlFor: tooltipId, targetId: tooltipId,
following: true,
...(isReactNode(tooltip) ? { children: tooltip } : tooltip), ...(isReactNode(tooltip) ? { children: tooltip } : tooltip),
}; };
if (!tooltipProps.following) {
targetProps.style = {
position: "relative",
...(targetProps.style || {})
}
}
targetProps.id = tooltipId; targetProps.id = tooltipId;
targetProps.children = ( targetProps.children = (
<> <>