🚚(app-desk) integrate next router with menu

Integrate next router with the menu.
Create most of the pages.
This commit is contained in:
Anthony LC
2024-01-26 16:13:06 +01:00
committed by Anthony LC
parent 66dbea3c6d
commit fc6487ddc1
12 changed files with 207 additions and 69 deletions

View File

@@ -0,0 +1,49 @@
import { Loader } from '@openfun/cunningham-react';
import { useEffect } from 'react';
import useAuthStore from '@/auth/useAuthStore';
import { Box } from '@/components';
import { HEADER_HEIGHT, Header } from './header';
import { Menu } from './menu';
export default function InnerLayout({
children,
}: {
children: React.ReactNode;
}) {
const { initAuth, authenticated, initialized } = useAuthStore();
useEffect(() => {
if (initialized) {
return;
}
initAuth();
}, [initAuth, initialized]);
if (!authenticated) {
return (
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
<Loader />
</Box>
);
}
return (
<Box as="main" $direction="column" $height="100vh" $css="overflow:hidden;">
<Header />
<Box $css="flex: 1;">
<Menu />
<Box
$direction="column"
$height={`calc(100vh - ${HEADER_HEIGHT})`}
$width="100%"
$css="overflow: auto;"
>
{children}
</Box>
</Box>
</Box>
);
}

View File

@@ -12,7 +12,7 @@ describe('Page', () => {
expect(screen.getByRole('status')).toBeInTheDocument();
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(
'Hello Desk!',
'Hello Desk !',
);
});
});

View File

@@ -0,0 +1,7 @@
'use client';
import { Box } from '@/components';
export default function Contacts() {
return <Box>Contacts</Box>;
}

View File

@@ -0,0 +1,7 @@
'use client';
import { Box } from '@/components';
export default function Favorite() {
return <Box>Favorite</Box>;
}

View File

@@ -0,0 +1,7 @@
'use client';
import { Box } from '@/components';
export default function Groups() {
return <Box>Groups</Box>;
}

View File

@@ -0,0 +1,7 @@
'use client';
import { Box } from '@/components';
export default function Help() {
return <Box>Help</Box>;
}

View File

@@ -5,7 +5,9 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useCunninghamTheme } from '@/cunningham';
import '@/i18n/initI18n';
import InnerLayout from './InnerLayout';
import './globals.css';
@@ -23,7 +25,9 @@ export default function RootLayout({
<body suppressHydrationWarning={process.env.NODE_ENV === 'development'}>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
<CunninghamProvider theme={theme}>{children}</CunninghamProvider>
<CunninghamProvider theme={theme}>
<InnerLayout>{children}</InnerLayout>
</CunninghamProvider>
</QueryClientProvider>
</body>
</html>

View File

@@ -25,12 +25,12 @@ export const Menu = () => {
$justify="space-between"
$direction="column"
>
<Box $direction="column">
<MenuItem Icon={IconSearch} label={t('Search')} />
<MenuItem Icon={IconFavorite} label={t('Favorite')} />
<MenuItem Icon={IconRecent} label={t('Recent')} />
<MenuItem Icon={IconContacts} label={t('Contacts')} />
<MenuItem Icon={IconGroup} label={t('Groups')} />
<Box className="pt-b" $direction="column" $gap="0.8rem">
<MenuItem Icon={IconSearch} label={t('Search')} href="/" />
<MenuItem Icon={IconFavorite} label={t('Favorite')} href="/favorite" />
<MenuItem Icon={IconRecent} label={t('Recent')} href="/recent" />
<MenuItem Icon={IconContacts} label={t('Contacts')} href="/contacts" />
<MenuItem Icon={IconGroup} label={t('Groups')} href="/groups" />
</Box>
</Box>
);

View File

@@ -1,6 +1,9 @@
import { Button } from '@openfun/cunningham-react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { Box } from '@/components/';
import { useCunninghamTheme } from '@/cunningham';
@@ -8,41 +11,71 @@ import { SVGComponent } from '@/types/components';
import { Tooltip } from './Tooltip';
const StyledLink = styled(Link)`
text-decoration: none;
color: #ffffff33;
&[aria-current='page'] {
color: #ffffff;
}
`;
interface MenuItemProps {
Icon: SVGComponent;
label: string;
href: string;
}
const MenuItem = ({ Icon, label }: MenuItemProps) => {
const MenuItem = ({ Icon, label, href }: MenuItemProps) => {
const { t } = useTranslation();
const buttonRef = useRef(null);
const pathname = usePathname();
const { colorsTokens } = useCunninghamTheme();
const buttonRef = useRef(null);
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
const isActive = pathname === href;
const { color, background, colorTooltip, backgroundTooltip } = isActive
? {
color: colorsTokens()['primary-600'],
background: colorsTokens()['primary-300'],
backgroundTooltip: 'white',
colorTooltip: 'black',
}
: {
color: '#ffffff55',
background: undefined,
backgroundTooltip: '#161616',
colorTooltip: 'white',
};
return (
<>
<StyledLink
href={href}
aria-current={isActive && 'page'}
ref={buttonRef}
onMouseOver={() => setIsTooltipOpen(true)}
onMouseOut={() => setIsTooltipOpen(false)}
>
<Box
className="m-st p-t"
as="li"
$justify="center"
ref={buttonRef}
$css={`
& > button { padding: 0};
transition: all 0.2s ease-in-out
`}
$background={colorsTokens()['primary-300']}
$background={background}
$radius="10px"
onMouseOver={() => setIsTooltipOpen(true)}
onMouseOut={() => setIsTooltipOpen(false)}
>
<Button
aria-label={t(`{{label}} button`, { label })}
icon={
<Box $color={colorsTokens()['primary-600']}>
<Box $color={color}>
<Icon
width="2.375rem"
aria-label={t(`{{label}} icon`, { label })}
color="#ffffff"
style={{
transition: 'color 0.2s ease-in-out',
}}
/>
</Box>
}
@@ -55,11 +88,11 @@ const MenuItem = ({ Icon, label }: MenuItemProps) => {
<Tooltip
buttonRef={buttonRef}
label={label}
backgroundColor="#ffffff"
textColor="#000000"
backgroundColor={backgroundTooltip}
textColor={colorTooltip}
/>
)}
</>
</StyledLink>
);
};

View File

@@ -1,47 +1,18 @@
'use client';
import { Loader } from '@openfun/cunningham-react';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import useAuthStore from '@/auth/useAuthStore';
import { Box } from '@/components';
import Header, { HEADER_HEIGHT } from './Header/Header';
import { Teams } from './Teams';
import { MENU_WIDTH, Menu } from './menu';
export default function Home() {
const { initAuth, authenticated, initialized } = useAuthStore();
const { t } = useTranslation();
useEffect(() => {
if (initialized) {
return;
}
initAuth();
}, [initAuth, initialized]);
if (!authenticated) {
return <Loader />;
}
return (
<main>
<Header />
<Box $css={`margin-top:${HEADER_HEIGHT}`}>
<Menu />
<Box
$css={`margin-left:${MENU_WIDTH}`}
$direction="column"
$height="300vh"
className="p-b"
>
<h1>{t('Hello Desk !')}</h1>
<Teams />
</Box>
</Box>
</main>
<Box $direction="column" className="p-b">
<h1>{t('Hello Desk !')}</h1>
<Teams />
</Box>
);
}

View File

@@ -0,0 +1,7 @@
'use client';
import { Box } from '@/components';
export default function Recent() {
return <Box>Recent</Box>;
}

View File

@@ -1,26 +1,72 @@
import { expect, test } from "@playwright/test";
import { expect, test } from '@playwright/test';
import { keyCloakSignIn } from "./common";
import { keyCloakSignIn } from './common';
test.beforeEach(async ({ page }) => {
await page.goto("/");
await page.goto('/');
await keyCloakSignIn(page);
});
test.describe("Menu", () => {
test("checks all the elements are visible", async ({ page }) => {
const menu = page.locator("menu").first();
test.describe('Menu', () => {
const menuItems = [
{ name: 'Search', isDefault: true },
{ name: 'Favorite', isDefault: false },
{ name: 'Recent', isDefault: false },
{ name: 'Contacts', isDefault: false },
{ name: 'Groups', isDefault: false },
];
for (const { name, isDefault } of menuItems) {
test(`checks that ${name} menu item is displaying correctly`, async ({
page,
}) => {
const menu = page.locator('menu').first();
await expect(menu.getByLabel("Search button")).toBeVisible();
await expect(menu.getByLabel("Favoris button")).toBeVisible();
await expect(menu.getByLabel("Recent button")).toBeVisible();
await expect(menu.getByLabel("Contacts button")).toBeVisible();
await expect(menu.getByLabel("Groups button")).toBeVisible();
const buttonMenu = menu.getByLabel(`${name} button`);
await expect(buttonMenu).toBeVisible();
await buttonMenu.hover();
await expect(menu.getByLabel('tooltip')).toHaveText(name);
await menu.getByLabel("Search button").hover();
await expect(menu.getByLabel("tooltip")).toHaveText("Search");
// Checks the tooltip is with inactive color
await expect(menu.getByLabel('tooltip')).toHaveCSS(
'background-color',
isDefault ? 'rgb(255, 255, 255)' : 'rgb(22, 22, 22)',
);
await menu.getByLabel("Contacts button").hover();
await expect(menu.getByLabel("tooltip")).toHaveText("Contacts");
});
await buttonMenu.click();
// Checks the tooltip has active color
await buttonMenu.hover();
await expect(menu.getByLabel('tooltip')).toHaveCSS(
'background-color',
'rgb(255, 255, 255)',
);
});
test(`checks that ${name} menu item is routing correctly`, async ({
page,
}) => {
await expect(
page.locator('h1').first().getByText('Hello Desk !'),
).toBeVisible();
const menu = page.locator('menu').first();
const buttonMenu = menu.getByLabel(`${name} button`);
await buttonMenu.click();
// eslint-disable-next-line playwright/no-conditional-in-test
if (isDefault) {
await expect(
page.locator('h1').first().getByText('Hello Desk !'),
).toBeVisible();
} else {
await expect(
page.locator('h1').first().getByText('Hello Desk !'),
).toBeHidden();
const reg = new RegExp(name.toLowerCase());
await expect(page).toHaveURL(reg);
}
});
}
});