1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-03 01:18:43 +02:00

chore: new create project dialog UI fixes (#7167)

This PR addresses several related fixes to the new project creation
dialog to prevent unnecessary growing and shifting:

- use a fixed width for the guidance sidebar
- use a fixed height for the guidance code snippet
- use a fixed height for the mobile guidance
- use a fixed width for the mode selector button
- cap description height

This is a little tricky because we don't want the changes for the dialog
to affect other forms. As such, I've added some new options you can use
when you create the guidance components / sidebar.
This commit is contained in:
Thomas Heartman 2024-05-28 07:10:34 +02:00 committed by GitHub
parent f7214c6cd0
commit 9a51f68f5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 144 additions and 53 deletions

View File

@ -14,7 +14,7 @@ import Info from '@mui/icons-material/Info';
import Loader from '../Loader/Loader';
import copy from 'copy-to-clipboard';
import useToast from 'hooks/useToast';
import type React from 'react';
import React from 'react';
import { type ReactNode, useState } from 'react';
import { ReactComponent as MobileGuidanceBG } from 'assets/img/mobileGuidanceBg.svg';
import { formTemplateSidebarWidth } from './FormTemplate.styles';
@ -36,6 +36,7 @@ interface ICreateProps {
footer?: ReactNode;
compact?: boolean;
showGuidance?: boolean;
useFixedSidebar?: boolean;
}
const StyledContainer = styled('section', {
@ -54,7 +55,21 @@ const StyledContainer = styled('section', {
},
}));
const StyledRelativeDiv = styled('div')(({ theme }) => relative);
const StyledMobileGuidanceWrapper = styled('div', {
shouldForwardProp: (prop) => !['guidanceHeight'].includes(prop.toString()),
})<{ guidanceHeight?: string }>(({ theme, guidanceHeight }) => ({
...relative,
// todo: review this. We're reaching down into a nested
// component, but due to the component structure, it'd be a
// lot of work to pass this down as a prop.
...(guidanceHeight
? {
aside: {
height: guidanceHeight,
},
}
: {}),
}));
const StyledMain = styled('div')(({ theme }) => ({
display: 'flex',
@ -151,20 +166,32 @@ const StyledInfoIcon = styled(Info)(({ theme }) => ({
fill: theme.palette.primary.contrastText,
}));
const StyledSidebar = styled('aside')(({ theme }) => ({
backgroundColor: theme.palette.background.sidebar,
padding: theme.spacing(4),
flexGrow: 0,
flexShrink: 0,
width: formTemplateSidebarWidth,
[theme.breakpoints.down(1100)]: {
width: '100%',
color: 'red',
},
[theme.breakpoints.down(500)]: {
padding: theme.spacing(4, 2),
},
}));
const StyledSidebar = styled('aside', {
shouldForwardProp: (prop) =>
!['sidebarWidth', 'fixedCodeHeight'].includes(prop.toString()),
})<{ sidebarWidth?: string; fixedCodeHeight?: string }>(
({ theme, sidebarWidth, fixedCodeHeight }) => ({
backgroundColor: theme.palette.background.sidebar,
padding: theme.spacing(4),
flexGrow: 0,
flexShrink: 0,
width: sidebarWidth || formTemplateSidebarWidth,
[theme.breakpoints.down(1100)]: {
width: '100%',
color: 'red',
},
[theme.breakpoints.down(500)]: {
padding: theme.spacing(4, 2),
},
...(fixedCodeHeight
? {
pre: {
height: fixedCodeHeight,
},
}
: {}),
}),
);
const StyledDescriptionCard = styled('article')(({ theme }) => ({
display: 'flex',
@ -218,6 +245,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
footer,
compact,
showGuidance = true,
useFixedSidebar,
}) => {
const { setToastData } = useToast();
const smallScreen = useMediaQuery(`(max-width:${1099}px)`);
@ -265,19 +293,23 @@ const FormTemplate: React.FC<ICreateProps> = ({
}
};
const SidebarComponent = useFixedSidebar ? FixedGuidance : Guidance;
return (
<StyledContainer modal={modal} compact={compact}>
<ConditionallyRender
condition={showGuidance && smallScreen}
show={
<StyledRelativeDiv>
<StyledMobileGuidanceWrapper
guidanceHeight={useFixedSidebar ? '240px' : undefined}
>
<MobileGuidance
description={description}
documentationIcon={documentationIcon}
documentationLink={documentationLink}
documentationLinkLabel={documentationLinkLabel}
/>
</StyledRelativeDiv>
</StyledMobileGuidanceWrapper>
}
/>
<StyledMain>
@ -312,7 +344,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
<ConditionallyRender
condition={showGuidance && !smallScreen}
show={
<Guidance
<SidebarComponent
documentationIcon={documentationIcon}
description={description}
documentationLink={documentationLink}
@ -324,7 +356,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
formatApiCode === undefined,
!(showDescription || showLink),
)}
</Guidance>
</SidebarComponent>
}
/>
</StyledContainer>
@ -380,7 +412,11 @@ interface IGuidanceProps {
showLink?: boolean;
}
const Guidance: React.FC<IGuidanceProps> = ({
const GuidanceContent: React.FC<
IGuidanceProps & {
fixedDocumentationHeight?: string;
}
> = ({
description,
children,
documentationLink,
@ -388,38 +424,79 @@ const Guidance: React.FC<IGuidanceProps> = ({
documentationLinkLabel = 'Learn more',
showDescription = true,
showLink = true,
fixedDocumentationHeight,
}) => {
const StyledDocumentationIconWrapper = styled('div')(({ theme }) => ({
height: '2rem',
display: 'grid',
placeItems: 'center',
svg: {
width: '100%',
},
}));
const StyledDocumentationWrapper = styled('div')({
height: fixedDocumentationHeight,
overflowY: 'auto',
});
const DocsWrapper = fixedDocumentationHeight
? StyledDocumentationWrapper
: React.Fragment;
return (
<>
<DocsWrapper>
<ConditionallyRender
condition={showDescription}
show={
<StyledDescriptionCard>
<ConditionallyRender
condition={!!documentationIcon}
show={
<StyledDocumentationIconWrapper>
{documentationIcon}
</StyledDocumentationIconWrapper>
}
/>
<StyledDescription>{description}</StyledDescription>
</StyledDescriptionCard>
}
/>
<ConditionallyRender
condition={showLink && !!documentationLink}
show={
<StyledLinkContainer>
<StyledLinkIcon />
<StyledDocumentationLink
href={documentationLink}
rel='noopener noreferrer'
target='_blank'
>
{documentationLinkLabel}
</StyledDocumentationLink>
</StyledLinkContainer>
}
/>
</DocsWrapper>
{children}
</>
);
};
const Guidance: React.FC<IGuidanceProps> = (props) => {
return (
<StyledSidebar>
<ConditionallyRender
condition={showDescription}
show={
<StyledDescriptionCard>
<ConditionallyRender
condition={!!documentationIcon}
show={documentationIcon}
/>
<StyledDescription>{description}</StyledDescription>
</StyledDescriptionCard>
}
/>
<GuidanceContent {...props} />
</StyledSidebar>
);
};
<ConditionallyRender
condition={showLink && !!documentationLink}
show={
<StyledLinkContainer>
<StyledLinkIcon />
<StyledDocumentationLink
href={documentationLink}
rel='noopener noreferrer'
target='_blank'
>
{documentationLinkLabel}
</StyledDocumentationLink>
</StyledLinkContainer>
}
/>
{children}
const FixedGuidance: React.FC<IGuidanceProps> = (props) => {
return (
<StyledSidebar sidebarWidth='420px' fixedCodeHeight='300px'>
<GuidanceContent {...props} fixedDocumentationHeight='170px' />
</StyledSidebar>
);
};

View File

@ -143,6 +143,7 @@ export const CreateProjectDialogue = ({
documentationLink={documentation.link?.url}
documentationLinkLabel={documentation.link?.label}
formatApiCode={formatApiCode}
useFixedSidebar
>
<NewProjectForm
errors={errors}

View File

@ -210,7 +210,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
className='description'
label='Description (optional)'
multiline
maxRows={20}
maxRows={3}
value={projectDesc}
onChange={(e) => setProjectDesc(e.target.value)}
data-testid={PROJECT_DESCRIPTION_INPUT}
@ -285,6 +285,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
button={{
label: projectMode,
icon: <ProjectModeIcon />,
labelWidth: `${`protected`.length}ch`,
}}
search={{
label: 'Filter project mode options',

View File

@ -1,6 +1,13 @@
import Search from '@mui/icons-material/Search';
import { v4 as uuidv4 } from 'uuid';
import { Box, Button, InputAdornment, List, ListItemText } from '@mui/material';
import {
Box,
Button,
InputAdornment,
List,
ListItemText,
styled,
} from '@mui/material';
import { type FC, type ReactNode, useRef, useState, useMemo } from 'react';
import {
StyledCheckbox,
@ -75,7 +82,7 @@ const useSelectionManagement = ({
type CombinedSelectProps = {
options: Array<{ label: string; value: string }>;
onChange: (value: string) => void;
button: { label: string; icon: ReactNode };
button: { label: string; icon: ReactNode; labelWidth?: string };
search: {
label: string;
placeholder: string;
@ -132,6 +139,11 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
const filteredOptions = options?.filter((option) =>
option.label.toLowerCase().includes(searchText.toLowerCase()),
);
const ButtonLabel = styled('span')(() => ({
width: button.labelWidth || 'unset',
}));
return (
<>
<Box ref={ref}>
@ -145,7 +157,7 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
}
}}
>
{button.label}
<ButtonLabel>{button.label}</ButtonLabel>
</Button>
</Box>
<StyledPopover