From 3be9f3200701b35373ffd450cea9ff7bcf55e652 Mon Sep 17 00:00:00 2001 From: IngramCapa Date: Sat, 15 Nov 2025 22:15:29 +0000 Subject: [PATCH 1/6] add dynamic property data fetching and update PropertyListing component --- api/getPropertiesData.js | 15 +++++++ api/index.js | 1 + .../PropertyListing/PropertyListing.jsx | 37 ++++++--------- .../tests/PropertyListing.spec.jsx | 45 ++++++++++++++++++- 4 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 api/getPropertiesData.js create mode 100644 api/index.js diff --git a/api/getPropertiesData.js b/api/getPropertiesData.js new file mode 100644 index 0000000..f91778e --- /dev/null +++ b/api/getPropertiesData.js @@ -0,0 +1,15 @@ +const PROPERTIES_URL = 'http://localhost:3000/api/properties'; + +export const getPropertiesData = async () => { + try { + const response = await fetch(PROPERTIES_URL); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error(`Error fetching properties:`, error); + return []; + } +}; diff --git a/api/index.js b/api/index.js new file mode 100644 index 0000000..c979625 --- /dev/null +++ b/api/index.js @@ -0,0 +1 @@ +export * from './getPropertiesData'; diff --git a/src/components/PropertyListing/PropertyListing.jsx b/src/components/PropertyListing/PropertyListing.jsx index ed43fb5..f37b5e3 100644 --- a/src/components/PropertyListing/PropertyListing.jsx +++ b/src/components/PropertyListing/PropertyListing.jsx @@ -1,32 +1,23 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import PropertyCard from '../PropertyCard'; import './PropertyListing.scss'; - -const DUMMY_PROPERTY = { - id: 73864112, - bedrooms: 3, - summary: 'Property 1 Situated moments from the River Thames in Old Chelsea...', - displayAddress: '1 CHEYNE WALK, CHELSEA, SW3', - propertyType: 'Flat', - price: 1950000, - branchName: 'M2 Property, London', - propertyUrl: '/property-for-sale/property-73864112.html', - contactUrl: '/property-for-sale/contactBranch.html?propertyId=73864112', - propertyTitle: '3 bedroom flat for sale', - mainImage: - 'https://media.rightmove.co.uk/dir/crop/10:9-16:9/38k/37655/53588679/37655_CAM170036_IMG_01_0000_max_476x317.jpg', -}; +import { getPropertiesData } from '../../../api/getPropertiesData'; const PropertyListing = () => { + const [properties, setProperties] = useState([]); + + useEffect(() => { + getPropertiesData() + .then((data) => setProperties(data)) + .catch((err) => console.error(err)); + }, []); return ( ); }; diff --git a/src/components/PropertyListing/tests/PropertyListing.spec.jsx b/src/components/PropertyListing/tests/PropertyListing.spec.jsx index 807d63b..08869a8 100644 --- a/src/components/PropertyListing/tests/PropertyListing.spec.jsx +++ b/src/components/PropertyListing/tests/PropertyListing.spec.jsx @@ -3,11 +3,52 @@ import { render, screen } from '@testing-library/react'; import { within } from '@testing-library/dom'; import PropertyListing from '../PropertyListing'; +const mockProperties = [ + { + id: 1, + bedrooms: 2, + summary: 'Summary for property 1.', + displayAddress: '123 Dummy Street, Testville', + propertyType: 'Detached', + price: 100000, + branchName: 'Test Branch', + propertyUrl: '/property-for-sale/property-1.html', + contactUrl: '/property-for-sale/contactBranch.html?propertyId=1', + propertyTitle: '2 bedroom detached house for sale', + mainImage: 'https://dummyimage.com/000000.jpg&text=Property+1', + }, + { + id: 2, + bedrooms: 3, + summary: 'Summary for property 2.', + displayAddress: '456 Example Avenue, Mocktown', + propertyType: 'Flat', + price: 200000, + branchName: 'Mock Branch', + propertyUrl: '/property-for-sale/property-2.html', + contactUrl: '/property-for-sale/contactBranch.html?propertyId=2', + propertyTitle: '3 bedroom flat for sale', + mainImage: 'https://dummyimage.com/000000.jpg&text=Property+2', + }, +]; + describe('PropertyListing', () => { - it('should render five property cards', async () => { + beforeEach(() => { + global.fetch = jest.fn(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve(mockProperties), + }) + ); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + it('should render the property cards correctly', async () => { render(); const propertiesList = screen.getByRole('list'); const propertyCards = await within(propertiesList).findAllByRole('listitem'); - expect(propertyCards).toHaveLength(5); + expect(propertyCards).toHaveLength(mockProperties.length); }); }); From 7155d8e306d2f2b69154f2c7c17e064831e375d6 Mon Sep 17 00:00:00 2001 From: IngramCapa Date: Sat, 15 Nov 2025 23:02:33 +0000 Subject: [PATCH 2/6] refactor: simplify data fetching --- src/components/PropertyListing/PropertyListing.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/PropertyListing/PropertyListing.jsx b/src/components/PropertyListing/PropertyListing.jsx index f37b5e3..954ef41 100644 --- a/src/components/PropertyListing/PropertyListing.jsx +++ b/src/components/PropertyListing/PropertyListing.jsx @@ -7,9 +7,11 @@ const PropertyListing = () => { const [properties, setProperties] = useState([]); useEffect(() => { - getPropertiesData() - .then((data) => setProperties(data)) - .catch((err) => console.error(err)); + const fetchData = async () => { + setProperties(await getPropertiesData()); + }; + + fetchData(); }, []); return (
    From 45e23e862e6c7cca583983c88555874c876374e0 Mon Sep 17 00:00:00 2001 From: IngramCapa Date: Sat, 15 Nov 2025 23:18:34 +0000 Subject: [PATCH 3/6] make PropertyListing accessible --- .../PropertyListing/PropertyListing.jsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/components/PropertyListing/PropertyListing.jsx b/src/components/PropertyListing/PropertyListing.jsx index 954ef41..4788a32 100644 --- a/src/components/PropertyListing/PropertyListing.jsx +++ b/src/components/PropertyListing/PropertyListing.jsx @@ -14,13 +14,16 @@ const PropertyListing = () => { fetchData(); }, []); return ( -
      - {properties.map((property, index) => ( -
    • - -
    • - ))} -
    +
    +

    Properties

    +
      + {properties.map((property, index) => ( +
    • + +
    • + ))} +
    +
    ); }; From 55cec010a5494cee7450b3530afa9a3f14b696fe Mon Sep 17 00:00:00 2001 From: IngramCapa Date: Sat, 15 Nov 2025 23:25:25 +0000 Subject: [PATCH 4/6] remove heading from PropertyListing component --- src/components/PropertyListing/PropertyListing.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/PropertyListing/PropertyListing.jsx b/src/components/PropertyListing/PropertyListing.jsx index 4788a32..371f346 100644 --- a/src/components/PropertyListing/PropertyListing.jsx +++ b/src/components/PropertyListing/PropertyListing.jsx @@ -15,7 +15,6 @@ const PropertyListing = () => { }, []); return (
    -

    Properties

      {properties.map((property, index) => (
    • From 6b0c1e0d6977d97900883a80066ea4e82f651c49 Mon Sep 17 00:00:00 2001 From: IngramCapa Date: Sat, 15 Nov 2025 23:35:17 +0000 Subject: [PATCH 5/6] refactor: add error handling and loading state in PropertyListing --- .../PropertyListing/PropertyListing.jsx | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/src/components/PropertyListing/PropertyListing.jsx b/src/components/PropertyListing/PropertyListing.jsx index 371f346..babe623 100644 --- a/src/components/PropertyListing/PropertyListing.jsx +++ b/src/components/PropertyListing/PropertyListing.jsx @@ -5,24 +5,60 @@ import { getPropertiesData } from '../../../api/getPropertiesData'; const PropertyListing = () => { const [properties, setProperties] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { - setProperties(await getPropertiesData()); + try { + setProperties(await getPropertiesData()); + } catch (err) { + setError('Failed to load properties'); + } finally { + setIsLoading(false); + } }; - fetchData(); }, []); + + if (error) { + return ( +
      + {error} +
      + ); + } + + if (!isLoading && properties.length === 0) { + return
      No properties found.
      ; + } + return ( -
      -
        - {properties.map((property, index) => ( -
      • - -
      • - ))} -
      -
      + <> + {isLoading ? ( +
      + Loading... +
      + ) : ( +
      +

      + Properties +

      +
        + {properties.map((property, index) => ( +
      • + +
      • + ))} +
      +
      + )} + ); }; From 5249474b7c7a942161330b7d5e92817face53017 Mon Sep 17 00:00:00 2001 From: IngramCapa Date: Sat, 15 Nov 2025 23:39:20 +0000 Subject: [PATCH 6/6] refactor: improve error handling in getPropertiesData and update PropertyListing styles --- api/getPropertiesData.js | 2 +- src/components/PropertyListing/PropertyListing.jsx | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/api/getPropertiesData.js b/api/getPropertiesData.js index f91778e..c2a26f1 100644 --- a/api/getPropertiesData.js +++ b/api/getPropertiesData.js @@ -10,6 +10,6 @@ export const getPropertiesData = async () => { return data; } catch (error) { console.error(`Error fetching properties:`, error); - return []; + throw error; } }; diff --git a/src/components/PropertyListing/PropertyListing.jsx b/src/components/PropertyListing/PropertyListing.jsx index babe623..ad2c694 100644 --- a/src/components/PropertyListing/PropertyListing.jsx +++ b/src/components/PropertyListing/PropertyListing.jsx @@ -49,7 +49,14 @@ const PropertyListing = () => {

      Properties

      -
        +
          {properties.map((property, index) => (