From 319e1443e10dc7408877ca87961c7c3ce837e1ec Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Wed, 11 Oct 2023 17:28:14 +0300 Subject: [PATCH] fix(storybook): cleanup v17 tasks --- .../storybook/documents/best-practices.md | 36 ++-- .../packages/storybook/documents/overview.md | 44 +++-- .../packages/storybook/executors/build.json | 15 +- .../storybook/generators/configuration.json | 2 +- .../storybook/generators/migrate-7.json | 2 +- .../packages/storybook/best-practices.md | 36 ++-- .../packages/storybook/plugin-overview.md | 44 +++-- .../storybook/angular-storybook-compodoc.md | 8 - .../storybook/custom-builder-configs.md | 92 ++++++---- .../recipes/storybook/interaction-tests.md | 8 + .../storybook/one-storybook-for-all.md | 78 +++++--- .../storybook/one-storybook-per-scope.md | 52 ++++-- .../one-storybook-with-composition.md | 33 ++-- .../recipes/storybook/plugin-angular.md | 88 ++++----- docs/shared/recipes/storybook/plugin-react.md | 93 ++++------ .../storybook/storybook-composition-setup.md | 34 ++-- .../generators/stories/stories-app.spec.ts | 2 +- .../generators/stories/stories-lib.spec.ts | 2 +- .../storybook-configuration.ts | 2 +- .../configuration.spec.ts | 2 +- .../storybook-configuration/configuration.ts | 2 +- .../docs/build-storybook-executor-examples.md | 12 +- .../docs/configuration-generator-examples.md | 26 +-- .../docs/migrate-7-generator-examples.md | 4 - .../build-storybook.impl.spec.ts | 58 ------ .../build-storybook/build-storybook.impl.ts | 68 ++----- .../src/executors/build-storybook/schema.json | 13 -- .../storybook/storybook.impl.spec.ts | 71 -------- .../src/executors/storybook/storybook.impl.ts | 76 +++----- packages/storybook/src/executors/utils.ts | 169 ------------------ .../generators/configuration/configuration.ts | 2 +- .../configuration/lib/util-functions.ts | 2 +- .../src/generators/configuration/schema.d.ts | 2 +- .../src/generators/migrate-7/schema.d.ts | 2 - packages/storybook/src/utils/models.ts | 11 -- .../src/utils/test-configs/.storybook/main.js | 10 +- packages/storybook/src/utils/utilities.ts | 5 +- 37 files changed, 444 insertions(+), 762 deletions(-) delete mode 100644 packages/storybook/src/executors/build-storybook/build-storybook.impl.spec.ts delete mode 100644 packages/storybook/src/executors/storybook/storybook.impl.spec.ts delete mode 100644 packages/storybook/src/executors/utils.ts diff --git a/docs/generated/packages/storybook/documents/best-practices.md b/docs/generated/packages/storybook/documents/best-practices.md index 319143e26ce7eb..1ed7dfc91b0762 100644 --- a/docs/generated/packages/storybook/documents/best-practices.md +++ b/docs/generated/packages/storybook/documents/best-practices.md @@ -19,7 +19,7 @@ Storybook helps you test your UIs. You can read more about testing with Storyboo ### Documentation -Storybook helps you document your UI elements, or your design system, effectively and in an interactive way. You can read more in the [How-to document components](https://storybook.js.org/docs/react/writing-docs/introduction) documentation page. Essentially, you can use Storybook to publish a catalog of your components. A catalog that you can share with the design team, the developer team, the product team, anyone else in the product development process, or even the client. The components are isolated, interactive, and can be represented in all possible forms that they can take (e.g. for a button: enabled, disabled, active, etc). You can read more about publishing your Storybook in the [Publish Storybook](https://storybook.js.org/docs/react/sharing/publish-storybook) documentation page. +Storybook helps you document your UI elements, or your design system, effectively and in an interactive way. You can read more in the [How to document components](https://storybook.js.org/docs/react/writing-docs/introduction) documentation page. Essentially, you can use Storybook to publish a catalog of your components. A catalog that you can share with the design team, the developer team, the product team, anyone else in the product development process, or even the client. The components are isolated, interactive, and can be represented in all possible forms that they can take (e.g. for a button: enabled, disabled, active, etc). You can read more about publishing your Storybook in the [Publish Storybook](https://storybook.js.org/docs/react/sharing/publish-storybook) documentation page. ## Nx and Storybook @@ -42,38 +42,34 @@ If your project is not configured yet, check out one of these guides: - [Set up Storybook for React Projects](/recipes/storybook/overview-react) - [Set up Storybook for Angular Projects](/recipes/storybook/overview-angular) +- +- [Set up Storybook for Vue Projects](/recipes/storybook/overview-vue) If your project is [already configured](/nx-api/storybook), you can use the `stories` generator: -- [React stories generator](/nx-api/react/generators/stories) +- [React (and Next.js) stories generator](/nx-api/react/generators/stories) - [React Native stories generator](/nx-api/react-native/generators/stories) +- [Vue stories generator](/nx-api/vue/generators/stories) + - [Angular stories generator](/nx-api/angular/generators/stories) The stories generator will read your inputs (if you’re using Angular), or your props (if you're using React), and will generate stories with the corresponding arguments/controls already prefilled. -#### Cypress tests generation - -Nx also generates Cypress tests for your components, that point to the corresponding component’s story. You can read more about how the Cypress tests are generated and how they look like in the [storybook-configuration generator documentation](/recipes/storybook/overview-react#cypress-tests-for-stories). +#### Storybook interaction tests -Take a look at the generated code of the Cypress test file, specifically at the URL which Cypress visits: - -```javascript -cy.visit( - '/iframe.html?id=buttoncomponent--primary&args=text:Click+me!;padding;style:default' -); -``` +[Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing) allow you to test user interactions within your Storybook stories. It enhances your [Storybook](https://storybook.js.org/) setup, ensuring that not only do your components look right, but they also work correctly when interacted with. -Cypress visits the URL that hosts the story of the component we are testing, adding values to its controls (eg. `args=text:Click+me!`). Then, the test attempts to validate that the values are correctly applied. +Nx will generate interaction tests for your stories. You can read more in our [Setting up Storybook Interaction Tests with Nx guide](/recipes/storybook/interaction-tests). ### CI/CD tools Now let’s see how Nx helps in the CI/CD journey, as well. -#### Cypress testing +#### Interaction tests in your CI -When you are running the Cypress tests for a project, Cypress will start the Storybook server of that project. The Storybook server will fire up a Storybook instance, hosting all the components's stories for that project. The e2e tests will then run, which actually visit the stories and perform the tests there. Cypress will be configured to start and stop the Storybook server. The results will be cached, and they will go through the Nx graph, meaning that Nx will know if the tests need to be run again or not, depending on the affected status of your project. +You can set up your interaction tests to run as part of your CI. You can read more in the [Storybook docs](https://storybook.js.org/docs/react/writing-tests/test-runner#set-up-ci-to-run-**tests**). #### Serve @@ -97,7 +93,7 @@ Setting up Storybook on Nx reflects - and takes advantage of - the [mental model ##### Development and debugging -In the process of setting up Storybook in your Nx workspace that we described above, you end up with one Storybook instance per project. That way, you can use your project’s Storybook targets to serve and build Storybook: +In the process of setting up Storybook in your Nx workspace that we described above, you end up with one Storybook instance per project. That way, you can use your project’s Storybook targets to serve, test and build Storybook: ```shell nx storybook my-project @@ -109,11 +105,13 @@ and nx build-storybook my-project ``` -This feature is extremely useful when developing locally. The containerized stories in your Storybook are the only ones that are built/served when you want to debug just one component, or just one library. You don’t have to wait for a huge Storybook containing all your stories in your repository to fire up. You just need to wait for the Storybook of a single project to start. This speeds up the process. +and -##### E2e tests with Cypress +```shell +nx test-storybook my-project +``` -If you’re using Cypress, and you’re taking advantage of the generated Cypress tests that our Storybook generators generate, then your e2e tests are also going to be much faster. When you run your e2e tests for a particular project, Cypress is only going to start the specific Storybook instance, and it’s going to take much less time than having to start an all-including universal Storybook. +This feature is extremely useful when developing locally. The containerized stories in your Storybook are the only ones that are built/served/tested when you want to debug just one component, or just one library. You don’t have to wait for a huge Storybook containing all your stories in your repository to fire up. You just need to wait for the Storybook of a single project to start. This speeds up the process. ##### Caching, affected, dependency management diff --git a/docs/generated/packages/storybook/documents/overview.md b/docs/generated/packages/storybook/documents/overview.md index 0b939e93d28111..6191b500440817 100644 --- a/docs/generated/packages/storybook/documents/overview.md +++ b/docs/generated/packages/storybook/documents/overview.md @@ -7,10 +7,6 @@ description: This is an overview page for the Storybook plugin in Nx. It explain This guide will briefly walk you through using Storybook within an Nx workspace. -{% callout type="info" title="Storybook 7 by default" %} -Starting with Nx 16, Storybook 7 is used by default to configure your projects. -{% /callout %} - ## Setting Up Storybook ### Add the Storybook plugin @@ -86,7 +82,7 @@ nx g @nx/react-native:storybook-configuration my-react-native-project {% /tab %} {% /tabs %} -These framework-specific generators will also **generate stories** for you. +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: @@ -113,9 +109,7 @@ Choosing one of these frameworks will have the following effects on your workspa 2. Nx will generate a project-level `.storybook` folder (located under `libs/your-project/.storybook` or `apps/your-project/.storybook`) containing the essential configuration files for Storybook. -3. Nx will create new `targets` in your project's `project.json`, called `storybook` and `build-storybook`, containing all the necessary configuration to serve and build Storybook. - -4. Nx will generate a new Cypress e2e app for your project (if there isn't one already) to run against the Storybook instance. +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. @@ -147,6 +141,20 @@ or nx build-storybook project-name ``` +### Testing Storybook + +With the Storybook server running, you can test Storybook (run all the interaction tests) using this command: + +```shell +nx run project-name:test-storybook +``` + +or + +```shell +nx test-storybook project-name +``` + ### Anatomy of the Storybook setup When running the Nx Storybook generator, it'll configure the Nx workspace to be able to run Storybook seamlessly. It'll create a project specific Storybook configuration. @@ -171,17 +179,21 @@ To register a [Storybook addon](https://storybook.js.org/addons/) for all Storyb 1. In your project's `.storybook/main.ts` file, in the `addons` array of the `module.exports` object, add the new addon: - ```typescript {% fileName="/.storybook/main.js" %} - module.exports = { - stories: [...], - ..., - addons: [..., '@storybook/addon-essentials'], - }; - ``` +```typescript {% fileName="/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/react-vite'; + +const config: StorybookConfig = { + ... + addons: ['@storybook/addon-essentials', '@storybook/addon-interactions', ...], + ... +}; + +export default config; +``` 2. If a decorator is required, in each project's `/.storybook/preview.ts`, you can export an array called `decorators`. - ```typescript {% fileName="/.storybook/preview.js" %} + ```typescript {% fileName="/.storybook/preview.ts" %} import someDecorator from 'some-storybook-addon'; export const decorators = [someDecorator]; ``` diff --git a/docs/generated/packages/storybook/executors/build.json b/docs/generated/packages/storybook/executors/build.json index 2715cb20aaf0a2..0f3075fb2ad783 100644 --- a/docs/generated/packages/storybook/executors/build.json +++ b/docs/generated/packages/storybook/executors/build.json @@ -89,19 +89,6 @@ "type": "boolean", "description": "Starts Storybook in documentation mode. Learn more about it : https://storybook.js.org/docs/react/writing-docs/build-documentation#preview-storybooks-documentation." }, - "uiFramework": { - "type": "string", - "description": "Storybook framework npm package.", - "enum": [ - "@storybook/react", - "@storybook/html", - "@storybook/web-components", - "@storybook/vue", - "@storybook/vue3", - "@storybook/svelte" - ], - "x-deprecated": "Upgrade to Storybook 7." - }, "webpackStatsJson": { "type": ["boolean", "string"], "description": "Write Webpack Stats JSON to disk.", @@ -147,7 +134,7 @@ }, "additionalProperties": true, "required": ["configDir"], - "examplesFile": "---\ntitle: Storybook builder executor examples\ndescription: This page contains examples for the @nx/storybook:build executor.\n---\n\n`project.json`:\n\n```json\n//...\n\"ui\": {\n \"targets\": {\n //...\n \"build-storybook\": {\n \"executor\": \"@nx/storybook:build\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"outputDir\": \"dist/storybook/ui\",\n \"configDir\": \"libs/ui/.storybook\"\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n }\n}\n```\n\n```bash\nnx run ui:build-storybook\n```\n\n## Examples\n\n### For non-Angular projects\n\n{% tabs %}\n{% tab label=\"Working in docsMode\" %}\n\nYou can work in docs mode, building a documentation-only site, by setting the `docsMode` option to `true` and using the `@storybook/addon-docs` addon.\n\nRead more on the [Storybook documentation page for `addon-docs`](https://storybook.js.org/addons/@storybook/addon-docs).\n\n```json\n\"storybook\": {\n \"executor\": \"@nx/storybook:build\",\n \"options\": {\n \"port\": 4400,\n \"configDir\": \"libs/ui/.storybook\",\n \"docsMode\": true\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n\n### For Angular projects\n\n{% tabs %}\n{% tab label=\"Default configuration\" %}\n\nThis is the default configuration for Angular projects using Storybook. You can see that it uses the native `@storybook/angular:build-storybook` executor. You can read more about the configuration options at the relevant [Storybook documentation page](https://storybook.js.org/docs/angular/get-started/install).\n\n```json\n\"build-storybook\": {\n \"executor\": \"@storybook/angular:build-storybook\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"outputDir\": \"dist/storybook/ngapp\",\n \"configDir\": \"libs/ui/.storybook\",\n \"browserTarget\": \"ui:build\",\n \"compodoc\": false\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Changing the browserTarget\" %}\n\nYou can set the [`browserTarget`](/deprecated/storybook/angular-browser-target) to use `build-storybook` as the builder. This is most useful in the cases where your project does not have a `build` target.\n\n```json\n\"build-storybook\": {\n \"executor\": \"@storybook/angular:build-storybook\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"outputDir\": \"dist/storybook/ngapp\",\n \"configDir\": \"libs/ui/.storybook\",\n \"browserTarget\": \"ui:build-storybook\",\n \"compodoc\": false\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"Adding styles\" %}\n\nYou can add paths to stylesheets to be included in the Storybook build by using the `styles` array. You can also add `stylePreprocessorOptions`, much like you would do in the Angular builder. You can read more in our guide about [styles and preprocessor options for Storybook](/recipes/storybook/angular-configuring-styles).\n\n```json\n\"build-storybook\": {\n \"executor\": \"@storybook/angular:build-storybook\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"outputDir\": \"dist/storybook/ngapp\",\n \"configDir\": \"libs/ui/.storybook\",\n \"browserTarget\": \"ui:build-storybook\",\n \"compodoc\": false,\n \"styles\": [\"some-styles.css\"],\n \"stylePreprocessorOptions\": {\n \"includePaths\": [\"some-style-paths\"]\n }\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n" + "examplesFile": "---\ntitle: Storybook builder executor examples\ndescription: This page contains examples for the @nx/storybook:build executor.\n---\n\n`project.json`:\n\n```json\n//...\n\"ui\": {\n \"targets\": {\n //...\n \"build-storybook\": {\n \"executor\": \"@nx/storybook:build\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"outputDir\": \"dist/storybook/ui\",\n \"configDir\": \"libs/ui/.storybook\"\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n }\n}\n```\n\n```bash\nnx run ui:build-storybook\n```\n\n## Examples\n\n### For non-Angular projects\n\n{% tabs %}\n{% tab label=\"Working in docsMode\" %}\n\nYou can work in docs mode, building a documentation-only site, by setting the `docsMode` option to `true` and using the `@storybook/addon-docs` addon.\n\nRead more on the [Storybook documentation page for `addon-docs`](https://storybook.js.org/addons/@storybook/addon-docs).\n\n```json\n\"storybook\": {\n \"executor\": \"@nx/storybook:build\",\n \"options\": {\n \"port\": 4400,\n \"configDir\": \"libs/ui/.storybook\",\n \"docsMode\": true\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n\n### For Angular projects\n\n{% tabs %}\n{% tab label=\"Default configuration\" %}\n\nThis is the default configuration for Angular projects using Storybook. You can see that it uses the native `@storybook/angular:build-storybook` executor. You can read more about the configuration options at the relevant [Storybook documentation page](https://storybook.js.org/docs/angular/get-started/install).\n\n```json\n\"build-storybook\": {\n \"executor\": \"@storybook/angular:build-storybook\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"outputDir\": \"dist/storybook/ngapp\",\n \"configDir\": \"apps/ngapp/.storybook\",\n \"browserTarget\": \"ngapp:build\",\n \"compodoc\": false\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Changing the browserTarget\" %}\n\nYou can set the [`browserTarget`](/deprecated/storybook/angular-browser-target) to use `build-storybook` as the builder. This is most useful in the cases where your project does not have a `build` target.\n\n```json\n\"build-storybook\": {\n \"executor\": \"@storybook/angular:build-storybook\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"outputDir\": \"dist/storybook/ngapp\",\n \"configDir\": \"apps/ngapp/.storybook\",\n \"browserTarget\": \"ngapp:build-storybook\",\n \"compodoc\": false\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"Adding styles\" %}\n\nYou can add paths to stylesheets to be included in the Storybook build by using the `styles` array. You can also add `stylePreprocessorOptions`, much like you would do in the Angular builder. You can read more in our guide about [styles and preprocessor options for Storybook](/recipes/storybook/angular-configuring-styles).\n\n```json\n\"build-storybook\": {\n \"executor\": \"@storybook/angular:build-storybook\",\n \"outputs\": [\"{options.outputDir}\"],\n \"options\": {\n \"outputDir\": \"dist/storybook/ngapp\",\n \"configDir\": \"apps/ngapp/.storybook\",\n \"browserTarget\": \"ngapp:build-storybook\",\n \"compodoc\": false,\n \"styles\": [\"some-styles.css\"],\n \"stylePreprocessorOptions\": {\n \"includePaths\": [\"some-style-paths\"]\n }\n },\n \"configurations\": {\n \"ci\": {\n \"quiet\": true\n }\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n" }, "description": "Build Storybook.", "aliases": [], diff --git a/docs/generated/packages/storybook/generators/configuration.json b/docs/generated/packages/storybook/generators/configuration.json index 727a01236570c5..553c517331cc54 100644 --- a/docs/generated/packages/storybook/generators/configuration.json +++ b/docs/generated/packages/storybook/generators/configuration.json @@ -104,7 +104,7 @@ } }, "required": ["name", "uiFramework"], - "examplesFile": "---\ntitle: Storybook configuration generator examples\ndescription: This page contains examples for the @nx/storybook:configuration generator.\n---\n\nThis is a framework-agnostic generator for setting up Storybook configuration for a project.\n\n```bash\nnx g @nx/storybook:configuration\n```\n\nStarting Nx 16, Nx does not support Storybook v6 any more. So, Nx will configure your project to use Storybook v7. If you are not on Storybook 7 yet, please migrate. You can read more about how to migrate to Storybook 7 in our [Storybook 7 migration generator](/packages/storybook/generators/migrate-7) guide.\n\nWhen running this generator, you will be prompted to provide the following:\n\n- The `name` of the project you want to generate the configuration for.\n- The `uiFramework` you want to use. Supported values are:\n - `@storybook/angular`\n - `@storybook/html-webpack5`\n - `@storybook/nextjs`\n - `@storybook/preact-webpack5`\n - `@storybook/react-webpack5`\n - `@storybook/react-vite`\n - `@storybook/server-webpack5`\n - `@storybook/svelte-webpack5`\n - `@storybook/svelte-vite`\n - `@storybook/sveltekit`\n - `@storybook/vue-webpack5`\n - `@storybook/vue-vite`\n - `@storybook/vue3-webpack5`\n - `@storybook/vue3-vite`\n - `@storybook/web-components-webpack5`\n - `@storybook/web-components-vite`\n- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, 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/angular/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/packages/storybook/documents/interaction-tests).\n\nYou must provide a `name` and a `uiFramework` for the generator to work.\n\nYou can read more about how this generator works, in the [Storybook package overview page](/packages/storybook#generating-storybook-configuration).\n\nIf you are using Angular, React, React Native or Next.js in your project, it's best to use the framework specific generator:\n\n- [React Storybook Configuration Generator](/nx-api/react/generators/storybook-configuration) (React and Next.js projects)\n\n- [Angular Storybook Configuration Generator](/nx-api/angular/generators/storybook-configuration)\n\n- [React Native Storybook Configuration Generator](/nx-api/react-native/generators/storybook-configuration)\n\n## Examples\n\n### Generate Storybook configuration using JavaScript\n\n```bash\nnx g @nx/storybook:configuration ui --uiFramework=@storybook/web-components-vite --tsConfiguration=false\n```\n\nBy 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`).\n", + "examplesFile": "---\ntitle: Storybook configuration generator examples\ndescription: This page contains examples for the @nx/storybook:configuration generator.\n---\n\nThis is a framework-agnostic generator for setting up Storybook configuration for a project.\n\n```bash\nnx g @nx/storybook:configuration\n```\n\n{% callout type=\"info\" title=\"Nx uses Storybook 7\" %}\nNx does not support Storybook v6 any more. So, Nx will configure your project to use Storybook v7. If you are not on Storybook 7 yet, please migrate. Please follow our [Storybook 7 migration generator](/packages/storybook/generators/migrate-7) guide.\n{% /callout %}\n\nIf you are using Angular, React, Next.js, Vue or React Native in your project, it's best to use the framework specific Storybook configuration generator:\n\n- [React Storybook Configuration Generator](/nx-api/react/generators/storybook-configuration) (React and Next.js projects)\n\n- [Angular Storybook Configuration Generator](/nx-api/angular/generators/storybook-configuration)\n\n- [Vue Storybook Configuration Generator](/nx-api/vue/generators/storybook-configuration)\n\n\n\n- [React Native Storybook Configuration Generator](/nx-api/react-native/generators/storybook-configuration)\n\nIf you are not using one of the framework-specific generators mentioned above, when running this generator you will be prompted to provide the following:\n\n- The `name` of the project you want to generate the configuration for.\n- The `uiFramework` you want to use. Supported values are:\n - `@storybook/angular`\n - `@storybook/html-webpack5`\n - `@storybook/nextjs`\n - `@storybook/preact-webpack5`\n - `@storybook/react-webpack5`\n - `@storybook/react-vite`\n - `@storybook/server-webpack5`\n - `@storybook/svelte-webpack5`\n - `@storybook/svelte-vite`\n - `@storybook/sveltekit`\n - `@storybook/vue-webpack5`\n - `@storybook/vue-vite`\n - `@storybook/vue3-webpack5`\n - `@storybook/vue3-vite`\n - `@storybook/web-components-webpack5`\n - `@storybook/web-components-vite`\n- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, 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/angular/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/packages/storybook/documents/interaction-tests).\n\nYou must provide a `name` and a `uiFramework` for the generator to work.\n\nYou can read more about how this generator works, in the [Storybook package overview page](/packages/storybook#generating-storybook-configuration).\n\n## Examples\n\n### Generate Storybook configuration using JavaScript\n\n```bash\nnx g @nx/storybook:configuration ui --uiFramework=@storybook/web-components-vite --tsConfiguration=false\n```\n\nBy 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`).\n", "presets": [] }, "description": "Add Storybook configuration to a UI library or an application.", diff --git a/docs/generated/packages/storybook/generators/migrate-7.json b/docs/generated/packages/storybook/generators/migrate-7.json index 8dc6de3e667072..441de62d77fa90 100644 --- a/docs/generated/packages/storybook/generators/migrate-7.json +++ b/docs/generated/packages/storybook/generators/migrate-7.json @@ -34,7 +34,7 @@ "default": false } }, - "examplesFile": "---\ntitle: Storybook 7 Migration Generator Examples\ndescription: This page contains examples for the @nx/storybook:migrate-7 generator.\n---\n\n{% callout type=\"info\" title=\"Available on Nx v15.9\" %}\nThis is a new feature available on Nx v15.9.0. If you are using an older version of Nx, please [upgrade](/packages/nx/documents/migrate).\n{% /callout %}\n\n{% callout type=\"info\" title=\"Setting up Storybook 7 in a new workspace\" %}\nFor setting up Storybook version 7 in a new Nx workspace, or a workspace that does NOT already have Storybook configured already, please refer to our [Storybook 7 setup guide](/packages/storybook/documents/storybook-7-setup).\n{% /callout %}\n\nStorybook 7 is a major release that brings a lot of new features and improvements. You can read more about it in the [Storybook 7.0.0 release article](https://storybook.js.org/blog/storybook-7-0/). Apart from the new features and improvements it introduces, it also brings some breaking changes. You can read more about them in the [Storybook 7 migration docs](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-65x-to-700) and the [Storybook 7.0.0 migration guide](https://storybook.js.org/docs/react/migration-guide).\n\nYou can now migrate your existing Nx workspace with Storybook configuration to use Storybook version 7. To help you, Nx offers the `@nx/storybook:migrate-7` generator. This generator will help you migrate your existing Storybook setup to version 7.\n\n## How to use it\n\nJust call:\n\n```bash\nnpx nx g @nx/storybook:migrate-7\n```\n\n{% callout type=\"warning\" title=\"Commit your changes\" %}\nIt is advised that you start with a clean git history before running this generator, since it is going to be making lots of changes to your workspace.\n{% /callout %}\n\nYou can run this generator using the above command, without passing any options. This will start the migration process for all your projects that have Storybook configured. The logs will explain what is happening in every step, and the logs are mixed Nx and Storybook CLI logs. During the process you will be prompted by the Storybook CLI to accept the automigration scripts. You can read more about that in the next section.\n\nWhen the generator finishes, you will see a summary of the changes that were made to your workspace, and it will also create a new file, called `storybook-migration-summary.md` at the root of your project, which will contain a list of all the changes that were made to your workspace.\n\n### Accept the automigration prompts\n\nThe Storybook CLI (running through our generator) will prompt you to run some code generators and modifiers.\n\nYou can say `yes` to these prompts, which are usually the following (there may be more or less, depending on your setup,\nand depending on the latest versions of the Storybook CLI - this code is NOT managed by Nx, but by Storybook):\n\n- `mainjsFramework`: It will try to add the `framework` field in your project's `.storybook/main.js|ts` file.\n- `eslintPlugin`: installs the `eslint-plugin-storybook`\n- `newFrameworks`: removes unused dependencies (eg. `@storybook/builder-webpack5`, `@storybook/manager-webpack5`, `@storybook/builder-vite`)\n- `autodocsTrue`: adds `autodocs: true` to your project's `.storybook/main.js|ts` file\n\n### Check the result\n\nOnce the generator finishes, and the Storybook CLI automigration scripts have run, you should check the result. Examples of migrated `.storybook/main.js|ts` files would look like this:\n\n#### Full example for Angular projects\n\nHere is an example of a project-level `.storybook/main.js|ts` file for an Angular project that has been migrated to Storybook version 7:\n\n```ts {% fileName=\"apps/my-angular-app/.storybook/main.js\" %}\nconst config = {\n stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],\n addons: ['@storybook/addon-essentials'],\n framework: {\n name: '@storybook/angular',\n options: {},\n },\n};\n\nexport default config;\n```\n\n#### Full example for React projects with Vite\n\nHere is an example of a project-level `.storybook/main.js|ts` file for a React project using Vite that has been migrated to Storybook version 7:\n\n```ts {% fileName=\"apps/my-react-app/.storybook/main.js\" %}\nconst config = {\n stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],\n addons: ['@storybook/addon-essentials'],\n framework: {\n name: '@storybook/react-vite',\n options: {\n builder: {\n viteConfigPath: 'apps/rv1/vite.config.ts',\n },\n },\n },\n};\n\nexport default config;\n```\n\n### Make sure that all works by running Storybook\n\nYou can now use Storybook 7! 🎉\n\n```bash\nnpx nx build-storybook PROJECT_NAME\n```\n\nand\n\n```bash\nnpx nx storybook PROJECT_NAME\n```\n\n## Run the generator by automatically accepting the Storybook CLI prompts\n\nYou can run the generator with the `--autoAcceptAllPrompts` flag, which will automatically accept all the Storybook CLI prompts. This is useful if you want to run the generator in a CI environment, or if you want to run the generator in a script. Or if you are sure that you want to accept all the prompts!\n\n```bash\nnpx nx g @nx/storybook:migrate-7 --autoAcceptAllPrompts\n```\n\nThe Storybook CLI may still ask you about some things, but mostly it should just run the whole migration suite uninterrupted.\n\n## Run the migration manually\n\nNx gives you the ability to run all the migration steps one by one, manually, but still with the help of our migrator. To help you out with the commands that you need to run, Nx will print out the instructions if you run the generator with the `--onlyShowListOfCommands` flag, like this:\n\n```bash\nnpx nx g @nx/storybook:migrate-7 --onlyShowListOfCommands\n```\n\nEssentially, the way to run the migration manually is the following:\n\n1. Call the Nx generator to show you the list of commands:\n `npx nx g @nx/storybook:migrate-7 --onlyShowListOfCommands`\n2. Call the Storybook upgrade script:\n `npx storybook@latest upgrade`\n3. Call the Nx generator to prepare your files for migration. The steps are explained in [Step 02](#step-02) above.\n `nx g @nx/storybook:migrate-7 --onlyPrepare`\n4. Call the Storybook automigrate scripts for each one of the projects using Storybook (the `@nx/storybook:migrate-7` will give you the list of all the commands)\n5. Call the Nx generator to finish the migration. The steps are explained in [Step 04](#step-04) above.\n `nx g @nx/storybook:migrate-7 --afterMigration`\n\n## How the generator works under the hood\n\nNow let's see how the `@nx/storybook:migrate-7` generator works under the hood. It essentially does the following things:\n\n### Step 01\n\nIt calls the Storybook CLI upgrade script:\n\n```bash\nnpx storybook@latest upgrade\n```\n\nThis script will upgrade your Storybook dependencies to the latest version, as explained in the [Storybook documentation](https://storybook.js.org/docs/7.0/react/configure/upgrading).\n\n### Step 02\n\nIt prepares all your project-level `.storybook/main.js|ts` files, so that\nthe Storybook automigration scripts can run successfully. This means that it makes the following adjustments to your files:\n\n- Remove the \"`as StorybookConfig`\" typecast from the `.storybook/main.ts` files, if you have any `.storybook/main.ts` files with typecast, since it is not needed any more\n- Remove the \"`path.resolve`\" calls from the Next.js Storybook configuration in project-level `.storybook/main.js|ts` files, if it exists, since it breaks the Storybook automigration scripts\n\n### Step 03\n\nIt calls the Storybook CLI automigrate script, for each one of your projects that have Storybook configured. It does that by passing the `--config-dir` flag and the `--renderer` flag, for each one of your projects that has Storybook configured. An example command would look like this:\n\n```bash\nnpx storybook@latest automigrate --config-dir apps/my-react-app/.storybook --renderer @storybook/react\n```\n\nThis script will make changes to your Storybook configuration files, and other changes to your repository, to make it work for Storybook 7, as explained in the [Storybook documentation](https://storybook.js.org/docs/7.0/react/configure/upgrading).\n\n### Step 04\n\nAfter the Storybook CLI automigrate scripts have run, some additional adjustments are made to your workspace, to make sure that everything is working as expected. These adjustments are as follows:\n\n- Remove the \"`vite-tsconfig-paths`\" plugin from the Storybook configuration files for Vite projects, since it's no longer needed in v7\n- Add the \"`viteConfigPath`\" option to the Storybook builder options for Vite projects, since now Storybook needs the path to the Vite config file\n- Change the import package for the `StorybookConfig` type to be framework specific (e.g. from `@storybook/common` to `@storybook/react-vite` for React projects using Vite)\n- Add the \"`lit`\" package to your workspace, if you are using Web Components\n- Remove the \"`uiFramework`\" option from your project's Storybook targets\n\nOur generator is based on the guide to migration using the Storybook CLI, sp please refer to the [Storybook 7 migration guide](https://chromatic-ui.notion.site/Storybook-7-migration-guide-dbf41fa347304eb2a5e9c69b34503937) for more information.\n\n## I am not on Nx 15.9.0 yet but I still want to migrate to Storybook 7\n\nYou can migrate to Storybook 7 by just using the [Storybook `upgrade` and `automigrate` scripts](https://storybook.js.org/docs/7.0/react/configure/upgrading), but you will have to manually point the `automigrate` script to each one of your projects using Storybook, explained in [Step 03](#step-03) above.\n\nFirst, you would have to run the `npx storybook@latest upgrade` to get the latest versions of all the `@storybook/*` packages. Then, for each one of your projects that use Storybook, you would have to run `npx storybook@latest automigrate --config-dir /.storybook --renderer @storybook/`.\n\nThe `@nx/storybook:migrate-7` generator helps you by figuring out all the project paths and the renderers that need to be passed in the `automigrate` script, and also by performing a number of adjustments to your code to make sure the migration scripts run smoothly, so it is recommended to use the generator instead of running the scripts manually.\n\n## Report any issues and bugs\n\nPlease report any issues and bugs you find [on the Nx GitHub page](https://github.com/nrwl/nx/issues/new/choose) or on the [Storybook GitHub page](https://github.com/storybookjs/storybook/issues/new/choose).\n", + "examplesFile": "---\ntitle: Storybook 7 Migration Generator Examples\ndescription: This page contains examples for the @nx/storybook:migrate-7 generator.\n---\n\n{% callout type=\"info\" title=\"Setting up Storybook 7 in a new workspace\" %}\nFor setting up Storybook version 7 in a new Nx workspace, or a workspace that does NOT already have Storybook configured already, please refer to our [Storybook 7 setup guide](/packages/storybook/documents/storybook-7-setup).\n{% /callout %}\n\nStorybook 7 is a major release that brings a lot of new features and improvements. You can read more about it in the [Storybook 7.0.0 release article](https://storybook.js.org/blog/storybook-7-0/). Apart from the new features and improvements it introduces, it also brings some breaking changes. You can read more about them in the [Storybook 7 migration docs](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-65x-to-700) and the [Storybook 7.0.0 migration guide](https://storybook.js.org/docs/react/migration-guide).\n\nYou can now migrate your existing Nx workspace with Storybook configuration to use Storybook version 7. To help you, Nx offers the `@nx/storybook:migrate-7` generator. This generator will help you migrate your existing Storybook setup to version 7.\n\n## How to use it\n\nJust call:\n\n```bash\nnpx nx g @nx/storybook:migrate-7\n```\n\n{% callout type=\"warning\" title=\"Commit your changes\" %}\nIt is advised that you start with a clean git history before running this generator, since it is going to be making lots of changes to your workspace.\n{% /callout %}\n\nYou can run this generator using the above command, without passing any options. This will start the migration process for all your projects that have Storybook configured. The logs will explain what is happening in every step, and the logs are mixed Nx and Storybook CLI logs. During the process you will be prompted by the Storybook CLI to accept the automigration scripts. You can read more about that in the next section.\n\nWhen the generator finishes, you will see a summary of the changes that were made to your workspace, and it will also create a new file, called `storybook-migration-summary.md` at the root of your project, which will contain a list of all the changes that were made to your workspace.\n\n### Accept the automigration prompts\n\nThe Storybook CLI (running through our generator) will prompt you to run some code generators and modifiers.\n\nYou can say `yes` to these prompts, which are usually the following (there may be more or less, depending on your setup,\nand depending on the latest versions of the Storybook CLI - this code is NOT managed by Nx, but by Storybook):\n\n- `mainjsFramework`: It will try to add the `framework` field in your project's `.storybook/main.js|ts` file.\n- `eslintPlugin`: installs the `eslint-plugin-storybook`\n- `newFrameworks`: removes unused dependencies (eg. `@storybook/builder-webpack5`, `@storybook/manager-webpack5`, `@storybook/builder-vite`)\n- `autodocsTrue`: adds `autodocs: true` to your project's `.storybook/main.js|ts` file\n\n### Check the result\n\nOnce the generator finishes, and the Storybook CLI automigration scripts have run, you should check the result. Examples of migrated `.storybook/main.js|ts` files would look like this:\n\n#### Full example for Angular projects\n\nHere is an example of a project-level `.storybook/main.js|ts` file for an Angular project that has been migrated to Storybook version 7:\n\n```ts {% fileName=\"apps/my-angular-app/.storybook/main.js\" %}\nconst config = {\n stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],\n addons: ['@storybook/addon-essentials'],\n framework: {\n name: '@storybook/angular',\n options: {},\n },\n};\n\nexport default config;\n```\n\n#### Full example for React projects with Vite\n\nHere is an example of a project-level `.storybook/main.js|ts` file for a React project using Vite that has been migrated to Storybook version 7:\n\n```ts {% fileName=\"apps/my-react-app/.storybook/main.js\" %}\nconst config = {\n stories: ['../src/app/**/*.stories.@(js|jsx|ts|tsx|mdx)'],\n addons: ['@storybook/addon-essentials'],\n framework: {\n name: '@storybook/react-vite',\n options: {\n builder: {\n viteConfigPath: 'apps/rv1/vite.config.ts',\n },\n },\n },\n};\n\nexport default config;\n```\n\n### Make sure that all works by running Storybook\n\nYou can now use Storybook 7! 🎉\n\n```bash\nnpx nx build-storybook PROJECT_NAME\n```\n\nand\n\n```bash\nnpx nx storybook PROJECT_NAME\n```\n\n## Run the generator by automatically accepting the Storybook CLI prompts\n\nYou can run the generator with the `--autoAcceptAllPrompts` flag, which will automatically accept all the Storybook CLI prompts. This is useful if you want to run the generator in a CI environment, or if you want to run the generator in a script. Or if you are sure that you want to accept all the prompts!\n\n```bash\nnpx nx g @nx/storybook:migrate-7 --autoAcceptAllPrompts\n```\n\nThe Storybook CLI may still ask you about some things, but mostly it should just run the whole migration suite uninterrupted.\n\n## Run the migration manually\n\nNx gives you the ability to run all the migration steps one by one, manually, but still with the help of our migrator. To help you out with the commands that you need to run, Nx will print out the instructions if you run the generator with the `--onlyShowListOfCommands` flag, like this:\n\n```bash\nnpx nx g @nx/storybook:migrate-7 --onlyShowListOfCommands\n```\n\nEssentially, the way to run the migration manually is the following:\n\n1. Call the Nx generator to show you the list of commands:\n `npx nx g @nx/storybook:migrate-7 --onlyShowListOfCommands`\n2. Call the Storybook upgrade script:\n `npx storybook@latest upgrade`\n3. Call the Nx generator to prepare your files for migration. The steps are explained in [Step 02](#step-02) above.\n `nx g @nx/storybook:migrate-7 --onlyPrepare`\n4. Call the Storybook automigrate scripts for each one of the projects using Storybook (the `@nx/storybook:migrate-7` will give you the list of all the commands)\n5. Call the Nx generator to finish the migration. The steps are explained in [Step 04](#step-04) above.\n `nx g @nx/storybook:migrate-7 --afterMigration`\n\n## How the generator works under the hood\n\nNow let's see how the `@nx/storybook:migrate-7` generator works under the hood. It essentially does the following things:\n\n### Step 01\n\nIt calls the Storybook CLI upgrade script:\n\n```bash\nnpx storybook@latest upgrade\n```\n\nThis script will upgrade your Storybook dependencies to the latest version, as explained in the [Storybook documentation](https://storybook.js.org/docs/7.0/react/configure/upgrading).\n\n### Step 02\n\nIt prepares all your project-level `.storybook/main.js|ts` files, so that\nthe Storybook automigration scripts can run successfully. This means that it makes the following adjustments to your files:\n\n- Remove the \"`as StorybookConfig`\" typecast from the `.storybook/main.ts` files, if you have any `.storybook/main.ts` files with typecast, since it is not needed any more\n- Remove the \"`path.resolve`\" calls from the Next.js Storybook configuration in project-level `.storybook/main.js|ts` files, if it exists, since it breaks the Storybook automigration scripts\n\n### Step 03\n\nIt calls the Storybook CLI automigrate script, for each one of your projects that have Storybook configured. It does that by passing the `--config-dir` flag and the `--renderer` flag, for each one of your projects that has Storybook configured. An example command would look like this:\n\n```bash\nnpx storybook@latest automigrate --config-dir apps/my-react-app/.storybook --renderer @storybook/react\n```\n\nThis script will make changes to your Storybook configuration files, and other changes to your repository, to make it work for Storybook 7, as explained in the [Storybook documentation](https://storybook.js.org/docs/7.0/react/configure/upgrading).\n\n### Step 04\n\nAfter the Storybook CLI automigrate scripts have run, some additional adjustments are made to your workspace, to make sure that everything is working as expected. These adjustments are as follows:\n\n- Remove the \"`vite-tsconfig-paths`\" plugin from the Storybook configuration files for Vite projects, since it's no longer needed in v7\n- Add the \"`viteConfigPath`\" option to the Storybook builder options for Vite projects, since now Storybook needs the path to the Vite config file\n- Change the import package for the `StorybookConfig` type to be framework specific (e.g. from `@storybook/common` to `@storybook/react-vite` for React projects using Vite)\n- Add the \"`lit`\" package to your workspace, if you are using Web Components\n- Remove the \"`uiFramework`\" option from your project's Storybook targets\n\nOur generator is based on the guide to migration using the Storybook CLI, sp please refer to the [Storybook 7 migration guide](https://chromatic-ui.notion.site/Storybook-7-migration-guide-dbf41fa347304eb2a5e9c69b34503937) for more information.\n\n## I am not on Nx 15.9.0 yet but I still want to migrate to Storybook 7\n\nYou can migrate to Storybook 7 by just using the [Storybook `upgrade` and `automigrate` scripts](https://storybook.js.org/docs/7.0/react/configure/upgrading), but you will have to manually point the `automigrate` script to each one of your projects using Storybook, explained in [Step 03](#step-03) above.\n\nFirst, you would have to run the `npx storybook@latest upgrade` to get the latest versions of all the `@storybook/*` packages. Then, for each one of your projects that use Storybook, you would have to run `npx storybook@latest automigrate --config-dir /.storybook --renderer @storybook/`.\n\nThe `@nx/storybook:migrate-7` generator helps you by figuring out all the project paths and the renderers that need to be passed in the `automigrate` script, and also by performing a number of adjustments to your code to make sure the migration scripts run smoothly, so it is recommended to use the generator instead of running the scripts manually.\n\n## Report any issues and bugs\n\nPlease report any issues and bugs you find [on the Nx GitHub page](https://github.com/nrwl/nx/issues/new/choose) or on the [Storybook GitHub page](https://github.com/storybookjs/storybook/issues/new/choose).\n", "presets": [] }, "description": "Migrate to Storybook version 7.", diff --git a/docs/shared/packages/storybook/best-practices.md b/docs/shared/packages/storybook/best-practices.md index 319143e26ce7eb..1ed7dfc91b0762 100644 --- a/docs/shared/packages/storybook/best-practices.md +++ b/docs/shared/packages/storybook/best-practices.md @@ -19,7 +19,7 @@ Storybook helps you test your UIs. You can read more about testing with Storyboo ### Documentation -Storybook helps you document your UI elements, or your design system, effectively and in an interactive way. You can read more in the [How-to document components](https://storybook.js.org/docs/react/writing-docs/introduction) documentation page. Essentially, you can use Storybook to publish a catalog of your components. A catalog that you can share with the design team, the developer team, the product team, anyone else in the product development process, or even the client. The components are isolated, interactive, and can be represented in all possible forms that they can take (e.g. for a button: enabled, disabled, active, etc). You can read more about publishing your Storybook in the [Publish Storybook](https://storybook.js.org/docs/react/sharing/publish-storybook) documentation page. +Storybook helps you document your UI elements, or your design system, effectively and in an interactive way. You can read more in the [How to document components](https://storybook.js.org/docs/react/writing-docs/introduction) documentation page. Essentially, you can use Storybook to publish a catalog of your components. A catalog that you can share with the design team, the developer team, the product team, anyone else in the product development process, or even the client. The components are isolated, interactive, and can be represented in all possible forms that they can take (e.g. for a button: enabled, disabled, active, etc). You can read more about publishing your Storybook in the [Publish Storybook](https://storybook.js.org/docs/react/sharing/publish-storybook) documentation page. ## Nx and Storybook @@ -42,38 +42,34 @@ If your project is not configured yet, check out one of these guides: - [Set up Storybook for React Projects](/recipes/storybook/overview-react) - [Set up Storybook for Angular Projects](/recipes/storybook/overview-angular) +- +- [Set up Storybook for Vue Projects](/recipes/storybook/overview-vue) If your project is [already configured](/nx-api/storybook), you can use the `stories` generator: -- [React stories generator](/nx-api/react/generators/stories) +- [React (and Next.js) stories generator](/nx-api/react/generators/stories) - [React Native stories generator](/nx-api/react-native/generators/stories) +- [Vue stories generator](/nx-api/vue/generators/stories) + - [Angular stories generator](/nx-api/angular/generators/stories) The stories generator will read your inputs (if you’re using Angular), or your props (if you're using React), and will generate stories with the corresponding arguments/controls already prefilled. -#### Cypress tests generation - -Nx also generates Cypress tests for your components, that point to the corresponding component’s story. You can read more about how the Cypress tests are generated and how they look like in the [storybook-configuration generator documentation](/recipes/storybook/overview-react#cypress-tests-for-stories). +#### Storybook interaction tests -Take a look at the generated code of the Cypress test file, specifically at the URL which Cypress visits: - -```javascript -cy.visit( - '/iframe.html?id=buttoncomponent--primary&args=text:Click+me!;padding;style:default' -); -``` +[Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing) allow you to test user interactions within your Storybook stories. It enhances your [Storybook](https://storybook.js.org/) setup, ensuring that not only do your components look right, but they also work correctly when interacted with. -Cypress visits the URL that hosts the story of the component we are testing, adding values to its controls (eg. `args=text:Click+me!`). Then, the test attempts to validate that the values are correctly applied. +Nx will generate interaction tests for your stories. You can read more in our [Setting up Storybook Interaction Tests with Nx guide](/recipes/storybook/interaction-tests). ### CI/CD tools Now let’s see how Nx helps in the CI/CD journey, as well. -#### Cypress testing +#### Interaction tests in your CI -When you are running the Cypress tests for a project, Cypress will start the Storybook server of that project. The Storybook server will fire up a Storybook instance, hosting all the components's stories for that project. The e2e tests will then run, which actually visit the stories and perform the tests there. Cypress will be configured to start and stop the Storybook server. The results will be cached, and they will go through the Nx graph, meaning that Nx will know if the tests need to be run again or not, depending on the affected status of your project. +You can set up your interaction tests to run as part of your CI. You can read more in the [Storybook docs](https://storybook.js.org/docs/react/writing-tests/test-runner#set-up-ci-to-run-**tests**). #### Serve @@ -97,7 +93,7 @@ Setting up Storybook on Nx reflects - and takes advantage of - the [mental model ##### Development and debugging -In the process of setting up Storybook in your Nx workspace that we described above, you end up with one Storybook instance per project. That way, you can use your project’s Storybook targets to serve and build Storybook: +In the process of setting up Storybook in your Nx workspace that we described above, you end up with one Storybook instance per project. That way, you can use your project’s Storybook targets to serve, test and build Storybook: ```shell nx storybook my-project @@ -109,11 +105,13 @@ and nx build-storybook my-project ``` -This feature is extremely useful when developing locally. The containerized stories in your Storybook are the only ones that are built/served when you want to debug just one component, or just one library. You don’t have to wait for a huge Storybook containing all your stories in your repository to fire up. You just need to wait for the Storybook of a single project to start. This speeds up the process. +and -##### E2e tests with Cypress +```shell +nx test-storybook my-project +``` -If you’re using Cypress, and you’re taking advantage of the generated Cypress tests that our Storybook generators generate, then your e2e tests are also going to be much faster. When you run your e2e tests for a particular project, Cypress is only going to start the specific Storybook instance, and it’s going to take much less time than having to start an all-including universal Storybook. +This feature is extremely useful when developing locally. The containerized stories in your Storybook are the only ones that are built/served/tested when you want to debug just one component, or just one library. You don’t have to wait for a huge Storybook containing all your stories in your repository to fire up. You just need to wait for the Storybook of a single project to start. This speeds up the process. ##### Caching, affected, dependency management diff --git a/docs/shared/packages/storybook/plugin-overview.md b/docs/shared/packages/storybook/plugin-overview.md index 0b939e93d28111..6191b500440817 100644 --- a/docs/shared/packages/storybook/plugin-overview.md +++ b/docs/shared/packages/storybook/plugin-overview.md @@ -7,10 +7,6 @@ description: This is an overview page for the Storybook plugin in Nx. It explain This guide will briefly walk you through using Storybook within an Nx workspace. -{% callout type="info" title="Storybook 7 by default" %} -Starting with Nx 16, Storybook 7 is used by default to configure your projects. -{% /callout %} - ## Setting Up Storybook ### Add the Storybook plugin @@ -86,7 +82,7 @@ nx g @nx/react-native:storybook-configuration my-react-native-project {% /tab %} {% /tabs %} -These framework-specific generators will also **generate stories** for you. +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: @@ -113,9 +109,7 @@ Choosing one of these frameworks will have the following effects on your workspa 2. Nx will generate a project-level `.storybook` folder (located under `libs/your-project/.storybook` or `apps/your-project/.storybook`) containing the essential configuration files for Storybook. -3. Nx will create new `targets` in your project's `project.json`, called `storybook` and `build-storybook`, containing all the necessary configuration to serve and build Storybook. - -4. Nx will generate a new Cypress e2e app for your project (if there isn't one already) to run against the Storybook instance. +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. @@ -147,6 +141,20 @@ or nx build-storybook project-name ``` +### Testing Storybook + +With the Storybook server running, you can test Storybook (run all the interaction tests) using this command: + +```shell +nx run project-name:test-storybook +``` + +or + +```shell +nx test-storybook project-name +``` + ### Anatomy of the Storybook setup When running the Nx Storybook generator, it'll configure the Nx workspace to be able to run Storybook seamlessly. It'll create a project specific Storybook configuration. @@ -171,17 +179,21 @@ To register a [Storybook addon](https://storybook.js.org/addons/) for all Storyb 1. In your project's `.storybook/main.ts` file, in the `addons` array of the `module.exports` object, add the new addon: - ```typescript {% fileName="/.storybook/main.js" %} - module.exports = { - stories: [...], - ..., - addons: [..., '@storybook/addon-essentials'], - }; - ``` +```typescript {% fileName="/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/react-vite'; + +const config: StorybookConfig = { + ... + addons: ['@storybook/addon-essentials', '@storybook/addon-interactions', ...], + ... +}; + +export default config; +``` 2. If a decorator is required, in each project's `/.storybook/preview.ts`, you can export an array called `decorators`. - ```typescript {% fileName="/.storybook/preview.js" %} + ```typescript {% fileName="/.storybook/preview.ts" %} import someDecorator from 'some-storybook-addon'; export const decorators = [someDecorator]; ``` diff --git a/docs/shared/recipes/storybook/angular-storybook-compodoc.md b/docs/shared/recipes/storybook/angular-storybook-compodoc.md index 31fb5a34d1ef1a..eceb3cec7b7f19 100644 --- a/docs/shared/recipes/storybook/angular-storybook-compodoc.md +++ b/docs/shared/recipes/storybook/angular-storybook-compodoc.md @@ -138,14 +138,6 @@ Let's see the result for our `web` app `storybook` target, for example (in `apps }, ``` -{% callout type="warning" title="Check the version!" %} -Make sure you are on Nx version `>=14.1.8` and your `storybook` target is using `@storybook/angular:start-storybook` as the `executor` (like the example above). - -If you are using an older version of Nx, you can use [`nx migrate`](/nx-api/nx/documents/migrate) to migrate your codebase to a later version. Using `nx migrate` will also make sure to update your `storybook` and `build-storybook` targets to match the new format. - -If you **are** on Nx `>=14.1.8` and you are still using the old executor (`@nx/storybook:storybook`), you can read our documentation about the [Angular Storybook targets](/deprecated/storybook/angular-storybook-targets) to help you change your `storybook` and `build-storybook` targets across your workspace for your Angular projects using Storybook. -{% /callout %} - ### 4. Let Storybook know of the `documentation.json` file In your project's `.storybook/preview.js` file (for example for your `web` app the path would be `apps/web/.storybook/preview.js`), add the following: diff --git a/docs/shared/recipes/storybook/custom-builder-configs.md b/docs/shared/recipes/storybook/custom-builder-configs.md index 34886558ae898f..3dcaf045087d5d 100644 --- a/docs/shared/recipes/storybook/custom-builder-configs.md +++ b/docs/shared/recipes/storybook/custom-builder-configs.md @@ -17,7 +17,7 @@ If you want to add a global configuration for Webpack or Vite in your workspace, The `webpackFinal` field would look like this: -```ts {% fileName=".storybook/main.js" %} +```ts {% fileName=".storybook/main.ts" %} webpackFinal: async (config, { configType }) => { // Make whatever fine-grained changes you need that should apply to all storybook configs @@ -30,7 +30,7 @@ webpackFinal: async (config, { configType }) => { The `viteFinal` field would look like this: -```ts {% fileName=".storybook/main.js" %} +```ts {% fileName=".storybook/main.ts" %} async viteFinal(config, { configType }) { if (configType === 'DEVELOPMENT') { // Your development configuration goes here @@ -46,7 +46,7 @@ async viteFinal(config, { configType }) { In the `viteFinal` case, you would have to import the `mergeConfig` function from `vite`. So, on the top of your root `.storybook/main.js|ts` file, you would have to add: -```ts {% fileName=".storybook/main.js" %} +```ts {% fileName=".storybook/main.ts" %} import { mergeConfig } from 'vite'; ``` @@ -56,37 +56,49 @@ import { mergeConfig } from 'vite'; You can customize the `webpack` configuration for a specific project by adding a `webpackFinal` field in your project-specific `.storybok/main.js|ts` file, like this: -```ts {% fileName="apps/my-react-webpack-app/.storybook/main.js" %} -export default { - ... - webpackFinal: async (config, { configType }) => { +```ts {% fileName="apps/my-react-webpack-app/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/react-webpack5'; +const config: StorybookConfig = { + stories: ..., + addons: ..., + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + webpackFinal: async (config, { configType }) => { // add your own webpack tweaks if needed - return config; }, }; + +export default config; ``` If you are using a global, root-level, `webpack` configuration in your project, you can customize or extend that for a specific project like this: -```ts {% fileName="apps/my-react-webpack-app/.storybook/main.js" %} +```ts {% fileName="apps/my-react-webpack-app/.storybook/main.ts" %} import rootMain from '../../../.storybook/main'; -export default { +const config: StorybookConfig = { ...rootMain, - ... + stories: ..., + addons: ..., + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, webpackFinal: async (config, { configType }) => { // apply any global webpack configs that might have been specified in .storybook/main.js if (rootMain.webpackFinal) { config = await rootMain.webpackFinal(config, { configType }); } - // add your own webpack tweaks if needed - return config; }, }; + +export default config; ``` Take note how, in this case, we are first applying the global `webpack` configuration, and then adding our own tweaks. If you don't want to apply any global configuration, you can just return your own configuration, and skip the `rootMain.webpackFinal` check. @@ -95,50 +107,56 @@ Take note how, in this case, we are first applying the global `webpack` configur You can customize the `vite` configuration for a specific project by adding a `viteFinal` field in your project-specific `.storybok/main.js|ts` file, like this: -```ts {% fileName="apps/my-react-vite-app/.storybook/main.js" %} +```ts {% fileName="apps/my-react-vite-app/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/react-vite'; import { mergeConfig } from 'vite'; -import viteTsConfigPaths from 'vite-tsconfig-paths'; -export default { - ... +const config: StorybookConfig = { + stories: ..., + addons: ..., + framework: { + name: '@storybook/react-vite', + options: { + builder: { + viteConfigPath: 'apps/web/vite.config.ts', + }, + }, + }, async viteFinal(config, { configType }) { return mergeConfig(config, { ... }); }, }; + +export default config; ``` If you are using a global, root-level, `vite` configuration in your workspace, you can customize or extend that for a specific project like this: -```ts {% fileName="apps/my-react-vite-app/.storybook/main.js" %} +```ts {% fileName="apps/my-react-vite-app/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/react-vite'; import { mergeConfig } from 'vite'; import rootMain from '../../../.storybook/main'; -export default { - ... +const config: StorybookConfig = { + ...rootMain, + stories: ..., + addons: ..., + framework: { + name: '@storybook/react-vite', + options: { + builder: { + viteConfigPath: 'apps/web/vite.config.ts', + }, + }, + }, async viteFinal(config, { configType }) { return mergeConfig(config, { ...((await rootMain.viteFinal(config, { configType })) ?? {}) }); }, }; -``` - -So, a full project-level `.storybook/main.js|ts` file for a Vite.js project would look like this: - -```ts {% fileName="apps/my-react-vite-app/.storybook/main.js" %} -import { mergeConfig } from 'vite'; -export default { - stories: ['../src/app/**/*.stories.@(mdx|js|jsx|ts|tsx)'], - addons: ['@storybook/addon-essentials'], - framework: { - name: '@storybook/react-vite', - options: {}, - }, - async viteFinal(config, { configType }) { - return mergeConfig(config, {}); - }, -}; +export default config; ``` diff --git a/docs/shared/recipes/storybook/interaction-tests.md b/docs/shared/recipes/storybook/interaction-tests.md index 6e72ed89fe56ac..2a60fc6293e2f2 100644 --- a/docs/shared/recipes/storybook/interaction-tests.md +++ b/docs/shared/recipes/storybook/interaction-tests.md @@ -15,6 +15,7 @@ You can read more about Storybook interaction tests in the following sections of - [Storybook interaction tests for React](https://storybook.js.org/docs/react/writing-tests/interaction-testing) - [Storybook interaction tests for Angular](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) +- [Storybook interaction tests for Vue](https://storybook.js.org/docs/vue/writing-tests/interaction-testing) - [Storybook test runner](https://storybook.js.org/docs/react/writing-tests/test-runner) - [The `play` function](https://storybook.js.org/docs/react/writing-stories/play-function) @@ -42,6 +43,13 @@ nx g @nx/angular:storybook-configuration project-name --interactionTests=true nx g @nx/react:storybook-configuration project-name --interactionTests=true ``` +{% /tab %} +{% tab label="Vue" %} + +```shell +nx g @nx/vue:storybook-configuration project-name --interactionTests=true +``` + {% /tab %} {% tab label="No framework" %} diff --git a/docs/shared/recipes/storybook/one-storybook-for-all.md b/docs/shared/recipes/storybook/one-storybook-for-all.md index 39af36a494edfd..0aaddf3bc9f261 100644 --- a/docs/shared/recipes/storybook/one-storybook-for-all.md +++ b/docs/shared/recipes/storybook/one-storybook-for-all.md @@ -8,7 +8,7 @@ description: Dive into a comprehensive guide on how to consolidate all your Stor This guide extends the [Using Storybook in a Nx workspace - Best practices](/nx-api/storybook/documents/best-practices) guide. In that guide, we discussed the best practices of using Storybook in a Nx workspace. We explained the main concepts and the mental model of how to best set up Storybook. In this guide, we are going to see how to put that into practice, by looking at a real-world example. We are going to see how you can publish one single Storybook for your workspace. -This case would work if all your projects (applications and libraries) containing stories that you want to use are using the same framework (Angular, React, Vue, etc). The reason is that you will be importing the stories in a central host Storybook's `.storybook/main.js`, and we will be using one specific builder to build that Storybook. Storybook does not support mixing frameworks in the same Storybook instance. +This case would work if all your projects (applications and libraries) containing stories that you want to use are using the same framework (Angular, React, Vue, etc). The reason is that you will be importing the stories in a central host Storybook's `.storybook/main.ts`, and we will be using one specific builder to build that Storybook. Storybook does not support mixing frameworks in the same Storybook instance. Let’s see how we can implement this solution: @@ -18,14 +18,14 @@ Let’s see how we can implement this solution: ### Generate a new library that will host our Storybook instance -According to the framework you are using, use the corresponding generator to generate a new library. Let’s suppose that you are using React and all your stories are using `@storybook/react`: +According to the framework you are using, use the corresponding generator to generate a new library. Let’s suppose that you are using React and all your stories are using the `@storybook/react-vite` framework: {% callout type="note" title="Directory Flag Behavior Changes" %} The command below uses the `as-provided` directory flag behavior, which is the default in Nx 16.8.0. If you're on an earlier version of Nx or using the `derived` option, omit the `--directory` flag. See the [workspace layout documentation](/reference/nx-json#workspace-layout) for more details. {% /callout %} ```shell -nx g @nx/react:library storybook-host --directory=libs/storybook-host +nx g @nx/react:library storybook-host --directory=libs/storybook-host --bundler=none --unitTestRunner=none --projectNameAndRootFormat=as-provided ``` Now, you have a new library, which will act as a shell/host for all your stories. @@ -35,28 +35,40 @@ Now, you have a new library, which will act as a shell/host for all your stories Now let’s configure our new library to use Storybook, using the [`@nx/storybook:configuration` generator](/nx-api/storybook/generators/configuration). Run: ```shell -nx g @nx/storybook:configuration storybook-host +nx g @nx/storybook:configuration storybook-host --interactionTests=true --uiFramework=@storybook/react-vite ``` -and choose the framework you want to use (in our case, choose `@storybook/react`). +This generator will only generate the `storybook`, `build-storybook` and `test-storybook` targets in our new library's `project.json` (`libs/storybook-host/project.json`), and also the `libs/storybook-host/.storybook` folder. This is all we care about. We don’t need any stories in this project, since we will be importing the stories from other projects in our workspace. So, if you want, you can delete the contents of the `src/lib` folder. You may also delete the `lint` and `test` targets in `libs/storybook-host/project.json`. We will not be needing those. -This generator will only generate the `storybook` and `build-storybook` targets in our new library's `project.json` (`libs/storybook-host/project.json`), and also the `libs/storybook-host/.storybook` folder. This is all we care about. We don’t need any stories in this project, since we will be importing the stories from other projects in our workspace. So, if you want, you can delete the contents of the `src/lib` folder. You may also delete the `lint` and `test` targets in `libs/storybook-host/project.json`. We will not be needing those. +### Import the stories in our library's main.ts -### Import the stories in our library's main.js +Now it’s time to import the stories of our other projects in our new library's `./storybook/main.ts`. -Now it’s time to import the stories of our other projects in our new library's `./storybook/main.js`. +Here is a sample `libs/storybook-host/.storybook/main.ts` file: -Here is a sample `libs/storybook-host/.storybook/main.js` file: +```javascript {% fileName="libs/storybook-host/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/react-vite'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { mergeConfig } from 'vite'; -```javascript {% fileName="libs/storybook-host/.storybook/main.js" %} -module.exports = { - core: { builder: 'webpack5' }, +const config: StorybookConfig = { stories: ['../../**/ui/**/src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'], - addons: ['@storybook/addon-essentials', '@nx/react/plugins/storybook'], + addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'], + framework: { + name: '@storybook/react-vite', + options: {}, + }, + + viteFinal: async (config) => + mergeConfig(config, { + plugins: [nxViteTsPaths()], + }), }; + +export default config; ``` -Notice how we only link the stories matching that pattern. According to your workspace set-up, you can adjust the pattern, or add more patterns, so that you can match all the stories in all the projects you want. +Notice how we only link the stories matching a specific pattern. According to your workspace set-up, you can adjust the pattern, or add more patterns, so that you can match all the stories in all the projects you want. For example: @@ -68,28 +80,38 @@ stories: [ ]; ``` -### Import the stories in Storybook’s tsconfig.json +### Import the stories in tsconfig.storybook.json -If you are using Angular, do not forget to import the stories in the TypeScript configuration of Storybook. +Here is a sample `libs/storybook-host/tsconfig.storybook.json` file: -Here is a sample `libs/storybook-host-angular/.storybook/tsconfig.json` file: - -```json {% fileName="libs/storybook-host-angular/.storybook/tsconfig.json" %} +```json {% fileName="libs/storybook-host/tsconfig.storybook.json" %} { "extends": "../tsconfig.json", "compilerOptions": { "emitDecoratorMetadata": true }, - "exclude": ["../**/*.spec.ts"], - "include": ["../../**/ui/**/src/lib/**/*.stories.ts", "*.js"] + ... + "include": [ + "../../**/ui/**/src/**/*.stories.ts", + "../../**/ui/**/src/**/*.stories.js", + "../../**/ui/**/src/**/*.stories.jsx", + "../../**/ui/**/src/**/*.stories.tsx", + "../../**/ui/**/src/**/*.stories.mdx", + ".storybook/*.js", + ".storybook/*.ts" + ] } ``` -Notice how in the `include` array we are specifying the paths to our stories, using the same pattern we used in our `.storybook/main.js`. +Notice how in the `include` array we are specifying the paths to our stories, using the same pattern we used in our `.storybook/main.ts`. + +{% callout type="note" title="Angular Storybook tsconfig.json" %} +For Angular, that file is in your project's `.storybook` directory, so in this case it would be under `libs/storybook-host/.storybook/tsconfig.json`. +{% /callout %} -### Serve or build your Storybook! +### Serve or build your Storybook -Now you can serve or build your Storybook as you would, normally. And then you can publish the bundled app! +Now you can serve, test or build your Storybook as you would, normally. And then you can publish the bundled app! ```shell nx storybook storybook-host @@ -101,6 +123,12 @@ or nx build-storybook storybook-host ``` +or + +```shell +nx test-storybook storybook-host +``` + ## Use cases that apply to this solution Can be used for: @@ -113,7 +141,7 @@ Ideal for: ## Extras - Dependencies -Your new Storybook host, essentially, depends on all the projects from which it is importing stories. This means whenever one of these projects updates a component, or updates a story, our Storybook host would have to rebuild, to reflect these changes. It cannot rely on the cached result. However, Nx does not understand the imports in `libs/storybook-host/.storybook/main.js`, and the result is that Nx does not know which projects the Storybook host depends on, based solely on the `main.js` imports. The good thing is that there is a solution to this. You can manually add the projects your Storybook host depends on as implicit dependencies in your project’s `project.json`, in the implicit dependencies array. +Your new Storybook host, essentially, depends on all the projects from which it is importing stories. This means whenever one of these projects updates a component, or updates a story, our Storybook host would have to rebuild, to reflect these changes. It cannot rely on the cached result. However, Nx does not understand the imports in `libs/storybook-host/.storybook/main.ts`, and the result is that Nx does not know which projects the Storybook host depends on, based solely on the `main.ts` imports. The good thing is that there is a solution to this. You can manually add the projects your Storybook host depends on as implicit dependencies in your project’s `project.json`, in the implicit dependencies array. For example, `libs/storybook-host/project.json`: diff --git a/docs/shared/recipes/storybook/one-storybook-per-scope.md b/docs/shared/recipes/storybook/one-storybook-per-scope.md index 5bf13b486c44b6..edbef1bef47d55 100644 --- a/docs/shared/recipes/storybook/one-storybook-per-scope.md +++ b/docs/shared/recipes/storybook/one-storybook-per-scope.md @@ -22,12 +22,7 @@ Say, for example, that you have a client app, an admin app, and a number of UI l happynrwl/ ├── apps/ │ ├── client/ -│ ├── client-e2e/ │ ├── admin/ -│ ├── admin-e2e/ -│ ├── client-ui-header-e2e/ -│ ├── admin-ui-dashboard-e2e/ -│ └── shared-ui-cta-e2e/ ├── libs/ │ ├── client/ │ │ ├── feature/ @@ -75,7 +70,7 @@ happynrwl/ In this case you can see that we have two deployable applications, `client` and `admin`, and we have a number of UI libraries, each associated with a specific app. For example, `client-ui-header` is a UI library associated with the `client` app, and `admin-ui-dashboard` is a UI library associated with the `admin` app. We also have one more library, the `shared-ui-cta` library, which is shared between the two apps. The way we have structured our folders is such that any new library that is related to the `client` app will go in the `libs/client` folder, and in that folder we have a sub-folder to determine if the new library is related to `ui` or anything else. The same applies to the `admin` app. Any library shared between the two apps will live under a subfolder of the `libs/shared` folder. -Notice how we have already generated Storybook configuration and stories for all of our `ui` libraries. We have also generated a `e2e` application for each of these `ui` libraries, which is going to use the corresponding Storybook instance to run end-to-end tests. +Notice how we have already generated Storybook configuration and stories for all of our `ui` libraries. ## Setting up the thematic Storybook instances @@ -96,19 +91,31 @@ The commands below uses the `as-provided` directory flag behavior, which is the {% /callout %} ```shell -nx g @nx/angular:lib storybook-host-client --directory=libs/storybook-host-client -nx g @nx/angular:lib storybook-host-admin --directory=libs/storybook-host-admin -nx g @nx/angular:lib storybook-host-shared --directory=libs/storybook-host-shared +nx g @nx/angular:lib storybook-host-client --directory=libs/storybook-host-client --projectNameAndRootFormat=as-provided +``` + +```shell +nx g @nx/angular:lib storybook-host-admin --directory=libs/storybook-host-admin --projectNameAndRootFormat=as-provided +``` + +```shell +nx g @nx/angular:lib storybook-host-shared --directory=libs/storybook-host-shared --projectNameAndRootFormat=as-provided ``` ### Generate the Storybook configuration for the libraries -Now, we need to generate Storybook configuration for all these new libraries. We don't want to generate `stories` or a new Cypress project for these libraries, so we can run the following commands: +Now, we need to generate Storybook configuration for all these new libraries. We don't want to generate `stories` for these libraries, so we can run the following commands: ```shell -nx g @nx/storybook:configuration storybook-host-client --uiFramework=@storybook/angular -nx g @nx/storybook:configuration storybook-host-admin --uiFramework=@storybook/angular -nx g @nx/storybook:configuration storybook-host-shared --uiFramework=@storybook/angular +nx g @nx/storybook:configuration storybook-host-client --uiFramework=@storybook/angular --interactionTests=true +``` + +```shell +nx g @nx/storybook:configuration storybook-host-admin --uiFramework=@storybook/angular --interactionTests=true +``` + +```shell +nx g @nx/storybook:configuration storybook-host-shared --uiFramework=@storybook/angular --interactionTests=true ``` ### Import the stories @@ -117,14 +124,21 @@ Now that our Storybook configuration is ready for our new libraries, we can go a Thanks to our folder structure, we can easily configure Storybook to import all the stories under a specific folder, for example, which are associated with a specific scope. -For example, `libs/storybook-host-admin/.storybook/main.js`: +For example, `libs/storybook-host-admin/.storybook/main.ts`: -```javascript {% fileName="libs/storybook-host-admin/.storybook/main.js" %} -module.exports = { - core: { builder: 'webpack5' }, +```javascript {% fileName="libs/storybook-host-admin/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/angular'; + +const config: StorybookConfig = { stories: ['../../admin/ui/**/src/lib/**/*.stories.ts'], - addons: ['@storybook/addon-essentials'], + addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'], + framework: { + name: '@storybook/angular', + options: {}, + }, }; + +export default config; ``` And don't forget the `libs/storybook-host-admin/.storybook/tsconfig.json`: @@ -136,7 +150,7 @@ And don't forget the `libs/storybook-host-admin/.storybook/tsconfig.json`: "emitDecoratorMetadata": true }, "exclude": ["../**/*.spec.ts"], - "include": ["../../admin/ui/**/src/lib/**/*.stories.ts", "*.js"] + "include": ["../../admin/ui/**/src/lib/**/*.stories.ts", "*.ts"] } ``` diff --git a/docs/shared/recipes/storybook/one-storybook-with-composition.md b/docs/shared/recipes/storybook/one-storybook-with-composition.md index 0b9e2e7184c682..a5ec060360c486 100644 --- a/docs/shared/recipes/storybook/one-storybook-with-composition.md +++ b/docs/shared/recipes/storybook/one-storybook-with-composition.md @@ -35,7 +35,7 @@ The command below uses the `as-provided` directory flag behavior, which is the d So, let’s use React for the Storybook Composition host library: ```shell -nx g @nx/react:lib storybook-host --directory=libs/storybook-host +nx g @nx/react:lib storybook-host --directory=libs/storybook-host --bundler=none --unitTestRunner=none --projectNameAndRootFormat=as-provided ``` Now that your library is generated, you can write your intro in the generated component (you can also do this later, it does not matter). @@ -45,22 +45,29 @@ Now that your library is generated, you can write your intro in the generated co Since you do need a story for your host Storybook, you should use the React storybook configuration generator, and actually choose to generate stories (not an e2e project though): ```shell -nx g @nx/react:storybook-configuration –-name=storybook-host +nx g @nx/react:storybook-configuration storybook-host --interactionTests=true --generateStories=true ``` -And choose `yes` to generate stories, and `no` to generate a Cypress app. - ### Change the Storybook port in the hosted apps Now it’s important to change the Storybook ports in the `storybook-host-angular` and `storybook-host-react`. Go to the `project.json` of each of these libraries (`libs/storybook-host-angular/project.json` and `libs/storybook-host-react/project.json`), find the `storybook` target, and set the port to `4401` and `4402` accordingly. This is because the Storybook Composition host is going to be looking at these ports to find which Storybooks to host, and which Storybook goes where. -### Add the `refs` to the main.js of the host library +### Add the `refs` to the main.ts of the host library Create the composition in ``: -```javascript {% fileName="libs/storybook-host/.storybook/main.js" %} -module.exports = { - core: { builder: 'webpack5' }, +```javascript {% fileName="libs/storybook-host/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/react-vite'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { mergeConfig } from 'vite'; + +const config: StorybookConfig = { + stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'], + addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'], + framework: { + name: '@storybook/react-vite', + options: {}, + }, refs: { 'angular-stories': { title: 'Angular Stories', @@ -71,9 +78,13 @@ module.exports = { url: 'http://localhost:4402', }, }, - stories: ['../src/lib/**/*.stories.tsx'], - addons: ['@storybook/addon-essentials', '@nx/react/plugins/storybook'], + viteFinal: async (config) => + mergeConfig(config, { + plugins: [nxViteTsPaths()], + }), }; + +export default config; ``` ### Serve the Storybook instances @@ -96,7 +107,7 @@ To deploy the composed Storybooks you need to do the following: 1. Deploy the `storybook-host-angular` Storybook 2. Deploy the `storybook-host-react` Storybook -3. Change the `refs` in `libs/storybook-host/.storybook/main.js` to point to the URLs of the deployed Storybooks mentioned above +3. Change the `refs` in `libs/storybook-host/.storybook/main.ts` to point to the URLs of the deployed Storybooks mentioned above 4. Deploy the `storybook-host` Storybook ## Use cases that apply to this solution diff --git a/docs/shared/recipes/storybook/plugin-angular.md b/docs/shared/recipes/storybook/plugin-angular.md index 40ebc46ba2a653..db3e38353c8906 100644 --- a/docs/shared/recipes/storybook/plugin-angular.md +++ b/docs/shared/recipes/storybook/plugin-angular.md @@ -73,74 +73,74 @@ and the result would be the following: {% /callout %} -## Cypress tests for Stories +## Example Files -The [`@nx/angular:storybook-configuration` generator](/nx-api/angular/generators/storybook-configuration) gives the option to set up an e2e Cypress app that is configured to run against the project's Storybook instance. +Let's take for example a library in your workspace, under `libs/feature/ui`, called `feature-ui` with a component, called `my-button`. -To launch Storybook and run the Cypress tests against the iframe inside of Storybook: +Let's say that the template for that component looks like this: -```shell -nx run project-name-e2e:e2e +```html {% fileName="libs/feature/ui/src/lib/my-button/my-button.component.html" %} + ``` -The url that Cypress points to should look like this: - -`'/iframe.html?id=buttoncomponent--primary&args=text:Click+me!;padding;style:default'` - -- `buttoncomponent` is a lowercase version of the `Title` in the `*.stories.ts` file. -- `primary` is the name of an individual story. -- `style=default` sets the `style` arg to a value of `default`. - -Changing args in the url query parameters allows your Cypress tests to test different configurations of your component. You can [read the documentation](https://storybook.js.org/docs/angular/writing-stories/args#setting-args-through-the-url) for more information. - -## Example Files - -Let's take for a example a library in your workspace, under `libs/feature/ui`, called `feature-ui` with a component, called `my-button`. +and the component looks like this: + +```typescript {% fileName="libs/feature/ui/src/lib/my-button/my-button.component.ts" %} +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'feature-ui-my-button', + templateUrl: './my-button.component.html', + styleUrls: ['./my-button.component.css'], +}) +export class MyButtonComponent { + @Input() text = 'Click me!'; + @Input() padding = 10; + @Input() disabled = true; +} +``` ### Story file The [`@nx/angular:storybook-configuration` generator](/nx-api/angular/generators/storybook-configuration) would generate a Story file that looks like this: ```typescript {% fileName="libs/feature/ui/src/lib/my-button/my-button.component.stories.ts" %} -import { Meta } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; import { MyButtonComponent } from './my-button.component'; +import { within } from '@storybook/testing-library'; +import { expect } from '@storybook/jest'; -export default { - title: 'MyButtonComponent', +const meta: Meta = { component: MyButtonComponent, -} as Meta; + title: 'MyButtonComponent', +}; +export default meta; +type Story = StoryObj; -export const Primary = { - render: (args: MyButtonComponent) => ({ - props: args, - }), +export const Primary: Story = { args: { text: 'Click me!', padding: 10, disabled: true, }, }; -``` - -### Cypress test file - -For the library described above, Nx would generate an E2E project called `feature-ui-e2e` with a Cypress test file that looks like this: -```typescript {% fileName="apps/feature-ui-e2e/src/e2e/my-button/my-button.component.cy.ts" %} -describe('feature-ui', () => { - beforeEach(() => - cy.visit( - '/iframe.html?id=mybuttoncomponent--primary&args=text:Click+me!;padding:10;disabled:true;' - ) - ); - - it('should contain the right text', () => { - cy.get('button').should('contain', 'Click me!'); - }); -}); +export const Heading: Story = { + args: { + text: 'Click me!', + padding: 10, + disabled: true, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + expect(canvas.getByText(/my-button works!/gi)).toBeTruthy(); + }, +}; ``` -Depending on your Cypress version, the file will end with `.spec.ts` or `.cy.ts`. +Notice the interaction test on the second story, inside the `play` function. This just tests if the default text that appears on generated components exists in the rendered component. You can edit this test to suit your needs. You can read more about interaction tests [here](https://storybook.js.org/docs/angular/writing-tests/interaction-testing). ## More Documentation diff --git a/docs/shared/recipes/storybook/plugin-react.md b/docs/shared/recipes/storybook/plugin-react.md index d468ca14f2564d..42c8dcdb801341 100644 --- a/docs/shared/recipes/storybook/plugin-react.md +++ b/docs/shared/recipes/storybook/plugin-react.md @@ -19,18 +19,6 @@ You can generate Storybook configuration for an individual React project by usin nx g @nx/react:storybook-configuration project-name ``` -## Nx React Storybook Preset - -The [`@nx/react`](/nx-api/react) package ships with a Storybook addon to make sure it uses the very same configuration as your Nx React application. When you generate a Storybook configuration for a project, it'll automatically add the addon to your configuration. - -```typescript -module.exports = { - ... - addons: ['@storybook/addon-essentials', ..., '@nx/react/plugins/storybook'], - ... -}; -``` - ## Auto-generate Stories The [`@nx/react:storybook-configuration` generator](/nx-api/react/generators/storybook-configuration) has the option to automatically generate `*.stories.ts|tsx` files for each component declared in the library. The stories will be generated using [Component Story Format 3 (CSF3)](https://storybook.js.org/blog/storybook-csf3-is-here/). @@ -85,72 +73,69 @@ and the result would be the following: {% /callout %} -## Cypress tests for Stories - -The [`@nx/react:storybook-configuration` generator](/nx-api/react/generators/storybook-configuration) gives the option to set up an e2e Cypress app that is configured to run against the project's Storybook instance. - -To launch Storybook and run the Cypress tests against the iframe inside of Storybook: - -```shell -nx run project-name-e2e:e2e -``` - -The url that Cypress points to should look like this: +## Example Files -`'/iframe.html?id=mybutton--primary&args=text:Click+me!;padding;style:default'` +Let's take for a example a library in your workspace, under `libs/feature/ui`, called `feature-ui` with a component, called `my-button`. -- `buttoncomponent` is a lowercase version of the `Title` in the `*.stories.tsx` file. -- `primary` is the name of an individual story. -- `style=default` sets the `style` arg to a value of `default`. +Let's say that your component code looks like this: -Changing args in the url query parameters allows your Cypress tests to test different configurations of your component. You can [read the documentation](https://storybook.js.org/docs/react/writing-stories/args#setting-args-through-the-url) for more information. +```typescript {% fileName="libs/feature/ui/src/lib/my-button/my-button.tsx" %} +export interface MyButtonProps { + text: string; + padding: number; + disabled: boolean; +} -## Example Files +export function MyButton(props: MyButtonProps) { + return ( + + ); +} -Let's take for a example a library in your workspace, under `libs/feature/ui`, called `feature-ui` with a component, called `my-button`. +export default MyButton; +``` ### Story file The [`@nx/react:storybook-configuration` generator](/nx-api/react/generators/storybook-configuration) would generate a Story file that looks like this: ```typescript {% fileName="libs/feature/ui/src/lib/my-button/my-button.stories.tsx" %} -import type { Meta } from '@storybook/react'; +import type { Meta, StoryObj } from '@storybook/react'; import { MyButton } from './my-button'; +import { within } from '@storybook/testing-library'; +import { expect } from '@storybook/jest'; -const Story: Meta = { +const meta: Meta = { component: MyButton, title: 'MyButton', }; -export default Story; +export default meta; +type Story = StoryObj; export const Primary = { args: { - text: 'Click me!', - padding: 10, - disabled: true, + text: '', + padding: 0, + disabled: false, }, }; -``` - -### Cypress test file - -For the library described above, Nx would generate an E2E project called `feature-ui-e2e` with a Cypress test file that looks like this: -```typescript {% fileName="apps/feature-ui-e2e/src/e2e/my-button/my-button.cy.ts" %} -describe('feature-ui: MyButton component', () => { - beforeEach(() => - cy.visit( - '/iframe.html?id=mybutton--primary&args=text:Click+me!;padding;style:default' - ) - ); - - it('should contain the right text', () => { - cy.get('button').should('contain', 'Click me!'); - }); -}); +export const Heading: Story = { + args: { + text: '', + padding: 0, + disabled: false, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + expect(canvas.getByText(/Welcome to MyButton!/gi)).toBeTruthy(); + }, +}; ``` -Depending on your Cypress version, the file will end with `.spec.ts` or `.cy.ts`. +Notice the interaction test on the second story, inside the `play` function. This just tests if the default text that appears on generated components exists in the rendered component. You can edit this test to suit your needs. You can read more about interaction tests [here](https://storybook.js.org/docs/react/writing-tests/interaction-testing). ## More Documentation diff --git a/docs/shared/recipes/storybook/storybook-composition-setup.md b/docs/shared/recipes/storybook/storybook-composition-setup.md index efe72f73584eb5..61defdb8217a5a 100644 --- a/docs/shared/recipes/storybook/storybook-composition-setup.md +++ b/docs/shared/recipes/storybook/storybook-composition-setup.md @@ -18,14 +18,17 @@ In essence, you have a Storybook running, which will be the host of the embeded ## How to use it -All you need is a URL of a live Storybook, and a "host" Storybook. In the `.storybook/main.js` file of the "host" Storybook, inside `module.exports` you add a new `refs` attribute, which will contain the link(s) for the composed Storybook(s). +All you need is a URL of a live Storybook, and a "host" Storybook. In the `.storybook/main.ts` file of the "host" Storybook, inside `module.exports` you add a new `refs` attribute, which will contain the link(s) for the composed Storybook(s). In the example below, we have a host Storybook running on local port 4400 (http://localhost:4400) - not displayed here. In it, we want to compose three other Storybooks. The "one-composed" and "two-composed", running on local ports `4401` and `4402` accordingly, as well as the [Storybook website's Storybook](https://next--storybookjs.netlify.app/official-storybook) which is live on the address that you see. ```javascript -// .storybook/main.js of our Host Storybook - assuming it's running on port 4400 -module.exports = { - ..., +// .storybook/main.ts of our Host Storybook - assuming it's running on port 4400 +import type { StorybookConfig } from '@storybook/react-vite'; +... + +const config: StorybookConfig = { + ... refs: { 'one-composed': { title: 'One composed', @@ -40,14 +43,17 @@ module.exports = { url: 'https://next--storybookjs.netlify.app/official-storybook/', }, }, + ... }; + +export default config; ``` -You can always read more in the [official Storybook docs](https://storybook.js.org/docs/angular/workflows/storybook-composition#compose-published-storybooks). +You can always read more in the [official Storybook docs](https://storybook.js.org/docs/react/workflows/storybook-composition#compose-published-storybooks). ## How to use it in Nx -It's quite easy to use this feature, in Nx and in general, since you do not need to make any code changes, you just need to have the "composed" Storybook instances (the ones you need to "compose") running, choose a "host" Storybook, and just add the composed Storybooks in it's `.storybook/main.js` file. +It's quite easy to use this feature, in Nx and in general, since you do not need to make any code changes, you just need to have the "composed" Storybook instances (the ones you need to "compose") running, choose a "host" Storybook, and just add the composed Storybooks in it's `.storybook/main.ts` file. Nx provides the [`run-many`](/nx-api/nx/documents/run-many) command, which will allow you to easily run multiple Storybooks at the same time. You need to run the `run-many` command with the parallel flag (eg. `--parallel=3`), because you want to run all your Storybooks in parallel. You can change the value of the `parallel` flag to be of as many Storybooks you want to run in parallel as you need. However, be **very carefull** with putting large numbers in this flag, since it can cause big delays or get stuck. You can play around and adjust that number to one your machine runs comfortably with. Keep in mind that you can add in this feature however many live/public Storybooks as you need (Storybooks that you do not run locally). @@ -102,13 +108,16 @@ Since we are using the `--parallel` flag, and the commands are executed in paral If we don't change the port numbers, and there are projects that want to use the same port for their Storybooks, the `run-many` command will change that port, and the result will be that we will not know for sure which of our projects runs on which port. The problem that this creates is that we will not be able to create the proper configuration for Storybook Composition, since we will not be able to tell which URLs our composed Storybooks run on. -### Add the refs in our host project's `.storybook/main.js` file +### Add the refs in our host project's `.storybook/main.ts` file -Now, we need to add to our host project's `main.js` file (the path of which would be `apps/main-host/.storybook/main.js`) a `refs` object, to configure our composition. An example of such a configuration looks like this: +Now, we need to add to our host project's `main.ts` file (the path of which would be `apps/main-host/.storybook/main.ts`) a `refs` object, to configure our composition. An example of such a configuration looks like this: -```javascript {% fileName="apps/main-host/.storybook/main.js" %} -module.exports = { - ..., +```javascript {% fileName="apps/main-host/.storybook/main.ts" %} +import type { StorybookConfig } from '@storybook/react-vite'; +... + +const config: StorybookConfig = { + ... refs: { one-composed: { title: 'One composed', @@ -123,7 +132,10 @@ module.exports = { url: 'http://localhost:4403', }, }, + ... }; + +export default config; ``` ### Optional: use `run-commands` and create a `storybook-composition` target diff --git a/packages/angular/src/generators/stories/stories-app.spec.ts b/packages/angular/src/generators/stories/stories-app.spec.ts index 9d502863d5fce0..4bcfb47ec89814 100644 --- a/packages/angular/src/generators/stories/stories-app.spec.ts +++ b/packages/angular/src/generators/stories/stories-app.spec.ts @@ -11,7 +11,7 @@ import { stripIndents } from '@nx/devkit'; // which is v9 while we are testing for the new v10 version jest.mock('@nx/cypress/src/utils/cypress-version'); -// TODO(v18): remove Cypress +// TODO(katerina): Nx 18 -> remove Cypress describe('angularStories generator: applications', () => { let tree: Tree; diff --git a/packages/angular/src/generators/stories/stories-lib.spec.ts b/packages/angular/src/generators/stories/stories-lib.spec.ts index b5b38e9230cfe9..4dd9e6674e8ea8 100644 --- a/packages/angular/src/generators/stories/stories-lib.spec.ts +++ b/packages/angular/src/generators/stories/stories-lib.spec.ts @@ -14,7 +14,7 @@ import { angularStoriesGenerator } from './stories'; // need to mock cypress otherwise it'll use the nx installed version from package.json // which is v9 while we are testing for the new v10 version jest.mock('@nx/cypress/src/utils/cypress-version'); -// TODO(v18): remove Cypress +// TODO(katerina): Nx 18 -> remove Cypress describe('angularStories generator: libraries', () => { const libName = 'test-ui-lib'; diff --git a/packages/angular/src/generators/storybook-configuration/storybook-configuration.ts b/packages/angular/src/generators/storybook-configuration/storybook-configuration.ts index e0c264394b8ebf..0a77114f359c5b 100644 --- a/packages/angular/src/generators/storybook-configuration/storybook-configuration.ts +++ b/packages/angular/src/generators/storybook-configuration/storybook-configuration.ts @@ -5,7 +5,7 @@ import { generateStorybookConfiguration } from './lib/generate-storybook-configu import { validateOptions } from './lib/validate-options'; import type { StorybookConfigurationOptions } from './schema'; -// TODO(v18): remove Cypress +// TODO(katerina): Nx 18 -> remove Cypress export async function storybookConfigurationGenerator( tree: Tree, options: StorybookConfigurationOptions diff --git a/packages/react/src/generators/storybook-configuration/configuration.spec.ts b/packages/react/src/generators/storybook-configuration/configuration.spec.ts index 595bcd6b8c1056..9b6b0a75095713 100644 --- a/packages/react/src/generators/storybook-configuration/configuration.spec.ts +++ b/packages/react/src/generators/storybook-configuration/configuration.spec.ts @@ -1,4 +1,4 @@ -// TODO(v18): remove Cypress +// TODO(katerina): Nx 18 -> remove Cypress import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { logger, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/react/src/generators/storybook-configuration/configuration.ts b/packages/react/src/generators/storybook-configuration/configuration.ts index f1da53ccda7197..d96478322f21ce 100644 --- a/packages/react/src/generators/storybook-configuration/configuration.ts +++ b/packages/react/src/generators/storybook-configuration/configuration.ts @@ -10,7 +10,7 @@ import { import { nxVersion } from '../../utils/versions'; async function generateStories(host: Tree, schema: StorybookConfigureSchema) { - // TODO(v18): remove Cypress + // TODO(katerina): Nx 18 -> remove Cypress ensurePackage('@nx/cypress', nxVersion); const { getE2eProjectName } = await import( '@nx/cypress/src/utils/project-name' diff --git a/packages/storybook/docs/build-storybook-executor-examples.md b/packages/storybook/docs/build-storybook-executor-examples.md index b07a201f341c3e..ae6f1245e2a393 100644 --- a/packages/storybook/docs/build-storybook-executor-examples.md +++ b/packages/storybook/docs/build-storybook-executor-examples.md @@ -74,8 +74,8 @@ This is the default configuration for Angular projects using Storybook. You can "outputs": ["{options.outputDir}"], "options": { "outputDir": "dist/storybook/ngapp", - "configDir": "libs/ui/.storybook", - "browserTarget": "ui:build", + "configDir": "apps/ngapp/.storybook", + "browserTarget": "ngapp:build", "compodoc": false }, "configurations": { @@ -97,8 +97,8 @@ You can set the [`browserTarget`](/deprecated/storybook/angular-browser-target) "outputs": ["{options.outputDir}"], "options": { "outputDir": "dist/storybook/ngapp", - "configDir": "libs/ui/.storybook", - "browserTarget": "ui:build-storybook", + "configDir": "apps/ngapp/.storybook", + "browserTarget": "ngapp:build-storybook", "compodoc": false }, "configurations": { @@ -121,8 +121,8 @@ You can add paths to stylesheets to be included in the Storybook build by using "outputs": ["{options.outputDir}"], "options": { "outputDir": "dist/storybook/ngapp", - "configDir": "libs/ui/.storybook", - "browserTarget": "ui:build-storybook", + "configDir": "apps/ngapp/.storybook", + "browserTarget": "ngapp:build-storybook", "compodoc": false, "styles": ["some-styles.css"], "stylePreprocessorOptions": { diff --git a/packages/storybook/docs/configuration-generator-examples.md b/packages/storybook/docs/configuration-generator-examples.md index 35c0b05c6897f9..275c98f17f5d7d 100644 --- a/packages/storybook/docs/configuration-generator-examples.md +++ b/packages/storybook/docs/configuration-generator-examples.md @@ -9,9 +9,23 @@ This is a framework-agnostic generator for setting up Storybook configuration fo nx g @nx/storybook:configuration ``` -Starting Nx 16, Nx does not support Storybook v6 any more. So, Nx will configure your project to use Storybook v7. If you are not on Storybook 7 yet, please migrate. You can read more about how to migrate to Storybook 7 in our [Storybook 7 migration generator](/packages/storybook/generators/migrate-7) guide. +{% callout type="info" title="Nx uses Storybook 7" %} +Nx does not support Storybook v6 any more. So, Nx will configure your project to use Storybook v7. If you are not on Storybook 7 yet, please migrate. Please follow our [Storybook 7 migration generator](/packages/storybook/generators/migrate-7) guide. +{% /callout %} -When running this generator, you will be prompted to provide the following: +If you are using Angular, React, Next.js, Vue or React Native in your project, it's best to use the framework specific Storybook configuration generator: + +- [React Storybook Configuration Generator](/nx-api/react/generators/storybook-configuration) (React and Next.js projects) + +- [Angular Storybook Configuration Generator](/nx-api/angular/generators/storybook-configuration) + +- [Vue Storybook Configuration Generator](/nx-api/vue/generators/storybook-configuration) + + + +- [React Native Storybook Configuration Generator](/nx-api/react-native/generators/storybook-configuration) + +If you are not using one of the framework-specific generators mentioned above, when running this generator you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - The `uiFramework` you want to use. Supported values are: @@ -37,14 +51,6 @@ You must provide a `name` and a `uiFramework` for the generator to work. You can read more about how this generator works, in the [Storybook package overview page](/packages/storybook#generating-storybook-configuration). -If you are using Angular, React, React Native or Next.js in your project, it's best to use the framework specific generator: - -- [React Storybook Configuration Generator](/nx-api/react/generators/storybook-configuration) (React and Next.js projects) - -- [Angular Storybook Configuration Generator](/nx-api/angular/generators/storybook-configuration) - -- [React Native Storybook Configuration Generator](/nx-api/react-native/generators/storybook-configuration) - ## Examples ### Generate Storybook configuration using JavaScript diff --git a/packages/storybook/docs/migrate-7-generator-examples.md b/packages/storybook/docs/migrate-7-generator-examples.md index 5e3f253772df0f..34aa0e8e408798 100644 --- a/packages/storybook/docs/migrate-7-generator-examples.md +++ b/packages/storybook/docs/migrate-7-generator-examples.md @@ -3,10 +3,6 @@ title: Storybook 7 Migration Generator Examples description: This page contains examples for the @nx/storybook:migrate-7 generator. --- -{% callout type="info" title="Available on Nx v15.9" %} -This is a new feature available on Nx v15.9.0. If you are using an older version of Nx, please [upgrade](/packages/nx/documents/migrate). -{% /callout %} - {% callout type="info" title="Setting up Storybook 7 in a new workspace" %} For setting up Storybook version 7 in a new Nx workspace, or a workspace that does NOT already have Storybook configured already, please refer to our [Storybook 7 setup guide](/packages/storybook/documents/storybook-7-setup). {% /callout %} diff --git a/packages/storybook/src/executors/build-storybook/build-storybook.impl.spec.ts b/packages/storybook/src/executors/build-storybook/build-storybook.impl.spec.ts deleted file mode 100644 index 53729e939c3f63..00000000000000 --- a/packages/storybook/src/executors/build-storybook/build-storybook.impl.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { ExecutorContext, logger } from '@nx/devkit'; -import { join } from 'path'; -import storybookBuilder from './build-storybook.impl'; -import * as executorContext from '../../utils/test-configs/executor-context.json'; -jest.mock('@storybook/core-server', () => { - // TODO(katerina): Fix when Nx17 - const buildStaticStandalone = jest - .fn() - .mockImplementation(() => Promise.resolve()); - const build = jest.fn().mockImplementation(() => Promise.resolve()); - return { - buildStaticStandalone, - build, - }; -}); -import * as build from '@storybook/core-server'; -import { CLIOptions } from '@storybook/types'; -import { CommonNxStorybookConfig } from '../../utils/models'; - -describe('Build storybook', () => { - let context: ExecutorContext; - let options: CLIOptions & CommonNxStorybookConfig; - - beforeEach(async () => { - options = { - configDir: join(__dirname, `/../../utils/test-configs/.storybook`), - outputDir: `/root/dist/storybook`, - }; - - context = executorContext as ExecutorContext; - }); - - it('should call the storybook build', async () => { - const loggerSpy = jest.spyOn(logger, 'info'); - - const buildSpy = jest - .spyOn(build, 'build') - .mockImplementation(() => Promise.resolve()); - - const result = await storybookBuilder(options, context); - - expect(buildSpy).toHaveBeenCalled(); - expect(loggerSpy).toHaveBeenNthCalledWith( - 1, - 'NX Storybook builder starting ...' - ); - expect(loggerSpy).toHaveBeenNthCalledWith( - 2, - 'NX Storybook builder finished ...' - ); - expect(loggerSpy).toHaveBeenNthCalledWith( - 3, - 'NX Storybook files available in /root/dist/storybook' - ); - - expect(result.success).toBeTruthy(); - }); -}); diff --git a/packages/storybook/src/executors/build-storybook/build-storybook.impl.ts b/packages/storybook/src/executors/build-storybook/build-storybook.impl.ts index 6b437c71157d07..4e7aa4617265fd 100644 --- a/packages/storybook/src/executors/build-storybook/build-storybook.impl.ts +++ b/packages/storybook/src/executors/build-storybook/build-storybook.impl.ts @@ -6,73 +6,33 @@ import { storybookConfigExistsCheck, storybookMajorVersion, } from '../../utils/utilities'; -import { CommonNxStorybookConfig } from '../../utils/models'; -import { getStorybookFrameworkPath, runStorybookSetupCheck } from '../utils'; export default async function buildStorybookExecutor( - options: CLIOptions & CommonNxStorybookConfig, + options: CLIOptions, context: ExecutorContext ) { storybookConfigExistsCheck(options.configDir, context.projectName); const storybook7 = storybookMajorVersion() === 7; - if (storybook7) { - const buildOptions: CLIOptions = options; - logger.info(`NX Storybook builder starting ...`); - await runInstance(buildOptions, storybook7); - logger.info(`NX Storybook builder finished ...`); - logger.info(`NX Storybook files available in ${buildOptions.outputDir}`); - return { success: true }; - } else { - // TODO(katerina): Remove Nx17 - // print warnings - runStorybookSetupCheck(options); - logger.error(pleaseUpgrade()); - - const frameworkPath = getStorybookFrameworkPath(options.uiFramework); - const { default: frameworkOptions } = await import(frameworkPath); - - const buildOptions: CLIOptions = { - ...options, - ...frameworkOptions, - frameworkPresets: [...(frameworkOptions.frameworkPresets || [])], - }; - - logger.info(`NX Storybook builder starting ...`); - await runInstance(buildOptions, storybook7); - logger.info(`NX Storybook builder finished ...`); - logger.info(`NX Storybook files available in ${buildOptions.outputDir}`); - return { success: true }; + if (!storybook7) { + throw pleaseUpgrade(); } + const buildOptions: CLIOptions = options; + logger.info(`NX Storybook builder starting ...`); + await runInstance(buildOptions); + logger.info(`NX Storybook builder finished ...`); + logger.info(`NX Storybook files available in ${buildOptions.outputDir}`); + return { success: true }; } -function runInstance( - options: CLIOptions, - storybook7: boolean -): Promise { const env = process.env.NODE_ENV ?? 'production'; process.env.NODE_ENV = env; - - if (storybook7) { - return build.build({ - ...options, - mode: 'static', - }); - } else { - // TODO(katerina): Remove Nx17 - const nodeVersion = process.version.slice(1).split('.'); - if (+nodeVersion[0] === 18) { - logger.warn(` - If you are using the @storybook/builder-vite you may experience issues with Node 18. - Please use Node 16 if you are using @storybook/builder-vite. - `); - } - return build.buildStaticStandalone({ - ...options, - ci: true, - } as any); - } + return build.build({ + ...options, + mode: 'static', + }); } diff --git a/packages/storybook/src/executors/build-storybook/schema.json b/packages/storybook/src/executors/build-storybook/schema.json index e84f16a1362c55..1811de5094014d 100644 --- a/packages/storybook/src/executors/build-storybook/schema.json +++ b/packages/storybook/src/executors/build-storybook/schema.json @@ -69,19 +69,6 @@ "type": "boolean", "description": "Starts Storybook in documentation mode. Learn more about it : https://storybook.js.org/docs/react/writing-docs/build-documentation#preview-storybooks-documentation." }, - "uiFramework": { - "type": "string", - "description": "Storybook framework npm package.", - "enum": [ - "@storybook/react", - "@storybook/html", - "@storybook/web-components", - "@storybook/vue", - "@storybook/vue3", - "@storybook/svelte" - ], - "x-deprecated": "Upgrade to Storybook 7." - }, "webpackStatsJson": { "type": ["boolean", "string"], "description": "Write Webpack Stats JSON to disk.", diff --git a/packages/storybook/src/executors/storybook/storybook.impl.spec.ts b/packages/storybook/src/executors/storybook/storybook.impl.spec.ts deleted file mode 100644 index 4bf6ee88d426bf..00000000000000 --- a/packages/storybook/src/executors/storybook/storybook.impl.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { ExecutorContext } from '@nx/devkit'; - -jest.mock('@storybook/core-server', () => ({ - // TODO(katerina): Fix when Nx17 - buildDev: jest.fn().mockImplementation(() => Promise.resolve()), - build: jest.fn().mockImplementation(() => - Promise.resolve({ - port: 4400, - }) - ), -})); -import { build } from '@storybook/core-server'; - -import storybookExecutor from './storybook.impl'; -import { join } from 'path'; -import { CLIOptions } from '@storybook/types'; -import { CommonNxStorybookConfig } from '../../utils/models'; - -describe('@nx/storybook:storybook', () => { - let context: ExecutorContext; - let options: CLIOptions & CommonNxStorybookConfig; - beforeEach(() => { - const rootPath = join(__dirname, `../../../../../`); - - options = { - port: 4400, - configDir: join(__dirname, `/../../utils/test-configs/.storybook`), - }; - - context = { - root: rootPath, - cwd: rootPath, - projectName: 'proj', - targetName: 'storybook', - projectsConfigurations: { - version: 2, - projects: { - proj: { - root: '', - sourceRoot: 'src', - targets: { - build: { - executor: '@nx/web:webpack', - options: {}, - }, - storybook: { - executor: '@nx/storybook:storybook', - options, - }, - }, - }, - }, - }, - nxJsonConfiguration: {}, - isVerbose: false, - }; - }); - - it('should provide options to storybook', async () => { - const iterator = storybookExecutor(options, context); - const { value } = await iterator.next(); - expect(value).toEqual({ - success: true, - info: { - baseUrl: 'http://localhost:4400', - port: 4400, - }, - }); - expect(build).toHaveBeenCalled(); - }); -}); diff --git a/packages/storybook/src/executors/storybook/storybook.impl.ts b/packages/storybook/src/executors/storybook/storybook.impl.ts index 27fdfc92e79a2b..c600c438ce318e 100644 --- a/packages/storybook/src/executors/storybook/storybook.impl.ts +++ b/packages/storybook/src/executors/storybook/storybook.impl.ts @@ -1,77 +1,47 @@ -import { ExecutorContext, logger } from '@nx/devkit'; +import { ExecutorContext } from '@nx/devkit'; import * as build from '@storybook/core-server'; import { pleaseUpgrade, storybookConfigExistsCheck, storybookMajorVersion, } from '../../utils/utilities'; -import { getStorybookFrameworkPath, runStorybookSetupCheck } from '../utils'; -import { CLIOptions } from '@storybook/types'; // TODO(katerina): Remove Nx17 -import { CommonNxStorybookConfig } from '../../utils/models'; +import { CLIOptions } from '@storybook/types'; export default async function* storybookExecutor( - options: CLIOptions & CommonNxStorybookConfig, + options: CLIOptions, context: ExecutorContext ): AsyncGenerator<{ success: boolean; info?: { port: number; baseUrl?: string }; }> { const storybook7 = storybookMajorVersion() === 7; - storybookConfigExistsCheck(options.configDir, context.projectName); - if (storybook7) { - const buildOptions: CLIOptions = options; - const result = await runInstance(buildOptions, storybook7); - yield { - success: true, - info: { - port: result?.['port'], - baseUrl: `${options.https ? 'https' : 'http'}://${ - options.host ?? 'localhost' - }:${result?.['port']}`, - }, - }; - await new Promise<{ success: boolean }>(() => {}); - } else { - // TODO(katerina): Remove Nx17 - // print warnings - runStorybookSetupCheck(options); - logger.error(pleaseUpgrade()); - - let frameworkPath = getStorybookFrameworkPath(options.uiFramework); - const frameworkOptions = (await import(frameworkPath)).default; - const buildOptions: CLIOptions = { - ...options, - ...frameworkOptions, - frameworkPresets: [...(frameworkOptions.frameworkPresets || [])], - }; - - await runInstance(buildOptions, storybook7); - yield { success: true }; - await new Promise<{ success: boolean }>(() => {}); + if (!storybook7) { + throw pleaseUpgrade(); } + storybookConfigExistsCheck(options.configDir, context.projectName); + const buildOptions: CLIOptions = options; + const result = await runInstance(buildOptions); + yield { + success: true, + info: { + port: result?.['port'], + baseUrl: `${options.https ? 'https' : 'http'}://${ + options.host ?? 'localhost' + }:${result?.['port']}`, + }, + }; + await new Promise<{ success: boolean }>(() => {}); } -function runInstance( - options: CLIOptions, - storybook7: boolean -): Promise { const env = process.env.NODE_ENV ?? 'development'; process.env.NODE_ENV = env; - if (storybook7) { - return build.build({ - ...options, - mode: 'dev', - }); - } else { - // TODO(katerina): Remove Nx17 - return build['buildDev']({ - ...options, - configType: env.toUpperCase(), - mode: 'dev', - } as any); - } + return build.build({ + ...options, + mode: 'dev', + }); } diff --git a/packages/storybook/src/executors/utils.ts b/packages/storybook/src/executors/utils.ts deleted file mode 100644 index 47500706dd7e27..00000000000000 --- a/packages/storybook/src/executors/utils.ts +++ /dev/null @@ -1,169 +0,0 @@ -// TODO(katerina): Remove Nx17 - DELETE whole file - -import { joinPathFragments, logger } from '@nx/devkit'; -import { findNodes } from '@nx/js'; -import { existsSync, readFileSync } from 'fs'; -import { join } from 'path'; -import { gte } from 'semver'; -import ts = require('typescript'); -import { CommonNxStorybookConfig } from '../utils/models'; -import { CLIOptions } from '@storybook/types'; - -export function getStorybookFrameworkPath(uiFramework) { - const serverOptionsPaths = { - '@storybook/react': '@storybook/react/dist/cjs/server/options', - '@storybook/html': '@storybook/html/dist/cjs/server/options', - '@storybook/vue': '@storybook/vue/dist/cjs/server/options', - '@storybook/vue3': '@storybook/vue3/dist/cjs/server/options', - '@storybook/web-components': - '@storybook/web-components/dist/cjs/server/options', - '@storybook/svelte': '@storybook/svelte/dist/cjs/server/options', - }; - - if (isStorybookV62onwards(uiFramework)) { - return serverOptionsPaths[uiFramework]; - } else { - return `${uiFramework}/dist/server/options`; - } -} - -function isStorybookV62onwards(uiFramework: string) { - try { - const storybookPackageVersion = require(join( - uiFramework, - 'package.json' - )).version; - return gte(storybookPackageVersion, '6.2.0-rc.4'); - } catch (e) { - try { - const storybookPackageVersion = require(join( - '@storybook/core-server', - 'package.json' - )).version; - return gte(storybookPackageVersion, '6.2.0-rc.4'); - } catch (e) { - throw new Error( - `Error: ${e} - - It looks like you don\'t have Storybook installed. - Please run the @nx/storybook:configuration generator, - or run "npm/yarn" again to install your dependencies.` - ); - } - } -} - -export function runStorybookSetupCheck( - options: CLIOptions & CommonNxStorybookConfig -) { - webpackFinalPropertyCheck(options); - reactWebpack5Check(options); -} - -function reactWebpack5Check(options: CLIOptions & CommonNxStorybookConfig) { - if (options.uiFramework === '@storybook/react') { - const source = mainJsTsFileContent(options.configDir); - const rootSource = mainJsTsFileContent('.storybook'); - // check whether the current Storybook configuration has the webpack 5 builder enabled - if ( - builderIsWebpackButNotWebpack5(source) && - builderIsWebpackButNotWebpack5(rootSource) - ) { - logger.warn(` - It looks like you use Webpack 5 but your Storybook setup is not configured to leverage that - and thus falls back to Webpack 4. - Make sure you upgrade your Storybook config to use Webpack 5. - - - https://gist.github.com/shilman/8856ea1786dcd247139b47b270912324#upgrade - - `); - } - } -} - -function mainJsTsFileContent(configFolder: string): ts.SourceFile { - let storybookConfigFilePath = joinPathFragments(configFolder, 'main.js'); - - if (!existsSync(storybookConfigFilePath)) { - storybookConfigFilePath = joinPathFragments(configFolder, 'main.ts'); - } - - if (!existsSync(storybookConfigFilePath)) { - // looks like there's no main config file, so skip - return; - } - - const storybookConfig = readFileSync(storybookConfigFilePath, { - encoding: 'utf8', - }); - - return ts.createSourceFile( - storybookConfigFilePath, - storybookConfig, - ts.ScriptTarget.Latest, - true - ); -} - -function webpackFinalPropertyCheck( - options: CLIOptions & CommonNxStorybookConfig -) { - let placesToCheck = [ - { - path: joinPathFragments('.storybook', 'webpack.config.js'), - result: false, - }, - { - path: joinPathFragments(options.configDir, 'webpack.config.js'), - result: false, - }, - ]; - - placesToCheck = placesToCheck - .map((entry) => { - return { - ...entry, - result: existsSync(entry.path), - }; - }) - .filter((x) => x.result === true); - - if (placesToCheck.length > 0) { - logger.warn( - ` - You have a webpack.config.js files in your Storybook configuration: - ${placesToCheck.map((x) => `- "${x.path}"`).join('\n ')} - - Consider switching to the "webpackFinal" property declared in "main.js" (or "main.ts") instead. - ${ - options.uiFramework === '@storybook/react' - ? 'https://nx.dev/storybook/migrate-webpack-final-react' - : 'https://nx.dev/storybook/migrate-webpack-final-angular' - } - ` - ); - } -} - -export function builderIsWebpackButNotWebpack5( - storybookConfig: ts.SourceFile -): boolean { - const importArray = findNodes(storybookConfig, [ - ts.SyntaxKind.PropertyAssignment, - ]); - let builderIsWebpackNot5 = false; - importArray.forEach((parent) => { - const identifier = findNodes(parent, ts.SyntaxKind.Identifier); - const sbBuilder = findNodes(parent, ts.SyntaxKind.StringLiteral); - const builderText = sbBuilder?.[0]?.getText() ?? ''; - if ( - identifier?.[0]?.getText() === 'builder' && - builderText.includes('webpack') && - !builderText.includes('webpack5') - ) { - builderIsWebpackNot5 = true; - } - }); - - return builderIsWebpackNot5; -} diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index 85d81e3d79c1ef..9d926ea9318095 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -146,7 +146,7 @@ export async function configurationGenerator( addStaticTarget(tree, schema); } - // TODO(v18): remove Cypress + // TODO(katerina): Nx 18 -> remove Cypress if (schema.configureCypress) { const e2eProject = await getE2EProjectName(tree, schema.name); if (!e2eProject) { diff --git a/packages/storybook/src/generators/configuration/lib/util-functions.ts b/packages/storybook/src/generators/configuration/lib/util-functions.ts index f4f2654e09ae81..3b893cf8684453 100644 --- a/packages/storybook/src/generators/configuration/lib/util-functions.ts +++ b/packages/storybook/src/generators/configuration/lib/util-functions.ts @@ -36,7 +36,7 @@ const DEFAULT_PORT = 4400; export function addStorybookTask( tree: Tree, projectName: string, - uiFramework: string, + uiFramework: UiFramework7, interactionTests: boolean ) { if (uiFramework === '@storybook/react-native') { diff --git a/packages/storybook/src/generators/configuration/schema.d.ts b/packages/storybook/src/generators/configuration/schema.d.ts index 715148a762e190..b49eed3e7f07dd 100644 --- a/packages/storybook/src/generators/configuration/schema.d.ts +++ b/packages/storybook/src/generators/configuration/schema.d.ts @@ -1,5 +1,5 @@ import { Linter } from '@nx/linter'; -import { UiFramework7, UiFramework } from '../../utils/models'; +import { UiFramework7 } from '../../utils/models'; export interface StorybookConfigureSchema { name: string; diff --git a/packages/storybook/src/generators/migrate-7/schema.d.ts b/packages/storybook/src/generators/migrate-7/schema.d.ts index e4788e10bb7eab..b204e28928654e 100644 --- a/packages/storybook/src/generators/migrate-7/schema.d.ts +++ b/packages/storybook/src/generators/migrate-7/schema.d.ts @@ -1,5 +1,3 @@ -import { UiFramework, UiFramework7 } from '../../utils/models'; - export interface Schema { autoAcceptAllPrompts?: boolean; onlyShowListOfCommands?: boolean; diff --git a/packages/storybook/src/utils/models.ts b/packages/storybook/src/utils/models.ts index 9f3c0d6227d2ac..f2ac88856e0619 100644 --- a/packages/storybook/src/utils/models.ts +++ b/packages/storybook/src/utils/models.ts @@ -1,14 +1,3 @@ -export interface CommonNxStorybookConfig { - uiFramework?: - | '@storybook/angular' - | '@storybook/react' - | '@storybook/html' - | '@storybook/web-components' - | '@storybook/vue' - | '@storybook/vue3' - | '@storybook/svelte'; // TODO(katerina): Remove when Storybook 7 -} - export type UiFramework7 = | '@storybook/angular' | '@storybook/html-webpack5' diff --git a/packages/storybook/src/utils/test-configs/.storybook/main.js b/packages/storybook/src/utils/test-configs/.storybook/main.js index 067fe72c7ed4e3..0262923b81e8c0 100644 --- a/packages/storybook/src/utils/test-configs/.storybook/main.js +++ b/packages/storybook/src/utils/test-configs/.storybook/main.js @@ -1,8 +1,6 @@ module.exports = { - core: { builder: 'webpack5' }, - stories: [ - '../src/app/**/*.stories.mdx', - '../src/app/**/*.stories.@(js|jsx|ts|tsx)', - ], - addons: ['@storybook/addon-essentials', '@nx/react/plugins/storybook'], + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, }; diff --git a/packages/storybook/src/utils/utilities.ts b/packages/storybook/src/utils/utilities.ts index 7846997dcf090d..8280a1e37f00a1 100644 --- a/packages/storybook/src/utils/utilities.ts +++ b/packages/storybook/src/utils/utilities.ts @@ -262,9 +262,10 @@ export function getTsSourceFile(host: Tree, path: string): ts.SourceFile { export function pleaseUpgrade(): string { return ` - Storybook 6 is no longer maintained. Please upgrade to Storybook 7. + Storybook 6 is no longer maintained, and not supported in Nx. + Please upgrade to Storybook 7. Here is a guide on how to upgrade: - https://nx.dev/packages/storybook/generators/migrate-7 + https://nx.dev/nx-api/storybook/generators/migrate-7 `; }