Skip to content

Commit a827fff

Browse files
New events and meetups (#2230)
## Description Refreshed the events page according to Figma. I'll do a follow up PR with the `Add an event`, issue template, and possibly "load more" instead of a scrollview, because this is big enough already and I'd rather keep the convo about the event proposal issue template separate from the code review of this page. https://github.com/user-attachments/assets/c6c38a21-f30b-4afc-85b3-1f65f1a04991 --------- Co-authored-by: Jonathan Brennan <jonathanawesome@users.noreply.github.com>
1 parent 21011b1 commit a827fff

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3192
-553
lines changed

.github/workflows/check.yml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Lint and check formatting
1+
name: lint & test
22

33
on: pull_request
44

@@ -26,6 +26,23 @@ jobs:
2626
- name: Validate code snippets
2727
run: pnpm validate:snippets
2828

29+
unit-test:
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@v4
33+
34+
- uses: the-guild-org/shared-config/setup@main
35+
name: setup env
36+
with:
37+
packageManager: pnpm
38+
workingDirectory: ./
39+
40+
- name: Install Dependencies
41+
run: pnpm i
42+
43+
- name: Run unit tests
44+
run: pnpm test:unit
45+
2946
playwright:
3047
runs-on: ubuntu-latest
3148
steps:
@@ -46,12 +63,9 @@ jobs:
4663
- name: Install Playwright Browsers
4764
run: ./node_modules/.bin/playwright install --with-deps
4865

49-
- name: Run Playwright tests
66+
- name: Run end-to-end tests
5067
run: ./node_modules/.bin/playwright test
5168

52-
- name: Run unit tests
53-
run: pnpm test:unit
54-
5569
- uses: actions/upload-artifact@v4
5670
if: ${{ !cancelled() }}
5771
with:

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,5 @@ out/
6565
tsconfig.tsbuildinfo
6666

6767
playwright-report/
68+
69+
.pnpm-store/

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"@hasparus/lezer-json-shikified": "1.1.3",
3636
"@headlessui/react": "^2.2.4",
3737
"@igorkowalczyk/is-browser": "^5.1.0",
38-
"@lezer/highlight": "1.2.1",
38+
"@lezer/highlight": "^1.2.1",
3939
"@next/bundle-analyzer": "^15.4.5",
4040
"@plaiceholder/next": "^3.0.0",
4141
"@sparticuz/chromium": "^138.0.2",

playwright.config.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,35 @@ export default defineConfig({
88
outputDir: "./test/out",
99
fullyParallel: true,
1010
forbidOnly: !!process.env.CI,
11-
retries: process.env.CI ? 2 : 0,
11+
retries: process.env.CI ? 2 : 1,
1212
workers: process.env.CI ? 1 : undefined,
13-
reporter: "html",
13+
reporter: process.env.CI ? [["github"], ["html"]] : "list",
1414
use: {
1515
baseURL: "http://localhost:3000",
16-
trace: "on-first-retry",
16+
trace: "retain-on-first-failure",
17+
screenshot: "only-on-failure",
1718
},
1819

20+
timeout: 60 * 1000,
21+
1922
projects: [
2023
{
2124
name: "chromium",
22-
use: { ...devices["Desktop Chrome"] },
25+
use: {
26+
...devices["Desktop Chrome"],
27+
channel: "chromium",
28+
...(process.env.CI
29+
? {
30+
args: [
31+
"--enable-gpu",
32+
"--use-gl=angle",
33+
"--use-angle=gl-egl",
34+
"--ignore-gpu-blocklist",
35+
"--enable-unsafe-swiftshader",
36+
],
37+
}
38+
: {}),
39+
},
2340
},
2441
],
2542

src/app/(development)/layout.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from "react"
22
import { notFound } from "next/navigation"
33
import { NewFontsStyleTag } from "../fonts"
44

5-
// @ts-expect-error: we want to import the same version as Nextra for the main page
65
import { ThemeProvider } from "next-themes"
76

87
import "../colors.css"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ReactNode } from "react"
2+
3+
export function BenefitCard({
4+
title,
5+
description,
6+
icon,
7+
}: {
8+
title: string
9+
description: string
10+
icon: ReactNode
11+
}) {
12+
return (
13+
<article className="flex h-full flex-col gap-6 border border-neu-200 bg-neu-0 p-6 text-left dark:border-neu-100">
14+
{icon}
15+
<div className="flex flex-col gap-3 text-neu-900">
16+
<h3 className="text-[20px] font-normal leading-tight">{title}</h3>
17+
<p className="typography-body-md text-neu-800">{description}</p>
18+
</div>
19+
</article>
20+
)
21+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import UsersIcon from "@/app/conf/_design-system/pixelarticons/users.svg?svgr"
2+
import CommentIcon from "@/app/conf/_design-system/pixelarticons/comment.svg?svgr"
3+
import SlidersIcon from "@/app/conf/_design-system/pixelarticons/sliders.svg?svgr"
4+
import EyeIcon from "@/app/conf/_design-system/pixelarticons/eye.svg?svgr"
5+
6+
import { BenefitCard } from "./benefit-card"
7+
8+
export function BenefitsSection() {
9+
return (
10+
<section className="gql-section">
11+
<div className="mx-auto max-w-3xl text-center">
12+
<h2 className="typography-h2 text-balance">
13+
Benefits of getting involved
14+
</h2>
15+
<p className="typography-body-lg mt-4 text-balance text-neu-800 lg:mt-6">
16+
Contributing to GraphQL means more than writing code — it’s a chance
17+
to collaborate, share ideas, and shape the future of the ecosystem.
18+
</p>
19+
</div>
20+
21+
<div className="mt-10 grid gap-4 md:grid-cols-2 lg:mt-16 xl:grid-cols-4">
22+
<BenefitCard
23+
icon={<UsersIcon aria-hidden className="size-10 text-sec-darker" />}
24+
title="Valuable networking opportunities"
25+
description="Engage in conversations and hands-on projects to deepen your understanding of GraphQL."
26+
/>
27+
<BenefitCard
28+
icon={<CommentIcon aria-hidden className="size-10 text-sec-darker" />}
29+
title="Collaborate with others"
30+
description="Connect with contributors and teams building GraphQL tools and platforms."
31+
/>
32+
<BenefitCard
33+
icon={<SlidersIcon aria-hidden className="size-10 text-sec-darker" />}
34+
title="Help guide the spec"
35+
description="Share ideas, give feedback, or participate in working groups to influence the future of GraphQL."
36+
/>
37+
<BenefitCard
38+
icon={<EyeIcon aria-hidden className="size-10 text-sec-darker" />}
39+
title="Connect in real life"
40+
description="Put a face to the handle — meet contributors in person at events and meetups. Build lasting connections beyond the screen."
41+
/>
42+
</div>
43+
</section>
44+
)
45+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Button } from "../../../conf/_design-system/button"
2+
import { StripesDecoration } from "../../../conf/_design-system/stripes-decoration"
3+
import { DISCORD_CHANNEL_LINK } from "./links"
4+
5+
export function BringGraphQLToYourCommunity() {
6+
return (
7+
<section className="gql-section gql-container">
8+
<div className="relative flex gap-8 border border-sec-dark bg-sec-lighter px-6 py-10 dark:border-sec-base/40 dark:bg-sec-darker/20 max-lg:flex-col sm:px-10 md:gap-12 lg:flex-row lg:items-center lg:gap-16 lg:px-16 xl:gap-24">
9+
<Stripes />
10+
<div>
11+
<h2 className="typography-h2">Bring GraphQL to your community</h2>
12+
<p className="typography-body-lg mt-6">
13+
Learn how to start a local initiative and create your own – host
14+
events, share knowledge, and grow the GraphQL community where you
15+
live.
16+
</p>
17+
</div>
18+
<div className="mt-auto flex shrink-0 flex-col gap-2 lg:w-[324px]">
19+
<Button
20+
href={
21+
"#"
22+
// TODO: Where does this link? Docs?
23+
}
24+
variant="primary"
25+
>
26+
Learn more
27+
</Button>
28+
<Button
29+
href={DISCORD_CHANNEL_LINK}
30+
variant="tertiary"
31+
className="[.light_&]:bg-white"
32+
>
33+
Start GraphQL Local
34+
</Button>
35+
</div>
36+
</div>
37+
</section>
38+
)
39+
}
40+
41+
function Stripes() {
42+
const mask = "linear-gradient(20deg, transparent 80%, rgb(0 0 0 / 0.6))"
43+
return (
44+
<div
45+
className="absolute inset-0"
46+
role="presentation"
47+
style={{
48+
maskImage: mask,
49+
WebkitMaskImage: mask,
50+
}}
51+
>
52+
<StripesDecoration oddClassName="bg-gradient-to-b from-sec-dark to-sec-base/10" />
53+
</div>
54+
)
55+
}

src/components/events/event-card.tsx renamed to src/app/(main)/community/events/event-card.tsx

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { clsx } from "clsx"
33

44
import { CalendarIcon } from "@/app/conf/_design-system/pixelarticons/calendar-icon"
55
import { PinIcon } from "@/app/conf/_design-system/pixelarticons/pin-icon"
6-
import { Tag } from "../../app/conf/_design-system/tag"
6+
import { Tag } from "@/app/conf/_design-system/tag"
7+
import { eventTagColors } from "./event-filter-tag"
78

89
const dateFormatter = new Intl.DateTimeFormat("en", {
910
day: "numeric",
@@ -55,6 +56,7 @@ export interface EventCardProps {
5556
name: ReactNode
5657
meta?: ReactNode
5758
official?: boolean
59+
kind: "meetup" | "conference" | "working-group"
5860
}
5961

6062
export function EventCard({
@@ -64,43 +66,43 @@ export function EventCard({
6466
name,
6567
meta,
6668
official,
69+
kind,
6770
}: EventCardProps) {
6871
const dateLabel = formatDateLabel(date)
6972
const parsedDate = normaliseDate(date)
70-
7173
return (
7274
<a
7375
href={href}
7476
className={clsx(
75-
"gql-focus-visible group flex min-w-[260px] flex-col overflow-hidden border border-neu-200 bg-neu-0 text-left text-current no-underline ring-neu-400 hover:bg-sec-base/[.035] hover:ring-1 hover:ring-offset-1 hover:ring-offset-neu-0 dark:border-neu-100 dark:ring-neu-100 xs:min-w-[352px]",
77+
"gql-focus-visible group flex min-h-[214px] min-w-[260px] flex-col overflow-hidden border border-neu-200 text-left text-current no-underline ring-neu-400 hover:ring-1 hover:ring-offset-1 hover:ring-offset-neu-0 dark:border-neu-50 dark:ring-neu-100 xs:min-w-[352px]",
78+
"[--bg-opacity:0.05] hover:[--bg-opacity:0.07] dark:[--bg-opacity:0.03] hover:dark:[--bg-opacity:0.06]",
79+
80+
"z-[4]",
81+
kind === "meetup" &&
82+
"bg-[hsl(var(--color-sec-base)/var(--bg-opacity))]",
83+
kind === "conference" &&
84+
"bg-[hsl(var(--color-pri-base)/var(--bg-opacity))] dark:bg-white/5",
85+
kind === "working-group" &&
86+
"bg-[hsl(229deg_100%_70.4%_/_var(--bg-opacity))]",
7687
)}
7788
target="_blank"
7889
rel="noreferrer"
7990
>
8091
<div className="flex flex-1 flex-col">
8192
<div
8293
className={clsx(
83-
"flex items-center justify-between gap-2 px-2 text-neu-700 dark:text-neu-600 xs:px-4",
94+
"flex h-[45px] items-center justify-between gap-2 px-2 text-neu-800 dark:text-neu-600 xs:px-4",
8495
meta
85-
? "border-b border-neu-200 py-2.5 dark:border-neu-100"
96+
? "border-b border-neu-200 py-2.5 dark:border-neu-50"
8697
: "-mb-2 pt-2 xs:-mb-4 xs:pt-3",
8798
)}
8899
>
100+
<Tag color={eventTagColors[kind]}>{kind}</Tag>
89101
{meta ? (
90-
<span className="typography-body-md font-medium">{meta}</span>
102+
<span className="typography-body-md">{meta}</span>
91103
) : (
92104
<span className="sr-only">Official GraphQL Local</span>
93105
)}
94-
{official ? (
95-
<Tag color="hsl(var(--color-pri-base))" className="*:gap-1">
96-
<span className="font-sans" aria-hidden>
97-
98-
</span>
99-
Official
100-
</Tag>
101-
) : meta ? null : (
102-
<div className="h-[22px]" />
103-
)}
104106
</div>
105107

106108
<div className="typography-h3 flex min-h-[100px] flex-1 flex-col justify-center px-2 py-3 text-neu-900 xs:min-h-[124px] xs:px-4 xs:py-6">
@@ -109,15 +111,15 @@ export function EventCard({
109111

110112
<div
111113
className={clsx(
112-
"flex flex-wrap border-t border-neu-200 text-neu-700 dark:border-neu-100",
114+
"flex flex-wrap border-t border-neu-200 text-neu-800 dark:border-neu-50",
113115
dateLabel && city
114-
? "grid grid-cols-2 divide-x divide-neu-200 dark:divide-neu-100"
116+
? "grid grid-cols-2 divide-x divide-neu-200 dark:divide-neu-50"
115117
: "",
116118
)}
117119
>
118120
{dateLabel && (
119-
<div className="typography-body-sm flex items-center gap-1 px-2 py-1.5 text-neu-700 dark:text-neu-600 xs:gap-1.5 xs:px-4 xs:py-2.5">
120-
<CalendarIcon className="size-4 shrink-0 translate-y-[-.5px] text-neu-600 dark:text-neu-500 xs:size-5" />
121+
<div className="typography-body-sm flex items-center gap-1 px-2 py-1.5 text-neu-800 dark:text-neu-600 xs:gap-1.5 xs:px-4 xs:py-2.5">
122+
<CalendarIcon className="size-4 shrink-0 translate-y-[-.5px] text-neu-800 dark:text-neu-500 xs:size-5" />
121123
{parsedDate ? (
122124
<time dateTime={parsedDate.toISOString()}>{dateLabel}</time>
123125
) : (
@@ -126,8 +128,8 @@ export function EventCard({
126128
</div>
127129
)}
128130
{city && (
129-
<div className="typography-body-sm flex items-center gap-1.5 whitespace-pre px-2 py-1.5 text-neu-700 dark:text-neu-600 xs:px-4 xs:py-2.5">
130-
<PinIcon className="size-4 shrink-0 translate-y-[-.5px] text-neu-600 dark:text-neu-500 xs:size-5" />
131+
<div className="typography-body-sm flex items-center gap-1.5 whitespace-pre px-2 py-1.5 text-neu-800 dark:text-neu-600 xs:px-4 xs:py-2.5">
132+
<PinIcon className="size-4 shrink-0 translate-y-[-.5px] text-neu-800 dark:text-neu-500 xs:size-5" />
131133
{city}
132134
</div>
133135
)}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Tag } from "@/app/conf/_design-system/tag"
2+
import { CheckboxIcon } from "@/app/conf/_design-system/pixelarticons/checkbox-icon"
3+
import clsx from "clsx"
4+
5+
export type EventKind = "meetup" | "conference" | "working-group"
6+
7+
export const eventTagColors = {
8+
conference: "hsl(var(--color-pri-base))",
9+
meetup: "hsl(var(--color-sec-dark))",
10+
"working-group": "#6883FF",
11+
}
12+
13+
export interface EventFilterTagProps
14+
extends Omit<React.HTMLAttributes<HTMLLabelElement>, "onChange"> {
15+
kind: EventKind
16+
checked: boolean
17+
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
18+
}
19+
20+
export function EventFilterTag({
21+
kind,
22+
checked,
23+
onChange,
24+
...rest
25+
}: EventFilterTagProps) {
26+
return (
27+
<label
28+
{...rest}
29+
className="cursor-pointer select-none hover:opacity-90 hover:ring hover:ring-neu-100 dark:hover:ring-neu-50"
30+
>
31+
<input
32+
type="checkbox"
33+
className="sr-only"
34+
onChange={onChange}
35+
checked={checked}
36+
/>
37+
<Tag
38+
color={eventTagColors[kind]}
39+
className="flex py-[3px] pl-[3px] *:gap-1"
40+
>
41+
<CheckboxIcon
42+
checked={checked}
43+
style={{
44+
color: eventTagColors[kind],
45+
}}
46+
/>
47+
{kind.replace("-", " ")}
48+
</Tag>
49+
</label>
50+
)
51+
}

0 commit comments

Comments
 (0)