Skip to content

Commit

Permalink
feat: Added Treeview feature (#558)
Browse files Browse the repository at this point in the history
Co-authored-by: Noam Gaash <[email protected]>
  • Loading branch information
Darkmift and NoamGaash authored Mar 7, 2024
1 parent 56fc2f2 commit 4aa6213
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 46 deletions.
152 changes: 140 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
"type": "commonjs",
"dependencies": {
"@applitools/eyes-playwright": "^1.23.6",
"@types/git-username": "^1.0.5",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.12",
"@mui/lab": "^5.0.0-alpha.167",
"@mui/material": "^5.15.11",
"@mui/x-date-pickers": "^6.19.5",
"@mui/x-tree-view": "^6.17.0",
"@types/git-username": "^1.0.5",
"@types/leaflet.markercluster": "^1.5.2",
"@vitejs/plugin-react-swc": "^3.6.0",
"antd": "^5.14.2",
Expand Down
91 changes: 91 additions & 0 deletions src/pages/components/CustomTreeView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { TreeView } from '@mui/x-tree-view/TreeView';

Check failure on line 1 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
import { TreeItem } from '@mui/x-tree-view/TreeItem';

Check failure on line 2 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';

Check failure on line 3 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
import ChevronRightIcon from '@mui/icons-material/ChevronRight';

Check failure on line 4 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`

interface BaseTreeNode {
id: string;

Check failure on line 7 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
name: string;

Check failure on line 8 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
}

// Generic TreeNode interface with additional properties
interface TreeNode<T extends BaseTreeNode> extends BaseTreeNode {
children?: TreeNode<T>[];

Check failure on line 13 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
// You can now include additional properties from T
}

// Generic CustomTreeViewProps interface
interface CustomTreeViewProps<T> {
data: T;

Check failure on line 19 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
name: string;

Check failure on line 20 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
id: string;

Check failure on line 21 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
}

// A utility function to transform any object into a TreeNode structure
const objectToTreeNode = (
obj: any,

Check warning on line 26 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
key: string = 'root',
nodeId: string = '0'
): TreeNode<BaseTreeNode> => {
const isPrimitive = (value: any) => value !== Object(value) || value === null;

Check warning on line 30 in src/pages/components/CustomTreeView.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

const children = Object.keys(obj).map((k, index) => {
const value = obj[k];
if (isPrimitive(value)) {
// Handle primitive types directly
return {
id: `${nodeId}-${index}`,
name: `${k}: ${value}`,
};
} else if (Array.isArray(value)) {
// If it's an array, create a node that lists all elements
return {
id: `${nodeId}-${index}`,
name: k,
children: value.map((item, itemIndex) => {
// Handle primitive items in the array directly
if (isPrimitive(item)) {
return {
id: `${nodeId}-${index}-${itemIndex}`,
name: `${item}`,
};
}
// Recursively handle objects in the array
return objectToTreeNode(item, k, `${nodeId}-${index}-${itemIndex}`);
}),
};
} else {
// Recursively handle objects
return objectToTreeNode(value, k, `${nodeId}-${index}`);
}
});

return {
id: nodeId,
name: obj.name || key,
children: children.length ? children : undefined,
};
};

// Render tree function utilizing the TreeNode interface
const renderTree = <T extends BaseTreeNode>(nodes: TreeNode<T>): JSX.Element => (
<TreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name}>
{nodes.children?.map((node) => renderTree(node))}
</TreeItem>
);

// CustomTreeView component using TypeScript
const CustomTreeView = <T,>({ data, name, id }: CustomTreeViewProps<T>) => {
const dataAsTreeNode = objectToTreeNode({ id, name, children: [data] });
return (
<TreeView
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
defaultEndIcon={<div style={{ width: 24 }} />}
>
{renderTree(dataAsTreeNode)}
</TreeView>
);
};

export default CustomTreeView;
50 changes: 50 additions & 0 deletions src/pages/components/CutomTreeView.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// CustomTreeView.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import CustomTreeView from './CustomTreeView';
import '../../shared/shared.css'; // Assuming you have some shared styles

// Define a base type for your tree nodes, considering the object structure you provided earlier
interface CustomTreeNode {
id: string;
name: string;
children?: CustomTreeNode[];
}

// Example data for the story
const exampleData = {
id: 'root',
name: 'Root Node',
children: [
{
id: '1',
name: 'Child Node 1',
children: [
{ id: '1-1', name: 'Grandchild Node 1-1' },
{ id: '1-2', name: 'Grandchild Node 1-2' },
],
},
{
id: '2',
name: 'Child Node 2',
},
],
};

const meta: Meta<typeof CustomTreeView> = {
title: 'Components/CustomTreeView',
component: CustomTreeView,
parameters: {
layout: 'centered',
},
tags: ['tree', 'view', 'react', 'mui'], // Adjust tags as needed
} satisfies Meta<typeof CustomTreeView>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
data: exampleData, // Using the example data for the default story
},
};
Loading

0 comments on commit 4aa6213

Please sign in to comment.