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

Can't get emotion's css prop working inside storybook #7540

Closed
jaredh159 opened this issue Jul 23, 2019 · 61 comments
Closed

Can't get emotion's css prop working inside storybook #7540

jaredh159 opened this issue Jul 23, 2019 · 61 comments

Comments

@jaredh159
Copy link

jaredh159 commented Jul 23, 2019

Describe the bug
I can't seem to get emotion's css prop to work inside of Storybook. Seems like emotion is now the dominant css-in-js library, and the css prop is emotion's recommended primary way of styling components, so I thought I was choosing a well-travelled happy path here, but I'm stuck. I'm very open to the fact that I'm doing something dumb though, of course.

In my actual project repo I'm getting the same thing described here and here. I tried all of the recommendations in both those threads, and still no joy -- and it looks like I’m not the only one.

So, I created a brand new React Storybook repo to just hello-world using emotion css prop and storybook. And I can't get that to work either! I'm wondering if someone could look at the super simple repro repo I made and tell me where I went wrong, here it is:

https://github.com/jaredh159/storybook-emotion-css

I'd be up for submitting a PR to the docs clarifying how to do this, if someone can show me where I messed up.

To Reproduce
Steps to reproduce the behavior:

  1. git clone [email protected]:jaredh159/storybook-emotion-css.git
  2. cd storybook-emotion-css && yarn && yarn storybook
  3. Load http://localhost:6016 and see the error :(

TypeError: Cannot read property 'name' of null

Expand for full error message
ERROR in ./src/CssPropButton.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: Cannot read property 'name' of null
   at getDeclaratorName (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:212:32)
   at getIdentifierName (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:267:24)
   at getLabelFromPath (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:190:19)
   at transformExpressionWithStyles (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:450:19)
   at transformCssCallExpression (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:708:31)
   at /test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:797:9
   at Array.forEach (<anonymous>)
   at /test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:796:26
   at Array.forEach (<anonymous>)
   at Object.emotionCoreMacroThatsNotARealMacro [as @emotion/core] (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:794:27)
   at PluginPass.ImportDeclaration (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:891:45)
   at newFn (/test-repo/node_modules/@babel/traverse/lib/visitors.js:193:21)
   at NodePath._call (/test-repo/node_modules/@babel/traverse/lib/path/context.js:53:20)
   at NodePath.call (/test-repo/node_modules/@babel/traverse/lib/path/context.js:40:17)
   at NodePath.visit (/test-repo/node_modules/@babel/traverse/lib/path/context.js:88:12)
   at TraversalContext.visitQueue (/test-repo/node_modules/@babel/traverse/lib/context.js:118:16)
@ ./stories/index.stories.js 5:0-49 6:143-156
@ ./stories sync \.stories\.js$
@ ./.storybook/config.js
@ multi ./node_modules/@storybook/core/dist/server/common/polyfills.js ./node_modules/@storybook/core/dist/server/preview/globals.js ./.storybook/config.js (webpack)-hot-middleware/client.js?reload=true

Expected behavior
I expected to be able to use the css prop from emotion within Storybook.

Code snippets
The repro repo does add a .babelrc inside the stories dir as recommended in the emotion docs and on this similar issue, like so:

{
  "presets": ["@emotion/babel-preset-css-prop"]
}

I also tried this workaround, but no joy.

System:

  • OS: Mac
  • Device: Macbook Pro 2015
  • Browser: Chrome
  • Framework: React
  • Version: 5.1.9
@stale
Copy link

stale bot commented Aug 15, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@ryanlanciaux
Copy link

I'm seemingly bumping into this too. Also have tried all the suggested workarounds in the referenced issues.

My CSS props work just fine outside of Storybook but once I am running in storybook my css props aren't working. Anything that's basically just a string works but any time I am referencing props in the css / styled tag, the css property name isn't present.

@stale
Copy link

stale bot commented Sep 10, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Sep 10, 2019
@brekk
Copy link
Contributor

brekk commented Sep 15, 2019

I'm seeing this same issue in @storybook/[email protected], it makes Storybook's core features basically unusable. I've also attempted all suggested solutions, including .babelrc inside .storybook, updating .storybook/webpack.config.js and others.

Has there been any traction on this issue?

@stale stale bot removed the inactive label Sep 15, 2019
@brekk
Copy link
Contributor

brekk commented Sep 15, 2019

Also just upgraded to 5.2.0, seeing the same issues.

@brekk
Copy link
Contributor

brekk commented Sep 15, 2019

🙌 require.resolve('@emotion/babel-preset-css-prop') + blowing away node_modules and rerunning yarn fixed it.

Based our now working config kinda on this: emotion-js/emotion#1359 (comment) but with some additional crap for resolving sass stuff (we're in the process of migrating and our storybook stuff stopped working without anyone noticing).

Sorry for any inconvenience and thanks for the awesome library.

@brekk
Copy link
Contributor

brekk commented Sep 15, 2019

@shilman in case you missed, I think this ticket is potentially resolvable but I'd ask @ryanlanciaux / @jaredh159 to verify

@jaredh159
Copy link
Author

@brekk I'm not clear exactly what the fix is you found, and where I would apply it. Could you send a PR to my simple repro repo linked in the OP?

@brekk
Copy link
Contributor

brekk commented Sep 17, 2019

@jaredh159 @shilman I was able to track at least part of the problem down in the linked storybook-emotion-css repo: https://github.com/emotion-js/emotion/blob/master/packages/babel-plugin-emotion/src/utils/label.js#L66 Seems to be the relevant line -- At least in my testing I was able to get one of the two examples (CssPropButton) rendering correctly by modifying the if to be:

if (parent.isFunctionDeclaration() && parent.node && parent.node.id && parent.node.id.name) {

With that one of the two components renders correctly. I'd be happy to help troubleshoot more but that's all I was able to uncover in that repo.

@shilman shilman modified the milestones: 5.1.x, 5.2.x Sep 23, 2019
@illinar
Copy link

illinar commented Oct 20, 2019

I also ran into this issue with Babel7+Typescript+React+Emotion+Storybook setup. After hours of digging through docs and experiments I confirmed that .storybook/webpack.config.js:

const path = require("path");
module.exports = function({ config }) {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    loader: require.resolve("babel-loader"),
    options: {
      presets: [["react-app", { flow: false, typescript: true }], require.resolve("@emotion/babel-preset-css-prop")],
    },
  });

  config.resolve.extensions.push(".ts", ".tsx");
  return config;
};

works only when root .babelrc does not specify @emotion/babel-preset-css-prop plugin (which creates problems when transpiling for publish).

Here is working .babelrc in the package root:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-typescript"
    // "@emotion/babel-preset-css-prop"
  ]
}

Un-commenting babel-preset-css-prop plugin mysteriously reverts to non-working behavior.

I understand that ordering of presets is key here, since we want babel-preset-css-prop plugins to run before plugin-transform-react-jsx from preset-react. In fact, as far I understand we don't need plugin-transform-react-jsx since the goal is to use jsx from emotion.

At the same time I don't completely understand how presets in root .babelrc interact with webpack babel-loader options. The observed behavior suggests that root presets are processed first, then loader option presets are processed and babel-preset-css-prop from options is merged into the first instance from the root config. This would explain the situation, but I am speculating at this point. In any case I still don't understand how to make configuration work for both storybook and regular publish.

@shilman
Copy link
Member

shilman commented Oct 21, 2019

@illinar Have you tried the babelrc: false option in the loader #8062 (comment)

@illinar
Copy link

illinar commented Oct 21, 2019

@illinar Have you tried the babelrc: false option in the loader

Thanks. This option seems to do the trick.

@shilman
Copy link
Member

shilman commented Oct 31, 2019

Can somebody create a preset for this? https://storybook.js.org/docs/presets/writing-presets/#docs-content

@justdanallen
Copy link

justdanallen commented Nov 21, 2019

The OG post is unrelated to most of the discussion on this thread. I actually pulled his repo and fixed the error. He was exporting an anonymous component.

Error message:
TypeError: /storybook-emotion-css/src/CssPropButton.js: Cannot read property 'name' of null

Original CssPropButton.js:

export default function() {
  return (
    <button
      css={css`
        color: hotpink;
      `}
    >
      I should be hot pink!
    </button>
  );
}

Fixed with no error:

export default function CssPropButton() {
  return (
    <button
      css={css`
        color: hotpink;
      `}
    >
      I should be hot pink!
    </button>
  );
}

Unfortunately, this didn't solve the error I am having. His build set with the .bablerc in .storybook dir is working fine.

@jscheel
Copy link

jscheel commented Dec 11, 2019

I cannot get this to work at all. The only way I can get this to work is if I use the jsx pragma, and then, it breaks storybook's ability to generate or infer prop descriptions for the docs. I'm not certain, but I'm assuming this is because both storybook's doc addon and emotion want to rewrite React.createElement?

@duncanleung
Copy link

duncanleung commented Jan 7, 2020

In my project I was able to get Storybook to recognize css-prop (without using the jsx pragma) by adding this config to /.storybook/webpack.config.js.

Line 17 - Line 19:

  config.module.rules[0].use[0].options.presets = [
    require.resolve('@babel/preset-react'),
    require.resolve('@babel/preset-env'),
    // Emotion preset must run BEFORE reacts preset to properly convert css-prop.
    // Babel preset-ordering runs reversed (from last to first). Emotion has to be after React preset.
    require.resolve('@emotion/babel-preset-css-prop'),
  ];

Also, I am including the @emotion/babel-preset-css-prop for .ts and .tsx files

Line 41 - Line 43:

  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    loader: require.resolve('babel-loader'),
    options: {
      presets: [
        ['react-app', { flow: false, typescript: true }],
        // Emotion preset must run BEFORE reacts preset to properly convert css-prop.
        // Babel preset-ordering runs reversed (from last to first). Emotion has to be after React preset.
        require.resolve('@emotion/babel-preset-css-prop'),
      ],
      // other plugins here...
    },
  });

I created a template to share my project config that uses Gatsby + TypeScript + Emotion + Storybook + React Intl + SVGR + Jest:
https://github.com/duncanleung/gatsby-typescript-emotion-storybook

Here's a link to my webpack.config.js that enables @emotion/babel-preset-css-prop:

@shilman shilman removed this from the 5.2.x milestone Jan 11, 2020
@Sawtaytoes
Copy link

Sawtaytoes commented Mar 11, 2021

@redshoga Your fix worked for me.

This is the JavaScript version:

.storybook/main.js

module.exports = {
  webpackFinal: (config) => {
    config.module.rules.push({
      test: /\.(js|jsx)$/,
      loader: require.resolve('babel-loader'),
      options: {
        cacheDirectory: true,
        presets: [require.resolve('@emotion/babel-preset-css-prop')],
      },
    });
    return config;
  },
};

This project is brand new built with Emotion and Next.js just tonight. I'm surprised I had to add this hack for it to work.

I have Storybook + Emotion playing nicely together without a hack on two separate projects at work, but I'm not sure why it works there and not here.

A few things to note:

  1. I'm on Windows. Dunno if it matters, but maybe it's not able to find the right file.
  2. I named my Babel config file babel.config.js rather than .babelrc.

@jeongsd
Copy link

jeongsd commented Mar 12, 2021

I fixed it with the settings below.
Hope it helps someone.

pacakge.json

"react": "^17.0.1",
"react-dom": "^17.0.1",
"@storybook/react": "^6.1.21",
"@emotion/react": "^11.1.2",
"typescript": "^4.1.3",

.storybook/main.js

module.exports = {
  reactOptions: {
    fastRefresh: true,
    strictMode: true,
  },
  stories: ['../src/**/*.stories.@(ts|tsx|mdx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  webpackFinal: async (config) => {
    config.module.rules[0].use[0].options.presets = [
      require.resolve('@babel/preset-env'),
      require.resolve('@babel/preset-typescript'),
      [
        require.resolve('@babel/preset-react'),
        {
          runtime: 'automatic',
          importSource: '@emotion/react',
        },
      ],
    ]

    config.module.rules[0].use[0].options.plugins = [
      ...config.module.rules[0].use[0].options.plugins,
      '@emotion/babel-plugin',
    ]

    return config
  },
}

@ndelangen
Copy link
Member

It'd be great to have this code encapsulated in a preset!

@ghost
Copy link

ghost commented Apr 22, 2021

I managed to make it work using TypeScript + Emotion 11 and JSX Pragma.

  • TypeScript: v4.1.4
  • Emotion: 11.1.5
  • Storybook: v6.1.17

I'm not using Babel at all because I want to typecheck during build.

I had to change the @babel/preset-react runtime to be "classic" in order to use the JSX Pragma.

.storybook/main.js:

const path = require('path')

const toPath = _path => path.join(process.cwd(), _path)

module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  babel: config => {
    config.presets[2][1] = { runtime: 'classic' } // <-- this is the change
    return config
  },
  webpackFinal: async config => {
    return {
      ...config,
      resolve: {
        ...config.resolve,
        alias: {
          ...config.resolve.alias,
          '@emotion/core': toPath('node_modules/@emotion/react'),
          '@emotion/styled': toPath('node_modules/@emotion/styled'),
          'emotion-theming': toPath('node_modules/@emotion/react'),
        },
      },
    }
  },
}

@crushjz Thanks sir, now it's works!

daniel-hauser added a commit to daniel-hauser/react-organizational-chart that referenced this issue Jun 3, 2021
Removed because of storybook not loading when a @jsx pragma is used
See storybookjs/storybook#7540
chrisirhc added a commit to chrisirhc/modular-ramp that referenced this issue Jun 28, 2021
@Sawtaytoes
Copy link

Sawtaytoes commented Jul 20, 2021

There are 2 solutions:

  1. Using the older @emotion/babel-preset-css-prop.
  2. Modifying Storybook's babel-loader to use a custom configuration.

Both of these versions are super hacky and aren't the correct solution nor will they always be guaranteed to work through Storybook upgrades.

Instead, forget the Webpack config altogether. Storybook has a native way of modifying the Babel config:
https://storybook.js.org/docs/react/configure/babel

The proper option is adding Babel overrides:

// The location of your Babel config.
const babelConfig = require('../babel.config.js');

const storybookConfig = {
  // ...
  babel: async (
    options
  ) => {
    options
      .overrides
      .push({
        ...babelConfig,
        test: '*', // This says "for all files, use this override".
      })

    return options;
  },
  // ...
}

module.exports = storybookConfig

@deadlyhifi
Copy link

We've just encountered this issue. The project uses "typescript": "^4.3.2", "@emotion/react": "^11.4.1", "@storybook/react": "^6.3.8".

tsconfig.json has "jsx": "react-jsx", "jsxImportSource": "@emotion/react", settings.

To fix it, npm i -D @emotion/babel-preset-css-prop and in .storybook/main.js add it to the default presets:

module.exports = {
  stories: […],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  babel: async (options) => {
    options.presets.push('@emotion/babel-preset-css-prop')
    return options
  },
}

So far so good.

@tpict
Copy link

tpict commented Oct 19, 2021

With the solutions above I get this in the DOM:

css="You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop).

Don't know what I'm missing. I'm using this same Babel configuration for the project build and it works fine.

@Sawtaytoes
Copy link

Sawtaytoes commented Dec 3, 2021

There are 2 babel configs you have to worry about. Depending on if you have JSX Pragma setup correctly and the latest babel-preset-react, it changes which you'll want to use.

@emotion/babel-plugin is used when you're using React v17 or higher, you'll want this comment at the top of each file using Emotion:

/** @jsxRuntime classic @jsx jsx */
import {
  css,
  jsx,
} from '@emotion/react'

^^ You need this for Create-React-App.

If you have the correct react babel preset with JSX pragma set to automatic, then using the @emotion/babel-plugin works out-of-the-box without any jsx import or that comment.

If you have an older React, you'll want to use @emotion/babel-preset-css-prop. You can use both in your project, but I'd recommend figuring out how to get @emotion/babel-plugin for all builds or simply add the jsxRuntime comment.

I know this is confusing, and I don't fully understand it myself, but I've dealt with these issues on many projects.

Getting Emotion working Next.js was probably one of the hardest, but I figured out a hack-fix to make it work.

warugaki-web-developer added a commit to warugaki-web-developer/starter-vite-react-ts that referenced this issue Dec 11, 2021
@stale
Copy link

stale bot commented Jan 9, 2022

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Jan 9, 2022
@velopert
Copy link

For those people who want to use css prop in CRA based project with TS, modify your .storybook/main.js as below:

module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/preset-create-react-app',
  ],
  framework: '@storybook/react',
  core: {
    builder: 'webpack5',
  },
  webpackFinal: async (config, { configType }) => {
    const oneOfRule = config.module.rules.find((rule) => rule.oneOf)
    const babelRule = oneOfRule.oneOf.find((rule) =>
      rule.loader?.includes('babel-loader')
    )

    babelRule.options.presets.push('@emotion/babel-preset-css-prop')

    return config
  },
}

@D1no
Copy link

D1no commented Mar 7, 2022

Thank you @velopert ! For people coming here using xstyled (might apply to other people too), you might also need to set emotionAlias to false. Leading to this:

// .storybook/main.js
module.exports = {
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
    "@storybook/preset-create-react-app",
    "storybook-dark-mode",
  ],
  framework: "@storybook/react",
  core: {
    builder: "webpack5",
  },
  features: {
    emotionAlias: false,  // <----------------------------- here
  },
  webpackFinal: async (config, { configType }) => {
    const oneOfRule = config.module.rules.find((rule) => rule.oneOf);
    const babelRule = oneOfRule.oneOf.find((rule) =>
      rule.loader?.includes("babel-loader"),
    );

    babelRule.options.presets.push("@emotion/babel-preset-css-prop");

    return config;
  },
};

Works as in

├─ @xstyled/emotion@npm:3.5.1

├─ @emotion/babel-preset-css-prop@npm:11.2.0
├─ @emotion/react@npm:11.8.1
├─ @emotion/styled@npm:11.8.1

├─ @mdx-js/react@npm:1.6.22
├─ @storybook/addon-actions@npm:6.4.19
├─ @storybook/addon-docs@npm:6.4.19
├─ @storybook/addon-essentials@npm:6.4.19
├─ @storybook/addon-interactions@npm:6.4.19
├─ @storybook/addon-links@npm:6.4.19
├─ @storybook/builder-webpack5@npm:6.4.19
├─ @storybook/manager-webpack5@npm:6.4.19
├─ @storybook/node-logger@npm:6.4.19
├─ @storybook/preset-create-react-app@npm:4.0.1
├─ @storybook/react@npm:6.4.19
├─ @storybook/testing-library@npm:0.0.9

├─ react-dom@npm:17.0.2
├─ react-router-dom@npm:6.2.2
├─ react-scripts@npm:5.0.0
├─ react@npm:17.0.2

@ndelangen
Copy link
Member

The emotionAlias should no longer be needed at all in the alpha version of storybook now.

I think this issue can be closed It is fixed in 6.5.apha thanks to #17000

@hatton
Copy link

hatton commented Apr 2, 2022

For projects using Vite instead of Wepack, see: storybookjs/builder-vite#210

@testerez
Copy link
Contributor

testerez commented Apr 4, 2023

If that helps anyone, here is what worked for me (using storybook 7):

// .storybook/main.js
module.exports = {
  // ...
  babel: (options) => ({
    ...options,
    presets: [...options.presets, '@emotion/babel-preset-css-prop'],
  }),
}

kimyouknow added a commit to depromeet/www.depromeet.com that referenced this issue Sep 9, 2023
kimyouknow added a commit to depromeet/www.depromeet.com that referenced this issue Sep 10, 2023
* chore: storybook emotion 설정

- storybookjs/storybook#7540

* feat: svg wrapper component

* feat: arrow icon
kimyouknow added a commit to depromeet/www.depromeet.com that referenced this issue Sep 10, 2023
* chore: storybook emotion 설정

- storybookjs/storybook#7540

* feat: svg wrapper component

* feat: arrow icon

* feat: typo theme

* feat: emotion theme

* feat: emotion faq example

* feat: emotion theme

* feat: emotion faq example

* feat: typo theme

* chore: vscode typescript 버전 세팅

* chore: rename FAQ -> FAQItem

* feat: create FAQList

* feat: create FAQList story

* feat: li list style none

* chore: mock 데이터 위치 수정
@chrmcg
Copy link

chrmcg commented Mar 5, 2024

In case this helps anybody, I just upgraded to storybook 7 and the babel-preset-css-prop solution didn't work for me, but setting importSource: "@emotion/react" did!

// .storybook/main.tsx
import type { StorybookConfig } from "@storybook/react-webpack5";

const config: StorybookConfig = {
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-onboarding",
    "@storybook/addon-interactions",
    "@storybook/addon-mdx-gfm"
  ],
  framework: {
    name: "@storybook/react-webpack5",
    options: {
      builder: {
        useSWC: true
      }
    }
  },
  swc: (config, options) => ({
    jsc: {
      transform: {
        react: {
          runtime: "automatic",
          importSource: "@emotion/react"
        }
      }
    }
  }),
  docs: {
    autodocs: "tag"
  }
};
export default config;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests