Skip to content

Commit 0ca1b94

Browse files
committed
Add Slider/RangeSlider tooltip.format and tooltip.style.
1 parent 000ec18 commit 0ca1b94

File tree

7 files changed

+121
-15
lines changed

7 files changed

+121
-15
lines changed

components/dash-core-components/src/components/RangeSlider.react.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@ RangeSlider.propTypes = {
125125
'bottomLeft',
126126
'bottomRight',
127127
]),
128+
/**
129+
* Format to apply to the tooltip
130+
* The string must contain `{value}`
131+
*/
132+
format: PropTypes.string,
133+
/**
134+
* Custom style for the tooltip.
135+
*/
136+
style: PropTypes.object,
128137
}),
129138

130139
/**

components/dash-core-components/src/components/Slider.react.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React, {Component, lazy, Suspense} from 'react';
22
import PropTypes from 'prop-types';
33
import slider from '../utils/LazyLoader/slider';
44

5+
import './css/sliders.css';
6+
57
const RealSlider = lazy(slider);
68

79
/**
@@ -105,6 +107,15 @@ Slider.propTypes = {
105107
'bottomLeft',
106108
'bottomRight',
107109
]),
110+
/**
111+
* Format to apply to the tooltip
112+
* The string must contain `{value}`
113+
*/
114+
format: PropTypes.string,
115+
/**
116+
* Custom style for the tooltip.
117+
*/
118+
style: PropTypes.object,
108119
}),
109120

110121
/**
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* Fix the default tooltip style height conflicting with the actual size of the tooltip. */
2+
.rc-slider-tooltip-content > .rc-slider-tooltip-inner {
3+
height: unset;
4+
min-height: 20px;
5+
}

components/dash-core-components/src/fragments/RangeSlider.react.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, {Component} from 'react';
2-
import {assoc, pick, isNil} from 'ramda';
2+
import {assoc, pick, isNil, pipe, dissoc} from 'ramda';
33
import {Range, createSliderWithTooltip} from 'rc-slider';
44
import computeSliderStyle from '../utils/computeSliderStyle';
55

@@ -11,6 +11,7 @@ import {
1111
setUndefined,
1212
} from '../utils/computeSliderMarkers';
1313
import {propTypes, defaultProps} from '../components/RangeSlider.react';
14+
import {formatSliderTooltip} from '../utils/formatSliderTooltip';
1415

1516
const sliderProps = [
1617
'min',
@@ -72,17 +73,29 @@ export default class RangeSlider extends Component {
7273
} = this.props;
7374
const value = this.state.value;
7475

75-
let tipProps;
76-
if (tooltip && tooltip.always_visible) {
76+
let tipProps, tipFormatter;
77+
if (tooltip) {
7778
/**
7879
* clone `tooltip` but with renamed key `always_visible` -> `visible`
79-
* the rc-tooltip API uses `visible`, but `always_visible is more semantic
80+
* the rc-tooltip API uses `visible`, but `always_visible` is more semantic
8081
* assigns the new (renamed) key to the old key and deletes the old key
8182
*/
82-
tipProps = assoc('visible', tooltip.always_visible, tooltip);
83-
delete tipProps.always_visible;
84-
} else {
85-
tipProps = tooltip;
83+
tipProps = pipe(
84+
assoc('visible', tooltip.always_visible),
85+
dissoc('always_visible'),
86+
dissoc('format'),
87+
dissoc('style')
88+
)(tooltip);
89+
if (tooltip.format || tooltip.style) {
90+
tipFormatter = tipValue => (
91+
<div style={tooltip.style}>
92+
{formatSliderTooltip(
93+
tooltip.format || '{value}',
94+
tipValue
95+
)}
96+
</div>
97+
);
98+
}
8699
}
87100

88101
return (
@@ -116,6 +129,7 @@ export default class RangeSlider extends Component {
116129
...tipProps,
117130
getTooltipContainer: node => node,
118131
}}
132+
tipFormatter={tipFormatter}
119133
style={{position: 'relative'}}
120134
value={value ? value : calcValue(min, max, value)}
121135
marks={sanitizeMarks({min, max, marks, step})}

components/dash-core-components/src/fragments/Slider.react.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, {Component} from 'react';
22
import ReactSlider, {createSliderWithTooltip} from 'rc-slider';
3-
import {assoc, isNil, pick} from 'ramda';
3+
import {assoc, isNil, pick, pipe, dissoc} from 'ramda';
44
import computeSliderStyle from '../utils/computeSliderStyle';
55

66
import 'rc-slider/assets/index.css';
@@ -11,6 +11,7 @@ import {
1111
setUndefined,
1212
} from '../utils/computeSliderMarkers';
1313
import {propTypes, defaultProps} from '../components/Slider.react';
14+
import {formatSliderTooltip} from '../utils/formatSliderTooltip';
1415

1516
const sliderProps = [
1617
'min',
@@ -72,17 +73,29 @@ export default class Slider extends Component {
7273
} = this.props;
7374
const value = this.state.value;
7475

75-
let tipProps;
76-
if (tooltip && tooltip.always_visible) {
76+
let tipProps, tipFormatter;
77+
if (tooltip) {
7778
/**
7879
* clone `tooltip` but with renamed key `always_visible` -> `visible`
7980
* the rc-tooltip API uses `visible`, but `always_visible` is more semantic
8081
* assigns the new (renamed) key to the old key and deletes the old key
8182
*/
82-
tipProps = assoc('visible', tooltip.always_visible, tooltip);
83-
delete tipProps.always_visible;
84-
} else {
85-
tipProps = tooltip;
83+
tipProps = pipe(
84+
assoc('visible', tooltip.always_visible),
85+
dissoc('always_visible'),
86+
dissoc('format'),
87+
dissoc('style')
88+
)(tooltip);
89+
if (tooltip.format || tooltip.style) {
90+
tipFormatter = tipValue => (
91+
<div style={tooltip.style}>
92+
{formatSliderTooltip(
93+
tooltip.format || '{value}',
94+
tipValue
95+
)}
96+
</div>
97+
);
98+
}
8699
}
87100

88101
return (
@@ -116,6 +129,7 @@ export default class Slider extends Component {
116129
...tipProps,
117130
getTooltipContainer: node => node,
118131
}}
132+
tipFormatter={tipFormatter}
119133
style={{position: 'relative'}}
120134
value={value}
121135
marks={sanitizeMarks({min, max, marks, step})}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {replace} from 'ramda';
2+
3+
export const formatSliderTooltip = (template, value) => {
4+
return replace('{value}', value, template);
5+
};

components/dash-core-components/tests/integration/sliders/test_sliders.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,51 @@ def test_slsl015_range_slider_no_min_max(dash_dcc):
559559
)
560560

561561
assert dash_dcc.get_logs() == []
562+
563+
564+
def test_sls016_sliders_format_tooltips(dash_dcc):
565+
app = Dash(__name__)
566+
app.layout = html.Div(
567+
[
568+
dcc.Slider(
569+
value=34,
570+
min=20,
571+
max=100,
572+
id="slider",
573+
tooltip={
574+
"format": "Custom tooltip: {value}",
575+
"always_visible": True,
576+
"style": {"padding": "8px"},
577+
},
578+
),
579+
dcc.RangeSlider(
580+
value=[48, 60],
581+
min=20,
582+
max=100,
583+
id="range-slider",
584+
tooltip={"format": "Custom tooltip: {value}", "always_visible": True},
585+
),
586+
],
587+
style={"padding": "12px", "marginTop": "48px"},
588+
)
589+
590+
dash_dcc.start_server(app)
591+
# dash_dcc.wait_for_element("#slider")
592+
593+
dash_dcc.wait_for_text_to_equal(
594+
"#slider .rc-slider-tooltip-content", "Custom tooltip: 34"
595+
)
596+
dash_dcc.wait_for_text_to_equal(
597+
"#range-slider .rc-slider-tooltip-content", "Custom tooltip: 48"
598+
)
599+
dash_dcc.wait_for_text_to_equal(
600+
"#range-slider > div:nth-child(1) > div:last-child .rc-slider-tooltip-content",
601+
"Custom tooltip: 60",
602+
)
603+
dash_dcc.wait_for_style_to_equal(
604+
"#slider .rc-slider-tooltip-inner > div", "padding", "8px"
605+
)
606+
607+
dash_dcc.percy_snapshot("sliders-format-tooltips")
608+
609+
assert dash_dcc.get_logs() == []

0 commit comments

Comments
 (0)