Fix and improve chart type selection (#16033)

The new version of the side panel is smaller so it introduced a
regression on the chart type selection because there was not enough
space for all the chart types to fit with the label.

This PR removes the label to gain some space and display the chart type
label in a tooltip.

## Before

<img width="834" height="1406" alt="CleanShot 2025-11-24 at 17 33 45@2x"
src="https://github.com/user-attachments/assets/76bbea54-af16-4643-9de6-ab3da6741e11"
/>


## After

### Without disabled charts


https://github.com/user-attachments/assets/74d62b86-e440-41f5-a3e6-c5754940c28e

### With disabled charts


https://github.com/user-attachments/assets/f4ce3a3a-5dee-4f41-8b96-5f671794197d
This commit is contained in:
Raphaël Bosi
2025-11-24 17:44:28 +01:00
committed by GitHub
parent d526b07078
commit 86aff36035
5 changed files with 92 additions and 35 deletions
@@ -47,17 +47,18 @@ export const ChartTypeSelectionSection = ({
return (
<MenuPicker
id={graphType}
selected={currentGraphType === graphType}
key={graphType}
icon={GRAPH_TYPE_INFORMATION[graphType].icon}
onClick={() => {
setCurrentGraphType(graphType);
}}
label={
isDisabled ? t`Soon` : t(GRAPH_TYPE_INFORMATION[graphType].label)
}
showLabel
disabled={isDisabled}
tooltipContent={
isDisabled ? t`Soon` : t(GRAPH_TYPE_INFORMATION[graphType].label)
}
/>
);
})}
@@ -26,32 +26,32 @@ export const GRAPH_TYPE_INFORMATION: Record<
}
> = {
[GraphType.VERTICAL_BAR]: {
label: msg`Vertical`,
label: msg`Vertical Bar Chart`,
icon: IconChartBar,
settings: getBarChartSettings(GraphType.VERTICAL_BAR),
},
[GraphType.HORIZONTAL_BAR]: {
label: msg`Horizontal`,
label: msg`Horizontal Bar Chart`,
icon: IconChartBarHorizontal,
settings: getBarChartSettings(GraphType.HORIZONTAL_BAR),
},
[GraphType.PIE]: {
label: msg`Pie`,
label: msg`Pie Chart`,
icon: IconChartPie,
settings: PIE_CHART_SETTINGS,
},
[GraphType.LINE]: {
label: msg`Line`,
label: msg`Line Chart`,
icon: IconChartLine,
settings: LINE_CHART_SETTINGS,
},
[GraphType.AGGREGATE]: {
label: msg`Aggregate`,
label: msg`Aggregate Chart`,
icon: IconSum,
settings: AGGREGATE_CHART_SETTINGS,
},
[GraphType.GAUGE]: {
label: msg`Gauge`,
label: msg`Gauge Chart`,
icon: IconGauge,
settings: GAUGE_CHART_SETTINGS,
},
@@ -1,4 +1,5 @@
import styled from '@emotion/styled';
import { isDefined } from 'twenty-shared/utils';
import {
IconAlertTriangle,
IconInfoCircle,
@@ -66,7 +67,7 @@ export const SidePanelInformationBanner = ({
)}
</StyledIconContainer>
<StyledMessage>{message}</StyledMessage>
{tooltipMessage && (
{isDefined(tooltipMessage) && (
<AppTooltip
anchorSelect={`[data-tooltip-id='${tooltipId}']`}
content={tooltipMessage}
@@ -1,6 +1,13 @@
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { type IconComponent } from '@ui/display';
import { isNonEmptyString } from '@sniptt/guards';
import {
AppTooltip,
TooltipDelay,
TooltipPosition,
type IconComponent,
} from '@ui/display';
import { isDefined } from 'twenty-shared/utils';
const StyledMenuPicker = styled.button<{
selected: boolean;
@@ -101,17 +108,22 @@ const StyledLabel = styled.div<{
`;
export type MenuPickerProps = {
id: string;
className?: string;
disabled?: boolean;
icon: IconComponent;
label: string;
label?: string;
onClick?: () => void;
selected?: boolean;
showLabel?: boolean;
testId?: string;
tooltipContent?: string;
tooltipDelay?: TooltipDelay;
tooltipOffset?: number;
};
export const MenuPicker = ({
id,
icon: Icon,
label,
selected = false,
@@ -120,29 +132,47 @@ export const MenuPicker = ({
onClick,
className,
testId,
tooltipContent,
tooltipDelay = TooltipDelay.noDelay,
tooltipOffset = 5,
}: MenuPickerProps) => {
const theme = useTheme();
return (
<StyledMenuPicker
selected={selected}
disabled={disabled}
onClick={onClick}
className={className}
data-testid={testId}
aria-pressed={selected}
aria-disabled={disabled}
aria-label={label}
>
<StyledIconContainer>
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
</StyledIconContainer>
<>
<StyledMenuPicker
id={id}
selected={selected}
disabled={disabled}
onClick={onClick}
className={className}
data-testid={testId}
aria-pressed={selected}
aria-disabled={disabled}
aria-label={label}
>
<StyledIconContainer>
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
</StyledIconContainer>
{showLabel && (
<StyledLabel selected={selected} disabled={disabled}>
{label}
</StyledLabel>
{isDefined(label) && showLabel && (
<StyledLabel selected={selected} disabled={disabled}>
{label}
</StyledLabel>
)}
</StyledMenuPicker>
{isNonEmptyString(tooltipContent) && (
<AppTooltip
anchorSelect={`#${id}`}
offset={tooltipOffset}
content={tooltipContent}
place={TooltipPosition.Bottom}
positionStrategy="fixed"
delay={tooltipDelay}
noArrow
/>
)}
</StyledMenuPicker>
</>
);
};
@@ -1,6 +1,6 @@
import styled from '@emotion/styled';
import { type Meta, type StoryObj } from '@storybook/react';
import { IconChartPie } from '@ui/display';
import { IconChartPie, TooltipDelay } from '@ui/display';
import { MenuPicker } from '@ui/navigation/menu/components/MenuPicker';
import { ComponentDecorator } from '@ui/testing';
@@ -67,19 +67,44 @@ export const AllStates: Story = {
>
<div>
<StyledTitle>Default</StyledTitle>
<MenuPicker icon={IconChartPie} label="Default" />
<MenuPicker id="default" icon={IconChartPie} label="Default" />
</div>
<div>
<StyledTitle>Selected</StyledTitle>
<MenuPicker icon={IconChartPie} label="Selected" selected />
<MenuPicker
id="selected"
icon={IconChartPie}
label="Selected"
selected
/>
</div>
<div>
<StyledTitle>Disabled</StyledTitle>
<MenuPicker icon={IconChartPie} label="Disabled" disabled />
<MenuPicker
id="disabled"
icon={IconChartPie}
label="Disabled"
disabled
/>
</div>
<div>
<StyledTitle>No Label</StyledTitle>
<MenuPicker icon={IconChartPie} label="No Label" showLabel={false} />
<MenuPicker
id="no-label"
icon={IconChartPie}
label="No Label"
showLabel={false}
/>
</div>
<div>
<StyledTitle> With Tooltip</StyledTitle>
<MenuPicker
id="tooltip"
icon={IconChartPie}
label="Tooltip"
tooltipContent="Tooltip"
tooltipDelay={TooltipDelay.mediumDelay}
/>
</div>
</div>
),