🚚(app-desk) integrate next router with menu
Integrate next router with the menu. Create most of the pages.
This commit is contained in:
49
src/frontend/apps/desk/src/app/InnerLayout.tsx
Normal file
49
src/frontend/apps/desk/src/app/InnerLayout.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ describe('Page', () => {
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(
|
||||
'Hello Desk!',
|
||||
'Hello Desk !',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
7
src/frontend/apps/desk/src/app/contacts/page.tsx
Normal file
7
src/frontend/apps/desk/src/app/contacts/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { Box } from '@/components';
|
||||
|
||||
export default function Contacts() {
|
||||
return <Box>Contacts</Box>;
|
||||
}
|
||||
7
src/frontend/apps/desk/src/app/favorite/page.tsx
Normal file
7
src/frontend/apps/desk/src/app/favorite/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { Box } from '@/components';
|
||||
|
||||
export default function Favorite() {
|
||||
return <Box>Favorite</Box>;
|
||||
}
|
||||
7
src/frontend/apps/desk/src/app/groups/page.tsx
Normal file
7
src/frontend/apps/desk/src/app/groups/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { Box } from '@/components';
|
||||
|
||||
export default function Groups() {
|
||||
return <Box>Groups</Box>;
|
||||
}
|
||||
7
src/frontend/apps/desk/src/app/help/page.tsx
Normal file
7
src/frontend/apps/desk/src/app/help/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { Box } from '@/components';
|
||||
|
||||
export default function Help() {
|
||||
return <Box>Help</Box>;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
7
src/frontend/apps/desk/src/app/recent/page.tsx
Normal file
7
src/frontend/apps/desk/src/app/recent/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { Box } from '@/components';
|
||||
|
||||
export default function Recent() {
|
||||
return <Box>Recent</Box>;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user