1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

Feat: change request scheduled timeline (#5346)

Adds the scheduled state to the timeline

Closes #
[1-1632](https://linear.app/unleash/issue/1-1632/update-the-progress-bar-for-scheduled-changes)

![Screenshot 2023-11-16 at 10 51
03](https://github.com/Unleash/unleash/assets/104830839/6267299e-d5c3-4cbf-9ab2-25da53f2d526)

---------

Signed-off-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
andreas-unleash 2023-11-17 11:20:14 +02:00 committed by GitHub
parent f8a9d7f355
commit 0dc5f306cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 6 deletions

View File

@ -238,7 +238,10 @@ export const ChangeRequestOverview: FC = () => {
<ChangeRequestHeader changeRequest={changeRequest} />
<ChangeRequestBody>
<StyledAsideBox>
<ChangeRequestTimeline state={changeRequest.state} />
<ChangeRequestTimeline
state={changeRequest.state}
scheduledAt={changeRequest.schedule?.scheduledAt}
/>
<ChangeRequestReviewers changeRequest={changeRequest} />
</StyledAsideBox>
<StyledPaper elevation={0}>

View File

@ -41,6 +41,21 @@ test('rejected timeline shows all states', () => {
expect(screen.queryByText('Applied')).not.toBeInTheDocument();
});
test('scheduled timeline shows all states', () => {
render(
<ChangeRequestTimeline
state={'Scheduled'}
scheduledAt={new Date().toISOString()}
/>,
);
expect(screen.getByText('Draft')).toBeInTheDocument();
expect(screen.getByText('In review')).toBeInTheDocument();
expect(screen.queryByText('Approved')).toBeInTheDocument();
expect(screen.getByText('Scheduled')).toBeInTheDocument();
expect(screen.queryByText('Applied')).toBeInTheDocument();
});
const irrelevantIndex = -99; // Using a number that's unlikely to be a valid index
test('returns grey for Cancelled state regardless of displayed stage', () => {
@ -87,6 +102,16 @@ test('returns success for stages other than Rejected in Rejected state', () => {
),
).toBe('success');
});
test('returns warning for Scheduled stage in Scheduled state', () => {
expect(
determineColor(
'Scheduled',
irrelevantIndex,
'Scheduled',
irrelevantIndex,
),
).toBe('warning');
});
test('returns success for stages at or before activeIndex', () => {
expect(determineColor('In review', 1, 'Draft', 0)).toBe('success');

View File

@ -1,5 +1,5 @@
import { FC } from 'react';
import { Box, Paper, styled } from '@mui/material';
import { Box, Paper, styled, Typography } from '@mui/material';
import Timeline from '@mui/lab/Timeline';
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
import TimelineSeparator from '@mui/lab/TimelineSeparator';
@ -7,9 +7,11 @@ import TimelineDot from '@mui/lab/TimelineDot';
import TimelineConnector from '@mui/lab/TimelineConnector';
import TimelineContent from '@mui/lab/TimelineContent';
import { ChangeRequestState } from '../../changeRequest.types';
import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender';
interface ISuggestChangeTimelineProps {
state: ChangeRequestState;
scheduledAt?: string;
}
const StyledPaper = styled(Paper)(({ theme }) => ({
@ -36,6 +38,13 @@ const steps: ChangeRequestState[] = [
'Applied',
];
const rejectedSteps: ChangeRequestState[] = ['Draft', 'In review', 'Rejected'];
const scheduledSteps: ChangeRequestState[] = [
'Draft',
'In review',
'Approved',
'Scheduled',
'Applied',
];
export const determineColor = (
changeRequestState: ChangeRequestState,
@ -44,21 +53,40 @@ export const determineColor = (
displayStageIndex: number,
) => {
if (changeRequestState === 'Cancelled') return 'grey';
if (changeRequestState === 'Rejected')
return displayStage === 'Rejected' ? 'error' : 'success';
if (
changeRequestStateIndex !== -1 &&
changeRequestStateIndex >= displayStageIndex
changeRequestStateIndex > displayStageIndex
)
return 'success';
if (
changeRequestStateIndex !== -1 &&
changeRequestStateIndex === displayStageIndex
) {
return changeRequestState === 'Scheduled' ? 'warning' : 'success';
}
if (changeRequestStateIndex + 1 === displayStageIndex) return 'primary';
return 'grey';
};
export const ChangeRequestTimeline: FC<ISuggestChangeTimelineProps> = ({
state,
scheduledAt,
}) => {
const data = state === 'Rejected' ? rejectedSteps : steps;
let data;
switch (state) {
case 'Rejected':
data = rejectedSteps;
break;
case 'Scheduled':
data = scheduledSteps;
break;
default:
data = steps;
}
const activeIndex = data.findIndex((item) => item === state);
return (
@ -66,6 +94,12 @@ export const ChangeRequestTimeline: FC<ISuggestChangeTimelineProps> = ({
<StyledBox>
<StyledTimeline>
{data.map((title, index) => {
const subtitle =
scheduledAt &&
state === 'Scheduled' &&
state === title
? new Date(scheduledAt).toLocaleString()
: undefined;
const color = determineColor(
state,
activeIndex,
@ -85,6 +119,7 @@ export const ChangeRequestTimeline: FC<ISuggestChangeTimelineProps> = ({
return createTimelineItem(
color,
title,
subtitle,
index < data.length - 1,
timelineDotProps,
);
@ -96,8 +131,9 @@ export const ChangeRequestTimeline: FC<ISuggestChangeTimelineProps> = ({
};
const createTimelineItem = (
color: 'primary' | 'success' | 'grey' | 'error',
color: 'primary' | 'success' | 'grey' | 'error' | 'warning',
title: string,
subtitle: string | undefined,
shouldConnectToNextItem: boolean,
timelineDotProps: { [key: string]: string | undefined } = {},
) => (
@ -106,6 +142,17 @@ const createTimelineItem = (
<TimelineDot color={color} {...timelineDotProps} />
{shouldConnectToNextItem && <TimelineConnector />}
</TimelineSeparator>
<TimelineContent>{title}</TimelineContent>
<TimelineContent>
{title}
<br />
<ConditionallyRender
condition={Boolean(subtitle)}
show={
<Typography
color={'text.secondary'}
>{`(for ${subtitle})`}</Typography>
}
/>
</TimelineContent>
</TimelineItem>
);