mirror of
https://github.com/suitenumerique/docs.git
synced 2026-08-17 21:25:43 +02:00
📱(frontend) reduce breakpoint mobile view
Most of the application was switching to mobile view at the medium breakpoint. Medium breakpoint let enough place to display most of the application features and it is more user friendly to switch to mobile view at the small breakpoint. We are switching to mobile view at the small breakpoint to give more place to the application features and to be more user friendly.
This commit is contained in:
@@ -84,16 +84,13 @@ test.describe('Left panel desktop', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Left panel mobile', () => {
|
||||
test.use({ viewport: { width: 500, height: 1200 } });
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('checks all the desktop elements are hidden and all mobile elements are visible', async ({
|
||||
test.describe('Left panel responsive', () => {
|
||||
test('checks elements visibility on different screen sizes', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 500, height: 1200 });
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.getByTestId('left-panel-desktop')).toBeHidden();
|
||||
await expect(page.getByTestId('left-panel-mobile')).not.toBeInViewport();
|
||||
|
||||
@@ -120,12 +117,27 @@ test.describe('Left panel mobile', () => {
|
||||
await expect(newDocButton).toBeInViewport();
|
||||
await expect(languageButton).toBeInViewport();
|
||||
await expect(logoutButton).toBeInViewport();
|
||||
|
||||
await header.getByLabel('Close the header menu').click();
|
||||
|
||||
// Tablet size - like in desktop, left panel should be visible
|
||||
await page.setViewportSize({ width: 900, height: 1200 });
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.getByRole('link', { name: 'All docs' })).toBeInViewport();
|
||||
await expect(newDocButton).toBeInViewport();
|
||||
await expect(languageButton).toBeInViewport();
|
||||
await expect(logoutButton).toBeInViewport();
|
||||
await expect(header.getByLabel('Open the header menu')).toBeHidden();
|
||||
});
|
||||
|
||||
test('checks panel closes when clicking on a subdoc', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 500, height: 1200 });
|
||||
await page.goto('/');
|
||||
|
||||
const [docTitle] = await createDoc(
|
||||
page,
|
||||
'mobile-doc-test',
|
||||
@@ -162,12 +174,4 @@ test.describe('Left panel mobile', () => {
|
||||
await verifyDocName(page, docChild);
|
||||
await expect(page.getByTestId('left-panel-mobile')).not.toBeInViewport();
|
||||
});
|
||||
|
||||
test('checks resize handle is not present on mobile', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Verify the resize handle is NOT present on mobile
|
||||
const resizeHandle = page.locator('[data-panel-resize-handle-id]');
|
||||
await expect(resizeHandle).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -110,7 +110,7 @@ const DocTitleEmojiPicker = ({ doc }: DocTitleProps) => {
|
||||
};
|
||||
|
||||
const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
const { t } = useTranslation();
|
||||
const { isTopRoot } = useDocUtils(doc);
|
||||
const { untitledDocument } = useTrans();
|
||||
@@ -227,9 +227,9 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
pointer-events: none;
|
||||
font-style: italic;
|
||||
}
|
||||
font-size: ${isDesktop
|
||||
? css`var(--c--globals--font--sizes--h2)`
|
||||
: css`var(--c--globals--font--sizes--sm)`};
|
||||
font-size: ${isLargeScreen
|
||||
? 'var(--c--globals--font--sizes--h2)'
|
||||
: 'var(--c--globals--font--sizes--sm)'};
|
||||
font-weight: 700;
|
||||
outline: none;
|
||||
`}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useResponsiveStore } from '@/stores';
|
||||
*/
|
||||
export const FloatingBar = () => {
|
||||
const { spacingsTokens } = useCunninghamTheme();
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
|
||||
const FLOATING_STYLES = useMemo(() => {
|
||||
const base = spacingsTokens['base'];
|
||||
@@ -69,7 +69,7 @@ export const FloatingBar = () => {
|
||||
$direction="row"
|
||||
$justify="space-between"
|
||||
>
|
||||
{isDesktop ? <LeftPanelCollapseButton /> : <Box />}
|
||||
{isLargeScreen ? <LeftPanelCollapseButton /> : <Box />}
|
||||
<RightPanelCollapseButton />
|
||||
</Box>
|
||||
);
|
||||
|
||||
+11
-3
@@ -4,6 +4,7 @@ import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
import { Box, ButtonCloseModal, Text } from '@/components';
|
||||
@@ -20,6 +21,12 @@ import EmptySearchIcon from '../assets/illustration-docs-empty.png';
|
||||
|
||||
import { DocSearchContent } from './DocSearchContent';
|
||||
|
||||
const ModalStyle = createGlobalStyle`
|
||||
.c__modal__scroller {
|
||||
overflow: inherit ;
|
||||
}
|
||||
`;
|
||||
|
||||
type DocSearchModalGlobalProps = {
|
||||
onClose: () => void;
|
||||
isOpen: boolean;
|
||||
@@ -43,7 +50,7 @@ const DocSearchModalGlobal = ({
|
||||
const [filters, setFilters] = useState<DocSearchFiltersValues>(
|
||||
defaultFilters ?? {},
|
||||
);
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
const handleInputSearch = useDebouncedCallback(setSearch, 300);
|
||||
|
||||
const handleSelect = (doc: Doc) => {
|
||||
@@ -60,10 +67,11 @@ const DocSearchModalGlobal = ({
|
||||
<Modal
|
||||
{...modalProps}
|
||||
closeOnClickOutside
|
||||
size={isDesktop ? ModalSize.LARGE : ModalSize.FULL}
|
||||
size={isLargeScreen ? ModalSize.LARGE : ModalSize.FULL}
|
||||
hideCloseButton
|
||||
aria-describedby="doc-search-modal-title"
|
||||
>
|
||||
<ModalStyle />
|
||||
<Box
|
||||
aria-label={t('Search modal')}
|
||||
$direction="column"
|
||||
@@ -107,7 +115,7 @@ const DocSearchModalGlobal = ({
|
||||
>
|
||||
<Box
|
||||
$padding={{ horizontal: '10px', vertical: 'base' }}
|
||||
$height={isDesktop ? '500px' : 'calc(100vh - 68px - 1rem)'}
|
||||
$height={isLargeScreen ? '500px' : 'calc(100vh - 68px - 1rem)'}
|
||||
>
|
||||
{search.length === 0 && (
|
||||
<Box
|
||||
|
||||
@@ -97,7 +97,7 @@ export const DocsGrid = ({
|
||||
$position="relative"
|
||||
$width="100%"
|
||||
$maxWidth="960px"
|
||||
$maxHeight="calc(100vh - 52px - 2rem)"
|
||||
$maxHeight={`calc(100vh - 52px - ${isDesktop ? '2rem' : '0rem'})`}
|
||||
$align="center"
|
||||
className="--docs--doc-grid"
|
||||
>
|
||||
|
||||
@@ -19,7 +19,7 @@ export const Header = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: config } = useConfig();
|
||||
const { spacingsTokens } = useCunninghamTheme();
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
|
||||
const icon = config?.theme_customization?.header?.icon;
|
||||
|
||||
@@ -46,7 +46,7 @@ export const Header = () => {
|
||||
var(--c--contextuals--border--surface--primary);
|
||||
`}
|
||||
>
|
||||
{!isDesktop && <LeftPanelToggleMobile />}
|
||||
{!isLargeScreen && <LeftPanelToggleMobile />}
|
||||
<StyledLink
|
||||
href="/"
|
||||
data-testid="header-logo-link"
|
||||
@@ -82,7 +82,7 @@ export const Header = () => {
|
||||
/>
|
||||
</Box>
|
||||
</StyledLink>
|
||||
{!isDesktop ? (
|
||||
{!isLargeScreen ? (
|
||||
<Box $direction="row" $gap={spacingsTokens['sm']}>
|
||||
<Waffle />
|
||||
</Box>
|
||||
|
||||
@@ -24,8 +24,8 @@ const MobileLeftPanelStyle = createGlobalStyle`
|
||||
`;
|
||||
|
||||
export const LeftPanel = () => {
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
if (isDesktop) {
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
if (isLargeScreen) {
|
||||
return <LeftPanelDesktop />;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ type LeftPanelFavoriteItemProps = {
|
||||
|
||||
export const LeftPanelFavoriteItem = ({ doc }: LeftPanelFavoriteItemProps) => {
|
||||
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -26,7 +26,7 @@ export const LeftPanelFavoriteItem = ({ doc }: LeftPanelFavoriteItemProps) => {
|
||||
padding: ${spacingsTokens['2xs']};
|
||||
border-radius: 4px;
|
||||
.pinned-actions {
|
||||
opacity: ${isDesktop ? 0 : 1};
|
||||
opacity: ${isLargeScreen ? 0 : 1};
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(
|
||||
|
||||
@@ -51,7 +51,7 @@ export const ResizableLeftPanel = ({
|
||||
maxPanelSizePx = 450,
|
||||
}: ResizableLeftPanelProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
const { isPanelOpen } = useLeftPanelStore();
|
||||
const ref = useRef<ImperativePanelHandle>(null);
|
||||
const savedWidthPxRef = useRef<number>(minPanelSizePx);
|
||||
@@ -89,7 +89,7 @@ export const ResizableLeftPanel = ({
|
||||
* to either expand/collapse
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!ref.current || !isDesktop) {
|
||||
if (!ref.current || !isLargeScreen) {
|
||||
return;
|
||||
}
|
||||
if (isPanelOpen) {
|
||||
@@ -97,11 +97,11 @@ export const ResizableLeftPanel = ({
|
||||
} else {
|
||||
ref.current.collapse();
|
||||
}
|
||||
}, [isPanelOpen, isDesktop]);
|
||||
}, [isPanelOpen, isLargeScreen]);
|
||||
|
||||
// Keep pixel width constant on window resize
|
||||
useEffect(() => {
|
||||
if (!isDesktop) {
|
||||
if (!isLargeScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ export const ResizableLeftPanel = ({
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, [isDesktop]);
|
||||
}, [isLargeScreen]);
|
||||
|
||||
/**
|
||||
* Workaround: NVDA does not enter focus mode for role="separator"
|
||||
@@ -158,15 +158,15 @@ export const ResizableLeftPanel = ({
|
||||
}}
|
||||
order={0}
|
||||
defaultSize={
|
||||
isDesktop
|
||||
isLargeScreen
|
||||
? Math.max(
|
||||
minPanelSizePercent,
|
||||
Math.min(panelSizePercent, maxPanelSizePercent),
|
||||
)
|
||||
: 0
|
||||
}
|
||||
minSize={isDesktop ? minPanelSizePercent : 0}
|
||||
maxSize={isDesktop ? maxPanelSizePercent : 0}
|
||||
minSize={isLargeScreen ? minPanelSizePercent : 0}
|
||||
maxSize={isLargeScreen ? maxPanelSizePercent : 0}
|
||||
onResize={handleResize}
|
||||
>
|
||||
{leftPanel}
|
||||
@@ -193,7 +193,7 @@ export const ResizableLeftPanel = ({
|
||||
cursor: 'col-resize',
|
||||
}}
|
||||
onDragging={setIsDragging}
|
||||
disabled={!isDesktop}
|
||||
disabled={!isLargeScreen}
|
||||
/>
|
||||
)}
|
||||
<Panel order={1}>{children}</Panel>
|
||||
|
||||
@@ -52,7 +52,7 @@ export function MainLayoutContent({
|
||||
backgroundColor,
|
||||
enableResizablePanel,
|
||||
}: PropsWithChildren<MainLayoutContentProps>) {
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
|
||||
if (enableResizablePanel) {
|
||||
return (
|
||||
@@ -71,7 +71,7 @@ export function MainLayoutContent({
|
||||
);
|
||||
}
|
||||
|
||||
if (!isDesktop) {
|
||||
if (!isLargeScreen) {
|
||||
return (
|
||||
<>
|
||||
<LeftPanel />
|
||||
|
||||
@@ -18,7 +18,7 @@ export function PageLayout({
|
||||
children,
|
||||
withFooter = true,
|
||||
}: PropsWithChildren<PageLayoutProps>) {
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { isLargeScreen } = useResponsiveStore();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Box
|
||||
@@ -45,7 +45,7 @@ export function PageLayout({
|
||||
`}
|
||||
aria-label={t('Main content')}
|
||||
>
|
||||
{!isDesktop && <LeftPanel />}
|
||||
{!isLargeScreen && <LeftPanel />}
|
||||
{children}
|
||||
</Box>
|
||||
{withFooter && <Footer />}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { create } from 'zustand';
|
||||
export type ScreenSize = 'small-mobile' | 'mobile' | 'tablet' | 'desktop';
|
||||
|
||||
export interface UseResponsiveStore {
|
||||
isLargeScreen: boolean;
|
||||
isMobile: boolean;
|
||||
isTablet: boolean;
|
||||
isSmallMobile: boolean;
|
||||
@@ -10,7 +11,6 @@ export interface UseResponsiveStore {
|
||||
screenWidth: number;
|
||||
setScreenSize: (size: ScreenSize) => void;
|
||||
isDesktop: boolean;
|
||||
isLargeScreen: boolean;
|
||||
initializeResizeListener: () => () => void;
|
||||
}
|
||||
|
||||
@@ -57,20 +57,20 @@ export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
|
||||
} else if (width >= 768 && width < 1024) {
|
||||
set({
|
||||
isDesktop: false,
|
||||
isLargeScreen: true,
|
||||
screenSize: 'tablet',
|
||||
isTablet: true,
|
||||
isMobile: false,
|
||||
isSmallMobile: false,
|
||||
isLargeScreen: true,
|
||||
});
|
||||
} else {
|
||||
set({
|
||||
isDesktop: true,
|
||||
isLargeScreen: true,
|
||||
screenSize: 'desktop',
|
||||
isTablet: false,
|
||||
isMobile: false,
|
||||
isSmallMobile: false,
|
||||
isLargeScreen: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user