Running React Testing Library to generate snapshots on JSX which uses the Emotion css prop results in no CSS being rendered.
I have tried using the "#emotion/jest/serializer" but still no luck.
Component:
<button
role="button"
css={(theme)=> {
backgroundColor: 'hotpink',
'&:hover': {
color: theme('lightgreen'),
},
}}
/>
Test:
import React from 'react';
import { render } from '#testing-library/react';
import { createSerializer } from '#emotion/jest';
import { Component } from './component';
expect.addSnapshotSerializer(createSerializer());
describe('IconComponent', () => {
it('should match the snapshot for the given props', () => {
const { asFragment } = render(<Component icon="knownIcon" />);
expect(asFragment()).toMatchSnapshot();
});
Snapshot:
(This gets rendered as an anonymous object rather than CSS)
exports[` 1`] = `
<DocumentFragment>
<button
css="[object Object]"
role="button"
/>
</DocumentFragment>
`;
I think you are just missing the final step.
https://emotion.sh/docs/css-prop
Set the jsx pragma at the top of your source file that uses the css prop. This option works best for testing out the css prop feature ...... such as Create React App 4 then /** #jsx jsx / pragma might not work and you should use /* #jsxImportSource #emotion/react */ instead.
From the emotion doc, adding /* #jsxImportSource #emotion/react */ on the top of your component file helps css option to render probably in the test.
CustomButton.js
/** #jsxImportSource #emotion/react */
export function CustomButton() {
return (
<button
css={{
"backgroundColor": "hotpink",
"&:hover": {
color: "lightgreen"
}
}}
></button>
);
}
Result
exports[`IconComponent should match the snapshot for the given props 1`] = `
<DocumentFragment>
.emotion-0 {
background-color: hotpink;
}
.emotion-0:hover {
color: lightgreen;
}
<button
class="emotion-0"
/>
</DocumentFragment>
`;
If you are not using create-react-app, use the follow instead:
/** #jsx jsx */
import { jsx } from '#emotion/react'
Here is the repo, you can clone it to test it.
Older Version
For older version of react (< 16.4), you will need to use back "#emotion/core" instead of "#emotion/react" to transpile the file in old way.
package.json
"#emotion/core": "10.1.1",
Button.js
/** #jsx jsx */
import { jsx } from '#emotion/core' <--- use the #emotion/core to transpile the file in old way.
import React from "react";
const Button = () => {
return (
<button
css={{
backgroundColor: "hotpink",
"&:hover": {
color: "lightgreen"
}
}}
></button>
);
};
export default Button;
Here is the repo for demonstration
Another solution https://emotion.sh/docs/testing#writing-a-test
import React from 'react'
import renderer from 'react-test-renderer'
const Button = props => (
<button
css={{
color: 'hotpink'
}}
{...props}
/>
)
test('Button renders correctly', () => {
expect(
renderer.create(<Button>This is hotpink.</Button>).toJSON()
).toMatchSnapshot()
})
Related
i'm currently working on a project using stitches with cra but i've stuck to a problem with css props.
here's my code
Texts/index.tsx
import React from 'react';
import { TextStyle } from './textStyle';
const Texts = ({ text, css }: PropsType) => {
console.log(css);
return (
<>
<TextStyle css={{ ...css }} >
<>{text}</>
</TextStyle>
</>
);
};
export default Texts;
and this index.tsx is exported to another components
Container/index.tsx
import { styled, css } from '../../../stitches.config';
// atoms
import Texts from 'src/components/atoms/texts';
const PageContainer = () => {
return (
<Container>
<Contents >
<div>
<Texts
css={{ color: 'red' }}
/>
<Texts
css={{ paddingTop: '20px' }}
/>
</div>
</Contents>
</Container>
);
};
export default PageContainer;
as you can see with the above code, contains css as its child but css is never rendered at all
can anyone help me with this issue?
FYI, console.log(css); returned undefined to me.
Thank you in advance!
I'am using Emotion CSS prop to style my Gatsby / Typescript application.
I am using Gatsby 2 and Emotion 11.
In e.g. my Header component I render a Logo component:
/** #jsx jsx */
import { FC } from 'react';
import { jsx } from '#emotion/react';
import Logo from 'components/Logo';
import useSiteMetaData from 'hooks/useSiteMetadata';
import Styles from './Header.styles';
import { Props } from './Header.types';
const Header: FC<Props> = () => {
const styles = Styles();
const { title } = useSiteMetaData();
return (
<header css={styles.root}>
<Logo siteTitle={title} css={styles.logo} />
</header>
);
};
export default Header;
But on the css prop of my child component I get a Typescript error:
Property 'css' does not exist on type 'IntrinsicAttributes &
Props'.ts(2322)
I added below to tsconfig.json and that solves the linting error:
{
"compilerOptions": {
"types": ["#emotion/react/types/css-prop"],
}
}
But somehow the styles are not applied from within my Header.styles.ts, so I am able to overwrite/add some styles of my child component (Logo)?
You have such example in css-prop docs.
The P component acts like your Logo component, and ArticleText is your Header
const P = props => (
<p
css={{
margin: 0,
fontSize: 12,
lineHeight: '1.5',
fontFamily: 'sans-serif',
color: 'black'
}}
{...props} // <- props contains the `className` prop
/>
)
const ArticleText = props => (
<P
css={{
fontSize: 14,
fontFamily: 'Georgia, serif',
color: 'darkgray'
}}
{...props} // <- props contains the `className` prop
/>
)
You mean need to pass the className prop, see related answer.
I am just started to learn reactjs using material-ui but getting this error when apply style to my component. My code:
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}));
class NavMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
}
render() {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="Menu"
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
className={classes.title}
>
News
</Typography>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
</div>
);
}
}
export default NavMenu;
and this is Error:
material-ui makeStyles function only works inside function components, as it uses the new React Hooks APIs inside.
You have two options:
Convert your class component to a functional component.
Use a Higher Order Component as in material-ui docs
I personally recommend the first approach, as this is becoming the new standard in React development.
This tutorial may help you get started with functional components
and check the docs for React Hooks
Use withStyles:
App.js:
import {withStyles} from '#material-ui/core/styles'
// ...
const styles = theme => ({
paper: {
padding: theme.spacing(2),
// ...
},
// ...
})
class App extends React.Component {
render() {
const {classes} = this.props
// ...
}
}
export default withStyles(styles)(App)
Root.js:
import React, {Component} from 'react'
import App from './App'
import {ThemeProvider} from '#material-ui/styles'
import theme from '../theme'
export default class Root extends Component {
render() {
return (
<ThemeProvider theme={theme}>
<App/>
</ThemeProvider>
)
}
}
theme.js:
import {createMuiTheme} from '#material-ui/core/styles'
const theme = createMuiTheme({
palette: {
primary: ...
secondary: ...
},
// ...
}
export default theme
See Theming - Material-UI.
See Higher-order component API.
if you have created a functional component and still run into this issue... the next thing to look for are the dependency versions.
I tried a new stackblitz project to test a material-ui component and got this error. My dependencies were:
react 16.12
react-dom 16.12
#material-ui/core 4.9.14
So I had to change to latest react version using react#latest and react-dom#latest which got me to the following:
react 16.13.1
react-dom 16.13.1
#material-ui/core 4.9.14
Sharing here so that it can help other people who run into this... thanks to this post for the hint
I'm trying to implement some styling using Material-UI's withStyles method, however I'm not able to get classes as a prop. Any suggestions as to what I'm missing? I've included the relevant code below, but note that there is an <App> component in this file that I'm leaving out for brevity.
import React from 'react'
import ReactDOM from "react-dom";
import {Paper, Typography} from '#material-ui/core'
import {withStyles} from '#material-ui/core/styles'
import NavBar from "./navBar";
class Recipe extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log('Recipe Did Mount')
}
render() {
const {recipeData, classes} = this.props;
return (
<Paper>
<Typography className={classes.recipeName}>{recipeData.name}</Typography>
<Typography className={classes.recipeIngredients}>{recipeData.ingredients}</Typography>
<Typography className={classes.recipeInstructions}>{recipeData.instructions}</Typography>
</Paper>
)
}
}
const styles = {
root: {
fontSize: "1.0rem",
margin: "0px"
},
recipeName: {
fontSize: "1.0rem",
margin: "0px"
},
recipeIngredients: {
fontSize: "1.0rem",
margin: "0px" },
recipeInstructions: {
fontSize: "1.0rem",
margin: "0px" }
};
withStyles(styles)(Recipe);
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<App/>,
document.body.appendChild(document.createElement('div')),
)
});
Since you aren't setting withStyles(styles)(Recipe); into a variable, I suspect you must be using Recipe directly within App.
withStyles doesn't change Recipe. withStyles creates a new component that wraps Recipe and passes the classes prop to it. In order to see the classes prop, you need to use the newly-created component with something like the following:
const StyledRecipe = withStyles(styles)(Recipe);
const App = ()=> {
return <StyledRecipe/>;
}
Assuming App is defined in a separate file (for others who may come looking for this question), change the
`withStyles(styles)(Recipe);`
To
export default withStyles(styles)(Recipe);
As Ryan already explained ' withStyles is the higher order component that creates and returns a new component'
I am trying to use emotion.js with create-react-app and TypeScript.
Following the documentation I added #emotion/core, used import {jsx, css} from '#emotion/core'; and added /** #jsx jsx */ at the top of my file.
But when I am running the app, the following error gets thrown:
ReferenceError: jsx is not defined
This is my example component:
/** #jsx jsx */
import {jsx, css} from '#emotion/core';
import React from 'react';
import {NavigationBar} from "./NavigationBar";
export const Header: React.FunctionComponent<{}> = () => (
<div css={{
backgroundColor: 'hotpink',
'&:hover': {
color: 'lightgreen'
}
}}>
<NavigationBar/>
Test
</div>
);
The line, the error gets thrown at is the definition of the function: export const Header: React.FunctionComponent<{}> = () => (
Any help how to get this to work (other than ejecting create-react-app scripts) would be much appreciated.
UPDATE: Emotion should work right out of the box now according to this comment on github.
The solution is to put a jsx; statement somewhere in the file, like so:
/** #jsx jsx */
import {jsx, css} from '#emotion/core';
import React from 'react';
import {NavigationBar} from "./NavigationBar";
jsx;
export const Header: React.FunctionComponent<{}> = () => (
<div css={{
backgroundColor: 'hotpink',
'&:hover': {
color: 'lightgreen'
}
}}>
<NavigationBar/>
Test
</div>
);
To add to Johannes Klauß's answer, you don't need to reference jsx;
You just need the pragma statement (top-line and import):
/** #jsx jsx */
import { jsx } from "theme-ui"
Example:
/** #jsx jsx */
import {jsx, css} from '#emotion/core';
import React from 'react';
import {NavigationBar} from "./NavigationBar";
export const Header: React.FunctionComponent<{}> = () => (
<div css={{
backgroundColor: 'hotpink',
'&:hover': {
color: 'lightgreen'
}
}}>
<NavigationBar/>
Test
</div>
);
you may need to add:
/** #jsxRuntime classic */
as well if using theme-ui and next.JS