From 28333815d50edde201c6ed2ac3a8a0d173971952 Mon Sep 17 00:00:00 2001
From: nitin <142569587+ehconitin@users.noreply.github.com>
Date: Thu, 30 Oct 2025 19:07:59 +0530
Subject: [PATCH] update chart tooltip designs (#15426)
closes
https://discord.com/channels/1130383047699738754/1430845815634657320
~~TODO: add truncation~~
---
.../graph/components/GraphWidgetTooltip.tsx | 132 ++++++++++++++----
.../GraphWidgetBarChart.stories.tsx | 120 ++++++++++++++++
.../GraphWidgetTooltip.stories.tsx | 40 ++++--
.../components/GraphWidgetBarChart.tsx | 10 +-
.../hooks/useBarChartTooltip.ts | 41 ++++--
.../components/GraphWidgetLineChart.tsx | 4 +-
.../hooks/useLineChartTooltip.ts | 6 +-
7 files changed, 292 insertions(+), 61 deletions(-)
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetTooltip.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetTooltip.tsx
index 44e34ab540d..fcf419ce0e3 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetTooltip.tsx
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetTooltip.tsx
@@ -3,23 +3,36 @@ import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { IconArrowUpRight } from 'twenty-ui/display';
-const StyledTooltipContent = styled.div`
+const StyledTooltip = styled.div`
background: ${({ theme }) => theme.background.primary};
- border: 1px solid ${({ theme }) => theme.border.color.medium};
- border-radius: ${({ theme }) => theme.border.radius.sm};
+ border: 1px solid ${({ theme }) => theme.border.color.light};
+ border-radius: ${({ theme }) => theme.border.radius.md};
box-shadow: ${({ theme }) => theme.boxShadow.strong};
display: flex;
flex-direction: column;
- gap: ${({ theme }) => theme.spacing(3)};
- padding: ${({ theme }) => theme.spacing(3)};
+ gap: 2px;
+ max-width: min(300px, calc(100vw - 40px));
+ min-width: 160px;
pointer-events: none;
`;
+const StyledTooltipContent = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: ${({ theme }) => theme.spacing(3)};
+ padding: ${({ theme }) => theme.spacing(2)};
+`;
+
const StyledTooltipRow = styled.div`
align-items: center;
- color: ${({ theme }) => theme.font.color.extraLight};
display: flex;
- font-size: ${({ theme }) => theme.font.size.xs};
+
+ gap: ${({ theme }) => theme.spacing(2)};
+`;
+
+const StyledTooltipRowContainer = styled.div`
+ display: flex;
+ flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
`;
@@ -31,22 +44,68 @@ const StyledDot = styled.div<{ $color: string }>`
flex-shrink: 0;
`;
-const StyledTooltipValue = styled.span`
- margin-left: auto;
- white-space: nowrap;
-`;
-
const StyledTooltipLink = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.light};
cursor: default;
display: flex;
+ justify-content: space-between;
+ height: ${({ theme }) => theme.spacing(6)};
+ font-weight: ${({ theme }) => theme.font.weight.regular};
+ padding-inline: ${({ theme }) => theme.spacing(2)};
+ line-height: 140%;
+`;
+
+const StyledTooltipSeparator = styled.div`
+ background-color: ${({ theme }) => theme.border.color.light};
+ min-height: 1px;
+ width: 100%;
`;
const StyledTooltipHeader = styled.div`
color: ${({ theme }) => theme.font.color.primary};
- font-size: ${({ theme }) => theme.font.size.sm};
+ font-size: ${({ theme }) => theme.font.size.xs};
font-weight: ${({ theme }) => theme.font.weight.medium};
+ line-height: 140%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+`;
+
+const StyledTooltipRowRightContent = styled.div`
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+ font-size: ${({ theme }) => theme.font.size.xs};
+ color: ${({ theme }) => theme.font.color.extraLight};
+ font-weight: ${({ theme }) => theme.font.weight.regular};
+ gap: ${({ theme }) => theme.spacing(2)};
+ min-width: 0;
+ width: 100%;
+`;
+
+const StyledTooltipLabel = styled.span`
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+`;
+
+const StyledTooltipValue = styled.span`
+ flex-shrink: 0;
+ font-weight: ${({ theme }) => theme.font.weight.medium};
+ white-space: nowrap;
+`;
+
+const StyledHorizontalSectionPadding = styled.div<{
+ $addTop?: boolean;
+ $addBottom?: boolean;
+}>`
+ padding-inline: ${({ theme }) => theme.spacing(1)};
+ margin-top: ${({ $addTop, theme }) => ($addTop ? theme.spacing(1) : 0)};
+ margin-bottom: ${({ $addBottom, theme }) =>
+ $addBottom ? theme.spacing(1) : 0};
`;
export type GraphWidgetTooltipItem = {
@@ -58,32 +117,47 @@ export type GraphWidgetTooltipItem = {
type GraphWidgetTooltipProps = {
items: GraphWidgetTooltipItem[];
showClickHint?: boolean;
- title?: string;
+ indexLabel?: string;
};
export const GraphWidgetTooltip = ({
items,
showClickHint = false,
- title,
+ indexLabel,
}: GraphWidgetTooltipProps) => {
const theme = useTheme();
return (
-
- {title && {title}}
- {items.map((item, index) => (
-
-
- {item.label}
- {item.formattedValue}
-
- ))}
+
+
+
+ {indexLabel && (
+ {indexLabel}
+ )}
+
+ {items.map((item, index) => (
+
+
+
+ {item.label}
+ {item.formattedValue}
+
+
+ ))}
+
+
+
{showClickHint && (
-
- {t`Click to see data`}
-
-
+ <>
+
+
+
+ {t`Click to see data`}
+
+
+
+ >
)}
-
+
);
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetBarChart.stories.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetBarChart.stories.tsx
index 66f58fd2a5e..7ed4ef14474 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetBarChart.stories.tsx
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetBarChart.stories.tsx
@@ -253,6 +253,8 @@ export const Stacked: Story = {
data={args.data}
indexBy={args.indexBy}
keys={args.keys}
+ groupMode={args.groupMode}
+ seriesLabels={args.seriesLabels}
showLegend={args.showLegend}
showGrid={args.showGrid}
xAxisLabel={args.xAxisLabel}
@@ -492,6 +494,124 @@ export const Currency: Story = {
),
};
+export const GroupedWithAllBarsTooltip: Story = {
+ args: {
+ data: [
+ {
+ period: 'January',
+ lastYear: 18000,
+ thisYear: 19500,
+ to: '/comparison/january',
+ },
+ {
+ period: 'February',
+ lastYear: 20000,
+ thisYear: 20000,
+ to: '/comparison/february',
+ },
+ {
+ period: 'March',
+ lastYear: 22000,
+ thisYear: 24500,
+ to: '/comparison/march',
+ },
+ {
+ period: 'April',
+ lastYear: 21000,
+ thisYear: 23000,
+ to: '/comparison/april',
+ },
+ ],
+ indexBy: 'period',
+ keys: ['lastYear', 'thisYear'],
+ seriesLabels: {
+ lastYear: 'Last year',
+ thisYear: 'This year',
+ },
+ displayType: 'shortNumber',
+ showLegend: true,
+ showGrid: true,
+ xAxisLabel: 'Period',
+ yAxisLabel: 'Revenue',
+ groupMode: 'grouped',
+ id: 'bar-chart-grouped-all-tooltip',
+ enableGroupTooltip: true,
+ },
+ render: (args) => (
+
+
+
+ ),
+};
+
+export const GroupedDefaultTooltip: Story = {
+ args: {
+ data: [
+ {
+ period: 'January',
+ lastYear: 18000,
+ thisYear: 19500,
+ to: '/comparison/january',
+ },
+ {
+ period: 'February',
+ lastYear: 20000,
+ thisYear: 20000,
+ to: '/comparison/february',
+ },
+ {
+ period: 'March',
+ lastYear: 22000,
+ thisYear: 24500,
+ to: '/comparison/march',
+ },
+ ],
+ indexBy: 'period',
+ keys: ['lastYear', 'thisYear'],
+ seriesLabels: {
+ lastYear: 'Last year',
+ thisYear: 'This year',
+ },
+ displayType: 'shortNumber',
+ showLegend: true,
+ showGrid: true,
+ xAxisLabel: 'Period',
+ yAxisLabel: 'Revenue',
+ groupMode: 'grouped',
+ id: 'bar-chart-grouped-default',
+ },
+ render: (args) => (
+
+
+
+ ),
+};
+
export const Catalog: Story = {
decorators: [CatalogDecorator],
parameters: {
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetTooltip.stories.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetTooltip.stories.tsx
index 202d447067a..3678cddb701 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetTooltip.stories.tsx
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetTooltip.stories.tsx
@@ -25,6 +25,7 @@ export const Default: Story = {
},
],
showClickHint: false,
+ indexLabel: 'March 09, 2024',
},
};
@@ -45,21 +46,38 @@ export const MultipleItems: Story = {
args: {
items: [
{
- label: 'Q1',
- formattedValue: '$12,345',
+ label: 'Last year',
+ formattedValue: '20k',
dotColor: 'blue',
},
{
- label: 'Q2',
- formattedValue: '$23,456',
- dotColor: 'green',
- },
- {
- label: 'Q3',
- formattedValue: '$34,567',
- dotColor: 'red',
+ label: 'This year',
+ formattedValue: '20k',
+ dotColor: 'purple',
},
],
- showClickHint: false,
+ showClickHint: true,
+ indexLabel: 'February 2',
+ },
+};
+
+export const SuperLongText: Story = {
+ args: {
+ items: [
+ {
+ label:
+ 'Total Annual Recurring Revenue (North America Region including Canada)',
+ formattedValue: '$2,450,000',
+ dotColor: 'blue',
+ },
+ {
+ label: 'Customer Acquisition Cost (Marketing & Sales Combined)',
+ formattedValue: '$125,500',
+ dotColor: 'purple',
+ },
+ ],
+ showClickHint: true,
+ indexLabel:
+ 'Q4 2024 Financial Year End (October - December) - North America Regional Performance Summary',
},
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx
index cc23e2fc50d..e3de7490587 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx
@@ -42,6 +42,7 @@ type GraphWidgetBarChartProps = {
seriesLabels?: Record;
rangeMin?: number;
rangeMax?: number;
+ enableGroupTooltip?: boolean;
} & GraphValueFormatOptions;
const StyledContainer = styled.div`
@@ -69,6 +70,7 @@ export const GraphWidgetBarChart = ({
seriesLabels,
rangeMin,
rangeMax,
+ enableGroupTooltip,
displayType,
decimals,
prefix,
@@ -81,6 +83,9 @@ export const GraphWidgetBarChart = ({
const [chartHeight, setChartHeight] = useState(0);
const containerRef = useRef(null);
+ const shouldEnableGroupTooltip =
+ enableGroupTooltip ?? groupMode === 'stacked';
+
const formatOptions: GraphValueFormatOptions = {
displayType,
decimals,
@@ -112,6 +117,7 @@ export const GraphWidgetBarChart = ({
data,
indexBy,
formatOptions,
+ enableGroupTooltip: shouldEnableGroupTooltip,
});
const isLargeChart = data.length * keys.length > LABEL_THRESHOLD;
@@ -140,9 +146,9 @@ export const GraphWidgetBarChart = ({
return (
);
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartTooltip.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartTooltip.ts
index bca10a61695..105c9ed0a8a 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartTooltip.ts
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartTooltip.ts
@@ -13,6 +13,7 @@ type UseBarChartTooltipProps = {
data: BarChartDataItem[];
indexBy: string;
formatOptions: GraphValueFormatOptions;
+ enableGroupTooltip?: boolean;
};
export const useBarChartTooltip = ({
@@ -21,26 +22,38 @@ export const useBarChartTooltip = ({
data,
indexBy,
formatOptions,
+ enableGroupTooltip = true,
}: UseBarChartTooltipProps) => {
const renderTooltip = (datum: ComputedDatum) => {
- const hoveredKey = hoveredBar?.key;
- if (!isDefined(hoveredKey)) return null;
-
- const enrichedKey = enrichedKeys.find((item) => item.key === hoveredKey);
- if (!enrichedKey) return null;
-
const dataItem = data.find((d) => d[indexBy] === datum.indexValue);
- const seriesValue = Number(datum.data[hoveredKey] || 0);
- const tooltipItem = {
- label: enrichedKey.label,
- formattedValue: formatGraphValue(seriesValue, formatOptions),
- dotColor: enrichedKey.colorScheme.solid,
- };
+
+ let keysToShow: BarChartEnrichedKey[];
+
+ if (enableGroupTooltip) {
+ keysToShow = enrichedKeys;
+ } else {
+ const hoveredKey = hoveredBar?.key;
+ if (!isDefined(hoveredKey)) return null;
+
+ const enrichedKey = enrichedKeys.find((item) => item.key === hoveredKey);
+ if (!isDefined(enrichedKey)) return null;
+
+ keysToShow = [enrichedKey];
+ }
+
+ const tooltipItems = keysToShow.map((enrichedKey) => {
+ const seriesValue = Number(datum.data[enrichedKey.key] ?? 0);
+ return {
+ label: enrichedKey.label,
+ formattedValue: formatGraphValue(seriesValue, formatOptions),
+ dotColor: enrichedKey.colorScheme.solid,
+ };
+ });
return {
- tooltipItem,
+ tooltipItems,
showClickHint: isDefined(dataItem?.to),
- title: String(datum.indexValue),
+ indexLabel: String(datum.indexValue),
};
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx
index 411a9c67388..ffa9aefe99b 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx
@@ -144,7 +144,7 @@ export const GraphWidgetLineChart = ({
);
};
@@ -158,7 +158,7 @@ export const GraphWidgetLineChart = ({
);
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartTooltip.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartTooltip.ts
index a79378afc5f..257318a44d5 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartTooltip.ts
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartTooltip.ts
@@ -31,7 +31,7 @@ export const useLineChartTooltip = ({
return {
items: [],
showClickHint: false,
- title: undefined,
+ indexLabel: undefined,
};
}
@@ -67,7 +67,7 @@ export const useLineChartTooltip = ({
return {
items: tooltipItems,
showClickHint: hasClickablePoint,
- title: isDefined(xValue) ? String(xValue) : undefined,
+ indexLabel: isDefined(xValue) ? String(xValue) : undefined,
};
};
@@ -90,7 +90,7 @@ export const useLineChartTooltip = ({
},
],
showClickHint: isDefined(dataPoint?.to),
- title: isDefined(point.data.x) ? String(point.data.x) : undefined,
+ indexLabel: isDefined(point.data.x) ? String(point.data.x) : undefined,
};
};