style(FE/layout): Fix app scroll behaviour and sidenav footer section inconsistency (#3426)

This commit is contained in:
Kanishka Chowdhury 2023-09-06 13:00:20 +05:30 committed by GitHub
parent 32a55f3c4f
commit 9aa72f847c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 151 additions and 162 deletions

View File

@ -25,7 +25,7 @@ import {
} from 'types/actions/app';
import AppReducer from 'types/reducer/app';
import { ChildrenContainer, Layout } from './styles';
import { ChildrenContainer, Layout, LayoutContent } from './styles';
import { getRouteKey } from './utils';
function AppLayout(props: AppLayoutProps): JSX.Element {
@ -240,12 +240,12 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
{isToDisplayLayout && <Header />}
<Layout>
{isToDisplayLayout && <SideNav />}
<Layout.Content>
<LayoutContent>
<ChildrenContainer>
{isToDisplayLayout && <TopNav />}
{children}
</ChildrenContainer>
</Layout.Content>
</LayoutContent>
</Layout>
</Layout>
);

View File

@ -7,9 +7,14 @@ export const Layout = styled(LayoutComponent)`
position: relative;
min-height: calc(100vh - 4rem);
overflow: hidden;
height: 100%;
}
`;
export const LayoutContent = styled(LayoutComponent.Content)`
overflow-y: auto;
`;
export const ChildrenContainer = styled.div`
margin: 0 1rem;
display: flex;

View File

@ -1,33 +1,30 @@
import { CheckCircleTwoTone, WarningOutlined } from '@ant-design/icons';
import { Menu, MenuProps } from 'antd';
import { MenuProps } from 'antd';
import getLocalStorageKey from 'api/browser/localstorage/get';
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import {
ReactNode,
useCallback,
useLayoutEffect,
useMemo,
useState,
} from 'react';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { SideBarCollapse } from 'store/actions/app';
import { sideBarCollapse } from 'store/actions/app';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
import { routeConfig, styles } from './config';
import { getQueryString } from './helper';
import menus from './menuItems';
import menuItems from './menuItems';
import { MenuItem, SecondaryMenuItemKey } from './sideNav.types';
import { getActiveMenuKeyFromPath } from './sideNav.utils';
import Slack from './Slack';
import {
MenuLabelContainer,
RedDot,
Sider,
SlackButton,
SlackMenuItemContainer,
VersionContainer,
StyledPrimaryMenu,
StyledSecondaryMenu,
StyledText,
} from './styles';
function SideNav(): JSX.Element {
@ -49,7 +46,7 @@ function SideNav(): JSX.Element {
}, []);
useLayoutEffect(() => {
dispatch(SideBarCollapse(collapsed));
dispatch(sideBarCollapse(collapsed));
}, [collapsed, dispatch]);
const onClickHandler = useCallback(
@ -80,90 +77,56 @@ function SideNav(): JSX.Element {
const isNotCurrentVersion = currentVersion !== latestVersion;
const sidebar: SidebarItem[] = [
const secondaryMenuItems: MenuItem[] = [
{
onClick: onClickSlackHandler,
icon: <Slack />,
text: <SlackButton>Support</SlackButton>,
key: 'slack',
},
{
onClick: onClickVersionHandler,
key: 'version',
key: SecondaryMenuItemKey.Version,
icon: isNotCurrentVersion ? (
<WarningOutlined style={{ color: '#E87040' }} />
) : (
<CheckCircleTwoTone twoToneColor={['#D5F2BB', '#1f1f1f']} />
),
text: (
<VersionContainer>
{!isCurrentVersionError ? (
<SlackButton>{currentVersion}</SlackButton>
) : (
<SlackButton>{t('n_a')}</SlackButton>
)}
label: (
<MenuLabelContainer>
<StyledText ellipsis>
{!isCurrentVersionError ? currentVersion : t('n_a')}
</StyledText>
{isNotCurrentVersion && <RedDot />}
</VersionContainer>
</MenuLabelContainer>
),
onClick: onClickVersionHandler,
},
{
key: SecondaryMenuItemKey.Slack,
icon: <Slack />,
label: <StyledText>Support</StyledText>,
onClick: onClickSlackHandler,
},
];
const currentMenu = useMemo(() => {
const routeKeys = Object.keys(ROUTES) as (keyof typeof ROUTES)[];
const currentRouteKey = routeKeys.find((key) => {
const route = ROUTES[key];
return pathname === route;
});
if (!currentRouteKey) return null;
return ROUTES[currentRouteKey];
}, [pathname]);
const sidebarItems = (props: SidebarItem, index: number): SidebarItem => ({
key: `${index}`,
icon: props.icon,
onClick: props.onClick,
label: props.text,
});
const activeMenuKey = useMemo(() => getActiveMenuKeyFromPath(pathname), [
pathname,
]);
return (
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
<Menu
<StyledPrimaryMenu
theme="dark"
defaultSelectedKeys={[ROUTES.APPLICATION]}
selectedKeys={currentMenu ? [currentMenu] : []}
selectedKeys={activeMenuKey ? [activeMenuKey] : []}
mode="vertical"
style={styles}
items={menus}
items={menuItems}
onClick={onClickMenuHandler}
/>
{sidebar.map((props, index) => (
<SlackMenuItemContainer
index={index + 1}
key={`${index + 1}`}
collapsed={collapsed}
>
<Menu
theme="dark"
defaultSelectedKeys={[ROUTES.APPLICATION]}
selectedKeys={currentMenu ? [currentMenu] : []}
mode="inline"
style={styles}
items={[sidebarItems(props, index)]}
/>
</SlackMenuItemContainer>
))}
<StyledSecondaryMenu
theme="dark"
selectedKeys={activeMenuKey ? [activeMenuKey] : []}
mode="vertical"
style={styles}
items={secondaryMenuItems}
/>
</Sider>
);
}
interface SidebarItem {
onClick: VoidFunction;
icon?: ReactNode;
text?: ReactNode;
key: string;
label?: ReactNode;
}
export default SideNav;

View File

@ -1,6 +1,6 @@
interface ISlackProps {
width?: number;
height?: number;
width?: number | string;
height?: number | string;
}
function Slack({ width, height }: ISlackProps): JSX.Element {
return (
@ -47,8 +47,8 @@ function Slack({ width, height }: ISlackProps): JSX.Element {
);
}
Slack.defaultProps = {
width: 28,
height: 28,
width: '1em',
height: '1em',
};
export default Slack;

View File

@ -0,0 +1,3 @@
import SideNav from './SideNav';
export default SideNav;

View File

@ -10,28 +10,11 @@ import {
MenuOutlined,
SettingOutlined,
} from '@ant-design/icons';
import { MenuProps, Space, Typography } from 'antd';
import ROUTES from 'constants/routes';
import { Tags } from './styles';
import { SidebarMenu } from './sideNav.types';
type MenuItem = Required<MenuProps>['items'][number];
export const createLabelWithTags = (
label: string,
tags: string[],
): JSX.Element => (
<Space>
<div>{label}</div>
{tags.map((tag) => (
<Tags key={tag}>
<Typography.Text>{tag}</Typography.Text>
</Tags>
))}
</Space>
);
const menus: SidebarMenu[] = [
const menuItems: SidebarMenu[] = [
{
key: ROUTES.APPLICATION,
label: 'Services',
@ -111,8 +94,11 @@ const menus: SidebarMenu[] = [
},
];
type SidebarMenu = MenuItem & {
tags?: string[];
/** Mapping of some newly added routes and their corresponding active sidebar menu key */
export const NEW_ROUTES_MENU_ITEM_KEY_MAP = {
[ROUTES.TRACES_EXPLORER]: ROUTES.TRACE,
[ROUTES.TRACE_EXPLORER]: ROUTES.TRACE,
[ROUTES.LOGS_EXPLORER]: ROUTES.LOGS,
};
export default menus;
export default menuItems;

View File

@ -0,0 +1,21 @@
import { MenuProps } from 'antd';
import { ReactNode } from 'react';
export type MenuItem = Required<MenuProps>['items'][number];
export type SidebarMenu = MenuItem & {
tags?: string[];
};
export interface SidebarItem {
onClick: VoidFunction;
icon?: ReactNode;
text?: ReactNode;
key: string;
label?: ReactNode;
}
export enum SecondaryMenuItemKey {
Slack = 'slack',
Version = 'version',
}

View File

@ -0,0 +1,11 @@
import { NEW_ROUTES_MENU_ITEM_KEY_MAP } from './menuItems';
export const getActiveMenuKeyFromPath = (pathname: string): string => {
const basePath = pathname?.split('/')?.[1]; // Get the base path, Eg; /dashboard/dc5beb63-589c-46a3-ad4c-1b9ca248ee33 -> dashboard
if (!basePath) return '';
const baseRoute = `/${basePath}`;
return NEW_ROUTES_MENU_ITEM_KEY_MAP[baseRoute] || baseRoute;
};

View File

@ -1,87 +1,68 @@
import { Layout, Tag, Typography } from 'antd';
import { StyledCSS } from 'container/GantChart/Trace/styles';
import styled, { css } from 'styled-components';
import { Layout, Menu, Typography } from 'antd';
import styled from 'styled-components';
const { Sider: SiderComponent } = Layout;
interface LogoProps {
collapsed: boolean;
index: number;
}
export const Sider = styled(SiderComponent)`
&&& {
background: #1f1f1f;
.ant-layout-sider-children {
display: flex;
flex-direction: column;
}
.ant-layout-sider-trigger {
background: #1f1f1f;
}
}
`;
export const SlackButton = styled(Typography)`
&&& {
margin-left: 1rem;
color: white;
}
export const StyledPrimaryMenu = styled(Menu)`
flex: 1;
overflow-y: auto;
`;
export const SlackMenuItemContainer = styled.div<LogoProps>`
position: fixed;
bottom: ${({ index }): string => `${index * 48 + (index + 16)}px`};
background: #262626;
width: ${({ collapsed }): string => (!collapsed ? '200px' : '80px')};
transition: inherit;
background: #1f1f1f;
export const StyledSecondaryMenu = styled(Menu)`
&&& {
li {
${({ collapsed }): StyledCSS =>
collapsed &&
css`
padding-left: 24px;
padding-top: 6px;
`}
:not(.ant-menu-inline-collapsed) > .ant-menu-item {
padding-inline: 48px;
display: flex;
align-items: center;
justify-content: center;
}
svg {
margin-left: ${({ collapsed }): string => (collapsed ? '0' : '24px')};
width: 28px;
height: 28px;
${({ collapsed }): StyledCSS =>
collapsed &&
css`
height: 100%;
margin: 0 auto;
`}
}
.ant-menu-title-content {
margin: 0;
margin-inline-start: 10px;
width: 100%;
}
&.ant-menu-inline-collapsed .ant-menu-title-content {
opacity: 0;
}
}
`;
export const RedDot = styled.div`
width: 12px;
height: 12px;
width: 10px;
height: 10px;
background: #d32029;
border-radius: 50%;
margin-left: 0.5rem;
margin-top: 0.5rem;
`;
export const VersionContainer = styled.div`
&&& {
display: flex;
}
export const MenuLabelContainer = styled.div`
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
`;
export const Tags = styled(Tag)`
&&& {
position: absolute;
top: 0;
border-radius: 0.5rem;
}
export const StyledText = styled(Typography.Text)`
width: 100%;
color: white;
`;

View File

@ -4,6 +4,5 @@ import styled from 'styled-components';
export const Container = styled(Row)`
&&& {
margin-top: 2rem;
min-height: 8vh;
}
`;

View File

@ -0,0 +1,18 @@
import { createGlobalStyle } from 'styled-components';
const GlobalStyles = createGlobalStyle`
#root,
html,
body {
height: 100%;
overflow: hidden;
}
body {
padding: 0;
margin: 0;
box-sizing: border-box;
}
`;
export default GlobalStyles;

View File

@ -57,7 +57,7 @@
rel="stylesheet"
/>
</head>
<body style="margin: 0; padding: 0; box-sizing: border-box">
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>

View File

@ -2,6 +2,7 @@ import './wdyr';
import './ReactI18';
import AppRoutes from 'AppRoutes';
import GlobalStyles from 'globalStyles';
import { ThemeProvider } from 'hooks/useDarkMode';
import { createRoot } from 'react-dom/client';
import { HelmetProvider } from 'react-helmet-async';
@ -33,6 +34,7 @@ if (container) {
<ThemeProvider>
<QueryClientProvider client={queryClient}>
<Provider store={store}>
<GlobalStyles />
<AppRoutes />
</Provider>
{process.env.NODE_ENV === 'development' && (

View File

@ -3,7 +3,7 @@ import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
export const SideBarCollapse = (
export const sideBarCollapse = (
collapseState: boolean,
): ((dispatch: Dispatch<AppActions>) => void) => {
setLocalStorageKey(IS_SIDEBAR_COLLAPSED, `${collapseState}`);