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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ next-env.d.ts
# IDEs
.vscode/
.idea/

.pnpm-store/
*storybook.log
storybook-static
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ node_modules
coverage
out
build
storybook-static
next-env.d.ts
*.tsbuildinfo

Expand Down
11 changes: 11 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineMain } from "@storybook/nextjs-vite/node";

export default defineMain({
stories: ["../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: ["@storybook/addon-mcp"],
framework: "@storybook/nextjs-vite",
staticDirs: ["../public"],
features: {
experimentalRSC: true,
},
});
27 changes: 27 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { definePreview } from "@storybook/nextjs-vite";
import { sb } from "storybook/test";
import "../app/globals.css";

sb.mock(import("../lib/session.ts"));
sb.mock(import("../lib/events/queries.ts"));
sb.mock(import("../lib/events/actions.ts"));
sb.mock(import("../lib/auth-client.ts"));
sb.mock(import("../lib/auth-config.ts"));
sb.mock(import("../lib/email-delivery.ts"));
sb.mock(import("../app/admin/users/actions.ts"));

export default definePreview({
addons: [],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
layout: "fullscreen",
nextjs: {
appDirectory: true,
},
},
});
6 changes: 6 additions & 0 deletions app/admin/users/__mocks__/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { fn } from "storybook/test";

const idleResult = () => ({ message: "", status: "idle" as const });

export const updateUserBan = fn(async () => idleResult());
export const updateUserRoles = fn(async () => idleResult());
12 changes: 10 additions & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ export const metadata: Metadata = {
"Meet people, share what you know, and find technology events around Sacramento.",
};

export default function Home() {
interface HomePageProps {
inviteLink?: string;
}

export function HomePage({ inviteLink }: HomePageProps) {
return (
<main className={style.page} id="main-content">
<section className={style.hero}>
Expand Down Expand Up @@ -119,9 +123,13 @@ export default function Home() {
in the local community.
</p>
</div>
<JoinCommunity inviteLink={process.env.NEXT_PUBLIC_INVITE_LINK} />
<JoinCommunity inviteLink={inviteLink} />
</div>
</section>
</main>
);
}

export default function Home() {
return <HomePage inviteLink={process.env.NEXT_PUBLIC_INVITE_LINK} />;
}
5 changes: 5 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
import storybook from "eslint-plugin-storybook";

import { defineConfig, globalIgnores } from "eslint/config";
import eslintConfigPrettier from "eslint-config-prettier/flat";
import nextVitals from "eslint-config-next/core-web-vitals";
Expand All @@ -11,7 +14,9 @@ export default defineConfig([
".netlify/**",
"out/**",
"build/**",
"storybook-static/**",
"next-env.d.ts",
]),
eslintConfigPrettier,
...storybook.configs["flat/recommended"],
]);
15 changes: 15 additions & 0 deletions lib/__mocks__/auth-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { fn } from "storybook/test";

const successfulResult = () => ({ data: null, error: null });

export const authClient = {
requestPasswordReset: fn(async () => successfulResult()),
resetPassword: fn(async () => successfulResult()),
signIn: {
email: fn(async () => successfulResult()),
},
signOut: fn(async () => successfulResult()),
signUp: {
email: fn(async () => successfulResult()),
},
};
8 changes: 8 additions & 0 deletions lib/__mocks__/auth-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { fn } from "storybook/test";

export const auth = {
api: {
getSession: fn(async () => null),
listUsers: fn(async () => ({ total: 0, users: [] })),
},
};
4 changes: 4 additions & 0 deletions lib/__mocks__/email-delivery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { fn } from "storybook/test";

export const isEmailDeliveryEnabled = fn(() => true);
export const isLocalDevelopmentEnvironment = fn(() => false);
42 changes: 42 additions & 0 deletions lib/__mocks__/session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { fn } from "storybook/test";

interface SessionLike {
user: {
role?: string | null;
};
}

function rolesFor(session: SessionLike | null) {
return new Set(
session?.user.role
?.split(",")
.map((role) => role.trim())
.filter(Boolean) ?? ["submitter"],
);
}

export const getCurrentSession = fn(async () => null);
export const requireSession = fn(async () => null);
export const requireAdminSession = fn(async () => null);
export const requireEventReviewerSession = fn(async () => null);

export const sessionIsAdmin = fn((session: SessionLike | null) =>
rolesFor(session).has("admin"),
);

export const sessionCanSubmitEvents = fn(
(session: SessionLike | null) => session !== null,
);

export const sessionCanCancelOwnEvents = fn(
(session: SessionLike | null) => session !== null,
);

export const sessionCanReviewEvents = fn((session: SessionLike | null) => {
const roles = rolesFor(session);
return roles.has("admin") || roles.has("approver");
});

export const sessionCanManageUsers = fn((session: SessionLike | null) =>
rolesFor(session).has("admin"),
);
10 changes: 10 additions & 0 deletions lib/events/__mocks__/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { fn } from "storybook/test";

const idleResult = () => ({ message: "", status: "idle" as const });

export const cancelEvent = fn(async () => idleResult());
export const inviteEventCollaborator = fn(async () => idleResult());
export const moderateEvent = fn(async () => idleResult());
export const moderateEventEdit = fn(async () => idleResult());
export const requestEventEdit = fn(async () => idleResult());
export const submitEvent = fn(async () => idleResult());
7 changes: 7 additions & 0 deletions lib/events/__mocks__/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { fn } from "storybook/test";

export const getApprovedEvents = fn(async () => []);
export const getManagedEventForEdit = fn(async () => null);
export const getPendingEventEdits = fn(async () => []);
export const getPendingEvents = fn(async () => []);
export const getSubmissionsForUser = fn(async () => []);
16 changes: 11 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
"typecheck": "next typegen && tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest",
"verify": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test && pnpm build",
"verify": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test && pnpm build-storybook && pnpm build",
"db:generate": "drizzle-kit generate",
"db:migrate": "netlify database migrations apply",
"db:status": "netlify database status",
"auth:generate": "BETTER_AUTH_SCHEMA_GENERATION=1 auth generate --config ./lib/auth-config.ts --output ./db/auth-schema.ts --yes",
"auth:create-admin": "auth create-admin --config ./lib/auth-config.ts"
"auth:create-admin": "auth create-admin --config ./lib/auth-config.ts",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@better-auth/drizzle-adapter": "1.7.1",
Expand Down Expand Up @@ -61,8 +63,8 @@
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^6.0.5",
"@vitest/browser": "5.0.0-beta.7",
"@vitest/browser-playwright": "5.0.0-beta.7",
"@vitest/browser": "5.0.0-rc.4",
"@vitest/browser-playwright": "5.0.0-rc.4",
"auth": "^1.7.1",
"drizzle-kit": "1.0.0-rc.4",
"eslint": "^9.31.0",
Expand All @@ -73,6 +75,10 @@
"prettier": "3.9.6",
"typescript": "^6.0.3",
"vite": "^8.2.1",
"vitest": "5.0.0-beta.7"
"vitest": "5.0.0-rc.4",
"storybook": "^10.5.10",
"@storybook/nextjs-vite": "^10.5.10",
"@storybook/addon-mcp": "^0.7.0",
"eslint-plugin-storybook": "^10.5.10"
}
}
Loading