mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge pull request #168 from bvaughn/collapse-nodes-by-default
Configurable tree default collapsed/expanded behavior
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Store should display Suspense nodes properly in various states: 1: loading 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should display Suspense nodes properly in various states: 1: loading 1`] = `
|
||||
[root]
|
||||
▾ <Wrapper>
|
||||
<Component key="Outside">
|
||||
@@ -8,7 +8,7 @@ exports[`Store should display Suspense nodes properly in various states: 1: load
|
||||
<Loading>
|
||||
`;
|
||||
|
||||
exports[`Store should display Suspense nodes properly in various states: 2: resolved 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should display Suspense nodes properly in various states: 2: resolved 1`] = `
|
||||
[root]
|
||||
▾ <Wrapper>
|
||||
<Component key="Outside">
|
||||
@@ -16,7 +16,7 @@ exports[`Store should display Suspense nodes properly in various states: 2: reso
|
||||
<Component key="Inside">
|
||||
`;
|
||||
|
||||
exports[`Store should filter DOM nodes from the store tree: 1: mount 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should filter DOM nodes from the store tree: 1: mount 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
@@ -25,7 +25,7 @@ exports[`Store should filter DOM nodes from the store tree: 1: mount 1`] = `
|
||||
<Child>
|
||||
`;
|
||||
|
||||
exports[`Store should support collapsing parts of the tree: 1: mount 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support collapsing parts of the tree: 1: mount 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
@@ -36,7 +36,7 @@ exports[`Store should support collapsing parts of the tree: 1: mount 1`] = `
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Store should support collapsing parts of the tree: 2: collapse first Parent 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support collapsing parts of the tree: 2: collapse first Parent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▸ <Parent>
|
||||
@@ -45,14 +45,14 @@ exports[`Store should support collapsing parts of the tree: 2: collapse first Pa
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Store should support collapsing parts of the tree: 3: collapse second Parent 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support collapsing parts of the tree: 3: collapse second Parent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▸ <Parent>
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store should support collapsing parts of the tree: 4: expand first Parent 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support collapsing parts of the tree: 4: expand first Parent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
@@ -61,12 +61,12 @@ exports[`Store should support collapsing parts of the tree: 4: expand first Pare
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store should support collapsing parts of the tree: 5: collapse Grandparent 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support collapsing parts of the tree: 5: collapse Grandparent 1`] = `
|
||||
[root]
|
||||
▸ <Grandparent>
|
||||
`;
|
||||
|
||||
exports[`Store should support collapsing parts of the tree: 6: expand Grandparent 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support collapsing parts of the tree: 6: expand Grandparent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
@@ -75,7 +75,7 @@ exports[`Store should support collapsing parts of the tree: 6: expand Grandparen
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store should support mount and update operations for multiple roots: 1: mount 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support mount and update operations for multiple roots: 1: mount 1`] = `
|
||||
[root]
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
@@ -87,7 +87,7 @@ exports[`Store should support mount and update operations for multiple roots: 1:
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Store should support mount and update operations for multiple roots: 2: update 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support mount and update operations for multiple roots: 2: update 1`] = `
|
||||
[root]
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
@@ -99,7 +99,7 @@ exports[`Store should support mount and update operations for multiple roots: 2:
|
||||
<Child key="0">
|
||||
`;
|
||||
|
||||
exports[`Store should support mount and update operations for multiple roots: 3: unmount B 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support mount and update operations for multiple roots: 3: unmount B 1`] = `
|
||||
[root]
|
||||
▾ <Parent key="A">
|
||||
<Child key="0">
|
||||
@@ -108,9 +108,9 @@ exports[`Store should support mount and update operations for multiple roots: 3:
|
||||
<Child key="3">
|
||||
`;
|
||||
|
||||
exports[`Store should support mount and update operations for multiple roots: 4: unmount A 1`] = ``;
|
||||
exports[`Store collapseNodesByDefault:false should support mount and update operations for multiple roots: 4: unmount A 1`] = ``;
|
||||
|
||||
exports[`Store should support mount and update operations: 1: mount 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support mount and update operations: 1: mount 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
@@ -125,7 +125,7 @@ exports[`Store should support mount and update operations: 1: mount 1`] = `
|
||||
<Child key="3">
|
||||
`;
|
||||
|
||||
exports[`Store should support mount and update operations: 2: update 1`] = `
|
||||
exports[`Store collapseNodesByDefault:false should support mount and update operations: 2: update 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
@@ -136,4 +136,248 @@ exports[`Store should support mount and update operations: 2: update 1`] = `
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Store should support mount and update operations: 3: unmount 1`] = ``;
|
||||
exports[`Store collapseNodesByDefault:false should support mount and update operations: 3: unmount 1`] = ``;
|
||||
|
||||
exports[`Store collapseNodesByDefault:false should support reordering of children: 1: mount 1`] = `
|
||||
[root]
|
||||
▾ <Root>
|
||||
▾ <Foo key="foo">
|
||||
<Component key="0">
|
||||
▾ <Bar key="bar">
|
||||
<Component key="0">
|
||||
<Component key="1">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:false should support reordering of children: 3: reorder children 1`] = `
|
||||
[root]
|
||||
▾ <Root>
|
||||
▾ <Bar key="bar">
|
||||
<Component key="0">
|
||||
<Component key="1">
|
||||
▾ <Foo key="foo">
|
||||
<Component key="0">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:false should support reordering of children: 4: collapse root 1`] = `
|
||||
[root]
|
||||
▸ <Root>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:false should support reordering of children: 5: expand root 1`] = `
|
||||
[root]
|
||||
▾ <Root>
|
||||
▾ <Bar key="bar">
|
||||
<Component key="0">
|
||||
<Component key="1">
|
||||
▾ <Foo key="foo">
|
||||
<Component key="0">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should display Suspense nodes properly in various states: 1: loading 1`] = `
|
||||
[root]
|
||||
▸ <Wrapper>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should display Suspense nodes properly in various states: 2: expand Wrapper and Suspense 1`] = `
|
||||
[root]
|
||||
▾ <Wrapper>
|
||||
<Component key="Outside">
|
||||
▾ <Suspense>
|
||||
<Loading>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should display Suspense nodes properly in various states: 2: resolved 1`] = `
|
||||
[root]
|
||||
▾ <Wrapper>
|
||||
<Component key="Outside">
|
||||
▾ <Suspense>
|
||||
<Component key="Inside">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should filter DOM nodes from the store tree: 1: mount 1`] = `
|
||||
[root]
|
||||
▸ <Grandparent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should filter DOM nodes from the store tree: 2: expand Grandparent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▸ <Parent>
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should filter DOM nodes from the store tree: 3: expand Parent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
<Child>
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding deep parts of the tree: 1: mount 1`] = `
|
||||
[root]
|
||||
▸ <Wrapper>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding deep parts of the tree: 2: expand deepest node 1`] = `
|
||||
[root]
|
||||
▾ <Wrapper>
|
||||
▾ <Nested>
|
||||
▾ <Nested>
|
||||
▾ <Nested>
|
||||
<Nested>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding deep parts of the tree: 3: collapse root 1`] = `
|
||||
[root]
|
||||
▸ <Wrapper>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding deep parts of the tree: 4: expand root 1`] = `
|
||||
[root]
|
||||
▾ <Wrapper>
|
||||
▾ <Nested>
|
||||
▾ <Nested>
|
||||
▾ <Nested>
|
||||
<Nested>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding deep parts of the tree: 5: collapse middle node 1`] = `
|
||||
[root]
|
||||
▾ <Wrapper>
|
||||
▸ <Nested>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding deep parts of the tree: 6: expand middle node 1`] = `
|
||||
[root]
|
||||
▾ <Wrapper>
|
||||
▾ <Nested>
|
||||
▾ <Nested>
|
||||
▾ <Nested>
|
||||
<Nested>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding parts of the tree: 1: mount 1`] = `
|
||||
[root]
|
||||
▸ <Grandparent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding parts of the tree: 2: expand Grandparent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▸ <Parent>
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding parts of the tree: 3: expand first Parent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding parts of the tree: 4: expand second Parent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▾ <Parent>
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
▾ <Parent>
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding parts of the tree: 5: collapse first Parent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▸ <Parent>
|
||||
▾ <Parent>
|
||||
<Child key="0">
|
||||
<Child key="1">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding parts of the tree: 6: collapse second Parent 1`] = `
|
||||
[root]
|
||||
▾ <Grandparent>
|
||||
▸ <Parent>
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support expanding parts of the tree: 7: collapse Grandparent 1`] = `
|
||||
[root]
|
||||
▸ <Grandparent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support mount and update operations for multiple roots: 1: mount 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
[root]
|
||||
▸ <Parent key="B">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support mount and update operations for multiple roots: 2: update 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
[root]
|
||||
▸ <Parent key="B">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support mount and update operations for multiple roots: 3: unmount B 1`] = `
|
||||
[root]
|
||||
▸ <Parent key="A">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support mount and update operations for multiple roots: 4: unmount A 1`] = ``;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support mount and update operations: 1: mount 1`] = `
|
||||
[root]
|
||||
▸ <Parent>
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support mount and update operations: 2: update 1`] = `
|
||||
[root]
|
||||
▸ <Parent>
|
||||
▸ <Parent>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support mount and update operations: 3: unmount 1`] = ``;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support reordering of children: 1: mount 1`] = `
|
||||
[root]
|
||||
▸ <Root>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support reordering of children: 3: reorder children 1`] = `
|
||||
[root]
|
||||
▸ <Root>
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support reordering of children: 4: expand root 1`] = `
|
||||
[root]
|
||||
▾ <Root>
|
||||
▸ <Bar key="bar">
|
||||
▸ <Foo key="foo">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support reordering of children: 5: expand leaves 1`] = `
|
||||
[root]
|
||||
▾ <Root>
|
||||
▾ <Bar key="bar">
|
||||
<Component key="0">
|
||||
<Component key="1">
|
||||
▾ <Foo key="foo">
|
||||
<Component key="0">
|
||||
`;
|
||||
|
||||
exports[`Store collapseNodesByDefault:true should support reordering of children: 6: collapse root 1`] = `
|
||||
[root]
|
||||
▸ <Root>
|
||||
`;
|
||||
|
||||
exports[`Store should not allow a root node to be collapsed: 1: mount 1`] = `
|
||||
[root]
|
||||
<Component>
|
||||
`;
|
||||
|
||||
@@ -31,8 +31,11 @@ env.beforeEach(() => {
|
||||
const agent = new Agent();
|
||||
agent.addBridge(bridge);
|
||||
|
||||
initBackend(global.__REACT_DEVTOOLS_GLOBAL_HOOK__, agent, global);
|
||||
const hook = global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
|
||||
initBackend(hook, agent, global);
|
||||
|
||||
global.agent = agent;
|
||||
global.bridge = bridge;
|
||||
global.store = new Store(bridge);
|
||||
});
|
||||
|
||||
+413
-111
@@ -21,142 +21,444 @@ describe('Store', () => {
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
|
||||
it('should support mount and update operations', () => {
|
||||
const Grandparent = ({ count }) => (
|
||||
<React.Fragment>
|
||||
<Parent count={count} />
|
||||
<Parent count={count} />
|
||||
</React.Fragment>
|
||||
it('should not allow a root node to be collapsed', () => {
|
||||
const Component = () => <div>Hi</div>;
|
||||
|
||||
act(() =>
|
||||
ReactDOM.render(<Component count={4} />, document.createElement('div'))
|
||||
);
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
act(() => ReactDOM.render(<Grandparent count={4} />, container));
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => ReactDOM.render(<Grandparent count={2} />, container));
|
||||
expect(store).toMatchSnapshot('2: update');
|
||||
expect(store.roots).toHaveLength(1);
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(container));
|
||||
expect(store).toMatchSnapshot('3: unmount');
|
||||
const rootID = store.roots[0];
|
||||
|
||||
expect(() => store.toggleIsCollapsed(rootID, true)).toThrow(
|
||||
'Root nodes cannot be collapsed'
|
||||
);
|
||||
});
|
||||
|
||||
it('should support mount and update operations for multiple roots', () => {
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
const containerA = document.createElement('div');
|
||||
const containerB = document.createElement('div');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={3} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={2} />, containerB);
|
||||
describe('collapseNodesByDefault:false', () => {
|
||||
beforeEach(() => {
|
||||
store.collapseNodesByDefault = false;
|
||||
});
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={4} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={1} />, containerB);
|
||||
it('should support mount and update operations', () => {
|
||||
const Grandparent = ({ count }) => (
|
||||
<React.Fragment>
|
||||
<Parent count={count} />
|
||||
<Parent count={count} />
|
||||
</React.Fragment>
|
||||
);
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
act(() => ReactDOM.render(<Grandparent count={4} />, container));
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => ReactDOM.render(<Grandparent count={2} />, container));
|
||||
expect(store).toMatchSnapshot('2: update');
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(container));
|
||||
expect(store).toMatchSnapshot('3: unmount');
|
||||
});
|
||||
expect(store).toMatchSnapshot('2: update');
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerB));
|
||||
expect(store).toMatchSnapshot('3: unmount B');
|
||||
it('should support mount and update operations for multiple roots', () => {
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerA));
|
||||
expect(store).toMatchSnapshot('4: unmount A');
|
||||
});
|
||||
const containerA = document.createElement('div');
|
||||
const containerB = document.createElement('div');
|
||||
|
||||
it('should filter DOM nodes from the store tree', () => {
|
||||
const Grandparent = () => (
|
||||
<div>
|
||||
act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={3} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={2} />, containerB);
|
||||
});
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={4} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={1} />, containerB);
|
||||
});
|
||||
expect(store).toMatchSnapshot('2: update');
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerB));
|
||||
expect(store).toMatchSnapshot('3: unmount B');
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerA));
|
||||
expect(store).toMatchSnapshot('4: unmount A');
|
||||
});
|
||||
|
||||
it('should filter DOM nodes from the store tree', () => {
|
||||
const Grandparent = () => (
|
||||
<div>
|
||||
<div>
|
||||
<Parent />
|
||||
</div>
|
||||
<Parent />
|
||||
</div>
|
||||
<Parent />
|
||||
</div>
|
||||
);
|
||||
const Parent = () => (
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
);
|
||||
const Parent = () => (
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
act(() =>
|
||||
ReactDOM.render(<Grandparent count={4} />, document.createElement('div'))
|
||||
);
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
});
|
||||
|
||||
it('should display Suspense nodes properly in various states', () => {
|
||||
const Loading = () => <div>Loading...</div>;
|
||||
const SuspendingComponent = () => {
|
||||
throw new Promise(() => {});
|
||||
};
|
||||
const Component = () => {
|
||||
return <div>Hello</div>;
|
||||
};
|
||||
const Wrapper = ({ shouldSuspense }) => (
|
||||
<React.Fragment>
|
||||
<Component key="Outside" />
|
||||
<React.Suspense fallback={<Loading />}>
|
||||
{shouldSuspense ? (
|
||||
<SuspendingComponent />
|
||||
) : (
|
||||
<Component key="Inside" />
|
||||
)}
|
||||
</React.Suspense>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
act(() => ReactDOM.render(<Wrapper shouldSuspense={true} />, container));
|
||||
expect(store).toMatchSnapshot('1: loading');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Wrapper shouldSuspense={false} />, container);
|
||||
act(() =>
|
||||
ReactDOM.render(
|
||||
<Grandparent count={4} />,
|
||||
document.createElement('div')
|
||||
)
|
||||
);
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
});
|
||||
|
||||
it('should display Suspense nodes properly in various states', () => {
|
||||
const Loading = () => <div>Loading...</div>;
|
||||
const SuspendingComponent = () => {
|
||||
throw new Promise(() => {});
|
||||
};
|
||||
const Component = () => {
|
||||
return <div>Hello</div>;
|
||||
};
|
||||
const Wrapper = ({ shouldSuspense }) => (
|
||||
<React.Fragment>
|
||||
<Component key="Outside" />
|
||||
<React.Suspense fallback={<Loading />}>
|
||||
{shouldSuspense ? (
|
||||
<SuspendingComponent />
|
||||
) : (
|
||||
<Component key="Inside" />
|
||||
)}
|
||||
</React.Suspense>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
act(() => ReactDOM.render(<Wrapper shouldSuspense={true} />, container));
|
||||
expect(store).toMatchSnapshot('1: loading');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Wrapper shouldSuspense={false} />, container);
|
||||
});
|
||||
expect(store).toMatchSnapshot('2: resolved');
|
||||
});
|
||||
|
||||
it('should support collapsing parts of the tree', () => {
|
||||
const Grandparent = ({ count }) => (
|
||||
<React.Fragment>
|
||||
<Parent count={count} />
|
||||
<Parent count={count} />
|
||||
</React.Fragment>
|
||||
);
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
act(() =>
|
||||
ReactDOM.render(
|
||||
<Grandparent count={2} />,
|
||||
document.createElement('div')
|
||||
)
|
||||
);
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
const grandparentID = store.getElementIDAtIndex(0);
|
||||
const parentOneID = store.getElementIDAtIndex(1);
|
||||
const parentTwoID = store.getElementIDAtIndex(4);
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentOneID, true));
|
||||
expect(store).toMatchSnapshot('2: collapse first Parent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentTwoID, true));
|
||||
expect(store).toMatchSnapshot('3: collapse second Parent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentOneID, false));
|
||||
expect(store).toMatchSnapshot('4: expand first Parent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(grandparentID, true));
|
||||
expect(store).toMatchSnapshot('5: collapse Grandparent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(grandparentID, false));
|
||||
expect(store).toMatchSnapshot('6: expand Grandparent');
|
||||
});
|
||||
|
||||
it('should support reordering of children', () => {
|
||||
const Root = ({ children }) => children;
|
||||
const Component = () => null;
|
||||
|
||||
const Foo = () => [<Component key="0" />];
|
||||
const Bar = () => [<Component key="0" />, <Component key="1" />];
|
||||
const foo = <Foo key="foo" />;
|
||||
const bar = <Bar key="bar" />;
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
act(() => ReactDOM.render(<Root>{[foo, bar]}</Root>, container));
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => ReactDOM.render(<Root>{[bar, foo]}</Root>, container));
|
||||
expect(store).toMatchSnapshot('3: reorder children');
|
||||
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(0), true));
|
||||
expect(store).toMatchSnapshot('4: collapse root');
|
||||
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(0), false));
|
||||
expect(store).toMatchSnapshot('5: expand root');
|
||||
});
|
||||
expect(store).toMatchSnapshot('2: resolved');
|
||||
});
|
||||
|
||||
it('should support collapsing parts of the tree', () => {
|
||||
const Grandparent = ({ count }) => (
|
||||
<React.Fragment>
|
||||
<Parent count={count} />
|
||||
<Parent count={count} />
|
||||
</React.Fragment>
|
||||
);
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
describe('collapseNodesByDefault:true', () => {
|
||||
beforeEach(() => {
|
||||
store.collapseNodesByDefault = true;
|
||||
});
|
||||
|
||||
act(() =>
|
||||
ReactDOM.render(<Grandparent count={2} />, document.createElement('div'))
|
||||
);
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
it('should support mount and update operations', () => {
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
const grandparentID = store.getElementIDAtIndex(0);
|
||||
const parentOneID = store.getElementIDAtIndex(1);
|
||||
const parentTwoID = store.getElementIDAtIndex(4);
|
||||
const container = document.createElement('div');
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentOneID, true));
|
||||
expect(store).toMatchSnapshot('2: collapse first Parent');
|
||||
act(() =>
|
||||
ReactDOM.render(
|
||||
<React.Fragment>
|
||||
<Parent count={1} />
|
||||
<Parent count={3} />
|
||||
</React.Fragment>,
|
||||
container
|
||||
)
|
||||
);
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentTwoID, true));
|
||||
expect(store).toMatchSnapshot('3: collapse second Parent');
|
||||
act(() =>
|
||||
ReactDOM.render(
|
||||
<React.Fragment>
|
||||
<Parent count={2} />
|
||||
<Parent count={1} />
|
||||
</React.Fragment>,
|
||||
container
|
||||
)
|
||||
);
|
||||
expect(store).toMatchSnapshot('2: update');
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentOneID, false));
|
||||
expect(store).toMatchSnapshot('4: expand first Parent');
|
||||
act(() => ReactDOM.unmountComponentAtNode(container));
|
||||
expect(store).toMatchSnapshot('3: unmount');
|
||||
});
|
||||
|
||||
act(() => store.toggleIsCollapsed(grandparentID, true));
|
||||
expect(store).toMatchSnapshot('5: collapse Grandparent');
|
||||
it('should support mount and update operations for multiple roots', () => {
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
act(() => store.toggleIsCollapsed(grandparentID, false));
|
||||
expect(store).toMatchSnapshot('6: expand Grandparent');
|
||||
const containerA = document.createElement('div');
|
||||
const containerB = document.createElement('div');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={3} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={2} />, containerB);
|
||||
});
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Parent key="A" count={4} />, containerA);
|
||||
ReactDOM.render(<Parent key="B" count={1} />, containerB);
|
||||
});
|
||||
expect(store).toMatchSnapshot('2: update');
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerB));
|
||||
expect(store).toMatchSnapshot('3: unmount B');
|
||||
|
||||
act(() => ReactDOM.unmountComponentAtNode(containerA));
|
||||
expect(store).toMatchSnapshot('4: unmount A');
|
||||
});
|
||||
|
||||
it('should filter DOM nodes from the store tree', () => {
|
||||
const Grandparent = () => (
|
||||
<div>
|
||||
<div>
|
||||
<Parent />
|
||||
</div>
|
||||
<Parent />
|
||||
</div>
|
||||
);
|
||||
const Parent = () => (
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
act(() =>
|
||||
ReactDOM.render(
|
||||
<Grandparent count={4} />,
|
||||
document.createElement('div')
|
||||
)
|
||||
);
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(0), false));
|
||||
expect(store).toMatchSnapshot('2: expand Grandparent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(1), false));
|
||||
expect(store).toMatchSnapshot('3: expand Parent');
|
||||
});
|
||||
|
||||
it('should display Suspense nodes properly in various states', () => {
|
||||
const Loading = () => <div>Loading...</div>;
|
||||
const SuspendingComponent = () => {
|
||||
throw new Promise(() => {});
|
||||
};
|
||||
const Component = () => {
|
||||
return <div>Hello</div>;
|
||||
};
|
||||
const Wrapper = ({ shouldSuspense }) => (
|
||||
<React.Fragment>
|
||||
<Component key="Outside" />
|
||||
<React.Suspense fallback={<Loading />}>
|
||||
{shouldSuspense ? (
|
||||
<SuspendingComponent />
|
||||
) : (
|
||||
<Component key="Inside" />
|
||||
)}
|
||||
</React.Suspense>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
act(() => ReactDOM.render(<Wrapper shouldSuspense={true} />, container));
|
||||
expect(store).toMatchSnapshot('1: loading');
|
||||
|
||||
// This test isn't meaningful unless we expand the suspended tree
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(0), false));
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(2), false));
|
||||
expect(store).toMatchSnapshot('2: expand Wrapper and Suspense');
|
||||
|
||||
act(() => {
|
||||
ReactDOM.render(<Wrapper shouldSuspense={false} />, container);
|
||||
});
|
||||
expect(store).toMatchSnapshot('2: resolved');
|
||||
});
|
||||
|
||||
it('should support expanding parts of the tree', () => {
|
||||
const Grandparent = ({ count }) => (
|
||||
<React.Fragment>
|
||||
<Parent count={count} />
|
||||
<Parent count={count} />
|
||||
</React.Fragment>
|
||||
);
|
||||
const Parent = ({ count }) =>
|
||||
new Array(count).fill(true).map((_, index) => <Child key={index} />);
|
||||
const Child = () => <div>Hi!</div>;
|
||||
|
||||
act(() =>
|
||||
ReactDOM.render(
|
||||
<Grandparent count={2} />,
|
||||
document.createElement('div')
|
||||
)
|
||||
);
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
const grandparentID = store.getElementIDAtIndex(0);
|
||||
|
||||
act(() => store.toggleIsCollapsed(grandparentID, false));
|
||||
expect(store).toMatchSnapshot('2: expand Grandparent');
|
||||
|
||||
const parentOneID = store.getElementIDAtIndex(1);
|
||||
const parentTwoID = store.getElementIDAtIndex(2);
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentOneID, false));
|
||||
expect(store).toMatchSnapshot('3: expand first Parent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentTwoID, false));
|
||||
expect(store).toMatchSnapshot('4: expand second Parent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentOneID, true));
|
||||
expect(store).toMatchSnapshot('5: collapse first Parent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(parentTwoID, true));
|
||||
expect(store).toMatchSnapshot('6: collapse second Parent');
|
||||
|
||||
act(() => store.toggleIsCollapsed(grandparentID, true));
|
||||
expect(store).toMatchSnapshot('7: collapse Grandparent');
|
||||
});
|
||||
|
||||
it('should support expanding deep parts of the tree', () => {
|
||||
const Wrapper = ({ forwardedRef }) => (
|
||||
<Nested depth={3} forwardedRef={forwardedRef} />
|
||||
);
|
||||
const Nested = ({ depth, forwardedRef }) =>
|
||||
depth > 0 ? (
|
||||
<Nested depth={depth - 1} forwardedRef={forwardedRef} />
|
||||
) : (
|
||||
<div ref={forwardedRef} />
|
||||
);
|
||||
|
||||
const ref = React.createRef();
|
||||
|
||||
act(() =>
|
||||
ReactDOM.render(
|
||||
<Wrapper forwardedRef={ref} />,
|
||||
document.createElement('div')
|
||||
)
|
||||
);
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
const deepestedNodeID = global.agent.getIDForNode(ref.current);
|
||||
|
||||
act(() => store.toggleIsCollapsed(deepestedNodeID, false));
|
||||
expect(store).toMatchSnapshot('2: expand deepest node');
|
||||
|
||||
const rootID = store.getElementIDAtIndex(0);
|
||||
|
||||
act(() => store.toggleIsCollapsed(rootID, true));
|
||||
expect(store).toMatchSnapshot('3: collapse root');
|
||||
|
||||
act(() => store.toggleIsCollapsed(rootID, false));
|
||||
expect(store).toMatchSnapshot('4: expand root');
|
||||
|
||||
const id = store.getElementIDAtIndex(1);
|
||||
|
||||
act(() => store.toggleIsCollapsed(id, true));
|
||||
expect(store).toMatchSnapshot('5: collapse middle node');
|
||||
|
||||
act(() => store.toggleIsCollapsed(id, false));
|
||||
expect(store).toMatchSnapshot('6: expand middle node');
|
||||
});
|
||||
|
||||
it('should support reordering of children', () => {
|
||||
const Root = ({ children }) => children;
|
||||
const Component = () => null;
|
||||
|
||||
const Foo = () => [<Component key="0" />];
|
||||
const Bar = () => [<Component key="0" />, <Component key="1" />];
|
||||
const foo = <Foo key="foo" />;
|
||||
const bar = <Bar key="bar" />;
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
act(() => ReactDOM.render(<Root>{[foo, bar]}</Root>, container));
|
||||
expect(store).toMatchSnapshot('1: mount');
|
||||
|
||||
act(() => ReactDOM.render(<Root>{[bar, foo]}</Root>, container));
|
||||
expect(store).toMatchSnapshot('3: reorder children');
|
||||
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(0), false));
|
||||
expect(store).toMatchSnapshot('4: expand root');
|
||||
|
||||
act(() => {
|
||||
store.toggleIsCollapsed(store.getElementIDAtIndex(2), false);
|
||||
store.toggleIsCollapsed(store.getElementIDAtIndex(1), false);
|
||||
});
|
||||
expect(store).toMatchSnapshot('5: expand leaves');
|
||||
|
||||
act(() => store.toggleIsCollapsed(store.getElementIDAtIndex(0), true));
|
||||
expect(store).toMatchSnapshot('6: collapse root');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ describe('StoreStress', () => {
|
||||
beforeEach(() => {
|
||||
bridge = global.bridge;
|
||||
store = global.store;
|
||||
store.collapseNodesByDefault = false;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
|
||||
+127
-26
@@ -36,6 +36,8 @@ const debug = (methodName, ...args) => {
|
||||
|
||||
const LOCAL_STORAGE_CAPTURE_SCREENSHOTS_KEY =
|
||||
'React::DevTools::captureScreenshots';
|
||||
const LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY =
|
||||
'React::DevTools::collapseNodesByDefault';
|
||||
|
||||
const THROTTLE_CAPTURE_SCREENSHOT_DURATION = 500;
|
||||
|
||||
@@ -61,6 +63,9 @@ export default class Store extends EventEmitter {
|
||||
|
||||
_captureScreenshots: boolean = false;
|
||||
|
||||
// Should new nodes be collapsed by default when added to the tree?
|
||||
_collapseNodesByDefault: boolean = true;
|
||||
|
||||
// At least one of the injected renderers contains (DEV only) owner metadata.
|
||||
_hasOwnerMetadata: boolean = false;
|
||||
|
||||
@@ -123,6 +128,11 @@ export default class Store extends EventEmitter {
|
||||
debug('constructor', 'subscribing to Bridge');
|
||||
}
|
||||
|
||||
// Default this setting to true unless otherwise specified.
|
||||
this._collapseNodesByDefault =
|
||||
localStorage.getItem(LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY) !==
|
||||
'false';
|
||||
|
||||
if (config != null) {
|
||||
const {
|
||||
isProfiling,
|
||||
@@ -178,6 +188,20 @@ export default class Store extends EventEmitter {
|
||||
this.emit('captureScreenshots');
|
||||
}
|
||||
|
||||
get collapseNodesByDefault(): boolean {
|
||||
return this._collapseNodesByDefault;
|
||||
}
|
||||
set collapseNodesByDefault(value: boolean): void {
|
||||
this._collapseNodesByDefault = value;
|
||||
|
||||
localStorage.setItem(
|
||||
LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY,
|
||||
value ? 'true' : 'false'
|
||||
);
|
||||
|
||||
this.emit('collapseNodesByDefault');
|
||||
}
|
||||
|
||||
get hasOwnerMetadata(): boolean {
|
||||
return this._hasOwnerMetadata;
|
||||
}
|
||||
@@ -291,9 +315,8 @@ export default class Store extends EventEmitter {
|
||||
|
||||
// Find the element in the tree using the weight of each node...
|
||||
// Skip over the root itself, because roots aren't visible in the Elements tree.
|
||||
const firstChildID = ((root: any): Element).children[0];
|
||||
let currentElement = ((this._idToElement.get(firstChildID): any): Element);
|
||||
let currentWeight = rootWeight;
|
||||
let currentElement = ((root: any): Element);
|
||||
let currentWeight = rootWeight - 1;
|
||||
while (index !== currentWeight) {
|
||||
const numChildren = currentElement.children.length;
|
||||
for (let i = 0; i < numChildren; i++) {
|
||||
@@ -425,22 +448,74 @@ export default class Store extends EventEmitter {
|
||||
// We do this to avoid mismatches on e.g. CommitTreeBuilder that would cause errors.
|
||||
}
|
||||
|
||||
// TODO Maybe split this into two methods: expand() and collapse()
|
||||
toggleIsCollapsed(id: number, isCollapsed: boolean): void {
|
||||
const element = this.getElementByID(id);
|
||||
if (element !== null) {
|
||||
const oldWeight = element.isCollapsed ? 1 : element.weight;
|
||||
element.isCollapsed = isCollapsed;
|
||||
const newWeight = element.isCollapsed ? 1 : element.weight;
|
||||
const weightDelta = newWeight - oldWeight;
|
||||
if (isCollapsed) {
|
||||
if (element.type === ElementTypeRoot) {
|
||||
throw Error('Root nodes cannot be collapsed');
|
||||
}
|
||||
|
||||
this._weightAcrossRoots += weightDelta;
|
||||
if (element.isCollapsed) {
|
||||
// There's nothing to change in this case.
|
||||
// We can exit early (without even emiting a "mutated" event).
|
||||
return;
|
||||
}
|
||||
|
||||
let parentElement = this._idToElement.get(element.parentID);
|
||||
while (parentElement != null) {
|
||||
parentElement.weight += weightDelta;
|
||||
parentElement = this._idToElement.get(parentElement.parentID);
|
||||
element.isCollapsed = true;
|
||||
|
||||
const weightDelta = 1 - element.weight;
|
||||
|
||||
let parentElement = ((this._idToElement.get(
|
||||
element.parentID
|
||||
): any): Element);
|
||||
while (parentElement != null) {
|
||||
// We don't need to break on a collapsed parent in the same way as the expand case below.
|
||||
// That's because collapsing a node doesn't "bubble" and affect its parents.
|
||||
parentElement.weight += weightDelta;
|
||||
parentElement = this._idToElement.get(parentElement.parentID);
|
||||
}
|
||||
} else {
|
||||
let currentElement = element;
|
||||
while (currentElement != null) {
|
||||
const oldWeight = currentElement.isCollapsed
|
||||
? 1
|
||||
: currentElement.weight;
|
||||
currentElement.isCollapsed = false;
|
||||
const newWeight = currentElement.isCollapsed
|
||||
? 1
|
||||
: currentElement.weight;
|
||||
const weightDelta = newWeight - oldWeight;
|
||||
|
||||
let parentElement = ((this._idToElement.get(
|
||||
currentElement.parentID
|
||||
): any): Element);
|
||||
while (parentElement != null) {
|
||||
parentElement.weight += weightDelta;
|
||||
if (parentElement.isCollapsed) {
|
||||
// It's important to break on a collapsed parent when expanding nodes.
|
||||
// That's because expanding a node "bubbles" up and expands all parents as well.
|
||||
// Breaking in this case prevents us from over-incrementing the expanded weights.
|
||||
break;
|
||||
}
|
||||
parentElement = this._idToElement.get(parentElement.parentID);
|
||||
}
|
||||
|
||||
currentElement =
|
||||
currentElement.parentID !== 0
|
||||
? this.getElementByID(currentElement.parentID)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
let weightAcrossRoots = 0;
|
||||
this._roots.forEach(rootID => {
|
||||
const { weight } = ((this.getElementByID(rootID): any): Element);
|
||||
weightAcrossRoots += weight;
|
||||
});
|
||||
this._weightAcrossRoots = weightAcrossRoots;
|
||||
|
||||
// The Tree context's search reducer expects an explicit list of ids for nodes that were added or removed.
|
||||
// In this case, we can pass it empty arrays since nodes in a collapsed tree are still there (just hidden).
|
||||
// Updating the selected search index later may require auto-expanding a collapsed subtree though.
|
||||
@@ -476,6 +551,7 @@ export default class Store extends EventEmitter {
|
||||
}
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.groupCollapsed('onBridgeOperations');
|
||||
debug('onBridgeOperations', operations);
|
||||
}
|
||||
|
||||
@@ -552,7 +628,7 @@ export default class Store extends EventEmitter {
|
||||
depth: -1,
|
||||
displayName: null,
|
||||
id,
|
||||
isCollapsed: false,
|
||||
isCollapsed: false, // Never collapse roots; it would hide the entire tree.
|
||||
key: null,
|
||||
ownerID: 0,
|
||||
parentID: 0,
|
||||
@@ -601,7 +677,7 @@ export default class Store extends EventEmitter {
|
||||
depth: parentElement.depth + 1,
|
||||
displayName,
|
||||
id,
|
||||
isCollapsed: false,
|
||||
isCollapsed: this._collapseNodesByDefault,
|
||||
key,
|
||||
ownerID,
|
||||
parentID: parentElement.id,
|
||||
@@ -729,17 +805,19 @@ export default class Store extends EventEmitter {
|
||||
element = ((this._idToElement.get(id): any): Element);
|
||||
element.children = Array.from(children);
|
||||
|
||||
const prevWeight = element.weight;
|
||||
let childWeight = 0;
|
||||
if (!element.isCollapsed) {
|
||||
const prevWeight = element.weight;
|
||||
let childWeight = 0;
|
||||
|
||||
children.forEach(childID => {
|
||||
const child = ((this._idToElement.get(childID): any): Element);
|
||||
childWeight += child.weight;
|
||||
});
|
||||
children.forEach(childID => {
|
||||
const child = ((this._idToElement.get(childID): any): Element);
|
||||
childWeight += child.weight;
|
||||
});
|
||||
|
||||
element.weight = childWeight + 1;
|
||||
element.weight = childWeight + 1;
|
||||
|
||||
weightDelta = childWeight + 1 - prevWeight;
|
||||
weightDelta = childWeight + 1 - prevWeight;
|
||||
}
|
||||
break;
|
||||
case TREE_OPERATION_UPDATE_TREE_BASE_DURATION:
|
||||
// Base duration updates are only sent while profiling is in progress.
|
||||
@@ -793,6 +871,11 @@ export default class Store extends EventEmitter {
|
||||
this.emit('roots');
|
||||
}
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.log(this.__toSnapshot(true));
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
this.emit('mutated', [addedElementIDs, removedElementIDs]);
|
||||
};
|
||||
|
||||
@@ -839,19 +922,23 @@ export default class Store extends EventEmitter {
|
||||
|
||||
// Used for Jest snapshot testing.
|
||||
// May also be useful for visually debugging the tree, so it lives on the Store.
|
||||
__toSnapshot = () => {
|
||||
__toSnapshot = (includeWeight: boolean = false) => {
|
||||
const snapshotLines = [];
|
||||
|
||||
let rootWeight = 0;
|
||||
|
||||
this._roots.forEach(rootID => {
|
||||
snapshotLines.push('[root]');
|
||||
|
||||
const { weight } = ((this.getElementByID(rootID): any): Element);
|
||||
|
||||
snapshotLines.push('[root]' + (includeWeight ? ` (${weight})` : ''));
|
||||
|
||||
for (let i = rootWeight; i < rootWeight + weight; i++) {
|
||||
const element = ((this.getElementAtIndex(i): any): Element);
|
||||
|
||||
if (element == null) {
|
||||
throw Error(`No element for index ${i}`);
|
||||
}
|
||||
|
||||
let prefix = ' ';
|
||||
if (element.children.length > 0) {
|
||||
prefix = element.isCollapsed ? '▸' : '▾';
|
||||
@@ -862,15 +949,29 @@ export default class Store extends EventEmitter {
|
||||
key = ` key="${element.key}"`;
|
||||
}
|
||||
|
||||
let suffix = '';
|
||||
if (includeWeight) {
|
||||
suffix = ` (${element.isCollapsed ? 1 : element.weight})`;
|
||||
}
|
||||
|
||||
snapshotLines.push(
|
||||
`${' '.repeat(element.depth + 1)}${prefix} <${element.displayName ||
|
||||
'null'}${key}>`
|
||||
'null'}${key}>${suffix}`
|
||||
);
|
||||
}
|
||||
|
||||
rootWeight += weight;
|
||||
});
|
||||
|
||||
// Make sure the pretty-printed test align with the Store's reported number of total rows.
|
||||
if (rootWeight !== this._weightAcrossRoots) {
|
||||
throw Error(
|
||||
`Inconsistent store state. Individual root weights (${rootWeight}) do not match total weight (${
|
||||
this._weightAcrossRoots
|
||||
})`
|
||||
);
|
||||
}
|
||||
|
||||
return snapshotLines.join('\n');
|
||||
};
|
||||
}
|
||||
|
||||
@@ -748,11 +748,8 @@ function TreeContextController({ children, viewElementSource }: Props) {
|
||||
|
||||
if (state.selectedElementID !== null) {
|
||||
let element = store.getElementByID(state.selectedElementID);
|
||||
while (element !== null && element.parentID > 0) {
|
||||
element = ((store.getElementByID(element.parentID): any): Element);
|
||||
if (element.isCollapsed) {
|
||||
store.toggleIsCollapsed(element.id, false);
|
||||
}
|
||||
if (element !== null && element.parentID > 0) {
|
||||
store.toggleIsCollapsed(element.parentID, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,14 @@
|
||||
|
||||
.Section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.Header {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
font-size: var(--font-size-sans-large);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ function Settings(_: {||}) {
|
||||
SettingsContext
|
||||
);
|
||||
|
||||
const subscription = useMemo(
|
||||
const captureScreenshotsSubscription = useMemo(
|
||||
() => ({
|
||||
getCurrentValue: () => store.captureScreenshots,
|
||||
subscribe: (callback: Function) => {
|
||||
@@ -25,7 +25,23 @@ function Settings(_: {||}) {
|
||||
}),
|
||||
[store]
|
||||
);
|
||||
const captureScreenshots = useSubscription<boolean, Store>(subscription);
|
||||
const captureScreenshots = useSubscription<boolean, Store>(
|
||||
captureScreenshotsSubscription
|
||||
);
|
||||
|
||||
const collapseNodesByDefaultSubscription = useMemo(
|
||||
() => ({
|
||||
getCurrentValue: () => store.collapseNodesByDefault,
|
||||
subscribe: (callback: Function) => {
|
||||
store.addListener('collapseNodesByDefault', callback);
|
||||
return () => store.removeListener('collapseNodesByDefault', callback);
|
||||
},
|
||||
}),
|
||||
[store]
|
||||
);
|
||||
const collapseNodesByDefault = useSubscription<boolean, Store>(
|
||||
collapseNodesByDefaultSubscription
|
||||
);
|
||||
|
||||
const updateDisplayDensity = useCallback(
|
||||
({ currentTarget }) => {
|
||||
@@ -47,6 +63,12 @@ function Settings(_: {||}) {
|
||||
},
|
||||
[store]
|
||||
);
|
||||
const updateCollapseNodesByDefault = useCallback(
|
||||
({ currentTarget }) => {
|
||||
store.collapseNodesByDefault = currentTarget.checked;
|
||||
},
|
||||
[store]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.Settings}>
|
||||
@@ -85,6 +107,17 @@ function Settings(_: {||}) {
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.Section}>
|
||||
<div className={styles.Header}>Components tree</div>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={collapseNodesByDefault}
|
||||
onChange={updateCollapseNodesByDefault}
|
||||
/>{' '}
|
||||
Collapse tree by default
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles.Section}>
|
||||
<div className={styles.Header}>Display density</div>
|
||||
<div className={styles.OptionGroup}>
|
||||
@@ -111,22 +144,24 @@ function Settings(_: {||}) {
|
||||
</div>
|
||||
</div>
|
||||
{store.supportsCaptureScreenshots && (
|
||||
<div className={styles.Section}>
|
||||
<div className={styles.Header}>Profiler</div>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={captureScreenshots}
|
||||
onChange={updateCaptureScreenshotsWhileProfiling}
|
||||
/>{' '}
|
||||
Capture screenshots while profiling
|
||||
{captureScreenshots && (
|
||||
<p className={styles.ScreenshotThrottling}>
|
||||
Screenshots will be throttled in order to reduce the negative
|
||||
impact on performance.
|
||||
</p>
|
||||
)}
|
||||
</label>
|
||||
<div>
|
||||
<div className={styles.Section}>
|
||||
<div className={styles.Header}>Profiler</div>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={captureScreenshots}
|
||||
onChange={updateCaptureScreenshotsWhileProfiling}
|
||||
/>{' '}
|
||||
Capture screenshots while profiling
|
||||
</label>
|
||||
</div>
|
||||
{captureScreenshots && (
|
||||
<div className={styles.ScreenshotThrottling}>
|
||||
Screenshots will be throttled in order to reduce the negative
|
||||
impact on performance.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user