mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-27 01:19:00 +02:00
feat: search page - improved change request tooltip (#9750)
improved and tested tooltip for change requests and adjusted column width
This commit is contained in:
parent
01c1ec5c29
commit
3fd74262bb
@ -69,13 +69,10 @@ export interface IHtmlTooltipProps extends TooltipProps {
|
|||||||
maxHeight?: SpacingArgument;
|
maxHeight?: SpacingArgument;
|
||||||
fontSize?: string;
|
fontSize?: string;
|
||||||
tabIndex?: number;
|
tabIndex?: number;
|
||||||
|
disableFocusListener?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HtmlTooltip = (props: IHtmlTooltipProps) => {
|
export const HtmlTooltip = (props: IHtmlTooltipProps) => {
|
||||||
if (!props.title) return props.children;
|
if (!props.title) return props.children;
|
||||||
return (
|
return <StyledHtmlTooltip {...props}>{props.children}</StyledHtmlTooltip>;
|
||||||
<StyledHtmlTooltip {...props} disableFocusListener>
|
|
||||||
{props.children}
|
|
||||||
</StyledHtmlTooltip>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -54,7 +54,13 @@ export const FeatureLifecycleCell: VFC<IFeatureLifecycleProps> = ({
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
<Box
|
||||||
|
sx={(theme) => ({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: theme.spacing(0, expanded ? 1 : 0),
|
||||||
|
})}
|
||||||
|
>
|
||||||
<FeatureLifecycle
|
<FeatureLifecycle
|
||||||
onArchive={onArchive}
|
onArchive={onArchive}
|
||||||
onComplete={onComplete}
|
onComplete={onComplete}
|
||||||
|
@ -197,7 +197,6 @@ export const FeatureToggleListTable: FC = () => {
|
|||||||
<StatusCell {...original} />
|
<StatusCell {...original} />
|
||||||
),
|
),
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
size: 80,
|
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('project', {
|
columnHelper.accessor('project', {
|
||||||
header: 'Project',
|
header: 'Project',
|
||||||
@ -208,10 +207,12 @@ export const FeatureToggleListTable: FC = () => {
|
|||||||
)?.name;
|
)?.name;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Box sx={{ minWidth: '180px' }}>
|
||||||
<LinkCell
|
<LinkCell
|
||||||
title={projectName || projectId}
|
title={projectName || projectId}
|
||||||
to={`/projects/${projectId}`}
|
to={`/projects/${projectId}`}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
import { render } from 'utils/testRenderer';
|
||||||
|
import { StatusCell } from './StatusCell';
|
||||||
|
import { screen } from '@testing-library/dom';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
describe('StatusCell', () => {
|
||||||
|
it('displays "–" as default status', () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<StatusCell
|
||||||
|
project='test-project'
|
||||||
|
lifecycle={{
|
||||||
|
stage: 'live',
|
||||||
|
enteredStageAt: '2025-04-01T00:00:00Z',
|
||||||
|
}}
|
||||||
|
environments={[
|
||||||
|
{
|
||||||
|
name: 'production',
|
||||||
|
type: 'production',
|
||||||
|
enabled: true,
|
||||||
|
hasStrategies: true,
|
||||||
|
hasEnabledStrategies: true,
|
||||||
|
changeRequestIds: [],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(getByText('–')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows "change requests" icon', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<StatusCell
|
||||||
|
project='test-project'
|
||||||
|
lifecycle={{
|
||||||
|
stage: 'live',
|
||||||
|
enteredStageAt: '2025-04-01T00:00:00Z',
|
||||||
|
}}
|
||||||
|
environments={[
|
||||||
|
{
|
||||||
|
name: 'production',
|
||||||
|
type: 'production',
|
||||||
|
enabled: true,
|
||||||
|
hasStrategies: true,
|
||||||
|
hasEnabledStrategies: true,
|
||||||
|
changeRequestIds: [123],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByTestId('change-requests-icon')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows change requests on focus', async () => {
|
||||||
|
const ui = (
|
||||||
|
<StatusCell
|
||||||
|
project='test-project'
|
||||||
|
lifecycle={{
|
||||||
|
stage: 'live',
|
||||||
|
enteredStageAt: '2025-04-01T00:00:00Z',
|
||||||
|
}}
|
||||||
|
environments={[
|
||||||
|
{
|
||||||
|
name: 'production',
|
||||||
|
type: 'production',
|
||||||
|
enabled: true,
|
||||||
|
hasStrategies: true,
|
||||||
|
hasEnabledStrategies: true,
|
||||||
|
changeRequestIds: [123],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const { getByTestId, getByText, rerender } = render(ui);
|
||||||
|
|
||||||
|
expect(await screen.queryByText('Change requests:')).toBeNull();
|
||||||
|
await userEvent.hover(getByTestId('change-requests-icon'));
|
||||||
|
await screen.findByRole('tooltip');
|
||||||
|
expect(
|
||||||
|
await screen.queryByText('Change requests:'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('#123')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -5,25 +5,39 @@ import { getStatus } from './getStatus';
|
|||||||
import DifferenceIcon from '@mui/icons-material/Difference';
|
import DifferenceIcon from '@mui/icons-material/Difference';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||||
|
import { Truncator } from 'component/common/Truncator/Truncator';
|
||||||
|
|
||||||
const Container = styled('div')(({ theme }) => ({
|
const Container = styled('div')(({ theme }) => ({
|
||||||
padding: theme.spacing(0, 2),
|
padding: theme.spacing(0, 2),
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: theme.spacing(1),
|
gap: theme.spacing(0.5),
|
||||||
|
minWidth: '180px',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const ChangeRequestIcon = styled(DifferenceIcon)(({ theme }) => ({
|
const ChangeRequestIcon = styled(DifferenceIcon)(({ theme }) => ({
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
fontSize: theme.spacing(2.5),
|
fontSize: theme.spacing(3.5),
|
||||||
marginLeft: theme.spacing(0.5),
|
padding: theme.spacing(0.5),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const StatusCell: FC<FeatureSearchResponseSchema> = ({
|
const ChangeRequestTooltip = styled('div')(({ theme }) => ({
|
||||||
lifecycle,
|
display: 'flex',
|
||||||
environments,
|
flexDirection: 'column',
|
||||||
project,
|
alignItems: 'center',
|
||||||
}) => {
|
gap: theme.spacing(0.5),
|
||||||
|
ul: {
|
||||||
|
listStyle: 'none',
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const StatusCell: FC<
|
||||||
|
Pick<FeatureSearchResponseSchema, 'lifecycle' | 'environments' | 'project'>
|
||||||
|
> = ({ lifecycle, environments, project }) => {
|
||||||
const status = useMemo(
|
const status = useMemo(
|
||||||
() => getStatus({ lifecycle, environments }),
|
() => getStatus({ lifecycle, environments }),
|
||||||
[lifecycle, environments],
|
[lifecycle, environments],
|
||||||
@ -35,28 +49,35 @@ export const StatusCell: FC<FeatureSearchResponseSchema> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<div>{status}</div>
|
<Truncator title={status} lines={2}>
|
||||||
|
{status}
|
||||||
|
</Truncator>
|
||||||
{changeRequestIds.length > 0 && (
|
{changeRequestIds.length > 0 && (
|
||||||
<HtmlTooltip
|
<HtmlTooltip
|
||||||
arrow
|
arrow
|
||||||
title={
|
title={
|
||||||
<div>
|
<ChangeRequestTooltip>
|
||||||
<span>Change requests:</span>
|
<div>Change requests:</div>
|
||||||
<br />
|
<ul>
|
||||||
{changeRequestIds.map((id) => (
|
{changeRequestIds.map((id) => (
|
||||||
|
<li key={id}>
|
||||||
<Link
|
<Link
|
||||||
key={id}
|
|
||||||
to={`/projects/${project}/change-requests/${id}`}
|
to={`/projects/${project}/change-requests/${id}`}
|
||||||
target='_blank'
|
target='_blank'
|
||||||
rel='noopener noreferrer'
|
rel='noopener noreferrer'
|
||||||
>
|
>
|
||||||
{`#${id}`}
|
{`#${id}`}
|
||||||
</Link>
|
</Link>
|
||||||
|
</li>
|
||||||
))}
|
))}
|
||||||
</div>
|
</ul>
|
||||||
|
</ChangeRequestTooltip>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ChangeRequestIcon />
|
<ChangeRequestIcon
|
||||||
|
data-testid='change-requests-icon'
|
||||||
|
tabIndex={0}
|
||||||
|
/>
|
||||||
</HtmlTooltip>
|
</HtmlTooltip>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -148,4 +148,44 @@ describe('getStatus', () => {
|
|||||||
).toBe('No strategies');
|
).toBe('No strategies');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('release plan - milestones', () => {
|
||||||
|
it('should show the release plan', () => {
|
||||||
|
expect(
|
||||||
|
getStatus({
|
||||||
|
environments: [
|
||||||
|
{
|
||||||
|
...prodEnvEnabled,
|
||||||
|
totalMilestones: 2,
|
||||||
|
milestoneOrder: 0,
|
||||||
|
milestoneName: 'First step',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lifecycle: {
|
||||||
|
stage: 'live',
|
||||||
|
enteredStageAt: null as any,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toBe('Milestone: First step (1 of 2)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show the milestone if a flag is disabled', () => {
|
||||||
|
expect(
|
||||||
|
getStatus({
|
||||||
|
environments: [
|
||||||
|
{
|
||||||
|
...prodEnvDisabled,
|
||||||
|
totalMilestones: 2,
|
||||||
|
milestoneOrder: 0,
|
||||||
|
milestoneName: 'First step',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lifecycle: {
|
||||||
|
stage: 'live',
|
||||||
|
enteredStageAt: null as any,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).not.toBe('Release plan');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user