diff --git a/docs/generated/manifests/menus.json b/docs/generated/manifests/menus.json index 26a8567a18163..331218e86d88c 100644 --- a/docs/generated/manifests/menus.json +++ b/docs/generated/manifests/menus.json @@ -1711,6 +1711,14 @@ "children": [], "disableCollapsible": false }, + { + "name": "Set up Storybook for Angular Projects", + "path": "/recipes/storybook/overview-angular", + "id": "overview-angular", + "isExternal": false, + "children": [], + "disableCollapsible": false + }, { "name": "Set up Storybook for Vue Projects", "path": "/recipes/storybook/overview-vue", @@ -1720,9 +1728,9 @@ "disableCollapsible": false }, { - "name": "Set up Storybook for Angular Projects", - "path": "/recipes/storybook/overview-angular", - "id": "overview-angular", + "name": "Set up Storybook for Nuxt Projects", + "path": "/recipes/storybook/overview-nuxt", + "id": "overview-nuxt", "isExternal": false, "children": [], "disableCollapsible": false @@ -2908,6 +2916,14 @@ "children": [], "disableCollapsible": false }, + { + "name": "Set up Storybook for Angular Projects", + "path": "/recipes/storybook/overview-angular", + "id": "overview-angular", + "isExternal": false, + "children": [], + "disableCollapsible": false + }, { "name": "Set up Storybook for Vue Projects", "path": "/recipes/storybook/overview-vue", @@ -2917,9 +2933,9 @@ "disableCollapsible": false }, { - "name": "Set up Storybook for Angular Projects", - "path": "/recipes/storybook/overview-angular", - "id": "overview-angular", + "name": "Set up Storybook for Nuxt Projects", + "path": "/recipes/storybook/overview-nuxt", + "id": "overview-nuxt", "isExternal": false, "children": [], "disableCollapsible": false @@ -3007,6 +3023,14 @@ "children": [], "disableCollapsible": false }, + { + "name": "Set up Storybook for Angular Projects", + "path": "/recipes/storybook/overview-angular", + "id": "overview-angular", + "isExternal": false, + "children": [], + "disableCollapsible": false + }, { "name": "Set up Storybook for Vue Projects", "path": "/recipes/storybook/overview-vue", @@ -3016,9 +3040,9 @@ "disableCollapsible": false }, { - "name": "Set up Storybook for Angular Projects", - "path": "/recipes/storybook/overview-angular", - "id": "overview-angular", + "name": "Set up Storybook for Nuxt Projects", + "path": "/recipes/storybook/overview-nuxt", + "id": "overview-nuxt", "isExternal": false, "children": [], "disableCollapsible": false diff --git a/docs/generated/manifests/nx.json b/docs/generated/manifests/nx.json index 80c529d2880a9..651597621ffb0 100644 --- a/docs/generated/manifests/nx.json +++ b/docs/generated/manifests/nx.json @@ -2133,6 +2133,16 @@ "path": "/recipes/storybook/overview-react", "tags": ["storybook"] }, + { + "id": "overview-angular", + "name": "Set up Storybook for Angular Projects", + "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-angular", + "itemList": [], + "isExternal": false, + "path": "/recipes/storybook/overview-angular", + "tags": ["storybook"] + }, { "id": "overview-vue", "name": "Set up Storybook for Vue Projects", @@ -2144,13 +2154,13 @@ "tags": ["storybook"] }, { - "id": "overview-angular", - "name": "Set up Storybook for Angular Projects", - "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", - "file": "shared/recipes/storybook/plugin-angular", + "id": "overview-nuxt", + "name": "Set up Storybook for Nuxt Projects", + "description": "This guide explains how to set up Storybook for Nuxt projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-nuxt", "itemList": [], "isExternal": false, - "path": "/recipes/storybook/overview-angular", + "path": "/recipes/storybook/overview-nuxt", "tags": ["storybook"] }, { @@ -3626,6 +3636,16 @@ "path": "/recipes/storybook/overview-react", "tags": ["storybook"] }, + { + "id": "overview-angular", + "name": "Set up Storybook for Angular Projects", + "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-angular", + "itemList": [], + "isExternal": false, + "path": "/recipes/storybook/overview-angular", + "tags": ["storybook"] + }, { "id": "overview-vue", "name": "Set up Storybook for Vue Projects", @@ -3637,13 +3657,13 @@ "tags": ["storybook"] }, { - "id": "overview-angular", - "name": "Set up Storybook for Angular Projects", - "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", - "file": "shared/recipes/storybook/plugin-angular", + "id": "overview-nuxt", + "name": "Set up Storybook for Nuxt Projects", + "description": "This guide explains how to set up Storybook for Nuxt projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-nuxt", "itemList": [], "isExternal": false, - "path": "/recipes/storybook/overview-angular", + "path": "/recipes/storybook/overview-nuxt", "tags": ["storybook"] }, { @@ -3751,6 +3771,16 @@ "path": "/recipes/storybook/overview-react", "tags": ["storybook"] }, + "/recipes/storybook/overview-angular": { + "id": "overview-angular", + "name": "Set up Storybook for Angular Projects", + "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-angular", + "itemList": [], + "isExternal": false, + "path": "/recipes/storybook/overview-angular", + "tags": ["storybook"] + }, "/recipes/storybook/overview-vue": { "id": "overview-vue", "name": "Set up Storybook for Vue Projects", @@ -3761,14 +3791,14 @@ "path": "/recipes/storybook/overview-vue", "tags": ["storybook"] }, - "/recipes/storybook/overview-angular": { - "id": "overview-angular", - "name": "Set up Storybook for Angular Projects", - "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", - "file": "shared/recipes/storybook/plugin-angular", + "/recipes/storybook/overview-nuxt": { + "id": "overview-nuxt", + "name": "Set up Storybook for Nuxt Projects", + "description": "This guide explains how to set up Storybook for Nuxt projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-nuxt", "itemList": [], "isExternal": false, - "path": "/recipes/storybook/overview-angular", + "path": "/recipes/storybook/overview-nuxt", "tags": ["storybook"] }, "/recipes/storybook/configuring-storybook": { diff --git a/docs/generated/manifests/tags.json b/docs/generated/manifests/tags.json index 6ff0bc373bf3d..fea6d337200b0 100644 --- a/docs/generated/manifests/tags.json +++ b/docs/generated/manifests/tags.json @@ -892,6 +892,13 @@ "name": "Set up Storybook for React Projects", "path": "/recipes/storybook/overview-react" }, + { + "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-angular", + "id": "overview-angular", + "name": "Set up Storybook for Angular Projects", + "path": "/recipes/storybook/overview-angular" + }, { "description": "This guide explains how to set up Storybook for Vue projects in your Nx workspace.", "file": "shared/recipes/storybook/plugin-vue", @@ -900,11 +907,11 @@ "path": "/recipes/storybook/overview-vue" }, { - "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", - "file": "shared/recipes/storybook/plugin-angular", - "id": "overview-angular", - "name": "Set up Storybook for Angular Projects", - "path": "/recipes/storybook/overview-angular" + "description": "This guide explains how to set up Storybook for Nuxt projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-nuxt", + "id": "overview-nuxt", + "name": "Set up Storybook for Nuxt Projects", + "path": "/recipes/storybook/overview-nuxt" }, { "description": "This guide explains how Storybook is configured on your Nx workspace.", diff --git a/docs/generated/packages/storybook/documents/overview.md b/docs/generated/packages/storybook/documents/overview.md index 6191b50044081..6b241e8c1b161 100644 --- a/docs/generated/packages/storybook/documents/overview.md +++ b/docs/generated/packages/storybook/documents/overview.md @@ -72,6 +72,13 @@ nx g @nx/react:storybook-configuration my-react-project nx g @nx/vue:storybook-configuration my-vue-project ``` +{% /tab %} +{% tab label="Nuxt" %} + +```shell +nx g @nx/nuxt:storybook-configuration my-nuxt-app +``` + {% /tab %} {% tab label="React Native" %} @@ -84,7 +91,7 @@ nx g @nx/react-native:storybook-configuration my-react-native-project These framework-specific generators will also **generate stories** and interaction tests for you. -If you are NOT using a framework-specific generator (for [Angular](/nx-api/angular/generators/storybook-configuration), [React](/nx-api/react/generators/storybook-configuration), [React Native](/nx-api/react-native/generators/storybook-configuration), [Vue](/nx-api/vue/generators/storybook-configuration)), in the field `uiFramework` you must choose one of the following Storybook frameworks: +If you are NOT using a framework-specific generator (for [Angular](/nx-api/angular/generators/storybook-configuration), [React](/nx-api/react/generators/storybook-configuration), [React Native](/nx-api/react-native/generators/storybook-configuration), [Vue](/nx-api/vue/generators/storybook-configuration), [Nuxt](/nx-api/nuxt/generators/storybook-configuration)), in the field `uiFramework` you must choose one of the following Storybook frameworks: - `@storybook/angular` - `@storybook/html-webpack5` @@ -111,7 +118,7 @@ Choosing one of these frameworks will have the following effects on your workspa 3. Nx will create new `targets` in your project's `project.json`, called `storybook`, `test-storybook` and `build-storybook`, containing all the necessary configuration to serve, test and build Storybook. -Make sure to **use the framework-specific generators** if your project is using Angular, React, Next.js or React Native: [`@nx/angular:storybook-configuration`](/nx-api/angular/generators/storybook-configuration), [`@nx/react:storybook-configuration`](/nx-api/react/generators/storybook-configuration), [`@nx/react-native:storybook-configuration`](/nx-api/react-native/generators/storybook-configuration), as shown above. +Make sure to **use the framework-specific generators** if your project is using Angular, React, Next.js, Vue, Nuxt, or React Native: [`@nx/angular:storybook-configuration`](/nx-api/angular/generators/storybook-configuration), [`@nx/react:storybook-configuration`](/nx-api/react/generators/storybook-configuration), [`@nx/vue:storybook-configuration`](/nx-api/vue/generators/storybook-configuration), [`@nx/nuxt:storybook-configuration`](/nx-api/nuxt/generators/storybook-configuration), [`@nx/react-native:storybook-configuration`](/nx-api/react-native/generators/storybook-configuration), as shown above. ### Running Storybook @@ -211,6 +218,7 @@ You can find dedicated information for React and Angular: - [Set up Storybook for Angular Projects](/recipes/storybook/overview-angular) - [Set up Storybook for React Projects](/recipes/storybook/overview-react) - [Set up Storybook for Vue Projects](/recipes/storybook/overview-vue) +- [Set up Storybook for Nuxt Projects](/recipes/storybook/overview-nuxt) You can find all Storybook-related Nx documentation in the [Storybook recipes section](/recipes/storybook). diff --git a/docs/map.json b/docs/map.json index 09b0f444ad7be..89cd9c9b58bd6 100644 --- a/docs/map.json +++ b/docs/map.json @@ -656,6 +656,13 @@ "description": "This guide explains how to set up Storybook for React projects in your Nx workspace.", "file": "shared/recipes/storybook/plugin-react" }, + { + "name": "Set up Storybook for Angular Projects", + "id": "overview-angular", + "tags": ["storybook"], + "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-angular" + }, { "name": "Set up Storybook for Vue Projects", "id": "overview-vue", @@ -664,11 +671,11 @@ "file": "shared/recipes/storybook/plugin-vue" }, { - "name": "Set up Storybook for Angular Projects", - "id": "overview-angular", + "name": "Set up Storybook for Nuxt Projects", + "id": "overview-nuxt", "tags": ["storybook"], - "description": "This guide explains how to set up Storybook for Angular projects in your Nx workspace.", - "file": "shared/recipes/storybook/plugin-angular" + "description": "This guide explains how to set up Storybook for Nuxt projects in your Nx workspace.", + "file": "shared/recipes/storybook/plugin-nuxt" }, { "name": "Configuring Storybook on Nx", diff --git a/docs/shared/packages/storybook/plugin-overview.md b/docs/shared/packages/storybook/plugin-overview.md index 6191b50044081..6b241e8c1b161 100644 --- a/docs/shared/packages/storybook/plugin-overview.md +++ b/docs/shared/packages/storybook/plugin-overview.md @@ -72,6 +72,13 @@ nx g @nx/react:storybook-configuration my-react-project nx g @nx/vue:storybook-configuration my-vue-project ``` +{% /tab %} +{% tab label="Nuxt" %} + +```shell +nx g @nx/nuxt:storybook-configuration my-nuxt-app +``` + {% /tab %} {% tab label="React Native" %} @@ -84,7 +91,7 @@ nx g @nx/react-native:storybook-configuration my-react-native-project These framework-specific generators will also **generate stories** and interaction tests for you. -If you are NOT using a framework-specific generator (for [Angular](/nx-api/angular/generators/storybook-configuration), [React](/nx-api/react/generators/storybook-configuration), [React Native](/nx-api/react-native/generators/storybook-configuration), [Vue](/nx-api/vue/generators/storybook-configuration)), in the field `uiFramework` you must choose one of the following Storybook frameworks: +If you are NOT using a framework-specific generator (for [Angular](/nx-api/angular/generators/storybook-configuration), [React](/nx-api/react/generators/storybook-configuration), [React Native](/nx-api/react-native/generators/storybook-configuration), [Vue](/nx-api/vue/generators/storybook-configuration), [Nuxt](/nx-api/nuxt/generators/storybook-configuration)), in the field `uiFramework` you must choose one of the following Storybook frameworks: - `@storybook/angular` - `@storybook/html-webpack5` @@ -111,7 +118,7 @@ Choosing one of these frameworks will have the following effects on your workspa 3. Nx will create new `targets` in your project's `project.json`, called `storybook`, `test-storybook` and `build-storybook`, containing all the necessary configuration to serve, test and build Storybook. -Make sure to **use the framework-specific generators** if your project is using Angular, React, Next.js or React Native: [`@nx/angular:storybook-configuration`](/nx-api/angular/generators/storybook-configuration), [`@nx/react:storybook-configuration`](/nx-api/react/generators/storybook-configuration), [`@nx/react-native:storybook-configuration`](/nx-api/react-native/generators/storybook-configuration), as shown above. +Make sure to **use the framework-specific generators** if your project is using Angular, React, Next.js, Vue, Nuxt, or React Native: [`@nx/angular:storybook-configuration`](/nx-api/angular/generators/storybook-configuration), [`@nx/react:storybook-configuration`](/nx-api/react/generators/storybook-configuration), [`@nx/vue:storybook-configuration`](/nx-api/vue/generators/storybook-configuration), [`@nx/nuxt:storybook-configuration`](/nx-api/nuxt/generators/storybook-configuration), [`@nx/react-native:storybook-configuration`](/nx-api/react-native/generators/storybook-configuration), as shown above. ### Running Storybook @@ -211,6 +218,7 @@ You can find dedicated information for React and Angular: - [Set up Storybook for Angular Projects](/recipes/storybook/overview-angular) - [Set up Storybook for React Projects](/recipes/storybook/overview-react) - [Set up Storybook for Vue Projects](/recipes/storybook/overview-vue) +- [Set up Storybook for Nuxt Projects](/recipes/storybook/overview-nuxt) You can find all Storybook-related Nx documentation in the [Storybook recipes section](/recipes/storybook). diff --git a/docs/shared/recipes/storybook/plugin-nuxt.md b/docs/shared/recipes/storybook/plugin-nuxt.md new file mode 100644 index 0000000000000..844fef696693b --- /dev/null +++ b/docs/shared/recipes/storybook/plugin-nuxt.md @@ -0,0 +1,76 @@ +--- +title: Set up Storybook for Nuxt Projects +description: This guide explains how to set up Storybook for Nuxt projects in your Nx workspace. +--- + +# Set up Storybook for Nuxt Projects + +This guide will walk you through setting up [Storybook](https://storybook.js.org) for Nuxt projects in your Nx workspace. + +The generators for your Nuxt projects use the `@nx/vue:storybook-configuration` and `@nx/vue:stories` generators under the hood, with some additional configuration. There is no official support or settings for Nuxt3 in Storybook yet, so until then we are just configuring your Nuxt apps as if they were Vue apps, since the components are still Vue components. + +{% callout type="warning" title="Set up Storybook in your workspace" %} +You first need to set up Storybook for your Nx workspace, if you haven't already. You can read the [Storybook plugin overview guide](/nx-api/storybook) to get started. +{% /callout %} + +## Generate Storybook Configuration for a Nuxt project + +You can generate Storybook configuration for an individual Nuxt project by using the [`@nx/nuxt:storybook-configuration` generator](/nx-api/nuxt/generators/storybook-configuration), like this: + +```shell +nx g @nx/nuxt:storybook-configuration project-name +``` + +## Auto-generate Stories + +The [`@nx/nuxt:storybook-configuration` generator](/nx-api/nuxt/generators/storybook-configuration) has the option to automatically generate `*.stories.ts` files for each component declared in your project, under the `components` directory. This makes sure that no stories are generated for your `pages` components. The stories will be generated using [Component Story Format 3 (CSF3)](https://storybook.js.org/blog/storybook-csf3-is-here/). + +```text +/ +├── MyComponent.vue +└── MyComponent.stories.ts +``` + +If you add more components to your project, and want to generate stories for all your (new) components at any point, you can use the [`@nx/nuxt:stories` generator](/nx-api/nuxt/generators/stories): + +```shell +nx g @nx/nuxt:stories --project= +``` + +{% callout type="note" title="Example" %} +Let's take for a example a Nuxt application in your workspace, under `my-nuxt-app`, called `my-nuxt-app`. This application contains a component, called `my-button`. + +The command to generate stories for that application would be: + +```shell +nx g @nx/nuxt:stories --project=my-nuxt-app +``` + +and the result would be the following: + +```text +/ +my-nuxt-app +├── nuxt.config.ts +├── project.json +├── src +│   ├── app.vue +│   ├── assets +│   ├── components +│   │   └── my-button +│   │   ├── my-button.stories.ts +│   │   └── my-button.vue +│   ├── pages +│   ├── public +│   └── server +├── tsconfig.json +└── tsconfig.storybook.json +``` + +{% /callout %} + +## More Documentation + +You can find all Storybook-related Nx topics [here](/nx-api#storybook). + +For more on using Storybook, see the [official Storybook documentation](https://storybook.js.org/docs/vue/get-started/introduction). diff --git a/docs/shared/reference/sitemap.md b/docs/shared/reference/sitemap.md index c117c7ad5272f..44e508b87c14b 100644 --- a/docs/shared/reference/sitemap.md +++ b/docs/shared/reference/sitemap.md @@ -112,8 +112,9 @@ - [Wait for Tasks to Finish](/recipes/node/wait-for-tasks) - [Storybook](/recipes/storybook) - [Set up Storybook for React Projects](/recipes/storybook/overview-react) - - [Set up Storybook for Vue Projects](/recipes/storybook/overview-vue) - [Set up Storybook for Angular Projects](/recipes/storybook/overview-angular) + - [Set up Storybook for Vue Projects](/recipes/storybook/overview-vue) + - [Set up Storybook for Nuxt Projects](/recipes/storybook/overview-nuxt) - [Configuring Storybook on Nx](/recipes/storybook/configuring-storybook) - [One main Storybook instance for all projects](/recipes/storybook/one-storybook-for-all) - [One Storybook instance per scope](/recipes/storybook/one-storybook-per-scope) diff --git a/e2e/nuxt/src/nuxt.test.ts b/e2e/nuxt/src/nuxt.test.ts index 694bbd4a4fd42..4944e5b610ac9 100644 --- a/e2e/nuxt/src/nuxt.test.ts +++ b/e2e/nuxt/src/nuxt.test.ts @@ -1,4 +1,5 @@ import { + checkFilesExist, cleanupProject, killPorts, newProject, @@ -8,11 +9,13 @@ import { describe('Nuxt Plugin', () => { let proj: string; + const app = uniq('app'); beforeAll(() => { proj = newProject({ unsetProjectNameAndRootFormat: false, }); + runCLI(`generate @nx/nuxt:app ${app} --unitTestRunner=none`); }); afterAll(() => { @@ -21,13 +24,17 @@ describe('Nuxt Plugin', () => { }); it('should build application', async () => { - const app = uniq('app'); - - runCLI(`generate @nx/nuxt:app ${app} --unitTestRunner=none`); - const result = runCLI(`build ${app}`); expect(result).toContain( `Successfully ran target build for project ${app}` ); }); + + it('should build storybook for app', () => { + runCLI( + `generate @nx/vue:storybook-configuration ${app} --generateStories --no-interactive` + ); + runCLI(`run ${app}:build-storybook --verbose`); + checkFilesExist(`dist/storybook/${app}/index.html`); + }, 300_000); }); diff --git a/packages/nuxt/docs/stories-examples.md b/packages/nuxt/docs/stories-examples.md new file mode 100644 index 0000000000000..29a5f2e2608b4 --- /dev/null +++ b/packages/nuxt/docs/stories-examples.md @@ -0,0 +1,38 @@ +This generator will generate stories for all your components in your project. The stories will be generated using [Component Story Format 3 (CSF3)](https://storybook.js.org/blog/storybook-csf3-is-here/). + +```bash +nx g @nx/nuxt:stories project-name +``` + +You can read more about how this generator works, in the [Storybook for Nuxt overview page](/recipes/storybook/overview-nuxt#auto-generate-stories). + +When running this generator, you will be prompted to provide the following: + +- The `name` of the project you want to generate the configuration for. +- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. You can read more about this in the [Nx Storybook interaction tests documentation page](/packages/storybook/documents/interaction-tests).. + +You must provide a `name` for the generator to work. + +By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. + +There are a number of other options available. Let's take a look at some examples. + +## Examples + +### Ignore certain paths when generating stories + +```bash +nx g @nx/nuxt:stories --name=ui --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.* +``` + +This will generate stories for all the components in the `ui` project, except for the ones in the `libs/ui/src/not-stories` directory, and also for components that their file name is of the pattern `*.other.*`. + +This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. + +### Generate stories using JavaScript instead of TypeScript + +```bash +nx g @nx/nuxt:stories --name=ui --js=true +``` + +This will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components. diff --git a/packages/nuxt/docs/storybook-configuration-examples.md b/packages/nuxt/docs/storybook-configuration-examples.md new file mode 100644 index 0000000000000..d59fcb70c75cb --- /dev/null +++ b/packages/nuxt/docs/storybook-configuration-examples.md @@ -0,0 +1,55 @@ +This generator will set up Storybook for your **Nuxt** project. You can also use this generator to generate Storybook configuration for your **Next.js** project. By default, starting Nx 16, Storybook v7 is used. + +```bash +nx g @nx/nuxt:storybook-configuration project-name +``` + +You can read more about how this generator works, in the [Storybook for Nuxt overview page](/recipes/storybook/overview-nuxt#generate-storybook-configuration-for-a-nuxt-project). + +When running this generator, you will be prompted to provide the following: + +- The `name` of the project you want to generate the configuration for. +- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/nuxt/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/nuxt/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/packages/storybook/documents/interaction-tests).. +- Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project. + +You must provide a `name` for the generator to work. + +By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/nuxt/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. + +There are a number of other options available. Let's take a look at some examples. + +## Examples + +### Generate Storybook configuration + +```bash +nx g @nx/nuxt:storybook-configuration ui +``` + +This will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`). + +### Ignore certain paths when generating stories + +```bash +nx g @nx/nuxt:storybook-configuration ui --generateStories=true --ignorePaths="libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts" +``` + +This will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`. + +This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. + +### Generate stories using JavaScript instead of TypeScript + +```bash +nx g @nx/nuxt:storybook-configuration ui --generateStories=true --js=true +``` + +This will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components. + +### Generate Storybook configuration using JavaScript + +```bash +nx g @nx/nuxt:storybook-configuration ui --tsConfiguration=false +``` + +By default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`). diff --git a/packages/nuxt/generators.json b/packages/nuxt/generators.json index 39c6a24c84607..dfc623797ed45 100644 --- a/packages/nuxt/generators.json +++ b/packages/nuxt/generators.json @@ -27,6 +27,18 @@ "schema": "./src/generators/page/schema.json", "x-type": "page", "description": "Create a new page for your Nuxt application." + }, + "storybook-configuration": { + "factory": "./src/generators/storybook-configuration/configuration", + "schema": "./src/generators/storybook-configuration/schema.json", + "description": "Set up storybook for a Nuxt app.", + "hidden": false + }, + "stories": { + "factory": "./src/generators/stories/stories", + "schema": "./src/generators/stories/schema.json", + "description": "Create stories for all components declared in an app or library.", + "hidden": false } } } diff --git a/packages/nuxt/src/generators/stories/schema.json b/packages/nuxt/src/generators/stories/schema.json new file mode 100644 index 0000000000000..927d372d8c5b8 --- /dev/null +++ b/packages/nuxt/src/generators/stories/schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "$id": "NxNuxtStorybookStories", + "title": "Generate Nuxt Storybook stories", + "description": "Generate stories/specs for all components declared in a project.", + "type": "object", + "properties": { + "project": { + "type": "string", + "aliases": ["name", "projectName"], + "description": "Project for which to generate stories.", + "$default": { + "$source": "projectName", + "index": 0 + }, + "x-prompt": "For which project do you want to generate stories?", + "x-priority": "important" + }, + "generateCypressSpecs": { + "type": "boolean", + "description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator." + }, + "cypressProject": { + "type": "string", + "description": "The Cypress project to generate the stories under. This is inferred from `project` by default." + }, + "interactionTests": { + "type": "boolean", + "description": "Set up Storybook interaction tests.", + "x-prompt": "Do you want to set up Storybook interaction tests?", + "x-priority": "important", + "default": true + }, + "js": { + "type": "boolean", + "description": "Generate JavaScript files rather than TypeScript files.", + "default": false + }, + "ignorePaths": { + "type": "array", + "description": "Paths to ignore when looking for components.", + "items": { + "type": "string", + "description": "Path to ignore." + }, + "examples": [ + "apps/my-app/src/not-stories/**", + "**/**/src/**/not-stories/**", + "libs/my-lib/**/*.something.ts", + "**/**/src/**/*.other.*", + "libs/my-lib/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts" + ] + }, + "skipFormat": { + "description": "Skip formatting files.", + "type": "boolean", + "default": false, + "x-priority": "internal" + } + }, + "required": ["project"], + "examplesFile": "../../../docs/stories-examples.md" +} diff --git a/packages/nuxt/src/generators/stories/stories.ts b/packages/nuxt/src/generators/stories/stories.ts new file mode 100644 index 0000000000000..bbe3458c18cbd --- /dev/null +++ b/packages/nuxt/src/generators/stories/stories.ts @@ -0,0 +1,21 @@ +import { runTasksInSerial, Tree } from '@nx/devkit'; +import { + storiesGenerator as vueStoriesGenerator, + StorybookStoriesSchema, +} from '@nx/vue'; + +/* + * This generator is basically the Vue one + */ +export async function storiesGenerator( + host: Tree, + options: StorybookStoriesSchema +) { + const storiesGenerator = await vueStoriesGenerator(host, { + ...options, + }); + + return runTasksInSerial(storiesGenerator); +} + +export default storiesGenerator; diff --git a/packages/nuxt/src/generators/storybook-configuration/__snapshots__/configuration.spec.ts.snap b/packages/nuxt/src/generators/storybook-configuration/__snapshots__/configuration.spec.ts.snap new file mode 100644 index 0000000000000..7b7a17b4f2704 --- /dev/null +++ b/packages/nuxt/src/generators/storybook-configuration/__snapshots__/configuration.spec.ts.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`nuxt:storybook-configuration should configure with vue3 framework and styles import 1`] = ` +"import type { StorybookConfig } from '@storybook/vue3-vite'; + +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { mergeConfig } from 'vite'; + +const config: StorybookConfig = { + stories: ['../src/components/**/*.stories.@(js|jsx|ts|tsx|mdx)'], + addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'], + framework: { + name: '@storybook/vue3-vite', + options: {}, + }, + + viteFinal: async (config) => + mergeConfig(config, { + plugins: [nxViteTsPaths()], + }), +}; + +export default config; + +// To customize your Vite configuration you can use the viteFinal field. +// Check https://storybook.js.org/docs/react/builders/vite#configuration +// and https://nx.dev/recipes/storybook/custom-builder-configs +" +`; + +exports[`nuxt:storybook-configuration should configure with vue3 framework and styles import 2`] = `""`; diff --git a/packages/nuxt/src/generators/storybook-configuration/configuration.spec.ts b/packages/nuxt/src/generators/storybook-configuration/configuration.spec.ts new file mode 100644 index 0000000000000..6ebc14fdc8d0b --- /dev/null +++ b/packages/nuxt/src/generators/storybook-configuration/configuration.spec.ts @@ -0,0 +1,98 @@ +import { logger, Tree } from '@nx/devkit'; +import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; +import { Linter } from '@nx/linter'; +import applicationGenerator from '../application/application'; +import componentGenerator from '../component/component'; +import storybookConfigurationGenerator from './configuration'; + +const componentContent = ` + + + + +`; + +describe('nuxt:storybook-configuration', () => { + let appTree; + beforeEach(async () => { + jest.spyOn(logger, 'warn').mockImplementation(() => {}); + jest.spyOn(logger, 'debug').mockImplementation(() => {}); + jest.resetModules(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should configure with vue3 framework and styles import', async () => { + appTree = await createTestApp('test-ui-app'); + await storybookConfigurationGenerator(appTree, { + name: 'test-ui-app', + }); + + expect( + appTree.read('test-ui-app/.storybook/main.ts', 'utf-8') + ).toMatchSnapshot(); + expect( + appTree.read('test-ui-app/.storybook/preview.ts', 'utf-8') + ).toMatchSnapshot(); + expect(appTree.exists('test-ui-app/tsconfig.storybook.json')).toBeTruthy(); + }); + + it('should generate stories for components and not pages', async () => { + appTree = await createTestApp('test-ui-app'); + appTree.write( + 'test-ui-app/src/components/my-component/my-component.vue', + componentContent + ); + appTree.write('test-ui-app/src/pages/about.vue', componentContent); + + await storybookConfigurationGenerator(appTree, { + name: 'test-ui-app', + generateStories: true, + }); + + expect( + appTree.exists( + 'test-ui-app/src/components/my-component/my-component.stories.ts' + ) + ).toBeTruthy(); + expect( + appTree.exists('test-ui-app/src/pages/about.stories.ts') + ).toBeFalsy(); + }); +}); + +export async function createTestApp( + appName: string, + plainJS = false +): Promise { + let appTree = createTreeWithEmptyWorkspace(); + + await applicationGenerator(appTree, { + e2eTestRunner: 'none', + linter: Linter.EsLint, + skipFormat: false, + style: 'css', + unitTestRunner: 'none', + name: appName, + js: plainJS, + projectNameAndRootFormat: 'as-provided', + }); + + await componentGenerator(appTree, { + name: 'my-component', + project: appName, + }); + + return appTree; +} diff --git a/packages/nuxt/src/generators/storybook-configuration/configuration.ts b/packages/nuxt/src/generators/storybook-configuration/configuration.ts new file mode 100644 index 0000000000000..c966627146090 --- /dev/null +++ b/packages/nuxt/src/generators/storybook-configuration/configuration.ts @@ -0,0 +1,37 @@ +import { + formatFiles, + readProjectConfiguration, + runTasksInSerial, + Tree, +} from '@nx/devkit'; +import { storybookConfigurationGenerator as vueStorybookConfigurationGenerator } from '@nx/vue'; +import { Schema } from './schema'; + +/* + * This generator is basically the Vue one, but for Nuxt we + * are just adding the styles in `.storybook/preview.ts` + */ +export async function storybookConfigurationGenerator( + host: Tree, + options: Schema +) { + const storybookConfigurationGenerator = + await vueStorybookConfigurationGenerator(host, { + ...options, + }); + + const projectConfiguration = readProjectConfiguration(host, options.name); + + const storybookConfigFolder = + projectConfiguration.targets?.storybook?.options?.configDir; + + host.write( + `${storybookConfigFolder}/preview.${options.tsConfiguration ? 'ts' : 'js'}`, + `import '../src/assets/css/styles.css';` + ); + + await formatFiles(host); + return runTasksInSerial(storybookConfigurationGenerator); +} + +export default storybookConfigurationGenerator; diff --git a/packages/nuxt/src/generators/storybook-configuration/schema.d.ts b/packages/nuxt/src/generators/storybook-configuration/schema.d.ts new file mode 100644 index 0000000000000..bfab779db2beb --- /dev/null +++ b/packages/nuxt/src/generators/storybook-configuration/schema.d.ts @@ -0,0 +1,12 @@ +import { Linter } from '@nx/linter'; + +export interface Schema { + name: string; + interactionTests?: boolean; + generateStories?: boolean; + js?: boolean; + tsConfiguration?: boolean; + linter?: Linter; + ignorePaths?: string[]; + configureStaticServe?: boolean; +} diff --git a/packages/nuxt/src/generators/storybook-configuration/schema.json b/packages/nuxt/src/generators/storybook-configuration/schema.json new file mode 100644 index 0000000000000..457ef13c74e64 --- /dev/null +++ b/packages/nuxt/src/generators/storybook-configuration/schema.json @@ -0,0 +1,77 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "$id": "NxNuxtStorybookConfigure", + "title": "Nuxt Storybook Configure", + "description": "Set up Storybook for a Nuxt project.", + "type": "object", + "properties": { + "name": { + "type": "string", + "aliases": ["project", "projectName"], + "description": "Project for which to generate Storybook configuration.", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "For which project do you want to generate Storybook configuration?", + "x-dropdown": "projects", + "x-priority": "important" + }, + "interactionTests": { + "type": "boolean", + "description": "Set up Storybook interaction tests.", + "x-prompt": "Do you want to set up Storybook interaction tests?", + "x-priority": "important", + "alias": ["configureTestRunner"], + "default": true + }, + "generateStories": { + "type": "boolean", + "description": "Automatically generate `*.stories.ts` files for components declared in this project?", + "x-prompt": "Automatically generate *.stories.ts files for components declared in this project?", + "default": true, + "x-priority": "important" + }, + "configureStaticServe": { + "type": "boolean", + "description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.", + "x-prompt": "Configure a static file server for the storybook instance?", + "default": true, + "x-priority": "important" + }, + "js": { + "type": "boolean", + "description": "Generate JavaScript story files rather than TypeScript story files.", + "default": false + }, + "tsConfiguration": { + "type": "boolean", + "description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.", + "default": true + }, + "linter": { + "description": "The tool to use for running lint checks.", + "type": "string", + "enum": ["eslint"], + "default": "eslint" + }, + "ignorePaths": { + "type": "array", + "description": "Paths to ignore when looking for components.", + "items": { + "type": "string", + "description": "Path to ignore." + }, + "examples": [ + "apps/my-app/src/not-stories/**", + "**/**/src/**/not-stories/**", + "libs/my-lib/**/*.something.ts", + "**/**/src/**/*.other.*", + "libs/my-lib/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts" + ] + } + }, + "required": ["name"], + "examplesFile": "../../../docs/storybook-configuration-examples.md" +} diff --git a/packages/vue/index.ts b/packages/vue/index.ts index 984e8817bcb82..b06058f4b837d 100644 --- a/packages/vue/index.ts +++ b/packages/vue/index.ts @@ -2,6 +2,11 @@ export * from './src/utils/versions'; export { applicationGenerator } from './src/generators/application/application'; export { libraryGenerator } from './src/generators/library/library'; export { componentGenerator } from './src/generators/component/component'; +export { storybookConfigurationGenerator } from './src/generators/storybook-configuration/configuration'; +export { + storiesGenerator, + StorybookStoriesSchema, +} from './src/generators/stories/stories'; export { type InitSchema } from './src/generators/init/schema'; export { vueInitGenerator } from './src/generators/init/init'; export { addJest } from './src/generators/application/lib/add-jest'; diff --git a/packages/vue/src/generators/storybook-configuration/__snapshots__/configuration.spec.ts.snap b/packages/vue/src/generators/storybook-configuration/__snapshots__/configuration.spec.ts.snap index 4af5261ed2233..b76939391c21d 100644 --- a/packages/vue/src/generators/storybook-configuration/__snapshots__/configuration.spec.ts.snap +++ b/packages/vue/src/generators/storybook-configuration/__snapshots__/configuration.spec.ts.snap @@ -28,6 +28,95 @@ export default config; " `; -exports[`vue:storybook-configuration should generate stories for components 1`] = `null`; +exports[`vue:storybook-configuration should generate stories for components 1`] = ` +"import type { Meta, StoryObj } from '@storybook/vue3'; +import myComponent from './my-component.vue'; -exports[`vue:storybook-configuration should generate stories for components without interaction tests 1`] = `null`; +import { within } from '@storybook/testing-library'; +import { expect } from '@storybook/jest'; + +const meta: Meta = { + component: myComponent, + title: 'myComponent', +}; +export default meta; +type Story = StoryObj; + +export const Primary = { + args: { + name: 'name', + displayAge: false, + age: 0, + }, +}; + +export const Heading: Story = { + args: { + name: 'name', + displayAge: false, + age: 0, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect(canvas.getByText(/Welcome to myComponent!/gi)).toBeTruthy(); + }, +}; +" +`; + +exports[`vue:storybook-configuration should generate stories for components for app 1`] = ` +"import type { Meta, StoryObj } from '@storybook/vue3'; +import myComponent from './my-component.vue'; + +import { within } from '@storybook/testing-library'; +import { expect } from '@storybook/jest'; + +const meta: Meta = { + component: myComponent, + title: 'myComponent', +}; +export default meta; +type Story = StoryObj; + +export const Primary = { + args: { + name: 'name', + displayAge: false, + age: 0, + }, +}; + +export const Heading: Story = { + args: { + name: 'name', + displayAge: false, + age: 0, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect(canvas.getByText(/Welcome to myComponent!/gi)).toBeTruthy(); + }, +}; +" +`; + +exports[`vue:storybook-configuration should generate stories for components without interaction tests 1`] = ` +"import type { Meta, StoryObj } from '@storybook/vue3'; +import myComponent from './my-component.vue'; + +const meta: Meta = { + component: myComponent, + title: 'myComponent', +}; +export default meta; +type Story = StoryObj; + +export const Primary = { + args: { + name: 'name', + displayAge: false, + age: 0, + }, +}; +" +`; diff --git a/packages/vue/src/generators/storybook-configuration/configuration.spec.ts b/packages/vue/src/generators/storybook-configuration/configuration.spec.ts index 2a45bcd245695..da347a8888dea 100644 --- a/packages/vue/src/generators/storybook-configuration/configuration.spec.ts +++ b/packages/vue/src/generators/storybook-configuration/configuration.spec.ts @@ -66,6 +66,11 @@ describe('vue:storybook-configuration', () => { it('should generate stories for components', async () => { appTree = await createTestUILib('test-ui-lib'); + appTree.write( + 'test-ui-lib/src/components/my-component/my-component.vue', + componentContent + ); + await storybookConfigurationGenerator(appTree, { name: 'test-ui-lib', generateStories: true, @@ -74,6 +79,12 @@ describe('vue:storybook-configuration', () => { expect( appTree.exists('test-ui-lib/src/components/test-ui-lib.stories.ts') ).toBeTruthy(); + expect( + appTree.read( + 'test-ui-lib/src/components/my-component/my-component.stories.ts', + 'utf-8' + ) + ).toMatchSnapshot(); }); it('should configure everything at once', async () => { @@ -86,8 +97,12 @@ describe('vue:storybook-configuration', () => { expect(appTree.exists('test-ui-app/tsconfig.storybook.json')).toBeTruthy(); }); - it('should generate stories for components', async () => { + it('should generate stories for components for app', async () => { appTree = await createTestAppLib('test-ui-app'); + appTree.write( + 'test-ui-app/src/components/my-component/my-component.vue', + componentContent + ); await storybookConfigurationGenerator(appTree, { name: 'test-ui-app', generateStories: true, @@ -103,6 +118,10 @@ describe('vue:storybook-configuration', () => { it('should generate stories for components without interaction tests', async () => { appTree = await createTestAppLib('test-ui-app'); + appTree.write( + 'test-ui-app/src/components/my-component/my-component.vue', + componentContent + ); await storybookConfigurationGenerator(appTree, { name: 'test-ui-app', generateStories: true,