mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
wip
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@torkelo/react-select": "2.1.1",
|
||||
"@types/react-test-renderer": "^16.0.3",
|
||||
"@types/react-transition-group": "^2.0.15",
|
||||
"classnames": "^2.2.5",
|
||||
"jquery": "^3.2.1",
|
||||
"lodash": "^4.17.10",
|
||||
@@ -23,11 +25,11 @@
|
||||
"react-virtualized": "^9.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/classnames": "^2.2.6",
|
||||
"@types/jest": "^23.3.2",
|
||||
"@types/jquery": "^1.10.35",
|
||||
"@types/lodash": "^4.14.119",
|
||||
"@types/react": "^16.7.6",
|
||||
"@types/classnames": "^2.2.6",
|
||||
"@types/jquery": "^1.10.35",
|
||||
"typescript": "^3.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
69
packages/grafana-ui/src/components/Tooltip/Popper.tsx
Normal file
69
packages/grafana-ui/src/components/Tooltip/Popper.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import * as PopperJS from 'popper.js';
|
||||
import { Manager, Popper as ReactPopper } from 'react-popper';
|
||||
import Portal from 'app/core/components/Portal/Portal';
|
||||
import Transition from 'react-transition-group/Transition';
|
||||
|
||||
const defaultTransitionStyles = {
|
||||
transition: 'opacity 200ms linear',
|
||||
opacity: 0,
|
||||
};
|
||||
|
||||
const transitionStyles = {
|
||||
exited: { opacity: 0 },
|
||||
entering: { opacity: 0 },
|
||||
entered: { opacity: 1 },
|
||||
exiting: { opacity: 0 },
|
||||
};
|
||||
|
||||
interface Props extends React.DOMAttributes<HTMLDivElement> {
|
||||
renderContent: (content: any) => any;
|
||||
show: boolean;
|
||||
placement?: PopperJS.Placement;
|
||||
content: string | ((props: any) => JSX.Element);
|
||||
refClassName?: string;
|
||||
referenceElement: PopperJS.ReferenceObject;
|
||||
}
|
||||
|
||||
class Popper extends PureComponent<Props> {
|
||||
render() {
|
||||
const { renderContent, show, placement, onMouseEnter, onMouseLeave } = this.props;
|
||||
const { content } = this.props;
|
||||
|
||||
return (
|
||||
<Manager>
|
||||
<Transition in={show} timeout={100} mountOnEnter={true} unmountOnExit={true}>
|
||||
{transitionState => (
|
||||
<Portal>
|
||||
<ReactPopper placement={placement} referenceElement={this.props.referenceElement}>
|
||||
{({ ref, style, placement, arrowProps }) => {
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
ref={ref}
|
||||
style={{
|
||||
...style,
|
||||
...defaultTransitionStyles,
|
||||
...transitionStyles[transitionState],
|
||||
}}
|
||||
data-placement={placement}
|
||||
className="popper"
|
||||
>
|
||||
<div className="popper__background">
|
||||
{renderContent(content)}
|
||||
<div ref={arrowProps.ref} data-placement={placement} className="popper__arrow" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</ReactPopper>
|
||||
</Portal>
|
||||
)}
|
||||
</Transition>
|
||||
</Manager>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Popper;
|
||||
@@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import * as PopperJS from 'popper.js';
|
||||
|
||||
type PopperContent = string | (() => JSX.Element);
|
||||
|
||||
export interface UsingPopperProps {
|
||||
show?: boolean;
|
||||
placement?: PopperJS.Placement;
|
||||
content: PopperContent;
|
||||
children: JSX.Element;
|
||||
renderContent?: (content: PopperContent) => JSX.Element;
|
||||
}
|
||||
|
||||
type PopperControllerRenderProp = (
|
||||
showPopper: () => void,
|
||||
hidePopper: () => void,
|
||||
popperProps: {
|
||||
show: boolean;
|
||||
placement: PopperJS.Placement;
|
||||
content: string | ((props: any) => JSX.Element);
|
||||
renderContent: (content: any) => any;
|
||||
}
|
||||
) => JSX.Element;
|
||||
|
||||
interface Props {
|
||||
placement?: PopperJS.Placement;
|
||||
content: PopperContent;
|
||||
className?: string;
|
||||
children: PopperControllerRenderProp;
|
||||
}
|
||||
|
||||
interface State {
|
||||
placement: PopperJS.Placement;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
class PopperController extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
placement: this.props.placement || 'auto',
|
||||
show: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
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: PopperContent) {
|
||||
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 { children, content } = this.props;
|
||||
const { show, placement } = this.state;
|
||||
|
||||
return children(this.showPopper, this.hidePopper, {
|
||||
show,
|
||||
placement,
|
||||
content,
|
||||
renderContent: this.renderContent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default PopperController;
|
||||
18
packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx
Normal file
18
packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { Tooltip } from './Tooltip';
|
||||
|
||||
describe('Tooltip', () => {
|
||||
it('renders correctly', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Tooltip placement="auto" content="Tooltip text">
|
||||
<a className="test-class" href="http://www.grafana.com">
|
||||
Link with tooltip
|
||||
</a>
|
||||
</Tooltip>
|
||||
)
|
||||
.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
32
packages/grafana-ui/src/components/Tooltip/Tooltip.tsx
Normal file
32
packages/grafana-ui/src/components/Tooltip/Tooltip.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React, { createRef } from 'react';
|
||||
import * as PopperJS from 'popper.js';
|
||||
import Popper from './Popper';
|
||||
import PopperController, { UsingPopperProps } from './PopperController';
|
||||
|
||||
export const Tooltip = ({ children, renderContent, ...controllerProps }: UsingPopperProps) => {
|
||||
const tooltipTriggerRef = createRef<PopperJS.ReferenceObject>();
|
||||
|
||||
return (
|
||||
<PopperController {...controllerProps}>
|
||||
{(showPopper, hidePopper, popperProps) => {
|
||||
return (
|
||||
<>
|
||||
{tooltipTriggerRef.current && (
|
||||
<Popper
|
||||
{...popperProps}
|
||||
onMouseEnter={showPopper}
|
||||
onMouseLeave={hidePopper}
|
||||
referenceElement={tooltipTriggerRef.current}
|
||||
/>
|
||||
)}
|
||||
{React.cloneElement(children, {
|
||||
ref: tooltipTriggerRef,
|
||||
onMouseEnter: showPopper,
|
||||
onMouseLeave: hidePopper,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</PopperController>
|
||||
);
|
||||
};
|
||||
114
packages/grafana-ui/src/components/Tooltip/_Tooltip.scss
Normal file
114
packages/grafana-ui/src/components/Tooltip/_Tooltip.scss
Normal file
@@ -0,0 +1,114 @@
|
||||
$popper-margin-from-ref: 5px;
|
||||
|
||||
.popper {
|
||||
position: absolute;
|
||||
z-index: $zindex-tooltip;
|
||||
color: $tooltipColor;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.popper .popper__arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
position: absolute;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.popper .popper__arrow {
|
||||
border-color: $tooltipBackground;
|
||||
}
|
||||
|
||||
.popper__background {
|
||||
background: $tooltipBackground;
|
||||
border-radius: $border-radius;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
// Top
|
||||
.popper[data-placement^='top'] {
|
||||
padding-bottom: $popper-margin-from-ref;
|
||||
}
|
||||
.popper[data-placement^='top'] .popper__arrow {
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
bottom: -5px;
|
||||
left: calc(50% - 5px);
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
// Bottom
|
||||
.popper[data-placement^='bottom'] {
|
||||
padding-top: $popper-margin-from-ref;
|
||||
}
|
||||
.popper[data-placement^='bottom'] .popper__arrow {
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-top-color: transparent;
|
||||
top: 0;
|
||||
left: calc(50% - 5px);
|
||||
}
|
||||
|
||||
.popper[data-placement^='bottom-start'] {
|
||||
padding-top: $popper-margin-from-ref;
|
||||
}
|
||||
.popper[data-placement^='bottom-start'] .popper__arrow {
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-top-color: transparent;
|
||||
top: 0;
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
.popper[data-placement^='bottom-end'] {
|
||||
padding-top: $popper-margin-from-ref;
|
||||
}
|
||||
.popper[data-placement^='bottom-end'] .popper__arrow {
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-top-color: transparent;
|
||||
top: 0;
|
||||
left: calc(100% - 5px);
|
||||
}
|
||||
|
||||
// Right
|
||||
.popper[data-placement^='right'] {
|
||||
padding-left: $popper-margin-from-ref;
|
||||
}
|
||||
.popper[data-placement^='right'] .popper__arrow {
|
||||
border-width: 5px 5px 5px 0;
|
||||
border-left-color: transparent;
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
left: 0;
|
||||
top: calc(50% - 5px);
|
||||
}
|
||||
|
||||
// Left
|
||||
.popper[data-placement^='left'] {
|
||||
padding-right: $popper-margin-from-ref;
|
||||
}
|
||||
.popper[data-placement^='left'] .popper__arrow {
|
||||
border-width: 5px 0 5px 5px;
|
||||
border-top-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
right: -5px;
|
||||
top: calc(50% - 5px);
|
||||
}
|
||||
|
||||
.popper__target,
|
||||
.popper__manager {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.popper__manager--block {
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Tooltip renders correctly 1`] = `
|
||||
<a
|
||||
className="test-class"
|
||||
href="http://www.grafana.com"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
Link with tooltip
|
||||
</a>
|
||||
`;
|
||||
@@ -1 +1,2 @@
|
||||
@import 'DeleteButton/DeleteButton';
|
||||
@import 'Tooltip/Tooltip';
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export { DeleteButton } from './DeleteButton/DeleteButton';
|
||||
export { Tooltip } from './Tooltip/Tooltip';
|
||||
|
||||
Reference in New Issue
Block a user