/
index.ts
94 lines (88 loc) · 3.02 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import React from 'react';
import {
FixtureDecoratorId,
createValues,
getFixtureStateProps,
findFixtureStateProps,
createFixtureStateProps,
updateFixtureStateProps,
removeFixtureStateProps
} from 'react-cosmos-shared2/fixtureState';
import { areNodesEqual } from 'react-cosmos-shared2/react';
import { FixtureContext } from '../../FixtureContext';
import { getElementAtPath, getExpectedElementAtPath } from '../shared/nodeTree';
import { findRelevantElementPaths } from '../shared/findRelevantElementPaths';
import { getComponentName } from '../shared/componentName';
import { extendFixtureProps } from './extendFixtureProps';
export function usePropsCapture(
fixture: React.ReactNode,
decoratorId: FixtureDecoratorId
) {
const { fixtureState, setFixtureState } = React.useContext(FixtureContext);
const prevFixtureRef = React.useRef(fixture);
const elPaths = findRelevantElementPaths(fixture);
React.useEffect(() => {
// Create empty fixture state
if (!fixtureState.props && elPaths.length === 0) {
// Make sure not to override any (currently pending) fixture state props
setFixtureState(prevFs => ({ ...prevFs, props: prevFs.props || [] }));
return;
}
// Remove fixture state for removed child elements (likely via HMR)
// FIXME: Also invalidate fixture state at this element path if the
// component type of the corresponding element changed
const fsProps = getFixtureStateProps(fixtureState, decoratorId);
fsProps.forEach(({ elementId }) => {
if (elPaths.indexOf(elementId.elPath) === -1) {
setFixtureState(prevFs => ({
...prevFs,
props: removeFixtureStateProps(fixtureState, elementId)
}));
}
});
elPaths.forEach(elPath => {
const childEl = getExpectedElementAtPath(fixture, elPath);
const elementId = { decoratorId, elPath };
// Component fixture state can be provided before the fixture mounts (eg.
// a previous snapshot of a fixture state or the current fixture state
// from another renderer)
if (!findFixtureStateProps(fixtureState, elementId)) {
const componentName = getComponentName(childEl.type);
setFixtureState(prevFs => ({
...prevFs,
props: createFixtureStateProps({
fixtureState: prevFs,
elementId,
values: createValues(childEl.props),
componentName
})
}));
} else if (
!areNodesEqual(
childEl,
getElementAtPath(prevFixtureRef.current, elPath)
)
) {
setFixtureState(prevFs => ({
...prevFs,
props: updateFixtureStateProps({
fixtureState,
elementId,
values: createValues(childEl.props)
})
}));
}
});
}, [
fixture,
decoratorId,
elPaths,
fixtureState,
fixtureState.props,
setFixtureState
]);
React.useEffect(() => {
prevFixtureRef.current = fixture;
});
return extendFixtureProps(fixture, fixtureState, decoratorId);
}