1
0
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:
Nuno Góis 2024-09-24 13:43:30 +01:00 committed by GitHub
parent 54432f3f31
commit 5063e151ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 127 additions and 39 deletions

View File

@ -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={

View File

@ -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

View File

@ -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={

View File

@ -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={