diff --git a/README.md b/README.md index d665270..0c5812c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ The starterkit's cornerstone is our data-fetching solution and it's typesafety. - **Draft Mode** - preview mode for your application for Contentful Preview API usage - **Contentful Live Preview** - Contentful live previews let you edit components side by side with a visual representation and Live Preview SDK also lets you annotate specific fields you are editing to get to the editor screen by just clicking “Edit” button on the frontend. We also integrated live updates, which will show result of content changes immediately as opposed to waiting for content to auto-save in Contentful. - **Contentful Content Source Maps** - Optionally use Contentful Content Source Maps feature to get Live Preview SDK annotations without manually annotating your components, as well as get Vercel Content Link for Vercel customers. +- **Turborepo** - we use Turborepo for this project to showcase use of multiple packages for design system, CLI tooling, testing in addition to Next.js web app. ## Getting Started @@ -30,13 +31,13 @@ pnpm install - You could get access to an existing space that follows Contentful Marketing Template content model, for example a colleague could share his space with you - You could create your own space with https://www.contentful.com/starter-templates/marketing-website/. Keep in mind, new templates today can only be deployed on brand new Contentful accounts, so you might have to create a new account with a new email to do that, but this shouldn't be a problem, as it's free. -- You can start from scratch and use the contentful CLI and `/migrations/ctf-seed.json` to [import](https://www.contentful.com/developers/docs/tutorials/cli/import-and-export/) our demo content into your own contentful instance: `pnpm seed --environment-id=[ENVIRONMENT_ID] --space-id=[SPACE_ID]` +- You can start from scratch and use the contentful CLI and `apps/marketing/migrations/ctf-seed.json` to [import](https://www.contentful.com/developers/docs/tutorials/cli/import-and-export/) our demo content into your own contentful instance: `pnpm --filter=marketing seed --environment-id=[ENVIRONMENT_ID] --space-id=[SPACE_ID]` You will want to get a CDA and CPA API keys by using this [guide](https://www.contentful.com/developers/docs/references/authentication/#api-keys-in-the-contentful-web-app) 4. Configure environment -Create .env.local in root directory of the repo with the following contents: +Create .env.local in apps/marketing directory of the repo with the following contents: ``` CONTENTFUL_SPACE= @@ -48,10 +49,14 @@ CONTENTFUL_PREVIEW_SECRET=secret CONTENTFUL_USE_CONTENT_SOURCE_MAPS=true ``` -5. Run Dev Server +5. Run Next.js Dev Server ```bash -pnpm dev +turbo dev --filter=marketing +``` +or run both Next.js and Storybook dev servers with: +```bash +turbo dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. @@ -59,7 +64,7 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the ## Documentation - [Component Architecture](./docs/components.md) -- [Components UI Folder](./components/ui/README.md) +- [Components UI Folder](./packages/ui/README.md) - [Data-fetching guide](./docs/data-fetching.md) - [Features guide](./docs/features.md) - [Analytics guide](./components/analytics/README.md) @@ -70,7 +75,9 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +Customize the build command to be `turbo run build` and set the root directory to `apps/marketing` + +Check out [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. ## Support diff --git a/docs/components.md b/docs/components.md index 497684d..8a03cf6 100644 --- a/docs/components.md +++ b/docs/components.md @@ -6,6 +6,8 @@ There are a few interesting opinions that go into the component design of this p You will notice the following structure in the this project. +apps/marketing (Next.js app) + - app (reserved for routing and collocated imports) - components (separated from app folder in order to not commingle with routing) ... @@ -13,11 +15,14 @@ You will notice the following structure in the this project. - hero-banner-ctf-client.tsx - hero-banner-ctf.tsx - index.tsx (optional) - - ui - - hero-banner (UI component folder with no knowledge of Contentful or Next.js) - - hero-banner.tsx - - hero-banner-stories.tsx - - index.ts (optional) + +packages/ui (UI components package) + +- components + - hero-banner (UI component folder with no knowledge of Contentful or Next.js) + - hero-banner.tsx + - hero-banner-stories.tsx + - index.ts (optional) Let's break down each layer and component type and define it's role and purpose. @@ -27,7 +32,7 @@ We'll start from UI components and work our way up. > hero-banner.tsx -UI components are simply defined as pure React components with ability to manage state or render things in slots, but for the purposes of this project they are designed to be share-able across an ecosystem of React projects that are not necessarily built with Next.js or Contentful. More ideas about this concept are described in [UI folder docs](/components/ui/README.md). +UI components are simply defined as pure React components with ability to manage state or render things in slots, but for the purposes of this project they are designed to be share-able across an ecosystem of React projects that are not necessarily built with Next.js or Contentful. More ideas about this concept are described in [UI folder docs](/packages/ui/README.md). 2. Contentful integration components @@ -60,7 +65,7 @@ Let's explore how we can create new components and implementthem end to end. 1. Creating UI component This is a shadcn/ui project, and it means that simplest way to start building a new component is by either installing it from shadcn/ui library via shadcn CLI, or by generating one with AI on v0.dev and installing using same shadcn CLI. -The end result should be a component added to [components/ui](/components/ui/). Let's say it's accordion.tsx which exports Accordion and AccordionItem. You can start by testint the component in Storybook, working on some variants, updating styles and polishing the UI. +The end result should be a component added to [packages/ui/components](/packages/ui/components/). Let's say it's accordion.tsx which exports Accordion and AccordionItem. You can start by testint the component in Storybook, working on some variants, updating styles and polishing the UI. 2. Creating a content model in Contentful. Now that you have your pure UI component, you'll want to give it some data, for that you need to create a data model in Contentful that can serve your needs. @@ -137,7 +142,7 @@ export const ComponentAccordionFieldsFragment = graphql( You can define this fragment in any file, but we recommend collocating it with your component in the component folder. This will be enforced by [fragment-masking]() unless you opt out for some reason (not recommended). -Proceed to import and expand your fragment in [page.tsx](/app/[[...slug]]/page.tsx) as shown below: +Proceed to import and expand your fragment in [page.tsx](/apps/marketing/app/[[...slug]]/page.tsx) as shown below: ```jsx topSectionCollection(limit: 10) { @@ -153,7 +158,7 @@ Check out [fragment docs]() on gql.tada site As soon as you have the GraphQL fragment expanded on the top-level page query, you will get the exact shape of the GraphQL response as props in your component, but only after you implement a ComponentRenderer mapping. We'll implement mapping and come back to finishing this component. -4. Register your component in [components/component-renderer/mappings.ts](/components/component-renderer/mappings.ts) +4. Register your component in [components/component-renderer/mappings.ts](/apps/marketing/components/component-renderer/mappings.ts) All you need to do here is return a dynamic import of your component mapped to the Contentful content type GraphQL \_\_typename. The typename will be derived from your content type ID, for example ComponentAccordion is going to be the name for componentAccordion content type id. @@ -210,7 +215,7 @@ Now that we have the data, we could render the UI component here, but we'll take 6. Add Client component -We'll start of with a boilerplate in [components/accordion-ctf/accordion-ctf-client.tsx](/components/accordion-ctf/accordion-ctf-client.tsx). Create the component with the following code: +We'll start of with a boilerplate in `/apps/marketing/components/accordion-ctf/accordion-ctf-client.tsx`. Create the component with the following code: ```jsx import { ResultOf } from 'gql.tada'; diff --git a/docs/data-fetching.md b/docs/data-fetching.md index 9a8be64..cd0dc12 100644 --- a/docs/data-fetching.md +++ b/docs/data-fetching.md @@ -182,7 +182,7 @@ Coming soon... If you're adding new content types or making changes to the content model, you will need to generate a new graphql schema to get type inference in Typescript working and to get autocomplete in IDE. This can be done by running: ```bash -pnpm generate:schema +pnpm --filter=marketing generate:schema ``` After new types are generated, you will get changes in `./gql/` folder that you'll have to commit after you are done developing the feature. @@ -223,7 +223,7 @@ If using Webstorm, make sure you configure the Typescript interpreter from node_ If you can't use a Typescript server in your IDE, you can optionally generate a gql/graphql-env.d.ts by running this command: ```bash -pnpm generate:output +pnpm --filter=marketing generate:output ``` This command will also run `gql.tada turbo` which will generate a cache file that should also be commited. This cache file will speed up inference for new users who just checked out a new branch. diff --git a/packages/ui/components/README.md b/packages/ui/README.md similarity index 55% rename from packages/ui/components/README.md rename to packages/ui/README.md index 30a468e..14ec2b0 100644 --- a/packages/ui/components/README.md +++ b/packages/ui/README.md @@ -1,6 +1,6 @@ ## UI Components -This folder is meant to contain UI components that can be used to compose any other components in Next.js or other React frameworks. Although this folder is within Next.js App itself (for simplicity), you should think of it as external component library that either lives outside Next.js in a monorepo, or even lives in another repo for some cases. +This folder is meant to contain UI components that can be used to compose any other components in Next.js or other React frameworks. This is a sample design system implementation and exists as a separate packages to showcase the separation needed to reuse components outside of Next.js. ### Conventions @@ -8,11 +8,16 @@ This folder is meant to contain UI components that can be used to compose any ot There are a few boundaries and principles we set with this UI folder that we should maintain: -- UI folder has no knowledge of Contentful. In other words UI components could work in Contentful or outside. There shouldn't be any data-fetching activities or knowledge of contentful-specific things except when needed to hint to Contentful with external escape hatches (see [addAttributes](./hero-banner/hero-banner.tsx) function as an example) -- UI folder should not have any knowledge of Next.js either. It's easy to put NextLink and NextImage in your UI components, but that makes them not possible to reuse outside of Next.js. For this reason alone we have a demonstated path to use Next.js components. Check [HeroBannerCtfClient](../hero-banner-ctf/hero-banner-ctf-client.tsx) for example usage of radix-slots asChild property to pass the NextLink and NextImage primitives. -- Pure UI components in same folder makes it easy to understand when you violated that principle. If you see a Next.js or Contentful implementation detail in this folder, you did this wrong. -- This folder is ready for copy/paste into other projects or from other projects/libraries. Especially using shadcn/ui CLI, you can download components into this folder with commands like `npx shadcn@latest add [component]`. See [components.json](../../components.json) for configuration for shadcn/ui. +- UI folder has no knowledge of Contentful. In other words UI components could work in Contentful or outside. There shouldn't be any data-fetching activities or knowledge of contentful-specific things except when needed to hint to Contentful with external escape hatches (see [addAttributes](./components/hero-banner/hero-banner.tsx) function as an example) +- UI folder should not have any knowledge of Next.js either. It's easy to put NextLink and NextImage in your UI components, but that makes them not possible to reuse outside of Next.js. For this reason alone we have a demonstrated path to use Next.js components. Check [HeroBannerCtfClient](/apps/marketing/components/hero-banner-ctf/hero-banner-ctf-client.tsx) for example usage of radix-slots asChild property to pass the NextLink and NextImage primitives. +- Pure UI components in separate package makes it easy to understand when you violated that principle. If you see a Next.js or Contentful implementation detail in this package, you did this wrong. +- This folder is ready for copy/paste into other projects or from other projects/libraries. Especially using shadcn/ui CLI, you can download components into this folder with commands like `npx shadcn@latest add [component]`. See [components.json](components.json) for configuration for shadcn/ui. #### Storybook Storybook should be used to demonstrate UI components visuals, variations and interactions and it's much easier to do in isolation without the need to mock real API responses and data-fetching. +You can start Storybook dev server with: + +```bash +turbo dev --filter=@repo/ui +```