📱(frontend) make left panel responsive

We modify the left panel so that when the value changes in the
useResponsiveStore it is displayed on the screen
This commit is contained in:
Nathan Panchout
2024-11-14 08:59:44 +01:00
parent 61c093cb54
commit 8268b26d7a
5 changed files with 118 additions and 6 deletions
@@ -7,6 +7,7 @@ test.describe('Left panel desktop', () => {
test('checks all the elements are visible', async ({ page }) => {
await expect(page.getByTestId('left-panel-desktop')).toBeVisible();
await expect(page.getByTestId('left-panel-mobile')).toBeHidden();
await expect(page.getByRole('button', { name: 'house' })).toBeVisible();
await expect(page.getByRole('button', { name: 'New doc' })).toBeVisible();
});
@@ -19,7 +20,29 @@ test.describe('Left panel mobile', () => {
await page.goto('/');
});
test('checks all the desktop elements are hidden', async ({ page }) => {
test('checks all the desktop elements are hidden and all mobile elements are visible', async ({
page,
}) => {
await expect(page.getByTestId('left-panel-desktop')).toBeHidden();
await expect(page.getByTestId('left-panel-mobile')).not.toBeInViewport();
const header = page.locator('header').first();
const homeButton = page.getByRole('button', { name: 'house' });
const newDocButton = page.getByRole('button', { name: 'New doc' });
const languageButton = page.getByRole('combobox', { name: 'Language' });
const logoutButton = page.getByRole('button', { name: 'Logout' });
await expect(homeButton).not.toBeInViewport();
await expect(newDocButton).not.toBeInViewport();
await expect(languageButton).not.toBeInViewport();
await expect(logoutButton).not.toBeInViewport();
await header.getByLabel('Open the header menu').click();
await expect(page.getByTestId('left-panel-mobile')).toBeInViewport();
await expect(homeButton).toBeInViewport();
await expect(newDocButton).toBeInViewport();
await expect(languageButton).toBeInViewport();
await expect(logoutButton).toBeInViewport();
});
});
@@ -0,0 +1,34 @@
import { useCunninghamTheme } from '@/cunningham';
import { Box } from '../Box';
export enum SeparatorVariant {
LIGHT = 'light',
DARK = 'dark',
}
type Props = {
variant?: SeparatorVariant;
};
export const HorizontalSeparator = ({
variant = SeparatorVariant.LIGHT,
}: Props) => {
const { colorsTokens, themeTokens } = useCunninghamTheme();
const colors = colorsTokens();
const spacings = themeTokens().spacings;
return (
<Box
$css={`
width: 100%;
height: 1px;
margin: ${spacings?.['300W'] ?? '0.5rem'} 0 ;
background-color: ${
variant === SeparatorVariant.LIGHT
? colors['greyscale-200']
: colors['greyscale-800']
};
`}
/>
);
};
@@ -4,27 +4,33 @@ import { PropsWithChildren, ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, Icon } from '@/components';
import { HorizontalSeparator } from '@/components/separators/HorizontalSeparator';
import { SeparatedSection } from '@/components/separators/SeparatedSection';
import { ButtonLogin } from '@/core';
import { useCunninghamTheme } from '@/cunningham';
import { useCreateDoc } from '@/features/docs';
import { HEADER_HEIGHT } from '@/features/header/conf';
import { LanguagePicker } from '@/features/language';
import { useResponsiveStore } from '@/stores';
export const LeftPanel = ({ children }: PropsWithChildren) => {
const { t } = useTranslation();
const router = useRouter();
const { isResponsive } = useResponsiveStore();
const { isResponsive, isMobileMenuOpen, toggleMobileMenu } =
useResponsiveStore();
const theme = useCunninghamTheme();
const colors = theme.colorsTokens();
const { mutate: createDoc } = useCreateDoc({
onSuccess: (doc) => {
router.push(`/docs/${doc.id}`);
toggleMobileMenu();
},
});
const goToHome = () => {
router.push('/');
toggleMobileMenu();
};
const createNewDoc = () => {
@@ -72,6 +78,37 @@ export const LeftPanel = ({ children }: PropsWithChildren) => {
{getContent()}
</Box>
)}
{isResponsive && (
<Box
data-testid="left-panel-mobile"
$css={`
z-index: 1000;
width: 100dvw;
height: calc(100dvh - ${HEADER_HEIGHT}px);
position: fixed;
transition: 0.15s;
background-color: ${(colors?.['greyscale-000'] as string) ?? '#fff'};
left: ${isMobileMenuOpen ? '0' : '-100dvw'};
`}
>
{getContent()}
<Box
$css={`
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: var(--c--theme--spacings--200W);
`}
>
{children && <HorizontalSeparator />}
<ButtonLogin />
<LanguagePicker />
</Box>
</Box>
)}
</>
);
};
@@ -1,10 +1,11 @@
import { PropsWithChildren } from 'react';
import { PropsWithChildren, ReactNode } from 'react';
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Header } from '@/features/header';
import { HEADER_HEIGHT } from '@/features/header/conf';
import { LeftPanel } from '@/features/left-pannel/components/LeftPanel';
import { useResponsiveStore } from '@/stores';
export enum MainLayoutBackgroundColor {
WHITE = 'white',
@@ -13,12 +14,15 @@ export enum MainLayoutBackgroundColor {
type MainLayoutProps = {
backgroundColor?: MainLayoutBackgroundColor;
leftPanelContent?: ReactNode;
};
export function MainLayout({
children,
backgroundColor = MainLayoutBackgroundColor.WHITE,
leftPanelContent,
}: PropsWithChildren<MainLayoutProps>) {
const { isResponsive } = useResponsiveStore();
const { themeTokens, colorsTokens } = useCunninghamTheme();
const tokens = themeTokens();
const colors = colorsTokens();
@@ -27,17 +31,24 @@ export function MainLayout({
<div>
<Header />
<Box $direction="row" $width="100%">
<LeftPanel />
<LeftPanel>{leftPanelContent}</LeftPanel>
<Box
as="main"
id="mainContent"
$padding={{
vertical: !isResponsive
? ((tokens.spacings?.['200W'] as string) ?? '1.12rem')
: ((tokens.spacings?.['100V'] as string) ?? '0.5rem'),
horizontal: !isResponsive
? ((tokens.spacings?.['1200W'] as string) ?? '5.62rem')
: ((tokens.spacings?.['100V'] as string) ?? '0.5rem'),
}}
$css={`
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
flex: 1;
padding: ${tokens.spacings?.['200W'] ?? '1.12rem'} ${tokens.spacings?.['1200W'] ?? '5.62rem'};
height: calc(100dvh - ${HEADER_HEIGHT}px);
overflow-y: scroll;
background-color: ${backgroundColor === MainLayoutBackgroundColor.WHITE ? colors['greyscale-000'] : colors['greyscale-050']};
@@ -5,19 +5,22 @@ export type ScreenSize = 'small-mobile' | 'mobile' | 'tablet' | 'desktop';
export interface UseResponsiveStore {
isMobile: boolean;
isTablet: boolean;
isMobileMenuOpen: boolean;
isSmallMobile: boolean;
screenSize: ScreenSize;
screenWidth: number;
setScreenSize: (size: ScreenSize) => void;
toggleMobileMenu: () => void;
isResponsive: boolean;
initializeResizeListener: () => () => void;
}
const initialState = {
isMobile: false,
isSmallMobile: false,
isTablet: false,
isSmallMobile: false,
isResponsive: false,
isMobileMenuOpen: false,
screenSize: 'desktop' as ScreenSize,
screenWidth: 0,
};
@@ -27,9 +30,13 @@ export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
isTablet: initialState.isTablet,
isSmallMobile: initialState.isSmallMobile,
screenSize: initialState.screenSize,
isMobileMenuOpen: initialState.isMobileMenuOpen,
screenWidth: initialState.screenWidth,
setScreenSize: (size: ScreenSize) => set(() => ({ screenSize: size })),
isResponsive: initialState.isResponsive,
toggleMobileMenu: () => {
set((old) => ({ isMobileMenuOpen: !old.isMobileMenuOpen }));
},
initializeResizeListener: () => {
const resizeHandler = () => {
const width = window.innerWidth;