Skip to content

Commit 4abb826

Browse files
committed
feat: improve tree performance
Signed-off-by: Maxim Stykow <maxim.stykow@tngtech.com>
1 parent 0159b0f commit 4abb826

File tree

12 files changed

+266
-370
lines changed

12 files changed

+266
-370
lines changed

src/Frontend/Components/ResourceBrowser/LinkedResourcesTree/GeneralTreeItemLabel/GeneralTreeItemLabel.tsx

+31-17
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,54 @@ import MuiBox from '@mui/material/Box';
66
import MuiTypography from '@mui/material/Typography';
77
import { ReactElement } from 'react';
88

9+
import { ROOT_PATH } from '../../../../shared-constants';
910
import { treeItemClasses } from '../../../../shared-styles';
11+
import { useAppSelector } from '../../../../state/hooks';
12+
import {
13+
getAttributionBreakpoints,
14+
getFilesWithChildren,
15+
} from '../../../../state/selectors/resource-selectors';
1016
import { BreakpointIcon, DirectoryIcon, FileIcon } from '../../../Icons/Icons';
17+
import { TreeNodeLabelProps } from '../../../VirtualizedTree/VirtualizedTreeNode/VirtualizedTreeNode';
1118

12-
interface GeneralTreeItemLabelProps {
13-
labelText: string;
14-
canHaveChildren: boolean;
15-
isAttributionBreakpoint: boolean;
16-
showFolderIcon: boolean;
17-
}
19+
const labelDetail = 'without information';
20+
21+
export function GeneralTreeItemLabel({
22+
node,
23+
nodeId,
24+
nodeName,
25+
}: TreeNodeLabelProps): ReactElement {
26+
const attributionBreakpoints = useAppSelector(getAttributionBreakpoints);
27+
const filesWithChildren = useAppSelector(getFilesWithChildren);
1828

19-
export function GeneralTreeItemLabel(
20-
props: GeneralTreeItemLabelProps,
21-
): ReactElement {
22-
const iconSx = treeItemClasses.resourceWithoutInformation;
23-
const labelDetail = 'without information';
29+
const isAttributionBreakpoint = attributionBreakpoints.has(nodeId);
30+
const showFolderIcon = node !== 1 && !filesWithChildren.has(nodeId);
31+
const labelText = nodeName || ROOT_PATH;
2432

2533
return (
2634
<MuiBox sx={treeItemClasses.labelRoot}>
27-
{props.showFolderIcon ? (
28-
props.isAttributionBreakpoint ? (
35+
{showFolderIcon ? (
36+
isAttributionBreakpoint ? (
2937
<BreakpointIcon />
3038
) : (
31-
<DirectoryIcon sx={iconSx} labelDetail={labelDetail} />
39+
<DirectoryIcon
40+
sx={treeItemClasses.resourceWithoutInformation}
41+
labelDetail={labelDetail}
42+
/>
3243
)
3344
) : (
34-
<FileIcon sx={iconSx} labelDetail={labelDetail} />
45+
<FileIcon
46+
sx={treeItemClasses.resourceWithoutInformation}
47+
labelDetail={labelDetail}
48+
/>
3549
)}
3650
<MuiTypography
3751
sx={{
3852
...treeItemClasses.text,
39-
...(props.isAttributionBreakpoint ? treeItemClasses.breakpoint : {}),
53+
...(isAttributionBreakpoint && treeItemClasses.breakpoint),
4054
}}
4155
>
42-
{props.labelText}
56+
{labelText}
4357
</MuiTypography>
4458
</MuiBox>
4559
);

src/Frontend/Components/ResourceBrowser/LinkedResourcesTree/GeneralTreeItemLabel/__tests__/GeneralTreeItemLabel.test.tsx

+18-16
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
// SPDX-FileCopyrightText: TNG Technology Consulting GmbH <https://www.tngtech.com>
33
//
44
// SPDX-License-Identifier: Apache-2.0
5-
import { render, screen } from '@testing-library/react';
5+
import { screen } from '@testing-library/react';
66

7+
import { faker } from '../../../../../../testing/Faker';
8+
import { setAttributionBreakpoints } from '../../../../../state/actions/resource-actions/all-views-simple-actions';
9+
import { renderComponent } from '../../../../../test-helpers/render';
710
import { GeneralTreeItemLabel } from '../GeneralTreeItemLabel';
811

912
describe('StyledTreeItemLabel', () => {
1013
it('renders a file without information', () => {
11-
render(
14+
renderComponent(
1215
<GeneralTreeItemLabel
13-
labelText={'Test label'}
14-
canHaveChildren={false}
15-
isAttributionBreakpoint={false}
16-
showFolderIcon={false}
16+
nodeName={'Test label'}
17+
node={1}
18+
nodeId={faker.system.filePath()}
1719
/>,
1820
);
1921

@@ -25,12 +27,11 @@ describe('StyledTreeItemLabel', () => {
2527
});
2628

2729
it('renders a folder without information', () => {
28-
render(
30+
renderComponent(
2931
<GeneralTreeItemLabel
30-
labelText={'Test label'}
31-
canHaveChildren={true}
32-
isAttributionBreakpoint={false}
33-
showFolderIcon={true}
32+
nodeName={'Test label'}
33+
node={faker.opossum.resources()}
34+
nodeId={faker.system.filePath()}
3435
/>,
3536
);
3637

@@ -41,13 +42,14 @@ describe('StyledTreeItemLabel', () => {
4142
});
4243

4344
it('renders a breakpoint', () => {
44-
render(
45+
const nodeId = faker.system.filePath();
46+
renderComponent(
4547
<GeneralTreeItemLabel
46-
labelText={'Test label'}
47-
canHaveChildren={true}
48-
isAttributionBreakpoint={true}
49-
showFolderIcon={true}
48+
nodeName={'Test label'}
49+
node={faker.opossum.resources()}
50+
nodeId={nodeId}
5051
/>,
52+
{ actions: [setAttributionBreakpoints(new Set([nodeId]))] },
5153
);
5254

5355
expect(screen.getByText('Test label')).toBeInTheDocument();

src/Frontend/Components/ResourceBrowser/LinkedResourcesTree/LinkedResourcesTree.tsx

+4-18
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,14 @@ import { SxProps } from '@mui/system';
66
import { remove } from 'lodash';
77
import { useCallback, useEffect, useMemo, useState } from 'react';
88

9-
import { ROOT_PATH } from '../../../shared-constants';
109
import { OpossumColors } from '../../../shared-styles';
1110
import { navigateToSelectedPathOrOpenUnsavedPopup } from '../../../state/actions/popup-actions/popup-actions';
1211
import {
1312
getInitialExpandedIds,
1413
getResourcesFromPaths,
1514
} from '../../../state/helpers/resources-helpers';
1615
import { useAppDispatch, useAppSelector } from '../../../state/hooks';
17-
import {
18-
getAttributionBreakpoints,
19-
getFilesWithChildren,
20-
getSelectedResourceId,
21-
} from '../../../state/selectors/resource-selectors';
16+
import { getSelectedResourceId } from '../../../state/selectors/resource-selectors';
2217
import { VirtualizedTree } from '../../VirtualizedTree/VirtualizedTree';
2318
import { GeneralTreeItemLabel } from './GeneralTreeItemLabel/GeneralTreeItemLabel';
2419

@@ -36,11 +31,9 @@ export function LinkedResourcesTree({
3631
sx,
3732
}: Props) {
3833
const dispatch = useAppDispatch();
39-
const filesWithChildren = useAppSelector(getFilesWithChildren);
40-
const attributionBreakpoints = useAppSelector(getAttributionBreakpoints);
4134
const selectedResourceId = useAppSelector(getSelectedResourceId);
4235

43-
const nodes = useMemo(
36+
const resources = useMemo(
4437
() => getResourcesFromPaths(resourceIds),
4538
[resourceIds],
4639
);
@@ -80,17 +73,10 @@ export function LinkedResourcesTree({
8073
}),
8174
...sx,
8275
}}
83-
nodes={nodes}
76+
resources={resources}
8477
selectedNodeId={disableHighlightSelected ? '' : selectedResourceId}
8578
readOnly={readOnly}
86-
getTreeNodeLabel={(resourceName, resource, nodeId) => (
87-
<GeneralTreeItemLabel
88-
labelText={resourceName === '' ? ROOT_PATH : resourceName}
89-
canHaveChildren={resource !== 1}
90-
isAttributionBreakpoint={attributionBreakpoints.has(nodeId)}
91-
showFolderIcon={resource !== 1 && !filesWithChildren.has(nodeId)}
92-
/>
93-
)}
79+
TreeNodeLabel={GeneralTreeItemLabel}
9480
testId={'linked-resources-tree'}
9581
/>
9682
);

src/Frontend/Components/ResourceBrowser/LinkedResourcesTree/__tests__/LinkedResourcesTree.test.tsx

+16-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// SPDX-License-Identifier: Apache-2.0
55
import { fireEvent, screen } from '@testing-library/react';
66

7+
import { faker } from '../../../../../testing/Faker';
8+
import { setResources } from '../../../../state/actions/resource-actions/all-views-simple-actions';
79
import {
810
getExpandedIds,
911
getSelectedResourceId,
@@ -40,7 +42,20 @@ describe('LinkedResourcesTree', () => {
4042
});
4143

4244
it('collapses and expands folders', () => {
43-
renderComponent(<LinkedResourcesTree resourceIds={resourceIds} />);
45+
renderComponent(<LinkedResourcesTree resourceIds={resourceIds} />, {
46+
actions: [
47+
setResources(
48+
faker.opossum.resources({
49+
folder1: {
50+
folder2: {
51+
resource_1: 1,
52+
},
53+
},
54+
resource_2: 1,
55+
}),
56+
),
57+
],
58+
});
4459
expect(screen.getByText('resource_1')).toBeInTheDocument();
4560

4661
const collapseIcon = screen.getByLabelText('collapse /folder1/');

src/Frontend/Components/ResourceBrowser/ResourcesTree/ResourcesTree.tsx

+3-51
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,7 @@ import {
1515
import { getResourcesFromPaths } from '../../../state/helpers/resources-helpers';
1616
import { useAppDispatch, useAppSelector } from '../../../state/hooks';
1717
import {
18-
getAttributionBreakpoints,
1918
getExpandedIds,
20-
getExternalData,
21-
getFilesWithChildren,
22-
getManualAttributions,
23-
getResolvedExternalAttributions,
24-
getResourcesToExternalAttributions,
25-
getResourcesToManualAttributions,
26-
getResourcesWithExternalAttributedChildren,
27-
getResourcesWithManualAttributedChildren,
2819
getSelectedResourceId,
2920
} from '../../../state/selectors/resource-selectors';
3021
import { useFilteredAttributions } from '../../../state/variables/use-filtered-data';
@@ -40,29 +31,10 @@ export const ResourcesTree = ({ resourceIds, sx }: Props) => {
4031
const dispatch = useAppDispatch();
4132
const selectedResourceId = useAppSelector(getSelectedResourceId);
4233
const expandedIds = useAppSelector(getExpandedIds);
43-
const manualAttributions = useAppSelector(getManualAttributions);
44-
const resourcesToManualAttributions = useAppSelector(
45-
getResourcesToManualAttributions,
46-
);
47-
const resourcesWithManualAttributedChildren = useAppSelector(
48-
getResourcesWithManualAttributedChildren,
49-
);
50-
const resourcesToExternalAttributions = useAppSelector(
51-
getResourcesToExternalAttributions,
52-
);
53-
const resourcesWithExternalAttributedChildren = useAppSelector(
54-
getResourcesWithExternalAttributedChildren,
55-
);
56-
const resolvedExternalAttributions = useAppSelector(
57-
getResolvedExternalAttributions,
58-
);
59-
const attributionBreakpoints = useAppSelector(getAttributionBreakpoints);
60-
const filesWithChildren = useAppSelector(getFilesWithChildren);
61-
const externalData = useAppSelector(getExternalData);
6234

6335
const [_, setFilteredAttributions] = useFilteredAttributions();
6436

65-
const nodes = useMemo(
37+
const resources = useMemo(
6638
() => getResourcesFromPaths(resourceIds),
6739
[resourceIds],
6840
);
@@ -96,29 +68,9 @@ export const ResourcesTree = ({ resourceIds, sx }: Props) => {
9668
dispatch(setSelectedResourceIdOrOpenUnsavedPopup(nodeId));
9769
}}
9870
onToggle={handleToggle}
99-
nodes={nodes}
71+
resources={resources}
10072
selectedNodeId={selectedResourceId}
101-
getTreeNodeLabel={(resourceName, resource, nodeId) => (
102-
<ResourcesTreeNodeLabel
103-
resourceName={resourceName}
104-
resource={resource}
105-
nodeId={nodeId}
106-
resourcesToManualAttributions={resourcesToManualAttributions}
107-
resourcesToExternalAttributions={resourcesToExternalAttributions}
108-
manualAttributions={manualAttributions}
109-
resourcesWithExternalAttributedChildren={
110-
resourcesWithExternalAttributedChildren
111-
}
112-
resourcesWithManualAttributedChildren={
113-
resourcesWithManualAttributedChildren
114-
}
115-
resolvedExternalAttributions={resolvedExternalAttributions}
116-
attributionBreakpoints={attributionBreakpoints}
117-
filesWithChildren={filesWithChildren}
118-
externalData={externalData}
119-
/>
120-
)}
121-
breakpoints={attributionBreakpoints}
73+
TreeNodeLabel={ResourcesTreeNodeLabel}
12274
sx={sx}
12375
testId={'resources-tree'}
12476
/>

0 commit comments

Comments
 (0)