Skip to content

Commit

Permalink
chore: flag overview page redesign - environments
Browse files Browse the repository at this point in the history
  • Loading branch information
nunogois committed Nov 6, 2024
1 parent cfe19de commit 1175828
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 79 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Button, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';

const StyledTab = styled(Button)<{ selected: boolean }>(
({ theme, selected }) => ({
Expand All @@ -17,7 +18,8 @@ const StyledTab = styled(Button)<{ selected: boolean }>(
transition: 'background-color 0.2s ease',
color: theme.palette.text.primary,
textAlign: 'left',
padding: theme.spacing(2, 4),
padding: theme.spacing(0, 2),
gap: theme.spacing(1),
fontSize: theme.fontSizes.bodySize,
fontWeight: selected
? theme.fontWeight.bold
Expand All @@ -41,27 +43,53 @@ const StyledTab = styled(Button)<{ selected: boolean }>(
}),
);

const StyledTabLabel = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(0.5),
}));

const StyledTabDescription = styled('div')(({ theme }) => ({
fontWeight: theme.fontWeight.medium,
fontSize: theme.fontSizes.smallBody,
color: theme.palette.text.secondary,
}));

interface IVerticalTabProps {
label: string;
description?: string;
selected?: boolean;
onClick: () => void;
icon?: React.ReactNode;
startIcon?: React.ReactNode;
endIcon?: React.ReactNode;
}

export const VerticalTab = ({
label,
description,
selected,
onClick,
icon,
startIcon,
endIcon,
}: IVerticalTabProps) => (
<StyledTab
selected={Boolean(selected)}
className={selected ? 'selected' : ''}
onClick={onClick}
disableElevation
disableFocusRipple
fullWidth
>
{label}
{icon}
{startIcon}
<StyledTabLabel>
{label}
<ConditionallyRender
condition={Boolean(description)}
show={
<StyledTabDescription>{description}</StyledTabDescription>
}
/>
</StyledTabLabel>
{endIcon}
</StyledTab>
);
64 changes: 41 additions & 23 deletions frontend/src/component/common/VerticalTabs/VerticalTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { styled } from '@mui/material';
import { VerticalTab } from './VerticalTab/VerticalTab';
import type { HTMLAttributes } from 'react';

const StyledTabPage = styled('div')(({ theme }) => ({
display: 'flex',
Expand All @@ -15,11 +16,13 @@ const StyledTabPageContent = styled('div')(() => ({
flexDirection: 'column',
}));

const StyledTabs = styled('div')(({ theme }) => ({
const StyledTabs = styled('div', {
shouldForwardProp: (prop) => prop !== 'fullWidth',
})<{ fullWidth?: boolean }>(({ theme, fullWidth }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
width: theme.spacing(30),
width: fullWidth ? '100%' : theme.spacing(30),
flexShrink: 0,
[theme.breakpoints.down('xl')]: {
width: '100%',
Expand All @@ -29,38 +32,53 @@ const StyledTabs = styled('div')(({ theme }) => ({
export interface ITab {
id: string;
label: string;
description?: string;
path?: string;
hidden?: boolean;
icon?: React.ReactNode;
startIcon?: React.ReactNode;
endIcon?: React.ReactNode;
}

interface IVerticalTabsProps {
interface IVerticalTabsProps
extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
tabs: ITab[];
value: string;
onChange: (tab: ITab) => void;
children: React.ReactNode;
children?: React.ReactNode;
}

export const VerticalTabs = ({
tabs,
value,
onChange,
children,
}: IVerticalTabsProps) => (
<StyledTabPage>
<StyledTabs>
{tabs
.filter((tab) => !tab.hidden)
.map((tab) => (
<VerticalTab
key={tab.id}
label={tab.label}
selected={tab.id === value}
onClick={() => onChange(tab)}
icon={tab.icon}
/>
))}
</StyledTabs>
<StyledTabPageContent>{children}</StyledTabPageContent>
</StyledTabPage>
);
...props
}: IVerticalTabsProps) => {
const verticalTabs = tabs
.filter((tab) => !tab.hidden)
.map((tab) => (
<VerticalTab
key={tab.id}
label={tab.label}
description={tab.description}
selected={tab.id === value}
onClick={() => onChange(tab)}
startIcon={tab.startIcon}
endIcon={tab.endIcon}
/>
));

if (!children) {
return (
<StyledTabs fullWidth {...props}>
{verticalTabs}
</StyledTabs>
);
}
return (
<StyledTabPage>
<StyledTabs {...props}>{verticalTabs}</StyledTabs>
<StyledTabPageContent>{children}</StyledTabPageContent>
</StyledTabPage>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { FeatureOverviewSidePanel as NewFeatureOverviewSidePanel } from 'compone
import { useHiddenEnvironments } from 'hooks/useHiddenEnvironments';
import { styled } from '@mui/material';
import { FeatureStrategyCreate } from 'component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { useLastViewedFlags } from 'hooks/useLastViewedFlags';
import { useUiFlag } from 'hooks/useUiFlag';
import OldFeatureOverviewMetaData from './FeatureOverviewMetaData/OldFeatureOverviewMetaData';
import { OldFeatureOverviewSidePanel } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/OldFeatureOverviewSidePanel';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { NewFeatureOverviewEnvironment } from './NewFeatureOverviewEnvironment/NewFeatureOverviewEnvironment';

const StyledContainer = styled('div')(({ theme }) => ({
display: 'flex',
Expand Down Expand Up @@ -48,26 +50,40 @@ const FeatureOverview = () => {
useEffect(() => {
setLastViewed({ featureId, projectId });
}, [featureId]);
const [environmentId, setEnvironmentId] = useState('');

const flagOverviewRedesign = useUiFlag('flagOverviewRedesign');
const FeatureOverviewMetaData = flagOverviewRedesign
? NewFeatureOverviewMetaData
: OldFeatureOverviewMetaData;
const FeatureOverviewSidePanel = flagOverviewRedesign
? NewFeatureOverviewSidePanel
: OldFeatureOverviewSidePanel;
const FeatureOverviewSidePanel = flagOverviewRedesign ? (
<NewFeatureOverviewSidePanel
environmentId={environmentId}
setEnvironmentId={setEnvironmentId}
/>
) : (
<OldFeatureOverviewSidePanel
hiddenEnvironments={hiddenEnvironments}
setHiddenEnvironments={setHiddenEnvironments}
/>
);

return (
<StyledContainer>
<div>
<FeatureOverviewMetaData />
<FeatureOverviewSidePanel
hiddenEnvironments={hiddenEnvironments}
setHiddenEnvironments={setHiddenEnvironments}
/>
{FeatureOverviewSidePanel}
</div>
<StyledMainContent>
<FeatureOverviewEnvironments />
<ConditionallyRender
condition={flagOverviewRedesign}
show={
<NewFeatureOverviewEnvironment
environmentId={environmentId}
/>
}
elseShow={<FeatureOverviewEnvironments />}
/>
</StyledMainContent>
<Routes>
<Route
Expand Down
Original file line number Diff line number Diff line change
@@ -1,78 +1,83 @@
import { Box, styled } from '@mui/material';
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { FeatureOverviewSidePanelEnvironmentSwitches } from './FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitches';
import { Sticky } from 'component/common/Sticky/Sticky';
import {
type ITab,
VerticalTabs,
} from 'component/common/VerticalTabs/VerticalTabs';
import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon';
import { useEffect } from 'react';

const StyledContainer = styled(Box)(({ theme }) => ({
top: theme.spacing(2),
margin: theme.spacing(2),
marginLeft: 0,
padding: theme.spacing(3),
borderRadius: theme.shape.borderRadiusLarge,
backgroundColor: theme.palette.background.paper,
display: 'flex',
flexDirection: 'column',
maxWidth: '350px',
minWidth: '350px',
marginRight: '1rem',
marginTop: '1rem',
gap: theme.spacing(2),
width: '350px',
[theme.breakpoints.down(1000)]: {
marginBottom: '1rem',
width: '100%',
maxWidth: 'none',
minWidth: 'auto',
},
}));

const StyledHeader = styled('h3')(({ theme }) => ({
display: 'flex',
gap: theme.spacing(1),
alignItems: 'center',
fontSize: theme.fontSizes.bodySize,
margin: 0,
marginBottom: theme.spacing(3),
marginBottom: theme.spacing(1),
}));

// Make the help icon align with the text.
'& > :last-child': {
position: 'relative',
top: 1,
const StyledVerticalTabs = styled(VerticalTabs)(({ theme }) => ({
'&&& .selected': {
backgroundColor: theme.palette.secondary.light,
},
}));

interface IFeatureOverviewSidePanelProps {
hiddenEnvironments: Set<String>;
setHiddenEnvironments: (environment: string) => void;
environmentId: string;
setEnvironmentId: React.Dispatch<React.SetStateAction<string>>;
}

export const FeatureOverviewSidePanel = ({
hiddenEnvironments,
setHiddenEnvironments,
environmentId,
setEnvironmentId,
}: IFeatureOverviewSidePanelProps) => {
const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId');
const { feature } = useFeature(projectId, featureId);
const isSticky = feature.environments?.length <= 3;

const tabs: ITab[] = feature.environments.map(
({ name, enabled, strategies }) => ({
id: name,
label: name,
description:
strategies.length === 1
? '1 strategy'
: `${strategies.length || 'No'} strategies`,
startIcon: <EnvironmentIcon enabled={enabled} />,
}),
);

useEffect(() => {
if (!environmentId) {
setEnvironmentId(tabs[0]?.id);
}
}, [tabs]);

return (
<StyledContainer as={isSticky ? Sticky : Box}>
<FeatureOverviewSidePanelEnvironmentSwitches
header={
<StyledHeader data-loading>
Enabled in environments (
{
feature.environments.filter(
({ enabled }) => enabled,
).length
}
)
<HelpIcon
tooltip='When a feature is switched off in an environment, it will always return false. When switched on, it will return true or false depending on its strategies.'
placement='top'
/>
</StyledHeader>
}
feature={feature}
hiddenEnvironments={hiddenEnvironments}
setHiddenEnvironments={setHiddenEnvironments}
<StyledHeader data-loading>
Environments ({feature.environments.length})
</StyledHeader>
<StyledVerticalTabs
tabs={tabs}
value={environmentId}
onChange={({ id }) => setEnvironmentId(id)}
/>
</StyledContainer>
);
Expand Down
Loading

0 comments on commit 1175828

Please sign in to comment.