diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76d5c311..4684bb7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,11 @@ jobs: restore-keys: | vite-task-${{ runner.os }}-${{ runner.arch }}- + # apps/propel-docs type-checks its demos against @makeplane/propel's built + # dist (its published `exports`), and dist is gitignored — so pack propel + # before the workspace check, or those imports can't resolve (TS2307). + - run: vp run @makeplane/propel#build + - run: vp run -w check # Storybook stories run as browser tests (Vitest + Playwright), so CI needs # the Chromium binary + its system deps. `pnpm` isn't on PATH under diff --git a/.gitignore b/.gitignore index f1e57bc0..64d46c8d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,13 @@ dist-ssr # Storybook build output storybook-static +# Astro build cache +.astro + # Test coverage coverage # Playwright MCP output .playwright-mcp + +docs/superpowers/* \ No newline at end of file diff --git a/apps/propel-docs/README.md b/apps/propel-docs/README.md new file mode 100644 index 00000000..91ac2292 --- /dev/null +++ b/apps/propel-docs/README.md @@ -0,0 +1,55 @@ +# @makeplane/propel-docs + +The documentation site for `@makeplane/propel`'s components. Built with Astro 7, +React islands (via `@astrojs/react`), and MDX for the component pages, styled +with propel's own Tailwind v4 tokens (`@makeplane/propel/styles`). + +## Commands + +Run these from the repo root: + +- `pnpm --filter @makeplane/propel-docs dev` — starts the dev server. In dev + mode, `astro.config.mjs` aliases `@makeplane/propel/components/*` and + `@makeplane/propel/styles` directly to propel's `src`, so editing a propel + component hot-reloads this site instantly — no propel build needed. +- `pnpm --filter @makeplane/propel-docs build` — production build. This first + runs `vp pack` on `@makeplane/propel` (to produce its `dist`), then runs + `astro build`, so the site resolves propel through its real, published + `dist` exports — the same way a consumer of the package would. +- `pnpm --filter @makeplane/propel-docs typecheck` — runs `astro check` (this + also packs propel first, for the same reason as `build`). +- `pnpm --filter @makeplane/propel-docs preview` — serves the built `dist` + output locally for a final check before deploying. + +## IMPORTANT: the deploy seam + +The production build resolves `@makeplane/propel` through its built `dist`, +and `packages/propel/dist` is gitignored — it is not committed. Any CI or +Cloudflare build **must build propel before the site**. Use this package's +`build` script (`pnpm --filter @makeplane/propel-docs build`, which packs +propel first) rather than invoking `astro build` directly. A bare +`astro build` on a fresh checkout will fail to resolve +`@makeplane/propel/components/*`, since no `dist` exists yet. + +## Deployment placeholders + +`astro.config.mjs`'s `site` and `wrangler.jsonc`'s `account_id` / `routes` are +intentional TODO placeholders, to be filled in once a real domain and +Cloudflare account are assigned. The sitemap integration (`@astrojs/sitemap`) +reads `site` to generate absolute URLs, so update `site` and rebuild once the +domain is set. + +## Adding a component page + +1. Create `src/demos//.tsx` — a small React demo that imports + only from `@makeplane/propel/components/` and `lucide-react`, and + passes every required prop. +2. Create `src/pages/components/.mdx` following the existing pages: + frontmatter with `layout: ~/layouts/ComponentLayout.astro` plus `title` and + `description`; import each demo twice (once as the live component with + `client:visible`, once with a `?raw` suffix for the source snippet); + render it inside ``, add an "Installation" snippet, and + render `` for the props + table. +3. Add the page's entry (`slug`, `title`, `description`) to + `src/lib/components-registry.ts`. diff --git a/apps/propel-docs/astro.config.mjs b/apps/propel-docs/astro.config.mjs new file mode 100644 index 00000000..aa2d4098 --- /dev/null +++ b/apps/propel-docs/astro.config.mjs @@ -0,0 +1,42 @@ +// @ts-check + +import { resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +import mdx from "@astrojs/mdx"; +import react from "@astrojs/react"; +import sitemap from "@astrojs/sitemap"; +import tailwindcss from "@tailwindcss/vite"; +import { defineConfig } from "astro/config"; + +const __dirname = fileURLToPath(new URL(".", import.meta.url)); + +// Dev-mode alias to propel's real source, so editing a component hot-reloads +// this docs site without needing `vp pack --watch` running in parallel. +// Production builds resolve `@makeplane/propel` through its normal workspace +// package + dist exports instead (no alias). +const isDev = process.argv.includes("dev"); +const propelSrc = resolve(__dirname, "../../packages/propel/src"); + +export default defineConfig({ + // TODO: replace with the real deployed domain once one is assigned. + site: "https://propel-docs.example.com", + integrations: [mdx(), react(), sitemap()], + vite: { + plugins: [tailwindcss()], + resolve: isDev + ? { + alias: [ + { + find: /^@makeplane\/propel\/components\/(.*)$/, + replacement: resolve(propelSrc, "components/$1/index.tsx"), + }, + { + find: "@makeplane/propel/styles", + replacement: resolve(propelSrc, "styles/propel.css"), + }, + ], + } + : undefined, + }, +}); diff --git a/apps/propel-docs/package.json b/apps/propel-docs/package.json new file mode 100644 index 00000000..1a039ba0 --- /dev/null +++ b/apps/propel-docs/package.json @@ -0,0 +1,33 @@ +{ + "name": "@makeplane/propel-docs", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "astro dev", + "build": "vp run @makeplane/propel#build && astro build", + "preview": "astro preview", + "typecheck": "vp run @makeplane/propel#build && astro check" + }, + "dependencies": { + "@astrojs/mdx": "^7.0.3", + "@astrojs/react": "^6.0.1", + "@astrojs/sitemap": "^3.7.3", + "@makeplane/propel": "workspace:*", + "@tailwindcss/vite": "catalog:", + "astro": "^7.0.9", + "lucide-react": "catalog:", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "tailwindcss": "catalog:" + }, + "devDependencies": { + "@astrojs/check": "^0.9.9", + "@types/node": "catalog:", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "react-docgen-typescript": "^2.4.0", + "typescript": "catalog:", + "wrangler": "^4.110.0" + } +} diff --git a/apps/propel-docs/public/favicon.svg b/apps/propel-docs/public/favicon.svg new file mode 100644 index 00000000..19019d59 --- /dev/null +++ b/apps/propel-docs/public/favicon.svg @@ -0,0 +1,4 @@ + + + P + diff --git a/apps/propel-docs/src/components/CodeBlock.astro b/apps/propel-docs/src/components/CodeBlock.astro new file mode 100644 index 00000000..17ecaea5 --- /dev/null +++ b/apps/propel-docs/src/components/CodeBlock.astro @@ -0,0 +1,13 @@ +--- +import { Code } from "astro:components"; +import type { CodeLanguage } from "astro"; + +type Props = { + code: string; + lang?: CodeLanguage; +}; + +const { code, lang = "tsx" } = Astro.props; +--- + + diff --git a/apps/propel-docs/src/components/ComponentExample.astro b/apps/propel-docs/src/components/ComponentExample.astro new file mode 100644 index 00000000..cf0ec2ae --- /dev/null +++ b/apps/propel-docs/src/components/ComponentExample.astro @@ -0,0 +1,22 @@ +--- +import CodeBlock from "~/components/CodeBlock.astro"; + +type Props = { + code: string; +}; + +const { code } = Astro.props; +--- + +
+
+ +
+
+ + Show code + + + +
+
diff --git a/apps/propel-docs/src/components/PropsTable.astro b/apps/propel-docs/src/components/PropsTable.astro new file mode 100644 index 00000000..fee9f990 --- /dev/null +++ b/apps/propel-docs/src/components/PropsTable.astro @@ -0,0 +1,70 @@ +--- +import type { PropItemType } from "react-docgen-typescript"; + +import { getComponentDoc } from "~/lib/props-schema"; + +type Part = { + source: string; + component: string; +}; + +type Props = { + parts: Part[]; +}; + +const { parts } = Astro.props; + +// `shouldExtractLiteralValuesFromEnum` (props-schema.ts) puts a string-literal union's members in +// `type.value` (each `{ value: '"primary"' }`) while `type.name` stays the generic `"enum"` — join +// them back into the `"primary" | "secondary" | …` form so the table shows the real union, not the +// bare word "enum". +function formatType(type: PropItemType): string { + if (type.name === "enum" && Array.isArray(type.value)) { + return type.value.map((entry: { value: string }) => entry.value).join(" | "); + } + return type.name; +} +--- + +
+ { + parts.map(({ source, component }) => { + const doc = getComponentDoc(source, component); + if (!doc || Object.keys(doc.props).length === 0) { + console.warn( + `[PropsTable] No component doc found for "${component}" at source "${source}" — the props table will be empty. Check the source path (relative to packages/propel/src) and the exported component name.`, + ); + } + const props = doc ? Object.values(doc.props) : []; + return ( +
+

{component}

+
+ + + + + + + + + + + {props.map((prop) => ( + + + + + + + ))} + +
PropTypeRequiredDescription
{prop.name} + {formatType(prop.type)} + {prop.required ? "Yes" : "No"}{prop.description}
+
+
+ ); + }) + } +
diff --git a/apps/propel-docs/src/components/SidebarNav.astro b/apps/propel-docs/src/components/SidebarNav.astro new file mode 100644 index 00000000..2d0764d3 --- /dev/null +++ b/apps/propel-docs/src/components/SidebarNav.astro @@ -0,0 +1,32 @@ +--- +import { COMPONENTS } from "~/lib/components-registry"; + +const currentPath = Astro.url.pathname.replace(/\/$/, ""); +--- + + diff --git a/apps/propel-docs/src/demos/accordion/basic.tsx b/apps/propel-docs/src/demos/accordion/basic.tsx new file mode 100644 index 00000000..9782b6a0 --- /dev/null +++ b/apps/propel-docs/src/demos/accordion/basic.tsx @@ -0,0 +1,35 @@ +import { + Accordion, + AccordionHeader, + AccordionItem, + AccordionPanel, + AccordionTrigger, +} from "@makeplane/propel/components/accordion"; + +const ITEMS = [ + { + value: "what", + label: "What is Plane?", + body: "Plane is an open-source project management tool for tracking issues, sprints, and product roadmaps.", + }, + { + value: "pricing", + label: "How does pricing work?", + body: "Plane is free to self-host. Managed plans add hosting, backups, and support.", + }, +]; + +export default function BasicDemo() { + return ( + + {ITEMS.map((item) => ( + + + + + {item.body} + + ))} + + ); +} diff --git a/apps/propel-docs/src/demos/button/basic.tsx b/apps/propel-docs/src/demos/button/basic.tsx new file mode 100644 index 00000000..8c163905 --- /dev/null +++ b/apps/propel-docs/src/demos/button/basic.tsx @@ -0,0 +1,5 @@ +import { Button } from "@makeplane/propel/components/button"; + +export default function BasicDemo() { + return