mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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}
 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            <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={() => {
 | 
					                            onClick={() => {
 | 
				
			||||||
                                setIsSidebarOpen(true);
 | 
					                                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