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

Commit 7477eeb

Browse files
authored
Merge pull request #888 from almarklein/drag_value2
Adding drag_value to Slider
2 parents fdb511f + 6c9bcda commit 7477eeb

File tree

6 files changed

+152
-44
lines changed

6 files changed

+152
-44
lines changed

Diff for: src/components/RangeSlider.react.js

+13-8
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ RangeSlider.propTypes = {
4949
*/
5050
value: PropTypes.arrayOf(PropTypes.number),
5151

52+
/**
53+
* The value of the input during a drag
54+
*/
55+
drag_value: PropTypes.arrayOf(PropTypes.number),
56+
5257
/**
5358
* allowCross could be set as true to allow those handles to cross.
5459
*/
@@ -145,18 +150,18 @@ RangeSlider.propTypes = {
145150
verticalHeight: PropTypes.number,
146151

147152
/**
148-
* Determines when the component should update
149-
* its value. If `mouseup`, then the slider
150-
* will only trigger its value when the user has
151-
* finished dragging the slider. If `drag`, then
152-
* the slider will update its value continuously
153-
* as it is being dragged.
154-
* Only use `drag` if your updates are fast.
153+
* Determines when the component should update its `value`
154+
* property. If `mouseup` (the default) then the slider
155+
* will only trigger its value when the user has finished
156+
* dragging the slider. If `drag`, then the slider will
157+
* update its value continuously as it is being dragged.
158+
* Note that for the latter case, the `drag_value`
159+
* property could be used instead.
155160
*/
156161
updatemode: PropTypes.oneOf(['mouseup', 'drag']),
157162

158163
/**
159-
* Dash-assigned callback that gets fired when the value changes.
164+
* Dash-assigned callback that gets fired when the value or drag_value changes.
160165
*/
161166
setProps: PropTypes.func,
162167

Diff for: src/components/Slider.react.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ Slider.propTypes = {
4848
*/
4949
value: PropTypes.number,
5050

51+
/**
52+
* The value of the input during a drag
53+
*/
54+
drag_value: PropTypes.number,
55+
5156
/**
5257
* Additional CSS class for the root DOM node
5358
*/
@@ -125,18 +130,19 @@ Slider.propTypes = {
125130
verticalHeight: PropTypes.number,
126131

127132
/**
128-
* Determines when the component should update
129-
* its value. If `mouseup`, then the slider
130-
* will only trigger its value when the user has
131-
* finished dragging the slider. If `drag`, then
132-
* the slider will update its value continuously
133-
* as it is being dragged.
134-
* Only use `drag` if your updates are fast.
133+
* Determines when the component should update its `value`
134+
* property. If `mouseup` (the default) then the slider
135+
* will only trigger its value when the user has finished
136+
* dragging the slider. If `drag`, then the slider will
137+
* update its value continuously as it is being dragged.
138+
* If you want different actions during and after drag,
139+
* leave `updatemode` as `mouseup` and use `drag_value`
140+
* for the continuously updating value.
135141
*/
136142
updatemode: PropTypes.oneOf(['mouseup', 'drag']),
137143

138144
/**
139-
* Dash-assigned callback that gets fired when the value changes.
145+
* Dash-assigned callback that gets fired when the value or drag_value changes.
140146
*/
141147
setProps: PropTypes.func,
142148

Diff for: src/fragments/RangeSlider.react.js

+13-14
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,11 @@ import {propTypes, defaultProps} from '../components/RangeSlider.react';
1010
export default class RangeSlider extends Component {
1111
constructor(props) {
1212
super(props);
13-
this.propsToState = this.propsToState.bind(this);
1413
this.DashSlider = props.tooltip
1514
? createSliderWithTooltip(Range)
1615
: Range;
1716
this._computeStyle = computeSliderStyle();
18-
this.state = {
19-
value: props.value,
20-
};
21-
}
22-
23-
propsToState(newProps) {
24-
if (newProps.value !== this.props.value) {
25-
this.setState({value: newProps.value});
26-
}
17+
this.state = {value: props.value};
2718
}
2819

2920
UNSAFE_componentWillReceiveProps(newProps) {
@@ -32,11 +23,17 @@ export default class RangeSlider extends Component {
3223
? createSliderWithTooltip(Range)
3324
: Range;
3425
}
35-
this.propsToState(newProps);
26+
if (newProps.value !== this.props.value) {
27+
this.props.setProps({drag_value: newProps.value});
28+
this.setState({value: newProps.value});
29+
}
3630
}
3731

3832
UNSAFE_componentWillMount() {
39-
this.propsToState(this.props);
33+
if (this.props.value !== null) {
34+
this.props.setProps({drag_value: this.props.value});
35+
this.setState({value: this.props.value});
36+
}
4037
}
4138

4239
render() {
@@ -84,9 +81,10 @@ export default class RangeSlider extends Component {
8481
<this.DashSlider
8582
onChange={value => {
8683
if (updatemode === 'drag') {
87-
setProps({value});
84+
setProps({value: value, drag_value: value});
8885
} else {
89-
this.setState({value});
86+
this.setState({value: value});
87+
setProps({drag_value: value});
9088
}
9189
}}
9290
onAfterChange={value => {
@@ -101,6 +99,7 @@ export default class RangeSlider extends Component {
10199
[
102100
'className',
103101
'value',
102+
'drag_value',
104103
'setProps',
105104
'marks',
106105
'updatemode',

Diff for: src/fragments/Slider.react.js

+13-14
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,11 @@ import {propTypes, defaultProps} from '../components/Slider.react';
1313
export default class Slider extends Component {
1414
constructor(props) {
1515
super(props);
16-
this.propsToState = this.propsToState.bind(this);
1716
this.DashSlider = props.tooltip
1817
? createSliderWithTooltip(ReactSlider)
1918
: ReactSlider;
2019
this._computeStyle = computeSliderStyle();
21-
this.state = {
22-
value: props.value,
23-
};
24-
}
25-
26-
propsToState(newProps) {
27-
if (newProps.value !== this.props.value) {
28-
this.setState({value: newProps.value});
29-
}
20+
this.state = {value: props.value};
3021
}
3122

3223
UNSAFE_componentWillReceiveProps(newProps) {
@@ -35,11 +26,17 @@ export default class Slider extends Component {
3526
? createSliderWithTooltip(ReactSlider)
3627
: ReactSlider;
3728
}
38-
this.propsToState(newProps);
29+
if (newProps.value !== this.props.value) {
30+
this.props.setProps({drag_value: newProps.value});
31+
this.setState({value: newProps.value});
32+
}
3933
}
4034

4135
UNSAFE_componentWillMount() {
42-
this.propsToState(this.props);
36+
if (this.props.value !== null) {
37+
this.props.setProps({drag_value: this.props.value});
38+
this.setState({value: this.props.value});
39+
}
4340
}
4441

4542
render() {
@@ -87,9 +84,10 @@ export default class Slider extends Component {
8784
<this.DashSlider
8885
onChange={value => {
8986
if (updatemode === 'drag') {
90-
setProps({value});
87+
setProps({value: value, drag_value: value});
9188
} else {
92-
this.setState({value});
89+
this.setState({value: value});
90+
setProps({drag_value: value});
9391
}
9492
}}
9593
onAfterChange={value => {
@@ -114,6 +112,7 @@ export default class Slider extends Component {
114112
'setProps',
115113
'updatemode',
116114
'value',
115+
'drag_value',
117116
'marks',
118117
'verticalHeight',
119118
],

Diff for: tests/dash_core_components_page.py

+18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from selenium.webdriver.support.ui import WebDriverWait
33
from selenium.webdriver.common.by import By
44
from selenium.webdriver.support import expected_conditions as EC
5+
from selenium.webdriver.common.action_chains import ActionChains
56

67
logger = logging.getLogger(__name__)
78

@@ -100,3 +101,20 @@ def _wait_until_day_is_clickable(self, timeout=1):
100101
@property
101102
def date_picker_day_locator(self):
102103
return 'div[data-visible="true"] td.CalendarDay'
104+
105+
def click_and_hold_at_coord_fractions(self, elem_or_selector, fx, fy):
106+
elem = self._get_element(elem_or_selector)
107+
108+
ActionChains(self.driver).move_to_element_with_offset(
109+
elem, elem.size["width"] * fx, elem.size["height"] * fy
110+
).click_and_hold().perform()
111+
112+
def move_to_coord_fractions(self, elem_or_selector, fx, fy):
113+
elem = self._get_element(elem_or_selector)
114+
115+
ActionChains(self.driver).move_to_element_with_offset(
116+
elem, elem.size["width"] * fx, elem.size["height"] * fy
117+
).perform()
118+
119+
def release(self):
120+
ActionChains(self.driver).release().perform()

Diff for: tests/integration/sliders/test_sliders.py

+81
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,84 @@ def test_slsl005_slider_tooltip(dash_dcc):
160160
dash_dcc.percy_snapshot(
161161
"slider-make sure tooltips are only visible if parent slider is visible"
162162
)
163+
164+
165+
def test_slsl006_drag_value_slider(dash_dcc):
166+
app = dash.Dash(__name__)
167+
app.layout = html.Div(
168+
[
169+
dcc.Slider(
170+
id="slider",
171+
min=0,
172+
max=20,
173+
step=1,
174+
value=5,
175+
tooltip={"always_visible": True},
176+
),
177+
html.Div(id="out-value"),
178+
html.Div(id="out-drag-value"),
179+
]
180+
)
181+
182+
@app.callback(Output("out-drag-value", "children"), [Input("slider", "drag_value")])
183+
def update_output(value):
184+
return "You have dragged {}".format(value)
185+
186+
@app.callback(Output("out-value", "children"), [Input("slider", "value")])
187+
def update_output(value):
188+
return "You have selected {}".format(value)
189+
190+
dash_dcc.start_server(app)
191+
slider = dash_dcc.find_element("#slider")
192+
193+
dash_dcc.wait_for_text_to_equal("#out-value", "You have selected 5")
194+
dash_dcc.wait_for_text_to_equal("#out-drag-value", "You have dragged 5")
195+
196+
dash_dcc.click_and_hold_at_coord_fractions(slider, 0.25, 0.25)
197+
dash_dcc.move_to_coord_fractions(slider, 0.75, 0.25)
198+
dash_dcc.wait_for_text_to_equal("#out-drag-value", "You have dragged 15")
199+
dash_dcc.move_to_coord_fractions(slider, 0.5, 0.25)
200+
dash_dcc.wait_for_text_to_equal("#out-drag-value", "You have dragged 10")
201+
dash_dcc.wait_for_text_to_equal("#out-value", "You have selected 5")
202+
dash_dcc.release()
203+
dash_dcc.wait_for_text_to_equal("#out-value", "You have selected 10")
204+
205+
206+
def test_slsl007_drag_value_rangeslider(dash_dcc):
207+
app = dash.Dash(__name__)
208+
app.layout = html.Div(
209+
[
210+
dcc.RangeSlider(
211+
id="slider",
212+
min=0,
213+
max=20,
214+
step=1,
215+
value=(5, 15),
216+
tooltip={"always_visible": True},
217+
),
218+
html.Div(id="out-value"),
219+
html.Div(id="out-drag-value"),
220+
]
221+
)
222+
223+
@app.callback(Output("out-drag-value", "children"), [Input("slider", "drag_value")])
224+
def update_output(value):
225+
value = value or (None, None)
226+
return "You have dragged {}-{}".format(*value)
227+
228+
@app.callback(Output("out-value", "children"), [Input("slider", "value")])
229+
def update_output(value):
230+
return "You have selected {}-{}".format(*value)
231+
232+
dash_dcc.start_server(app)
233+
slider = dash_dcc.find_element("#slider")
234+
235+
dash_dcc.wait_for_text_to_equal("#out-value", "You have selected 5-15")
236+
dash_dcc.wait_for_text_to_equal("#out-drag-value", "You have dragged 5-15")
237+
238+
dash_dcc.click_and_hold_at_coord_fractions(slider, 0.25, 0.25)
239+
dash_dcc.move_to_coord_fractions(slider, 0.5, 0.25)
240+
dash_dcc.wait_for_text_to_equal("#out-drag-value", "You have dragged 10-15")
241+
dash_dcc.wait_for_text_to_equal("#out-value", "You have selected 5-15")
242+
dash_dcc.release()
243+
dash_dcc.wait_for_text_to_equal("#out-value", "You have selected 10-15")

0 commit comments

Comments
 (0)