addon-react-native-web
addon-react-native-web copied to clipboard
[Bug] @storybook/test package not working
Describe the bug
Whenever I add @storybook/addon-react-native-web package to addons array, @storybook/test package stops working.
When I comment out or remove @storybook/addon-react-native-web from addons array, the error is gone (screenshot of error below)
Here is my setup:
.storybook/main.js file:
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
const config = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-webpack5-compiler-swc",
"@storybook/addon-onboarding",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
"storybook-dark-mode",
"@storybook/addon-react-native-web", // it makes @storybook/test not working :D
],
framework: {
name: "@storybook/react-webpack5",
options: {},
},
core: {
builder: "webpack5",
},
webpackFinal: async (config, { configType }) => {
config.plugins.push(new NodePolyfillPlugin());
return config;
},
};
export default config;
.storybook/preview:
const preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;
.babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": ["react-native-web"]
}
package.json:
{
"name": "before-storybook",
"version": "1.0.0",
"description": "",
"private": true,
"author": "",
"scripts": {
"dev": "webpack serve --open",
"build": "webpack build",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@babel/core": "latest",
"@babel/preset-env": "latest",
"@babel/preset-react": "latest",
"@babel/preset-typescript": "latest",
"@react-native/babel-preset": "^0.74.86",
"@storybook/addon-react-native-web": "^0.0.24",
"@types/react": "^18",
"@types/react-dom": "^18",
"babel-loader": "latest",
"babel-plugin-react-native-web": "^0.19.12",
"babel-preset-react-app": "latest",
"babel-preset-react-native": "^4.0.1",
"html-webpack-plugin": "latest",
"metro-react-native-babel-preset": "^0.77.0",
"node-polyfill-webpack-plugin": "^4.0.0",
"react": "^18",
"react-dom": "^18.3.1",
"react-native-web": "^0.19.12",
"storybook-dark-mode": "^4.0.2",
"tty-browserify": "^0.0.1",
"typescript": "^4.8",
"webpack": "^5",
"webpack-cli": "latest",
"webpack-dev-server": "latest"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.6.1",
"@storybook/addon-a11y": "8.2.6",
"@storybook/addon-actions": "8.2.6",
"@storybook/addon-backgrounds": "8.2.6",
"@storybook/addon-controls": "8.2.6",
"@storybook/addon-docs": "8.2.6",
"@storybook/addon-essentials": "^8.2.6",
"@storybook/addon-highlight": "8.2.6",
"@storybook/addon-interactions": "^8.2.6",
"@storybook/addon-jest": "8.2.6",
"@storybook/addon-links": "^8.2.6",
"@storybook/addon-mdx-gfm": "8.2.6",
"@storybook/addon-measure": "8.2.6",
"@storybook/addon-onboarding": "^8.2.6",
"@storybook/addon-outline": "8.2.6",
"@storybook/addon-storysource": "8.2.6",
"@storybook/addon-themes": "8.2.6",
"@storybook/addon-toolbars": "8.2.6",
"@storybook/addon-viewport": "8.2.6",
"@storybook/addon-webpack5-compiler-swc": "^1.0.5",
"@storybook/blocks": "^8.2.6",
"@storybook/builder-manager": "8.2.6",
"@storybook/builder-vite": "8.2.6",
"@storybook/builder-webpack5": "8.2.6",
"@storybook/channels": "8.2.6",
"@storybook/cli": "8.2.6",
"@storybook/client-logger": "8.2.6",
"@storybook/codemod": "8.2.6",
"@storybook/components": "8.2.6",
"@storybook/core": "8.2.6",
"@storybook/core-common": "8.2.6",
"@storybook/core-events": "8.2.6",
"@storybook/core-server": "8.2.6",
"@storybook/core-webpack": "8.2.6",
"@storybook/csf-plugin": "8.2.6",
"@storybook/csf-tools": "8.2.6",
"@storybook/docs-tools": "8.2.6",
"@storybook/ember": "8.2.6",
"@storybook/html": "8.2.6",
"@storybook/html-vite": "8.2.6",
"@storybook/html-webpack5": "8.2.6",
"@storybook/instrumenter": "8.2.6",
"@storybook/manager": "8.2.6",
"@storybook/manager-api": "8.2.6",
"@storybook/nextjs": "8.2.6",
"@storybook/node-logger": "8.2.6",
"@storybook/preact": "8.2.6",
"@storybook/preact-vite": "8.2.6",
"@storybook/preact-webpack5": "8.2.6",
"@storybook/preset-create-react-app": "8.2.6",
"@storybook/preset-html-webpack": "8.2.6",
"@storybook/preset-preact-webpack": "8.2.6",
"@storybook/preset-react-webpack": "8.2.6",
"@storybook/preset-server-webpack": "8.2.6",
"@storybook/preset-svelte-webpack": "8.2.6",
"@storybook/preset-vue3-webpack": "8.2.6",
"@storybook/preview": "8.2.6",
"@storybook/preview-api": "8.2.6",
"@storybook/react": "^8.2.6",
"@storybook/react-dom-shim": "8.2.6",
"@storybook/react-vite": "8.2.6",
"@storybook/react-webpack5": "8.2.6",
"@storybook/router": "8.2.6",
"@storybook/server": "8.2.6",
"@storybook/server-webpack5": "8.2.6",
"@storybook/source-loader": "8.2.6",
"@storybook/svelte": "8.2.6",
"@storybook/svelte-vite": "8.2.6",
"@storybook/svelte-webpack5": "8.2.6",
"@storybook/sveltekit": "8.2.6",
"@storybook/telemetry": "8.2.6",
"@storybook/test": "^8.2.6",
"@storybook/theming": "8.2.6",
"@storybook/types": "8.2.6",
"@storybook/vue3": "8.2.6",
"@storybook/vue3-vite": "8.2.6",
"@storybook/vue3-webpack5": "8.2.6",
"@storybook/web-components": "8.2.6",
"@storybook/web-components-vite": "8.2.6",
"@storybook/web-components-webpack5": "8.2.6",
"jackspeak": "2.1.1",
"sb": "8.2.6",
"storybook": "^8.2.6"
}
}
webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
process.env.NODE_ENV = "development";
const host = process.env.HOST || "localhost";
module.exports = {
mode: "development",
devtool: "inline-source-map",
entry: "./src/index.tsx",
output: {
filename: "static/js/bundle.js",
},
devServer: {
compress: true,
hot: true,
host,
port: 3000,
},
plugins: [new HtmlWebpackPlugin()],
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx|mjs)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
resolve: {
extensions: [".mjs", ".js", ".cjs", ".jsx", ".tsx", ".ts"],
modules: ["node_modules"],
},
};
Screenshots and/or logs
Environment
- OS: macOS
- Node.js version: 18.19.0
- NPM version: 8.5.5
@Bronowsm hey, thanks for raising this issue. I have actually seen a similar issue but I'm not familiar with the test package implementation so I'll have to defer to others. Will look into it.
+1 👀
Sorry for the delay! This slipped off my radar but I will make sure to ask around tomorrow.
so looking back in my chat history with some people the information that I have is that it may have something to do with the CJS of the storybook/test package being messed up and forcing the mjs version could help. Not sure how to do that with webpack yet but leaving this note here for context.
I think first step would be to create a more minimal reproduction of the problem and then to verify which entrypoint is being used (i.e cjs/mjs)
one thing to try is adding to webpack final something like:
config.resolve.extensions = ['mjs', ...config.resolve.extensions]
to get mjs loading before cjs
@dannyhw thanks for hints, they were very useful - it was about forcing storybook/test package to use .mjs extension. But overriding config.resolve.extensions didn't do the trick. What I did instead, was to point out to the exact file in node_modules and storybook/test is working again!
config.resolve.alias = {
...config.resolve.alias,
'@storybook/test': path.resolve(__dirname, '../node_modules/@storybook/test/dist/index.mjs'),
};
'@storybook/test': path.resolve(__dirname, '../node_modules/@storybook/test/dist/index.mjs'),
Why didn't I think of that!? I moved on with the failure of the extension priority. Anywho, thanks @Bronowsm, works for me!
Interesting, its so strange that the wrong extension is always getting resolved 😕 . I wonder if theres something wrong with the exports in package.json for the storybook/test package.
Or maybe it has something to do with the babel loader that addon-react-native-web is including, not sure
Found another issue, it appears that the solution @Bronowsm proposed does work for stories, but not when mocking. The underlying implementation of fn appears to be just an export from @vitest/spy, so not immediately sure why this doesn't work.
Works: Button.stories.ts
import { fn } from '@storybook/test';
const meta = {
title: 'Example/Button',
component: Button,
args: { onClick: fn() },
} satisfies Meta<typeof Button>;
Works: useRouter.mock.ts
import { fn } from '@vitest/spy';
import * as actual from './useRouter';
export const useRouter = fn(actual.useRouter).mockName('useRouter').mockReturnValue({
back: () => null,
push: () => null,
replace: () => null,
parseNextPath: () => '',
});
Does NOT Work:
import { fn } from '@storybook/test';
import * as actual from './useRouter';
export const useRouter = fn(actual.useRouter).mockName('useRouter').mockReturnValue({
back: () => null,
push: () => null,
replace: () => null,
parseNextPath: () => '',
});
@JavanPoirier when you use the mock from storybook/test what problem/error do you get?
@JavanPoirier when you use the mock from storybook/test what problem/error do you get?
Just that it does not exist. Changing the import resolves it.
@JavanPoirier Does this happen in storybook 8.3? We changed the order of the export condition in 8.3. So that ESM always has highest prio:
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"node": "./dist/index.js",
"default": "./dist/index.mjs"
},
"./package.json": "./package.json"
},
Looks like I sorted it out, using the @vitest/spy import made it so the default mockReturnValue I specified in the useRouter.mock.ts was applied without directly importing it into the story to use in beforeEach (as the docs do say to do). I could then overwrite the default mock by importing it and specifying a chained mockReturnValue call.
Now when using the @storybook/test export I must ALWAYS have a beforeEach statement, import the mock and call mockReturnValue.
I kinda liked the ability to not have to add the beforeEach call and having just function stubs in place.
I am on storybook@npm:8.3.4
Example:
useRouter.mock.ts
import { fn } from '@vitest/spy';
import * as actual from './useRouter';
export const useRouter = fn(actual.useRouter).mockName('useRouter').mockReturnValue({
back: () => null,
push: () => null,
replace: () => null,
parseNextPath: () => '',
});
I never needed to import it anywhere or call beforeEach, the mock just worked.
Now with @storybook/test, I must import the mock into preview.tsx or a story and call mockReturnValue
Hey @JavanPoirier that's pretty weird. You should be using fn from @storybook/test instead. Could you share a reproduction repo so we can see what's going on?
Hey @JavanPoirier that's pretty weird. You should be using
fnfrom@storybook/testinstead. Could you share a reproduction repo so we can see what's going on?
https://github.com/JavanPoirier/rn-web-sb-mock