diff --git a/src/components/partySelectionSearch/PartySelectionSearch.tsx b/src/components/partySelectionSearch/PartySelectionSearch.tsx index e6e7f9e1..26c1910a 100644 --- a/src/components/partySelectionSearch/PartySelectionSearch.tsx +++ b/src/components/partySelectionSearch/PartySelectionSearch.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Grid, Typography, Box, useTheme } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; @@ -45,13 +45,44 @@ export default function PartySelectionSearch({ partyTitle, }: Props) { const { t } = useTranslation(); - const [input, setInput] = useState(''); + const theme = useTheme(); + const [visibleParties, setVisibleParties] = useState>([]); const [filteredParties, setFilteredParties] = useState>(parties); + const containerRef = useRef(null); + const [searchQuery, setSearchQuery] = useState(''); - const theme = useTheme(); + useEffect(() => { + setVisibleParties(filteredParties.slice(0, 50)); + }, [filteredParties]); + + useEffect(() => { + const handleScroll = () => { + if ( + containerRef.current && + containerRef.current.scrollHeight - containerRef.current.scrollTop <= + containerRef.current.clientHeight + 20 && + filteredParties.length > visibleParties.length + ) { + // User has scrolled to the bottom, load more parties + loadMoreParties(); + } + }; + + containerRef.current?.addEventListener('scroll', handleScroll); + + return () => { + containerRef.current?.removeEventListener('scroll', handleScroll); + }; + }, [visibleParties, filteredParties, selectedParty]); + + const loadMoreParties = () => { + const remainingParties = filteredParties.slice(visibleParties.length); + const nextBatch = remainingParties.slice(0, 50); + setVisibleParties((prevParties) => [...prevParties, ...nextBatch]); + }; const onFilterChange = (value: string) => { - setInput(value); + setSearchQuery(value); if (!value) { setFilteredParties(parties); } else { @@ -82,7 +113,7 @@ export default function PartySelectionSearch({ label={label} iconMarginRight={iconMarginRight} onChange={(e) => onFilterChange(e.target.value)} - input={input} + input={searchQuery} clearField={() => onFilterChange('')} iconColor={iconColor} /> @@ -127,9 +158,12 @@ export default function PartySelectionSearch({ {t('partySelection.notFoundResults')} ) : ( - - {filteredParties && - filteredParties.map((party) => { + + {visibleParties && + visibleParties.map((party) => { const isDisabled = party.status === 'PENDING' || party.status === 'TOBEVALIDATED'; return ( diff --git a/src/components/partySelectionSearch/__tests__/PartySelectionSearch.test.tsx b/src/components/partySelectionSearch/__tests__/PartySelectionSearch.test.tsx index 1196ab25..60932bdc 100644 --- a/src/components/partySelectionSearch/__tests__/PartySelectionSearch.test.tsx +++ b/src/components/partySelectionSearch/__tests__/PartySelectionSearch.test.tsx @@ -1,10 +1,13 @@ -import { fireEvent, getByText, render, screen } from '@testing-library/react'; -import { Party } from '../../../model/Party'; +import { fireEvent, getByText, render, screen, waitFor } from '@testing-library/react'; +import { BaseParty, Party } from '../../../model/Party'; import PartyAccountItemSelection from '../PartyAccountItemSelection'; import PartySelectionSearch from '../PartySelectionSearch'; import './../../../locale'; +import { mockedBaseParties } from '../../../services/__mocks__/partyService'; +import React from 'react'; +import { renderWithProviders } from '../../../utils/test-utils'; -let selectedParty: Party | null = null; +let selectedParty: BaseParty | null = null; const parties: Array = [ { @@ -215,3 +218,39 @@ test('Test TOBEVALIDATED party', () => { screen.getByText('In attesa'); } }); + +test('Test disabled party', async () => { + const generateMockedParties = (N: number): Array => + Array.from({ length: N }, (_, index) => { + const partyId = `party-${index}`; + return { + partyId, + description: `Party ${index}`, + status: index % 2 === 0 ? 'ACTIVE' : 'PENDING', + userRole: index % 2 === 0 ? 'ADMIN' : 'LIMITED', + }; + }); + + const mockedBaseParties = generateMockedParties(60); + renderWithProviders( + (selectedParty = p)} + selectedParty={selectedParty} + /> + ); + + const party40 = screen.getByText('Party 40'); + expect(party40).toBeInTheDocument(); + + const party52 = screen.queryByText('Party 52'); + expect(party52).not.toBeInTheDocument(); + + const input = document.getElementById('search') as HTMLInputElement; + fireEvent.change(input, { target: { value: 'Party 5' } }); + expect(input.getAttribute('value')).toBe('Party 5'); + + expect(party40).not.toBeInTheDocument(); + + await waitFor(() => expect(screen.getByText('Party 52')).toBeInTheDocument()); +});