mirror of
https://github.com/suitenumerique/docs.git
synced 2026-10-01 05:55:16 +02:00
📱(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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user