Skip to content

Commit

Permalink
fix: add grid w/container query for projects (#8344)
Browse files Browse the repository at this point in the history
The main goals of this are:

1. Make it so that the layout grid doesn't break on small screens
2. Fix an issue where the border of the box didn't fit the outline
3. (Bonus): make the layout of the info box depend on the **box's**
size, not the screen size.

To achieve those goals, this PR:
1. Switches to using a native CSS grid instead of MUI's grid component.
This gives us more power over the layout in various different sizes.
2. Switches from putting borders on the boxes inside the grid, instead
makes the grid container the color of the border and uses gaps to create
borders.
3. If your browser supports it, it will use container queries to
determine whether we should display the layout as a multi-column grid or
in a single column.


Container query demo (both with the same screen sizes):

Sidebar closed: 

![image](https://github.com/user-attachments/assets/9a7d9a78-de92-4429-bf06-8e98fbf40ed0)

Sidebar open:

![image](https://github.com/user-attachments/assets/90e790ba-13db-485c-8f5e-ee60fe36dabb)
  • Loading branch information
thomasheartman authored Oct 3, 2024
1 parent 9b1d9f5 commit 35a73a5
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 98 deletions.
62 changes: 62 additions & 0 deletions frontend/src/component/personalDashboard/Grid.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,68 @@
import { Box, Grid, styled } from '@mui/material';
import type { Theme } from '@mui/material/styles/createTheme';

export const ContentGridContainer = styled('div')({
containerType: 'inline-size',
});

const ContentGrid2 = styled('article')(({ theme }) => {
return {
backgroundColor: theme.palette.divider,
borderRadius: `${theme.shape.borderRadiusLarge}px`,
overflow: 'hidden',
border: `0.5px solid ${theme.palette.divider}`,
gap: `2px`,
display: 'flex',
flexFlow: 'column nowrap',

'&>*': {
backgroundColor: theme.palette.background.paper,
},
};
});

export const ProjectGrid = styled(ContentGrid2)(({ theme }) => ({
'@container (min-width: 1000px)': {
gridTemplateColumns: '1fr 1fr 1fr',
display: 'grid',
gridTemplateAreas: `
"title onboarding onboarding"
"projects box1 box2"
". owners owners"
`,
},

'@supports not (container-type: inline-size)': {
[theme.breakpoints.up('lg')]: {
gridTemplateColumns: '1fr 1fr 1fr',
display: 'grid',
gridTemplateAreas: `
"title onboarding onboarding"
"projects box1 box2"
". owners owners"
`,
},
},
}));

export const SpacedGridItem2 = styled('div')(({ theme }) => ({
padding: theme.spacing(4),
}));

export const EmptyGridItem = styled('div')(({ theme }) => ({
display: 'none',

'@container (min-width: 1000px)': {
display: 'block',
},

'@supports not (container-type: inline-size)': {
[theme.breakpoints.up('lg')]: {
display: 'block',
},
},
}));

export const ContentGrid = styled(Grid)(({ theme }) => ({
backgroundColor: theme.palette.background.paper,
borderRadius: `${theme.shape.borderRadiusLarge}px`,
Expand Down
223 changes: 125 additions & 98 deletions frontend/src/component/personalDashboard/MyProjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import type {
PersonalDashboardSchemaProjectsItem,
} from '../../openapi';
import {
ContentGrid,
ContentGridContainer,
EmptyGridItem,
ListItemBox,
listItemStyle,
SpacedGridItem,
ProjectGrid,
SpacedGridItem2,
} from './Grid';

const ActiveProjectDetails: FC<{
Expand Down Expand Up @@ -78,104 +80,129 @@ export const MyProjects: FC<{
activeProjectStage === 'first-flag-created';

return (
<ContentGrid container columns={{ lg: 12, md: 1 }}>
<SpacedGridItem item lg={4} md={1}>
<Typography variant='h3'>My projects</Typography>
</SpacedGridItem>
<SpacedGridItem
item
lg={8}
md={1}
sx={{ display: 'flex', justifyContent: 'flex-end' }}
>
{setupIncomplete ? (
<Badge color='warning'>Setup incomplete</Badge>
) : null}
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
<List
disablePadding={true}
sx={{ maxHeight: '400px', overflow: 'auto' }}
<ContentGridContainer>
<ProjectGrid>
<SpacedGridItem2
sx={{
gridArea: 'title',
}}
>
{projects.map((project) => {
return (
<ListItem
key={project.id}
disablePadding={true}
sx={{ mb: 1 }}
>
<ListItemButton
sx={listItemStyle}
selected={project.id === activeProject}
onClick={() => setActiveProject(project.id)}
<Typography variant='h3'>My projects</Typography>
</SpacedGridItem2>
<SpacedGridItem2
sx={{
gridArea: 'onboarding',
display: 'flex',
justifyContent: 'flex-end',
}}
>
{setupIncomplete ? (
<Badge color='warning'>Setup incomplete</Badge>
) : null}
</SpacedGridItem2>
<SpacedGridItem2
sx={{
gridArea: 'projects',
}}
>
<List
disablePadding={true}
sx={{ maxHeight: '400px', overflow: 'auto' }}
>
{projects.map((project) => {
return (
<ListItem
key={project.id}
disablePadding={true}
sx={{ mb: 1 }}
>
<ListItemBox>
<ProjectIcon color='primary' />
<StyledCardTitle>
{project.name}
</StyledCardTitle>
<IconButton
component={Link}
href={`projects/${project.id}`}
size='small'
sx={{ ml: 'auto' }}
>
<LinkIcon
titleAccess={`projects/${project.id}`}
<ListItemButton
sx={listItemStyle}
selected={project.id === activeProject}
onClick={() =>
setActiveProject(project.id)
}
>
<ListItemBox>
<ProjectIcon color='primary' />
<StyledCardTitle>
{project.name}
</StyledCardTitle>
<IconButton
component={Link}
href={`projects/${project.id}`}
size='small'
sx={{ ml: 'auto' }}
>
<LinkIcon
titleAccess={`projects/${project.id}`}
/>
</IconButton>
</ListItemBox>
{project.id === activeProject ? (
<ActiveProjectDetails
project={project}
/>
</IconButton>
</ListItemBox>
{project.id === activeProject ? (
<ActiveProjectDetails
project={project}
/>
) : null}
</ListItemButton>
</ListItem>
);
})}
</List>
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1}>
{activeProjectStage === 'onboarded' &&
personalDashboardProjectDetails ? (
<ProjectSetupComplete
project={activeProject}
insights={personalDashboardProjectDetails.insights}
/>
) : null}
{activeProjectStage === 'onboarding-started' ||
activeProjectStage === 'loading' ? (
<CreateFlag project={activeProject} />
) : null}
{activeProjectStage === 'first-flag-created' ? (
<ExistingFlag project={activeProject} />
) : null}
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1} sx={{ pr: 4 }}>
{activeProjectStage === 'onboarded' &&
personalDashboardProjectDetails ? (
<LatestProjectEvents
latestEvents={
personalDashboardProjectDetails.latestEvents
}
/>
) : null}
{setupIncomplete || activeProjectStage === 'loading' ? (
<ConnectSDK project={activeProject} />
) : null}
</SpacedGridItem>
<SpacedGridItem item lg={4} md={1} />
<SpacedGridItem item lg={8} md={1}>
{personalDashboardProjectDetails ? (
<RoleAndOwnerInfo
roles={personalDashboardProjectDetails.roles.map(
(role) => role.name,
)}
owners={personalDashboardProjectDetails.owners}
/>
) : null}
</SpacedGridItem>
</ContentGrid>
) : null}
</ListItemButton>
</ListItem>
);
})}
</List>
</SpacedGridItem2>
<SpacedGridItem2
sx={{
gridArea: 'box1',
}}
>
{activeProjectStage === 'onboarded' &&
personalDashboardProjectDetails ? (
<ProjectSetupComplete
project={activeProject}
insights={personalDashboardProjectDetails.insights}
/>
) : null}
{activeProjectStage === 'onboarding-started' ||
activeProjectStage === 'loading' ? (
<CreateFlag project={activeProject} />
) : null}
{activeProjectStage === 'first-flag-created' ? (
<ExistingFlag project={activeProject} />
) : null}
</SpacedGridItem2>
<SpacedGridItem2
sx={{
gridArea: 'box2',
}}
>
{activeProjectStage === 'onboarded' &&
personalDashboardProjectDetails ? (
<LatestProjectEvents
latestEvents={
personalDashboardProjectDetails.latestEvents
}
/>
) : null}
{setupIncomplete || activeProjectStage === 'loading' ? (
<ConnectSDK project={activeProject} />
) : null}
</SpacedGridItem2>
<EmptyGridItem />
<SpacedGridItem2
sx={{
gridArea: 'owners',
}}
>
{personalDashboardProjectDetails ? (
<RoleAndOwnerInfo
roles={personalDashboardProjectDetails.roles.map(
(role) => role.name,
)}
owners={personalDashboardProjectDetails.owners}
/>
) : null}
</SpacedGridItem2>
</ProjectGrid>
</ContentGridContainer>
);
};

0 comments on commit 35a73a5

Please sign in to comment.