diff --git a/src/frontend/apps/desk/cunningham.ts b/src/frontend/apps/desk/cunningham.ts index 59c1ded..7aed77a 100644 --- a/src/frontend/apps/desk/cunningham.ts +++ b/src/frontend/apps/desk/cunningham.ts @@ -3,6 +3,7 @@ const config = { default: { theme: { colors: { + 'primary-bg': '#FAFAFA', 'primary-100': '#EDF5FA', 'primary-150': '#E5EEFA', 'info-150': '#E5EEFA', @@ -259,6 +260,16 @@ const config = { }, button: { 'border-radius': '2px', + primary: { + background: { + color: 'var(--c--theme--colors--primary-text)', + 'color-hover': 'var(--c--theme--colors--primary-700)', + 'color-active': 'var(--c--theme--colors--primary-900)', + }, + color: '#ffffff', + 'color-hover': '#ffffff', + 'color-active': '#ffffff', + }, }, 'forms-checkbox': { 'border-radius': '0', diff --git a/src/frontend/apps/desk/src/app/__tests__/page.test.tsx b/src/frontend/apps/desk/src/app/__tests__/page.test.tsx index 51fa2db..ee91d1f 100644 --- a/src/frontend/apps/desk/src/app/__tests__/page.test.tsx +++ b/src/frontend/apps/desk/src/app/__tests__/page.test.tsx @@ -11,8 +11,10 @@ describe('Page', () => { expect(screen.getByRole('status')).toBeInTheDocument(); - expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent( - 'Hello Desk !', - ); + expect( + screen.getByRole('button', { + name: /Create a new team/i, + }), + ).toBeInTheDocument(); }); }); diff --git a/src/frontend/apps/desk/src/app/page.tsx b/src/frontend/apps/desk/src/app/page.tsx index 7c25f61..98baf2c 100644 --- a/src/frontend/apps/desk/src/app/page.tsx +++ b/src/frontend/apps/desk/src/app/page.tsx @@ -1,17 +1,32 @@ 'use client'; +import { Button } from '@openfun/cunningham-react'; import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; import { Box } from '@/components'; -import { Teams } from '@/features'; +import { useCunninghamTheme } from '@/cunningham'; +import { Panel } from '@/features'; + +const StyledButton = styled(Button)` + width: fit-content; +`; export default function Home() { const { t } = useTranslation(); + const { colorsTokens } = useCunninghamTheme(); return ( - -

{t('Hello Desk !')}

- + + + + {t('Create a new group')} + ); } diff --git a/src/frontend/apps/desk/src/features/menu/assets/icon-group.svg b/src/frontend/apps/desk/src/assets/icons/icon-group.svg similarity index 100% rename from src/frontend/apps/desk/src/features/menu/assets/icon-group.svg rename to src/frontend/apps/desk/src/assets/icons/icon-group.svg diff --git a/src/frontend/apps/desk/src/components/Text.tsx b/src/frontend/apps/desk/src/components/Text.tsx index c2ab7a7..4500892 100644 --- a/src/frontend/apps/desk/src/components/Text.tsx +++ b/src/frontend/apps/desk/src/components/Text.tsx @@ -14,6 +14,7 @@ export interface TextProps extends BoxProps { 'p' | 'span' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' >; $weight?: CSSProperties['fontWeight']; + $textAlign?: CSSProperties['textAlign']; // eslint-disable-next-line @typescript-eslint/ban-types $size?: TextSizes | (string & {}); $theme?: @@ -38,6 +39,7 @@ export interface TextProps extends BoxProps { } export const TextStyled = styled(Box)` + ${({ $textAlign }) => $textAlign && `text-align: ${$textAlign};`} ${({ $weight }) => $weight && `font-weight: ${$weight};`} ${({ $size }) => $size && diff --git a/src/frontend/apps/desk/src/cunningham/cunningham-style.css b/src/frontend/apps/desk/src/cunningham/cunningham-style.css index ee8dd78..e99b5cb 100644 --- a/src/frontend/apps/desk/src/cunningham/cunningham-style.css +++ b/src/frontend/apps/desk/src/cunningham/cunningham-style.css @@ -272,6 +272,13 @@ input:-webkit-autofill:focus { color: var(--c--components--button--primary--color); } +.c__button--primary:hover { + background-color: var( + --c--components--button--primary--background--color-hover + ); + color: var(--c--components--button--primary--color-hover); +} + .c__button--primary:active, .c__button--primary.c__button--active { background-color: var( diff --git a/src/frontend/apps/desk/src/cunningham/cunningham-tokens.css b/src/frontend/apps/desk/src/cunningham/cunningham-tokens.css index 80951d6..5c605dd 100644 --- a/src/frontend/apps/desk/src/cunningham/cunningham-tokens.css +++ b/src/frontend/apps/desk/src/cunningham/cunningham-tokens.css @@ -69,6 +69,7 @@ --c--theme--colors--success-text: var(--c--theme--colors--greyscale-000); --c--theme--colors--warning-text: var(--c--theme--colors--greyscale-000); --c--theme--colors--danger-text: var(--c--theme--colors--greyscale-000); + --c--theme--colors--primary-bg: #fafafa; --c--theme--colors--primary-150: #e5eefa; --c--theme--colors--info-150: #e5eefa; --c--theme--font--sizes--h1: 2.2rem; @@ -387,6 +388,18 @@ --c--theme--font--families--base: marianne; --c--components--alert--border-radius: 0; --c--components--button--border-radius: 2px; + --c--components--button--primary--background--color: var( + --c--theme--colors--primary-text + ); + --c--components--button--primary--background--color-hover: var( + --c--theme--colors--primary-700 + ); + --c--components--button--primary--background--color-active: var( + --c--theme--colors--primary-900 + ); + --c--components--button--primary--color: #fff; + --c--components--button--primary--color-hover: #fff; + --c--components--button--primary--color-active: #fff; --c--components--forms-checkbox--border-radius: 0; --c--components--forms-switch--handle-border-radius: 2px; --c--components--forms-switch--rail-border-radius: 4px; @@ -677,6 +690,10 @@ color: var(--c--theme--colors--danger-text); } +.clr-primary-bg { + color: var(--c--theme--colors--primary-bg); +} + .clr-primary-150 { color: var(--c--theme--colors--primary-150); } @@ -965,6 +982,10 @@ background-color: var(--c--theme--colors--danger-text); } +.bg-primary-bg { + background-color: var(--c--theme--colors--primary-bg); +} + .bg-primary-150 { background-color: var(--c--theme--colors--primary-150); } diff --git a/src/frontend/apps/desk/src/cunningham/cunningham-tokens.ts b/src/frontend/apps/desk/src/cunningham/cunningham-tokens.ts index 611856b..d860bd2 100644 --- a/src/frontend/apps/desk/src/cunningham/cunningham-tokens.ts +++ b/src/frontend/apps/desk/src/cunningham/cunningham-tokens.ts @@ -73,6 +73,7 @@ export const tokens = { 'success-text': '#FFFFFF', 'warning-text': '#FFFFFF', 'danger-text': '#FFFFFF', + 'primary-bg': '#FAFAFA', 'primary-150': '#E5EEFA', 'info-150': '#E5EEFA', }, @@ -396,7 +397,19 @@ export const tokens = { }, components: { alert: { 'border-radius': '0' }, - button: { 'border-radius': '2px' }, + button: { + 'border-radius': '2px', + primary: { + background: { + color: 'var(--c--theme--colors--primary-text)', + 'color-hover': 'var(--c--theme--colors--primary-700)', + 'color-active': 'var(--c--theme--colors--primary-900)', + }, + color: '#ffffff', + 'color-hover': '#ffffff', + 'color-active': '#ffffff', + }, + }, 'forms-checkbox': { 'border-radius': '0' }, 'forms-switch': { 'handle-border-radius': '2px', diff --git a/src/frontend/apps/desk/src/features/menu/Menu.tsx b/src/frontend/apps/desk/src/features/menu/Menu.tsx index c606a41..de94f4d 100644 --- a/src/frontend/apps/desk/src/features/menu/Menu.tsx +++ b/src/frontend/apps/desk/src/features/menu/Menu.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import IconGroup from '@/assets/icons/icon-group.svg'; import { Box } from '@/components/'; import useCunninghamTheme from '@/cunningham/useCunninghamTheme'; import MenuItem from './MenuItems'; import IconRecent from './assets/icon-clock.svg'; import IconContacts from './assets/icon-contacts.svg'; -import IconGroup from './assets/icon-group.svg'; import IconSearch from './assets/icon-search.svg'; import IconFavorite from './assets/icon-stars.svg'; diff --git a/src/frontend/apps/desk/src/features/teams/__tests__/PanelTeams.test.tsx b/src/frontend/apps/desk/src/features/teams/__tests__/PanelTeams.test.tsx new file mode 100644 index 0000000..1573ddf --- /dev/null +++ b/src/frontend/apps/desk/src/features/teams/__tests__/PanelTeams.test.tsx @@ -0,0 +1,89 @@ +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/react'; +import fetchMock from 'fetch-mock'; + +import { AppWrapper } from '@/tests/utils'; + +import { PanelTeams } from '../components/PanelTeams'; + +describe('PanelTeams', () => { + afterEach(() => { + fetchMock.restore(); + }); + + it('renders with no team to display', async () => { + fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, { + count: 0, + results: [], + }); + + render(, { wrapper: AppWrapper }); + + expect(screen.getByRole('status')).toBeInTheDocument(); + + expect( + await screen.findByText( + 'Create your first team by clicking on the "Create a new team" button.', + ), + ).toBeInTheDocument(); + }); + + it('renders with empty team to display', async () => { + fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, { + count: 1, + results: [ + { + id: '1', + name: 'Team 1', + accesses: [], + }, + ], + }); + + render(, { wrapper: AppWrapper }); + + expect(screen.getByRole('status')).toBeInTheDocument(); + + expect(await screen.findByLabelText('Empty team icon')).toBeInTheDocument(); + }); + + it('renders with not team to display', async () => { + fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, { + count: 1, + results: [ + { + id: '1', + name: 'Team 1', + accesses: [ + { + id: '1', + role: 'admin', + }, + ], + }, + ], + }); + + render(, { wrapper: AppWrapper }); + + expect(screen.getByRole('status')).toBeInTheDocument(); + + expect(await screen.findByLabelText('Team icon')).toBeInTheDocument(); + }); + + it('renders the error', async () => { + fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, { + status: 500, + }); + + render(, { wrapper: AppWrapper }); + + expect(screen.getByRole('status')).toBeInTheDocument(); + + expect( + await screen.findByText( + 'Something bad happens, please refresh the page.', + ), + ).toBeInTheDocument(); + }); +}); diff --git a/src/frontend/apps/desk/src/features/teams/__tests__/teams.test.tsx b/src/frontend/apps/desk/src/features/teams/__tests__/teams.test.tsx deleted file mode 100644 index dd417dc..0000000 --- a/src/frontend/apps/desk/src/features/teams/__tests__/teams.test.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import '@testing-library/jest-dom'; -import { render, screen } from '@testing-library/react'; -import fetchMock from 'fetch-mock'; - -import { AppWrapper } from '@/tests/utils'; - -import { Teams } from '..'; - -describe('Teams', () => { - afterEach(() => { - fetchMock.restore(); - }); - - it('checks Teams rendering', async () => { - fetchMock.mock(`/api/teams/`, { - results: [ - { - id: '1', - name: 'Team 1', - }, - { - id: '2', - name: 'Team 2', - }, - ], - }); - - render(, { wrapper: AppWrapper }); - - expect(screen.getByRole('status')).toBeInTheDocument(); - - expect( - await screen.findByRole('button', { - name: 'Create Team', - }), - ).toBeInTheDocument(); - - expect(screen.getByText(/Team 1/)).toBeInTheDocument(); - expect(screen.getByText(/Team 2/)).toBeInTheDocument(); - }); -}); diff --git a/src/frontend/apps/desk/src/features/teams/api/useTeams.tsx b/src/frontend/apps/desk/src/features/teams/api/useTeams.tsx index 9a107bb..62e0107 100644 --- a/src/frontend/apps/desk/src/features/teams/api/useTeams.tsx +++ b/src/frontend/apps/desk/src/features/teams/api/useTeams.tsx @@ -2,9 +2,22 @@ import { UseQueryOptions, useQuery } from '@tanstack/react-query'; import { APIList, fetchAPI } from '@/api'; +enum Role { + MEMBER = 'member', + ADMIN = 'administrator', + OWNER = 'owner', +} + +interface Access { + id: string; + role: Role; + user: string; +} + interface TeamResponse { id: string; name: string; + accesses: Access[]; } type TeamsResponse = APIList; diff --git a/src/frontend/apps/desk/src/features/teams/assets/icon-add.svg b/src/frontend/apps/desk/src/features/teams/assets/icon-add.svg new file mode 100644 index 0000000..fe02a9c --- /dev/null +++ b/src/frontend/apps/desk/src/features/teams/assets/icon-add.svg @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/src/frontend/apps/desk/src/features/teams/assets/icon-none.svg b/src/frontend/apps/desk/src/features/teams/assets/icon-none.svg new file mode 100644 index 0000000..9f80850 --- /dev/null +++ b/src/frontend/apps/desk/src/features/teams/assets/icon-none.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/src/frontend/apps/desk/src/features/teams/assets/icon-sort.svg b/src/frontend/apps/desk/src/features/teams/assets/icon-sort.svg new file mode 100644 index 0000000..cfa94cb --- /dev/null +++ b/src/frontend/apps/desk/src/features/teams/assets/icon-sort.svg @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/src/frontend/apps/desk/src/features/teams/components/Panel.tsx b/src/frontend/apps/desk/src/features/teams/components/Panel.tsx new file mode 100644 index 0000000..a757701 --- /dev/null +++ b/src/frontend/apps/desk/src/features/teams/components/Panel.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Box, Text } from '@/components'; +import { useCunninghamTheme } from '@/cunningham'; + +import { PanelActions } from './PanelActions'; +import { PanelTeams } from './PanelTeams'; + +export const Panel = () => { + const { t } = useTranslation(); + const { colorsTokens } = useCunninghamTheme(); + + return ( + + + + {t('Recents')} + + + + + + ); +}; diff --git a/src/frontend/apps/desk/src/features/teams/components/PanelActions.tsx b/src/frontend/apps/desk/src/features/teams/components/PanelActions.tsx new file mode 100644 index 0000000..f4ccb17 --- /dev/null +++ b/src/frontend/apps/desk/src/features/teams/components/PanelActions.tsx @@ -0,0 +1,38 @@ +import { Button } from '@openfun/cunningham-react'; +import Image from 'next/image'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Box } from '@/components'; + +import { default as IconAdd } from '../assets/icon-add.svg?url'; +import { default as IconSort } from '../assets/icon-sort.svg?url'; + +export const PanelActions = () => { + const { t } = useTranslation(); + + return ( + button { + padding: 0; + } + `} + > + - -
-
    - {data?.results.map((post, index) => ( -
  • -
    - - {index + 1}. {post.name} - -
    -
  • - ))} -
-
- - ); -}; +export * from './components/Panel'; diff --git a/src/frontend/apps/desk/src/i18n/translations.json b/src/frontend/apps/desk/src/i18n/translations.json index 932f8bb..0a4a9c1 100644 --- a/src/frontend/apps/desk/src/i18n/translations.json +++ b/src/frontend/apps/desk/src/i18n/translations.json @@ -24,7 +24,8 @@ "{{label}} button": "Bouton {{label}}", "{{label}} icon": "Icône {{label}}", "Team name": "Nom de l’équipe", - "Create Team": "Créer une équipe" + "Create Team": "Créer une équipe", + "Create a new team": "Créer un nouveau groupe" } } } diff --git a/src/frontend/apps/e2e/__tests__/app-desk/app.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/app.spec.ts deleted file mode 100644 index 0d43c00..0000000 --- a/src/frontend/apps/e2e/__tests__/app-desk/app.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { expect, test } from '@playwright/test'; - -import { keyCloakSignIn } from './common'; - -test.beforeEach(async ({ page }) => { - await page.goto('/'); - await keyCloakSignIn(page); -}); - -test.describe('App', () => { - test('should display the main elements', async ({ page }) => { - await expect(page.locator('header').first()).toContainText('Desk'); - await expect(page.getByLabel('Team name')).toBeVisible(); - }); - - test('creates 2 teams and displayed them', async ({ page }) => { - await page.getByLabel('Team name').fill('My new team'); - await page.click('button:has-text("Create Team")'); - await page.getByLabel('Team name').fill('My second new team'); - await page.click('button:has-text("Create Team")'); - - await expect( - page.locator('li').getByText('My new team').first(), - ).toBeVisible(); - await expect( - page.locator('li').getByText('My second new team').first(), - ).toBeVisible(); - }); -}); diff --git a/src/frontend/apps/e2e/__tests__/app-desk/header.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/header.spec.ts index 244f3b5..dc45b3e 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/header.spec.ts @@ -24,7 +24,7 @@ test.describe('Header', () => { ); await expect(header.locator('h2').getByText('Desk')).toHaveCSS( 'font-family', - 'marianne', + /Marianne/i, ); await expect( diff --git a/src/frontend/apps/e2e/__tests__/app-desk/language.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/language.spec.ts index ac46033..4d8f217 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/language.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/language.spec.ts @@ -10,7 +10,9 @@ test.beforeEach(async ({ page }) => { test.describe('Language', () => { test('checks the language picker', async ({ page }) => { await expect( - page.locator('h1').first().getByText('Hello Desk !'), + page.getByRole('button', { + name: 'Create a new team', + }), ).toBeVisible(); const header = page.locator('header').first(); @@ -19,7 +21,9 @@ test.describe('Language', () => { await expect(header.getByRole('combobox').getByText('FR')).toBeVisible(); await expect( - page.locator('h1').first().getByText('Bonjour Desk !'), + page.getByRole('button', { + name: 'Créer un nouveau groupe', + }), ).toBeVisible(); }); }); diff --git a/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts index 49504c2..dd59e62 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts @@ -46,7 +46,9 @@ test.describe('Menu', () => { page, }) => { await expect( - page.locator('h1').first().getByText('Hello Desk !'), + page.getByRole('button', { + name: 'Create a new team', + }), ).toBeVisible(); const menu = page.locator('menu').first(); @@ -57,11 +59,15 @@ test.describe('Menu', () => { // eslint-disable-next-line playwright/no-conditional-in-test if (isDefault) { await expect( - page.locator('h1').first().getByText('Hello Desk !'), + page.getByRole('button', { + name: 'Create a new team', + }), ).toBeVisible(); } else { await expect( - page.locator('h1').first().getByText('Hello Desk !'), + page.getByRole('button', { + name: 'Create a new team', + }), ).toBeHidden(); const reg = new RegExp(name.toLowerCase()); diff --git a/src/frontend/apps/e2e/__tests__/app-desk/teams.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/teams.spec.ts new file mode 100644 index 0000000..99415a5 --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-desk/teams.spec.ts @@ -0,0 +1,34 @@ +import { expect, test } from "@playwright/test"; + +import { keyCloakSignIn } from "./common"; + +test.beforeEach(async ({ page }) => { + await page.goto("/"); + await keyCloakSignIn(page); +}); + +test.describe("Teams", () => { + test("checks all the elements are visible", async ({ page }) => { + const panel = page.getByLabel("Teams panel").first(); + + await expect(panel.getByText("Recents")).toBeVisible(); + + await expect( + panel.getByRole("button", { + name: "Sort the teams", + }), + ).toBeVisible(); + + await expect( + panel.getByRole("button", { + name: "Add a team", + }), + ).toBeVisible(); + + await expect( + panel.getByText( + 'Create your first team by clicking on the "Create a new team" button.', + ), + ).toBeVisible(); + }); +});