Skip to content

Commit

Permalink
json tree theme
Browse files Browse the repository at this point in the history
  • Loading branch information
amcdnl committed Apr 16, 2024
1 parent dfb4b21 commit 05e345b
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 17 deletions.
26 changes: 26 additions & 0 deletions docs/components/layout/JsonTree.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Meta, Canvas, ArgTypes } from '@storybook/addon-docs';
import { jsonTreeTheme, JsonTree, JsonTreeNode } from '../../../src/layout/Tree';
import * as TreeStories from '../../../src/layout/Tree/JsonTree/JsonTree.story.tsx';
import ThemeRender from '../../ThemeRender.tsx';

<Meta title="Docs/Components/Layout/Json Tree" />

# Json Tree
Tree component designed for showing JSON data.

## Example
<Canvas sourceState="shown" of={TreeStories.Simple} />

## Theme
This component uses the following default theme:

<ThemeRender theme={jsonTreeTheme} />

Learn more about how to customize in the [Theme documentation](?path=/docs/docs-theme-getting-started--docs).

## API
### Tree
<ArgTypes of={JsonTree} />

### TreeNode
<ArgTypes of={JsonTreeNode} />
119 changes: 108 additions & 11 deletions src/layout/Tree/JsonTree/JsonTree.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,113 @@ export default {
components: JsonTree
};

const data = {
name: 'John Doe',
age: 30,
over21: true,
children: [
{ name: 'Jane Doe', age: 25 },
{ name: 'Jim Doe', age: 33 }
]
};
export const Simple = () => (
<JsonTree
data={{
name: 'John Doe',
age: 30,
over21: true,
children: [
{ name: 'Jane Doe', age: 25 },
{ name: 'Jim Doe', age: 33 }
]
}}
/>
);

export const Simple = () => <JsonTree data={data} />;
export const Expanded = () => (
<JsonTree
data={{
name: 'John Doe',
age: 30,
over21: true,
children: [
{ name: 'Jane Doe', age: 25 },
{ name: 'Jim Doe', age: 33 }
]
}}
expandDepth={Infinity}
/>
);

export const Expanded = () => <JsonTree data={data} expandDepth={Infinity} />;
export const Complex = () => (
<JsonTree
data={{
in_bytes: 0,
sensor_id: 'staging-alpha-network-sensor',
uid: 'a3731aac-5687-408a-8ed6-7693f360d491',
description: `Haxx0r ipsum mailbomb while void ssh leet deadlock over clock mega frack ack. Rsa I'm sorry Dave, I'm afraid I can't do that fopen ban eof. Recursively irc less ascii case d00dz shell emacs cd foad script kiddies gcc unix rm -rf rsa linux grep throw Leslie Lamport snarf. Lib function xss man pages packet sniffer root for continue exception ddos headers *.* protocol chown injection Dennis Ritchie thread January 1, 1970. If hack the mainframe echo race condition packet giga gurfle semaphore long python mainframe server. Tera salt cat flood false hash malloc ifdef cache port var Linus Torvalds. Daemon todo alloc new I'm compiling bar dereference memory leak crack stdio.h gnu fork highjack foo leapfrog daemon cookie. Socket ip gobble hello world system buffer ctl-c James T. Kirk segfault mountain dew client spoof loop true default wombat all your base are belong to us. Terminal overflow protected Donald Knuth float tcp big-endian. Public mutex firewall then gc null syn Trojan horse sudo machine code finally fail bin win kilo epoch regex stack trace wabbit fatal.`,
in_packets: 0,
month: 12,
out_ip_bytes: 48,
duration: 0,
year: 2018,
sensor_zone: 'default',
src_port: 1105,
uuid: 'd918540a-a152-40ab-a478-6b4cb836212d',
conn_state: 'S0',
in_ip_bytes: 0,
extra: {},
src_ips: [
{
city: null,
isp: null,
address: '192.168.242.179',
latitude: null,
org: null,
asn: null
},
{
city: 'Austin',
isp: 'Unassigned',
address: '192.168.242.179',
latitude: 50,
org: 'Unassigned',
asn: 50
},
{
city: 'New York',
isp: 'Unassigned',
address: '192.168.242.179',
latitude: 0,
org: 'Unassigned',
nested: [1, 2, 3]
}
],
type: 'flow',
src_mac: '00:0c:29:30:b9:68',
dst_mac: '00:50:56:f0:a3:52',
out_packets: 1,
timestamp: 1544475648,
source_type: 'bro',
services: [],
day: 10,
out_bytes: 0,
sensor_name: 'staging-alpha-network-sensor',
hour: 21,
detections: {},
dst_port: 80,
ip_proto: 'tcp',
dst_ip: {
city: null,
isp: 'US Department of Defense Network',
region: 'Unassigned',
is_internal: false,
longitude: -97.8219985961914,
country_name: 'United States',
version: 4,
location: '',
country_code: 'US',
address: '6.6.6.167',
latitude: 37.750999450683594,
org: 'US Department of Defense Network',
asn: 0
},
history: 'S',
show_more: [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23
]
}}
/>
);
43 changes: 43 additions & 0 deletions src/layout/Tree/JsonTree/JsonTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,63 @@ import { JsonTreeNode } from './JsonTreeNode';
import { parseJsonTree } from './utils';

export interface JsonTreeProps {
/**
* The data to be rendered as a JSON tree.
*/
data: { [key: string]: any };

/**
* If true, all nodes in the JSON tree will be expanded by default.
*/
showAll?: boolean;

/**
* The limit for the number of nodes to show when `showAll` is true.
*/
showAllLimit?: number;

/**
* The threshold for the number of nodes at which `showAll` will take effect.
*/
showAllThreshold?: number;

/**
* If true, the count of child nodes will be shown next to each node.
*/
showCount?: boolean;

/**
* If true, empty nodes will be shown in the JSON tree.
*/
showEmpty?: boolean;

/**
* If true, long text in nodes will be truncated and replaced with an ellipsis.
*/
ellipsisText?: boolean;

/**
* The maximum length of text in a node before it is truncated and replaced with an ellipsis.
*/
ellipsisTextLength?: number;

/**
* The depth at which the JSON tree nodes should be expanded by default.
*/
expandDepth?: number;

/**
* The CSS class name to be applied to the JSON tree.
*/
className?: string;
}

export const JsonTree: FC<JsonTreeProps> = ({
data,
className,
expandDepth,
ellipsisText,
ellipsisTextLength,
...rest
}) => {
const tree = parseJsonTree({ data });
Expand All @@ -32,6 +73,8 @@ export const JsonTree: FC<JsonTreeProps> = ({
depth={1}
data={tree}
expandDepth={expandDepth}
ellipsisText={ellipsisText}
ellipsisTextLength={ellipsisTextLength}
/>
</Tree>
</div>
Expand Down
34 changes: 28 additions & 6 deletions src/layout/Tree/JsonTree/JsonTreeNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { JsonTreeData } from './utils';
import { useComponentTheme } from '../../../utils/Theme/hooks';
import { JsonTreeTheme } from './JsonTreeTheme';
import { twMerge } from 'tailwind-merge';
import { Ellipsis } from '../../../data/Ellipsis';

export interface JsonTreeNodeProps {
/**
Expand All @@ -30,13 +31,25 @@ export interface JsonTreeNodeProps {
* Theme for the Json Tree
*/
theme?: JsonTreeTheme;

/**
* If true, long text in nodes will be truncated and replaced with an ellipsis.
*/
ellipsisText?: boolean;

/**
* The maximum length of text in a node before it is truncated and replaced with an ellipsis.
*/
ellipsisTextLength?: number;
}

export const JsonTreeNode: FC<JsonTreeNodeProps> = ({
depth,
data,
expandDepth,
className,
ellipsisText,
ellipsisTextLength,
theme: customTheme
}) => {
const theme = useComponentTheme('jsonTree', customTheme);
Expand All @@ -49,22 +62,29 @@ export const JsonTreeNode: FC<JsonTreeNodeProps> = ({
<>
<span className={twMerge(theme.node.label)}>{data.label}</span>
<span className={twMerge(theme.node.symbol)}>{symbol}</span>
<span
className={twMerge(theme.node.count)}
>{`(${data.data.length.toLocaleString()} ${label})`}</span>
<span className={twMerge(theme.node.count)}>
{`(${data.data.length.toLocaleString()} ${label})`}
</span>
</>
);
}, [data]);
}, [data, theme, type]);

const renderPrimativeNode = useCallback(() => {
const ellipsis = type === 'string' && ellipsisText;
return (
<>
<span className={twMerge(theme.node.label)}>{data.label}</span>
<span className={twMerge(theme.node.delimiter)}>:</span>
<span className={twMerge(theme.node.value)}>{`${data.data}`}</span>
<span className={twMerge(theme.node.value)}>
{ellipsis ? (
<Ellipsis value={data.data} limit={ellipsisTextLength} />
) : (
data.data
)}
</span>
</>
);
}, [data]);
}, [data, ellipsisText, ellipsisTextLength, theme, type]);

return (
<TreeNode
Expand All @@ -87,6 +107,8 @@ export const JsonTreeNode: FC<JsonTreeNodeProps> = ({
depth={depth + 1}
expandDepth={expandDepth}
type={item.type}
ellipsisText={ellipsisText}
ellipsisTextLength={ellipsisTextLength}
/>
))}
</>
Expand Down
1 change: 1 addition & 0 deletions src/layout/Tree/JsonTree/JsonTreeTheme.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface JsonTreeTheme {
node: {
base: string;
label: string;
value: string;
delimiter: string;
Expand Down

0 comments on commit 05e345b

Please sign in to comment.