Skip to content

Commit 8ae3b22

Browse files
committed
adding support for sending hashes to components as props
adjusted the components as props test to work with redrawing
1 parent cbc93b9 commit 8ae3b22

File tree

6 files changed

+377
-200
lines changed

6 files changed

+377
-200
lines changed

dash/dash-renderer/src/actions/callbacks.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
CallbackResponseData,
3535
SideUpdateOutput
3636
} from '../types/callbacks';
37+
import {getComponentLayout} from '../wrapper/wrapping';
3738
import {isMultiValued, stringifyId, isMultiOutputProp} from './dependencies';
3839
import {urlBase} from './utils';
3940
import {getCSRFHeader, dispatchError} from '.';
@@ -358,10 +359,13 @@ function updateComponent(component_id: any, props: any, cb: ICallbackPayload) {
358359
// error.
359360
return;
360361
}
362+
const component = getComponentLayout(componentPath, _state);
361363
dispatch(
362364
updateProps({
363365
props,
364-
itempath: componentPath
366+
itempath: componentPath,
367+
component,
368+
config
365369
})
366370
);
367371
dispatch(notifyObservers({id: component_id, props}));

dash/dash-renderer/src/observers/executedCallbacks.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {ICallback, IStoredCallback} from '../types/callbacks';
3434

3535
import {updateProps, setPaths, handleAsyncError} from '../actions';
3636
import {getPath, computePaths} from '../actions/paths';
37+
import {getComponentLayout} from '../wrapper/wrapping';
3738

3839
import {applyPersistence, prunePersistence} from '../persistence';
3940
import {IStoreObserverDefinition} from '../StoreObserver';
@@ -46,7 +47,7 @@ const observer: IStoreObserverDefinition<IStoreState> = {
4647

4748
function applyProps(id: any, updatedProps: any) {
4849
const _state = getState();
49-
const {layout, paths} = _state;
50+
const {layout, paths, config} = _state;
5051
const itempath = getPath(paths, id);
5152
if (!itempath) {
5253
return false;
@@ -64,12 +65,14 @@ const observer: IStoreObserverDefinition<IStoreState> = {
6465
// In case the update contains whole components, see if any of
6566
// those components have props to update to persist user edits.
6667
const {props} = applyPersistence({props: updatedProps}, dispatch);
67-
68+
const component = getComponentLayout(itempath, _state);
6869
dispatch(
6970
updateProps({
7071
itempath,
7172
props,
72-
source: 'response'
73+
source: 'response',
74+
component,
75+
config
7376
})
7477
);
7578

dash/dash-renderer/src/reducers/reducer.js

Lines changed: 166 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,177 @@ export const apiRequests = [
2727
'loginRequest'
2828
];
2929

30+
function handleChildrenPropsUpdate({
31+
component,
32+
config,
33+
action,
34+
actionPath,
35+
state
36+
}) {
37+
const childrenProps = pathOr(
38+
[],
39+
['children_props', component?.namespace, component?.type],
40+
config
41+
);
42+
43+
// Ensure "children" is always considered
44+
if (!childrenProps.includes('children[]')) {
45+
childrenProps.push('children[]');
46+
}
47+
48+
childrenProps.forEach(childrenProp => {
49+
const segments = childrenProp.split('.');
50+
const includesArray = childrenProp.includes('[]');
51+
const includesObject = childrenProp.includes('{}');
52+
53+
const cleanSegments = segments.map(s =>
54+
s.replace('[]', '').replace('{}', '')
55+
);
56+
57+
const getFrontBack = () => {
58+
const front = [];
59+
const back = [];
60+
let found = false;
61+
62+
for (const segment of segments) {
63+
const clean = segment.replace('{}', '').replace('[]', '');
64+
if (
65+
!found &&
66+
(segment.includes('[]') || segment.includes('{}'))
67+
) {
68+
found = true;
69+
front.push(clean);
70+
} else if (found) {
71+
back.push(clean);
72+
} else {
73+
front.push(clean);
74+
}
75+
}
76+
77+
return [front, back];
78+
};
79+
80+
const [frontPath, backPath] = getFrontBack();
81+
const basePath = [...actionPath, 'props', ...frontPath];
82+
const propRoot = pathOr({}, ['payload', 'props'], action);
83+
84+
if (!(cleanSegments[0] in propRoot)) return;
85+
86+
const _fullValue = path(cleanSegments, propRoot);
87+
const fullValues = Array.isArray(_fullValue)
88+
? _fullValue
89+
: [_fullValue];
90+
91+
fullValues.forEach((fullValue, y) => {
92+
if (includesArray) {
93+
if (Array.isArray(fullValue)) {
94+
fullValue.forEach((el, i) => {
95+
let value = el;
96+
if (includesObject && backPath.length) {
97+
value = path(backPath, el);
98+
}
99+
100+
if (value) {
101+
const itempath = [...basePath, i, ...backPath];
102+
state = adjustHashes(state, {
103+
payload: {
104+
itempath,
105+
props: value?.props,
106+
component: value,
107+
config
108+
}
109+
});
110+
}
111+
});
112+
} else if (
113+
fullValue &&
114+
typeof fullValue === 'object' &&
115+
!('props' in fullValue)
116+
) {
117+
Object.entries(fullValue).forEach(([key, value]) => {
118+
const finalVal = backPath.length
119+
? path(backPath, value)
120+
: value;
121+
if (finalVal) {
122+
const itempath = [...basePath, y, key, ...backPath];
123+
state = adjustHashes(state, {
124+
payload: {
125+
itempath,
126+
props: finalVal?.props,
127+
component: finalVal,
128+
config
129+
}
130+
});
131+
}
132+
});
133+
} else if (fullValue) {
134+
const itempath = [...basePath, ...backPath];
135+
if (Array.isArray(_fullValue)) {
136+
itempath.push(y);
137+
}
138+
state = adjustHashes(state, {
139+
payload: {
140+
itempath,
141+
props: fullValue?.props,
142+
component: fullValue,
143+
config
144+
}
145+
});
146+
}
147+
} else if (includesObject) {
148+
if (fullValue && typeof fullValue === 'object') {
149+
Object.entries(fullValue).forEach(([key, value]) => {
150+
const finalVal = backPath.length
151+
? path(backPath, value)
152+
: value;
153+
if (finalVal) {
154+
const itempath = [...basePath, key, ...backPath];
155+
state = adjustHashes(state, {
156+
payload: {
157+
itempath,
158+
props: finalVal?.props,
159+
component: finalVal,
160+
config
161+
}
162+
});
163+
}
164+
});
165+
}
166+
} else {
167+
if (fullValue) {
168+
const itempath = [...actionPath, 'props', ...cleanSegments];
169+
if (Array.isArray(_fullValue)) {
170+
itempath.push(y);
171+
}
172+
state = adjustHashes(state, {
173+
payload: {
174+
itempath,
175+
props: fullValue?.props,
176+
component: fullValue,
177+
config
178+
}
179+
});
180+
}
181+
}
182+
});
183+
});
184+
185+
return state;
186+
}
187+
30188
function adjustHashes(state, action) {
31189
const actionPath = action.payload.itempath;
32190
const strPath = stringifyPath(actionPath);
33191
const prev = pathOr(0, [strPath], state);
192+
const {component, config} = action.payload;
34193
state = assoc(strPath, prev + 1, state);
35-
36-
// check if children was adjusted
37-
if ('children' in pathOr({}, ['payload', 'props'], action)) {
38-
const children = pathOr({}, ['payload', 'props', 'children'], action);
39-
const basePath = [...actionPath, 'props', 'children'];
40-
if (Array.isArray(children)) {
41-
children.forEach((v, i) => {
42-
state = adjustHashes(state, {
43-
payload: {itempath: [...basePath, i], props: v?.props}
44-
});
45-
});
46-
} else if (children) {
47-
state = adjustHashes(state, {
48-
payload: {itempath: [...basePath], props: children?.props}
49-
});
50-
}
51-
}
194+
state = handleChildrenPropsUpdate({
195+
component,
196+
config,
197+
action,
198+
actionPath,
199+
state
200+
});
52201
return state;
53202
}
54203

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {updateProps, notifyObservers} from '../actions/index';
22
import {getPath} from '../actions/paths';
33
import {getStores} from './stores';
4+
import {getComponentLayout} from '../wrapper/wrapping';
45

56
/**
67
* Set the props of a dash component by id or path.
@@ -17,16 +18,19 @@ function set_props(
1718
const {dispatch, getState} = ds[y];
1819
let componentPath;
1920
const _state = getState();
20-
const {paths} = _state;
21+
const {paths, config} = _state;
2122
if (!Array.isArray(idOrPath)) {
2223
componentPath = getPath(paths, idOrPath);
2324
} else {
2425
componentPath = idOrPath;
2526
}
27+
const component = getComponentLayout(componentPath, _state);
2628
dispatch(
2729
updateProps({
2830
props,
29-
itempath: componentPath
31+
itempath: componentPath,
32+
component,
33+
config
3034
})
3135
);
3236
dispatch(notifyObservers({id: idOrPath, props}));

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ function DashWrapper({
111111
dispatch(
112112
updateProps({
113113
props: changedProps,
114-
itempath: componentPath
114+
itempath: componentPath,
115+
component,
116+
config
115117
})
116118
);
117119
});

0 commit comments

Comments
 (0)