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

theme context not working with styled components in jest #2351

Closed
coltonehrman opened this issue Apr 15, 2021 · 12 comments
Closed

theme context not working with styled components in jest #2351

coltonehrman opened this issue Apr 15, 2021 · 12 comments

Comments

@coltonehrman
Copy link

coltonehrman commented Apr 15, 2021

I am upgrading to emotion v11

I am getting errors in my unit tests indicating that the theme context is not provided properly, it is just an empty object {}

Sample render util

import { Router } from 'react-router-dom'
import { render } from '@testing-library/react'
import { createMemoryHistory } from 'history'
import { ThemeProvider } from '@emotion/react'
import theme from '../src/theme'

function renderWithRouterAndTheme(ui, { route = '/', ...renderOptions } = {}) {
  const history = createMemoryHistory({ initialEntries: [route] })
  const utils = render(
    <ThemeProvider theme={theme}>
      <Router history={history}>{ui}</Router>
    </ThemeProvider>,
    renderOptions,
  )
  return {
    ...utils,
    history,
  }
}

Sample test case

test('will render without error', () => {
  const { container, getByText } = renderWithRouterAndTheme(
    <Component {...data} />,
  )
})

Sample component

import styled from '@emotion/styled'
import { Link } from 'react-router-dom'
import { Box } from '../box'

const StyledComponent = styled(Box)`
  ...
  background-color: ${props => props.theme.colors.secondary.greylight};
  ...
`

const App = (props) => {
  ...

  return (
    <StyledComponent />
    ...
  )
}
@iChenLei
Copy link
Contributor

https://github.com/emotion-js/emotion/blob/main/packages/react/src/theming.js

ThemeContext is just a simple React Context, maybe a small reproducation github repo is more helpful for us to debug, thanks.

@coltonehrman
Copy link
Author

coltonehrman commented Apr 16, 2021

https://github.com/emotion-js/emotion/blob/main/packages/react/src/theming.js

ThemeContext is just a simple React Context, maybe a small reproducation github repo is more helpful for us to debug, thanks.

Unfortunately, I will not be able to provide anything like that as the code base I am working on is private and has some custom tooling/packages involved.

I can try to reproduce any bits that would be helpful to debug, such as the jest, babel, and webpack configs involved.

I have a hunch it is babel config related but cannot seem to narrow down exactly what config works or why. I was hoping to maybe get some insight into what dependencies emotion requires for the theme context to work properly, or what could be causing it behave strangely.

@iChenLei
Copy link
Contributor

2021-04-16 09 57 58

If you are a vscode user, you can create a simple javascript debug terminal, and then run npm run jest in this ternimal. Add breakpoint on the code line where is ${({ theme }) => theme.inViewStyle()}. this first question we need to solve is that theme object we get is what? at unit test runtime.

@coltonehrman
Copy link
Author

2021-04-16 09 57 58

If you are a vscode user, you can create a simple javascript debug terminal, and then run npm run jest in this ternimal. Add breakpoint on the code line where is ${({ theme }) => theme.inViewStyle()}. this first question we need to solve is that theme object we get is what? at unit test runtime.

This sounds useful, let me play around with it a bit and see what all I discover.

The 'theme' object is always just an empty object by the way '{}'

@coltonehrman
Copy link
Author

coltonehrman commented Apr 16, 2021

Update on this.

I tested with using useTheme hook from @emotion/react and passing the theme down to the styled component directly and it worked.

So, it seems like the styled component from @emotion/styled may have a qwerk in it where it doesn't consume the theme context properly in certain situations.

Below is a code snippet of what I tested with, please refer to comments above to see what I had previously

import styled from '@emotion/styled'
import { useTheme } from '@emotion/react'
import { Link } from 'react-router-dom'
import { Box } from '../box'

const StyledComponent = styled(Box)`
  ...
  background-color: ${props => props._theme.colors.secondary.greylight};
  ...
`

const App = (props) => {
  ...
  const _theme = useTheme()

  return (
    <StyledComponent
      _theme={_theme}
    />
    ...
  )
}

@coltonehrman coltonehrman changed the title theme not available in jest testing theme context not working in styled components during jest testing Apr 16, 2021
@coltonehrman coltonehrman changed the title theme context not working in styled components during jest testing theme context not working with styled components in jest Apr 16, 2021
@coltonehrman
Copy link
Author

I have dug into this a bit and found that my project includes a styled-base package which appears to use ThemeContext from @emotion/core instead of @emotion/react

Screen Shot 2021-04-16 at 12 35 30 PM

Not sure why this package is here, as it appears to have been replaced by styled/base here https://github.com/emotion-js/emotion/blob/6c4a9e50f177900f54f7b3368a55b1a7d5e3c002/packages/styled/src/base.js

@iChenLei
Copy link
Contributor

@emotion/reactand@emotion/core(deprecated) is different npm package, please upgrade @emotion/core to @emotion/react every where.
Why rename this package? look this issue #1883 for detail and this official RFC #1635 about Package renaming .

Anyway, @emotion/react's React context and @emotion/core's React context is not a same context, so you can't use @emotion/react's useTheme hook to access @emotion/core's ThemeContext. That's all.

@Andarist
Copy link
Member

Yep, this means that for some reason your dependency tree is wrong and you load separate versions of our libraries.

@coltonehrman
Copy link
Author

I was able to finally resolve this by using the babel-macro libs
See - https://emotion.sh/docs/babel-macros

So, I assume the issue lied within my app's babel/webpack configs across dev and test environment.

Anyways, thanks @iChenLei and @Andarist for your input and help :)

@santosh1997
Copy link

santosh1997 commented Apr 10, 2023

@iChenLei @Andarist

I'm facing the same issue right after upgrading the jest version to 29
theme prop in the styled component is passed with empty object {}
theme object from useTheme works perfectly fine
Not sure how would babel-macros be a solution?
Can anyone help me on this?

@oleksandr-danylchenko
Copy link

oleksandr-danylchenko commented Mar 21, 2024

I hit the same issue yesterday using the following app structure:

image

For some reason, within the library, the theme returned from the useTheme contained a fully populated object. But the theme in the css callback was {} 🤷🏻‍♂️

Here's what I found on the styles serialization debugging:
image


Then I checked the bundle generated by the library and I found that it has its own instance of the ThemeContext, unrelated to the parent app❗
image
So on serialization of the lib styles, @emotion was using that distinct empty instance to populate the theme in the css callback!


But why did the useTheme work though? That's because the useTheme was imported from the @emotion/react package. And under the hood - useTheme was using the populated ThemeContext of the parent app!
image

export const ThemeContext = /* #__PURE__ */ React.createContext<Object>({})
if (process.env.NODE_ENV !== 'production') {
ThemeContext.displayName = 'EmotionThemeContext'
}
export const useTheme = () => React.useContext(ThemeContext)


Combining that, here're the findings I made in the bundled library code:
image


The solution I found for now is to mark all the @emotion dependencies as external for the library!

image

In that way, the parent app is now a single source of truth for the ThemeContext and all the @emotion methods and transforms became imported!
image

@AnsonH
Copy link

AnsonH commented Aug 19, 2024

Thanks @oleksandr-danylchenko for the detailed investigation 👍

For my case, solving the below warning that I saw in the console solved the theme context issue:

You are loading @emotion/react when it is already loaded. Running multiple instances may cause problems. This can happen if multiple versions are used, or if multiple builds of the same version are used.

I'm using Create React App, so this solution worked for me. Check out other solutions in that thread for other bundlers.


My case is similar to @oleksandr-danylchenko's case, where I have a "Main App" and "Component Library":

Component Library package.json:

{
  "dependencies": {
    "@emotion/styled": "^11.11.0",
  }
}

Main App package.json:

{
  "dependencies": {
    "@emotion/styled": "^11.9.3",
    "my-component-library": "^0.1.0",  // My Component Library
  }
}

Since my component library is using a different Emotion version than my main app, it caused two versions of Emotion being installed in my Main App.

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

No branches or pull requests

6 participants