From 737c5446b34802a8b5aa5967eb45fa3a9bb4f806 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Fri, 21 Apr 2023 17:42:48 +0300 Subject: [PATCH] feat(repo): update storybook to v7 (#16174) --- .eslintrc.json | 2 +- .../src/storybook-angular.test.ts | 13 ++- e2e/storybook/src/storybook-nested.test.ts | 101 ++++++++++-------- e2e/storybook/src/storybook.test.ts | 39 ++----- graph/client/.storybook/main.js | 14 ++- graph/client/project.json | 2 - graph/ui-components/.storybook/main.js | 16 +-- graph/ui-components/project.json | 2 - graph/ui-graph/.storybook/main.js | 16 +-- graph/ui-graph/project.json | 2 - graph/ui-tooltips/.storybook/main.js | 16 +-- graph/ui-tooltips/project.json | 2 - package.json | 16 +-- .../storybook-configuration.spec.ts.snap | 11 +- .../build-storybook.impl.spec.ts | 24 +++-- .../build-storybook/build-storybook.impl.ts | 13 ++- .../storybook/storybook.impl.spec.ts | 61 +++-------- .../src/executors/storybook/storybook.impl.ts | 25 +++-- .../storybook/src/generators/init/init.ts | 12 +++ 19 files changed, 199 insertions(+), 188 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index b6d0b279831c4e..b77527aa929a5d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,7 +6,7 @@ }, "ignorePatterns": ["**/*.ts"], "plugins": ["@typescript-eslint", "@nrwl/nx"], - "extends": [], + "extends": ["plugin:storybook/recommended"], "rules": { "@typescript-eslint/explicit-module-boundary-types": "off", "no-restricted-imports": ["error", "create-nx-workspace"], diff --git a/e2e/storybook-angular/src/storybook-angular.test.ts b/e2e/storybook-angular/src/storybook-angular.test.ts index 15a58fcac27c64..8ccd123ab4c34e 100644 --- a/e2e/storybook-angular/src/storybook-angular.test.ts +++ b/e2e/storybook-angular/src/storybook-angular.test.ts @@ -15,9 +15,9 @@ describe('Storybook executors for Angular', () => { const angularStorybookLib = uniq('test-ui-ng-lib'); beforeAll(() => { newProject(); - createTestUILib(angularStorybookLib); + runCLI(`g @nx/angular:library ${angularStorybookLib} --no-interactive`); runCLI( - `generate @nrwl/angular:storybook-configuration ${angularStorybookLib} --configureCypress --generateStories --generateCypressSpecs --no-interactive` + `generate @nx/angular:storybook-configuration ${angularStorybookLib} --configureCypress --generateStories --generateCypressSpecs --no-interactive` ); }); @@ -25,8 +25,7 @@ describe('Storybook executors for Angular', () => { cleanupProject(); }); - // TODO: Enable on SB7 - xdescribe('serve and build storybook', () => { + describe('serve and build storybook', () => { afterAll(() => killPorts()); it('should serve an Angular based Storybook setup', async () => { @@ -49,6 +48,7 @@ describe('Storybook executors for Angular', () => { xdescribe('run cypress tests using storybook', () => { it('should execute e2e tests using Cypress running against Storybook', async () => { if (runCypressTests()) { + addTestButtonToUILib(angularStorybookLib); writeFileSync( tmpProjPath( `apps/${angularStorybookLib}-e2e/src/e2e/test-button/test-button.component.cy.ts` @@ -82,10 +82,9 @@ describe('Storybook executors for Angular', () => { }); }); -export function createTestUILib(libName: string): void { - runCLI(`g @nrwl/angular:library ${libName} --no-interactive`); +function addTestButtonToUILib(libName: string): void { runCLI( - `g @nrwl/angular:component test-button --project=${libName} --no-interactive` + `g @nx/angular:component test-button --project=${libName} --no-interactive` ); writeFileSync( diff --git a/e2e/storybook/src/storybook-nested.test.ts b/e2e/storybook/src/storybook-nested.test.ts index 51e93312212fdb..389a166eb93021 100644 --- a/e2e/storybook/src/storybook-nested.test.ts +++ b/e2e/storybook/src/storybook-nested.test.ts @@ -1,6 +1,7 @@ import { checkFilesExist, cleanupProject, + getSelectedPackageManager, killPorts, readJson, runCLI, @@ -10,6 +11,7 @@ import { uniq, } from '@nrwl/e2e/utils'; import { writeFileSync } from 'fs'; +import { createFileSync } from 'fs-extra'; describe('Storybook generators and executors for standalone workspaces - using React + Vite', () => { const wsName = uniq('react'); @@ -22,10 +24,11 @@ describe('Storybook generators and executors for standalone workspaces - using R appName, style: 'css', bundler: 'vite', + packageManager: getSelectedPackageManager(), }); runCLI( - `generate @nrwl/react:storybook-configuration ${appName} --generateStories --no-interactive` + `generate @nx/react:storybook-configuration ${appName} --generateStories --no-interactive` ); runCLI(`report`); @@ -50,8 +53,7 @@ describe('Storybook generators and executors for standalone workspaces - using R }); }); - // TODO: Use --storybook7Configuration and re-enable this test - or else it NEEDS NODE 16 - xdescribe('serve storybook', () => { + describe('serve storybook', () => { afterEach(() => killPorts()); it('should serve a React based Storybook setup that uses Vite', async () => { @@ -59,73 +61,86 @@ describe('Storybook generators and executors for standalone workspaces - using R return /Storybook.*started/gi.test(output); }); p.kill(); - }, 40000); + }, 60000); }); - // TODO: Use --storybook7Configuration and re-enable this test - or else it NEEDS NODE 16 - xdescribe('build storybook', () => { + describe('build storybook', () => { it('should build a React based storybook that uses Vite', () => { runCLI(`run ${appName}:build-storybook --verbose`); checkFilesExist(`dist/storybook/${appName}/index.html`); - }, 40000); + }, 60000); - // This test makes sure path resolution works + // This needs fixing on the Storybook side + // vite paths resolution is not working on standalone xit('should build a React based storybook that references another lib and uses Vite', () => { - const reactLib = uniq('test-lib-react'); - runCLI(`generate @nrwl/react:lib ${reactLib} --no-interactive`); + const anotherReactLib = uniq('test-another-lib-react'); + runCLI(`generate @nx/react:lib ${anotherReactLib} --no-interactive`); // create a React component we can reference + createFileSync(tmpProjPath(`${anotherReactLib}/src/lib/mytestcmp.tsx`)); writeFileSync( - tmpProjPath(`${reactLib}/src/lib/mytestcmp.tsx`), + tmpProjPath(`${anotherReactLib}/src/lib/mytestcmp.tsx`), ` - import React from 'react'; - - /* eslint-disable-next-line */ - export interface MyTestCmpProps {} - - export const MyTestCmp = (props: MyTestCmpProps) => { - return ( -
-

Welcome to test cmp!

-
- ); - }; - - export default MyTestCmp; + export function MyTestCmp() { + return ( +
+

Welcome to OtherLib!

+
+ ); + } + + export default MyTestCmp; ` ); // update index.ts and export it writeFileSync( - tmpProjPath(`${reactLib}/src/index.ts`), + tmpProjPath(`${anotherReactLib}/src/index.ts`), ` export * from './lib/mytestcmp'; ` ); - // create a story in the first lib to reference the cmp from the 2nd lib + // create a component and a story in the first lib to reference the cmp from the 2nd lib + createFileSync(tmpProjPath(`src/app/test-button.tsx`)); writeFileSync( - tmpProjPath(`${reactLib}/src/lib/myteststory.stories.tsx`), + tmpProjPath(`src/app/test-button.tsx`), ` - import React from 'react'; - - import { MyTestCmp, MyTestCmpProps } from '@${wsName}/${reactLib}'; + import { MyTestCmp } from '@${wsName}/${anotherReactLib}'; - export default { - component: MyTestCmp, - title: 'MyTestCmp', - }; + export function TestButton() { + return ( +
+ +
+ ); + } - export const primary = () => { - /* eslint-disable-next-line */ - const props: MyTestCmpProps = {}; + export default TestButton; + ` + ); - return ; - }; + // create a story in the first lib to reference the cmp from the 2nd lib + createFileSync(tmpProjPath(`src/app/test-button.stories.tsx`)); + writeFileSync( + tmpProjPath(`src/app/test-button.stories.tsx`), ` + import type { Meta } from '@storybook/react'; + import { TestButton } from './test-button'; + + const Story: Meta = { + component: TestButton, + title: 'TestButton', + }; + export default Story; + + export const Primary = { + args: {}, + }; + ` ); // build React lib - runCLI(`run ${reactLib}:build-storybook --verbose`); - checkFilesExist(`dist/storybook/${reactLib}/index.html`); - }, 40000); + runCLI(`run ${appName}:build-storybook --verbose`); + checkFilesExist(`dist/storybook/${appName}/index.html`); + }, 60000); }); }); diff --git a/e2e/storybook/src/storybook.test.ts b/e2e/storybook/src/storybook.test.ts index 6f00cdcfddcdcb..a02f823426585a 100644 --- a/e2e/storybook/src/storybook.test.ts +++ b/e2e/storybook/src/storybook.test.ts @@ -2,47 +2,28 @@ import { checkFilesExist, cleanupProject, killPorts, + newProject, runCLI, runCommandUntil, tmpProjPath, uniq, - getPackageManagerCommand, - runCommand, - newProject, - updateJson, } from '@nrwl/e2e/utils'; import { writeFileSync } from 'fs'; -describe('Storybook generators and executors for monorepos', () => { - const previousPM = process.env.SELECTED_PM; +// TODO: re-enable once the issue is fixed with long build times +describe.skip('Storybook generators and executors for monorepos', () => { const reactStorybookLib = uniq('test-ui-lib-react'); let proj; beforeAll(() => { - process.env.SELECTED_PM = 'yarn'; - proj = newProject({ - packageManager: 'yarn', - }); - runCLI(`generate @nrwl/react:lib ${reactStorybookLib} --no-interactive`); + proj = newProject(); + runCLI(`generate @nx/react:lib ${reactStorybookLib} --no-interactive`); runCLI( - `generate @nrwl/react:storybook-configuration ${reactStorybookLib} --generateStories --no-interactive --bundler=webpack` + `generate @nx/react:storybook-configuration ${reactStorybookLib} --generateStories --no-interactive --bundler=webpack` ); - - // TODO(jack): Overriding enhanced-resolve to 5.10.0 now until the package is fixed. - // TODO: Use --storybook7Configuration and remove this - // See: https://github.com/webpack/enhanced-resolve/issues/362 - updateJson('package.json', (json) => { - json['overrides'] = { - 'enhanced-resolve': '5.10.0', - }; - - return json; - }); - runCommand(getPackageManagerCommand().install); }); afterAll(() => { cleanupProject(); - process.env.SELECTED_PM = previousPM; }); describe('serve and build storybook', () => { @@ -57,18 +38,18 @@ describe('Storybook generators and executors for monorepos', () => { } ); p.kill(); - }, 50000); + }, 60000); it('should build a React based storybook setup that uses webpack', () => { // build runCLI(`run ${reactStorybookLib}:build-storybook --verbose`); checkFilesExist(`dist/storybook/${reactStorybookLib}/index.html`); - }, 50000); + }, 60000); // This test makes sure path resolution works it('should build a React based storybook that references another lib and uses webpack', () => { const anotherReactLib = uniq('test-another-lib-react'); - runCLI(`generate @nrwl/react:lib ${anotherReactLib} --no-interactive`); + runCLI(`generate @nx/react:lib ${anotherReactLib} --no-interactive`); // create a React component we can reference writeFileSync( tmpProjPath(`libs/${anotherReactLib}/src/lib/mytestcmp.tsx`), @@ -117,6 +98,6 @@ describe('Storybook generators and executors for monorepos', () => { // build React lib runCLI(`run ${reactStorybookLib}:build-storybook --verbose`); checkFilesExist(`dist/storybook/${reactStorybookLib}/index.html`); - }, 50000); + }, 60000); }); }); diff --git a/graph/client/.storybook/main.js b/graph/client/.storybook/main.js index 782a151c0cea83..44a7e8d67e79d7 100644 --- a/graph/client/.storybook/main.js +++ b/graph/client/.storybook/main.js @@ -1,12 +1,16 @@ +/* eslint-disable storybook/no-uninstalled-addons */ module.exports = { - core: { builder: 'webpack5' }, - stories: [ - '../src/app/**/*.stories.mdx', - '../src/app/**/*.stories.@(js|jsx|ts|tsx)', - ], + stories: ['../src/app/**/*.stories.@(mdx|js|jsx|ts|tsx)'], addons: [ '@storybook/addon-essentials', '@nx/react/plugins/storybook', 'storybook-dark-mode', ], + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + docs: { + autodocs: true, + }, }; diff --git a/graph/client/project.json b/graph/client/project.json index de53ab4fa60f68..49d35594e5fcf2 100644 --- a/graph/client/project.json +++ b/graph/client/project.json @@ -245,7 +245,6 @@ "storybook": { "executor": "@nx/storybook:storybook", "options": { - "uiFramework": "@storybook/react", "port": 4400, "configDir": "graph/client/.storybook" }, @@ -259,7 +258,6 @@ "executor": "@nx/storybook:build", "outputs": ["{options.outputDir}"], "options": { - "uiFramework": "@storybook/react", "configDir": "graph/client/.storybook", "outputDir": "dist/storybook/graph-client" }, diff --git a/graph/ui-components/.storybook/main.js b/graph/ui-components/.storybook/main.js index 9868b92f87106e..327f35de781c2b 100644 --- a/graph/ui-components/.storybook/main.js +++ b/graph/ui-components/.storybook/main.js @@ -1,8 +1,12 @@ +/* eslint-disable storybook/no-uninstalled-addons */ module.exports = { - core: { builder: 'webpack5' }, - stories: [ - '../src/lib/**/*.stories.mdx', - '../src/lib/**/*.stories.@(js|jsx|ts|tsx)', - ], - addons: ['@storybook/addon-essentials', '@nx/react/plugins/storybook'], + stories: ['../src/lib/**/*.stories.@(mdx|js|jsx|ts|tsx)'], + addons: ['@storybook/addon-essentials', '@nrwl/react/plugins/storybook'], + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + docs: { + autodocs: true, + }, }; diff --git a/graph/ui-components/project.json b/graph/ui-components/project.json index ac5530b057e97b..7fdfc258d0c7e3 100644 --- a/graph/ui-components/project.json +++ b/graph/ui-components/project.json @@ -10,7 +10,6 @@ "storybook": { "executor": "@nx/storybook:storybook", "options": { - "uiFramework": "@storybook/react", "port": 4400, "configDir": "graph/ui-components/.storybook" }, @@ -24,7 +23,6 @@ "executor": "@nx/storybook:build", "outputs": ["{options.outputDir}"], "options": { - "uiFramework": "@storybook/react", "configDir": "graph/ui-components/.storybook", "outputDir": "dist/storybook/graph-ui-components" }, diff --git a/graph/ui-graph/.storybook/main.js b/graph/ui-graph/.storybook/main.js index 9868b92f87106e..327f35de781c2b 100644 --- a/graph/ui-graph/.storybook/main.js +++ b/graph/ui-graph/.storybook/main.js @@ -1,8 +1,12 @@ +/* eslint-disable storybook/no-uninstalled-addons */ module.exports = { - core: { builder: 'webpack5' }, - stories: [ - '../src/lib/**/*.stories.mdx', - '../src/lib/**/*.stories.@(js|jsx|ts|tsx)', - ], - addons: ['@storybook/addon-essentials', '@nx/react/plugins/storybook'], + stories: ['../src/lib/**/*.stories.@(mdx|js|jsx|ts|tsx)'], + addons: ['@storybook/addon-essentials', '@nrwl/react/plugins/storybook'], + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + docs: { + autodocs: true, + }, }; diff --git a/graph/ui-graph/project.json b/graph/ui-graph/project.json index 872ca957ddfd3e..1de3612af84c6a 100644 --- a/graph/ui-graph/project.json +++ b/graph/ui-graph/project.json @@ -10,7 +10,6 @@ "storybook": { "executor": "@nx/storybook:storybook", "options": { - "uiFramework": "@storybook/react", "port": 4400, "configDir": "graph/ui-graph/.storybook" }, @@ -24,7 +23,6 @@ "executor": "@nx/storybook:build", "outputs": ["{options.outputDir}"], "options": { - "uiFramework": "@storybook/react", "configDir": "graph/ui-graph/.storybook", "outputDir": "dist/storybook/graph-ui-graph" }, diff --git a/graph/ui-tooltips/.storybook/main.js b/graph/ui-tooltips/.storybook/main.js index 9868b92f87106e..327f35de781c2b 100644 --- a/graph/ui-tooltips/.storybook/main.js +++ b/graph/ui-tooltips/.storybook/main.js @@ -1,8 +1,12 @@ +/* eslint-disable storybook/no-uninstalled-addons */ module.exports = { - core: { builder: 'webpack5' }, - stories: [ - '../src/lib/**/*.stories.mdx', - '../src/lib/**/*.stories.@(js|jsx|ts|tsx)', - ], - addons: ['@storybook/addon-essentials', '@nx/react/plugins/storybook'], + stories: ['../src/lib/**/*.stories.@(mdx|js|jsx|ts|tsx)'], + addons: ['@storybook/addon-essentials', '@nrwl/react/plugins/storybook'], + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + docs: { + autodocs: true, + }, }; diff --git a/graph/ui-tooltips/project.json b/graph/ui-tooltips/project.json index 81c337be0eadc5..3645bbbc5ccf4d 100644 --- a/graph/ui-tooltips/project.json +++ b/graph/ui-tooltips/project.json @@ -10,7 +10,6 @@ "storybook": { "executor": "@nx/storybook:storybook", "options": { - "uiFramework": "@storybook/react", "port": 4400, "configDir": "graph/ui-tooltips/.storybook" }, @@ -24,7 +23,6 @@ "executor": "@nx/storybook:build", "outputs": ["{options.outputDir}"], "options": { - "uiFramework": "@storybook/react", "configDir": "graph/ui-tooltips/.storybook", "outputDir": "dist/storybook/graph-ui-tooltips" }, diff --git a/package.json b/package.json index 90fc21322b4ce6..e74eebbe3b0203 100644 --- a/package.json +++ b/package.json @@ -73,13 +73,12 @@ "@rollup/plugin-node-resolve": "^13.0.4", "@rollup/plugin-url": "^7.0.0", "@schematics/angular": "~15.2.0", - "@storybook/addon-essentials": "^6.5.15", - "@storybook/angular": "^6.5.15", - "@storybook/builder-webpack5": "^6.5.15", - "@storybook/core-server": "^6.5.15", - "@storybook/manager-webpack5": "^6.5.15", - "@storybook/react": "^6.5.15", - "@storybook/types": "^7.0.0-alpha.44", + "@storybook/addon-essentials": "^7.0.2", + "@storybook/angular": "^7.0.2", + "@storybook/core-server": "^7.0.2", + "@storybook/react": "^7.0.2", + "@storybook/react-webpack5": "^7.0.2", + "@storybook/types": "^7.0.2", "@svgr/rollup": "^6.1.2", "@svgr/webpack": "^6.1.2", "@swc-node/register": "^1.4.2", @@ -150,6 +149,7 @@ "eslint-plugin-jsx-a11y": "6.6.1", "eslint-plugin-react": "7.31.11", "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-storybook": "^0.6.11", "express": "^4.18.1", "fast-xml-parser": "^4.0.9", "file-loader": "^6.2.0", @@ -223,7 +223,7 @@ "source-map": "0.7.3", "source-map-loader": "^3.0.0", "source-map-support": "0.5.19", - "storybook-dark-mode": "^1.1.2", + "storybook-dark-mode": "^3.0.0", "style-loader": "^3.3.0", "styled-components": "5.3.6", "stylus": "^0.55.0", diff --git a/packages/angular/src/generators/storybook-configuration/__snapshots__/storybook-configuration.spec.ts.snap b/packages/angular/src/generators/storybook-configuration/__snapshots__/storybook-configuration.spec.ts.snap index 812757792ff549..88398ee8f08a46 100644 --- a/packages/angular/src/generators/storybook-configuration/__snapshots__/storybook-configuration.spec.ts.snap +++ b/packages/angular/src/generators/storybook-configuration/__snapshots__/storybook-configuration.spec.ts.snap @@ -1,12 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`StorybookConfiguration generator should configure storybook to use webpack 5 1`] = ` -"module.exports = { - core: { builder: 'webpack5' }, - stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|ts|tsx)'], +"const config = { + stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'], addons: ['@storybook/addon-essentials'], + framework: { + name: '@storybook/angular', + options: {}, + }, }; +export default config; + // To customize your webpack configuration you can use the webpackFinal field. // Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config // and https://nx.dev/packages/storybook/documents/custom-builder-configs 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 index 4ddebbe3aabb3c..e8323e41ad36d8 100644 --- a/packages/storybook/src/executors/build-storybook/build-storybook.impl.spec.ts +++ b/packages/storybook/src/executors/build-storybook/build-storybook.impl.spec.ts @@ -24,27 +24,35 @@ describe('Build storybook', () => { beforeEach(async () => { options = { configDir: join(__dirname, `/../../utils/test-configs/.storybook`), - uiFramework: '@storybook/react', outputDir: `/root/dist/storybook`, }; context = executorContext as ExecutorContext; }); - it('should call the storybook buildStaticStandalone', async () => { + it('should call the storybook build', async () => { const loggerSpy = jest.spyOn(logger, 'info'); - const standaloneSpy = jest - .spyOn(build, 'buildStaticStandalone') + const buildSpy = jest + .spyOn(build, 'build') .mockImplementation(() => Promise.resolve()); const result = await storybookBuilder(options, context); - expect(standaloneSpy).toHaveBeenCalled(); - expect(loggerSpy).toHaveBeenCalledWith(`NX ui framework: @storybook/react`); - expect(loggerSpy).toHaveBeenCalledWith( - `NX Storybook files available in /root/dist/storybook` + 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 0b4ef483c90374..e8dd99368d36b5 100644 --- a/packages/storybook/src/executors/build-storybook/build-storybook.impl.ts +++ b/packages/storybook/src/executors/build-storybook/build-storybook.impl.ts @@ -51,15 +51,22 @@ export default async function buildStorybookExecutor( } } -function runInstance(options: CLIOptions, storybook7: boolean): Promise { +function runInstance( + options: CLIOptions, + storybook7: boolean +): Promise { const env = process.env.NODE_ENV ?? 'production'; process.env.NODE_ENV = env; if (storybook7) { - return build['build']({ + return build.build({ ...options, mode: 'static', - } as any); // TODO(katerina): Change to actual types when Storybook 7 + }); } else { const nodeVersion = process.version.slice(1).split('.'); if (+nodeVersion[0] === 18) { diff --git a/packages/storybook/src/executors/storybook/storybook.impl.spec.ts b/packages/storybook/src/executors/storybook/storybook.impl.spec.ts index 147d8ad6f8c50c..72045220d4a9e2 100644 --- a/packages/storybook/src/executors/storybook/storybook.impl.spec.ts +++ b/packages/storybook/src/executors/storybook/storybook.impl.spec.ts @@ -1,45 +1,31 @@ -import { fs as fsMock, vol } from 'memfs'; -import * as fs from 'fs'; - import { ExecutorContext } from '@nx/devkit'; jest.mock('@storybook/core-server', () => ({ buildDev: jest.fn().mockImplementation(() => Promise.resolve()), - build: jest.fn().mockImplementation(() => Promise.resolve()), + build: jest.fn().mockImplementation(() => + Promise.resolve({ + port: 4400, + }) + ), })); -import { buildDev } from '@storybook/core-server'; +import { build } from '@storybook/core-server'; import storybookExecutor from './storybook.impl'; import { join } from 'path'; -import { readFileSync } from 'fs-extra'; import { CLIOptions } from '@storybook/types'; import { CommonNxStorybookConfig } from '../../utils/models'; -// TODO(katerina): Update when Storybook 7 - describe('@nx/storybook:storybook', () => { let context: ExecutorContext; let options: CLIOptions & CommonNxStorybookConfig; beforeEach(() => { - // preserve original package.json file to memory const rootPath = join(__dirname, `../../../../../`); - const packageJsonPath = join( - rootPath, - `node_modules/@storybook/react/package.json` - ); - const storybookPath = join(rootPath, '.storybook'); options = { - uiFramework: '@storybook/react', port: 4400, - configDir: storybookPath, + configDir: join(__dirname, `/../../utils/test-configs/.storybook`), }; - vol.fromJSON({ - [packageJsonPath]: readFileSync(packageJsonPath).toString(), - }); - vol.mkdirSync(storybookPath, { - recursive: true, - }); + context = { root: rootPath, cwd: rootPath, @@ -54,22 +40,7 @@ describe('@nx/storybook:storybook', () => { targets: { build: { executor: '@nx/web:webpack', - options: { - compiler: 'babel', - outputPath: 'dist/apps/webre', - index: 'apps/webre/src/index.html', - baseHref: '/', - main: 'apps/webre/src/main.tsx', - polyfills: 'apps/webre/src/polyfills.ts', - tsConfig: 'apps/webre/tsconfig.app.json', - assets: [ - 'apps/webre/src/favicon.ico', - 'apps/webre/src/assets', - ], - styles: ['apps/webre/src/styles.css'], - scripts: [], - webpackConfig: '@nx/react/plugins/webpack', - }, + options: {}, }, storybook: { executor: '@nx/storybook:storybook', @@ -82,16 +53,18 @@ describe('@nx/storybook:storybook', () => { nxJsonConfiguration: {}, isVerbose: false, }; - jest.mock('fs', () => fsMock); - jest.spyOn(fs, 'statSync').mockReturnValue({ - isDirectory: () => true, - } as fs.Stats); }); it('should provide options to storybook', async () => { const iterator = storybookExecutor(options, context); const { value } = await iterator.next(); - expect(value).toEqual({ success: true }); - expect(buildDev).toHaveBeenCalled(); + 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 52e93aa1c02caf..8a85878fcec328 100644 --- a/packages/storybook/src/executors/storybook/storybook.impl.ts +++ b/packages/storybook/src/executors/storybook/storybook.impl.ts @@ -24,17 +24,14 @@ export default async function* storybookExecutor( storybookConfigExistsCheck(options.configDir, context.projectName); if (storybook7) { const buildOptions: CLIOptions = options; - const result: { port: number } = await runInstance( - buildOptions, - storybook7 - ); + const result = await runInstance(buildOptions, storybook7); yield { success: true, info: { - port: result?.port, + port: result?.['port'], baseUrl: `${options.https ? 'https' : 'http'}://${ options.host ?? 'localhost' - }:${result?.port}`, + }:${result?.['port']}`, }, }; await new Promise<{ success: boolean }>(() => {}); @@ -58,18 +55,24 @@ export default async function* storybookExecutor( } } -function runInstance(options: CLIOptions, storybook7: 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']({ + return build.build({ ...options, mode: 'dev', - } as any); // TODO(katerina): Change to actual types when Storybook 7 + }); } else { // TODO(katerina): Remove when Storybook 7 - return build.buildDev({ + return build['buildDev']({ ...options, configType: env.toUpperCase(), mode: 'dev', diff --git a/packages/storybook/src/generators/init/init.ts b/packages/storybook/src/generators/init/init.ts index 6444369f434b09..1c6119c1dff850 100644 --- a/packages/storybook/src/generators/init/init.ts +++ b/packages/storybook/src/generators/init/init.ts @@ -1,6 +1,7 @@ import { addDependenciesToPackageJson, convertNxGenerator, + detectPackageManager, GeneratorCallback, readJson, readNxJson, @@ -76,6 +77,17 @@ function checkDependenciesInstalled(host: Tree, schema: Schema) { devDependencies['@storybook/react-native'] = storybookReactNativeVersion; } else { devDependencies[schema.uiFramework] = storybook7VersionToInstall; + const isPnpm = detectPackageManager(host.root) === 'pnpm'; + if (isPnpm) { + // If it's pnpm, it needs the framework without the builder + // as a dependency too (eg. @storybook/react) + const matchResult = schema.uiFramework?.match(/^@storybook\/(\w+)/); + const uiFrameworkWithoutBuilder = matchResult ? matchResult[0] : null; + if (uiFrameworkWithoutBuilder) { + devDependencies[uiFrameworkWithoutBuilder] = + storybook7VersionToInstall; + } + } } devDependencies['@storybook/core-server'] = storybook7VersionToInstall; devDependencies['@storybook/addon-essentials'] = storybook7VersionToInstall;