Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react-native:storybook-configuration does not work with TS or React Native? #28802

Closed
1 of 4 tasks
fwielstra opened this issue Nov 5, 2024 · 3 comments · Fixed by #29210
Closed
1 of 4 tasks

react-native:storybook-configuration does not work with TS or React Native? #28802

fwielstra opened this issue Nov 5, 2024 · 3 comments · Fixed by #29210
Assignees
Labels
outdated scope: react-native Issues relating to React Native type: bug

Comments

@fwielstra
Copy link

fwielstra commented Nov 5, 2024

UPDATE

I've started a new project from scratch with a similar setup using the "React Native" recipe:

npx create-nx-workspace nx-react-native --preset=react-native --appName=storybookdemo
npx nx g @nx/react-native:lib libs/shared-ui-layout

Added a Button component, used it in the app, all good. Then adding Storybook using the @nx/storybook documentation:

nx add @nx/storybook
nx g @nx/react-native:storybook-configuration shared-ui-layout

Then starting Storybook using

nx run shared-ui-layout:storybook

This leads to the following compiler error:

✘ [ERROR] Unexpected "typeof"

    ../../node_modules/react-native/index.js:14:7:
      14 │ import typeof ActionSheetIOS from './Libraries/ActionSheetIOS/ActionSheetIOS';

According to this issue, this should be fixable by adding an alias in the resolve section of the Vite config, but this is all set up by the generators already. I'm stuck at this point now. I've uploaded the whole project here: https://github.com/fwielstra/nx-react-native-storybook-bug, if anyone can point out anything I've missed, that would be appreciated.


Current Behavior

I have an existing React Native monorepo with three apps and a dozen libraries. I've built my own 'component demo' app because when we started, Storybook didn't work well with React Native yet, however this should have improved by now.

I've followed the instructions on the @nx/storybook page:

nx add @nx/storybook
# generate Storybook configuration for `components` library
nx g @nx/react-native:storybook-configuration components

The wizard and output:

 NX  Generating @nx/react-native:storybook-configuration

✔ Do you want to set up Storybook interaction tests? (Y/n) · true
✔ Automatically generate *.stories.ts files for components declared in this project? (Y/n) · true
✔ Configure a static file server for the storybook instance? (Y/n) · true
Please run 'nx run @nx/react:storybook-configuration components' instead.
Fetching prettier...
UPDATE package.json
CREATE .prettierrc
CREATE .prettierignore
CREATE libs/components/.storybook/main.ts
CREATE libs/components/.storybook/preview.ts
CREATE libs/components/tsconfig.storybook.json
UPDATE libs/components/tsconfig.lib.json
UPDATE libs/components/tsconfig.json
UPDATE libs/components/.eslintrc.json
UPDATE nx.json
UPDATE libs/components/project.json

[omitted irrellevant npm output]

The first signal something is up is the line "Please run 'nx run @nx/react:storybook-configuration components' instead.". It doesn't generate configuration that works with React Native components, but does something with vite (which is otherwise unused in our application) and vanilla react instead, while I expect that it should generate a Metro configuration and the like.

Anyway, then I started Storybook using

nx run components:storybook

So far so good; it says No story files found for the specified pattern: libs/components/src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx)) but that's alright because I don't have a lib folder under src, it's all straight under src. I updated the config accordingly:

stories: ['../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],

and restarted Storybook:

nx run components:storybook

I had Storybook generate a story for one of my components:

import type { Meta, StoryObj } from '@storybook/react';

import { PrimaryButton } from './PrimaryButton';

const meta = {
  component: PrimaryButton,
} satisfies Meta<typeof PrimaryButton>;

export default meta;

type Story = StoryObj<typeof meta>;

// this has an error in my editor:
/*  Property 'args' is missing in type '{}' but required in type '{ args: { children: string; accessibilityHint?: string | undefined; onPress: (e: GestureResponderEvent) => void; loading?: boolean | undefined; disabled?: boolean | undefined; ... 6 more ...; disableSkeleton?: boolean | undefined; }; }'.ts(2322)
index.d.ts(458, 5): 'args' is declared here.
*/
export const Default: Story = {};

Then I restarted / reloaded Storybook, and this is where things fall apart:

In the Storybook web interface itself:

TypeError: importers[path] is not a function
    at un.importFn (http://localhost:4400/@id/__x00__/virtual:/@storybook/builder-vite/storybook-stories.js:6:31)
    at un.loadCSFFileByStoryId (http://localhost:4400/sb-preview/runtime.js:5577:89)
    at un.loadStory (http://localhost:4400/sb-preview/runtime.js:5607:24)
    at http://localhost:4400/sb-preview/runtime.js:5879:37
    at mn.runPhase (http://localhost:4400/sb-preview/runtime.js:5872:100)
    at mn.prepare (http://localhost:4400/sb-preview/runtime.js:5878:20)
    at Un.renderSelection (http://localhost:4400/sb-preview/runtime.js:6750:15)
    at async Un.onStoriesChanged (http://localhost:4400/sb-preview/runtime.js:6674:99)
    at async Un.onStoryIndexChanged (http://localhost:4400/sb-preview/runtime.js:6172:9)

In the terminal:

15:45:43 [vite] Internal server error: /Users/[my project folder]/libs/components/src/Buttons/PrimaryButton.stories.tsx: Unexpected token, expected "from" (1:12)

> 1 | import type { Meta, StoryObj } from '@storybook/react';
    |             ^
  2 |
  3 | import { PrimaryButton } from './PrimaryButton';

One possible solution suggested by the internet is to add "@babel/preset-typescript" to .babelrc (in our codebase we still use babel.config.json), but this does not seem to get picked up, nor does it appear anywhere else in our exclusively Typescript project. For completeness, this is the babel.config.json from the components library:

{
  "presets": [
    [
      "@nx/react/babel",
      {
        "runtime": "automatic",
        "useBuiltIns": "usage"
      }
    ]
  ],
  "plugins": [["react-native-reanimated/plugin"]],
  "env": {
    "test": {
      "plugins": [
        [
          "react-native-reanimated/plugin",
          {
            "relativeSourceLocation": true
          }
        ]
      ],
      "presets": ["module:@react-native/babel-preset"]
    }
  }
}

I hope that it's something simple or something I missed from the (incredibly straightforward) instructions. I've followed similar instructions from 3rd party websites as well.

One other thing I can try is to generate an Expo app using nx g @nx/expo:app apps/storybook, then try and apply the configuration changes from the example React Native / Storybook template, but I'd prefer if things like this work out of the box.

Expected Behavior

Adding @nx/storybook and running nx g @nx/react-native:storybook-configuration components should generate a working and running Storybook instance for an existing React Native component library.

GitHub Repo

No response

Steps to Reproduce

  1. Have an existing React Native based NX repo with apps and a components library, generated around two and a half years ago
  2. Add Storybook according to the instructions on https://nx.dev/nx-api/storybook, specifically the react native variant under the "Generating Storybook Configuration" heading
  3. Update libs/components/.storybook/main.ts, remove the /lib path part from the stories field
  4. Run Storybook using nx run components:storybook
  5. Use the '+'

Nx Report

NX Report complete - copy this into the issue template

Node : 18.20.4
OS : darwin-arm64
Native Target : aarch64-macos
npm : 10.7.0

nx (global) : 20.0.0
nx : 20.0.8
@nx/js : 20.0.8
@nx/jest : 20.0.8
@nx/eslint : 20.0.8
@nx/workspace : 20.0.8
@nx/cypress : 20.0.8
@nx/devkit : 20.0.8
@nx/eslint-plugin : 20.0.8
@nx/react : 20.0.8
@nx/react-native : 20.0.8
@nx/storybook : 20.0.8
@nx/vite : 20.0.8
@nx/web : 20.0.8
typescript : 5.5.2

Community plugins:
@diogovcs/stryker-mutator : 0.2.6

Failure Logs

15:45:43 [vite] Internal server error: /Users/[my project folder]/libs/components/src/Buttons/PrimaryButton.stories.tsx: Unexpected token, expected "from" (1:12)

1 | import type { Meta, StoryObj } from '@storybook/react';
| ^
2 |
3 | import { PrimaryButton } from './PrimaryButton';

Package Manager Version

No response

Operating System

  • macOS
  • Linux
  • Windows
  • Other (Please specify)

Additional Information

I know this is a lot of information to parse and yet probably not enough information, and I don't have a reproduction yet without starting a new project from scratch emulating the circumstances of the large project I'm working in right now. It's possible that if I start from scratch, everything will work out of the box, but I can't be sure. I just hope there's something obvious I missed.

@FrozenPandaz FrozenPandaz added the scope: react-native Issues relating to React Native label Nov 12, 2024
@jnbt
Copy link

jnbt commented Nov 19, 2024

I experience the same situation with nx and "@nx/storybook (20.1.2) on react-native. My current guess is that some "unification" of react / react-native and vite leads to an incorrect state of the project.

Especially on a fresh project, which is setup like @fwielstra described, the storyboard integration via nx doesn't work on React Native.

@xiongemi
Copy link
Collaborator

xiongemi commented Dec 5, 2024

i create a pr in your repo to fix the storybook. https://github.com/fwielstra/nx-react-native-storybook-bug/pull/1/files#

I submitted a pr to fix this, but here are the steps to manually fix it:

webpack

if you chose webpack as bundler for your react-native app, after you run the command nx g @nx/react-native:storybook-configuration you might need to change the .storybook/main.ts in your library to something like:

import type { StorybookConfig } from '@storybook/react-webpack5';

const config: StorybookConfig = {
  stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@nx/react/plugins/storybook',
  ],
  framework: {
    name: '@storybook/react-webpack5',
    options: {},
  },
  webpackFinal: async (config) => {
    if (config.resolve) {
      config.resolve.alias = {
        ...config.resolve.alias,
        'react-native$': 'react-native-web',
      };
      config.resolve.extensions = [
        '.web.tsx',
        '.web.ts',
        '.web.jsx',
        '.web.js',
        ...(config.resolve.extensions ?? []),
      ];
    }
    return config;
  },
};

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/recipes/storybook/custom-builder-configs

or you can use https://www.npmjs.com/package/@storybook/addon-react-native-web, after you install @storybook/addon-react-native-web, you can add this addon to .storybook/main.ts:

import type { StorybookConfig } from '@storybook/react-webpack5';

const config: StorybookConfig = {
  stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@nx/react/plugins/storybook',
  `@storybook/addon-react-native-web`
  ],
  framework: {
    name: '@storybook/react-webpack5',
    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/recipes/storybook/custom-builder-configs

vite

if you chose vite as your bundler, you can manually add vite.config.ts under your library, something like:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import * as esbuild from 'esbuild';
import { readFileSync } from 'fs';

const extensions = [
  '.mjs',
  '.web.tsx',
  '.tsx',
  '.web.ts',
  '.ts',
  '.web.jsx',
  '.jsx',
  '.web.js',
  '.js',
  '.css',
  '.json',
];

const rollupPlugin = (matchers: RegExp[]) => ({
  name: 'js-in-jsx',
  load(id: string) {
    if (matchers.some((matcher) => matcher.test(id)) && id.endsWith('.js')) {
      const file = readFileSync(id, { encoding: 'utf-8' });
      return esbuild.transformSync(file, { loader: 'jsx', jsx: 'automatic' });
    }
  },
});

export default defineConfig({
  root: __dirname,
  cacheDir: '../../node_modules/.vite/shared-ui-layout',
  define: {
    global: 'window',
  },
  resolve: {
    extensions,
    alias: {
      'react-native': 'react-native-web',
    },
  },
  build: {
    reportCompressedSize: true,
    commonjsOptions: { transformMixedEsModules: true },
    outDir: '../../dist/lib/shared-ui-layout/web',
    rollupOptions: {
      plugins: [rollupPlugin([/react-native-vector-icons/])],
    },
  },
  server: {
    port: 4200,
    host: 'localhost',
    fs: {
      // Allow serving files from one level up to the project root
      allow: ['..'],
    },
  },
  preview: {
    port: 4300,
    host: 'localhost',
  },
  optimizeDeps: {
    esbuildOptions: {
      resolveExtensions: extensions,
      jsx: 'automatic',
      loader: { '.js': 'jsx' },
    },
  },
  plugins: [react(), nxViteTsPaths()],
  // Uncomment this if you are using workers.
  // worker: {
  //  plugins: [ nxViteTsPaths() ],
  // },
});

then you can run the command nx g @nx/react-native:storybook-configuration.

if you already run the command, you might need to change .storybook/main.ts under your library something like:

import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/lib/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
  addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
  framework: {
    name: '@storybook/react-vite',
    options: {
      builder: {
        viteConfigPath: 'vite.config.ts',
      },
    },
  },
};

export default config;

// To customize your Vite configuration you can use the viteFinal field.
// Check https://storybook.js.org/docs/react/builders/vite#configuration
// and https://nx.dev/recipes/storybook/custom-builder-configs

Copy link

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 11, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated scope: react-native Issues relating to React Native type: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants