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:
parent
f7214c6cd0
commit
9a51f68f5f
@ -14,7 +14,7 @@ import Info from '@mui/icons-material/Info';
|
|||||||
import Loader from '../Loader/Loader';
|
import Loader from '../Loader/Loader';
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import type React from 'react';
|
import React from 'react';
|
||||||
import { type ReactNode, useState } from 'react';
|
import { type ReactNode, useState } from 'react';
|
||||||
import { ReactComponent as MobileGuidanceBG } from 'assets/img/mobileGuidanceBg.svg';
|
import { ReactComponent as MobileGuidanceBG } from 'assets/img/mobileGuidanceBg.svg';
|
||||||
import { formTemplateSidebarWidth } from './FormTemplate.styles';
|
import { formTemplateSidebarWidth } from './FormTemplate.styles';
|
||||||
@ -36,6 +36,7 @@ interface ICreateProps {
|
|||||||
footer?: ReactNode;
|
footer?: ReactNode;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
showGuidance?: boolean;
|
showGuidance?: boolean;
|
||||||
|
useFixedSidebar?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledContainer = styled('section', {
|
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 }) => ({
|
const StyledMain = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -151,20 +166,32 @@ const StyledInfoIcon = styled(Info)(({ theme }) => ({
|
|||||||
fill: theme.palette.primary.contrastText,
|
fill: theme.palette.primary.contrastText,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledSidebar = styled('aside')(({ theme }) => ({
|
const StyledSidebar = styled('aside', {
|
||||||
backgroundColor: theme.palette.background.sidebar,
|
shouldForwardProp: (prop) =>
|
||||||
padding: theme.spacing(4),
|
!['sidebarWidth', 'fixedCodeHeight'].includes(prop.toString()),
|
||||||
flexGrow: 0,
|
})<{ sidebarWidth?: string; fixedCodeHeight?: string }>(
|
||||||
flexShrink: 0,
|
({ theme, sidebarWidth, fixedCodeHeight }) => ({
|
||||||
width: formTemplateSidebarWidth,
|
backgroundColor: theme.palette.background.sidebar,
|
||||||
[theme.breakpoints.down(1100)]: {
|
padding: theme.spacing(4),
|
||||||
width: '100%',
|
flexGrow: 0,
|
||||||
color: 'red',
|
flexShrink: 0,
|
||||||
},
|
width: sidebarWidth || formTemplateSidebarWidth,
|
||||||
[theme.breakpoints.down(500)]: {
|
[theme.breakpoints.down(1100)]: {
|
||||||
padding: theme.spacing(4, 2),
|
width: '100%',
|
||||||
},
|
color: 'red',
|
||||||
}));
|
},
|
||||||
|
[theme.breakpoints.down(500)]: {
|
||||||
|
padding: theme.spacing(4, 2),
|
||||||
|
},
|
||||||
|
...(fixedCodeHeight
|
||||||
|
? {
|
||||||
|
pre: {
|
||||||
|
height: fixedCodeHeight,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const StyledDescriptionCard = styled('article')(({ theme }) => ({
|
const StyledDescriptionCard = styled('article')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -218,6 +245,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
|||||||
footer,
|
footer,
|
||||||
compact,
|
compact,
|
||||||
showGuidance = true,
|
showGuidance = true,
|
||||||
|
useFixedSidebar,
|
||||||
}) => {
|
}) => {
|
||||||
const { setToastData } = useToast();
|
const { setToastData } = useToast();
|
||||||
const smallScreen = useMediaQuery(`(max-width:${1099}px)`);
|
const smallScreen = useMediaQuery(`(max-width:${1099}px)`);
|
||||||
@ -265,19 +293,23 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SidebarComponent = useFixedSidebar ? FixedGuidance : Guidance;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer modal={modal} compact={compact}>
|
<StyledContainer modal={modal} compact={compact}>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={showGuidance && smallScreen}
|
condition={showGuidance && smallScreen}
|
||||||
show={
|
show={
|
||||||
<StyledRelativeDiv>
|
<StyledMobileGuidanceWrapper
|
||||||
|
guidanceHeight={useFixedSidebar ? '240px' : undefined}
|
||||||
|
>
|
||||||
<MobileGuidance
|
<MobileGuidance
|
||||||
description={description}
|
description={description}
|
||||||
documentationIcon={documentationIcon}
|
documentationIcon={documentationIcon}
|
||||||
documentationLink={documentationLink}
|
documentationLink={documentationLink}
|
||||||
documentationLinkLabel={documentationLinkLabel}
|
documentationLinkLabel={documentationLinkLabel}
|
||||||
/>
|
/>
|
||||||
</StyledRelativeDiv>
|
</StyledMobileGuidanceWrapper>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<StyledMain>
|
<StyledMain>
|
||||||
@ -312,7 +344,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
|||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={showGuidance && !smallScreen}
|
condition={showGuidance && !smallScreen}
|
||||||
show={
|
show={
|
||||||
<Guidance
|
<SidebarComponent
|
||||||
documentationIcon={documentationIcon}
|
documentationIcon={documentationIcon}
|
||||||
description={description}
|
description={description}
|
||||||
documentationLink={documentationLink}
|
documentationLink={documentationLink}
|
||||||
@ -324,7 +356,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
|||||||
formatApiCode === undefined,
|
formatApiCode === undefined,
|
||||||
!(showDescription || showLink),
|
!(showDescription || showLink),
|
||||||
)}
|
)}
|
||||||
</Guidance>
|
</SidebarComponent>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
@ -380,7 +412,11 @@ interface IGuidanceProps {
|
|||||||
showLink?: boolean;
|
showLink?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Guidance: React.FC<IGuidanceProps> = ({
|
const GuidanceContent: React.FC<
|
||||||
|
IGuidanceProps & {
|
||||||
|
fixedDocumentationHeight?: string;
|
||||||
|
}
|
||||||
|
> = ({
|
||||||
description,
|
description,
|
||||||
children,
|
children,
|
||||||
documentationLink,
|
documentationLink,
|
||||||
@ -388,38 +424,79 @@ const Guidance: React.FC<IGuidanceProps> = ({
|
|||||||
documentationLinkLabel = 'Learn more',
|
documentationLinkLabel = 'Learn more',
|
||||||
showDescription = true,
|
showDescription = true,
|
||||||
showLink = 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 (
|
return (
|
||||||
<StyledSidebar>
|
<StyledSidebar>
|
||||||
<ConditionallyRender
|
<GuidanceContent {...props} />
|
||||||
condition={showDescription}
|
</StyledSidebar>
|
||||||
show={
|
);
|
||||||
<StyledDescriptionCard>
|
};
|
||||||
<ConditionallyRender
|
|
||||||
condition={!!documentationIcon}
|
|
||||||
show={documentationIcon}
|
|
||||||
/>
|
|
||||||
<StyledDescription>{description}</StyledDescription>
|
|
||||||
</StyledDescriptionCard>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ConditionallyRender
|
const FixedGuidance: React.FC<IGuidanceProps> = (props) => {
|
||||||
condition={showLink && !!documentationLink}
|
return (
|
||||||
show={
|
<StyledSidebar sidebarWidth='420px' fixedCodeHeight='300px'>
|
||||||
<StyledLinkContainer>
|
<GuidanceContent {...props} fixedDocumentationHeight='170px' />
|
||||||
<StyledLinkIcon />
|
|
||||||
<StyledDocumentationLink
|
|
||||||
href={documentationLink}
|
|
||||||
rel='noopener noreferrer'
|
|
||||||
target='_blank'
|
|
||||||
>
|
|
||||||
{documentationLinkLabel}
|
|
||||||
</StyledDocumentationLink>
|
|
||||||
</StyledLinkContainer>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{children}
|
|
||||||
</StyledSidebar>
|
</StyledSidebar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -143,6 +143,7 @@ export const CreateProjectDialogue = ({
|
|||||||
documentationLink={documentation.link?.url}
|
documentationLink={documentation.link?.url}
|
||||||
documentationLinkLabel={documentation.link?.label}
|
documentationLinkLabel={documentation.link?.label}
|
||||||
formatApiCode={formatApiCode}
|
formatApiCode={formatApiCode}
|
||||||
|
useFixedSidebar
|
||||||
>
|
>
|
||||||
<NewProjectForm
|
<NewProjectForm
|
||||||
errors={errors}
|
errors={errors}
|
||||||
|
@ -210,7 +210,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
|
|||||||
className='description'
|
className='description'
|
||||||
label='Description (optional)'
|
label='Description (optional)'
|
||||||
multiline
|
multiline
|
||||||
maxRows={20}
|
maxRows={3}
|
||||||
value={projectDesc}
|
value={projectDesc}
|
||||||
onChange={(e) => setProjectDesc(e.target.value)}
|
onChange={(e) => setProjectDesc(e.target.value)}
|
||||||
data-testid={PROJECT_DESCRIPTION_INPUT}
|
data-testid={PROJECT_DESCRIPTION_INPUT}
|
||||||
@ -285,6 +285,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
|
|||||||
button={{
|
button={{
|
||||||
label: projectMode,
|
label: projectMode,
|
||||||
icon: <ProjectModeIcon />,
|
icon: <ProjectModeIcon />,
|
||||||
|
labelWidth: `${`protected`.length}ch`,
|
||||||
}}
|
}}
|
||||||
search={{
|
search={{
|
||||||
label: 'Filter project mode options',
|
label: 'Filter project mode options',
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
import Search from '@mui/icons-material/Search';
|
import Search from '@mui/icons-material/Search';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
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 { type FC, type ReactNode, useRef, useState, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
StyledCheckbox,
|
StyledCheckbox,
|
||||||
@ -75,7 +82,7 @@ const useSelectionManagement = ({
|
|||||||
type CombinedSelectProps = {
|
type CombinedSelectProps = {
|
||||||
options: Array<{ label: string; value: string }>;
|
options: Array<{ label: string; value: string }>;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
button: { label: string; icon: ReactNode };
|
button: { label: string; icon: ReactNode; labelWidth?: string };
|
||||||
search: {
|
search: {
|
||||||
label: string;
|
label: string;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
@ -132,6 +139,11 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
|
|||||||
const filteredOptions = options?.filter((option) =>
|
const filteredOptions = options?.filter((option) =>
|
||||||
option.label.toLowerCase().includes(searchText.toLowerCase()),
|
option.label.toLowerCase().includes(searchText.toLowerCase()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const ButtonLabel = styled('span')(() => ({
|
||||||
|
width: button.labelWidth || 'unset',
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box ref={ref}>
|
<Box ref={ref}>
|
||||||
@ -145,7 +157,7 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{button.label}
|
<ButtonLabel>{button.label}</ButtonLabel>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<StyledPopover
|
<StyledPopover
|
||||||
|
Loading…
Reference in New Issue
Block a user