1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-18 13:48:58 +02:00

feat: add truncation and tooltips (#10498)

This PR uses the existing Truncator component to add truncation and
tooltips for long names.
This commit is contained in:
Fredrik Strand Oseberg 2025-08-18 13:16:52 +02:00 committed by GitHub
parent 05ea405bf6
commit a99e5bc4b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 122 additions and 93 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@ npm-debug
.DS_Store .DS_Store
/dist /dist
.vscode .vscode
.claude
# Logs # Logs
logs logs

View File

@ -5,6 +5,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink'; import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { Highlighter } from 'component/common/Highlighter/Highlighter'; import { Highlighter } from 'component/common/Highlighter/Highlighter';
import { Truncator } from 'component/common/Truncator/Truncator';
const StyledBox = styled(Box)(({ theme }) => ({ const StyledBox = styled(Box)(({ theme }) => ({
display: 'flex', display: 'flex',
@ -13,10 +14,7 @@ const StyledBox = styled(Box)(({ theme }) => ({
padding: theme.spacing(1, 0, 1, 2), padding: theme.spacing(1, 0, 1, 2),
})); }));
const StyledLink = styled(Link)(({ theme }) => ({ const StyledLink = styled(Link)(() => ({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
textDecoration: 'none', textDecoration: 'none',
'&:hover, &:focus': { '&:hover, &:focus': {
textDecoration: 'underline', textDecoration: 'underline',
@ -53,12 +51,13 @@ export const FeaturesCell: VFC<FeaturesCellProps> = ({ value, project }) => {
show={featureNames?.map((featureName: string) => ( show={featureNames?.map((featureName: string) => (
<StyledLink <StyledLink
key={featureName} key={featureName}
title={featureName}
to={`/projects/${project}/features/${featureName}`} to={`/projects/${project}/features/${featureName}`}
> >
<Highlighter search={searchQuery}> <Truncator lines={1} title={featureName} arrow>
{featureName} <Highlighter search={searchQuery}>
</Highlighter> {featureName}
</Highlighter>
</Truncator>
</StyledLink> </StyledLink>
))} ))}
elseShow={ elseShow={
@ -69,12 +68,17 @@ export const FeaturesCell: VFC<FeaturesCellProps> = ({ value, project }) => {
{featureNames?.map((featureName: string) => ( {featureNames?.map((featureName: string) => (
<StyledTooltipLink <StyledTooltipLink
key={featureName} key={featureName}
title={featureName}
to={`/projects/${project}/features/${featureName}`} to={`/projects/${project}/features/${featureName}`}
> >
<Highlighter search={searchQuery}> <Truncator
{featureName} lines={1}
</Highlighter> title={featureName}
arrow
>
<Highlighter search={searchQuery}>
{featureName}
</Highlighter>
</Truncator>
</StyledTooltipLink> </StyledTooltipLink>
))} ))}
</StyledTooltipContainer> </StyledTooltipContainer>

View File

@ -5,7 +5,7 @@ import useFeatureTypes from 'hooks/api/getters/useFeatureTypes/useFeatureTypes';
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons'; import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
import { useSearchHighlightContext } from '../../SearchHighlightContext/SearchHighlightContext.tsx'; import { useSearchHighlightContext } from '../../SearchHighlightContext/SearchHighlightContext.tsx';
import { Highlighter } from '../../../Highlighter/Highlighter.tsx'; import { Highlighter } from '../../../Highlighter/Highlighter.tsx';
import { StyledDescription, StyledTitle } from '../LinkCell/LinkCell.styles'; import { StyledDescription } from '../LinkCell/LinkCell.styles';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Badge } from '../../../Badge/Badge.tsx'; import { Badge } from '../../../Badge/Badge.tsx';
import { HtmlTooltip } from '../../../HtmlTooltip/HtmlTooltip.tsx'; import { HtmlTooltip } from '../../../HtmlTooltip/HtmlTooltip.tsx';
@ -15,6 +15,7 @@ import { useLocationSettings } from 'hooks/useLocationSettings';
import { getLocalizedDateString } from '../../../util.ts'; import { getLocalizedDateString } from '../../../util.ts';
import { Tag } from 'component/common/Tag/Tag'; import { Tag } from 'component/common/Tag/Tag';
import { formatTag } from 'utils/format-tag'; import { formatTag } from 'utils/format-tag';
import { Truncator } from 'component/common/Truncator/Truncator';
interface IFeatureNameCellProps { interface IFeatureNameCellProps {
row: { row: {
@ -135,15 +136,20 @@ const FeatureName: FC<{
return ( return (
<Box sx={(theme) => ({ fontWeight: theme.typography.fontWeightBold })}> <Box sx={(theme) => ({ fontWeight: theme.typography.fontWeightBold })}>
<StyledFeatureLink to={`/projects/${project}/features/${feature}`}> <StyledFeatureLink to={`/projects/${project}/features/${feature}`}>
<StyledTitle <Truncator
style={{ lines={1}
WebkitLineClamp: 1, title={feature}
lineClamp: 1, arrow
sx={{
overflowWrap: 'anywhere', overflowWrap: 'anywhere',
}} }}
> >
<Highlighter search={searchQuery}>{feature}</Highlighter> <span>
</StyledTitle> <Highlighter search={searchQuery}>
{feature}
</Highlighter>
</span>
</Truncator>
</StyledFeatureLink> </StyledFeatureLink>
</Box> </Box>
); );
@ -154,14 +160,18 @@ const ArchivedFeatureName: FC<{
searchQuery: string; searchQuery: string;
}> = ({ feature, searchQuery }) => { }> = ({ feature, searchQuery }) => {
return ( return (
<Box <Truncator
lines={1}
title={feature}
arrow
sx={(theme) => ({ sx={(theme) => ({
fontWeight: theme.typography.fontWeightBold, fontWeight: theme.typography.fontWeightBold,
color: theme.palette.neutral.main, color: theme.palette.neutral.main,
overflowWrap: 'anywhere',
})} })}
> >
<Highlighter search={searchQuery}>{feature}</Highlighter> <Highlighter search={searchQuery}>{feature}</Highlighter>
</Box> </Truncator>
); );
}; };

View File

@ -5,6 +5,7 @@ import { useSearchHighlightContext } from 'component/common/Table/SearchHighligh
import { Box, styled } from '@mui/material'; import { Box, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
import { Truncator } from 'component/common/Truncator/Truncator';
interface IHighlightCellProps { interface IHighlightCellProps {
value: string; value: string;
@ -21,14 +22,7 @@ const StyledContainer = styled(Box)(({ theme }) => ({
padding: theme.spacing(1, 2), padding: theme.spacing(1, 2),
})); }));
const StyledTitle = styled('span')(({ theme }) => ({ const StyledTitle = styled('span')(() => ({}));
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: '1',
lineClamp: '1',
}));
const StyledSubtitle = styled('span')(({ theme }) => ({ const StyledSubtitle = styled('span')(({ theme }) => ({
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
@ -73,16 +67,17 @@ export const HighlightCell: FC<IHighlightCellProps> = ({
return ( return (
<StyledContainer> <StyledContainer>
<StyledTitle <Truncator
style={{ lines={subtitle ? 1 : 2}
WebkitLineClamp: subtitle ? 1 : 2, title={value}
lineClamp: subtitle ? 1 : 2, arrow
}}
data-loading data-loading
> >
<Highlighter search={searchQuery}>{value}</Highlighter> <StyledTitle>
{afterTitle} <Highlighter search={searchQuery}>{value}</Highlighter>
</StyledTitle> {afterTitle}
</StyledTitle>
</Truncator>
<ConditionallyRender <ConditionallyRender
condition={Boolean(subtitle)} condition={Boolean(subtitle)}
show={renderSubtitle} show={renderSubtitle}

View File

@ -33,13 +33,6 @@ export const StyledContainer = styled('div')(({ theme }) => ({
wordBreak: 'break-all', wordBreak: 'break-all',
})); }));
export const StyledTitle = styled('span')(({ theme }) => ({
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
}));
export const StyledDescription = styled('span')(({ theme }) => ({ export const StyledDescription = styled('span')(({ theme }) => ({
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
textDecoration: 'none', textDecoration: 'none',

View File

@ -4,11 +4,11 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import { Highlighter } from 'component/common/Highlighter/Highlighter'; import { Highlighter } from 'component/common/Highlighter/Highlighter';
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
import { Truncator } from 'component/common/Truncator/Truncator';
import { import {
StyledWrapper, StyledWrapper,
StyledLink, StyledLink,
StyledContainer, StyledContainer,
StyledTitle,
StyledDescription, StyledDescription,
} from './LinkCell.styles'; } from './LinkCell.styles';
@ -51,16 +51,17 @@ export const LinkCell: React.FC<ILinkCellProps> = ({
const content = ( const content = (
<StyledContainer> <StyledContainer>
<StyledTitle <Truncator
lines={subtitle ? 1 : 2}
title={title}
arrow
data-loading data-loading
style={{
WebkitLineClamp: subtitle ? 1 : 2,
lineClamp: subtitle ? 1 : 2,
}}
> >
<Highlighter search={searchQuery}>{title}</Highlighter> <span>
{children} <Highlighter search={searchQuery}>{title}</Highlighter>
</StyledTitle> {children}
</span>
</Truncator>
<ConditionallyRender <ConditionallyRender
condition={Boolean(subtitle)} condition={Boolean(subtitle)}
show={renderSubtitle} show={renderSubtitle}

View File

@ -275,16 +275,21 @@ exports[`renders an empty list correctly 1`] = `
className="css-u8cmsa" className="css-u8cmsa"
> >
<span <span
className="css-697v50" aria-label=""
aria-labelledby={null}
className=" MuiBox-root css-gzore8"
data-loading={true} data-loading={true}
style={ data-mui-internal-clone-element={true}
{ onBlur={[Function]}
"WebkitLineClamp": 1, onFocus={[Function]}
"lineClamp": 1, onMouseLeave={[Function]}
} onMouseOver={[Function]}
} onTouchEnd={[Function]}
onTouchStart={[Function]}
> >
Tag type name <span>
Tag type name
</span>
</span> </span>
<span <span
className="css-1121jr7" className="css-1121jr7"
@ -449,16 +454,21 @@ exports[`renders an empty list correctly 1`] = `
className="css-u8cmsa" className="css-u8cmsa"
> >
<span <span
className="css-697v50" aria-label=""
aria-labelledby={null}
className=" MuiBox-root css-gzore8"
data-loading={true} data-loading={true}
style={ data-mui-internal-clone-element={true}
{ onBlur={[Function]}
"WebkitLineClamp": 1, onFocus={[Function]}
"lineClamp": 1, onMouseLeave={[Function]}
} onMouseOver={[Function]}
} onTouchEnd={[Function]}
onTouchStart={[Function]}
> >
Tag type name <span>
Tag type name
</span>
</span> </span>
<span <span
className="css-1121jr7" className="css-1121jr7"
@ -623,16 +633,21 @@ exports[`renders an empty list correctly 1`] = `
className="css-u8cmsa" className="css-u8cmsa"
> >
<span <span
className="css-697v50" aria-label=""
aria-labelledby={null}
className=" MuiBox-root css-gzore8"
data-loading={true} data-loading={true}
style={ data-mui-internal-clone-element={true}
{ onBlur={[Function]}
"WebkitLineClamp": 1, onFocus={[Function]}
"lineClamp": 1, onMouseLeave={[Function]}
} onMouseOver={[Function]}
} onTouchEnd={[Function]}
onTouchStart={[Function]}
> >
Tag type name <span>
Tag type name
</span>
</span> </span>
<span <span
className="css-1121jr7" className="css-1121jr7"
@ -797,16 +812,21 @@ exports[`renders an empty list correctly 1`] = `
className="css-u8cmsa" className="css-u8cmsa"
> >
<span <span
className="css-697v50" aria-label=""
aria-labelledby={null}
className=" MuiBox-root css-gzore8"
data-loading={true} data-loading={true}
style={ data-mui-internal-clone-element={true}
{ onBlur={[Function]}
"WebkitLineClamp": 1, onFocus={[Function]}
"lineClamp": 1, onMouseLeave={[Function]}
} onMouseOver={[Function]}
} onTouchEnd={[Function]}
onTouchStart={[Function]}
> >
Tag type name <span>
Tag type name
</span>
</span> </span>
<span <span
className="css-1121jr7" className="css-1121jr7"
@ -971,16 +991,21 @@ exports[`renders an empty list correctly 1`] = `
className="css-u8cmsa" className="css-u8cmsa"
> >
<span <span
className="css-697v50" aria-label=""
aria-labelledby={null}
className=" MuiBox-root css-gzore8"
data-loading={true} data-loading={true}
style={ data-mui-internal-clone-element={true}
{ onBlur={[Function]}
"WebkitLineClamp": 1, onFocus={[Function]}
"lineClamp": 1, onMouseLeave={[Function]}
} onMouseOver={[Function]}
} onTouchEnd={[Function]}
onTouchStart={[Function]}
> >
Tag type name <span>
Tag type name
</span>
</span> </span>
<span <span
className="css-1121jr7" className="css-1121jr7"