Center metric layer on pie chart + cleanup (#16105)

https://github.com/user-attachments/assets/bb48bebe-d0be-4006-8f63-e686c70c0011

---------

Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
This commit is contained in:
nitin
2025-11-27 16:45:48 +05:30
committed by GitHub
parent 1a45576990
commit d7f8c2d338
64 changed files with 343 additions and 826 deletions
@@ -53,7 +53,6 @@ export const WithCustomColors: Story = {
min: 0,
max: 100,
color: 'purple',
to: '/metrics/progress',
label: 'Progress',
},
displayType: 'number',
@@ -83,7 +82,6 @@ export const Default: Story = {
value: 0.5,
min: 0,
max: 1,
to: '/metrics/conversion',
label: 'Conversion rate',
},
displayType: 'percentage',
@@ -120,7 +118,6 @@ export const Catalog: Story = {
min: 0,
max: 1,
label: 'Percentage',
to: '/metrics/catalog',
},
displayType: 'percentage' as const,
id: `gauge-chart-catalog-${value}`,
@@ -157,7 +154,6 @@ export const WithoutValue: Story = {
value: 65,
min: 0,
max: 100,
to: '/metrics/conversion-without-value',
label: 'Conversion rate',
},
displayType: 'percentage',
@@ -186,7 +182,6 @@ export const Revenue: Story = {
value: 750,
min: 0,
max: 1000,
to: '/financials/revenue',
label: 'Revenue',
},
displayType: 'shortNumber',
@@ -215,7 +210,6 @@ export const Temperature: Story = {
value: 22,
min: -10,
max: 40,
to: '/sensors/temperature',
label: 'Temperature',
},
suffix: '°C',
@@ -244,7 +238,6 @@ export const Storage: Story = {
value: 384,
min: 0,
max: 512,
to: '/system/storage',
label: 'Storage Used',
},
suffix: ' GB',
@@ -273,7 +266,6 @@ export const Rating: Story = {
value: 4.2,
min: 0,
max: 5,
to: '/reviews/rating',
label: 'Average Rating',
},
suffix: ' ⭐',
@@ -303,7 +295,6 @@ export const WithoutLegend: Story = {
value: 65,
min: 0,
max: 100,
to: '/metrics/conversion-no-legend',
label: 'Conversion rate',
},
displayType: 'percentage',
@@ -1,12 +1,13 @@
import { type Meta, type StoryObj } from '@storybook/react';
import { CatalogDecorator, ComponentDecorator } from 'twenty-ui/testing';
import { GraphWidgetPieChart } from '@/page-layout/widgets/graph/graphWidgetPieChart/components/GraphWidgetPieChart';
import { CatalogDecorator, ComponentDecorator } from 'twenty-ui/testing';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
const meta: Meta<typeof GraphWidgetPieChart> = {
title: 'Modules/PageLayout/Widgets/GraphWidgetPieChart',
component: GraphWidgetPieChart,
decorators: [ComponentDecorator],
decorators: [ComponentDecorator, I18nFrontDecorator],
parameters: {
layout: 'centered',
},
@@ -33,6 +34,12 @@ const meta: Meta<typeof GraphWidgetPieChart> = {
id: {
control: 'text',
},
showCenterMetric: {
control: 'boolean',
},
showDataLabels: {
control: 'boolean',
},
},
};
@@ -43,91 +50,14 @@ const Container = ({ children }: { children: React.ReactNode }) => (
<div style={{ width: '300px', height: '300px' }}>{children}</div>
);
export const WithCustomColors: Story = {
args: {
data: [
{
id: 'segment1',
value: 30,
label: 'Segment A',
color: 'blue',
to: '/segments/a',
},
{
id: 'segment2',
value: 25,
label: 'Segment B',
color: 'purple',
to: '/segments/b',
},
{
id: 'segment3',
value: 20,
label: 'Segment C',
color: 'turquoise',
to: '/segments/c',
},
{
id: 'segment4',
value: 15,
label: 'Segment D',
color: 'orange',
to: '/segments/d',
},
{
id: 'segment5',
value: 10,
label: 'Segment E',
color: 'pink',
to: '/segments/e',
},
],
showLegend: true,
id: 'pie-chart-custom-colors',
},
render: (args) => (
<Container>
<GraphWidgetPieChart
data={args.data}
displayType={args.displayType}
prefix={args.prefix}
suffix={args.suffix}
decimals={args.decimals}
showLegend={args.showLegend}
id={args.id}
/>
</Container>
),
};
export const Default: Story = {
args: {
data: [
{
id: 'qualified',
value: 35,
label: 'Qualified',
to: '/leads/qualified',
},
{
id: 'contacted',
value: 25,
label: 'Contacted',
to: '/leads/contacted',
},
{
id: 'unqualified',
value: 20,
label: 'Unqualified',
to: '/leads/unqualified',
},
{ id: 'proposal', value: 15, label: 'Proposal', to: '/leads/proposal' },
{
id: 'negotiation',
value: 5,
label: 'Negotiation',
to: '/leads/negotiation',
},
{ id: 'Qualified', value: 35 },
{ id: 'Contacted', value: 25 },
{ id: 'Unqualified', value: 20 },
{ id: 'Proposal', value: 15 },
{ id: 'Negotiation', value: 5 },
],
showLegend: true,
id: 'pie-chart-default',
@@ -147,33 +77,54 @@ export const Default: Story = {
),
};
export const WithCenterMetric: Story = {
args: {
data: [
{ id: 'Qualified', value: 35 },
{ id: 'Contacted', value: 25 },
{ id: 'Unqualified', value: 20 },
],
showCenterMetric: true,
id: 'pie-chart-with-center-metric',
},
render: (args) => (
<Container>
<GraphWidgetPieChart
data={args.data}
showCenterMetric={args.showCenterMetric}
id={args.id}
/>
</Container>
),
};
export const WithDataLabels: Story = {
args: {
data: [
{ id: 'Qualified', value: 35 },
{ id: 'Contacted', value: 25 },
{ id: 'Unqualified', value: 20 },
],
showDataLabels: true,
id: 'pie-chart-with-data-labels',
},
render: (args) => (
<Container>
<GraphWidgetPieChart
data={args.data}
showDataLabels={args.showDataLabels}
id={args.id}
/>
</Container>
),
};
export const Revenue: Story = {
args: {
data: [
{
id: 'product-a',
value: 420000,
label: 'Product A',
to: '/products/a/revenue',
},
{
id: 'product-b',
value: 380000,
label: 'Product B',
to: '/products/b/revenue',
},
{
id: 'product-c',
value: 250000,
label: 'Product C',
to: '/products/c/revenue',
},
{
id: 'product-d',
value: 180000,
label: 'Product D',
to: '/products/d/revenue',
},
{ id: 'Product A', value: 420000 },
{ id: 'Product B', value: 380000 },
{ id: 'Product C', value: 250000 },
{ id: 'Product D', value: 180000 },
],
prefix: '$',
displayType: 'shortNumber',
@@ -198,19 +149,9 @@ export const Revenue: Story = {
export const TaskStatus: Story = {
args: {
data: [
{
id: 'completed',
value: 45,
label: 'Completed',
to: '/tasks/completed',
},
{
id: 'in-progress',
value: 30,
label: 'In Progress',
to: '/tasks/in-progress',
},
{ id: 'todo', value: 25, label: 'To Do', to: '/tasks/todo' },
{ id: 'Completed', value: 45 },
{ id: 'In Progress', value: 30 },
{ id: 'To Do', value: 25 },
],
displayType: 'percentage',
showLegend: true,
@@ -234,8 +175,8 @@ export const TaskStatus: Story = {
export const TwoSlices: Story = {
args: {
data: [
{ id: 'active', value: 75, label: 'Active', to: '/users/active' },
{ id: 'inactive', value: 25, label: 'Inactive', to: '/users/inactive' },
{ id: 'Active', value: 75 },
{ id: 'Inactive', value: 25 },
],
displayType: 'percentage',
showLegend: true,
@@ -259,14 +200,14 @@ export const TwoSlices: Story = {
export const ManySlices: Story = {
args: {
data: [
{ id: 'category-1', value: 20, label: 'Category 1', to: '/categories/1' },
{ id: 'category-2', value: 18, label: 'Category 2', to: '/categories/2' },
{ id: 'category-3', value: 16, label: 'Category 3', to: '/categories/3' },
{ id: 'category-4', value: 14, label: 'Category 4', to: '/categories/4' },
{ id: 'category-5', value: 12, label: 'Category 5', to: '/categories/5' },
{ id: 'category-6', value: 10, label: 'Category 6', to: '/categories/6' },
{ id: 'category-7', value: 6, label: 'Category 7' },
{ id: 'category-8', value: 4, label: 'Category 8' },
{ id: 'Category 1', value: 20 },
{ id: 'Category 2', value: 18 },
{ id: 'Category 3', value: 16 },
{ id: 'Category 4', value: 14 },
{ id: 'Category 5', value: 12 },
{ id: 'Category 6', value: 10 },
{ id: 'Category 7', value: 6 },
{ id: 'Category 8', value: 4 },
],
showLegend: true,
id: 'pie-chart-many-slices',
@@ -289,9 +230,9 @@ export const ManySlices: Story = {
export const WithoutLegend: Story = {
args: {
data: [
{ id: 'web', value: 45, label: 'Web', to: '/platforms/web' },
{ id: 'mobile', value: 35, label: 'Mobile', to: '/platforms/mobile' },
{ id: 'desktop', value: 20, label: 'Desktop', to: '/platforms/desktop' },
{ id: 'Web', value: 45 },
{ id: 'Mobile', value: 35 },
{ id: 'Desktop', value: 20 },
],
displayType: 'percentage',
showLegend: false,
@@ -315,10 +256,10 @@ export const WithoutLegend: Story = {
export const MarketShare: Story = {
args: {
data: [
{ id: 'brand-a', value: 35.5, label: 'Brand A', to: '/market/brand-a' },
{ id: 'brand-b', value: 28.2, label: 'Brand B', to: '/market/brand-b' },
{ id: 'brand-c', value: 18.7, label: 'Brand C', to: '/market/brand-c' },
{ id: 'others', value: 17.6, label: 'Others', to: '/market/others' },
{ id: 'Brand A', value: 35.5 },
{ id: 'Brand B', value: 28.2 },
{ id: 'Brand C', value: 18.7 },
{ id: 'Others', value: 17.6 },
],
displayType: 'percentage',
showLegend: true,
@@ -342,20 +283,10 @@ export const MarketShare: Story = {
export const Storage: Story = {
args: {
data: [
{
id: 'documents',
value: 125,
label: 'Documents',
to: '/storage/documents',
},
{ id: 'media', value: 280, label: 'Media', to: '/storage/media' },
{
id: 'applications',
value: 95,
label: 'Applications',
to: '/storage/applications',
},
{ id: 'system', value: 50, label: 'System', to: '/storage/system' },
{ id: 'Documents', value: 125 },
{ id: 'Media', value: 280 },
{ id: 'Applications', value: 95 },
{ id: 'System', value: 50 },
],
suffix: ' GB',
showLegend: true,
@@ -377,7 +308,7 @@ export const Storage: Story = {
};
export const Catalog: Story = {
decorators: [CatalogDecorator],
decorators: [CatalogDecorator, I18nFrontDecorator],
parameters: {
catalog: {
dimensions: [
@@ -387,23 +318,23 @@ export const Catalog: Story = {
props: (sliceCount: number) => {
const dataMap: Record<
number,
Array<{ id: string; value: number; label?: string }>
Array<{ id: string; value: number }>
> = {
2: [
{ id: 'yes', value: 65, label: 'Yes' },
{ id: 'no', value: 35, label: 'No' },
{ id: 'Yes', value: 65 },
{ id: 'No', value: 35 },
],
3: [
{ id: 'gold', value: 45, label: 'Gold' },
{ id: 'silver', value: 35, label: 'Silver' },
{ id: 'bronze', value: 20, label: 'Bronze' },
{ id: 'Gold', value: 45 },
{ id: 'Silver', value: 35 },
{ id: 'Bronze', value: 20 },
],
5: [
{ id: 'item-1', value: 30, label: 'Item 1' },
{ id: 'item-2', value: 25, label: 'Item 2' },
{ id: 'item-3', value: 20, label: 'Item 3' },
{ id: 'item-4', value: 15, label: 'Item 4' },
{ id: 'item-5', value: 10, label: 'Item 5' },
{ id: 'Item 1', value: 30 },
{ id: 'Item 2', value: 25 },
{ id: 'Item 3', value: 20 },
{ id: 'Item 4', value: 15 },
{ id: 'Item 5', value: 10 },
],
};