Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ cd packages/web && pnpm dev

# API server
cd packages/server && pnpm dev

# Storybook
pnpm storybook:web
pnpm storybook:mobile
```

Tests use [Vitest](https://vitest.dev/). TDD is the standard workflow — write tests first, then implement. Test files are colocated with source files (e.g. `index.test.ts` next to `index.ts`). E2E tests use [Cypress](https://www.cypress.io/) and run against a Docker Compose stack in CI. [Stryker](https://stryker-mutator.io/) mutation testing runs on PRs to verify test quality.
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@
"e2e:web": "pnpm e2e:web:up && pnpm e2e:web:run; pnpm e2e:web:down",
"mobile": "pnpm --filter dofek-mobile start",
"mobile:device": "pnpm --filter dofek-mobile ios:device",
"storybook": "pnpm --filter dofek-web storybook",
"storybook:build": "pnpm --filter dofek-web build-storybook",
"storybook:web": "pnpm --filter dofek-web storybook",
"storybook:web:build": "pnpm --filter dofek-web build-storybook",
"storybook:mobile": "pnpm --filter dofek-mobile storybook",
"storybook:mobile:ios": "pnpm --filter dofek-mobile storybook:ios",
"size": "size-limit",
"depcruise": "depcruise --config .dependency-cruiser.cjs src/ packages/",
"spellcheck": "cspell --no-progress",
Expand Down
3 changes: 3 additions & 0 deletions packages/mobile/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@

# Node
node_modules/

# Storybook generated file
.rnstorybook/storybook.requires.ts
5 changes: 5 additions & 0 deletions packages/mobile/.rnstorybook/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { view } from "./storybook.requires";

const StorybookUIRoot = view.getStorybookUI({});

export default StorybookUIRoot;
8 changes: 8 additions & 0 deletions packages/mobile/.rnstorybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { StorybookConfig } from "@storybook/react-native";

const config: StorybookConfig = {
stories: ["../components/**/*.stories.?(ts|tsx|js|jsx)"],
addons: ["@storybook/addon-ondevice-actions", "@storybook/addon-ondevice-controls"],
};

export default config;
19 changes: 19 additions & 0 deletions packages/mobile/.rnstorybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Preview } from "@storybook/react-native";
import { View } from "react-native";

const preview: Preview = {
decorators: [
(Story) => (
<View style={{ flex: 1, backgroundColor: "#eef3ed", padding: 16 }}>
<Story />
</View>
),
],
parameters: {
controls: {
expanded: true,
},
},
};

export default preview;
25 changes: 25 additions & 0 deletions packages/mobile/components/Card.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Meta, StoryObj } from "@storybook/react-native";
import { Text } from "react-native";
import { Card } from "./Card";

const meta = {
title: "Layout/Card",
component: Card,
args: {
title: "Recovery",
children: <Text>Ready for a moderate workout today.</Text>,
},
} satisfies Meta<typeof Card>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {};

export const WithoutTitle: Story = {
args: {
title: undefined,
children: <Text>Cards can also render plain content with no section label.</Text>,
},
};
5 changes: 5 additions & 0 deletions packages/mobile/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if (process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === "true") {
module.exports = require("./.rnstorybook");
} else {
module.exports = require("expo-router/entry");
}
6 changes: 5 additions & 1 deletion packages/mobile/metro.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { getDefaultConfig } = require("expo/metro-config");
const { withStorybook } = require("@storybook/react-native/metro/withStorybook");
const fs = require("node:fs");
const path = require("node:path");

Expand Down Expand Up @@ -61,4 +62,7 @@ config.resolver.resolveRequest = (context, moduleName, platform) => {
return context.resolveRequest(enhanced, moduleName, platform);
};

module.exports = config;
module.exports = withStorybook(config, {
enabled: process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === "true",
configPath: path.resolve(projectRoot, ".rnstorybook"),
});
7 changes: 6 additions & 1 deletion packages/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
"name": "dofek-mobile",
"version": "0.1.0",
"private": true,
"main": "expo-router/entry",
"main": "index.js",
"scripts": {
"start": "expo start",
"storybook": "EXPO_PUBLIC_STORYBOOK_ENABLED=true sb-rn-get-stories && EXPO_PUBLIC_STORYBOOK_ENABLED=true expo start --clear",
"storybook:ios": "EXPO_PUBLIC_STORYBOOK_ENABLED=true expo run:ios",

Copilot AI Apr 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The storybook:ios script enables Storybook but does not run sb-rn-get-stories first. On a fresh checkout, .rnstorybook/storybook.requires.ts is gitignored and won't exist, so packages/mobile/.rnstorybook/index.tsx will fail to import ./storybook.requires. Consider adding sb-rn-get-stories to storybook:ios (or committing a non-generated stub) so the command works reliably.

Suggested change
"storybook:ios": "EXPO_PUBLIC_STORYBOOK_ENABLED=true expo run:ios",
"storybook:ios": "EXPO_PUBLIC_STORYBOOK_ENABLED=true sb-rn-get-stories && EXPO_PUBLIC_STORYBOOK_ENABLED=true expo run:ios",

Copilot uses AI. Check for mistakes.
"ios": "expo run:ios",
"ios:device": "expo run:ios --device",
"prebuild": "expo prebuild --platform ios --clean",
Expand Down Expand Up @@ -53,6 +55,9 @@
"zod": "3.25.76"
},
"devDependencies": {
"@storybook/addon-ondevice-actions": "10.3.1",
"@storybook/addon-ondevice-controls": "10.3.1",
"@storybook/react-native": "10.3.1",
"@testing-library/react": "16.3.2",
"@types/react": "19.2.14",
"typescript": "5.9.3"
Expand Down
20 changes: 20 additions & 0 deletions packages/web/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { StorybookConfig } from "@storybook/react-vite";

const config: StorybookConfig = {
stories: ["../src/**/*.stories.@(ts|tsx|js|jsx)"],
addons: ["@storybook/addon-a11y"],
framework: {
name: "@storybook/react-vite",
options: {
builder: {
viteConfigPath: ".storybook/vite.config.ts",
},
},
},
staticDirs: ["../src/public"],
docs: {
autodocs: "tag",
},
};

export default config;
16 changes: 16 additions & 0 deletions packages/web/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Preview } from "@storybook/react-vite";
import "../src/index.css";

const preview: Preview = {
parameters: {
layout: "centered",
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};

export default preview;
6 changes: 6 additions & 0 deletions packages/web/.storybook/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";

export default defineConfig({
plugins: [tailwindcss()],
});
7 changes: 6 additions & 1 deletion packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@dofek/auth": "workspace:*",
Expand Down Expand Up @@ -37,12 +39,15 @@
},
"devDependencies": {
"@sentry/vite-plugin": "5.1.1",
"@storybook/addon-a11y": "10.3.3",
"@storybook/react-vite": "10.3.3",
"@tanstack/router-cli": "1.166.7",
"@tanstack/router-plugin": "1.166.7",
"@types/leaflet": "1.9.21",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-react": "4.7.0",
"storybook": "10.3.3",
"typescript": "5.9.3",
"vite": "6.4.1"
}
Expand Down
29 changes: 29 additions & 0 deletions packages/web/src/components/PageSection.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { PageSection } from "./PageSection";

const meta = {
title: "Layout/PageSection",
component: PageSection,
tags: ["autodocs"],
args: {
title: "Training Summary",
subtitle: "Weekly trend and highlights from your recent sessions.",
children: <div className="text-sm text-dim">This content is rendered in the section body.</div>,
},
} satisfies Meta<typeof PageSection>;

export default meta;

type Story = StoryObj<typeof meta>;

export const WithCard: Story = {
args: {
card: true,
},
};

export const WithoutCard: Story = {
args: {
card: false,
},
};
Loading
Loading