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, }; };