diff --git a/package-lock.json b/package-lock.json index e28e7226..b2995c76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "dotenv": "^16.4.5", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.2.1", "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", "redux": "^5.0.1" @@ -6709,6 +6710,14 @@ "react": "^18.3.1" } }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 3a0a7c04..7616426c 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "dotenv": "^16.4.5", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.2.1", "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", "redux": "^5.0.1" diff --git a/public/avatar.jpg b/public/avatar.jpg new file mode 100644 index 00000000..f1ba6777 Binary files /dev/null and b/public/avatar.jpg differ diff --git a/public/cart.png b/public/cart.png new file mode 100644 index 00000000..4b621c72 Binary files /dev/null and b/public/cart.png differ diff --git a/public/down.png b/public/down.png new file mode 100644 index 00000000..757f34fb Binary files /dev/null and b/public/down.png differ diff --git a/public/edit.png b/public/edit.png new file mode 100644 index 00000000..ece0eb72 Binary files /dev/null and b/public/edit.png differ diff --git a/public/hamburger.png b/public/hamburger.png new file mode 100644 index 00000000..87c88444 Binary files /dev/null and b/public/hamburger.png differ diff --git a/public/heart.png b/public/heart.png new file mode 100644 index 00000000..fd0ab902 Binary files /dev/null and b/public/heart.png differ diff --git a/public/help.png b/public/help.png new file mode 100644 index 00000000..9bcf4ab5 Binary files /dev/null and b/public/help.png differ diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 00000000..aa929921 Binary files /dev/null and b/public/logo.png differ diff --git a/public/moon.png b/public/moon.png new file mode 100644 index 00000000..b8680814 Binary files /dev/null and b/public/moon.png differ diff --git a/public/settings.png b/public/settings.png new file mode 100644 index 00000000..bc7ae2b2 Binary files /dev/null and b/public/settings.png differ diff --git a/public/signout.png b/public/signout.png new file mode 100644 index 00000000..e59c6532 Binary files /dev/null and b/public/signout.png differ diff --git a/public/user.png b/public/user.png new file mode 100644 index 00000000..df33b675 Binary files /dev/null and b/public/user.png differ diff --git a/src/__test__/navbar.test.tsx b/src/__test__/navbar.test.tsx new file mode 100644 index 00000000..91c1012c --- /dev/null +++ b/src/__test__/navbar.test.tsx @@ -0,0 +1,123 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import { describe, it, expect } from 'vitest'; +import Navbar from '@/components/Navbar'; + +describe('Navbar Component', () => { + it('renders Navbar component', () => { + render( + + + + ); + + const logo = screen.getByAltText(/logo/i); + expect(logo).toBeInTheDocument(); + + const title = screen.getByText(/dynamites/i); + expect(title).toBeInTheDocument(); + + const homeLink = screen.getByText(/Home/i); + expect(homeLink).toBeInTheDocument(); + + const shopLink = screen.getByText(/Shop/i); + expect(shopLink).toBeInTheDocument(); + + const aboutLink = screen.getByText(/About us/i); + expect(aboutLink).toBeInTheDocument(); + + const contactLink = screen.getAllByText(/Contact/i)[0]; + expect(contactLink).toBeInTheDocument(); + + const cartIcon = screen.getByTitle('cart'); + expect(cartIcon).toBeInTheDocument(); + + const avatar = screen.getByAltText(/profile/i); + expect(avatar).toBeInTheDocument(); + + const username = screen.getByText(/amanda green/i); + expect(username).toBeInTheDocument(); + }); + + it('highlights the correct navigation link based on the current route', () => { + render( + + + + ); + + expect(screen.getByText('Shop')).toHaveClass( + 'border-b-[2px] border-primary text-primary' + ); + }); + + it('toggles menu on hamburger icon click', () => { + render( + + + + ); + + const hamburgerIcon = screen.getByTitle('hamburger'); + fireEvent.click(hamburgerIcon); + + expect(screen.getAllByText(/home/i)[0]).toBeInTheDocument(); + }); + + it('renders links with correct paths', () => { + render( + + + + ); + + const homeLink = screen.getByText(/home/i); + expect(homeLink.closest('a')).toHaveAttribute('href', '/'); + + const shopLink = screen.getByText(/shop/i); + expect(shopLink.closest('a')).toHaveAttribute('href', '/shop'); + + const aboutLink = screen.getByText(/about us/i); + expect(aboutLink.closest('a')).toHaveAttribute('href', '/about'); + + const contactLink = screen.getAllByText(/contact/i)[0]; + expect(contactLink.closest('a')).toHaveAttribute('href', '/contact'); + }); + + it('displays cart item count', () => { + render( + + + + ); + + const cartCount = screen.getByText(/5/i); + expect(cartCount).toBeInTheDocument(); + }); + + it('renders profile options on avatar click', () => { + render( + + + + ); + + const profileIcon = screen.getByTitle('toggleProfile'); + fireEvent.click(profileIcon); + + const editProfileOption = screen.getByText(/edit profile/i); + expect(editProfileOption).toBeInTheDocument(); + + const preferencesOption = screen.getByText(/preferences/i); + expect(preferencesOption).toBeInTheDocument(); + + const nightModeOption = screen.getByText(/night mode/i); + expect(nightModeOption).toBeInTheDocument(); + + const helpCenterOption = screen.getByText(/help center/i); + expect(helpCenterOption).toBeInTheDocument(); + + const signOutOption = screen.getByText(/sign out/i); + expect(signOutOption).toBeInTheDocument(); + }); +}); diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx new file mode 100644 index 00000000..b7cbb604 --- /dev/null +++ b/src/components/Navbar.tsx @@ -0,0 +1,153 @@ +import { Link, useLocation } from 'react-router-dom'; +import { useState } from 'react'; +import { LuShoppingCart } from 'react-icons/lu'; +import { FiHeart } from 'react-icons/fi'; +import { FaAngleDown } from 'react-icons/fa6'; +import { RxHamburgerMenu } from 'react-icons/rx'; + +function Navbar() { + const location = useLocation(); + const [toggleMenu, setToggleMenu] = useState(false); + const [toggleProfileMenu, setToggleProfileMenu] = useState(false); + return ( +
+ setToggleMenu(!toggleMenu)} + /> +
+ logo +

Dynamites

+
+ +
+
+
+ +
+ 5 +
+
+ +
+
+
+ profile +
+

Amanda Green

+ setToggleProfileMenu(!toggleProfileMenu)} + /> +
+
+ {toggleMenu && ( +
+ Home + Shop + About + Contact +
+
+ edit +

Edit profile

+
+
+ settings +

Preferences

+
+
+
+
+ moon +

Night mode

+
+
+
+
+
+
+ help +

Help center

+
+
+ signout +

Sign out

+
+
+ )} + {toggleProfileMenu && ( +
+
+
+ edit +

Edit profile

+
+
+ settings +

Preferences

+
+
+
+
+ moon +

Night mode

+
+
+
+
+
+
+ help +

Help center

+
+
+ signout +

Sign out

+
+
+ )} +
+ ); +} + +export default Navbar; diff --git a/src/layout/HomeLayout.tsx b/src/layout/HomeLayout.tsx index 4917b997..bca7eabb 100644 --- a/src/layout/HomeLayout.tsx +++ b/src/layout/HomeLayout.tsx @@ -1,13 +1,16 @@ import { Outlet } from 'react-router-dom'; +import Navbar from '@/components/Navbar'; function HomeLayout() { return (
-
Nav
+
+ +
-
Fotter
+
Footer
); } diff --git a/tailwind.config.js b/tailwind.config.js index f63f318e..7b75751c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,7 +5,7 @@ export default { xs: '320px', sm: '640px', md: '768px', - lg: '1440px', + lg: '1024px', }, extend: { colors: { @@ -18,7 +18,9 @@ export default { grayDark: '#CCD0D8', grayLight: '#F3F4F6', redBg: '#DC2627', - black: '#171A1F', + textBlack: '#171A1F', + lightGrey: '#DEE1E6', + grey: '#565D6D', }, fontFamily: { Lexend: ['Lexend'],