Skip to content

Commit 5747647

Browse files
feat(ui): add TreeMap chart (#5781)
* feat(ui): add TreeMap chart * chore: use ResponsiveTreeMapHtml instead of ResponsiveTreeMap * test: fix snapshots * chore: lisa feedback * chore: rename story
1 parent 2974892 commit 5747647

File tree

19 files changed

+1112
-1
lines changed

19 files changed

+1112
-1
lines changed

.changeset/ready-taxis-stand.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ultraviolet/ui": minor
3+
---
4+
5+
feat(ui): add TreeMap chart

packages/ui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"@nivo/line": "0.89.1",
9595
"@nivo/pie": "0.89.1",
9696
"@nivo/scales": "0.89.0",
97+
"@nivo/treemap": "0.89.1",
9798
"@scaleway/fuzzy-search": "1.0.0",
9899
"@scaleway/random-name": "5.1.2",
99100
"@scaleway/use-media": "3.0.4",
@@ -108,4 +109,4 @@
108109
"react-toastify": "11.0.5",
109110
"react-use-clipboard": "1.0.9"
110111
}
111-
}
112+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use client'
2+
3+
import { Text } from '../Text'
4+
import { treeMapTooltipContainer } from './styles.css'
5+
import type { DataType } from './types'
6+
7+
type TooltipProps = Pick<DataType, 'content' | 'value'> & {
8+
'data-testid'?: string
9+
}
10+
11+
export const Tooltip = ({
12+
value,
13+
content,
14+
'data-testid': dataTestId,
15+
}: TooltipProps) => (
16+
<div className={treeMapTooltipContainer} data-testid={dataTestId}>
17+
<Text as="p" sentiment="primary" variant="bodyStronger">
18+
{content}
19+
</Text>
20+
{value ? (
21+
<Text as="p" sentiment="primary" variant="bodyStronger">
22+
{value}
23+
</Text>
24+
) : null}
25+
</div>
26+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { StoryFn } from '@storybook/react-vite'
2+
import { TreeMapChart } from '..'
3+
import { treeMapChartColorsData } from './mockData'
4+
5+
export const ColorsGenerator: StoryFn<typeof TreeMapChart> = props => (
6+
<TreeMapChart {...props} data={treeMapChartColorsData} height="300px" />
7+
)
8+
9+
ColorsGenerator.parameters = {
10+
docs: {
11+
description: {
12+
story: 'You can show a lots of data and colors should vary accordingly',
13+
},
14+
},
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { StoryFn } from '@storybook/react-vite'
2+
import { TreeMapChart } from '..'
3+
import { treeMapChartWithCustomContentData } from './mockData'
4+
5+
export const CustomContent: StoryFn<typeof TreeMapChart> = props => (
6+
<TreeMapChart
7+
{...props}
8+
data={treeMapChartWithCustomContentData}
9+
height="300px"
10+
/>
11+
)
12+
13+
CustomContent.parameters = {
14+
docs: {
15+
description: {
16+
story: 'You can show custom content with ReactNode instead of string',
17+
},
18+
},
19+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { StoryFn } from '@storybook/react-vite'
2+
import { TreeMapChart } from '..'
3+
import { treeMapChartSimpleData } from './mockData'
4+
5+
export const CustomTooltip: StoryFn<typeof TreeMapChart> = props => (
6+
<TreeMapChart
7+
{...props}
8+
data={treeMapChartSimpleData}
9+
height="300px"
10+
tooltipFunction={({ value, content }) => ({
11+
content: <>Custom: {content}</>,
12+
value: (value || 0) * 2,
13+
})}
14+
/>
15+
)
16+
17+
CustomTooltip.parameters = {
18+
docs: {
19+
description: {
20+
story: 'Use `tooltipFunction` to customize the tooltip.',
21+
},
22+
},
23+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { Template } from './Template.stories'
2+
3+
export const Playground = Template.bind({})
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { StoryFn } from '@storybook/react-vite'
2+
import { TreeMapChart } from '..'
3+
import { treeMapChartSimpleData } from './mockData'
4+
5+
export const Template: StoryFn<typeof TreeMapChart> = ({ ...props }) => (
6+
<TreeMapChart
7+
// @ts-expect-error - Data before props so it can be overwritten in Storybook
8+
data={treeMapChartSimpleData}
9+
height="300px"
10+
{...props}
11+
/>
12+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Meta } from '@storybook/react-vite'
2+
import { TreeMapChart } from '..'
3+
4+
export default {
5+
component: TreeMapChart,
6+
title: 'Components/Data Display/Chart/TreeMapChart',
7+
parameters: {
8+
experimental: true,
9+
},
10+
tags: ['experimental'],
11+
} as Meta<typeof TreeMapChart>
12+
13+
export { Playground } from './Playground.stories'
14+
export { CustomTooltip } from './CustomTooltip.stories'
15+
export { ColorsGenerator } from './ColorsGenerator.stories'
16+
export { CustomContent } from './CustomContent.stories'
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { Stack } from '../../Stack'
2+
import { Text } from '../../Text'
3+
4+
export const treeMapChartSimpleData = {
5+
children: [
6+
{
7+
content: 'Compute',
8+
id: 'compute',
9+
value: 10,
10+
},
11+
{
12+
content: 'Network',
13+
id: 'network',
14+
value: 17.2,
15+
},
16+
{
17+
content: 'AI',
18+
id: 'ai',
19+
value: 14.2,
20+
},
21+
],
22+
content: 'root',
23+
id: 'root',
24+
}
25+
26+
export const treeMapChartWithCustomContentData = {
27+
children: [
28+
{
29+
content: (
30+
<Stack>
31+
<Text as="p" variant="body">
32+
Compute
33+
</Text>
34+
<Text as="p" variant="bodySmall">
35+
10 kgco2
36+
</Text>
37+
</Stack>
38+
),
39+
id: 'compute',
40+
value: 10,
41+
},
42+
{
43+
content: (
44+
<Stack>
45+
<Text as="p" variant="body">
46+
Network
47+
</Text>
48+
<Text as="p" variant="bodySmall">
49+
10 kgco2
50+
</Text>
51+
</Stack>
52+
),
53+
id: 'network',
54+
value: 17.2,
55+
},
56+
{
57+
content: (
58+
<Stack>
59+
<Text as="p" variant="body">
60+
AI
61+
</Text>
62+
<Text as="p" variant="bodySmall">
63+
10 kgco2
64+
</Text>
65+
</Stack>
66+
),
67+
id: 'ai',
68+
value: 14.2,
69+
},
70+
],
71+
content: 'root',
72+
id: 'root',
73+
}
74+
75+
export const treeMapChartColorsData = {
76+
children: [
77+
{
78+
content: 'Compute',
79+
id: 'compute',
80+
value: 40,
81+
},
82+
{
83+
content: 'Network',
84+
id: 'network',
85+
value: 17.2,
86+
},
87+
{
88+
content: 'AI',
89+
id: 'ai',
90+
value: 14.2,
91+
},
92+
{
93+
content: 'Managed services',
94+
id: 'managed-services',
95+
value: 12.2,
96+
},
97+
{
98+
content: 'Observability',
99+
id: 'observability',
100+
value: 13.452,
101+
},
102+
{
103+
content: 'Domain & Webhosting',
104+
id: 'domain-and-webhosting',
105+
value: 9.32,
106+
},
107+
{
108+
content: 'Containers',
109+
id: 'containers',
110+
value: 31.47,
111+
},
112+
{
113+
content: 'Storage',
114+
id: 'storage',
115+
value: 10.65,
116+
},
117+
{
118+
content: 'Security and Identity',
119+
id: 'security-and-identity',
120+
value: 1.2,
121+
},
122+
{
123+
content: 'IAM',
124+
id: 'iam',
125+
value: 11.2,
126+
},
127+
{
128+
content: 'Billing',
129+
id: 'billing',
130+
value: 11.2,
131+
},
132+
{
133+
content: 'Environmental Footprint',
134+
id: 'environmental-footprint',
135+
value: 2,
136+
},
137+
{
138+
content: 'Audit Trail',
139+
id: 'audit-trail',
140+
value: 5.2,
141+
},
142+
{
143+
content: 'VPC',
144+
id: 'vpc',
145+
value: 3,
146+
},
147+
],
148+
content: 'root',
149+
id: 'root',
150+
}

0 commit comments

Comments
 (0)