storybookjs / addon-react-native-web

Build react-native-web projects in Storybook for React
MIT License
77 stars 21 forks source link

Is it compatible with Vite ? #94

Closed juliencurro-aptar closed 20 hours ago

juliencurro-aptar commented 5 days ago

Not a bug, but a question :

I tried to run this addon without webpack package and got failures, given the addon is almost a single "webpack.ts" file, I assume that webpack is required. But then can I have Vite for storybook itself and webpack for this addon ?

dannyhw commented 5 days ago

You can use vite for react native web but this addon currently is webpack specific

I have a vite example though

https://github.com/dannyhw/rn-web-vite-storybook-example/tree/main

The vite config is the important part

juliencurro-aptar commented 3 days ago

Thanks to your example (and other github repos), I am able to run storybook, but I have a lot of remaining issues.

Major one is tailwind styles not being applied, would you have a repo mixing vite and tailwind ?

And the other one "String contains an invalid character", for a component that uses an Svg icon.

Here is my config :

// vite.config.ts 

import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import tsconfigPaths from "vite-tsconfig-paths";
import tailwindcss from "tailwindcss";
import svgr from 'vite-plugin-svgr';
import path from 'path';

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), tsconfigPaths(), svgr()],
  server: {
    open: true,
    port: 3000,
  },
  css: {
    postcss: {
      plugins: [tailwindcss()],
    },
  },
  optimizeDeps: {
    esbuildOptions: {
      resolveExtensions: extensions,
    },
  },
  resolve: {
    extensions: extensions,
    alias: {
      'react-native': 'react-native-web',
      'tailwindConfig': path.resolve(__dirname, './tailwind.config.ts')
    }
  },
})

// tsconfig.json

{
    "extends": "@react-native/typescript-config/tsconfig.json",
    "include": [
        "./src/**/*",
        "./globals.d.ts",
        "./tailwind.config.ts"
    ],
    "exclude": [
        "node_modules"
    ],
    "compilerOptions": {
        "allowJs": true,
        "importHelpers": true,
        "forceConsistentCasingInFileNames": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "moduleResolution": "Bundler",
        "noUncheckedIndexedAccess": true,
        "noEmit": true,
        "resolveJsonModule": true,
        "skipLibCheck": true,
        "jsx": "react-native",
        "target": "es6",
        "module": "ESNext",
        "baseUrl": "./src/",
        "paths": {
            "@src/*": ["./*"],
            "@assets/*": ["./../assets/*"],
            "@component/*": ["./src/components/*"],
            "tailwindConfig": ["./../tailwind.config.js"]
        },
        "strict": true,
        "noImplicitAny": true,
        "pretty": true,
        "removeComments": true,
        "strictFunctionTypes": true,
        "traceResolution": true
    }
}

// tailwind.config.ts

export default {
  content: ["./index.{js,jsx,ts,tsx}", "./src/**/*.{js,jsx,ts,tsx}", "./.storybook/preview.js"],
  theme: {
// ...
   }
  plugins: [],
}

// globals.css

@tailwind base;
@tailwind components;
@tailwind utilities;

// .storybook/main.js

import { join, dirname } from "path";

/**
 * This function is used to resolve the absolute path of a package.
 * It is needed in projects that use Yarn PnP or are set up within a monorepo.
 */
function getAbsolutePath(value) {
  return dirname(require.resolve(join(value, "package.json")));
}

/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {

  stories: [
    "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
  ],
  addons: [
    getAbsolutePath("@storybook/addon-onboarding"),
    getAbsolutePath("@storybook/addon-links"),
    getAbsolutePath("@storybook/addon-essentials"),
    getAbsolutePath("@chromatic-com/storybook"),
    getAbsolutePath("@storybook/addon-interactions"),
    { name: getAbsolutePath("@storybook/addon-react-native-web"), 
      options: {
        modulesToTranspile: [
//          'react-native-reanimated',
          'nativewind',
          'react-native-css-interop',
        ],
        babelPresets: ['nativewind/babel'],
        babelPresetReactOptions: { jsxImportSource: 'nativewind' },
        babelPlugins: [
//          'react-native-reanimated/plugin',
           [
            '@babel/plugin-transform-react-jsx',
            {
              runtime: 'automatic',
              importSource: 'nativewind',
            },
          ],
         ],
      },
    },
  ],
  framework: {
    name: getAbsolutePath("@storybook/react-vite"),
    options: {},
  },
  core: {
    disableTelemetry: true,
    builder: '@storybook/builder-vite',
  },
};
export default config;

// preview.js

/** @type { import('@storybook/react').Preview } */
import "../globals.css";

const preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
};

export default preview;

// package.json

{
  "name": "BreatheSmart",
  "packageManager": "yarn@4.2.2",
  "type": "module",
  "devDependencies": {
    "@babel/preset-react": "^7.24.7",
    "@chromatic-com/storybook": "^1.9.0",
    "@react-native/babel-preset": "^0.75.3",
    "@storybook/addon-essentials": "^8.3.3",
    "@storybook/addon-interactions": "^8.3.3",
    "@storybook/addon-links": "^8.3.3",
    "@storybook/addon-onboarding": "^8.3.3",
    "@storybook/addon-react-native-web": "^0.0.25",
    "@storybook/blocks": "^8.3.3",
    "@storybook/builder-vite": "^8.3.3",
    "@storybook/react": "^8.3.3",
    "@storybook/react-vite": "^8.3.3",
    "@storybook/test": "^8.3.3",
    "@types/react-dom": "^18.3.0",
    "@types/tailwindcss": "^3.1.0",
    "@vitejs/plugin-react": "^4.3.1",
    "babel-plugin-react-native-web": "^0.19.12",
    "prettier": "^3.3.3",
    "prop-types": "^15.8.1",
    "react-dom": "^18.3.1",
    "react-native-web": "^0.19.12",
    "storybook": "^8.3.3",
    "tailwindcss": "3.3.2",
    "typescript": "^5.6.2",
    "vite-plugin-svgr": "^4.2.0"
  },
  "scripts": {
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "peerDependencies": {
    "nativewind": "^2.0.11"
  },
  "dependencies": {
    "nativewind": "^2.0.11",
    "react": "^18.3.1",
    "react-native-gesture-handler": "^2.19.0",
    "vite": "^5.4.7",
    "webpack": "^5.94.0"
  }
}

If you have few minutes to look at what could be wrong in my configuration, that would be really helpful, thanks :)

juliencurro-aptar commented 2 days ago

I solved the 2nd issue by changing the svgr() call inside the vite.config.ts with that :

plugins: [react(), tsconfigPaths(), svgr({svgrOptions: { exportType: "default", ref: true, svgo: false, titleProp: true },

source : https://medium.com/@praizjosh/how-to-import-svg-files-as-react-components-in-vite-97d6e1f2c046

Also If that can be helpful to someone, I had an issue with a component wrapped inside a SafeAreaView that needed a SafeAreaProvider (which is usually in another component), and for that I added a decorator to my story like that :

  decorators: [
    (Story) => (
      <SafeAreaProvider>
        <Story />
      </SafeAreaProvider>
    ),

Remains the main issue with NativeWind...

dannyhw commented 2 days ago

regarding tailwind styles you probably need to apply the nativewind babel stuff to the vite setup

juliencurro-aptar commented 2 days ago

You mean in vite.config.ts ? or a viteFinal block in the .storybook/main.js ?

dannyhw commented 2 days ago

in the vite.config.ts since thats getting used by storybook anyway

juliencurro-aptar commented 2 days ago

I am wondering if I am not falling into this bug : https://github.com/nativewind/nativewind/issues/992 Because it's only the className that doesn't seem to be working.

dannyhw commented 2 days ago

Heres a new example with nativewind https://github.com/dannyhw/vite-rnw-example

juliencurro-aptar commented 20 hours ago

It works, thanks.