Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
a9ad077
feat: add Sparkline chart component to fidesui
kruulik Feb 23, 2026
fc25291
add empty data state
kruulik Feb 23, 2026
e6a011a
feat: add RadarChart component to fidesui
kruulik Feb 23, 2026
ef5f228
wip radar
kruulik Feb 23, 2026
df894ff
Create StatCard and Stat component
kruulik Feb 24, 2026
398b72f
adjust styling
kruulik Feb 24, 2026
4897396
fix antd in storybook
kruulik Feb 24, 2026
e45f779
clip
kruulik Feb 24, 2026
4e587df
theme
kruulik Feb 24, 2026
bf0799e
Merge branch 'main' into karolis-sparkline
kruulik Feb 24, 2026
51b1154
Merge branch 'main' into karolis-sparkline
kruulik Feb 25, 2026
65f1c71
Create Card and Statistic HOCs
kruulik Feb 25, 2026
fae3a19
changelog and lint
kruulik Feb 25, 2026
550af77
lock
kruulik Feb 25, 2026
8684df3
Merge branch 'main' into karolis-sparkline
kruulik Feb 25, 2026
7c4d7f3
move recharts dep
kruulik Feb 25, 2026
6e32240
Merge branch 'karolis-sparkline' into kk-dashboard-card
kruulik Feb 25, 2026
7320be4
Merge branch 'karolis-sparkline' into kk-dashboard-card
kruulik Feb 25, 2026
e77327b
lint and changelog
kruulik Feb 25, 2026
0b4b05c
Merge branch 'kk-dashboard-card' of https://github.com/ethyca/fides i…
kruulik Feb 25, 2026
7028b8e
Merge branch 'karolis-sparkline' into karolis-radarchart
kruulik Feb 25, 2026
0d2a817
experimenting with colors
kruulik Feb 25, 2026
939ba3a
move recharts package
kruulik Feb 25, 2026
bd633a5
comments
kruulik Feb 25, 2026
84a2f67
enforce colors
kruulik Feb 25, 2026
4274208
add todos
kruulik Feb 25, 2026
9656d2a
typo
kruulik Feb 25, 2026
82ae42c
Merge branch 'main' into karolis-sparkline
kruulik Feb 25, 2026
f6c8ecb
lint
kruulik Feb 25, 2026
f85bd54
Merge branch 'karolis-sparkline' of https://github.com/ethyca/fides i…
kruulik Feb 25, 2026
02cfe63
Merge branch 'karolis-sparkline' into karolis-radarchart
kruulik Feb 25, 2026
0243cb8
wip
kruulik Feb 25, 2026
7028ccf
wip
kruulik Feb 25, 2026
df8ebf4
add cline to gitignore
kruulik Feb 26, 2026
b7c0bfc
storybook theming
kruulik Feb 26, 2026
7e4eb77
clean up radar comp
kruulik Feb 26, 2026
28a4388
Merge branch 'main' into karolis-radarchart
kruulik Feb 26, 2026
175de5f
changelog
kruulik Feb 26, 2026
bb42a9e
Delete changelog/create-sparkline.yaml
kruulik Feb 26, 2026
d300e22
address comments
kruulik Feb 26, 2026
d6a8cbf
Update clients/fidesui/src/components/charts/RadarChart.stories.tsx
kruulik Feb 26, 2026
5a93ac6
Update clients/fidesui/src/components/charts/RadarChart.tsx
kruulik Feb 26, 2026
748d289
Update clients/fidesui/src/components/charts/chart-constants.ts
kruulik Feb 26, 2026
4f95111
Merge branch 'karolis-radarchart' of https://github.com/ethyca/fides …
kruulik Feb 26, 2026
fe688e7
fix aliasign and refactor
kruulik Feb 26, 2026
7653b32
standardize styles
kruulik Feb 26, 2026
38db29d
too many comments
kruulik Feb 26, 2026
317462e
more standardization
kruulik Feb 26, 2026
7b2775c
Merge branch 'main' into karolis-radarchart
kruulik Feb 26, 2026
578d648
Update clients/fidesui/src/components/charts/chart-constants.ts
kruulik Feb 26, 2026
1114b5f
lint
kruulik Feb 26, 2026
e5ca421
Merge branch 'karolis-radarchart' of https://github.com/ethyca/fides …
kruulik Feb 26, 2026
929ac41
typo
kruulik Feb 26, 2026
a553e27
Update clients/fidesui/src/index.ts
kruulik Feb 26, 2026
2ccdbd8
comments
kruulik Feb 26, 2026
362a6c7
constants
kruulik Feb 26, 2026
63c20f2
comment
kruulik Feb 26, 2026
97d50ea
Merge branch 'karolis-radarchart' into kk-dashboard-card
kruulik Feb 26, 2026
b50da2d
Import radar chart
kruulik Feb 26, 2026
339cee3
Merge branch 'main' into kk-dashboard-card
kruulik Feb 26, 2026
91eab56
Update clients/fidesui/src/hoc/CustomStatistic.tsx
kruulik Feb 26, 2026
7181530
comments and types
kruulik Feb 26, 2026
68b6f6c
title and tabs
kruulik Feb 26, 2026
b5fb4a8
Merge branch 'main' into kk-dashboard-card
kruulik Feb 26, 2026
5c226cd
Update clients/fidesui/src/components/dashboard/StatCard.stories.tsx
kruulik Feb 27, 2026
9b6f704
Update clients/fidesui/src/hoc/CustomStatistic.tsx
kruulik Feb 27, 2026
4634731
lint
kruulik Feb 27, 2026
4614c67
Update clients/fidesui/src/hoc/CustomCard.module.scss
kruulik Feb 27, 2026
4796ea1
comments
kruulik Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/dashboard-card.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type: Added
description: Create Card and Statistic HOCs, demonstrate in StatCard
pr: 7477
labels: []
28 changes: 14 additions & 14 deletions clients/fidesui/src/components/charts/RadarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export const RadarChart = ({
data={empty ? EMPTY_PLACEHOLDER_DATA : data}
cx="50%"
cy="50%"
outerRadius="70%"
outerRadius="80%"
>
<ChartGradient
id={gradientId}
Expand All @@ -149,19 +149,6 @@ export const RadarChart = ({

<PolarRadiusAxis tick={false} axisLine={false} />

{!empty && (
<PolarAngleAxis
dataKey="subject"
tick={
<RadarTick
data={data!}
statusColors={STATUS_COLORS}
chartColor={chartColor}
/>
}
/>
)}

<Radar
dataKey="value"
stroke={chartColor}
Expand All @@ -187,6 +174,19 @@ export const RadarChart = ({
animationDuration={animationDuration}
animationEasing={CHART_ANIMATION.easing}
/>

{!empty && (
<PolarAngleAxis
dataKey="subject"
tick={
<RadarTick
data={data!}
statusColors={STATUS_COLORS}
chartColor={chartColor}
/>
}
/>
)}
</RechartsRadarChart>
</ResponsiveContainer>
</div>
Expand Down
293 changes: 293 additions & 0 deletions clients/fidesui/src/components/dashboard/StatCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
import type { Meta, StoryObj } from "@storybook/react-vite";
import { Flex, theme } from "antd";
import { Card, Statistic } from "fidesui";
import { useState } from "react";

import { RadarChart } from "../charts/RadarChart";
import { Sparkline } from "../charts/Sparkline";

const upwardTrendData = [12, 18, 15, 22, 28, 25, 34, 30, 38, 42, 39, 47];

const downwardTrendData = [47, 42, 45, 38, 34, 36, 28, 24, 26, 18, 15, 12];

const neutralTrendData = [28, 32, 25, 30, 27, 33, 26, 31, 28, 34, 29, 30];

const meta = {
title: "Dashboard/Card",
component: Card,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
argTypes: {
coverPosition: {
description:
"Position of the cover relative to the card body. Use `bottom` to place a sparkline below the stat.",
control: "radio",
options: ["top", "bottom"],
},
cover: {
description:
'Cover content (e.g. a sparkline). Set `coverPosition="bottom"` to display it below the card body.',
control: false,
},
children: {
description: "Card body content, e.g. `<Statistic />` component.",
control: false,
},
},
decorators: [
(Story) => (
<div className="w-full min-w-72">
<Story />
</div>
),
],
} satisfies Meta<typeof Card>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
variant: "borderless",
coverPosition: "bottom",
title: "Default Card",
},
render: (args) => {
const { token } = theme.useToken();
return (
<Card {...args}>
<Statistic title="Data Sharing" value="15,112,893" />
<Statistic
trend="up"
value="112,893"
prefix={<ArrowUpOutlined />}
valueStyle={{ fontSize: token.fontSize }}
/>
</Card>
);
},
};

export const NoTitle: Story = {
args: {
variant: "borderless",
coverPosition: "bottom",
},
render: (args) => {
const { token } = theme.useToken();
return (
<Card {...args}>
<Statistic title="Data Sharing" value="15,112,893" />
<Statistic
trend="up"
value="112,893"
prefix={<ArrowUpOutlined />}
valueStyle={{ fontSize: token.fontSize }}
/>
</Card>
);
},
};

export const WithSparkline: Story = {
args: {
variant: "borderless",
cover: (
<div className="h-16">
<Sparkline data={downwardTrendData} />
</div>
),
title: "Active Users",
coverPosition: "bottom",
},
render: (args) => {
const { token } = theme.useToken();
return (
<Card {...args}>
<Statistic title="Active Users" value="8,430" />
<Statistic
trend="down"
value="1,204"
prefix={<ArrowDownOutlined />}
valueStyle={{ fontSize: token.fontSize }}
/>
</Card>
);
},
};

export const WithRadarChart: Story = {
args: {
variant: "borderless",
},
render: (args) => {
const { token } = theme.useToken();
return (
<Card title="Posture" {...args}>
<>
<Statistic value="67" />
<Statistic
trend="up"
value="4"
prefix={<ArrowUpOutlined />}
valueStyle={{ fontSize: token.fontSize }}
/>
<div
className="h-48"
style={{
marginLeft: -token.paddingLG,
marginRight: -token.paddingLG,
}}
>
<RadarChart
data={[
{ subject: "Coverage", value: 80 },
{ subject: "Classification", value: 65 },
{ subject: "Consent", value: 50, status: "warning" },
{ subject: "DSR", value: 30, status: "error" },
{ subject: "Enforcement", value: 70 },
{ subject: "Assessments", value: 45, status: "warning" },
]}
/>
</div>
</>
</Card>
);
},
};

export const Loading: Story = {
args: {
variant: "borderless",
loading: true,
children: <Statistic title="Data Sharing" value="15,112,893" />,
},
};

const TAB_CONTENT: Record<string, string> = {
a: "Content A",
b: "Content B",
c: "Content C",
};

export const WithTabs: Story = {
args: {
variant: "borderless",
title: "Overview",
},
render: (args) => {
const [activeTab, setActiveTab] = useState("a");
return (
<Card
{...args}
tabList={[
{ key: "a", label: "Tab A" },
{ key: "b", label: "Tab B" },
{ key: "c", label: "Tab C" },
]}
activeTabKey={activeTab}
onTabChange={setActiveTab}
>
<Flex align="center" justify="center" className="bg-gray-50 p-8">
{TAB_CONTENT[activeTab]}
</Flex>
</Card>
);
},
};

export const WithInlineTabs: Story = {
args: {
variant: "borderless",
title: "Overview",
headerLayout: "inline",
},
render: (args) => {
const [activeTab, setActiveTab] = useState("a");
return (
<div className="w-[400px]">
<Card
{...args}
tabList={[
{ key: "a", label: "Tab A" },
{ key: "b", label: "Tab B" },
{ key: "c", label: "Tab C" },
]}
activeTabKey={activeTab}
onTabChange={setActiveTab}
>
<div className="flex items-center justify-center bg-gray-50 p-8">
{TAB_CONTENT[activeTab]}
</div>
</Card>
</div>
);
},
};

export const DashboardRow: Story = {
args: {},
decorators: [
() => {
const { token } = theme.useToken();
return (
<div className="grid grid-cols-3 gap-4 w-[900px]">
<Card
variant="borderless"
title="Data Sharing"
className="overflow-clip"
cover={
<div className="h-16">
<Sparkline data={upwardTrendData} />
</div>
}
coverPosition="bottom"
>
<Statistic value="15,112,893" />
<Statistic
trend="up"
value="112,893"
prefix={<ArrowUpOutlined />}
valueStyle={{ fontSize: token.fontSize }}
/>
</Card>
<Card
variant="borderless"
title="Active Users"
className="overflow-clip"
cover={
<div className="h-16">
<Sparkline data={downwardTrendData} />
</div>
}
coverPosition="bottom"
>
<Statistic value="8,430" />
<Statistic
trend="down"
value="1,204"
prefix={<ArrowDownOutlined />}
valueStyle={{ fontSize: token.fontSize }}
/>
</Card>
<Card
variant="borderless"
title="Total Requests"
className="overflow-clip"
cover={
<div className="h-16">
<Sparkline data={neutralTrendData} />
</div>
}
coverPosition="bottom"
>
<Statistic value="3,201,554" />
</Card>
</div>
);
},
],
};
Loading
Loading