Skip to content

Commit 011ded9

Browse files
committed
Add get_props, extras to wrapper, set_props takes path
1 parent 65ffabd commit 011ded9

File tree

6 files changed

+108
-51
lines changed

6 files changed

+108
-51
lines changed

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

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {clone} from 'ramda';
21
import React from 'react';
32
import PropTypes from 'prop-types';
43

@@ -20,26 +19,15 @@ export default class ConfirmDialogProvider extends React.Component {
2019
render() {
2120
const {displayed, id, setProps, children, loading_state} = this.props;
2221

23-
// Will lose the previous onClick of the child
24-
const wrapClick = child => {
25-
const props = clone(child.props);
26-
props._dashprivate_layout.props.onClick = () => {
27-
setProps({displayed: true});
28-
};
29-
30-
return React.cloneElement(child, props);
31-
};
32-
3322
return (
3423
<div
3524
id={id}
3625
data-dash-is-loading={
3726
(loading_state && loading_state.is_loading) || undefined
3827
}
28+
onClick={() => setProps({displayed: !displayed})}
3929
>
40-
{Array.isArray(children)
41-
? children.map(wrapClick)
42-
: wrapClick(children)}
30+
{children}
4331
<ConfirmDialog {...this.props} displayed={displayed} />
4432
</div>
4533
);

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

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,6 @@ export default class Tabs extends Component {
135135
super(props);
136136

137137
this.selectHandler = this.selectHandler.bind(this);
138-
this.parseChildrenToArray = this.parseChildrenToArray.bind(this);
139-
this.valueOrDefault = this.valueOrDefault.bind(this);
140138

141139
if (!has('value', this.props)) {
142140
this.props.setProps({
@@ -150,8 +148,12 @@ export default class Tabs extends Component {
150148
return this.props.value;
151149
}
152150
const children = this.parseChildrenToArray();
153-
if (children && children[0].props.children) {
154-
return children[0].props.children.props.value || 'tab-1';
151+
if (children && children.length) {
152+
const firstChildren = window.dash_clientside.get_props(
153+
children[0].props.componentPath,
154+
'value'
155+
);
156+
return firstChildren || 'tab-1';
155157
}
156158
return 'tab-1';
157159
}
@@ -173,6 +175,8 @@ export default class Tabs extends Component {
173175
let EnhancedTabs;
174176
let selectedTab;
175177

178+
const value = this.valueOrDefault();
179+
176180
if (this.props.children) {
177181
const children = this.parseChildrenToArray();
178182

@@ -183,28 +187,16 @@ export default class Tabs extends Component {
183187
// enhance Tab components coming from Dash (as dcc.Tab) with methods needed for handling logic
184188
let childProps;
185189

186-
if (
187-
// disabled is a defaultProp (so it's always set)
188-
// meaning that if it's not set on child.props, the actual
189-
// props we want are lying a bit deeper - which means they
190-
// are coming from Dash
191-
isNil(child.props.disabled) &&
192-
child.props._dashprivate_layout &&
193-
child.props._dashprivate_layout.props
194-
) {
195-
// props are coming from Dash
196-
childProps = child.props._dashprivate_layout.props;
197-
} else {
198-
// else props are coming from React (Demo.react.js, or Tabs.test.js)
199-
childProps = child.props;
200-
}
190+
childProps = window.dash_clientside.get_props(
191+
child.props.componentPath
192+
);
201193

202194
if (!childProps.value) {
203195
childProps = {...childProps, value: `tab-${index + 1}`};
204196
}
205197

206198
// check if this child/Tab is currently selected
207-
if (childProps.value === this.valueOrDefault()) {
199+
if (childProps.value === value) {
208200
selectedTab = child;
209201
}
210202

@@ -213,7 +205,7 @@ export default class Tabs extends Component {
213205
key={index}
214206
id={childProps.id}
215207
label={childProps.label}
216-
selected={this.valueOrDefault() === childProps.value}
208+
selected={value === childProps.value}
217209
selectHandler={this.selectHandler}
218210
className={childProps.className}
219211
style={childProps.style}

dash/dash-renderer/src/APIController.react.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,33 @@ const UnconnectedContainer = props => {
7171
layoutRequest.status &&
7272
!includes(layoutRequest.status, [STATUS.OK, 'loading'])
7373
) {
74-
content = <div className='_dash-error'>Error loading layout</div>;
74+
if (config.ui) {
75+
content = (
76+
<div
77+
dangerouslySetInnerHTML={{__html: layoutRequest.content}}
78+
></div>
79+
);
80+
} else {
81+
content = <div className='_dash-error'>Error loading layout</div>;
82+
}
7583
} else if (
7684
errorLoading ||
7785
(dependenciesRequest.status &&
7886
!includes(dependenciesRequest.status, [STATUS.OK, 'loading']))
7987
) {
80-
content = <div className='_dash-error'>Error loading dependencies</div>;
88+
if (config.ui) {
89+
content = (
90+
<div
91+
dangerouslySetInnerHTML={{
92+
__html: dependenciesRequest.content
93+
}}
94+
></div>
95+
);
96+
} else {
97+
content = (
98+
<div className='_dash-error'>Error loading dependencies</div>
99+
);
100+
}
81101
} else if (appLifecycle === getAppState('HYDRATED')) {
82102
renderedTree.current = true;
83103

@@ -90,15 +110,15 @@ const UnconnectedContainer = props => {
90110
) : (
91111
<DashWrapper
92112
_dashprivate_error={error}
93-
_dashprivate_path={[i]}
113+
componentPath={[i]}
94114
key={i}
95115
/>
96116
)
97117
)
98118
) : (
99119
<DashWrapper
100120
_dashprivate_error={error}
101-
_dashprivate_path={[]}
121+
componentPath={[]}
102122
/>
103123
)}
104124
</>

dash/dash-renderer/src/actions/api.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,16 @@ export default function apiThunk(endpoint, method, store, id, body) {
116116
return json;
117117
});
118118
}
119+
const content = await res.text();
119120
logWarningOnce(
120121
'Response is missing header: content-type: application/json'
121122
);
122123
return dispatch({
123124
type: store,
124125
payload: {
125126
id,
126-
status: res.status
127+
status: res.status,
128+
content
127129
}
128130
});
129131
} catch (err) {

dash/dash-renderer/src/utils/clientsideFunctions.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
1+
import {concat, path} from 'ramda';
12
import {updateProps, notifyObservers} from '../actions/index';
23
import {getPath} from '../actions/paths';
34

4-
const set_props = (id: string | object, props: {[k: string]: any}) => {
5+
/**
6+
* Set the props of a dash component by id or path.
7+
*
8+
* @param idOrPath Path or id of the dash component to update.
9+
* @param props The props to update.
10+
*/
11+
function set_props(
12+
idOrPath: string | object | string[],
13+
props: {[k: string]: any}
14+
) {
515
const ds = ((window as any).dash_stores =
616
(window as any).dash_stores || []);
717
for (let y = 0; y < ds.length; y++) {
818
const {dispatch, getState} = ds[y];
9-
const {paths} = getState();
10-
const componentPath = getPath(paths, id);
19+
let componentPath;
20+
if (!Array.isArray(idOrPath)) {
21+
const {paths} = getState();
22+
componentPath = getPath(paths, idOrPath);
23+
} else {
24+
componentPath = idOrPath;
25+
}
1126
dispatch(
1227
updateProps({
1328
props,
1429
itempath: componentPath
1530
})
1631
);
17-
dispatch(notifyObservers({id, props}));
32+
dispatch(notifyObservers({id: idOrPath, props}));
1833
}
19-
};
34+
}
2035

2136
// Clean url code adapted from https://github.com/braintree/sanitize-url/blob/main/src/constants.ts
2237
// to allow for data protocol.
@@ -42,7 +57,39 @@ const clean_url = (url: string, fallback = 'about:blank') => {
4257
return url;
4358
};
4459

60+
/**
61+
* Get the dash props from a component path or id.
62+
*
63+
* @param componentPathOrId The path or the id of the component to get the props of.
64+
* @param propPath Additional key to get the property instead of plain props.
65+
* @returns
66+
*/
67+
function get_props(
68+
componentPathOrId: string[] | string,
69+
...propPath: string[]
70+
): any {
71+
const ds = ((window as any).dash_stores =
72+
(window as any).dash_stores || []);
73+
for (let y = 0; y < ds.length; y++) {
74+
const {paths, layout} = ds[y].getState();
75+
let componentPath;
76+
if (!Array.isArray(componentPathOrId)) {
77+
componentPath = getPath(paths, componentPathOrId);
78+
} else {
79+
componentPath = componentPathOrId;
80+
}
81+
const props = path(
82+
concat(componentPath, ['props', ...propPath]),
83+
layout
84+
);
85+
if (props !== undefined) {
86+
return props;
87+
}
88+
}
89+
}
90+
4591
const dc = ((window as any).dash_clientside =
4692
(window as any).dash_clientside || {});
4793
dc['set_props'] = set_props;
4894
dc['clean_url'] = dc['clean_url'] === undefined ? clean_url : dc['clean_url'];
95+
dc['get_props'] = get_props;

dash/dash-renderer/src/wrapper/DashWrapper.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,20 @@ import {
3434
import CheckedComponent from './CheckedComponent';
3535

3636
type DashWrapperProps = {
37-
_dashprivate_path: DashLayoutPath;
37+
/**
38+
* Path of the component in the layout.
39+
*/
40+
componentPath: DashLayoutPath;
41+
/**
42+
* extras props to be merged with the dash props from the store.
43+
*/
44+
extras?: any;
3845
_dashprivate_error?: any;
3946
};
4047

4148
function DashWrapper({
42-
_dashprivate_path: componentPath,
49+
componentPath,
50+
extras,
4351
_dashprivate_error
4452
}: DashWrapperProps) {
4553
const dispatch = useDispatch();
@@ -126,7 +134,7 @@ function DashWrapper({
126134
key
127135
}
128136
_dashprivate_error={_dashprivate_error}
129-
_dashprivate_path={containerPath}
137+
componentPath={containerPath}
130138
/>
131139
);
132140
},
@@ -165,7 +173,7 @@ function DashWrapper({
165173
const extraProps = {
166174
loading_state,
167175
setProps,
168-
_dashprivate_layout: component
176+
...extras
169177
};
170178

171179
const element = useMemo(() => Registry.resolve(component), [component]);
@@ -418,7 +426,7 @@ function DashWrapper({
418426
export default memo(
419427
DashWrapper,
420428
(prevProps, nextProps) =>
421-
JSON.stringify(prevProps._dashprivate_path) ===
422-
JSON.stringify(nextProps._dashprivate_path) &&
429+
JSON.stringify(prevProps.componentPath) ===
430+
JSON.stringify(nextProps.componentPath) &&
423431
prevProps._dashprivate_error === nextProps._dashprivate_error
424432
);

0 commit comments

Comments
 (0)