mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
feat: stateful banner (#2346)
Conditional banner for change request status
This commit is contained in:
parent
f76ac0edb3
commit
88a9e0cb9b
@ -1,4 +1,4 @@
|
|||||||
import { VFC } from 'react';
|
import { FC, VFC } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@ -58,6 +58,15 @@ const BackButton = styled(Button)(({ theme }) => ({
|
|||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const SubmitChangeRequestButton: FC<{ onClick: () => void; count: number }> = ({
|
||||||
|
onClick,
|
||||||
|
count,
|
||||||
|
}) => (
|
||||||
|
<Button sx={{ mt: 2, ml: 'auto' }} variant="contained" onClick={onClick}>
|
||||||
|
Submit change request ({count})
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
|
export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
|
||||||
open,
|
open,
|
||||||
project,
|
project,
|
||||||
@ -218,17 +227,18 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
|
|||||||
}
|
}
|
||||||
show={
|
show={
|
||||||
<>
|
<>
|
||||||
<Button
|
<SubmitChangeRequestButton
|
||||||
sx={{ mt: 2, ml: 'auto' }}
|
|
||||||
variant="contained"
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
onReview(
|
onReview(
|
||||||
environmentChangeRequest.id
|
environmentChangeRequest.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
count={
|
||||||
Request changes
|
environmentChangeRequest
|
||||||
</Button>
|
.features.length
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
sx={{ mt: 2, ml: 2 }}
|
sx={{ mt: 2, ml: 2 }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
@ -1,74 +1,100 @@
|
|||||||
import { useState, VFC } from 'react';
|
import { FC, useState, VFC } from 'react';
|
||||||
import { Box, Button, Typography } from '@mui/material';
|
import { Box, Button, styled, Typography } from '@mui/material';
|
||||||
import { useStyles as useAppStyles } from 'component/App.styles';
|
import { useStyles as useAppStyles } from 'component/App.styles';
|
||||||
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { ChangeRequestSidebar } from '../ChangeRequestSidebar/ChangeRequestSidebar';
|
import { ChangeRequestSidebar } from '../ChangeRequestSidebar/ChangeRequestSidebar';
|
||||||
import { useChangeRequestOpen } from 'hooks/api/getters/useChangeRequestOpen/useChangeRequestOpen';
|
import { useChangeRequestOpen } from 'hooks/api/getters/useChangeRequestOpen/useChangeRequestOpen';
|
||||||
|
import { IChangeRequest } from '../changeRequest.types';
|
||||||
|
|
||||||
interface IDraftBannerProps {
|
interface IDraftBannerProps {
|
||||||
project: string;
|
project: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DraftBanner: VFC<IDraftBannerProps> = ({ project }) => {
|
const DraftBannerContentWrapper = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: theme.spacing(1, 1.5),
|
||||||
|
color: theme.palette.warning.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const DraftBannerContent: FC<{
|
||||||
|
changeRequest: IChangeRequest;
|
||||||
|
onClick: () => void;
|
||||||
|
}> = ({ changeRequest, onClick }) => {
|
||||||
const { classes } = useAppStyles();
|
const { classes } = useAppStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box className={classes.content}>
|
||||||
|
<DraftBannerContentWrapper>
|
||||||
|
<WarningAmberIcon />
|
||||||
|
<Typography variant="body2" sx={{ ml: 1 }}>
|
||||||
|
<strong>Draft mode!</strong> – You have changes{' '}
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(changeRequest.environment)}
|
||||||
|
show={
|
||||||
|
<>
|
||||||
|
in <strong>{changeRequest.environment} </strong>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={changeRequest.state === 'Draft'}
|
||||||
|
show={'that need to be reviewed'}
|
||||||
|
/>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={changeRequest.state === 'In review'}
|
||||||
|
show={'that are in review'}
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={onClick}
|
||||||
|
sx={{ ml: 'auto' }}
|
||||||
|
>
|
||||||
|
View changes ({changeRequest.features.length})
|
||||||
|
</Button>
|
||||||
|
<Button variant="text" onClick={() => {}} sx={{ ml: 1 }}>
|
||||||
|
Discard all
|
||||||
|
</Button>
|
||||||
|
</DraftBannerContentWrapper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const StickyBanner = styled(Box)(({ theme }) => ({
|
||||||
|
position: 'sticky',
|
||||||
|
top: -1,
|
||||||
|
zIndex: theme.zIndex.appBar,
|
||||||
|
borderTop: `1px solid ${theme.palette.warning.border}`,
|
||||||
|
borderBottom: `1px solid ${theme.palette.warning.border}`,
|
||||||
|
backgroundColor: theme.palette.warning.light,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const DraftBanner: VFC<IDraftBannerProps> = ({ project }) => {
|
||||||
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||||
const { draft, loading } = useChangeRequestOpen(project);
|
const { draft, loading } = useChangeRequestOpen(project);
|
||||||
const environment = '';
|
|
||||||
|
|
||||||
if ((!loading && !draft) || draft?.length === 0) {
|
if ((!loading && !draft) || draft?.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<StickyBanner>
|
||||||
sx={{
|
{draft &&
|
||||||
position: 'sticky',
|
draft
|
||||||
top: -1,
|
.filter(changeRequest =>
|
||||||
zIndex: theme => theme.zIndex.appBar,
|
['Draft', 'In review'].includes(changeRequest.state)
|
||||||
borderTop: theme => `1px solid ${theme.palette.warning.border}`,
|
)
|
||||||
borderBottom: theme =>
|
.map(changeRequest => (
|
||||||
`1px solid ${theme.palette.warning.border}`,
|
<DraftBannerContent
|
||||||
backgroundColor: theme => theme.palette.warning.light,
|
changeRequest={changeRequest}
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
setIsSidebarOpen(true);
|
||||||
<Box className={classes.content}>
|
}}
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
px: 1,
|
|
||||||
py: 1.5,
|
|
||||||
color: theme => theme.palette.warning.main,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<WarningAmberIcon />
|
|
||||||
<Typography variant="body2" sx={{ ml: 1 }}>
|
|
||||||
<strong>Draft mode!</strong> – You have changes{' '}
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(environment)}
|
|
||||||
show={
|
|
||||||
<>
|
|
||||||
in <strong>{environment} </strong>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
that need to be reviewed
|
))}
|
||||||
</Typography>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
setIsSidebarOpen(true);
|
|
||||||
}}
|
|
||||||
sx={{ ml: 'auto' }}
|
|
||||||
>
|
|
||||||
Review changes
|
|
||||||
</Button>
|
|
||||||
<Button variant="text" onClick={() => {}} sx={{ ml: 1 }}>
|
|
||||||
Discard all
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<ChangeRequestSidebar
|
<ChangeRequestSidebar
|
||||||
project={project}
|
project={project}
|
||||||
open={isSidebarOpen}
|
open={isSidebarOpen}
|
||||||
@ -76,6 +102,6 @@ export const DraftBanner: VFC<IDraftBannerProps> = ({ project }) => {
|
|||||||
setIsSidebarOpen(false);
|
setIsSidebarOpen(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</StickyBanner>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { makeStyles } from 'tss-react/mui';
|
|
||||||
|
|
||||||
export const useStyles = makeStyles()(() => ({
|
|
||||||
modal: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
height: '100vh',
|
|
||||||
maxWidth: '98vw',
|
|
||||||
width: 1300,
|
|
||||||
overflow: 'auto',
|
|
||||||
boxShadow: '0 0 1rem rgba(0, 0, 0, 0.25)',
|
|
||||||
},
|
|
||||||
}));
|
|
@ -1,7 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { Modal, Backdrop } from '@mui/material';
|
import { Modal, Backdrop, styled } from '@mui/material';
|
||||||
import Fade from '@mui/material/Fade';
|
import Fade from '@mui/material/Fade';
|
||||||
import { useStyles } from 'component/common/SidebarModal/SidebarModal.styles';
|
|
||||||
import { SIDEBAR_MODAL_ID } from 'utils/testIds';
|
import { SIDEBAR_MODAL_ID } from 'utils/testIds';
|
||||||
|
|
||||||
interface ISidebarModalProps {
|
interface ISidebarModalProps {
|
||||||
@ -13,14 +12,24 @@ interface ISidebarModalProps {
|
|||||||
|
|
||||||
const TRANSITION_DURATION = 250;
|
const TRANSITION_DURATION = 250;
|
||||||
|
|
||||||
|
const ModalContentWrapper = styled('div')({
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
height: '100vh',
|
||||||
|
maxWidth: '98vw',
|
||||||
|
width: 1300,
|
||||||
|
overflow: 'auto',
|
||||||
|
boxShadow: '0 0 1rem rgba(0, 0, 0, 0.25)',
|
||||||
|
});
|
||||||
|
|
||||||
export const SidebarModal = ({
|
export const SidebarModal = ({
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
label,
|
label,
|
||||||
children,
|
children,
|
||||||
}: ISidebarModalProps) => {
|
}: ISidebarModalProps) => {
|
||||||
const { classes: styles } = useStyles();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
@ -32,7 +41,7 @@ export const SidebarModal = ({
|
|||||||
data-testid={SIDEBAR_MODAL_ID}
|
data-testid={SIDEBAR_MODAL_ID}
|
||||||
>
|
>
|
||||||
<Fade timeout={TRANSITION_DURATION} in={open}>
|
<Fade timeout={TRANSITION_DURATION} in={open}>
|
||||||
<div className={styles.modal}>{children}</div>
|
<ModalContentWrapper>{children}</ModalContentWrapper>
|
||||||
</Fade>
|
</Fade>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user