diff --git a/CHANGELOG.md b/CHANGELOG.md
index 621981113..16f514e1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+### Added
+- Ability to add tooltips to `Slider` and `RangeSlider`, which can be visible always or on hover. Tooltips also take a position argument. [#564](https://github.com/plotly/dash-core-components/pull/564)
+
### Fixed
- Fixed `min_date_allowed` and `max_date_allowed` bug in `DatePickerRange` [#551]https://github.com/plotly/dash-core-components/issues/551)
- Fixed unwanted `resize()` calls on unmounted `Graph`s [#534](https://github.com/plotly/dash-core-components/issues/534)
diff --git a/package.json b/package.json
index 107bf1a2d..0c2bd605e 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
"moment": "^2.20.1",
"prop-types": "^15.6.0",
"ramda": "^0.24.1",
- "rc-slider": "^8.3.1",
+ "rc-slider": "^8.6.11",
"react-addons-shallow-compare": "^15.6.0",
"react-dates": "^20.1.0",
"react-docgen": "^3.0.0",
diff --git a/src/components/RangeSlider.react.js b/src/components/RangeSlider.react.js
index 4352554ce..629809152 100644
--- a/src/components/RangeSlider.react.js
+++ b/src/components/RangeSlider.react.js
@@ -1,7 +1,7 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {omit} from 'ramda';
-import {Range} from 'rc-slider';
+import {Range, createSliderWithTooltip} from 'rc-slider';
/**
* A double slider with two handles.
@@ -11,6 +11,9 @@ export default class RangeSlider extends Component {
constructor(props) {
super(props);
this.propsToState = this.propsToState.bind(this);
+ this.DashSlider = props.tooltip
+ ? createSliderWithTooltip(Range)
+ : Range;
}
propsToState(newProps) {
@@ -18,6 +21,11 @@ export default class RangeSlider extends Component {
}
componentWillReceiveProps(newProps) {
+ if (newProps.tooltip !== this.props.tooltip) {
+ this.DashSlider = newProps.tooltip
+ ? createSliderWithTooltip(Range)
+ : Range;
+ }
this.propsToState(newProps);
}
@@ -31,11 +39,27 @@ export default class RangeSlider extends Component {
id,
loading_state,
setProps,
+ tooltip,
updatemode,
vertical,
} = this.props;
const value = this.state.value;
+ let tipProps;
+ if (tooltip && tooltip.always_visible) {
+ /**
+ * clone `tooltip` but with renamed key `always_visible` -> `visible`
+ * the rc-tooltip API uses `visible`, but `always_visible is more semantic
+ * assigns the new (renamed) key to the old key and deletes the old key
+ */
+ tipProps = Object.assign(tooltip, {
+ visible: tooltip.always_visible,
+ });
+ delete tipProps.always_visible;
+ } else {
+ tipProps = tooltip;
+ }
+
return (
-
{
if (updatemode === 'drag') {
setProps({value});
@@ -58,6 +82,7 @@ export default class RangeSlider extends Component {
setProps({value});
}
}}
+ tipProps={tipProps}
value={value}
{...omit(
['className', 'value', 'setProps', 'updatemode'],
@@ -152,6 +177,31 @@ RangeSlider.propTypes = {
*/
pushable: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
+ tooltip: PropTypes.exact({
+ /**
+ * Determines whether tooltips should always be visible
+ * (as opposed to the default, visible on hover)
+ */
+ always_visible: PropTypes.bool,
+
+ /**
+ * Determines the placement of tooltips
+ * See https://github.com/react-component/tooltip#api
+ * top/bottom{*} sets the _origin_ of the tooltip, so e.g. `topLeft` will
+ * in reality appear to be on the top right of the handle
+ */
+ placement: PropTypes.oneOf([
+ 'left',
+ 'right',
+ 'top',
+ 'bottom',
+ 'topLeft',
+ 'topRight',
+ 'bottomLeft',
+ 'bottomRight',
+ ]),
+ }),
+
/**
* Value by which increments or decrements are made
*/
diff --git a/src/components/Slider.react.js b/src/components/Slider.react.js
index 6397f0d3a..760740cdc 100644
--- a/src/components/Slider.react.js
+++ b/src/components/Slider.react.js
@@ -1,5 +1,5 @@
import React, {Component} from 'react';
-import ReactSlider from 'rc-slider';
+import ReactSlider, {createSliderWithTooltip} from 'rc-slider';
import PropTypes from 'prop-types';
import {omit} from 'ramda';
import './css/rc-slider@6.1.2.css';
@@ -11,6 +11,9 @@ export default class Slider extends Component {
constructor(props) {
super(props);
this.propsToState = this.propsToState.bind(this);
+ this.DashSlider = props.tooltip
+ ? createSliderWithTooltip(ReactSlider)
+ : ReactSlider;
}
propsToState(newProps) {
@@ -18,6 +21,11 @@ export default class Slider extends Component {
}
componentWillReceiveProps(newProps) {
+ if (newProps.tooltip !== this.props.tooltip) {
+ this.DashSlider = newProps.tooltip
+ ? createSliderWithTooltip(ReactSlider)
+ : ReactSlider;
+ }
this.propsToState(newProps);
}
@@ -31,11 +39,27 @@ export default class Slider extends Component {
id,
loading_state,
setProps,
+ tooltip,
updatemode,
vertical,
} = this.props;
const value = this.state.value;
+ let tipProps;
+ if (tooltip && tooltip.always_visible) {
+ /**
+ * clone `tooltip` but with renamed key `always_visible` -> `visible`
+ * the rc-tooltip API uses `visible`, but `always_visible is more semantic
+ * assigns the new (renamed) key to the old key and deletes the old key
+ */
+ tipProps = Object.assign(tooltip, {
+ visible: tooltip.always_visible,
+ });
+ delete tipProps.always_visible;
+ } else {
+ tipProps = tooltip;
+ }
+
return (
- {
if (updatemode === 'drag') {
setProps({value});
@@ -58,6 +82,7 @@ export default class Slider extends Component {
setProps({value});
}
}}
+ tipProps={tooltip}
value={value}
{...omit(
['className', 'setProps', 'updatemode', 'value'],
@@ -133,6 +158,31 @@ Slider.propTypes = {
*/
max: PropTypes.number,
+ tooltip: PropTypes.exact({
+ /**
+ * Determines whether tooltips should always be visible
+ * (as opposed to the default, visible on hover)
+ */
+ always_visible: PropTypes.bool,
+
+ /**
+ * Determines the placement of tooltips
+ * See https://github.com/react-component/tooltip#api
+ * top/bottom{*} sets the _origin_ of the tooltip, so e.g. `topLeft` will
+ * in reality appear to be on the top right of the handle
+ */
+ placement: PropTypes.oneOf([
+ 'left',
+ 'right',
+ 'top',
+ 'bottom',
+ 'topLeft',
+ 'topRight',
+ 'bottomLeft',
+ 'bottomRight',
+ ]),
+ }),
+
/**
* Value by which increments or decrements are made
*/