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

Commit 8171a52

Browse files
Merge pull request #284 from plotly/282-tabs-content-fix
Don't break Tabs if no Tab is selected
2 parents 557d2fd + 0158725 commit 8171a52

File tree

9 files changed

+320
-92
lines changed

9 files changed

+320
-92
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
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.28.2] - 2018-09-06
6+
### Fixed
7+
- Fixed bug in Tabs component where initial tab content wasn't rendering, [#282](https://github.com/plotly/dash-core-components/issues/282)
8+
- Fixed bug in Tabs component where no default Tab is selected if Tabs.value is empty
9+
510
## [0.28.1] - 2018-08-29
611
### Changed
712
- `candlestick` and `OHLC` charts are now plotted using the `Plotly.react` method instead of the `Plotly.newPlot` method.

dash_core_components/Dropdown.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,17 @@ class Dropdown(Component):
3131
- multi (boolean; optional): If true, the user can select multiple values
3232
- placeholder (string; optional): The grey, default text shown when no option is selected
3333
- searchable (boolean; optional): Whether to enable the searching feature or not
34+
- style (dict; optional)
3435
3536
Available events: 'change'"""
3637
@_explicitize_args
37-
def __init__(self, id=Component.UNDEFINED, options=Component.UNDEFINED, value=Component.UNDEFINED, className=Component.UNDEFINED, clearable=Component.UNDEFINED, disabled=Component.UNDEFINED, multi=Component.UNDEFINED, placeholder=Component.UNDEFINED, searchable=Component.UNDEFINED, **kwargs):
38-
self._prop_names = ['id', 'options', 'value', 'className', 'clearable', 'disabled', 'multi', 'placeholder', 'searchable']
38+
def __init__(self, id=Component.UNDEFINED, options=Component.UNDEFINED, value=Component.UNDEFINED, className=Component.UNDEFINED, clearable=Component.UNDEFINED, disabled=Component.UNDEFINED, multi=Component.UNDEFINED, placeholder=Component.UNDEFINED, searchable=Component.UNDEFINED, style=Component.UNDEFINED, **kwargs):
39+
self._prop_names = ['id', 'options', 'value', 'className', 'clearable', 'disabled', 'multi', 'placeholder', 'searchable', 'style']
3940
self._type = 'Dropdown'
4041
self._namespace = 'dash_core_components'
4142
self._valid_wildcard_attributes = []
4243
self.available_events = ['change']
43-
self.available_properties = ['id', 'options', 'value', 'className', 'clearable', 'disabled', 'multi', 'placeholder', 'searchable']
44+
self.available_properties = ['id', 'options', 'value', 'className', 'clearable', 'disabled', 'multi', 'placeholder', 'searchable', 'style']
4445
self.available_wildcard_properties = []
4546

4647
_explicit_args = kwargs.pop('_explicit_args')

dash_core_components/bundle.js

+33-61
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dash_core_components/metadata.json

+44-2
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,13 @@
658658
"value": "'singledate'",
659659
"computed": false
660660
}
661+
},
662+
"fireEvent": {
663+
"type": {
664+
"name": "func"
665+
},
666+
"required": false,
667+
"description": ""
661668
}
662669
}
663670
},
@@ -950,6 +957,13 @@
950957
},
951958
"required": false,
952959
"description": "Dash-assigned callback that gets fired when the value changes."
960+
},
961+
"fireEvent": {
962+
"type": {
963+
"name": "func"
964+
},
965+
"required": false,
966+
"description": ""
953967
}
954968
}
955969
},
@@ -1075,6 +1089,13 @@
10751089
"required": false,
10761090
"description": "Dash-assigned callback that gets fired when the input changes"
10771091
},
1092+
"style": {
1093+
"type": {
1094+
"name": "object"
1095+
},
1096+
"required": false,
1097+
"description": ""
1098+
},
10781099
"dashEvents": {
10791100
"type": {
10801101
"name": "enum",
@@ -1087,6 +1108,13 @@
10871108
},
10881109
"required": false,
10891110
"description": ""
1111+
},
1112+
"fireEvent": {
1113+
"type": {
1114+
"name": "func"
1115+
},
1116+
"required": false,
1117+
"description": ""
10901118
}
10911119
}
10921120
},
@@ -2002,6 +2030,13 @@
20022030
"value": "true",
20032031
"computed": false
20042032
}
2033+
},
2034+
"setProps": {
2035+
"type": {
2036+
"name": "func"
2037+
},
2038+
"required": false,
2039+
"description": ""
20052040
}
20062041
}
20072042
},
@@ -2748,6 +2783,13 @@
27482783
"description": "A Dash component that lets you render pages with tabs - the Tabs component's children\ncan be dcc.Tab components, which can hold a label that will be displayed as a tab, and can in turn hold\nchildren components that will be that tab's content.",
27492784
"displayName": "Tabs",
27502785
"methods": [
2786+
{
2787+
"name": "parseChildrenToArray",
2788+
"docblock": null,
2789+
"modifiers": [],
2790+
"params": [],
2791+
"returns": null
2792+
},
27512793
{
27522794
"name": "selectHandler",
27532795
"docblock": null,
@@ -2864,7 +2906,7 @@
28642906
"required": false,
28652907
"description": "Holds the colors used by the Tabs and Tab components. If you set these, you should specify colors for all properties, so:\ncolors: {\n border: '#d6d6d6',\n primary: '#1975FA',\n background: '#f9f9f9'\n }",
28662908
"defaultValue": {
2867-
"value": "{\n border: '#d6d6d6',\n primary: '#1975FA',\n background: '#f9f9f9'\n}",
2909+
"value": "{\n border: '#d6d6d6',\n primary: '#1975FA',\n background: '#f9f9f9',\n}",
28682910
"computed": false
28692911
}
28702912
}
@@ -3331,4 +3373,4 @@
33313373
}
33323374
}
33333375
}
3334-
}
3376+
}

dash_core_components/package.json

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"name": "dash-core-components",
3+
"version": "0.28.1",
4+
"description": "Core component suite for Dash",
5+
"repository": {
6+
"type": "git",
7+
"url": "git://github.com/plotly/dash-core-components.git"
8+
},
9+
"main": "src/index.js",
10+
"scripts": {
11+
"build-dev": "builder run clean-lib && builder run extract-metadata && builder run generate-python-classes && webpack -w --config=./config/webpack/webpack.config.dev.js",
12+
"build-dist": "builder run clean-lib && builder run extract-metadata && builder run generate-python-classes && cross-env NODE_ENV=production webpack --config=./config/webpack/webpack.config.dist.js",
13+
"copy-lib": "cp -f lib/* dash_core_components",
14+
"generate-python-classes": "python -c \"import dash; dash.development.component_loader.generate_classes('dash_core_components', 'dash_core_components/metadata.json');\"",
15+
"install-local": "npm run copy-lib && python setup.py install",
16+
"prepublish": "npm test && builder run build-dist && npm run copy-lib",
17+
"publish-all": "npm publish && python setup.py sdist upload",
18+
"publish-pypi": "npm run prepublish && python setup.py sdist && twine upload --sign --skip-existing",
19+
"start": "./node_modules/.bin/builder run build-dev",
20+
"test": "eslint src",
21+
"test-watch": "./node_modules/.bin/builder run test-frontend-watch",
22+
"test-debug": "./node_modules/.bin/builder run test-frontend-debug",
23+
"uninstall-local": "pip uninstall dash-core-components -y",
24+
"build:js": "webpack --mode production",
25+
"build:py": "node ./extract-meta src/components > dash_core_components/metadata.json && cp package.json dash_core_components && npm run generate-python-classes"
26+
},
27+
"author": "Chris Parmer <[email protected]>",
28+
"license": "MIT",
29+
"dependencies": {
30+
"babel-core": "^6.26.3",
31+
"babel-loader": "^7.1.4",
32+
"babel-preset-env": "^1.7.0",
33+
"babel-preset-react": "^6.24.1",
34+
"babel-eslint": "^8.2.6",
35+
"builder": "3.2.2",
36+
"copyfiles": "^2.0.0",
37+
"cross-env": "^5.2.0",
38+
"css-loader": "^0.28.11",
39+
"dash-components-archetype": "^0.3.0-rc1",
40+
"eslint": "^5.4.0",
41+
"eslint-plugin-react": "^7.11.1",
42+
"moment": "^2.20.1",
43+
"prop-types": "^15.6.0",
44+
"radium": "^0.19.4",
45+
"ramda": "^0.24.1",
46+
"rc-slider": "^8.3.1",
47+
"react-addons-shallow-compare": "^15.6.0",
48+
"react-dates": "^12.3.0",
49+
"react-dropzone": "^4.1.2",
50+
"react-markdown": "^2.4.5",
51+
"react-select": "^1.0.0-rc.10",
52+
"react-select-fast-filter-options": "^0.2.2",
53+
"react-syntax-highlighter": "^5.0.0",
54+
"react-virtualized-select": "^3.1.0",
55+
"react-docgen": "^2.20.1",
56+
"style-loader": "^0.21.0",
57+
"styled-jsx": "^2.2.6",
58+
"webpack": "^4.8.3",
59+
"webpack-cli": "^2.1.3",
60+
"webpack-serve": "^1.0.2",
61+
"enzyme": "^2.4.1",
62+
"eslint-config-prettier": "^3.0.1",
63+
"prettier": "^1.14.2"
64+
},
65+
"devDependencies": {
66+
"component-playground": "^2.0.0",
67+
"dash-components-archetype-dev": "^0.3.0-rc1",
68+
"enzyme": "^2.4.1"
69+
},
70+
"peerDependencies": {
71+
"react": "^15.4.0 || ^16.0.0",
72+
"react-dom": "^15.4.0 || ^16.0.0"
73+
}
74+
}

dash_core_components/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.28.1'
1+
__version__ = '0.28.2'

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dash-core-components",
3-
"version": "0.28.1",
3+
"version": "0.28.2",
44
"description": "Core component suite for Dash",
55
"repository": {
66
"type": "git",
@@ -10,7 +10,7 @@
1010
"scripts": {
1111
"build-dev": "builder run clean-lib && builder run extract-metadata && builder run generate-python-classes && webpack -w --config=./config/webpack/webpack.config.dev.js",
1212
"build-dist": "builder run clean-lib && builder run extract-metadata && builder run generate-python-classes && cross-env NODE_ENV=production webpack --config=./config/webpack/webpack.config.dist.js",
13-
"copy-lib": "copyfiles -f lib/* dash_core_components",
13+
"copy-lib": "cp -f lib/* dash_core_components",
1414
"generate-python-classes": "python -c \"import dash; dash.development.component_loader.generate_classes('dash_core_components', 'dash_core_components/metadata.json');\"",
1515
"install-local": "npm run copy-lib && python setup.py install",
1616
"prepublish": "npm test && builder run build-dist && npm run copy-lib",
@@ -22,7 +22,7 @@
2222
"test-debug": "./node_modules/.bin/builder run test-frontend-debug",
2323
"uninstall-local": "pip uninstall dash-core-components -y",
2424
"build:js": "webpack --mode production",
25-
"build:py": "node ./extract-meta src/components > dash_core_components/metadata.json && copyfiles package.json dash_core_components && npm run generate-python-classes"
25+
"build:py": "node ./extract-meta src/components > dash_core_components/metadata.json && cp package.json dash_core_components && npm run generate-python-classes"
2626
},
2727
"author": "Chris Parmer <[email protected]>",
2828
"license": "MIT",

src/components/Tabs.react.js

+50-22
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const EnhancedTab = ({
1818
disabled_className,
1919
mobile_breakpoint,
2020
amountOfTabs,
21-
colors
21+
colors,
2222
}) => {
2323
let tabStyle = style;
2424
if (disabled) {
@@ -110,50 +110,78 @@ const EnhancedTab = ({
110110
export default class Tabs extends Component {
111111
constructor(props) {
112112
super(props);
113-
this.state = {
114-
selected: this.props.value || 'tab-1'
115-
};
116113

117114
this.selectHandler = this.selectHandler.bind(this);
115+
this.parseChildrenToArray = this.parseChildrenToArray.bind(this);
116+
117+
this.parseChildrenToArray();
118+
119+
if (!this.props.value) {
120+
// if no value specified on Tabs component, set it to the first child's (which should be a Tab component) value
121+
const value =
122+
this.props.children[0].props.children.props.value || 'tab-1';
123+
this.state = {
124+
selected: value,
125+
};
126+
if (this.props.setProps) {
127+
// updating the prop in Dash is necessary so that callbacks work
128+
this.props.setProps({
129+
value: value,
130+
});
131+
}
132+
} else {
133+
this.state = {
134+
selected: this.props.value,
135+
};
136+
}
137+
}
138+
parseChildrenToArray() {
139+
if (this.props.children && !R.is(Array, this.props.children)) {
140+
// if dcc.Tabs.children contains just one single element, it gets passed as an object
141+
// instead of an array - so we put in in a array ourselves!
142+
this.props.children = [this.props.children];
143+
}
118144
}
119145
selectHandler(value) {
120146
this.setState({
121-
selected: value
147+
selected: value,
122148
});
123149
if (this.props.setProps) {
124150
this.props.setProps({value: value});
125151
}
126152
}
127153
componentWillReceiveProps(newProps) {
128154
const value = newProps.value;
129-
this.setState({
130-
selected: value
131-
});
155+
if (typeof value !== 'undefined') {
156+
this.setState({
157+
selected: value,
158+
});
159+
}
132160
}
133161
render() {
134162
let EnhancedTabs;
135163
let selectedTab;
136164
let selectedTabContent;
137165

138166
if (this.props.children) {
139-
if (!R.is(Array, this.props.children)) {
140-
// if dcc.Tabs.children contains just one single element, it gets passed as an object
141-
// instead of an array - so we put in in a array ourselves!
142-
this.props.children = [this.props.children];
143-
}
167+
this.parseChildrenToArray();
144168

145169
const amountOfTabs = this.props.children.length;
146170

171+
window.console.log('this.props.children', this.props.children);
147172
EnhancedTabs = this.props.children.map((child, index) => {
148173
// TODO: handle components that are not dcc.Tab components (throw error)
149174
// enhance Tab components coming from Dash (as dcc.Tab) with methods needed for handling logic
150175
let childProps;
151176

177+
window.console.log('child', child);
178+
152179
if (child.props.children) {
153180
// if props appears on .children, props are coming from Dash
154181
childProps = child.props.children.props;
155182
} else {
156183
// else props are coming from React (Demo.react.js)
184+
window.console.log('child props', child.props);
157185
childProps = child.props;
158186
}
159187

@@ -186,7 +214,9 @@ export default class Tabs extends Component {
186214
selectedTab = this.props.children.filter(child => {
187215
return child.props.children.props.value === this.state.selected;
188216
});
189-
selectedTabContent = selectedTab[0].props.children;
217+
if ('props' in selectedTab[0]) {
218+
selectedTabContent = selectedTab[0].props.children;
219+
}
190220
}
191221

192222
const tabContainerClass = this.props.vertical
@@ -245,13 +275,11 @@ export default class Tabs extends Component {
245275
border-bottom: none;
246276
}
247277
:global(.tab-container--vert .tab:last-of-type) {
248-
border-bottom: 1px solid
249-
${this.props.colors.border} !important;
278+
border-bottom: 1px solid ${this.props.colors.border} !important;
250279
}
251280
:global(.tab-container--vert .tab--selected) {
252281
border: 1px solid ${this.props.colors.border};
253-
border-left: 2px solid
254-
${this.props.colors.primary};
282+
border-left: 2px solid ${this.props.colors.primary};
255283
border-right: none;
256284
}
257285
@@ -271,8 +299,8 @@ Tabs.defaultProps = {
271299
colors: {
272300
border: '#d6d6d6',
273301
primary: '#1975FA',
274-
background: '#f9f9f9'
275-
}
302+
background: '#f9f9f9',
303+
},
276304
};
277305

278306
Tabs.propTypes = {
@@ -344,6 +372,6 @@ Tabs.propTypes = {
344372
colors: PropTypes.shape({
345373
border: PropTypes.string,
346374
primary: PropTypes.string,
347-
background: PropTypes.string
348-
})
375+
background: PropTypes.string,
376+
}),
349377
};

0 commit comments

Comments
 (0)