mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
panel-header: Updates for the new react-popper api and make it possible to hover the tooltip popper without it closing
This commit is contained in:
parent
6cd5bca9ab
commit
af859b2f4d
@ -1,34 +1,19 @@
|
|||||||
import React from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import withTooltip from './withTooltip';
|
import Popper from './Popper';
|
||||||
import { Target } from 'react-popper';
|
import withPopper, { UsingPopperProps } from './withPopper';
|
||||||
|
|
||||||
interface PopoverProps {
|
|
||||||
tooltipSetState: (prevState: object) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Popover extends React.Component<PopoverProps, any> {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.toggleTooltip = this.toggleTooltip.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleTooltip() {
|
|
||||||
const { tooltipSetState } = this.props;
|
|
||||||
tooltipSetState(prevState => {
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
show: !prevState.show,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
class Popover extends PureComponent<UsingPopperProps> {
|
||||||
render() {
|
render() {
|
||||||
|
const { children, hidePopper, showPopper, className, ...restProps } = this.props;
|
||||||
|
|
||||||
|
const togglePopper = restProps.show === true ? hidePopper : showPopper;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Target className="popper__target" onClick={this.toggleTooltip}>
|
<div className={`popper__manager ${className}`} onClick={togglePopper}>
|
||||||
{this.props.children}
|
<Popper {...restProps}>{children}</Popper>
|
||||||
</Target>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTooltip(Popover);
|
export default withPopper(Popover);
|
||||||
|
42
public/app/core/components/Tooltip/Popper.tsx
Normal file
42
public/app/core/components/Tooltip/Popper.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { Manager, Popper as ReactPopper, Reference } from 'react-popper';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
renderContent: (content: any) => any;
|
||||||
|
show: boolean;
|
||||||
|
placement?: any;
|
||||||
|
content: string | ((props: any) => JSX.Element);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Popper extends PureComponent<Props> {
|
||||||
|
render() {
|
||||||
|
const { children, renderContent, show, placement } = this.props;
|
||||||
|
const { content } = this.props;
|
||||||
|
const modifiers = {
|
||||||
|
flip: { enabled: false },
|
||||||
|
preventOverflow: { enabled: false },
|
||||||
|
hide: { enabled: false },
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Manager>
|
||||||
|
<Reference>{({ ref }) => <div ref={ref}>{children}</div>}</Reference>
|
||||||
|
{show && (
|
||||||
|
<ReactPopper placement={placement} modifiers={modifiers}>
|
||||||
|
{({ ref, style, placement, arrowProps }) => {
|
||||||
|
return (
|
||||||
|
<div ref={ref} style={style} data-placement={placement} className="popper">
|
||||||
|
<div className="popper__background">
|
||||||
|
{renderContent(content)}
|
||||||
|
<div ref={arrowProps.ref} data-placement={placement} className="popper__arrow" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ReactPopper>
|
||||||
|
)}
|
||||||
|
</Manager>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Popper;
|
@ -1,36 +1,17 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import withTooltip from './withTooltip';
|
import Popper from './Popper';
|
||||||
import { Target } from 'react-popper';
|
import withPopper, { UsingPopperProps } from './withPopper';
|
||||||
|
|
||||||
interface Props {
|
|
||||||
tooltipSetState: (prevState: object) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Tooltip extends PureComponent<Props> {
|
|
||||||
showTooltip = () => {
|
|
||||||
const { tooltipSetState } = this.props;
|
|
||||||
|
|
||||||
tooltipSetState(prevState => ({
|
|
||||||
...prevState,
|
|
||||||
show: true,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
hideTooltip = () => {
|
|
||||||
const { tooltipSetState } = this.props;
|
|
||||||
tooltipSetState(prevState => ({
|
|
||||||
...prevState,
|
|
||||||
show: false,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
class Tooltip extends PureComponent<UsingPopperProps> {
|
||||||
render() {
|
render() {
|
||||||
|
const { children, hidePopper, showPopper, className, ...restProps } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Target className="popper__target" onMouseOver={this.showTooltip} onMouseOut={this.hideTooltip}>
|
<div className={`popper__manager ${className}`} onMouseEnter={showPopper} onMouseLeave={hidePopper}>
|
||||||
{this.props.children}
|
<Popper {...restProps}>{children}</Popper>
|
||||||
</Target>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTooltip(Tooltip);
|
export default withPopper(Tooltip);
|
||||||
|
@ -3,11 +3,9 @@
|
|||||||
exports[`Popover renders correctly 1`] = `
|
exports[`Popover renders correctly 1`] = `
|
||||||
<div
|
<div
|
||||||
className="popper__manager test-class"
|
className="popper__manager test-class"
|
||||||
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
<div
|
<div>
|
||||||
className="popper__target"
|
|
||||||
onClick={[Function]}
|
|
||||||
>
|
|
||||||
<button>
|
<button>
|
||||||
Button with Popover
|
Button with Popover
|
||||||
</button>
|
</button>
|
||||||
|
@ -3,12 +3,10 @@
|
|||||||
exports[`Tooltip renders correctly 1`] = `
|
exports[`Tooltip renders correctly 1`] = `
|
||||||
<div
|
<div
|
||||||
className="popper__manager test-class"
|
className="popper__manager test-class"
|
||||||
|
onMouseEnter={[Function]}
|
||||||
|
onMouseLeave={[Function]}
|
||||||
>
|
>
|
||||||
<div
|
<div>
|
||||||
className="popper__target"
|
|
||||||
onMouseOut={[Function]}
|
|
||||||
onMouseOver={[Function]}
|
|
||||||
>
|
|
||||||
<a
|
<a
|
||||||
href="http://www.grafana.com"
|
href="http://www.grafana.com"
|
||||||
>
|
>
|
||||||
|
83
public/app/core/components/Tooltip/withPopper.tsx
Normal file
83
public/app/core/components/Tooltip/withPopper.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export interface UsingPopperProps {
|
||||||
|
showPopper: (prevState: object) => void;
|
||||||
|
hidePopper: (prevState: object) => void;
|
||||||
|
renderContent: (content: any) => any;
|
||||||
|
show: boolean;
|
||||||
|
placement?: string;
|
||||||
|
content: string | ((props: any) => JSX.Element);
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
placement?: string;
|
||||||
|
className?: string;
|
||||||
|
content: string | ((props: any) => JSX.Element);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
placement: string;
|
||||||
|
show: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function withPopper(WrappedComponent) {
|
||||||
|
return class extends React.Component<Props, State> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.setState = this.setState.bind(this);
|
||||||
|
this.state = {
|
||||||
|
placement: this.props.placement || 'auto',
|
||||||
|
show: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.placement && nextProps.placement !== this.state.placement) {
|
||||||
|
this.setState(prevState => {
|
||||||
|
return {
|
||||||
|
...prevState,
|
||||||
|
placement: nextProps.placement,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showPopper = () => {
|
||||||
|
this.setState(prevState => ({
|
||||||
|
...prevState,
|
||||||
|
show: true,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
hidePopper = () => {
|
||||||
|
this.setState(prevState => ({
|
||||||
|
...prevState,
|
||||||
|
show: false,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
renderContent(content) {
|
||||||
|
if (typeof content === 'function') {
|
||||||
|
// If it's a function we assume it's a React component
|
||||||
|
const ReactComponent = content;
|
||||||
|
return <ReactComponent />;
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { show, placement } = this.state;
|
||||||
|
return (
|
||||||
|
<WrappedComponent
|
||||||
|
{...this.props}
|
||||||
|
showPopper={this.showPopper}
|
||||||
|
hidePopper={this.hidePopper}
|
||||||
|
renderContent={this.renderContent}
|
||||||
|
show={show}
|
||||||
|
placement={placement}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,58 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Manager, Popper, Arrow } from 'react-popper';
|
|
||||||
|
|
||||||
interface IwithTooltipProps {
|
|
||||||
placement?: string;
|
|
||||||
content: string | ((props: any) => JSX.Element);
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function withTooltip(WrappedComponent) {
|
|
||||||
return class extends React.Component<IwithTooltipProps, any> {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.setState = this.setState.bind(this);
|
|
||||||
this.state = {
|
|
||||||
placement: this.props.placement || 'auto',
|
|
||||||
show: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
if (nextProps.placement && nextProps.placement !== this.state.placement) {
|
|
||||||
this.setState(prevState => {
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
placement: nextProps.placement,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderContent(content) {
|
|
||||||
if (typeof content === 'function') {
|
|
||||||
// If it's a function we assume it's a React component
|
|
||||||
const ReactComponent = content;
|
|
||||||
return <ReactComponent />;
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { content, className } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Manager className={`popper__manager ${className || ''}`}>
|
|
||||||
<WrappedComponent {...this.props} tooltipSetState={this.setState} />
|
|
||||||
{this.state.show ? (
|
|
||||||
<Popper placement={this.state.placement} className="popper">
|
|
||||||
{this.renderContent(content)}
|
|
||||||
<Arrow className="popper__arrow" />
|
|
||||||
</Popper>
|
|
||||||
) : null}
|
|
||||||
</Manager>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,12 +1,8 @@
|
|||||||
.popper {
|
.popper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: $zindex-tooltip;
|
z-index: $zindex-tooltip;
|
||||||
background: $tooltipBackground;
|
|
||||||
color: $tooltipColor;
|
color: $tooltipColor;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
border-radius: 3px;
|
|
||||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
|
||||||
padding: 10px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,10 +31,18 @@
|
|||||||
left: calc(50% - 5px);
|
left: calc(50% - 5px);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
padding-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popper[data-placement^='bottom'] {
|
.popper[data-placement^='bottom'] {
|
||||||
margin-top: 5px;
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popper__background {
|
||||||
|
background: $tooltipBackground;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popper[data-placement^='bottom'] .popper__arrow {
|
.popper[data-placement^='bottom'] .popper__arrow {
|
||||||
@ -46,21 +50,21 @@
|
|||||||
border-left-color: transparent;
|
border-left-color: transparent;
|
||||||
border-right-color: transparent;
|
border-right-color: transparent;
|
||||||
border-top-color: transparent;
|
border-top-color: transparent;
|
||||||
top: -5px;
|
top: 0;
|
||||||
left: calc(50% - 5px);
|
left: calc(50% - 8px);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
.popper[data-placement^='right'] {
|
.popper[data-placement^='right'] {
|
||||||
margin-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
.popper[data-placement^='right'] .popper__arrow {
|
.popper[data-placement^='right'] .popper__arrow {
|
||||||
border-width: 5px 5px 5px 0;
|
border-width: 5px 5px 5px 0;
|
||||||
border-left-color: transparent;
|
border-left-color: transparent;
|
||||||
border-top-color: transparent;
|
border-top-color: transparent;
|
||||||
border-bottom-color: transparent;
|
border-bottom-color: transparent;
|
||||||
left: -5px;
|
left: 0;
|
||||||
top: calc(50% - 5px);
|
top: calc(50% - 8px);
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user