mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
feat: show docs with icons in sidebar (#7109)
Adds icons to sidebar documentation and removes the link when you can't interact with it. I'm a little concerned that this won't be very accessible at the moment, because we don't announce that anything has changed (i.e. there's no way to find out that the text has changed if you can't see it), and the text isn't labeled as describing anything. (this is being addressed in #7110 ) ![image](https://github.com/Unleash/unleash/assets/17786332/2f482aa1-b74d-4b0f-97aa-2dbc1d1f82f9) There's a few caveats to this: 1. we don't set a min height at the moment. I've avoided this because we use the sidebar a number of other places and I wanted to touch as little as possible. This means we can still get height adjustments 2. The new project icon doesn't have the same proportions as the mui icons. This adds some additional jank. We should probably look at this, though.
This commit is contained in:
parent
688bac9f87
commit
78fcdbf132
@ -1,5 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="37" height="35" viewBox="0 0 37 35" fill="none">
|
||||
<rect x="8.9864" y="9.93245" width="3.78378" height="13.2432" rx="1.41892" fill="#6C65E5" stroke="#6C65E5" stroke-width="0.945947" />
|
||||
<rect x="16.5542" y="13.7161" width="3.78378" height="9.45946" rx="1.41892" fill="#6C65E5" stroke="#6C65E5" stroke-width="0.945947" />
|
||||
<rect x="24.1217" y="18.4456" width="3.78378" height="4.72973" rx="1.41892" fill="#6C65E5" stroke="#6C65E5" stroke-width="0.945947" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" focusable='false' aria-hidden='true' width="37" height="35" viewBox="0 0 37 35" fill="none">
|
||||
<rect x="8.9864" y="9.93245" width="3.78378" height="13.2432" rx="1.41892" stroke-width="0.945947" />
|
||||
<rect x="16.5542" y="13.7161" width="3.78378" height="9.45946" rx="1.41892" stroke-width="0.945947" />
|
||||
<rect x="24.1217" y="18.4456" width="3.78378" height="4.72973" rx="1.41892" stroke-width="0.945947" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 519 B After Width: | Height: | Size: 460 B |
@ -23,8 +23,9 @@ import { relative } from 'themes/themeStyles';
|
||||
interface ICreateProps {
|
||||
title?: ReactNode;
|
||||
description: string;
|
||||
documentationLink: string;
|
||||
documentationLinkLabel: string;
|
||||
documentationLink?: string;
|
||||
documentationIcon?: ReactNode;
|
||||
documentationLinkLabel?: string;
|
||||
loading?: boolean;
|
||||
modal?: boolean;
|
||||
disablePadding?: boolean;
|
||||
@ -165,16 +166,26 @@ const StyledSidebar = styled('aside')(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledDescription = styled('p')(({ theme }) => ({
|
||||
color: theme.palette.common.white,
|
||||
const StyledDescriptionCard = styled('article')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexFlow: 'column nowrap',
|
||||
gap: theme.spacing(2),
|
||||
alignItems: 'center',
|
||||
zIndex: 1,
|
||||
color: theme.palette.common.white,
|
||||
position: 'relative',
|
||||
marginBlockEnd: theme.spacing(3),
|
||||
}));
|
||||
|
||||
const StyledDescription = styled('p')(({ theme }) => ({
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
const StyledLinkContainer = styled('div')(({ theme }) => ({
|
||||
margin: theme.spacing(3, 0),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
const StyledLinkIcon = styled(MenuBookIcon)(({ theme }) => ({
|
||||
@ -195,6 +206,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
||||
description,
|
||||
children,
|
||||
documentationLink,
|
||||
documentationIcon,
|
||||
documentationLinkLabel,
|
||||
loading,
|
||||
modal,
|
||||
@ -261,6 +273,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
||||
<StyledRelativeDiv>
|
||||
<MobileGuidance
|
||||
description={description}
|
||||
documentationIcon={documentationIcon}
|
||||
documentationLink={documentationLink}
|
||||
documentationLinkLabel={documentationLinkLabel}
|
||||
/>
|
||||
@ -300,6 +313,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
||||
condition={showGuidance && !smallScreen}
|
||||
show={
|
||||
<Guidance
|
||||
documentationIcon={documentationIcon}
|
||||
description={description}
|
||||
documentationLink={documentationLink}
|
||||
documentationLinkLabel={documentationLinkLabel}
|
||||
@ -319,7 +333,8 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
||||
|
||||
interface IMobileGuidance {
|
||||
description: string;
|
||||
documentationLink: string;
|
||||
documentationLink?: string;
|
||||
documentationIcon?: ReactNode;
|
||||
documentationLinkLabel?: string;
|
||||
}
|
||||
|
||||
@ -327,6 +342,7 @@ const MobileGuidance = ({
|
||||
description,
|
||||
documentationLink,
|
||||
documentationLinkLabel,
|
||||
documentationIcon,
|
||||
}: IMobileGuidance) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@ -345,6 +361,7 @@ const MobileGuidance = ({
|
||||
</Tooltip>
|
||||
<Collapse in={open} timeout={500}>
|
||||
<Guidance
|
||||
documentationIcon={documentationIcon}
|
||||
description={description}
|
||||
documentationLink={documentationLink}
|
||||
documentationLinkLabel={documentationLinkLabel}
|
||||
@ -356,7 +373,8 @@ const MobileGuidance = ({
|
||||
|
||||
interface IGuidanceProps {
|
||||
description: string;
|
||||
documentationLink: string;
|
||||
documentationIcon?: ReactNode;
|
||||
documentationLink?: string;
|
||||
documentationLinkLabel?: string;
|
||||
showDescription?: boolean;
|
||||
showLink?: boolean;
|
||||
@ -366,6 +384,7 @@ const Guidance: React.FC<IGuidanceProps> = ({
|
||||
description,
|
||||
children,
|
||||
documentationLink,
|
||||
documentationIcon,
|
||||
documentationLinkLabel = 'Learn more',
|
||||
showDescription = true,
|
||||
showLink = true,
|
||||
@ -374,11 +393,19 @@ const Guidance: React.FC<IGuidanceProps> = ({
|
||||
<StyledSidebar>
|
||||
<ConditionallyRender
|
||||
condition={showDescription}
|
||||
show={<StyledDescription>{description}</StyledDescription>}
|
||||
show={
|
||||
<StyledDescriptionCard>
|
||||
<ConditionallyRender
|
||||
condition={!!documentationIcon}
|
||||
show={documentationIcon}
|
||||
/>
|
||||
<StyledDescription>{description}</StyledDescription>
|
||||
</StyledDescriptionCard>
|
||||
}
|
||||
/>
|
||||
|
||||
<ConditionallyRender
|
||||
condition={showLink}
|
||||
condition={showLink && !!documentationLink}
|
||||
show={
|
||||
<StyledLinkContainer>
|
||||
<StyledLinkIcon />
|
||||
@ -392,7 +419,6 @@ const Guidance: React.FC<IGuidanceProps> = ({
|
||||
</StyledLinkContainer>
|
||||
}
|
||||
/>
|
||||
|
||||
{children}
|
||||
</StyledSidebar>
|
||||
);
|
||||
|
@ -76,7 +76,8 @@ export const StyledParagraphInfo = styled('p')(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const StyledProjectIcon = styled(ProjectIcon)(({ theme }) => ({
|
||||
color: theme.palette.primary.main,
|
||||
fill: theme.palette.primary.main,
|
||||
stroke: theme.palette.primary.main,
|
||||
}));
|
||||
|
||||
export const StyledIconBox = styled(Box)(({ theme }) => ({
|
||||
|
@ -9,11 +9,12 @@ import useProjectForm, {
|
||||
DEFAULT_PROJECT_STICKINESS,
|
||||
} from '../../hooks/useProjectForm';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
import { useState } from 'react';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button, Dialog, styled } from '@mui/material';
|
||||
import { ReactComponent as ProjectIcon } from 'assets/icons/projectIconSmall.svg';
|
||||
|
||||
interface ICreateProjectDialogueProps {
|
||||
open: boolean;
|
||||
@ -24,6 +25,7 @@ const StyledDialog = styled(Dialog)(({ theme, maxWidth }) => ({
|
||||
'& .MuiDialog-paper': {
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
maxWidth: theme.spacing(170),
|
||||
width: '100%',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
padding: 0,
|
||||
@ -35,6 +37,11 @@ const StyledButton = styled(Button)(({ theme }) => ({
|
||||
marginLeft: theme.spacing(3),
|
||||
}));
|
||||
|
||||
const StyledProjectIcon = styled(ProjectIcon)(({ theme }) => ({
|
||||
fill: theme.palette.common.white,
|
||||
stroke: theme.palette.common.white,
|
||||
}));
|
||||
|
||||
export const CreateProjectDialogue = ({
|
||||
open,
|
||||
onClose,
|
||||
@ -67,8 +74,18 @@ export const CreateProjectDialogue = ({
|
||||
errors,
|
||||
} = useProjectForm();
|
||||
|
||||
const generalDocumentation =
|
||||
'Projects allows you to group feature flags together in the management UI.';
|
||||
const generalDocumentation: {
|
||||
icon: ReactNode;
|
||||
text: string;
|
||||
link?: { url: string; label: string };
|
||||
} = {
|
||||
icon: <StyledProjectIcon />,
|
||||
text: 'Projects allows you to group feature flags together in the management UI.',
|
||||
link: {
|
||||
url: 'https://docs.getunleash.io/reference/projects',
|
||||
label: 'Projects documentation',
|
||||
},
|
||||
};
|
||||
|
||||
const [documentation, setDocumentation] = useState(generalDocumentation);
|
||||
|
||||
@ -115,14 +132,16 @@ export const CreateProjectDialogue = ({
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledDialog open={open} onClose={onClose}>
|
||||
<FormTemplate
|
||||
compact
|
||||
disablePadding
|
||||
description={documentation}
|
||||
documentationLink='https://docs.getunleash.io/reference/projects'
|
||||
documentationLinkLabel='Projects documentation'
|
||||
description={documentation.text}
|
||||
documentationIcon={documentation.icon}
|
||||
documentationLink={documentation.link?.url}
|
||||
documentationLinkLabel={documentation.link?.label}
|
||||
formatApiCode={formatApiCode}
|
||||
>
|
||||
<NewProjectForm
|
||||
|
@ -15,6 +15,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
||||
import EnvironmentsIcon from '@mui/icons-material/CloudCircle';
|
||||
import { useStickinessOptions } from 'hooks/useStickinessOptions';
|
||||
import { ReactComponent as ChangeRequestIcon } from 'assets/icons/merge.svg';
|
||||
import type { ReactNode } from 'react';
|
||||
import theme from 'themes/theme';
|
||||
|
||||
const StyledForm = styled('form')(({ theme }) => ({
|
||||
@ -38,7 +39,8 @@ const TopGrid = styled(StyledFormSection)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
const StyledIcon = styled(ProjectIcon)(({ theme }) => ({
|
||||
color: theme.palette.primary.main,
|
||||
fill: theme.palette.primary.main,
|
||||
stroke: theme.palette.primary.main,
|
||||
}));
|
||||
|
||||
const StyledHeader = styled(Typography)(({ theme }) => ({
|
||||
@ -101,7 +103,7 @@ type FormProps = {
|
||||
mode: 'Create' | 'Edit';
|
||||
clearErrors: () => void;
|
||||
validateProjectId: () => void;
|
||||
overrideDocumentation: (description: string) => void;
|
||||
overrideDocumentation: (args: { text: string; icon: ReactNode }) => void;
|
||||
clearDocumentationOverride: () => void;
|
||||
};
|
||||
|
||||
@ -223,9 +225,10 @@ export const NewProjectForm: React.FC<FormProps> = ({
|
||||
placeholder: 'Select project environments',
|
||||
}}
|
||||
onOpen={() =>
|
||||
overrideDocumentation(
|
||||
`Each feature flag can have a separate configuration per environment. This setting configures which environments your project should start with.`,
|
||||
)
|
||||
overrideDocumentation({
|
||||
icon: <EnvironmentsIcon />,
|
||||
text: `Each feature flag can have a separate configuration per environment. This setting configures which environments your project should start with.`,
|
||||
})
|
||||
}
|
||||
onClose={clearDocumentationOverride}
|
||||
/>
|
||||
@ -247,9 +250,10 @@ export const NewProjectForm: React.FC<FormProps> = ({
|
||||
placeholder: 'Select default stickiness',
|
||||
}}
|
||||
onOpen={() =>
|
||||
overrideDocumentation(
|
||||
'Stickiness is used to guarantee that your users see the same result when using a gradual rollout. Default stickiness allows you to choose which field is used by default in this project.',
|
||||
)
|
||||
overrideDocumentation({
|
||||
icon: <StickinessIcon />,
|
||||
text: 'Stickiness is used to guarantee that your users see the same result when using a gradual rollout. Default stickiness allows you to choose which field is used by default in this project.',
|
||||
})
|
||||
}
|
||||
onClose={clearDocumentationOverride}
|
||||
/>
|
||||
@ -271,9 +275,10 @@ export const NewProjectForm: React.FC<FormProps> = ({
|
||||
placeholder: 'Select project mode',
|
||||
}}
|
||||
onOpen={() =>
|
||||
overrideDocumentation(
|
||||
'Mode defines who should be allowed to interact and see your project. Private mode hides the project from anyone except the project owner and members.',
|
||||
)
|
||||
overrideDocumentation({
|
||||
icon: <ProjectModeIcon />,
|
||||
text: 'Mode defines who should be allowed to interact and see your project. Private mode hides the project from anyone except the project owner and members.',
|
||||
})
|
||||
}
|
||||
onClose={clearDocumentationOverride}
|
||||
/>
|
||||
@ -316,9 +321,10 @@ export const NewProjectForm: React.FC<FormProps> = ({
|
||||
projectChangeRequestConfiguration
|
||||
}
|
||||
onOpen={() =>
|
||||
overrideDocumentation(
|
||||
'Change requests can be configured per environment and require changes to go through an approval process before being applied.',
|
||||
)
|
||||
overrideDocumentation({
|
||||
icon: <ChangeRequestIcon />,
|
||||
text: 'Change requests can be configured per environment and require changes to go through an approval process before being applied.',
|
||||
})
|
||||
}
|
||||
onClose={clearDocumentationOverride}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user