diff --git a/src/App.jsx b/src/App.jsx index aefa21e..cbc5359 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -41,12 +41,13 @@ export function App() { * the shopping lists that the user has access to. * Check ./api/firestore.js for its implementation. */ - const lists = useShoppingLists(userId, userEmail); + const { listsData, areListsLoading } = useShoppingLists(userId, userEmail); /** * This custom hook takes our token and fetches the data for our list. * Check ./api/firestore.js for its implementation. */ - const data = useShoppingListData(listPath); + const { shoppingListData, isShoppingListLoading } = + useShoppingListData(listPath); return ( @@ -57,7 +58,8 @@ export function App() { index element={ + } /> diff --git a/src/api/firebase.js b/src/api/firebase.js index 2a25a8a..848fc67 100644 --- a/src/api/firebase.js +++ b/src/api/firebase.js @@ -22,8 +22,8 @@ import { getFutureDate, sortByDaysBetweenDates } from '../utils/dates.js'; export function useShoppingLists(userId, userEmail) { // Start with an empty array for our data. const initialState = []; - const [data, setData] = useState(initialState); - const [isLoading, setIsLoading] = useState(true); + const [listsData, setListsData] = useState(initialState); + const [areListsLoading, setAreListsLoading] = useState(true); useEffect(() => { // If we don't have a userId or userEmail (the user isn't signed in), @@ -40,12 +40,13 @@ export function useShoppingLists(userId, userEmail) { // We keep the list's id and path so we can use them later. return { name: listRef.id, path: listRef.path }; }); - setData(newData); + setListsData(newData); + setAreListsLoading(false); } }); }, [userId, userEmail]); - return data; + return { listsData, areListsLoading }; } /** @@ -58,8 +59,8 @@ export function useShoppingListData(listPath) { // Start with an empty array for our data. /** @type {import('firebase/firestore').DocumentData[]} */ const initialState = []; - const [data, setData] = useState(initialState); - const [loading, setLoading] = useState(true); + const [shoppingListData, setData] = useState(initialState); + const [isShoppingListLoading, setIsShoppingListLoading] = useState(true); useEffect(() => { if (!listPath) return; @@ -81,12 +82,12 @@ export function useShoppingListData(listPath) { // Update our React state with the new data. setData(nextData); - setLoading(false); + setIsShoppingListLoading(false); }); }, [listPath]); // Return the data so it can be used by our React components. - return { data, loading }; + return { shoppingListData, isShoppingListLoading }; } /** diff --git a/src/components/AddItemForm.jsx b/src/components/AddItemForm.jsx index 5fade81..3b1160a 100644 --- a/src/components/AddItemForm.jsx +++ b/src/components/AddItemForm.jsx @@ -18,8 +18,8 @@ export default function AddItemForm({ listPath, data }) { const [itemDuration, setItemDuration] = useState(7); const normalizedItemNames = useMemo(() => { - return data?.data.map((item) => normalizeInput(item.name)); - }, [data?.data]); + return data?.map((item) => normalizeInput(item.name)); + }, [data]); const { isSuccess, diff --git a/src/utils/dates.js b/src/utils/dates.js index 31b7286..db2a3cd 100644 --- a/src/utils/dates.js +++ b/src/utils/dates.js @@ -79,7 +79,7 @@ export function sortByDaysBetweenDates(data) { let inactiveArr = []; const alphabetSort = (a, b) => a.name.localeCompare(b.name); - data.data.forEach((item) => { + data.forEach((item) => { const daysBetween = getDaysBetweenDates( item.dateLastPurchased?.toDate(), item.dateNextPurchased?.toDate(), diff --git a/src/views/Home.jsx b/src/views/Home.jsx index 30120aa..bef586b 100644 --- a/src/views/Home.jsx +++ b/src/views/Home.jsx @@ -1,8 +1,13 @@ import './Home.css'; import AddListForm from '../components/AddListForm.jsx'; import SelectListForm from '../components/SelectListForm.jsx'; -export function Home({ data, setListPath, listPath, listName }) { - console.log(data); +export function Home({ + data, + areListsLoading, + setListPath, + listPath, + listName, +}) { return (

Current List:

@@ -12,11 +17,15 @@ export function Home({ data, setListPath, listPath, listName }) {

----- OR -----

- + {areListsLoading ? ( +
Loading lists...
+ ) : ( + + )}
); } diff --git a/src/views/List.jsx b/src/views/List.jsx index 5bccfdb..86fd176 100644 --- a/src/views/List.jsx +++ b/src/views/List.jsx @@ -5,7 +5,7 @@ import './List.css'; import AddItemForm from '../components/AddItemForm'; import ShareForm from '../components/ShareForm'; -export function List({ data, listPath }) { +export function List({ data, isShoppingListLoading, listPath, listName }) { const [input, setInput] = useState(''); const sortedItems = comparePurchaseUrgency(data); @@ -28,13 +28,6 @@ export function List({ data, listPath }) { setInput(''); } - const listName = listPath?.substring(listPath.indexOf('/') + 1); - - if (data.data.loading) { - // If data is not loaded yet, render a loading indicator - return

Loading...

; - } - if (!listPath) { return ( <> @@ -43,13 +36,18 @@ export function List({ data, listPath }) { ); } + if (isShoppingListLoading) { + // If data is not loaded yet, render a loading indicator + return

Loading...

; + } + return ( <>

{listName}

- {data.data.length === 0 && ( + {data.length === 0 && (

You have no items in this list!

)} - {data.data.length !== 0 && ( + {data.length !== 0 && (
e.preventDefault()}> @@ -68,7 +66,7 @@ export function List({ data, listPath }) { ))}
- {data.data.length > 0 && filteredItems.length === 0 && ( + {data.length > 0 && filteredItems.length === 0 && (

No match found for that filter query.

diff --git a/tests/components/AddItemForm/AddItemForm.test.jsx b/tests/components/AddItemForm/AddItemForm.test.jsx index 1ec2e10..f49574c 100644 --- a/tests/components/AddItemForm/AddItemForm.test.jsx +++ b/tests/components/AddItemForm/AddItemForm.test.jsx @@ -22,7 +22,7 @@ describe('AddItemForm', () => { }; it('renders AddItemForm', () => { - const data = { data: [{ name: 'apple' }], loading: false }; + const data = [{ name: 'apple' }]; renderAddItemForm({ listPath: '/test', data }); }); @@ -36,7 +36,7 @@ describe('AddItemForm', () => { totalPurchases: 0, }; - const data = { data: [{ name: 'apple' }], loading: false }; + const data = [{ name: 'apple' }]; const mockedAddItem = vi.spyOn(FirebaseFunctions, 'addItem'); mockedAddItem.mockImplementationOnce(async () => { @@ -68,7 +68,7 @@ describe('AddItemForm', () => { }); it('Adds item but get an error', async () => { - const data = { data: [{ name: 'apple' }], loading: false }; + const data = [{ name: 'apple' }]; const mockedAddItem = vi.spyOn(FirebaseFunctions, 'addItem'); @@ -101,7 +101,7 @@ describe('AddItemForm', () => { }); it('Tries to add duplicate item but gets error', async () => { - const data = { data: [{ name: 'apple' }], loading: false }; + const data = [{ name: 'apple' }]; const mockedAddItem = vi.spyOn(FirebaseFunctions, 'addItem'); @@ -126,7 +126,7 @@ describe('AddItemForm', () => { expect(mockedAddItem).toHaveBeenCalledTimes(0); }); it('Tries to add empty string but gets error', async () => { - const data = { data: [{ name: 'apple' }], loading: false }; + const data = [{ name: 'apple' }]; const mockedAddItem = vi.spyOn(FirebaseFunctions, 'addItem'); diff --git a/tests/components/List/List.test.jsx b/tests/components/List/List.test.jsx index f6237ac..be66c1b 100644 --- a/tests/components/List/List.test.jsx +++ b/tests/components/List/List.test.jsx @@ -10,17 +10,27 @@ describe('List', () => { vi.clearAllMocks(); }); - const renderList = ({ listPath, data }) => { + const renderList = ({ data, isShoppingListLoading, listPath, listName }) => { return render( - + , ); }; it('renders AddItemForm and ShareForm if listPath is passed in', async () => { - const data = { data: [{ name: 'apple' }], loading: false }; - renderList({ listPath: '/test-list', data }); + const listProps = { + data: [{ name: 'apple' }], + isShoppingListLoading: false, + listPath: '/test-list', + listName: 'test-list', + }; + renderList(listProps); const addItemForm = screen.queryByTestId('addItemForm-header'); const shareForm = screen.queryByTestId('shareForm-header'); @@ -32,8 +42,13 @@ describe('List', () => { }); it('renders error message when listPath is not passed in', async () => { - const data = { data: [] }; - renderList({ listPath: '', data }); + const listProps = { + data: [{ name: 'apple' }], + isShoppingListLoading: false, + listPath: '', + listName: 'test-list', + }; + renderList(listProps); const addItemForm = screen.queryByTestId('addItemForm-header'); const shareForm = screen.queryByTestId('shareForm-header'); @@ -45,8 +60,13 @@ describe('List', () => { }); it('renders error message when items are not passed in', async () => { - const data = { data: [] }; - renderList({ listPath: '/test-list', data }); + const listProps = { + data: [], + isShoppingListLoading: false, + listPath: '/test-list', + listName: 'test-list', + }; + renderList(listProps); const errorMessage = screen.getByTestId('noItemsErrorMsg'); expect(errorMessage).toBeInTheDocument();