✨(app-desk) add interaction to sort button
In the team panel, we have the possibility to sort the teams. This commit adds the interaction to the button.
This commit is contained in:
@@ -4,3 +4,22 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #d6dee1;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 6px solid transparent;
|
||||||
|
background-clip: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #a8bbbf;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@openfun/cunningham-react';
|
import { Button, Field, Input } from '@openfun/cunningham-react';
|
||||||
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { Box } from '@/components';
|
import { Box } from '@/components';
|
||||||
import { useCunninghamTheme } from '@/cunningham';
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
import { Panel } from '@/features';
|
import { Panel, useCreateTeam } from '@/features';
|
||||||
|
|
||||||
const StyledButton = styled(Button)`
|
const StyledButton = styled(Button)`
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
@@ -14,6 +15,8 @@ const StyledButton = styled(Button)`
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { mutate: createTeam } = useCreateTeam();
|
||||||
|
const [teamName, setTeamName] = useState('');
|
||||||
const { colorsTokens } = useCunninghamTheme();
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -24,8 +27,23 @@ export default function Home() {
|
|||||||
$justify="center"
|
$justify="center"
|
||||||
$align="center"
|
$align="center"
|
||||||
$width="100%"
|
$width="100%"
|
||||||
|
$gap="5rem"
|
||||||
>
|
>
|
||||||
<StyledButton>{t('Create a new group')}</StyledButton>
|
<StyledButton>{t('Create a new team')}</StyledButton>
|
||||||
|
<Field>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
label={t('Team name')}
|
||||||
|
onChange={(e) => setTeamName(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
onClick={() => createTeam(teamName)}
|
||||||
|
className="mt-s"
|
||||||
|
>
|
||||||
|
{t('Create a team')}
|
||||||
|
</Button>
|
||||||
|
</Field>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
1
src/frontend/apps/desk/src/features/teams/api/index.ts
Normal file
1
src/frontend/apps/desk/src/features/teams/api/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './useCreateTeam';
|
||||||
@@ -34,7 +34,6 @@ export function useCreateTeam() {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
void queryClient.invalidateQueries({
|
void queryClient.invalidateQueries({
|
||||||
queryKey: [KEY_LIST_TEAM],
|
queryKey: [KEY_LIST_TEAM],
|
||||||
exact: true,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,13 +20,24 @@ interface TeamResponse {
|
|||||||
accesses: Access[];
|
accesses: Access[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum TeamsOrdering {
|
||||||
|
BY_CREATED_ON = 'created_at',
|
||||||
|
BY_CREATED_ON_DESC = '-created_at',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TeamsParams = {
|
||||||
|
ordering?: TeamsOrdering;
|
||||||
|
};
|
||||||
|
|
||||||
type TeamsResponse = APIList<TeamResponse>;
|
type TeamsResponse = APIList<TeamResponse>;
|
||||||
export interface TeamsResponseError {
|
export interface TeamsResponseError {
|
||||||
detail: string;
|
detail: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTeams = async () => {
|
export const getTeams = async (props?: TeamsParams) => {
|
||||||
const response = await fetchAPI(`teams/`);
|
const response = await fetchAPI(
|
||||||
|
`teams/${props?.ordering ? `?ordering=${props.ordering}` : ''}`,
|
||||||
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Couldn't fetch teams: ${response.statusText}`);
|
throw new Error(`Couldn't fetch teams: ${response.statusText}`);
|
||||||
@@ -37,6 +48,7 @@ export const getTeams = async () => {
|
|||||||
export const KEY_LIST_TEAM = 'teams';
|
export const KEY_LIST_TEAM = 'teams';
|
||||||
|
|
||||||
export function useTeams(
|
export function useTeams(
|
||||||
|
param?: TeamsParams,
|
||||||
queryConfig?: UseQueryOptions<
|
queryConfig?: UseQueryOptions<
|
||||||
TeamsResponse,
|
TeamsResponse,
|
||||||
TeamsResponseError,
|
TeamsResponseError,
|
||||||
@@ -44,8 +56,8 @@ export function useTeams(
|
|||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
return useQuery<TeamsResponse, TeamsResponseError, TeamsResponse>({
|
return useQuery<TeamsResponse, TeamsResponseError, TeamsResponse>({
|
||||||
queryKey: [KEY_LIST_TEAM],
|
queryKey: [KEY_LIST_TEAM, param],
|
||||||
queryFn: getTeams,
|
queryFn: () => getTeams(param),
|
||||||
...queryConfig,
|
...queryConfig,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import { Box } from '@/components';
|
|||||||
|
|
||||||
import { default as IconAdd } from '../assets/icon-add.svg?url';
|
import { default as IconAdd } from '../assets/icon-add.svg?url';
|
||||||
import { default as IconSort } from '../assets/icon-sort.svg?url';
|
import { default as IconSort } from '../assets/icon-sort.svg?url';
|
||||||
|
import { useTeamStore } from '../store/useTeamsStore';
|
||||||
|
|
||||||
export const PanelActions = () => {
|
export const PanelActions = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const changeOrdering = useTeamStore((state) => state.changeOrdering);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -26,6 +28,7 @@ export const PanelActions = () => {
|
|||||||
icon={<Image priority src={IconSort} alt={t('Sort teams icon')} />}
|
icon={<Image priority src={IconSort} alt={t('Sort teams icon')} />}
|
||||||
color="tertiary"
|
color="tertiary"
|
||||||
className="c__button-no-bg p-0 m-0"
|
className="c__button-no-bg p-0 m-0"
|
||||||
|
onClick={changeOrdering}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
aria-label={t('Add a team')}
|
aria-label={t('Add a team')}
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ import { useCunninghamTheme } from '@/cunningham';
|
|||||||
|
|
||||||
import { useTeams } from '../api/useTeams';
|
import { useTeams } from '../api/useTeams';
|
||||||
import IconNone from '../assets/icon-none.svg';
|
import IconNone from '../assets/icon-none.svg';
|
||||||
|
import { useTeamStore } from '../store/useTeamsStore';
|
||||||
|
|
||||||
export const PanelTeams = () => {
|
export const PanelTeams = () => {
|
||||||
const { data, isPending, isError } = useTeams();
|
const ordering = useTeamStore((state) => state.ordering);
|
||||||
|
const { data, isPending, isError } = useTeams({
|
||||||
|
ordering,
|
||||||
|
});
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { colorsTokens } = useCunninghamTheme();
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
@@ -48,7 +52,7 @@ export const PanelTeams = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box as="ul" $gap="1rem" className="p-s mt-t">
|
<Box as="ul" $gap="1rem" className="p-s mt-t" $css="overflow:auto;">
|
||||||
{data?.results.map((team) => (
|
{data?.results.map((team) => (
|
||||||
<Box
|
<Box
|
||||||
as="li"
|
as="li"
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export * from './components/Panel';
|
export * from './components';
|
||||||
|
export * from './api';
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
import { TeamsOrdering } from '../api/useTeams';
|
||||||
|
|
||||||
|
interface TeamsStore {
|
||||||
|
ordering: TeamsOrdering;
|
||||||
|
changeOrdering: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTeamStore = create<TeamsStore>((set) => ({
|
||||||
|
ordering: TeamsOrdering.BY_CREATED_ON_DESC,
|
||||||
|
changeOrdering: () =>
|
||||||
|
set(({ ordering }) => ({
|
||||||
|
ordering:
|
||||||
|
ordering === TeamsOrdering.BY_CREATED_ON
|
||||||
|
? TeamsOrdering.BY_CREATED_ON_DESC
|
||||||
|
: TeamsOrdering.BY_CREATED_ON,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
@@ -1,27 +1,27 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
import { keyCloakSignIn } from "./common";
|
import { keyCloakSignIn } from './common';
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto("/");
|
await page.goto('/');
|
||||||
await keyCloakSignIn(page);
|
await keyCloakSignIn(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Teams", () => {
|
test.describe('Teams', () => {
|
||||||
test("checks all the elements are visible", async ({ page }) => {
|
test('checks all the elements are visible', async ({ page }) => {
|
||||||
const panel = page.getByLabel("Teams panel").first();
|
const panel = page.getByLabel('Teams panel').first();
|
||||||
|
|
||||||
await expect(panel.getByText("Recents")).toBeVisible();
|
await expect(panel.getByText('Recents')).toBeVisible();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
panel.getByRole("button", {
|
panel.getByRole('button', {
|
||||||
name: "Sort the teams",
|
name: 'Sort the teams',
|
||||||
}),
|
}),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
panel.getByRole("button", {
|
panel.getByRole('button', {
|
||||||
name: "Add a team",
|
name: 'Add a team',
|
||||||
}),
|
}),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
@@ -31,4 +31,31 @@ test.describe("Teams", () => {
|
|||||||
),
|
),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('check sort button', async ({ page }) => {
|
||||||
|
const panel = page.getByLabel('Teams panel').first();
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
await page.getByText('Team name').fill(`team-sort${i}`);
|
||||||
|
await page.getByRole('button', { name: 'Create a team' }).click();
|
||||||
|
await expect(
|
||||||
|
panel.locator('li').getByText(`team-sort${i}`),
|
||||||
|
).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
await panel
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'Sort the teams',
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
await expect(
|
||||||
|
panel
|
||||||
|
.locator('li')
|
||||||
|
.nth(i)
|
||||||
|
.getByText(`team-sort${i + 1}`),
|
||||||
|
).toBeVisible();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user