MoneyBox Product Explorer Test
This is my first React component so I have made use of AI assistance in designing the page structure, this has allowing me more time to understand how the page fits together and how I could refine the intial page to make more integrated rather than seperate.
It has been built on a Laravel Herd site with a React starter kit, however if that is not usable by yourselves I have listed the files below needed to be able to drop it into any system.
resources/js/components/money-box.tsx - initial working version of the page resources/js/components/money-box2.tsx - refined version, make the navigation part of the category obj
resources/js/pages/testpage.tsx - holding page for working component resources/js/pages/testpage2.tsx - holding page for combined component
Future improvements
-
I would change the system for a caraousel model instead so that the active product was always the one in the centre rather than it just activating in its current location,this would maintain a single focal point in the middle of the page rather that having it shift each time you changed category
-
I would adjust the colours of the products to make them uniform per category, that way it makes it easier to associate where you are in the system by the colour as well as written clues. This might require some ARIA checks to make sure that the colours arent going to cause problems for those who are colour blind.
-
Add in some help functionality to help people navigate the system, however unlike the app where it wasnt clear how to exit the help section once it was up there would be a clear X or close button to allow people to return to the main content, additionally we could do it as a modal pop up with overlay.
-
The short category description text, I would consider switching it out for a bit more detail when the category is active so that users know what they are looking at and can make an informed choice without needing to leave the page.
HOW TO USE
- Drop the files in a Next.js or Vite React app as app/components/MoneyboxExplorer.tsx (or .jsx)
- Ensure Tailwind CSS is configured. (Classes used only; no custom theme required.)
- Option A (live data): point fetchCatalog() at your CMS-backed API route (see example Next.js API route below).
Option B (demo): pass the provided sampleCatalog as the
catalogprop. - Import and render: <Moneybox(±2) catalog={sampleCatalog} />
NOTE: This component is UI-framework agnostic and uses only React + Tailwind + lucide-react icons.
EXAMPLE NEXT.JS API ROUTE (pages/api/catalog.ts)
Export a read-only catalog API so the Mobile App can GET the same data. Replace data source with your CMS client (Sanity, Contentful, Strapi, etc.).
import type { NextApiRequest, NextApiResponse } from 'next' export default async function handler(req: NextApiRequest, res: NextApiResponse) { // Example: fetch from a CMS. Pseudocode below – swap for your CMS SDK. // const cmsCatalog = await cms.fetchCatalog(); // res.status(200).json(cmsCatalog) res.status(200).json(sampleCatalog); }
SANITY (or STRAPI/CONTENTFUL) – minimal schema example for business editing Category schema { name: 'category', title: 'Category', type: 'document', fields: [ { name: 'name', title: 'Name', type: 'string' }, { name: 'slug', title: 'Slug', type: 'slug', options: { source: 'name' } }, { name: 'products', title: 'Products', type: 'array', of: [{ type: 'reference', to: [{ type: 'product' }] }] } ] }
Product schema { name: 'product', title: 'Product', type: 'document', fields: [ { name: 'name', title: 'Name', type: 'string' }, { name: 'description', title: 'Description', type: 'text' }, { name: 'image', title: 'Image', type: 'image' } ] }
GROQ query (Sanity) to serve catalog in the shape this component expects: *[_type == 'category'] | order(name asc) { 'id': _id, name, products[]->{ 'id': _id, name, 'imageUrl': image.asset->url, description } }
—————————————————————————————————————————————————
ACCESSIBILITY NOTES
Keyboard support: left/right arrows navigate categories; product panels are button/aria-expanded. Focus rings are visible; buttons have accessible labels.
—————————————————————————————————————————————————
TESTS (Jest + React Testing Library). Put in tests/Moneybox.test.tsx
These demonstrate the core requirements.
/* import { render, screen, fireEvent, within } from '@testing-library/react'; import Moneybox, { sampleCatalog } from '@/components/Moneybox';
describe('Moneybox', () => { test('renders all categories', () => { render(); sampleCatalog.categories.forEach(cat => { expect(screen.getByRole('button', { name: cat.name })).toBeInTheDocument(); }); });
test('shows products for active category and toggles accordion', () => { render(); const activeCat = sampleCatalog.categories[0]; const firstProductBtn = screen.getByRole('button', { name: activeCat.products[0].name }); fireEvent.click(firstProductBtn); expect(screen.getByText(activeCat.products[0].description)).toBeInTheDocument(); // closes fireEvent.click(firstProductBtn); expect(screen.queryByText(activeCat.products[0].description)).not.toBeInTheDocument(); });
test('carousel arrows switch categories', () => { render(); const next = screen.getByRole('button', { name: /Next category/i }); fireEvent.click(next); expect(screen.getByRole('button', { name: sampleCatalog.categories[1].name })).toHaveAttribute('aria-pressed', 'true'); });
test('image fallback is displayed when imageUrl is missing', () => { render(); const firstProductBtn = screen.getByRole('button', { name: sampleCatalog.categories[0].products[0].name }); fireEvent.click(firstProductBtn); // There should be an inline SVG icon – query by title is not present, so count the img elements equals 0 const panel = screen.getByRole('button', { name: sampleCatalog.categories[0].products[0].name }).parentElement?.nextSibling as HTMLElement; expect(within(panel).queryAllByRole('img').length).toBe(0); }); }); */
————————————————————————————————————————————————————
DESIGN THEME The Tailwind styles implement the provided wireframe (header bar, explore bar with arrows, three category pills, and a central card with dropdown rows revealing image+description). Visuals are intentionally clean and production‑ready while staying close to the mock.