Skip to content

Commit e1a3c42

Browse files
Merge branch 'dev' of github.com:plotly/dash into python-310
2 parents 01a4d97 + 6a8da52 commit e1a3c42

File tree

19 files changed

+214
-31
lines changed

19 files changed

+214
-31
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
All notable changes to `dash` will be documented in this file.
33
This project adheres to [Semantic Versioning](https://semver.org/).
44

5-
## [UNRELEASED]
5+
## [2.15.0] - 2024-01-31
66

77
## Added
88
- [#2695](https://github.com/plotly/dash/pull/2695) Adds `triggered_id` to `dash_clientside.callback_context`. Fixes [#2692](https://github.com/plotly/dash/issues/2692)
9+
- [#2723](https://github.com/plotly/dash/pull/2723) Improve dcc Slider/RangeSlider tooltips. Fixes [#1846](https://github.com/plotly/dash/issues/1846)
10+
- Add `tooltip.template` a string for the format template, {value} will be formatted with the actual value.
11+
- Add `tooltip.style` a style object to give to the div of the tooltip.
12+
- Add `tooltip.transform` a reference to a function in the `window.dccFunctions` namespace.
913
- [#2732](https://github.com/plotly/dash/pull/2732) Add special key `_dash_error` to `setProps`, allowing component developers to send error without throwing in render. Usage `props.setProps({_dash_error: new Error("custom error")})`
1014

1115
## Fixed

components/dash-core-components/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/dash-core-components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dash-core-components",
3-
"version": "2.12.1",
3+
"version": "2.13.0",
44
"description": "Core component suite for Dash",
55
"repository": {
66
"type": "git",

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,31 @@ RangeSlider.propTypes = {
125125
'bottomLeft',
126126
'bottomRight',
127127
]),
128+
/**
129+
* Template string to display the tooltip in.
130+
* Must contain `{value}`, which will be replaced with either
131+
* the default string representation of the value or the result of the
132+
* transform function if there is one.
133+
*/
134+
template: PropTypes.string,
135+
/**
136+
* Custom style for the tooltip.
137+
*/
138+
style: PropTypes.object,
139+
/**
140+
* Reference to a function in the `window.dccFunctions` namespace.
141+
* This can be added in a script in the asset folder.
142+
*
143+
* For example, in `assets/tooltip.js`:
144+
* ```
145+
* window.dccFunctions = window.dccFunctions || {};
146+
* window.dccFunctions.multByTen = function(value) {
147+
* return value * 10;
148+
* }
149+
* ```
150+
* Then in the component `tooltip={'transform': 'multByTen'}`
151+
*/
152+
transform: PropTypes.string,
128153
}),
129154

130155
/**

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

Lines changed: 27 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,31 @@ Slider.propTypes = {
105107
'bottomLeft',
106108
'bottomRight',
107109
]),
110+
/**
111+
* Template string to display the tooltip in.
112+
* Must contain `{value}`, which will be replaced with either
113+
* the default string representation of the value or the result of the
114+
* transform function if there is one.
115+
*/
116+
template: PropTypes.string,
117+
/**
118+
* Custom style for the tooltip.
119+
*/
120+
style: PropTypes.object,
121+
/**
122+
* Reference to a function in the `window.dccFunctions` namespace.
123+
* This can be added in a script in the asset folder.
124+
*
125+
* For example, in `assets/tooltip.js`:
126+
* ```
127+
* window.dccFunctions = window.dccFunctions || {};
128+
* window.dccFunctions.multByTen = function(value) {
129+
* return value * 10;
130+
* }
131+
* ```
132+
* Then in the component `tooltip={'transform': 'multByTen'}`
133+
*/
134+
transform: PropTypes.string,
108135
}),
109136

110137
/**
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: 29 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, omit} from 'ramda';
33
import {Range, createSliderWithTooltip} from 'rc-slider';
44
import computeSliderStyle from '../utils/computeSliderStyle';
55

@@ -11,6 +11,10 @@ import {
1111
setUndefined,
1212
} from '../utils/computeSliderMarkers';
1313
import {propTypes, defaultProps} from '../components/RangeSlider.react';
14+
import {
15+
formatSliderTooltip,
16+
transformSliderTooltip,
17+
} from '../utils/formatSliderTooltip';
1418

1519
const sliderProps = [
1620
'min',
@@ -72,17 +76,33 @@ export default class RangeSlider extends Component {
7276
} = this.props;
7377
const value = this.state.value;
7478

75-
let tipProps;
76-
if (tooltip && tooltip.always_visible) {
79+
let tipProps, tipFormatter;
80+
if (tooltip) {
7781
/**
7882
* clone `tooltip` but with renamed key `always_visible` -> `visible`
79-
* the rc-tooltip API uses `visible`, but `always_visible is more semantic
83+
* the rc-tooltip API uses `visible`, but `always_visible` is more semantic
8084
* assigns the new (renamed) key to the old key and deletes the old key
8185
*/
82-
tipProps = assoc('visible', tooltip.always_visible, tooltip);
83-
delete tipProps.always_visible;
84-
} else {
85-
tipProps = tooltip;
86+
tipProps = pipe(
87+
assoc('visible', tooltip.always_visible),
88+
omit(['always_visible', 'template', 'style', 'transform'])
89+
)(tooltip);
90+
if (tooltip.template || tooltip.style || tooltip.transform) {
91+
tipFormatter = tipValue => {
92+
let t = tipValue;
93+
if (tooltip.transform) {
94+
t = transformSliderTooltip(tooltip.transform, tipValue);
95+
}
96+
return (
97+
<div style={tooltip.style}>
98+
{formatSliderTooltip(
99+
tooltip.template || '{value}',
100+
t
101+
)}
102+
</div>
103+
);
104+
};
105+
}
86106
}
87107

88108
return (
@@ -116,6 +136,7 @@ export default class RangeSlider extends Component {
116136
...tipProps,
117137
getTooltipContainer: node => node,
118138
}}
139+
tipFormatter={tipFormatter}
119140
style={{position: 'relative'}}
120141
value={value ? value : calcValue(min, max, value)}
121142
marks={sanitizeMarks({min, max, marks, step})}

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

Lines changed: 28 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, omit} from 'ramda';
44
import computeSliderStyle from '../utils/computeSliderStyle';
55

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

1519
const sliderProps = [
1620
'min',
@@ -72,17 +76,33 @@ export default class Slider extends Component {
7276
} = this.props;
7377
const value = this.state.value;
7478

75-
let tipProps;
76-
if (tooltip && tooltip.always_visible) {
79+
let tipProps, tipFormatter;
80+
if (tooltip) {
7781
/**
7882
* clone `tooltip` but with renamed key `always_visible` -> `visible`
7983
* the rc-tooltip API uses `visible`, but `always_visible` is more semantic
8084
* assigns the new (renamed) key to the old key and deletes the old key
8185
*/
82-
tipProps = assoc('visible', tooltip.always_visible, tooltip);
83-
delete tipProps.always_visible;
84-
} else {
85-
tipProps = tooltip;
86+
tipProps = pipe(
87+
assoc('visible', tooltip.always_visible),
88+
omit(['always_visible', 'template', 'style', 'transform'])
89+
)(tooltip);
90+
if (tooltip.template || tooltip.style || tooltip.transform) {
91+
tipFormatter = tipValue => {
92+
let t = tipValue;
93+
if (tooltip.transform) {
94+
t = transformSliderTooltip(tooltip.transform, tipValue);
95+
}
96+
return (
97+
<div style={tooltip.style}>
98+
{formatSliderTooltip(
99+
tooltip.template || '{value}',
100+
t
101+
)}
102+
</div>
103+
);
104+
};
105+
}
86106
}
87107

88108
return (
@@ -116,6 +136,7 @@ export default class Slider extends Component {
116136
...tipProps,
117137
getTooltipContainer: node => node,
118138
}}
139+
tipFormatter={tipFormatter}
119140
style={{position: 'relative'}}
120141
value={value}
121142
marks={sanitizeMarks({min, max, marks, step})}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {replace, path, split, concat, pipe} from 'ramda';
2+
3+
export const formatSliderTooltip = (template, value) => {
4+
return replace('{value}', value, template);
5+
};
6+
7+
export const transformSliderTooltip = (funcName, value) => {
8+
const func = pipe(
9+
split('.'),
10+
s => concat(['dccFunctions'], s),
11+
s => path(s, window)
12+
)(funcName);
13+
if (!func) {
14+
throw new Error(
15+
`Invalid func for slider tooltip transform: ${funcName}`
16+
);
17+
}
18+
return func(value);
19+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
window.dccFunctions = window.dccFunctions || {};
2+
window.dccFunctions.transformTooltip = function(value) {
3+
return "Transformed " + value
4+
}

0 commit comments

Comments
 (0)