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;
|
||||
fontSize?: string;
|
||||
tabIndex?: number;
|
||||
disableFocusListener?: boolean;
|
||||
}
|
||||
|
||||
export const HtmlTooltip = (props: IHtmlTooltipProps) => {
|
||||
if (!props.title) return props.children;
|
||||
return (
|
||||
<StyledHtmlTooltip {...props} disableFocusListener>
|
||||
{props.children}
|
||||
</StyledHtmlTooltip>
|
||||
);
|
||||
return <StyledHtmlTooltip {...props}>{props.children}</StyledHtmlTooltip>;
|
||||
};
|
||||
|
@ -54,7 +54,13 @@ export const FeatureLifecycleCell: VFC<IFeatureLifecycleProps> = ({
|
||||
: [];
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
padding: theme.spacing(0, expanded ? 1 : 0),
|
||||
})}
|
||||
>
|
||||
<FeatureLifecycle
|
||||
onArchive={onArchive}
|
||||
onComplete={onComplete}
|
||||
|
@ -197,7 +197,6 @@ export const FeatureToggleListTable: FC = () => {
|
||||
<StatusCell {...original} />
|
||||
),
|
||||
enableSorting: false,
|
||||
size: 80,
|
||||
}),
|
||||
columnHelper.accessor('project', {
|
||||
header: 'Project',
|
||||
@ -208,10 +207,12 @@ export const FeatureToggleListTable: FC = () => {
|
||||
)?.name;
|
||||
|
||||
return (
|
||||
<LinkCell
|
||||
title={projectName || projectId}
|
||||
to={`/projects/${projectId}`}
|
||||
/>
|
||||
<Box sx={{ minWidth: '180px' }}>
|
||||
<LinkCell
|
||||
title={projectName || 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 { Link } from 'react-router-dom';
|
||||
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||
import { Truncator } from 'component/common/Truncator/Truncator';
|
||||
|
||||
const Container = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(0, 2),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
gap: theme.spacing(0.5),
|
||||
minWidth: '180px',
|
||||
}));
|
||||
|
||||
const ChangeRequestIcon = styled(DifferenceIcon)(({ theme }) => ({
|
||||
color: theme.palette.primary.main,
|
||||
fontSize: theme.spacing(2.5),
|
||||
marginLeft: theme.spacing(0.5),
|
||||
fontSize: theme.spacing(3.5),
|
||||
padding: theme.spacing(0.5),
|
||||
}));
|
||||
|
||||
export const StatusCell: FC<FeatureSearchResponseSchema> = ({
|
||||
lifecycle,
|
||||
environments,
|
||||
project,
|
||||
}) => {
|
||||
const ChangeRequestTooltip = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
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(
|
||||
() => getStatus({ lifecycle, environments }),
|
||||
[lifecycle, environments],
|
||||
@ -35,28 +49,35 @@ export const StatusCell: FC<FeatureSearchResponseSchema> = ({
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<div>{status}</div>
|
||||
<Truncator title={status} lines={2}>
|
||||
{status}
|
||||
</Truncator>
|
||||
{changeRequestIds.length > 0 && (
|
||||
<HtmlTooltip
|
||||
arrow
|
||||
title={
|
||||
<div>
|
||||
<span>Change requests:</span>
|
||||
<br />
|
||||
{changeRequestIds.map((id) => (
|
||||
<Link
|
||||
key={id}
|
||||
to={`/projects/${project}/change-requests/${id}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
{`#${id}`}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<ChangeRequestTooltip>
|
||||
<div>Change requests:</div>
|
||||
<ul>
|
||||
{changeRequestIds.map((id) => (
|
||||
<li key={id}>
|
||||
<Link
|
||||
to={`/projects/${project}/change-requests/${id}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
{`#${id}`}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</ChangeRequestTooltip>
|
||||
}
|
||||
>
|
||||
<ChangeRequestIcon />
|
||||
<ChangeRequestIcon
|
||||
data-testid='change-requests-icon'
|
||||
tabIndex={0}
|
||||
/>
|
||||
</HtmlTooltip>
|
||||
)}
|
||||
</Container>
|
||||
|
@ -148,4 +148,44 @@ describe('getStatus', () => {
|
||||
).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