mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-31 01:16:01 +02:00
chore: event timeline header placement (#8234)
https://linear.app/unleash/issue/2-2662/make-the-event-timeline-available-globally-through-a-new-header-button https://github.com/user-attachments/assets/bde38ee8-cdd8-409d-a95e-0c06189e3d9b (In the video, you’ll notice a slight delay before new events show up. This happens because the timeline automatically refreshes every 10 seconds) Removes the event timeline from the event log and integrates it into a new header option. I chose a middle-ground approach between options 1 and 2 from our Figma sketches. This solution provides the best of both worlds IMO: the timeline stands out as a global component, distinct from the current page context, while sliding in rather than overlapping the content. This way, users can view the timeline alongside the page content.
This commit is contained in:
parent
54432f3f31
commit
5063e151ed
@ -1,4 +1,4 @@
|
||||
import { Switch, FormControlLabel, useMediaQuery, Box } from '@mui/material';
|
||||
import { Switch, FormControlLabel, useMediaQuery } from '@mui/material';
|
||||
import EventJson from 'component/events/EventJson/EventJson';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
@ -9,13 +9,11 @@ import theme from 'themes/theme';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { styled } from '@mui/system';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { EventLogFilters } from './EventLogFilters';
|
||||
import { useEventLogSearch } from './useEventLogSearch';
|
||||
import { StickyPaginationBar } from 'component/common/Table/StickyPaginationBar/StickyPaginationBar';
|
||||
import { EventActions } from './EventActions';
|
||||
import useLoading from 'hooks/useLoading';
|
||||
import { EventTimeline } from '../EventTimeline/EventTimeline';
|
||||
|
||||
interface IEventLogProps {
|
||||
title: string;
|
||||
@ -51,8 +49,7 @@ const Placeholder = styled('li')({
|
||||
});
|
||||
|
||||
export const EventLog = ({ title, project, feature }: IEventLogProps) => {
|
||||
const { isOss, isEnterprise } = useUiConfig();
|
||||
const eventTimeline = useUiFlag('eventTimeline') && !isOss();
|
||||
const { isEnterprise } = useUiConfig();
|
||||
const showFilters = isEnterprise();
|
||||
const {
|
||||
events,
|
||||
@ -134,21 +131,6 @@ export const EventLog = ({ title, project, feature }: IEventLogProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConditionallyRender
|
||||
condition={eventTimeline}
|
||||
show={
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
})}
|
||||
>
|
||||
<EventTimeline />
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
<PageContent
|
||||
bodyClass={'no-padding'}
|
||||
header={
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { forwardRef, type ReactNode } from 'react';
|
||||
import { forwardRef, useState, type ReactNode } from 'react';
|
||||
import { Box, Grid, styled, useMediaQuery, useTheme } from '@mui/material';
|
||||
import Header from 'component/menu/Header/Header';
|
||||
import OldHeader from 'component/menu/Header/OldHeader';
|
||||
@ -17,6 +17,8 @@ import { DraftBanner } from './DraftBanner/DraftBanner';
|
||||
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
||||
import { NavigationSidebar } from './NavigationSidebar/NavigationSidebar';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { EventTimeline } from 'component/events/EventTimeline/EventTimeline';
|
||||
import AnimateOnMount from 'component/common/AnimateOnMount/AnimateOnMount';
|
||||
|
||||
interface IMainLayoutProps {
|
||||
children: ReactNode;
|
||||
@ -61,7 +63,6 @@ const OldMainLayoutContent = styled(Grid)(({ theme }) => ({
|
||||
|
||||
const NewMainLayoutContent = styled(Grid)(({ theme }) => ({
|
||||
minWidth: 0, // this is a fix for overflowing flex
|
||||
width: '100%',
|
||||
maxWidth: '1512px',
|
||||
margin: '0 auto',
|
||||
paddingLeft: theme.spacing(2),
|
||||
@ -106,6 +107,20 @@ const MainLayoutContentContainer = styled('div')(({ theme }) => ({
|
||||
zIndex: 200,
|
||||
}));
|
||||
|
||||
const timelineAnimations = {
|
||||
start: {
|
||||
maxHeight: 0,
|
||||
overflow: 'hidden',
|
||||
transition: 'max-height 0.3s ease-in-out',
|
||||
},
|
||||
enter: {
|
||||
maxHeight: '105px',
|
||||
},
|
||||
leave: {
|
||||
maxHeight: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
({ children }, ref) => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
@ -113,6 +128,8 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled(
|
||||
projectId || '',
|
||||
);
|
||||
const eventTimeline = useUiFlag('eventTimeline');
|
||||
const [showTimeline, setShowTimeline] = useState(false);
|
||||
|
||||
const sidebarNavigationEnabled = useUiFlag('navigationSidebar');
|
||||
const StyledMainLayoutContent = sidebarNavigationEnabled
|
||||
@ -126,8 +143,18 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
<SkipNavLink />
|
||||
<ConditionallyRender
|
||||
condition={sidebarNavigationEnabled}
|
||||
show={<Header />}
|
||||
elseShow={<OldHeader />}
|
||||
show={
|
||||
<Header
|
||||
showTimeline={showTimeline}
|
||||
setShowTimeline={setShowTimeline}
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<OldHeader
|
||||
showTimeline={showTimeline}
|
||||
setShowTimeline={setShowTimeline}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SkipNavTarget />
|
||||
@ -154,18 +181,39 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
show={<NavigationSidebar />}
|
||||
/>
|
||||
|
||||
<StyledMainLayoutContent
|
||||
item
|
||||
xs={12}
|
||||
sm={12}
|
||||
my={2}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
<MainLayoutContentContainer ref={ref}>
|
||||
<BreadcrumbNav />
|
||||
<Proclamation toast={uiConfig.toast} />
|
||||
{children}
|
||||
</MainLayoutContentContainer>
|
||||
</StyledMainLayoutContent>
|
||||
<AnimateOnMount
|
||||
mounted={eventTimeline && showTimeline}
|
||||
start={timelineAnimations.start}
|
||||
enter={timelineAnimations.enter}
|
||||
leave={timelineAnimations.leave}
|
||||
>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
padding: theme.spacing(2),
|
||||
backgroundColor:
|
||||
theme.palette.background.paper,
|
||||
})}
|
||||
>
|
||||
<EventTimeline />
|
||||
</Box>
|
||||
</AnimateOnMount>
|
||||
|
||||
<StyledMainLayoutContent>
|
||||
<MainLayoutContentContainer ref={ref}>
|
||||
<BreadcrumbNav />
|
||||
<Proclamation toast={uiConfig.toast} />
|
||||
{children}
|
||||
</MainLayoutContentContainer>
|
||||
</StyledMainLayoutContent>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<ThemeMode
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useState, type VFC } from 'react';
|
||||
import { useState } from 'react';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -33,6 +33,7 @@ import { useAdminRoutes } from 'component/admin/useAdminRoutes';
|
||||
import InviteLinkButton from './InviteLink/InviteLinkButton/InviteLinkButton';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { CommandBar } from 'component/commandBar/CommandBar';
|
||||
import TimelineIcon from '@mui/icons-material/Timeline';
|
||||
|
||||
const HeaderComponent = styled(AppBar)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
@ -96,7 +97,12 @@ const StyledIconButton = styled(IconButton)<{
|
||||
},
|
||||
}));
|
||||
|
||||
const Header: VFC = () => {
|
||||
interface IHeaderProps {
|
||||
showTimeline: boolean;
|
||||
setShowTimeline: (show: boolean) => void;
|
||||
}
|
||||
|
||||
const Header = ({ showTimeline, setShowTimeline }: IHeaderProps) => {
|
||||
const { onSetThemeMode, themeMode } = useThemeMode();
|
||||
const theme = useTheme();
|
||||
|
||||
@ -106,6 +112,7 @@ const Header: VFC = () => {
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const toggleDrawer = () => setOpenDrawer((prev) => !prev);
|
||||
const celebatoryUnleash = useUiFlag('celebrateUnleash');
|
||||
const eventTimeline = useUiFlag('eventTimeline') && !isOss();
|
||||
|
||||
const routes = getRoutes();
|
||||
const adminRoutes = useAdminRoutes();
|
||||
@ -180,6 +187,28 @@ const Header: VFC = () => {
|
||||
<StyledNav>
|
||||
<StyledUserContainer>
|
||||
<CommandBar />
|
||||
<ConditionallyRender
|
||||
condition={eventTimeline}
|
||||
show={
|
||||
<Tooltip
|
||||
title={
|
||||
showTimeline
|
||||
? 'Hide timeline'
|
||||
: 'Show timeline'
|
||||
}
|
||||
arrow
|
||||
>
|
||||
<StyledIconButton
|
||||
onClick={() =>
|
||||
setShowTimeline(!showTimeline)
|
||||
}
|
||||
size='large'
|
||||
>
|
||||
<TimelineIcon />
|
||||
</StyledIconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<InviteLinkButton />
|
||||
<Tooltip
|
||||
title={
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useState, type VFC } from 'react';
|
||||
import { useState } from 'react';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -36,6 +36,7 @@ import { Notifications } from 'component/common/Notifications/Notifications';
|
||||
import { useAdminRoutes } from 'component/admin/useAdminRoutes';
|
||||
import InviteLinkButton from './InviteLink/InviteLinkButton/InviteLinkButton';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import TimelineIcon from '@mui/icons-material/Timeline';
|
||||
|
||||
const HeaderComponent = styled(AppBar)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
@ -130,7 +131,12 @@ const StyledIconButton = styled(IconButton)<{
|
||||
},
|
||||
}));
|
||||
|
||||
const OldHeader: VFC = () => {
|
||||
interface IOldHeaderProps {
|
||||
showTimeline: boolean;
|
||||
setShowTimeline: (show: boolean) => void;
|
||||
}
|
||||
|
||||
const OldHeader = ({ showTimeline, setShowTimeline }: IOldHeaderProps) => {
|
||||
const { onSetThemeMode, themeMode } = useThemeMode();
|
||||
const theme = useTheme();
|
||||
const adminId = useId();
|
||||
@ -146,6 +152,7 @@ const OldHeader: VFC = () => {
|
||||
const onAdminClose = () => setAdminRef(null);
|
||||
const onConfigureClose = () => setConfigRef(null);
|
||||
const celebatoryUnleash = useUiFlag('celebrateUnleash');
|
||||
const eventTimeline = useUiFlag('eventTimeline') && !isOss();
|
||||
|
||||
const routes = getRoutes();
|
||||
const adminRoutes = useAdminRoutes();
|
||||
@ -245,6 +252,28 @@ const OldHeader: VFC = () => {
|
||||
/>
|
||||
</StyledLinks>
|
||||
<StyledUserContainer>
|
||||
<ConditionallyRender
|
||||
condition={eventTimeline}
|
||||
show={
|
||||
<Tooltip
|
||||
title={
|
||||
showTimeline
|
||||
? 'Hide timeline'
|
||||
: 'Show timeline'
|
||||
}
|
||||
arrow
|
||||
>
|
||||
<StyledIconButton
|
||||
onClick={() =>
|
||||
setShowTimeline(!showTimeline)
|
||||
}
|
||||
size='large'
|
||||
>
|
||||
<TimelineIcon />
|
||||
</StyledIconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<InviteLinkButton />
|
||||
<Tooltip
|
||||
title={
|
||||
|
Loading…
Reference in New Issue
Block a user