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

Commit 09d2e6f

Browse files
authored
Merge pull request #37 from plotly/stateful
Link and Location Components and Stateful Updates
2 parents cde12de + ab5d24a commit 09d2e6f

13 files changed

+481
-93
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## [0.6.0] - 2017-07-18
6+
### Added
7+
- The `Slider` and the `RangeSlider` component can update when the user finishes dragging the slider rather than just while they drag. The default behaviour has remained the same (updates while dragging) but you can toggle that the updates only get fired on "mouse up" by setting `updatemode` to `'mouseup'` (`'drag'` is the default).
8+
- A `Link` and `Location` were added. `Location` represents the address bar of the web browser and `Link` provides a way to modify the address bar without refreshing the page. Combined, these two components can be used to create a "single page app" with multiple URLs. That is, apps that have mulitple URLs but surfing between the different pages doesn't trigger a full page refresh like it would with traditional links.
9+
- Previously, if callback functions weren't supplied to a component, it wouldn't update. This caused a lot of confusion: users would create a simple layout without any callbacks and then wonder why the sliders wouldn't slide or the text inputs wouldn't update. Now, all of the components manage their own state and their appearance will update regardless of whether Dash has assigned a callback to them.
10+
511
## [0.5.3] - 2017-07-03
612
### Added
713
- A `range` object is now included in the `selectedData` object that specifies

dash_core_components/metadata.json

+147-1
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,110 @@
566566
}
567567
}
568568
},
569+
"src/components/Link.react.js": {
570+
"description": "",
571+
"methods": [
572+
{
573+
"name": "updateLocation",
574+
"docblock": null,
575+
"modifiers": [],
576+
"params": [],
577+
"returns": null
578+
}
579+
],
580+
"props": {
581+
"href": {
582+
"type": {
583+
"name": "string"
584+
},
585+
"required": false,
586+
"description": ""
587+
},
588+
"refresh": {
589+
"type": {
590+
"name": "bool"
591+
},
592+
"required": false,
593+
"description": "",
594+
"defaultValue": {
595+
"value": "false",
596+
"computed": false
597+
}
598+
},
599+
"className": {
600+
"type": {
601+
"name": "string"
602+
},
603+
"required": false,
604+
"description": ""
605+
},
606+
"style": {
607+
"type": {
608+
"name": "object"
609+
},
610+
"required": false,
611+
"description": ""
612+
},
613+
"id": {
614+
"type": {
615+
"name": "string"
616+
},
617+
"required": false,
618+
"description": ""
619+
},
620+
"children": {
621+
"type": {
622+
"name": "node"
623+
},
624+
"required": false,
625+
"description": ""
626+
}
627+
}
628+
},
629+
"src/components/Location.react.js": {
630+
"description": "",
631+
"methods": [
632+
{
633+
"name": "updateLocation",
634+
"docblock": null,
635+
"modifiers": [],
636+
"params": [
637+
{
638+
"name": "props",
639+
"type": null
640+
}
641+
],
642+
"returns": null
643+
}
644+
],
645+
"props": {
646+
"id": {
647+
"type": {
648+
"name": "string"
649+
},
650+
"required": true,
651+
"description": ""
652+
},
653+
"pathname": {
654+
"type": {
655+
"name": "string"
656+
},
657+
"required": false,
658+
"description": ""
659+
},
660+
"refresh": {
661+
"type": {
662+
"name": "bool"
663+
},
664+
"required": false,
665+
"description": "",
666+
"defaultValue": {
667+
"value": "true",
668+
"computed": false
669+
}
670+
}
671+
}
672+
},
569673
"src/components/Markdown.react.js": {
570674
"description": "A component that renders Markdown text as specified by the\nCommonMark spec.",
571675
"methods": [],
@@ -860,6 +964,27 @@
860964
"required": false,
861965
"description": "If true, the slider will be vertical"
862966
},
967+
"updatemode": {
968+
"type": {
969+
"name": "enum",
970+
"value": [
971+
{
972+
"value": "'mouseup'",
973+
"computed": false
974+
},
975+
{
976+
"value": "'drag'",
977+
"computed": false
978+
}
979+
]
980+
},
981+
"required": false,
982+
"description": "Determines when the component should update\nits value. If `mouseup`, then the slider\nwill only trigger its value when the user has\nfinished dragging the slider. If `drag`, then\nthe slider will update its value continuously\nas it is being dragged.\nOnly use `drag` if your updates are fast.",
983+
"defaultValue": {
984+
"value": "'mouseup'",
985+
"computed": false
986+
}
987+
},
863988
"fireEvent": {
864989
"type": {
865990
"name": "func"
@@ -890,7 +1015,7 @@
8901015
}
8911016
},
8921017
"src/components/Slider.react.js": {
893-
"description": "A numerical slider with a single handle.",
1018+
"description": "A slider component with a single handle.",
8941019
"methods": [],
8951020
"props": {
8961021
"id": {
@@ -983,6 +1108,27 @@
9831108
"required": false,
9841109
"description": "If true, the slider will be vertical"
9851110
},
1111+
"updatemode": {
1112+
"type": {
1113+
"name": "enum",
1114+
"value": [
1115+
{
1116+
"value": "'mouseup'",
1117+
"computed": false
1118+
},
1119+
{
1120+
"value": "'drag'",
1121+
"computed": false
1122+
}
1123+
]
1124+
},
1125+
"required": false,
1126+
"description": "Determines when the component should update\nits value. If `mouseup`, then the slider\nwill only trigger its value when the user has\nfinished dragging the slider. If `drag`, then\nthe slider will update its value continuously\nas it is being dragged.\nOnly use `drag` if your updates are fast.",
1127+
"defaultValue": {
1128+
"value": "'mouseup'",
1129+
"computed": false
1130+
}
1131+
},
9861132
"fireEvent": {
9871133
"type": {
9881134
"name": "func"

dash_core_components/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.5.3'
1+
__version__ = '0.6.0'

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dash-core-components",
3-
"version": "0.5.3",
3+
"version": "0.6.0",
44
"description": "Core component suite for Dash",
55
"repository": {
66
"type": "git",

src/components/Checklist.react.js

+50-39
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,67 @@
11
import {append, contains, without} from 'ramda';
2-
import React, {PropTypes} from 'react';
2+
import React, {Component, PropTypes} from 'react';
33

44
/**
55
* Checklist is a component that encapsulates several checkboxes.
66
* The values and labels of the checklist is specified in the `options`
77
* property and the checked items are specified with the `values` property.
88
* Each checkbox is rendered as an input with a surrounding label.
99
*/
10-
export default function Checklist(props) {
11-
const {
12-
fireEvent,
13-
id,
14-
inputClassName,
15-
inputStyle,
16-
labelClassName,
17-
labelStyle,
18-
options,
19-
values,
20-
setProps
21-
} = props;
22-
return (
23-
<div id={id}>
24-
{options.map(option => (
25-
<label
26-
key={option.value}
27-
style={labelStyle}
28-
className={labelClassName}
29-
>
30-
<input
31-
checked={contains(option.value, values)}
32-
className={inputClassName}
33-
disabled={Boolean(option.disabled)}
34-
style={inputStyle}
35-
type="checkbox"
36-
onChange={() => {
37-
if (setProps) {
10+
export default class Checklist extends Component {
11+
constructor(props) {
12+
super(props);
13+
this.state = {values: props.values};
14+
}
15+
16+
componentWillReceiveProps(newProps) {
17+
this.setState({values: newProps.values});
18+
}
19+
20+
render() {
21+
const {
22+
fireEvent,
23+
id,
24+
inputClassName,
25+
inputStyle,
26+
labelClassName,
27+
labelStyle,
28+
options,
29+
setProps
30+
} = this.props;
31+
const {values} = this.state;
32+
33+
return (
34+
<div id={id}>
35+
{options.map(option => (
36+
<label
37+
key={option.value}
38+
style={labelStyle}
39+
className={labelClassName}
40+
>
41+
<input
42+
checked={contains(option.value, values)}
43+
className={inputClassName}
44+
disabled={Boolean(option.disabled)}
45+
style={inputStyle}
46+
type="checkbox"
47+
onChange={() => {
3848
let newValues;
3949
if (contains(option.value, values)) {
4050
newValues = without([option.value], values);
4151
} else {
4252
newValues = append(option.value, values);
4353
}
44-
setProps({values: newValues});
45-
}
46-
if (fireEvent) fireEvent({event: 'change'});
47-
}}
48-
/>
49-
{option.label}
50-
</label>
51-
))}
52-
</div>
53-
);
54+
this.setState({values: newValues});
55+
if (setProps) setProps({values: newValues});
56+
if (fireEvent) fireEvent({event: 'change'});
57+
}}
58+
/>
59+
{option.label}
60+
</label>
61+
))}
62+
</div>
63+
);
64+
}
5465
}
5566

5667
Checklist.propTypes = {

src/components/Dropdown.react.js

+15-3
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,18 @@ const DELIMETER = ',';
1515
* which have the benefit of showing the users all of the items at once.
1616
*/
1717
export default class Dropdown extends Component {
18+
constructor(props) {
19+
super(props);
20+
this.state = {value: props.value};
21+
}
22+
23+
componentWillReceiveProps(newProps) {
24+
this.setState({value: newProps.value});
25+
}
26+
1827
render() {
19-
const {id, fireEvent, multi, options, value, setProps} = this.props;
28+
const {id, fireEvent, multi, options, setProps} = this.props;
29+
const {value} = this.state;
2030
let selectedValue;
2131
if (R.type(value) === 'array') {
2232
selectedValue = value.join(DELIMETER);
@@ -31,9 +41,11 @@ export default class Dropdown extends Component {
3141
onChange={selectedOption => {
3242
if (multi) {
3343
const values = R.pluck('value', selectedOption);
34-
setProps({value: values});
44+
this.setState({value: values});
45+
if (setProps) setProps({value: values});
3546
} else {
36-
setProps({value: selectedOption.value});
47+
this.setState({value: selectedOption.value});
48+
if (setProps) setProps({value: selectedOption.value});
3749
}
3850
if (fireEvent) fireEvent('change');
3951
}}

src/components/Input.react.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,26 @@ import React, {Component, PropTypes} from 'react';
88
* are also supported through separate components.
99
*/
1010
export default class Input extends Component {
11-
render() {
11+
constructor(props) {
12+
super(props);
13+
this.state = {value: props.value};
14+
}
1215

16+
componentWillReceiveProps(nextProps) {
17+
this.setState({value: nextProps.value});
18+
}
19+
20+
render() {
1321
const {
1422
className,
1523
id,
1624
fireEvent,
1725
placeholder,
1826
style,
19-
value,
2027
type,
2128
setProps
2229
} = this.props;
30+
const {value} = this.state;
2331

2432
return (
2533
<input
@@ -30,6 +38,7 @@ export default class Input extends Component {
3038
placeholder={placeholder}
3139
style={style}
3240
onChange={e => {
41+
this.setState({value: e.target.value});
3342
if (setProps) setProps({value: e.target.value});
3443
if (fireEvent) fireEvent({event: 'change'});
3544
}}

0 commit comments

Comments
 (0)