1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-09 01:17:06 +02:00

feat: transparent header (#9108)

This PR adds header redesign behind a feature flag
This commit is contained in:
Fredrik Strand Oseberg 2025-01-17 09:45:45 +01:00 committed by GitHub
parent 4b3b98f263
commit 25c09c3627
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 172 additions and 50 deletions

View File

@ -30,6 +30,7 @@ import { CommandQuickSuggestions } from './CommandQuickSuggestions';
import { CommandSearchPages } from './CommandSearchPages'; import { CommandSearchPages } from './CommandSearchPages';
import { CommandBarFeedback } from './CommandBarFeedback'; import { CommandBarFeedback } from './CommandBarFeedback';
import { RecentlyVisitedRecorder } from './RecentlyVisitedRecorder'; import { RecentlyVisitedRecorder } from './RecentlyVisitedRecorder';
import { useUiFlag } from 'hooks/useUiFlag';
export const CommandResultsPaper = styled(Paper)(({ theme }) => ({ export const CommandResultsPaper = styled(Paper)(({ theme }) => ({
position: 'absolute', position: 'absolute',
@ -50,16 +51,20 @@ export const CommandResultsPaper = styled(Paper)(({ theme }) => ({
})); }));
const StyledContainer = styled('div', { const StyledContainer = styled('div', {
shouldForwardProp: (prop) => prop !== 'active', shouldForwardProp: (prop) =>
prop !== 'active' && prop !== 'frontendHeaderRedesign',
})<{ })<{
active: boolean | undefined; active: boolean | undefined;
}>(({ theme, active }) => ({ frontendHeaderRedesign?: boolean;
}>(({ theme, active, frontendHeaderRedesign }) => ({
border: `1px solid transparent`, border: `1px solid transparent`,
display: 'flex', display: 'flex',
flexGrow: 1, flexGrow: 1,
alignItems: 'center', alignItems: 'center',
position: 'relative', position: 'relative',
backgroundColor: theme.palette.background.paper, backgroundColor: frontendHeaderRedesign
? theme.palette.background.application
: theme.palette.background.paper,
maxWidth: active ? '100%' : '400px', maxWidth: active ? '100%' : '400px',
[theme.breakpoints.down('md')]: { [theme.breakpoints.down('md')]: {
marginTop: theme.spacing(1), marginTop: theme.spacing(1),
@ -97,6 +102,7 @@ interface IPageRouteInfo {
export const CommandBar = () => { export const CommandBar = () => {
const { trackEvent } = usePlausibleTracker(); const { trackEvent } = usePlausibleTracker();
const frontendHeaderRedesign = useUiFlag('frontendHeaderRedesign');
const searchInputRef = useRef<HTMLInputElement>(null); const searchInputRef = useRef<HTMLInputElement>(null);
const searchContainerRef = useRef<HTMLInputElement>(null); const searchContainerRef = useRef<HTMLInputElement>(null);
const [showSuggestions, setShowSuggestions] = useState(false); const [showSuggestions, setShowSuggestions] = useState(false);
@ -293,7 +299,11 @@ export const CommandBar = () => {
}; };
return ( return (
<StyledContainer ref={searchContainerRef} active={showSuggestions}> <StyledContainer
ref={searchContainerRef}
active={showSuggestions}
frontendHeaderRedesign={frontendHeaderRedesign}
>
<RecentlyVisitedRecorder /> <RecentlyVisitedRecorder />
<StyledSearch <StyledSearch
sx={{ sx={{

View File

@ -18,6 +18,7 @@ import { NavigationSidebar } from './NavigationSidebar/NavigationSidebar';
import { MainLayoutEventTimeline } from './MainLayoutEventTimeline'; import { MainLayoutEventTimeline } from './MainLayoutEventTimeline';
import { EventTimelineProvider } from 'component/events/EventTimeline/EventTimelineProvider'; import { EventTimelineProvider } from 'component/events/EventTimeline/EventTimelineProvider';
import { NewInUnleash } from './NavigationSidebar/NewInUnleash/NewInUnleash'; import { NewInUnleash } from './NavigationSidebar/NewInUnleash/NewInUnleash';
import { useUiFlag } from 'hooks/useUiFlag';
interface IMainLayoutProps { interface IMainLayoutProps {
children: ReactNode; children: ReactNode;
@ -93,6 +94,7 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
({ children }, ref) => { ({ children }, ref) => {
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const projectId = useOptionalPathParam('projectId'); const projectId = useOptionalPathParam('projectId');
const frontendHeaderRedesign = useUiFlag('frontendHeaderRedesign');
const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled( const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled(
projectId || '', projectId || '',
); );
@ -103,7 +105,10 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
return ( return (
<EventTimelineProvider> <EventTimelineProvider>
<SkipNavLink /> <SkipNavLink />
<Header /> <ConditionallyRender
condition={!frontendHeaderRedesign}
show={<Header />}
/>
<SkipNavTarget /> <SkipNavTarget />
<MainLayoutContainer> <MainLayoutContainer>
@ -119,7 +124,9 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
<Box <Box
sx={(theme) => ({ sx={(theme) => ({
display: 'flex', display: 'flex',
mt: theme.spacing(0.25), mt: frontendHeaderRedesign
? 0
: theme.spacing(0.25),
})} })}
> >
<ConditionallyRender <ConditionallyRender
@ -139,6 +146,11 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
minWidth: 0, minWidth: 0,
}} }}
> >
<ConditionallyRender
condition={frontendHeaderRedesign}
show={<Header />}
/>
<MainLayoutEventTimeline /> <MainLayoutEventTimeline />
<MainLayoutContent> <MainLayoutContent>

View File

@ -16,6 +16,17 @@ import { useInitialPathname } from './useInitialPathname';
import { useLastViewedProject } from 'hooks/useLastViewedProject'; import { useLastViewedProject } from 'hooks/useLastViewedProject';
import { useLastViewedFlags } from 'hooks/useLastViewedFlags'; import { useLastViewedFlags } from 'hooks/useLastViewedFlags';
import type { NewInUnleash } from './NewInUnleash/NewInUnleash'; import type { NewInUnleash } from './NewInUnleash/NewInUnleash';
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { flexRow, focusable } from 'themes/themeStyles';
import { ReactComponent as UnleashLogo } from 'assets/img/logoDarkWithText.svg';
import { ReactComponent as UnleashLogoWhite } from 'assets/img/logoWithWhiteText.svg';
import { ReactComponent as CelebatoryUnleashLogo } from 'assets/img/unleashHoliday.svg';
import { ReactComponent as CelebatoryUnleashLogoWhite } from 'assets/img/unleashHolidayDark.svg';
import { ReactComponent as LogoOnlyWhite } from 'assets/img/logo.svg';
import { ReactComponent as LogoOnly } from 'assets/img/logoDark.svg';
import { useUiFlag } from 'hooks/useUiFlag';
import { Link } from 'react-router-dom';
export const MobileNavigationSidebar: FC<{ export const MobileNavigationSidebar: FC<{
onClick: () => void; onClick: () => void;
@ -57,6 +68,26 @@ export const StretchContainer = styled(Box)<{ mode: string }>(
}), }),
); );
const StyledLink = styled(Link)(({ theme }) => focusable(theme));
const StyledUnleashLogoWhite = styled(UnleashLogoWhite)({ width: '150px' });
const StyledUnleashLogo = styled(UnleashLogo)({ width: '150px' });
const StyledCelebatoryLogo = styled(CelebatoryUnleashLogo)({ width: '150px' });
const StyledUnleashLogoOnly = styled(LogoOnly)(({ theme }) => ({
width: '58px',
marginTop: theme.spacing(0.5),
margin: '0 auto',
}));
const StyledUnleashLogoOnlyWhite = styled(LogoOnlyWhite)(({ theme }) => ({
width: '37px',
marginTop: theme.spacing(1),
margin: '0 auto',
}));
// This component is needed when the sticky item could overlap with nav items. You can replicate it on a short screen. // This component is needed when the sticky item could overlap with nav items. You can replicate it on a short screen.
const StickyContainer = styled(Box)(({ theme }) => ({ const StickyContainer = styled(Box)(({ theme }) => ({
position: 'sticky', position: 'sticky',
@ -71,6 +102,8 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
NewInUnleash, NewInUnleash,
}) => { }) => {
const { routes } = useRoutes(); const { routes } = useRoutes();
const celebatoryUnleash = useUiFlag('celebrateUnleash');
const frontendHeaderRedesign = useUiFlag('frontendHeaderRedesign');
const [mode, setMode] = useNavigationMode(); const [mode, setMode] = useNavigationMode();
const [expanded, changeExpanded] = useExpanded<'configure' | 'admin'>(); const [expanded, changeExpanded] = useExpanded<'configure' | 'admin'>();
@ -90,6 +123,49 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
return ( return (
<StretchContainer mode={mode}> <StretchContainer mode={mode}>
<ConditionallyRender
condition={frontendHeaderRedesign}
show={
<ConditionallyRender
condition={mode === 'full'}
show={
<StyledLink to='/' sx={flexRow} aria-label='Home'>
<ThemeMode
darkmode={
<ConditionallyRender
condition={celebatoryUnleash}
show={
<CelebatoryUnleashLogoWhite />
}
elseShow={
<StyledUnleashLogoWhite aria-label='Unleash logo' />
}
/>
}
lightmode={
<ConditionallyRender
condition={celebatoryUnleash}
show={<StyledCelebatoryLogo />}
elseShow={
<StyledUnleashLogo aria-label='Unleash logo' />
}
/>
}
/>
</StyledLink>
}
elseShow={
<StyledLink to='/' sx={flexRow} aria-label='Home'>
<ThemeMode
darkmode={<StyledUnleashLogoOnlyWhite />}
lightmode={<StyledUnleashLogoOnly />}
/>
</StyledLink>
}
/>
}
/>
<PrimaryNavigationList <PrimaryNavigationList
mode={mode} mode={mode}
onClick={setActiveItem} onClick={setActiveItem}

View File

@ -35,8 +35,13 @@ import { useUiFlag } from 'hooks/useUiFlag';
import { CommandBar } from 'component/commandBar/CommandBar'; import { CommandBar } from 'component/commandBar/CommandBar';
import { HeaderEventTimelineButton } from './HeaderEventTimelineButton'; import { HeaderEventTimelineButton } from './HeaderEventTimelineButton';
const HeaderComponent = styled(AppBar)(({ theme }) => ({ const HeaderComponent = styled(AppBar, {
backgroundColor: theme.palette.background.paper, shouldForwardProp: (prop) => prop !== 'frontendHeaderRedesign',
})<{ frontendHeaderRedesign?: boolean }>(
({ theme, frontendHeaderRedesign }) => ({
backgroundColor: frontendHeaderRedesign
? theme.palette.background.application
: theme.palette.background.paper,
padding: theme.spacing(1), padding: theme.spacing(1),
boxShadow: 'none', boxShadow: 'none',
position: 'relative', position: 'relative',
@ -54,7 +59,8 @@ const HeaderComponent = styled(AppBar)(({ theme }) => ({
minWidth: '100%', minWidth: '100%',
}, },
margin: '0 auto', margin: '0 auto',
})); }),
);
const ContainerComponent = styled(Box)(() => ({ const ContainerComponent = styled(Box)(() => ({
display: 'flex', display: 'flex',
@ -107,6 +113,7 @@ const Header = () => {
const [openDrawer, setOpenDrawer] = useState(false); const [openDrawer, setOpenDrawer] = useState(false);
const toggleDrawer = () => setOpenDrawer((prev) => !prev); const toggleDrawer = () => setOpenDrawer((prev) => !prev);
const celebatoryUnleash = useUiFlag('celebrateUnleash'); const celebatoryUnleash = useUiFlag('celebrateUnleash');
const frontendHeaderRedesign = useUiFlag('frontendHeaderRedesign');
const routes = getRoutes(); const routes = getRoutes();
const adminRoutes = useAdminRoutes(); const adminRoutes = useAdminRoutes();
@ -123,7 +130,10 @@ const Header = () => {
if (smallScreen) { if (smallScreen) {
return ( return (
<HeaderComponent position='static'> <HeaderComponent
position='static'
frontendHeaderRedesign={frontendHeaderRedesign}
>
<ContainerComponent> <ContainerComponent>
<Tooltip title='Menu' arrow> <Tooltip title='Menu' arrow>
<IconButton <IconButton
@ -153,8 +163,14 @@ const Header = () => {
} }
return ( return (
<HeaderComponent position='static'> <HeaderComponent
frontendHeaderRedesign={frontendHeaderRedesign}
position='static'
>
<ContainerComponent> <ContainerComponent>
<ConditionallyRender
condition={!frontendHeaderRedesign}
show={
<StyledLink to='/' sx={flexRow} aria-label='Home'> <StyledLink to='/' sx={flexRow} aria-label='Home'>
<ThemeMode <ThemeMode
darkmode={ darkmode={
@ -177,6 +193,8 @@ const Header = () => {
} }
/> />
</StyledLink> </StyledLink>
}
/>
<StyledNav> <StyledNav>
<StyledUserContainer> <StyledUserContainer>

View File

@ -92,6 +92,7 @@ export type UiFlags = {
granularAdminPermissions?: boolean; granularAdminPermissions?: boolean;
sortProjectRoles?: boolean; sortProjectRoles?: boolean;
lifecycleImprovements?: boolean; lifecycleImprovements?: boolean;
frontendHeaderRedesign?: boolean;
}; };
export interface IVersionInfo { export interface IVersionInfo {

View File

@ -62,7 +62,8 @@ export type IFlagKey =
| 'deltaApi' | 'deltaApi'
| 'uniqueSdkTracking' | 'uniqueSdkTracking'
| 'sortProjectRoles' | 'sortProjectRoles'
| 'lifecycleImprovements'; | 'lifecycleImprovements'
| 'frontendHeaderRedesign';
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
@ -295,6 +296,10 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_LIFECYCLE_IMPROVEMENTS, process.env.UNLEASH_EXPERIMENTAL_LIFECYCLE_IMPROVEMENTS,
false, false,
), ),
frontendHeaderRedesign: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_FRONTEND_HEADER_REDESIGN,
false,
),
}; };
export const defaultExperimentalOptions: IExperimentalOptions = { export const defaultExperimentalOptions: IExperimentalOptions = {

View File

@ -40,7 +40,6 @@ process.nextTick(async () => {
embedProxyFrontend: true, embedProxyFrontend: true,
anonymiseEventLog: false, anonymiseEventLog: false,
responseTimeWithAppNameKillSwitch: false, responseTimeWithAppNameKillSwitch: false,
celebrateUnleash: true,
userAccessUIEnabled: true, userAccessUIEnabled: true,
outdatedSdksBanner: true, outdatedSdksBanner: true,
disableShowContextFieldSelectionValues: false, disableShowContextFieldSelectionValues: false,
@ -58,6 +57,7 @@ process.nextTick(async () => {
deltaApi: true, deltaApi: true,
uniqueSdkTracking: true, uniqueSdkTracking: true,
lifecycleImprovements: true, lifecycleImprovements: true,
frontendHeaderRedesign: true,
}, },
}, },
authentication: { authentication: {