Fix menu item overflowing hiding icon buttons (#14847)

FIxes https://github.com/twentyhq/twenty/issues/14764

<img width="3944" height="886" alt="CleanShot 2025-10-02 at 16 01 43@2x"
src="https://github.com/user-attachments/assets/9d6cbb73-903d-4fd1-9456-395aa9745c45"
/>

<img width="525" height="1266" alt="CleanShot 2025-10-02 at 16 06 34@2x"
src="https://github.com/user-attachments/assets/cc6d6e89-48f7-48da-abff-b8458c2e7ae3"
/>
This commit is contained in:
Raphaël Bosi
2025-10-02 16:35:28 +02:00
committed by GitHub
parent 4a4777ef78
commit d4b83df3f5
4 changed files with 96 additions and 45 deletions
@@ -1,9 +1,5 @@
import { useTheme } from '@emotion/react';
import {
IconChevronRight,
OverflowingTextWithTooltip,
type IconComponent,
} from '@ui/display';
import { IconChevronRight, type IconComponent } from '@ui/display';
import { type LightIconButtonProps } from '@ui/input/button/components/LightIconButton';
import { LightIconButtonGroup } from '@ui/input/button/components/LightIconButtonGroup';
import {
@@ -14,16 +10,12 @@ import {
} from 'react';
import styled from '@emotion/styled';
import { isString } from '@sniptt/guards';
import { MenuItemHotKeys } from '@ui/navigation/menu/menu-item/components/MenuItemHotKeys';
import { motion } from 'framer-motion';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import {
StyledHoverableMenuItemBase,
StyledMenuItemLabel,
StyledMenuItemLeftContent,
StyledMenuItemRightContent,
StyledRightMenuItemContextualText,
} from '../internals/components/StyledMenuItemBase';
import { type MenuItemAccent } from '../types/MenuItemAccent';
@@ -111,30 +103,17 @@ export const MenuItem = ({
onMouseLeave={onMouseLeave}
focused={focused}
>
<StyledMenuItemLeftContent>
<MenuItemLeftContent
LeftIcon={LeftIcon ?? undefined}
LeftComponent={LeftComponent}
withIconContainer={withIconContainer}
text={text}
contextualText={
contextualTextPosition === 'left' ? contextualText : null
}
disabled={disabled}
/>
</StyledMenuItemLeftContent>
<MenuItemLeftContent
LeftIcon={LeftIcon ?? undefined}
LeftComponent={LeftComponent}
withIconContainer={withIconContainer}
text={text}
contextualText={contextualText}
contextualTextPosition={contextualTextPosition}
disabled={disabled}
/>
<StyledMenuItemRightContent>
{contextualTextPosition === 'right' && (
<StyledMenuItemLabel>
{isString(contextualText) ? (
<StyledRightMenuItemContextualText>
<OverflowingTextWithTooltip text={contextualText} />
</StyledRightMenuItemContextualText>
) : (
contextualText
)}
</StyledMenuItemLabel>
)}
{iconButtons && (
<div className="hoverable-buttons">
{showIconButtons && (
@@ -198,6 +198,7 @@ export const ContextualTextCatalog: CatalogStory<Story, typeof MenuItem> = {
},
decorators: [CatalogDecorator],
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [
{
@@ -227,6 +228,55 @@ export const ContextualTextCatalog: CatalogStory<Story, typeof MenuItem> = {
}
},
},
{
name: 'iconButtons',
values: ['no icon button', 'one icon button'],
props: (choice: string) => {
switch (choice) {
case 'no icon button': {
return {
iconButtons: [],
};
}
case 'one icon button': {
return {
iconButtons: [
{
Icon: IconBell,
onClick: action('Clicked on icon button'),
},
],
};
}
default:
return {};
}
},
labels: (choice: string) => {
switch (choice) {
case 'no icon button':
return 'No icon button';
case 'one icon button':
return 'One icon button';
default:
return choice;
}
},
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: 'hover' };
default:
return {};
}
},
},
],
options: {
elementContainer: {
@@ -13,6 +13,7 @@ import {
StyledMenuItemContextualText,
StyledMenuItemLabel,
StyledMenuItemLeftContent,
StyledRightMenuItemContextualText,
} from './StyledMenuItemBase';
const StyledMainText = styled.div`
@@ -32,6 +33,10 @@ const StyledIconContainer = styled.div`
padding: ${({ theme }) => theme.spacing(1)};
`;
const StyledMenuItemLabelRight = styled(StyledMenuItemLabel)`
margin-left: auto;
`;
type MenuItemLeftContentProps = {
className?: string;
LeftComponent?: ReactNode;
@@ -41,6 +46,7 @@ type MenuItemLeftContentProps = {
disabled?: boolean;
text: ReactNode;
contextualText?: ReactNode;
contextualTextPosition?: 'left' | 'right';
};
export const MenuItemLeftContent = ({
@@ -50,6 +56,7 @@ export const MenuItemLeftContent = ({
withIconContainer = false,
text,
contextualText,
contextualTextPosition = 'left',
showGrip = false,
disabled = false,
}: MenuItemLeftContentProps) => {
@@ -89,18 +96,29 @@ export const MenuItemLeftContent = ({
) : (
text
)}
{isString(contextualText) ? (
{contextualTextPosition === 'left' && (
<>
{isNonEmptyString(contextualText) && (
<StyledMenuItemContextualText>
<OverflowingTextWithTooltip text={`· ${contextualText}`} />
</StyledMenuItemContextualText>
)}
{isString(contextualText)
? isNonEmptyString(contextualText) && (
<StyledMenuItemContextualText>
<OverflowingTextWithTooltip text={`· ${contextualText}`} />
</StyledMenuItemContextualText>
)
: contextualText}
</>
) : (
contextualText
)}
</StyledMenuItemLabel>
{contextualTextPosition === 'right' && (
<StyledMenuItemLabelRight>
<StyledRightMenuItemContextualText>
{isString(contextualText) ? (
<OverflowingTextWithTooltip text={contextualText} />
) : (
contextualText
)}
</StyledRightMenuItemContextualText>
</StyledMenuItemLabelRight>
)}
</StyledMenuItemLeftContent>
);
};
@@ -112,16 +112,22 @@ export const StyledMenuItemLeftContent = styled.div`
gap: ${({ theme }) => theme.spacing(2)};
min-width: 0;
max-width: 100%;
width: 100%;
flex-shrink: 0;
& svg {
flex-shrink: 0;
}
`;
export const StyledMenuItemRightContent = styled.div`
align-items: center;
display: flex;
flex-direction: row;
overflow: hidden;
gap: ${({ theme }) => theme.spacing(2)};
& svg {
flex-shrink: 0;
}
`;
export const StyledDraggableItem = styled.div`
@@ -187,7 +193,5 @@ export const StyledMenuItemContextualText = styled.div`
export const StyledRightMenuItemContextualText = styled(
StyledMenuItemContextualText,
)`
display: flex;
text-align: right;
padding-right: ${({ theme }) => theme.spacing(1)};
`;