♻️(frontend) delete infinite scroll

In order to be in agreement with the back, we must modify the front
so as to no longer have infinite scroll in the team part.
It is also necessary to modify the tests accordingly
This commit is contained in:
Nathan Panchout
2024-10-30 20:35:10 +01:00
parent 7d695ab81c
commit 20a19d36b5
7 changed files with 61 additions and 158 deletions

View File

@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.

View File

@@ -1,11 +1,6 @@
import {
DefinedInitialDataInfiniteOptions,
InfiniteData,
QueryKey,
useInfiniteQuery,
} from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import { APIError, APIList, errorCauses, fetchAPI } from '@/api';
import { APIError, errorCauses, fetchAPI } from '@/api';
import { Team } from '../types';
@@ -17,18 +12,14 @@ export enum TeamsOrdering {
export type TeamsParams = {
ordering: TeamsOrdering;
};
type TeamsAPIParams = TeamsParams & {
page: number;
};
type TeamsResponse = APIList<Team>;
type TeamsResponse = Team[];
export const getTeams = async ({
ordering,
page,
}: TeamsAPIParams): Promise<TeamsResponse> => {
const orderingQuery = ordering ? `&ordering=${ordering}` : '';
const response = await fetchAPI(`teams/?page=${page}${orderingQuery}`);
}: TeamsParams): Promise<TeamsResponse> => {
const orderingQuery = ordering ? `?ordering=${ordering}` : '';
const response = await fetchAPI(`teams/${orderingQuery}`);
if (!response.ok) {
throw new APIError('Failed to get the teams', await errorCauses(response));
@@ -39,33 +30,9 @@ export const getTeams = async ({
export const KEY_LIST_TEAM = 'teams';
export function useTeams(
param: TeamsParams,
queryConfig?: DefinedInitialDataInfiniteOptions<
TeamsResponse,
APIError,
InfiniteData<TeamsResponse>,
QueryKey,
number
>,
) {
return useInfiniteQuery<
TeamsResponse,
APIError,
InfiniteData<TeamsResponse>,
QueryKey,
number
>({
initialPageParam: 1,
queryKey: [KEY_LIST_TEAM, param],
queryFn: ({ pageParam }) =>
getTeams({
...param,
page: pageParam,
}),
getNextPageParam(lastPage, allPages) {
return lastPage.next ? allPages.length + 1 : undefined;
},
...queryConfig,
export function useTeams(params: TeamsParams) {
return useQuery({
queryKey: [KEY_LIST_TEAM, params],
queryFn: () => getTeams(params),
});
}

View File

@@ -23,10 +23,7 @@ describe('PanelTeams', () => {
});
it('renders with no team to display', async () => {
fetchMock.mock(`end:/teams/?page=1&ordering=-created_at`, {
count: 0,
results: [],
});
fetchMock.mock(`end:/teams/?ordering=-created_at`, []);
render(<TeamList />, { wrapper: AppWrapper });
@@ -40,16 +37,13 @@ describe('PanelTeams', () => {
});
it('renders an empty team', async () => {
fetchMock.mock(`end:/teams/?page=1&ordering=-created_at`, {
count: 1,
results: [
{
id: '1',
name: 'Team 1',
accesses: [],
},
],
});
fetchMock.mock(`end:/teams/?ordering=-created_at`, [
{
id: '1',
name: 'Team 1',
accesses: [],
},
]);
render(<TeamList />, { wrapper: AppWrapper });
@@ -59,21 +53,18 @@ describe('PanelTeams', () => {
});
it('renders a team with only 1 member', async () => {
fetchMock.mock(`end:/teams/?page=1&ordering=-created_at`, {
count: 1,
results: [
{
id: '1',
name: 'Team 1',
accesses: [
{
id: '1',
role: 'owner',
},
],
},
],
});
fetchMock.mock(`end:/teams/?ordering=-created_at`, [
{
id: '1',
name: 'Team 1',
accesses: [
{
id: '1',
role: 'owner',
},
],
},
]);
render(<TeamList />, { wrapper: AppWrapper });
@@ -83,25 +74,22 @@ describe('PanelTeams', () => {
});
it('renders a non-empty team', () => {
fetchMock.mock(`end:/teams/?page=1&ordering=-created_at`, {
count: 1,
results: [
{
id: '1',
name: 'Team 1',
accesses: [
{
id: '1',
role: 'admin',
},
{
id: '2',
role: 'member',
},
],
},
],
});
fetchMock.mock(`end:/teams/?ordering=-created_at`, [
{
id: '1',
name: 'Team 1',
accesses: [
{
id: '1',
role: 'admin',
},
{
id: '2',
role: 'member',
},
],
},
]);
render(<TeamList />, { wrapper: AppWrapper });
@@ -109,7 +97,7 @@ describe('PanelTeams', () => {
});
it('renders the error', async () => {
fetchMock.mock(`end:/teams/?page=1&ordering=-created_at`, {
fetchMock.mock(`end:/teams/?ordering=-created_at`, {
status: 500,
});
@@ -125,10 +113,7 @@ describe('PanelTeams', () => {
});
it('renders with team panel open', async () => {
fetchMock.mock(`end:/teams/?page=1&ordering=-created_at`, {
count: 1,
results: [],
});
fetchMock.mock(`end:/teams/?ordering=-created_at`, []);
render(<Panel />, { wrapper: AppWrapper });
@@ -140,10 +125,7 @@ describe('PanelTeams', () => {
});
it('closes and opens the team panel', async () => {
fetchMock.get(`end:/teams/?page=1&ordering=-created_at`, {
count: 1,
results: [],
});
fetchMock.get(`end:/teams/?ordering=-created_at`, []);
render(<Panel />, { wrapper: AppWrapper });

View File

@@ -1,9 +1,8 @@
import { Loader } from '@openfun/cunningham-react';
import React, { useMemo, useRef } from 'react';
import React, { useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, Text } from '@/components';
import { InfiniteScroll } from '@/components/InfiniteScroll';
import { Team, useTeams } from '@/features/teams/team-management';
import { useTeamStore } from '../store';
@@ -57,39 +56,14 @@ const TeamListState = ({ isLoading, isError, teams }: PanelTeamsStateProps) => {
export const TeamList = () => {
const ordering = useTeamStore((state) => state.ordering);
const {
data,
isError,
isLoading,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useTeams({
const { data, isError, isLoading } = useTeams({
ordering,
});
const containerRef = useRef<HTMLDivElement>(null);
const teams = useMemo(() => {
return data?.pages.reduce((acc, page) => {
return acc.concat(page.results);
}, [] as Team[]);
}, [data?.pages]);
return (
<Box $css="overflow-y: auto; overflow-x: hidden;" ref={containerRef}>
<InfiniteScroll
hasMore={hasNextPage}
isLoading={isFetchingNextPage}
next={() => {
void fetchNextPage();
}}
scrollContainer={containerRef.current}
as="ul"
$margin={{ top: 'none' }}
$padding="none"
role="listbox"
>
<TeamListState isLoading={isLoading} isError={isError} teams={teams} />
</InfiniteScroll>
<TeamListState isLoading={isLoading} isError={isError} teams={data} />
</Box>
);
};

View File

@@ -26,9 +26,9 @@ const payloadGetTeams = {
};
const mockApiRequests = (page: Page) => {
void page.route('**/teams/?page=1&ordering=-created_at', (route) => {
void page.route('**/teams/?ordering=-created_at', (route) => {
void route.fulfill({
json: payloadGetTeams,
json: payloadGetTeams.results,
});
});
};

View File

@@ -1,7 +1,5 @@
import { expect, test } from '@playwright/test';
import { waitForElementCount } from '../helpers';
import { createTeam, keyCloakSignIn } from './common';
test.beforeEach(async ({ page, browserName }) => {
@@ -31,13 +29,13 @@ test.describe('Teams Panel', () => {
test('checks the sort button', async ({ page }) => {
const responsePromiseSortDesc = page.waitForResponse(
(response) =>
response.url().includes('/teams/?page=1&ordering=-created_at') &&
response.url().includes('/teams/?ordering=-created_at') &&
response.status() === 200,
);
const responsePromiseSortAsc = page.waitForResponse(
(response) =>
response.url().includes('/teams/?page=1&ordering=created_at') &&
response.url().includes('/teams/?ordering=created_at') &&
response.status() === 200,
);
@@ -62,24 +60,6 @@ test.describe('Teams Panel', () => {
expect(responseSortDesc.ok()).toBeTruthy();
});
test('checks the infinite scroll', async ({ page, browserName }) => {
test.setTimeout(90000);
const panel = page.getByLabel('Teams panel').first();
const randomTeams = await createTeam(
page,
'team-infinite',
browserName,
40,
);
await expect(panel.locator('li')).toHaveCount(20);
await panel.getByText(randomTeams[24]).click();
await waitForElementCount(panel.locator('li'), 21, 10000);
expect(await panel.locator('li').count()).toBeGreaterThan(20);
});
test('checks the hover and selected state', async ({ page, browserName }) => {
const panel = page.getByLabel('Teams panel').first();
await createTeam(page, 'team-hover', browserName, 2);

View File

@@ -3492,7 +3492,7 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
"@types/node@*":
"@types/node@*", "@types/node@20.16.10":
version "20.16.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.10.tgz#0cc3fdd3daf114a4776f54ba19726a01c907ef71"
integrity sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==
@@ -3509,7 +3509,7 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6"
integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==
"@types/react-dom@*":
"@types/react-dom@*", "@types/react-dom@18.3.0":
version "18.3.0"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0"
integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==
@@ -8485,7 +8485,7 @@ safe-regex-test@^1.0.3:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sass@^1.80.3:
sass@1.80.3:
version "1.80.3"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.80.3.tgz#3f63dd527647d2b3de35f36acb971bda80517423"
integrity sha512-ptDWyVmDMVielpz/oWy3YP3nfs7LpJTHIJZboMVs8GEC9eUmtZTZhMHlTW98wY4aEorDfjN38+Wr/XjskFWcfA==
@@ -9259,7 +9259,7 @@ typed-array-length@^1.0.6:
is-typed-array "^1.1.13"
possible-typed-array-names "^1.0.0"
typescript@*, typescript@^5.0.4:
typescript@*, typescript@5.6.2, typescript@^5.0.4:
version "5.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0"
integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==