Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions app/Navbar.css
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
nav {
background-color: rgba(75, 207, 141, 0.473);
color: black;
}

ul {
list-style-type: none;
margin: 1rem;
padding: 0;
display: flex;
justify-content: space-around;
}

li {
padding: 15px;
}

a {
color: black;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}
nav {
background-color: rgba(75, 207, 141, 0.473);
color: black;
}
ul {
list-style-type: none;
margin: 1rem;
padding: 0;
display: flex;
justify-content: space-around;
}
li {
padding: 15px;
}
a {
color: black;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
44 changes: 22 additions & 22 deletions app/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import React from 'react'
import './Navbar.css'

type navbarProps = {
links: { text: string; url: string }[]
}

const Navbar: React.FC<navbarProps> = ({ links }) => {
return (
<nav>
<ul>
{links.map((link, index) => (
<li key={index}>
<a href={link.url}>{link.text}</a>
</li>
))}
</ul>
</nav>
)
}

export default Navbar
import React from 'react';
import './Navbar.css';
type navbarProps = {
links: { text: string; url: string }[];
};
const Navbar: React.FC<navbarProps> = ({ links }) => {
return (
<nav>
<ul>
{links.map((link, index) => (
<li key={index}>
<a href={link.url}>{link.text}</a>
</li>
))}
</ul>
</nav>
);
};
export default Navbar;
6 changes: 1 addition & 5 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,7 @@ p {
align-items: end;
padding: 1.5rem 1.5rem;
background:
linear-gradient(
15deg,
transparent 0 22%,
rgba(75, 194, 188, 0.44) 82% 100%
),
linear-gradient(15deg, transparent 0 22%, rgba(75, 194, 188, 0.44) 82% 100%),
var(--accent-soft);
}

Expand Down
12 changes: 7 additions & 5 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@ import Navbar from './Navbar'

export const metadata: Metadata = {
title: 'SideCause',
description: 'Complete community service projects and level up!',
description:
'Complete community service projects and level up!',
}


const navLinks = [
{ text: 'SideCause', url: '/' },
{ text: 'Posts', url: '/' },
{ text: 'Login', url: '/' },
]
{ text: 'Posts', url: '/posts' },
{ text: 'Login', url: '/' }
];

export default function RootLayout({
children,
}: Readonly<{ children: ReactNode }>) {
return (
<html lang="en">
<body>
<body>
<div>
<Navbar links={navLinks} />
</div>
Expand Down
37 changes: 37 additions & 0 deletions app/posts/PostsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import styles from './posts.module.css'

export type PostPreview = {
id: string
title: string
location: string
}

type PostsListProps = {
posts: PostPreview[]
}

export function PostsList({ posts }: PostsListProps) {
if (posts.length === 0) {
return (
<p className={styles.emptyState} role="status">
No posts
</p>
)
}

return (
<ul className={styles.postsList} aria-label="Posts">
{posts.map((post) => (
<li className={styles.postCard} key={post.id}>
<div>
<h2>{post.title}</h2>
<p>{post.location}</p>
</div>
<button disabled type="button">
View details
</button>
</li>
))}
</ul>
)
}
26 changes: 26 additions & 0 deletions app/posts/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { PostsList } from './PostsList'
import { samplePosts } from './post-data'
import styles from './posts.module.css'

export default function PostsPage() {
return (
<main className={styles.postsPage}>
<section className={styles.postsBoard} aria-labelledby="posts-title">
<h1 className={styles.postsTitle} id="posts-title">
Posts
</h1>
<div className={styles.controls} aria-label="Post browsing controls">
<button disabled type="button">
Filters
</button>
<button disabled type="button">
Chico, CA
</button>
</div>
<div className={styles.listViewport}>
<PostsList posts={samplePosts} />
</div>
</section>
</main>
)
}
24 changes: 24 additions & 0 deletions app/posts/post-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { PostPreview } from './PostsList'

export const samplePosts: PostPreview[] = [
{
id: 'cedar-grove-cleanup',
title: 'Clean up Cedar Grove',
location: 'Chico, CA',
},
{
id: 'food-pantry-sorting',
title: 'Sort food pantry donations',
location: 'Downtown Chico, CA',
},
{
id: 'community-garden',
title: 'Water the community garden',
location: 'South Chico, CA',
},
{
id: 'trailhead-tidy',
title: 'Tidy the trailhead',
location: 'Upper Park, Chico, CA',
},
]
105 changes: 105 additions & 0 deletions app/posts/posts.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
.postsPage {
width: min(100%, 80rem);
margin: 0 auto;
padding: 1.5rem;
}

.postsBoard {
padding: 1.25rem;
border: 1px solid var(--ink);
background: #fbfaf5;
}

.postsTitle {
margin: 0;
font-size: clamp(2.25rem, 9vw, 4rem);
}

.controls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
margin: 1.25rem 0;
}

.controls button,
.postCard button {
font: inherit;
color: var(--ink);
border: 1px solid var(--ink);
border-radius: 0;
background: var(--canvas);
}

.controls button {
min-height: 2.75rem;
padding: 0.5rem 0.75rem;
text-align: left;
}

.controls button:disabled,
.postCard button:disabled {
cursor: not-allowed;
opacity: 1;
}

.listViewport {
height: clamp(19rem, 58dvh, 30rem);
overflow-y: auto;
border-top: 1px solid var(--ink);
border-bottom: 1px solid var(--ink);
scrollbar-color: var(--accent) var(--canvas);
}

.postsList {
display: grid;
gap: 0.75rem;
margin: 0;
padding: 0.75rem 0;
list-style: none;
}

.postCard {
display: flex;
align-items: end;
justify-content: space-between;
gap: 1rem;
min-height: 7.5rem;
padding: 1rem;
border: 1px solid var(--ink);
background: #fffefa;
}

.postCard h2 {
margin: 0;
font-size: clamp(1.25rem, 4vw, 1.75rem);
}

.postCard p {
margin-top: 0.5rem;
color: var(--muted);
}

.postCard button {
flex: 0 0 auto;
padding: 0.5rem 0.75rem;
white-space: nowrap;
}

.emptyState {
display: grid;
height: 100%;
margin: 0;
place-items: center;
color: var(--muted);
}

@media (min-width: 48rem) {
.postsPage {
padding: 3rem;
}

.postsBoard {
padding: 1.5rem;
}
}
8 changes: 0 additions & 8 deletions next-env.d.ts

This file was deleted.

29 changes: 29 additions & 0 deletions tests/e2e/posts-navigation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { expect, test } from '@playwright/test'

test('opens the Posts page from the homepage navigation', async ({ page }) => {
await page.goto('/')

await page.getByRole('link', { name: 'Posts' }).click()

await expect(page).toHaveURL('/posts')
await expect(page.getByRole('heading', { name: 'Posts' })).toBeVisible()
})

test('expands the posts board for desktop while keeping it within a narrow viewport', async ({
page,
}) => {
await page.setViewportSize({ width: 1440, height: 900 })
await page.goto('/posts')

const board = page.locator('section[aria-labelledby="posts-title"]')
const desktopBoard = await board.boundingBox()

expect(desktopBoard?.width).toBeGreaterThan(1000)

await page.setViewportSize({ width: 320, height: 900 })

const narrowBoard = await board.boundingBox()

expect(narrowBoard?.width).toBeGreaterThan(200)
expect(narrowBoard?.width).toBeLessThanOrEqual(320)
})
Loading
Loading