Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

ToolTip component #940

Closed
wants to merge 2 commits into from
Closed
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
27 changes: 25 additions & 2 deletions demo/Demo.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
Slider,
Interval,
Markdown,
Upload
Upload,
ToolTip
} from '../src';


Expand Down Expand Up @@ -293,6 +294,27 @@ class Controller extends Component {
ReactDOM.render(<Controller/>, mountNode);`


const ToolTipExample = `class Controller extends Component {
render() {
return (
<div>
<p>This is an inline <ToolTip direction="top" colors={{background:"#ccc", border:"#f00"}}>ToolTip top</ToolTip> tooltip.</p>
<p>
You can also position them using CSS:
<ToolTip direction="top" style={{position: 'relative', left: '140px', top: '-20px'}}>ToolTip top</ToolTip>
<ToolTip direction="right" style={{position: 'relative', left: '160px', top: '0px'}}>ToolTip right</ToolTip>
<ToolTip direction="bottom" style={{position: 'relative', left: '140px', top: '20px'}}>ToolTip bottom</ToolTip>
<ToolTip direction="left" style={{position: 'relative', left: '120px', top:'0px'}}>ToolTip left</ToolTip>
</p>
</div>
);
}
}

ReactDOM.render(<Controller/>, mountNode);
`;


const examples = [
{name: 'Upload', code: UploadExample},
{name: 'Markdown', code: MarkdownExample},
Expand All @@ -304,7 +326,8 @@ const examples = [
{name: 'Slider', code: SliderExample},
{name: 'RangeSlider', code: RangeSliderExample},
{name: 'Input', code: InputExample},
{name: 'DatePickerRange', code: DatePickerRangeExample}
{name: 'DatePickerRange', code: DatePickerRangeExample},
{name: 'Tooltip', code: ToolTipExample},
];

class Demo extends Component {
Expand Down
248 changes: 248 additions & 0 deletions src/components/ToolTip.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {omit} from 'ramda';

/**
* A basic HTML textarea for entering multiline text.
*
*/
export default class ToolTip extends Component {
render() {
const {loading_state, value, children, direction, colors} = this.props;

return (
<span
data-dash-is-loading={
(loading_state && loading_state.is_loading) || undefined
}
className={`hover hover-${direction}`}
{...omit([], this.props)}
>
<span className="hover-content">
{children}
</span>
<style jsx>{`
.hover,
.hover-right {
/* Offset so that the triangle caret lands directly on what's hovered */
transform: translate(5px, 0);

position: absolute;
}

.hover-left {
transform: translate(-5px, 0);
}

.hover-bottom {
transform: translate(0, 6px);
}

.hover-top {
transform: translate(0, -5px);
}

.hover-content {
position: absolute;
border: 1px solid ${colors.border};
border-radius: 2px;
padding: 5px 10px;
background: ${colors.background};
white-space: nowrap;
}

.hover .hover-content,
.hover-right .hover-content {
transform: translate(0, -50%);
}

.hover-left .hover-content {
transform: translate(-100%, -50%);
}

.hover-top .hover-content {
transform: translate(-50%, -100%);
}

.hover-bottom .hover-content {
transform: translate(-50%, 0);
}

/* Add a small triangle on the left side of the box */
.hover:before,
.hover:after {
content: '';
width: 0;
height: 0;
position: absolute;
border-style: solid;
top: -6px;
}

.hover:before,
.hover:after,
.hover-right:before,
.hover-right:after {
border-width: 6px 6px 6px 0;
}

.hover-top:before,
.hover-top:after {
border-width: 6px 6px 0 6px;
}

.hover-bottom:before,
.hover-bottom:after {
border-width: 0 6px 6px 6px;
}

.hover-left:before,
.hover-left:after {
border-width: 6px 0 6px 6px;
}

.hover:before,
.hover-right:before {
border-color: transparent ${colors.border} transparent transparent;
left: -5px;
z-index: 2;
}

.hover:after,
.hover-right:after {
border-color: transparent ${colors.background} transparent transparent;
left: -4px;
z-index: 3;
}

.hover-left:before {
border-color: transparent transparent transparent ${colors.border};
left: -1px;
}

.hover-left:after {
border-color: transparent transparent transparent ${colors.background};
left: -2px;
}

.hover-top:before,
.hover-top:after,
.hover-bottom:before,
.hover-bottom:after {
left: -6px;
}

.hover-bottom:before {
border-color: transparent transparent ${colors.border} transparent;
}

.hover-bottom:after {
border-color: transparent transparent ${colors.background} transparent;
top: -5px;
}

.hover-top:before {
border-color: ${colors.border} transparent transparent transparent;
top: -1px;
}

.hover-top:after {
border-color: ${colors.background} transparent transparent transparent;
top: -2px;
}
`}</style>
<style jsx global>{`
/* By default, hide the loader */
.hover-loader {
display: none;
}

/* Don't display hovers while server-side computation is taking place */
[data-dash-is-loading=true] .hover {
display: none;
}

/* Show a loader when the hover immediately preceeding it is loading */
[data-dash-is-loading=true] + .hover-loader {
display: block;
}
`}</style>
</span>
);
}
}

ToolTip.defaultProps = {
persisted_props: ['value'],
persistence_type: 'local',
direction: 'right',
colors: {
border: '#d6d6d6',
background: 'white',
},
};

ToolTip.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* Often used with CSS to style elements with common properties.
*/
className: PropTypes.string,

/**
* Defines the direction in which the hover opens.
*/
direction: PropTypes.oneOf([
'top',
'right',
'bottom',
'left'
]),

/**
* Holds the colors used by the ToolTip component. If you set these, you should specify colors for all properties, so:
* colors: {
* border: '#d6d6d6',
* primary: '#1975FA',
* background: '#f9f9f9'
* }
*/
colors: PropTypes.exact({
border: PropTypes.string,
background: PropTypes.string,
}),

/**
* Prevents rendering of given element, while keeping child elements, e.g. script elements, active.
*/
hidden: PropTypes.string,

/**
* Defines CSS styles which will override styles previously set.
*/
style: PropTypes.object,

/**
* Object that holds the loading state object coming from dash-renderer
*/
loading_state: PropTypes.shape({
/**
* Determines if the component is loading or not
*/
is_loading: PropTypes.bool,
/**
* Holds which property is loading
*/
prop_name: PropTypes.string,
/**
* Holds the name of the component that is loading
*/
component_name: PropTypes.string,
}),
};
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Tabs from './components/Tabs.react';
import Tab from './components/Tab.react';
import Store from './components/Store.react';
import LogoutButton from './components/LogoutButton.react';
import ToolTip from './components/ToolTip.react';

import 'react-dates/lib/css/_datepicker.css';
import './components/css/react-dates@20.1.0-fix.css';
Expand Down Expand Up @@ -49,5 +50,6 @@ export {
Upload,
Store,
LogoutButton,
Download
Download,
ToolTip
};
35 changes: 35 additions & 0 deletions tests/unit/ToolTip.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import ToolTip from '../../src/components/ToolTip.react.js';
import React from 'react';
import {mount, render} from 'enzyme';
import DashRendererMock from './mocks/DashRendererMock.react.js';

test('ToolTip render', () => {
const tabs = render(
<DashRendererMock>
<ToolTip>content</ToolTip>
</DashRendererMock>
);

expect(tabs.html()).toBeDefined();
});

describe('All props can be set properly', () => {
const defaultProps = {
id: 'test-tooltip',
colors: {
border: 'red',
background: 'blue',
},
};
const app = mount(
<DashRendererMock>
<ToolTip {...defaultProps}/>
</DashRendererMock>
);
test('props.id =>', () => {
expect(app.find(ToolTip).props().id).toEqual(defaultProps.id);
});
test('props.colors=>', () => {
expect(app.find(ToolTip).props().colors).toEqual(defaultProps.colors);
});
});