diff --git a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/OfferItemsContainer.js b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/OfferItemsContainer.js index c24f1101b..9c3ed7bce 100644 --- a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/OfferItemsContainer.js +++ b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/OfferItemsContainer.js @@ -69,8 +69,6 @@ const OfferItemsContainer = ({ return (overflowY === "scroll" || overflowY === "auto") && node.scrollHeight > node.clientHeight; }, []); - // BUG: there is no refetching of new offers when the initial_limit is not enough - const refetchTriggerRef = useCallback((node) => { if (node) setOfferResultsWrapperNode(node.parentElement); }, []); diff --git a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/OfferItemsContainer.spec.js b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/OfferItemsContainer.spec.js index 36cd8afb8..63efeb277 100644 --- a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/OfferItemsContainer.spec.js +++ b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/OfferItemsContainer.spec.js @@ -1,35 +1,20 @@ -import { createTheme } from "@material-ui/core"; import { getByRole, getByText, screen } from "@testing-library/react"; import React from "react"; -import { renderWithStoreAndTheme } from "../../../../test-utils"; +import { render } from "../../../../test-utils"; import Offer from "../Offer/Offer"; import OfferItemsContainer from "./OfferItemsContainer"; describe("OfferItemsContainer", () => { - const theme = createTheme(); - const initialState = {}; - - beforeEach(() => { - // IntersectionObserver isn't available in test environment - const mockIntersectionObserver = jest.fn(); - mockIntersectionObserver.mockReturnValue({ - observe: () => null, - unobserve: () => null, - disconnect: () => null, - }); - window.IntersectionObserver = mockIntersectionObserver; - }); - describe("render", () => { it("should show loading state when loading", () => { - renderWithStoreAndTheme( + render( {}} toggleShowSearchFilters={() => {}} setShouldFetchMoreOffers={() => {}} - />, { initialState, theme } + /> ); expect(screen.getAllByTestId("offer-item-loading")).toHaveLength(3); }); @@ -60,13 +45,13 @@ describe("OfferItemsContainer", () => { }), ]; - renderWithStoreAndTheme( + render( {}} toggleShowSearchFilters={() => {}} - />, { initialState, theme } + /> ); const items = await screen.findAllByTestId("offer-item"); expect(items).toHaveLength(2); diff --git a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsDesktop.js b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsDesktop.js index 28da53071..9058ecd16 100644 --- a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsDesktop.js +++ b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsDesktop.js @@ -61,6 +61,9 @@ OffersList.propTypes = { showSearchFilters: PropTypes.bool.isRequired, toggleShowSearchFilters: PropTypes.func.isRequired, offers: PropTypes.arrayOf(PropTypes.instanceOf(Offer)), + moreOffersLoading: PropTypes.bool, + loadMoreOffers: PropTypes.func, + searchQueryToken: PropTypes.string, }; const OfferWidgetSection = ({ diff --git a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsMobile.js b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsMobile.js index 97044921a..3b293e713 100644 --- a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsMobile.js +++ b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsMobile.js @@ -55,6 +55,8 @@ OffersList.propTypes = { toggleShowSearchFilters: PropTypes.func.isRequired, offers: PropTypes.arrayOf(PropTypes.instanceOf(Offer)), moreOffersLoading: PropTypes.bool, + loadMoreOffers: PropTypes.func, + searchQueryToken: PropTypes.string, }; export const OfferViewer = ({ diff --git a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsMobile.spec.js b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsMobile.spec.js index 888a0ec6c..f04435236 100644 --- a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsMobile.spec.js +++ b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsMobile.spec.js @@ -2,7 +2,7 @@ import React from "react"; import SearchResultsMobile from "./SearchResultsMobile"; import Offer from "../Offer/Offer"; import { createTheme } from "@material-ui/core/styles"; -import { renderWithStoreAndTheme, screen } from "../../../../test-utils"; +import { render, renderWithStoreAndTheme, screen } from "../../../../test-utils"; import { createMatchMedia } from "../../../../utils/media-queries"; import { waitForElementToBeRemoved } from "@testing-library/dom"; import { Simulate } from "react-dom/test-utils"; @@ -39,27 +39,14 @@ describe("SearchResultsMobile", () => { }), ]; - beforeEach(() => { - // IntersectionObserver isn't available in test environment - const mockIntersectionObserver = jest.fn(); - mockIntersectionObserver.mockReturnValue({ - observe: () => null, - unobserve: () => null, - disconnect: () => null, - }); - window.IntersectionObserver = mockIntersectionObserver; - }); - describe("render", () => { it("Should render offers if present", () => { - const initialState = {}; - const context = { offers }; - renderWithStoreAndTheme( + render( - , { initialState, theme } + ); expect(screen.getByRole("button", { name: "Adjust Filters" })).toBeInTheDocument(); expect(screen.getByText("position1")).toBeInTheDocument(); diff --git a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsUtils.js b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsUtils.js index df5eda469..64949b6eb 100644 --- a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsUtils.js +++ b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsUtils.js @@ -1,4 +1,4 @@ export const SearchResultsConstants = { - INITIAL_LIMIT: 2, - FETCH_NEW_OFFERS_LIMIT: 1, + INITIAL_LIMIT: 15, + FETCH_NEW_OFFERS_LIMIT: 10, }; diff --git a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsWidget.spec.js b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsWidget.spec.js index 51de96e20..33d41bc85 100644 --- a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsWidget.spec.js +++ b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/SearchResultsWidget.spec.js @@ -38,17 +38,6 @@ describe("SearchResults", () => { }, }; - beforeEach(() => { - // IntersectionObserver isn't available in test environment - const mockIntersectionObserver = jest.fn(); - mockIntersectionObserver.mockReturnValue({ - observe: () => null, - unobserve: () => null, - disconnect: () => null, - }); - window.IntersectionObserver = mockIntersectionObserver; - }); - it("should display OfferItemsContainer", () => { renderWithStoreAndTheme( diff --git a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/useOffersSearcher.js b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/useOffersSearcher.js index 3002b7621..7a06d1cda 100644 --- a/src/components/HomePage/SearchResultsArea/SearchResultsWidget/useOffersSearcher.js +++ b/src/components/HomePage/SearchResultsArea/SearchResultsWidget/useOffersSearcher.js @@ -1,5 +1,5 @@ -import { useCallback, useState } from "react"; -import { useDispatch } from "react-redux"; +import { useCallback, useState, useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; import { resetOffersFetchError, setLoadingOffers, @@ -16,12 +16,26 @@ import { export default (filters) => { const dispatch = useDispatch(); + const searchQueryToken = useSelector((state) => state.offerSearch.queryToken); - // TODO: Move this to redux!!! const [hasMoreOffers, setHasMoreOffers] = useState(true); const [moreOffersFetchError, setMoreOffersFetchError] = useState(null); const [moreOffersLoading, setMoreOffersLoading] = useState(false); + // The "search" and "loadMoreOffers" functions do not share the same state; + // When we run "setHasMoreOffers(false)" on the "loadMoreOffers" function, + // the "search" function does not know that the "hasMoreOffers" variable has changed; + // In the same way, when we run "setHasMoreOffers(true) on the "search" function, + // the "loadMoreOffers" function does not know that the "hasMoreOffers" variable has changed; + // Then, when the "loadMoreOffers" function is executed after a previous execution where the + // "hasMoreOffers" variable became "false", the state of this variable is still false, which + // prevents the fetching of new offers. + // Knowing that, I needed a way to set the "hasMoreOffers" variable to "true" when the previous fact + // happens: setting the "hasMoreOffers" to true when the "queryToken" (which is stored in redux) changes + useEffect(() => { + setHasMoreOffers(true); + }, [searchQueryToken]); + // isInitialRequest = true on the first time the search request is made // the following request will have isInitialRequest = false const loadOffers = useCallback((isInitialRequest) => async (queryToken, limit) => {