1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-14 01:16:17 +02:00

Refactor/make styles batch 6 part 2 (#2811)

Adds another batch of refactored components
This commit is contained in:
Fredrik Strand Oseberg 2023-01-03 16:15:22 +01:00 committed by GitHub
parent b631618532
commit 093156f5f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 271 additions and 327 deletions

View File

@ -33,7 +33,7 @@ export const FeatureStrategyItem = ({
style={{ style={{
borderColor: result.enabled borderColor: result.enabled
? theme.palette.success.main ? theme.palette.success.main
: 'inherit', : 'none',
}} }}
strategy={{ ...strategy, id: `${objectId(strategy)}` }} strategy={{ ...strategy, id: `${objectId(strategy)}` }}
orderNumber={index + 1} orderNumber={index + 1}

View File

@ -1,9 +1,7 @@
import { Chip, Typography, useTheme } from '@mui/material'; import { Chip, Typography, useTheme, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useStyles } from './PlaygroundParametertem.styles';
import StringTruncator from 'component/common/StringTruncator/StringTruncator'; import StringTruncator from 'component/common/StringTruncator/StringTruncator';
import { CancelOutlined } from '@mui/icons-material'; import { CancelOutlined } from '@mui/icons-material';
import classnames from 'classnames';
interface IConstraintItemProps { interface IConstraintItemProps {
value: Array<string | number>; value: Array<string | number>;
@ -12,29 +10,55 @@ interface IConstraintItemProps {
showReason?: boolean; showReason?: boolean;
} }
const StyledDivContainer = styled('div', {
shouldForwardProp: prop => prop !== 'showReason',
})<{ showReason?: boolean }>(({ theme, showReason }) => ({
width: '100%',
padding: theme.spacing(2, 3),
borderRadius: theme.shape.borderRadiusMedium,
border: `1px solid ${theme.palette.dividerAlternative}`,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: theme.spacing(2),
opacity: showReason ? 0.9 : 1,
backgroundColor: showReason ? theme.palette.background.paper : 'inherit',
}));
const StyledDivColumn = styled('div')(({ theme }) => ({
flexDirection: 'column',
}));
const StyledChip = styled(Chip)(({ theme }) => ({
margin: theme.spacing(0.5),
}));
const StyledParagraph = styled('p')(({ theme }) => ({
display: 'inline',
margin: theme.spacing(0.5, 0),
maxWidth: '95%',
textAlign: 'center',
wordBreak: 'break-word',
}));
export const PlaygroundParameterItem = ({ export const PlaygroundParameterItem = ({
value, value,
text, text,
input, input,
showReason = false, showReason = false,
}: IConstraintItemProps) => { }: IConstraintItemProps) => {
const { classes: styles } = useStyles();
const theme = useTheme(); const theme = useTheme();
const color = input === 'no value' ? 'error' : 'neutral'; const color = input === 'no value' ? 'error' : 'neutral';
const reason = `value does not match any ${text}`; const reason = `value does not match any ${text}`;
return ( return (
<div <StyledDivContainer showReason={showReason}>
className={classnames(
styles.container,
showReason ? styles.disabled : ''
)}
>
<Typography variant="subtitle1" color={theme.palette[color].main}> <Typography variant="subtitle1" color={theme.palette[color].main}>
{`${input}`} {`${input}`}
</Typography> </Typography>
<div className={styles.column}> <StyledDivColumn>
<ConditionallyRender <ConditionallyRender
condition={Boolean(showReason)} condition={Boolean(showReason)}
show={ show={
@ -51,13 +75,13 @@ export const PlaygroundParameterItem = ({
show={<p>No {text}s added yet.</p>} show={<p>No {text}s added yet.</p>}
elseShow={ elseShow={
<div> <div>
<p className={styles.paragraph}> <StyledParagraph>
{value.length}{' '} {value.length}{' '}
{value.length > 1 ? `${text}s` : text} will get {value.length > 1 ? `${text}s` : text} will get
access. access.
</p> </StyledParagraph>
{value.map((v: string | number) => ( {value.map((v: string | number) => (
<Chip <StyledChip
key={v} key={v}
label={ label={
<StringTruncator <StringTruncator
@ -66,18 +90,17 @@ export const PlaygroundParameterItem = ({
maxLength={50} maxLength={50}
/> />
} }
className={styles.chip}
/> />
))} ))}
</div> </div>
} }
/> />
</div> </StyledDivColumn>
<ConditionallyRender <ConditionallyRender
condition={Boolean(showReason)} condition={Boolean(showReason)}
show={<CancelOutlined color={'error'} />} show={<CancelOutlined color={'error'} />}
elseShow={<div />} elseShow={<div />}
/> />
</div> </StyledDivContainer>
); );
}; };

View File

@ -1,32 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
width: '100%',
padding: theme.spacing(2, 3),
borderRadius: theme.shape.borderRadiusMedium,
border: `1px solid ${theme.palette.dividerAlternative}`,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: theme.spacing(2),
},
disabled: {
backgroundColor: theme.palette.neutral.light,
opacity: '90%',
},
chip: {
margin: '0.25rem',
},
column: {
flexDirection: 'column',
},
paragraph: {
display: 'inline',
margin: '0.25rem 0',
maxWidth: '95%',
textAlign: 'center',
wordBreak: 'break-word',
},
}));

View File

@ -1,61 +1,87 @@
import { makeStyles } from 'tss-react/mui'; import { styled, Tab } from '@mui/material';
import { FavoriteIconButton } from 'component/common/FavoriteIconButton/FavoriteIconButton';
export const useStyles = makeStyles()(theme => ({ export const StyledDiv = styled('div')(() => ({
containerStyles: { display: 'flex',
display: 'flex', }));
[theme.breakpoints.down('md')]: {
flexDirection: 'column', export const StyledTopRow = styled('div')(() => ({
}, display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
}));
export const StyledColumn = styled('div')(() => ({
display: 'flex',
flexDirection: 'column',
}));
export const StyledName = styled('div')(() => ({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}));
export const StyledTitle = styled('span')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
fontWeight: 'normal',
}));
export const StyledText = styled(StyledTitle)(({ theme }) => ({
color: theme.palette.neutral.dark,
}));
export const StyledFavoriteIconButton = styled(FavoriteIconButton)(
({ theme }) => ({
marginLeft: theme.spacing(-1.5),
})
);
export const StyledHeader = styled('div')(({ theme }) => ({
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadiusLarge,
marginBottom: theme.spacing(2),
}));
export const StyledInnerContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(2.5, 5),
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
}));
export const StyledProjectTitle = styled('h2')(({ theme }) => ({
margin: 0,
width: '100%',
fontSize: theme.fontSizes.mainHeader,
fontWeight: 'bold',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: theme.spacing(2),
}));
export const StyledSeparator = styled('div')(({ theme }) => ({
width: '100%',
backgroundColor: theme.palette.tertiary.light,
height: '1px',
}));
export const StyledTabContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 4),
}));
export const StyledTab = styled(Tab)(({ theme }) => ({
textTransform: 'none',
fontSize: theme.fontSizes.bodySize,
flexGrow: 1,
flexBasis: 0,
[theme.breakpoints.down('md')]: {
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
}, },
projectToggles: { [theme.breakpoints.up('md')]: {
width: '100%', minWidth: 160,
minWidth: 0,
},
header: {
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadiusLarge,
marginBottom: '1rem',
},
innerContainer: {
padding: '1.25rem 2rem',
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
},
separator: {
width: '100%',
backgroundColor: theme.palette.grey[200],
height: '1px',
},
tabContainer: {
padding: '0 2rem',
},
tabButton: {
textTransform: 'none',
fontSize: '1rem',
flexGrow: 1,
flexBasis: 0,
[theme.breakpoints.down('md')]: {
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
},
[theme.breakpoints.up('md')]: {
minWidth: 160,
},
},
title: {
margin: 0,
width: '100%',
fontSize: theme.fontSizes.mainHeader,
fontWeight: 'bold',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: '1rem',
},
titleText: {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}, },
})); }));

View File

@ -2,8 +2,22 @@ import { useNavigate } from 'react-router';
import useProject from 'hooks/api/getters/useProject/useProject'; import useProject from 'hooks/api/getters/useProject/useProject';
import useLoading from 'hooks/useLoading'; import useLoading from 'hooks/useLoading';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useStyles } from './Project.styles'; import {
import { styled, Tab, Tabs } from '@mui/material'; StyledColumn,
StyledDiv,
StyledFavoriteIconButton,
StyledHeader,
StyledInnerContainer,
StyledName,
StyledProjectTitle,
StyledSeparator,
StyledTab,
StyledTabContainer,
StyledText,
StyledTitle,
StyledTopRow,
} from './Project.styles';
import { Tabs } from '@mui/material';
import { Delete, Edit } from '@mui/icons-material'; import { Delete, Edit } from '@mui/icons-material';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import useQueryParams from 'hooks/useQueryParams'; import useQueryParams from 'hooks/useQueryParams';
@ -28,53 +42,17 @@ import { MainLayout } from 'component/layout/MainLayout/MainLayout';
import { ProjectChangeRequests } from '../../changeRequest/ProjectChangeRequests/ProjectChangeRequests'; import { ProjectChangeRequests } from '../../changeRequest/ProjectChangeRequests/ProjectChangeRequests';
import { ProjectSettings } from './ProjectSettings/ProjectSettings'; import { ProjectSettings } from './ProjectSettings/ProjectSettings';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { FavoriteIconButton } from 'component/common/FavoriteIconButton/FavoriteIconButton';
import { useFavoriteProjectsApi } from 'hooks/api/actions/useFavoriteProjectsApi/useFavoriteProjectsApi'; import { useFavoriteProjectsApi } from 'hooks/api/actions/useFavoriteProjectsApi/useFavoriteProjectsApi';
const StyledDiv = styled('div')(() => ({
display: 'flex',
}));
const StyledTopRow = styled('div')(() => ({
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
}));
const Column = styled('div')(() => ({
display: 'flex',
flexDirection: 'column',
}));
const StyledName = styled('div')(() => ({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}));
const StyledTitle = styled('span')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
fontWeight: 'normal',
}));
const StyledText = styled(StyledTitle)(({ theme }) => ({
color: theme.palette.grey[800],
}));
const StyledFavoriteIconButton = styled(FavoriteIconButton)(({ theme }) => ({
marginLeft: theme.spacing(-1.5),
}));
const Project = () => { const Project = () => {
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const params = useQueryParams(); const params = useQueryParams();
const { project, loading, refetch } = useProject(projectId); const { project, loading, refetch } = useProject(projectId);
const ref = useLoading(loading); const ref = useLoading(loading);
const { setToastData } = useToast(); const { setToastData } = useToast();
const { classes: styles } = useStyles();
const navigate = useNavigate(); const navigate = useNavigate();
const { pathname } = useLocation(); const { pathname } = useLocation();
const { isOss, uiConfig } = useUiConfig(); const { isOss } = useUiConfig();
const basePath = `/projects/${projectId}`; const basePath = `/projects/${projectId}`;
const projectName = project?.name || projectId; const projectName = project?.name || projectId;
const { isChangeRequestConfiguredInAnyEnv } = const { isChangeRequestConfiguredInAnyEnv } =
@ -152,19 +130,19 @@ const Project = () => {
) : null ) : null
} }
> >
<div className={styles.header}> <StyledHeader>
<div className={styles.innerContainer}> <StyledInnerContainer>
<StyledTopRow> <StyledTopRow>
<StyledDiv> <StyledDiv>
<StyledFavoriteIconButton <StyledFavoriteIconButton
onClick={onFavorite} onClick={onFavorite}
isFavorite={project?.favorite} isFavorite={project?.favorite}
/> />
<h2 className={styles.title}> <StyledProjectTitle>
<StyledName data-loading> <StyledName data-loading>
{projectName} {projectName}
</StyledName> </StyledName>
</h2> </StyledProjectTitle>
</StyledDiv> </StyledDiv>
<StyledDiv> <StyledDiv>
<PermissionIconButton <PermissionIconButton
@ -197,8 +175,8 @@ const Project = () => {
</PermissionIconButton> </PermissionIconButton>
</StyledDiv> </StyledDiv>
</StyledTopRow> </StyledTopRow>
<Column> <StyledColumn>
<h2 className={styles.title}> <StyledProjectTitle>
<div> <div>
<ConditionallyRender <ConditionallyRender
condition={Boolean(project.description)} condition={Boolean(project.description)}
@ -222,12 +200,12 @@ const Project = () => {
</StyledText> </StyledText>
</StyledDiv> </StyledDiv>
</div> </div>
</h2> </StyledProjectTitle>
</Column> </StyledColumn>
</div> </StyledInnerContainer>
<div className={styles.separator} /> <StyledSeparator />
<div className={styles.tabContainer}> <StyledTabContainer>
<Tabs <Tabs
value={activeTab?.path} value={activeTab?.path}
indicatorColor="primary" indicatorColor="primary"
@ -236,17 +214,16 @@ const Project = () => {
allowScrollButtonsMobile allowScrollButtonsMobile
> >
{tabs.map(tab => ( {tabs.map(tab => (
<Tab <StyledTab
key={tab.title} key={tab.title}
label={tab.title} label={tab.title}
value={tab.path} value={tab.path}
onClick={() => navigate(tab.path)} onClick={() => navigate(tab.path)}
className={styles.tabButton}
/> />
))} ))}
</Tabs> </Tabs>
</div> </StyledTabContainer>
</div> </StyledHeader>
<DeleteProjectDialogue <DeleteProjectDialogue
project={projectId} project={projectId}
open={showDelDialog} open={showDelDialog}

View File

@ -1,19 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
cell: {
display: 'flex',
justifyContent: 'center',
paddingRight: theme.spacing(2),
},
menuContainer: {
borderRadius: theme.shape.borderRadiusLarge,
padding: theme.spacing(1, 1.5),
},
item: {
borderRadius: theme.shape.borderRadius,
},
text: {
fontSize: theme.fontSizes.smallBody,
},
}));

View File

@ -9,19 +9,26 @@ import {
Popover, Popover,
Tooltip, Tooltip,
Typography, Typography,
styled,
} from '@mui/material'; } from '@mui/material';
import { Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
import MoreVertIcon from '@mui/icons-material/MoreVert'; import MoreVertIcon from '@mui/icons-material/MoreVert';
import FileCopyIcon from '@mui/icons-material/FileCopy'; import FileCopyIcon from '@mui/icons-material/FileCopy';
import ArchiveIcon from '@mui/icons-material/Archive'; import ArchiveIcon from '@mui/icons-material/Archive';
import WatchLaterIcon from '@mui/icons-material/WatchLater'; import WatchLaterIcon from '@mui/icons-material/WatchLater';
import { useStyles } from './ActionsCell.styles';
import { PermissionHOC } from 'component/common/PermissionHOC/PermissionHOC'; import { PermissionHOC } from 'component/common/PermissionHOC/PermissionHOC';
import { import {
CREATE_FEATURE, CREATE_FEATURE,
DELETE_FEATURE, DELETE_FEATURE,
UPDATE_FEATURE, UPDATE_FEATURE,
} from 'component/providers/AccessProvider/permissions'; } from 'component/providers/AccessProvider/permissions';
import { defaultBorderRadius } from 'themes/themeStyles';
const StyledBoxCell = styled(Box)(({ theme }) => ({
display: 'flex',
justifyContent: 'center',
paddingRight: theme.spacing(2),
}));
interface IActionsCellProps { interface IActionsCellProps {
projectId: string; projectId: string;
@ -42,7 +49,6 @@ export const ActionsCell: VFC<IActionsCellProps> = ({
onOpenStaleDialog, onOpenStaleDialog,
}) => { }) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const { classes } = useStyles();
const { const {
original: { name: featureId, stale }, original: { name: featureId, stale },
} = row; } = row;
@ -58,7 +64,7 @@ export const ActionsCell: VFC<IActionsCellProps> = ({
const menuId = `${id}-menu`; const menuId = `${id}-menu`;
return ( return (
<Box className={classes.cell}> <StyledBoxCell>
<Tooltip title="Feature toggle actions" arrow describeChild> <Tooltip title="Feature toggle actions" arrow describeChild>
<IconButton <IconButton
id={id} id={id}
@ -80,7 +86,10 @@ export const ActionsCell: VFC<IActionsCellProps> = ({
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
disableScrollLock={true} disableScrollLock={true}
PaperProps={{ PaperProps={{
className: classes.menuContainer, sx: theme => ({
borderRadius: theme.shape.borderRadius,
padding: theme.spacing(1, 1.5),
}),
}} }}
> >
<MenuList aria-labelledby={id}> <MenuList aria-labelledby={id}>
@ -90,7 +99,7 @@ export const ActionsCell: VFC<IActionsCellProps> = ({
> >
{({ hasAccess }) => ( {({ hasAccess }) => (
<MenuItem <MenuItem
className={classes.item} sx={defaultBorderRadius}
onClick={handleClose} onClick={handleClose}
disabled={!hasAccess} disabled={!hasAccess}
component={RouterLink} component={RouterLink}
@ -113,7 +122,7 @@ export const ActionsCell: VFC<IActionsCellProps> = ({
> >
{({ hasAccess }) => ( {({ hasAccess }) => (
<MenuItem <MenuItem
className={classes.item} sx={defaultBorderRadius}
onClick={() => { onClick={() => {
onOpenArchiveDialog(featureId); onOpenArchiveDialog(featureId);
handleClose(); handleClose();
@ -137,7 +146,7 @@ export const ActionsCell: VFC<IActionsCellProps> = ({
> >
{({ hasAccess }) => ( {({ hasAccess }) => (
<MenuItem <MenuItem
className={classes.item} sx={defaultBorderRadius}
onClick={() => { onClick={() => {
handleClose(); handleClose();
onOpenStaleDialog({ onOpenStaleDialog({
@ -160,6 +169,6 @@ export const ActionsCell: VFC<IActionsCellProps> = ({
</PermissionHOC> </PermissionHOC>
</MenuList> </MenuList>
</Popover> </Popover>
</Box> </StyledBoxCell>
); );
}; };

View File

@ -1,35 +1,41 @@
import { makeStyles } from 'tss-react/mui'; import {
Box,
Checkbox,
Divider,
IconButton,
MenuItem,
styled,
} from '@mui/material';
export const useStyles = makeStyles()(theme => ({ import { flexRow } from 'themes/themeStyles';
container: {
display: 'flex', export const StyledBoxContainer = styled(Box)(() => ({
justifyContent: 'center', ...flexRow,
alignItems: 'center', justifyContent: 'center',
}, }));
button: {
margin: theme.spacing(-1, 0), export const StyledIconButton = styled(IconButton)(({ theme }) => ({
}, margin: theme.spacing(-1, 0),
menuContainer: { }));
borderRadius: theme.shape.borderRadiusLarge,
paddingBottom: theme.spacing(2), export const StyledBoxMenuHeader = styled(Box)(({ theme }) => ({
}, ...flexRow,
menuHeader: { justifyContent: 'space-between',
display: 'flex', padding: theme.spacing(1, 1, 0, 4),
alignItems: 'center', }));
justifyContent: 'space-between',
padding: theme.spacing(1, 1, 0, 4), export const StyledMenuItem = styled(MenuItem)(({ theme }) => ({
}, padding: theme.spacing(0, 2),
menuItem: { margin: theme.spacing(0, 2),
padding: theme.spacing(0, 2), borderRadius: theme.shape.borderRadius,
margin: theme.spacing(0, 2), }));
borderRadius: theme.shape.borderRadius,
}, export const StyledDivider = styled(Divider)(({ theme }) => ({
checkbox: { '&.MuiDivider-root.MuiDivider-fullWidth': {
padding: theme.spacing(0.75, 1), margin: theme.spacing(0.75, 0),
},
divider: {
'&.MuiDivider-root.MuiDivider-fullWidth': {
margin: theme.spacing(0.75, 0),
},
}, },
})); }));
export const StyledCheckbox = styled(Checkbox)(({ theme }) => ({
padding: theme.spacing(0.75, 1),
}));

View File

@ -1,12 +1,8 @@
import { useEffect, useState, VFC } from 'react'; import { useEffect, useState, VFC } from 'react';
import { import {
Box,
Checkbox,
Divider,
IconButton, IconButton,
ListItemIcon, ListItemIcon,
ListItemText, ListItemText,
MenuItem,
MenuList, MenuList,
Popover, Popover,
Tooltip, Tooltip,
@ -17,7 +13,14 @@ import {
import ColumnIcon from '@mui/icons-material/ViewWeek'; import ColumnIcon from '@mui/icons-material/ViewWeek';
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from '@mui/icons-material/Close';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useStyles } from './ColumnsMenu.styles'; import {
StyledBoxContainer,
StyledBoxMenuHeader,
StyledCheckbox,
StyledDivider,
StyledIconButton,
StyledMenuItem,
} from './ColumnsMenu.styles';
interface IColumnsMenuProps { interface IColumnsMenuProps {
allColumns: { allColumns: {
@ -51,7 +54,6 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
setHiddenColumns, setHiddenColumns,
}) => { }) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const { classes } = useStyles();
const theme = useTheme(); const theme = useTheme();
const isTinyScreen = useMediaQuery(theme.breakpoints.down('sm')); const isTinyScreen = useMediaQuery(theme.breakpoints.down('sm'));
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
@ -104,9 +106,9 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
const menuId = `columns-menu-list-${id}`; const menuId = `columns-menu-list-${id}`;
return ( return (
<Box className={classes.container}> <StyledBoxContainer>
<Tooltip title="Select columns" arrow describeChild> <Tooltip title="Select columns" arrow describeChild>
<IconButton <StyledIconButton
id={id} id={id}
aria-controls={isOpen ? menuId : undefined} aria-controls={isOpen ? menuId : undefined}
aria-haspopup="true" aria-haspopup="true"
@ -114,11 +116,10 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
onClick={handleClick} onClick={handleClick}
type="button" type="button"
size="large" size="large"
className={classes.button}
data-loading data-loading
> >
<ColumnIcon /> <ColumnIcon />
</IconButton> </StyledIconButton>
</Tooltip> </Tooltip>
<Popover <Popover
@ -136,34 +137,36 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
}} }}
disableScrollLock={true} disableScrollLock={true}
PaperProps={{ PaperProps={{
className: classes.menuContainer, sx: theme => ({
borderRadius: theme.shape.borderRadius,
paddingBottom: theme.spacing(2),
}),
}} }}
> >
<Box className={classes.menuHeader}> <StyledBoxMenuHeader>
<Typography variant="body2"> <Typography variant="body2">
<strong>Columns</strong> <strong>Columns</strong>
</Typography> </Typography>
<IconButton onClick={handleClose}> <IconButton onClick={handleClose}>
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
</Box> </StyledBoxMenuHeader>
<MenuList> <MenuList>
{allColumns {allColumns
.filter(({ hideInMenu }) => !hideInMenu) .filter(({ hideInMenu }) => !hideInMenu)
.map(column => [ .map(column => [
<ConditionallyRender <ConditionallyRender
condition={dividerBefore.includes(column.id)} condition={dividerBefore.includes(column.id)}
show={<Divider className={classes.divider} />} show={<StyledDivider />}
/>, />,
<MenuItem <StyledMenuItem
onClick={() => onClick={() =>
column.toggleHidden(column.isVisible) column.toggleHidden(column.isVisible)
} }
disabled={staticColumns.includes(column.id)} disabled={staticColumns.includes(column.id)}
className={classes.menuItem}
> >
<ListItemIcon> <ListItemIcon>
<Checkbox <StyledCheckbox
edge="start" edge="start"
checked={column.isVisible} checked={column.isVisible}
disableRipple disableRipple
@ -171,7 +174,6 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
'aria-labelledby': column.id, 'aria-labelledby': column.id,
}} }}
size="medium" size="medium"
className={classes.checkbox}
/> />
</ListItemIcon> </ListItemIcon>
<ListItemText <ListItemText
@ -195,14 +197,14 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
</Typography> </Typography>
} }
/> />
</MenuItem>, </StyledMenuItem>,
<ConditionallyRender <ConditionallyRender
condition={dividerAfter.includes(column.id)} condition={dividerAfter.includes(column.id)}
show={<Divider className={classes.divider} />} show={<StyledDivider />}
/>, />,
])} ])}
</MenuList> </MenuList>
</Popover> </Popover>
</Box> </StyledBoxContainer>
); );
}; };

View File

@ -1,9 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
mx: 'auto',
display: 'flex',
justifyContent: 'center',
},
}));

View File

@ -1,9 +1,14 @@
import { VFC } from 'react'; import { VFC } from 'react';
import { Box } from '@mui/material'; import { Box, styled } from '@mui/material';
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch'; import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions'; import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
import { useOptimisticUpdate } from './hooks/useOptimisticUpdate'; import { useOptimisticUpdate } from './hooks/useOptimisticUpdate';
import { useStyles } from './FeatureToggleSwitch.styles'; import { flexRow } from 'themes/themeStyles';
const StyledBoxContainer = styled(Box)(() => ({
mx: 'auto',
...flexRow,
}));
interface IFeatureToggleSwitchProps { interface IFeatureToggleSwitchProps {
featureName: string; featureName: string;
@ -25,7 +30,6 @@ export const FeatureToggleSwitch: VFC<IFeatureToggleSwitchProps> = ({
value, value,
onToggle, onToggle,
}) => { }) => {
const { classes } = useStyles();
const [isChecked, setIsChecked, rollbackIsChecked] = const [isChecked, setIsChecked, rollbackIsChecked] =
useOptimisticUpdate<boolean>(value); useOptimisticUpdate<boolean>(value);
@ -37,8 +41,7 @@ export const FeatureToggleSwitch: VFC<IFeatureToggleSwitchProps> = ({
}; };
return ( return (
<Box <StyledBoxContainer
className={classes.container}
key={`${featureName}-${environmentName}`} // Prevent animation when archiving rows key={`${featureName}-${environmentName}`} // Prevent animation when archiving rows
> >
<PermissionSwitch <PermissionSwitch
@ -50,6 +53,6 @@ export const FeatureToggleSwitch: VFC<IFeatureToggleSwitchProps> = ({
onClick={onClick} onClick={onClick}
disabled={isChecked !== value} disabled={isChecked !== value}
/> />
</Box> </StyledBoxContainer>
); );
}; };

View File

@ -13,60 +13,4 @@ export const useStyles = makeStyles()(theme => ({
width: 'inherit', width: 'inherit',
}, },
}, },
headerClass: {
'& th': {
fontSize: theme.fontSizes.smallerBody,
lineHeight: '1rem',
},
},
bodyClass: {
overflowX: 'auto',
padding: theme.spacing(4),
},
header: {
padding: '1rem',
},
title: {
display: 'unset',
},
iconButton: {
marginRight: '1rem',
},
icon: {
color: '#000',
height: '30px',
width: '30px',
},
noTogglesFound: {
marginBottom: '0.5rem',
},
link: {
textDecoration: 'none',
color: theme.palette.primary.main,
},
actionsContainer: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
search: {
border: `1px solid ${theme.palette.grey[300]}`,
height: 35,
marginRight: '2rem',
},
button: {
whiteSpace: 'nowrap',
},
row: {
position: 'absolute',
width: '100%',
},
cell: {
alignItems: 'center',
display: 'flex',
flexShrink: 0,
'& > *': {
flexGrow: 1,
},
},
})); }));

View File

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useMediaQuery, useTheme } from '@mui/material'; import { styled, useMediaQuery, useTheme } from '@mui/material';
import { Add } from '@mui/icons-material'; import { Add } from '@mui/icons-material';
import { useNavigate, useSearchParams } from 'react-router-dom'; import { useNavigate, useSearchParams } from 'react-router-dom';
import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table';
@ -9,7 +9,6 @@ import { PageContent } from 'component/common/PageContent/PageContent';
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton'; import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
import { getCreateTogglePath } from 'utils/routePathHelpers'; import { getCreateTogglePath } from 'utils/routePathHelpers';
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions'; import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
@ -47,6 +46,10 @@ import { FeatureTagCell } from 'component/common/Table/cells/FeatureTagCell/Feat
import { useGlobalLocalStorage } from 'hooks/useGlobalLocalStorage'; import { useGlobalLocalStorage } from 'hooks/useGlobalLocalStorage';
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
const StyledResponsiveButton = styled(ResponsiveButton)(() => ({
whiteSpace: 'nowrap',
}));
interface IProjectFeatureTogglesProps { interface IProjectFeatureTogglesProps {
features: IProject['features']; features: IProject['features'];
environments: IProject['environments']; environments: IProject['environments'];
@ -102,7 +105,6 @@ export const ProjectFeatureToggles = ({
useGlobalLocalStorage(); useGlobalLocalStorage();
const navigate = useNavigate(); const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const { uiConfig } = useUiConfig();
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId); const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
const environments = useEnvironmentsRef( const environments = useEnvironmentsRef(
loading ? ['a', 'b', 'c'] : newEnvironments loading ? ['a', 'b', 'c'] : newEnvironments
@ -488,10 +490,8 @@ export const ProjectFeatureToggles = ({
<PageContent <PageContent
isLoading={loading} isLoading={loading}
className={styles.container} className={styles.container}
bodyClass={styles.bodyClass}
header={ header={
<PageHeader <PageHeader
className={styles.title}
titleElement={`Feature toggles (${rows.length})`} titleElement={`Feature toggles (${rows.length})`}
actions={ actions={
<> <>
@ -515,7 +515,7 @@ export const ProjectFeatureToggles = ({
setHiddenColumns={setHiddenColumns} setHiddenColumns={setHiddenColumns}
/> />
<PageHeader.Divider sx={{ marginLeft: 0 }} /> <PageHeader.Divider sx={{ marginLeft: 0 }} />
<ResponsiveButton <StyledResponsiveButton
onClick={() => onClick={() =>
navigate(getCreateTogglePath(projectId)) navigate(getCreateTogglePath(projectId))
} }
@ -523,10 +523,9 @@ export const ProjectFeatureToggles = ({
Icon={Add} Icon={Add}
projectId={projectId} projectId={projectId}
permission={CREATE_FEATURE} permission={CREATE_FEATURE}
className={styles.button}
> >
New feature toggle New feature toggle
</ResponsiveButton> </StyledResponsiveButton>
</> </>
} }
> >

View File

@ -1,9 +1,9 @@
import useProject, { import useProject, {
useProjectNameOrId, useProjectNameOrId,
} from 'hooks/api/getters/useProject/useProject'; } from 'hooks/api/getters/useProject/useProject';
import { styled } from '@mui/material';
import { ProjectFeatureToggles } from './ProjectFeatureToggles/ProjectFeatureToggles'; import { ProjectFeatureToggles } from './ProjectFeatureToggles/ProjectFeatureToggles';
import ProjectInfo from './ProjectInfo/ProjectInfo'; import ProjectInfo from './ProjectInfo/ProjectInfo';
import { useStyles } from './Project.styles';
import { usePageTitle } from 'hooks/usePageTitle'; import { usePageTitle } from 'hooks/usePageTitle';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useLastViewedProject } from '../../../hooks/useLastViewedProject'; import { useLastViewedProject } from '../../../hooks/useLastViewedProject';
@ -11,12 +11,23 @@ import { useEffect } from 'react';
const refreshInterval = 15 * 1000; const refreshInterval = 15 * 1000;
const StyledContainer = styled('div')(({ theme }) => ({
display: 'flex',
[theme.breakpoints.down('md')]: {
flexDirection: 'column',
},
}));
const StyledProjectToggles = styled('div')(() => ({
width: '100%',
minWidth: 0,
}));
const ProjectOverview = () => { const ProjectOverview = () => {
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const projectName = useProjectNameOrId(projectId); const projectName = useProjectNameOrId(projectId);
const { project, loading } = useProject(projectId, { refreshInterval }); const { project, loading } = useProject(projectId, { refreshInterval });
const { members, features, health, description, environments } = project; const { members, features, health, description, environments } = project;
const { classes: styles } = useStyles();
usePageTitle(`Project overview ${projectName}`); usePageTitle(`Project overview ${projectName}`);
const { setLastViewed } = useLastViewedProject(); const { setLastViewed } = useLastViewedProject();
@ -25,7 +36,7 @@ const ProjectOverview = () => {
}, [projectId, setLastViewed]); }, [projectId, setLastViewed]);
return ( return (
<div className={styles.containerStyles}> <StyledContainer>
<ProjectInfo <ProjectInfo
id={projectId} id={projectId}
description={description} description={description}
@ -33,14 +44,14 @@ const ProjectOverview = () => {
health={health} health={health}
featureCount={features?.length} featureCount={features?.length}
/> />
<div className={styles.projectToggles}> <StyledProjectToggles>
<ProjectFeatureToggles <ProjectFeatureToggles
features={features} features={features}
environments={environments} environments={environments}
loading={loading} loading={loading}
/> />
</div> </StyledProjectToggles>
</div> </StyledContainer>
); );
}; };

View File

@ -14,7 +14,11 @@ export const focusable = (theme: Theme) => ({
export const flexRow = { export const flexRow = {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
}; } as const;
export const defaultBorderRadius = (theme: Theme) => ({
borderRadius: `${theme.shape.borderRadius}px`,
});
/** /**
* Please extract styles below into MUI fragments as shown above * Please extract styles below into MUI fragments as shown above