How to ignore style changes in styled-components during snapshot tests in jest - reactjs

Assume i have a simple component I wish to snapshot test:
const Component = styled.div`{
color: red;
}`
My snapshot test wishes to ensure that the div renders, but the styling is not important. So if I did a snapshot test on this Component, and i changed color from red to blue, and a new classname is generated, it would not fail the snapshot test.
In short, is there any way I can either ignore className in snapshot tests, or tell styled-components to generate static class names to be deterministic in tests?

I think what you are looking for is the jest-styled-components package which does exactly this.
For an example like yours, this would result in a snapshot like this:
exports[`it works 1`] = `
.c0 {
color: red;
}
<div
className="c0"
/>
`;
where c0 is always stable.

Related

Dynamically changing Less variables in React Gatsby app with Ant Design at runtime

We are building a White Label platform using React, GatsbyJs and Ant Design. We are stuck with Gatsby and Ant Design because we are migrating from an existing system and changing any of those would bring huge impact. Also, we must have a single deploy. Having a build for each White Label is not an option.
So, we need to be able to change style (mainly color) at runtime.
The problem is: Ant Design uses less variables to define it's themes and we're not able to change them at runtime, not even with less's modifyVars.
The thing is we MUST change less variables, and not global CSS or use other means
Ant Design derivates the main variables many times to get adjacent properties. So, for instance, if we define #primary-color as red, when we add a Button to the screen, Ant Design also defines it's border color, hover color, and many other details with different shades of red.
This means that, if we were to use other styling tool, we would need to generate those color derivations and replace every little property for every component. This would be chaos.
Scenario
We are using gatsby-plugin-antd and gatsby-plugin-less to load less and change vars at build time. Our gatsby-config.js looks like this:
module.exports = {
siteMetadata: {
siteUrl: 'https://www.yourdomain.tld',
title: 'yourtitle'
},
plugins: [
'gatsby-plugin-root-import',
'gatsby-plugin-typescript',
{
resolve: 'gatsby-plugin-antd',
options: {
style: true
}
},
{
resolve: 'gatsby-plugin-less',
options: {
lessOptions: {
javascriptEnabled: true,
modifyVars: {
'primary-color': '#FFFFFF',
'link-color': '#000000',
'success-color': '#FFFFFF',
'warning-color': '#000000'
}
}
}
}
]
};
We import styling in our gatsby-browser.js file:
import './src/styles/index';
Our styles/index has:
import 'tachyons';
import './global.css';
import './antd.less';
antd.less:
#import '~antd/dist/antd.less';
And global.css has some general CSS for the project.
It's working fine with the defined variables at build time.
What we attempted so far...
We have tried out this plugin:
https://github.com/mzohaibqc/antd-theme-webpack-plugin
Which supposedly does exactly what we need. But there's no example using Gatsby.
We then tried to add the plugin using the gatsby-node.js as mentioned here:
https://www.gatsbyjs.com/docs/how-to/custom-configuration/add-custom-webpack-config/
First, we tried using index.html as the indexFileName for the pluggin. It just doesn't work.
Then, following the plugin docs, we tried using indexFileName as false and importing the following scripts using Helmet at our pages/index.tsx:
<script> window.less = { async: false, env: 'production' };
</script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.2/less.min.js"></script> ```
Also didn't work. If we define indexFileName as false, we get memory over the heap.
If we keep indexFileName as 'index.html' and just add the scripts, we are able to call window.less.modifyVars and it returns successfully (we are logging the Promise's then and error) but it doesn't affect antd's variables.
We then tried doing something similar, but instead of loading less externally, we installed it as a node_module and imported it to the file and used it directly in a similar fashion. Got the same result: modifyVars runs and returns successfully but doesn't affect antd.
Then, we tried something a bit different: we removed gatsby plugins and tried importing less from antd directly, as suggested here:
https://ant.design/docs/react/customize-theme
So we imported it like this:
#import '~antd/lib/style/themes/default.less';
#import '~antd/dist/antd.less';
#import 'your-theme-file.less';
Also, no good. It's different from the previous scenario, because style gets updated after you save your code. No need to stop Gatsby, as the first solutions. But, modifyVars still has no affect on antd components.
Then, to isolate the issue, we tried to style a basic HTML component - a button - to check if the issue was with gatsby or antd. And... still no success. less.modifyVars didn't work to change a basic button style on runtime.
So, we think it's probably something between Gatsby and Less. We checked gatsby-plugin-antd and gatsby-plugin-less to see if we could find something, but found nothing useful.
We assume that the "less instance" or "less context" used by gatsby's less-loader during build time is not the same we are calling modifyVars on. So it doesn't affect the original vars.
Totally stuck. Please, help!
EDIT - SOLUTION
Ant Design team has just released - TODAY - a new alpha version that includes dynamic theming, using CSS Variables.
https://ant.design/docs/react/customize-theme-variable
It works fine, so far. Closing the issue.
EDIT 2
There's a more detailed solution on the accepted answer.
Ant Design team has just released - TODAY - a new alpha version that includes dynamic theming, using CSS Variables.
https://ant.design/docs/react/customize-theme-variable
It works fine, so far.
EDIT - Detailed solution
I removed gatsby-plugin-antd and gatsby-plugin-less from the project. Also removed the import of antd less file.
Instead, in my styles/index.tsx (which is imported in gatsby-browser.js), I'm importing the variables.min.css file:
import 'antd/dist/antd.variable.min.css';
Then, whenever I want to change Ant Design variables, I just use:
import { ConfigProvider } from 'antd';
...
ConfigProvider.config({
theme: {
primaryColor: '#[DESIRED_COLOR_HEX]'
}
});
Provider
Since this has to be done every time the site is loaded, I'm creating a ThemeProvider that wraps every page and defines the theme. It fetches theme data from the backend and sets Ant Design theme variables.
Example code:
import { Spin } from 'antd';
import React, { useEffect, useState } from 'react';
import { ConfigProvider } from 'antd';
import { Theme } from './theme.interface';
interface Props {
children: React.ReactNode;
}
export const ThemeProvider = ({ children }: Props): JSX.Element => {
const [themeVars, setThemeVars] = useState<Theme>(null);
useEffect(() => {
async function fetchMyAPI() {
const result = await getThemeFromBackend(); // Make API call with Axios
if (result) setThemeVars(result);
}
fetchMyAPI();
}, []);
useEffect(() => {
if (themeVars) {
ConfigProvider.config({
theme: {
primaryColor: `#${themeVars.primaryColor}`
}
});
}
}, [themeVars]);
return <div>{!themeVars ? <Spin size="large" /> : <>{children}</>}</div>;
};
And it can be used like this:
...
<ThemeProvider>
<h1>My page header</h1>
<p>Page content...</p>
</ThemeProvider>
...
Note: You can save theme data on local storage for performance improvement, so you don't have to call your backend everytime your site reloads. Maybe you'll just have to refresh it from time to time.

How to test the style inside the CSS class for React component using Jest and Enzyme?

Consider in CSS,
.content
{
width:500px;
box-sizing: border-box;
}
I have added this css class inside react component as following
const Banner = () => {
return (<div className="content">This is message content</div>);
}
How to write jest and enzyme test cases to ensure the 'box-sizing' with 'border-box' applied properly?
Notes: I can write the test case to ensure the ".content" class added to this element. But exactly, i need to write test case to ensure the value in 'box-sizing'.
I am not sure how to fix your issue, but keep in mind your test will not cover
* {
box-sizing: border-box;
}
if you will defined somewhere alse.
From my perspective, it does not make sense to test styles, because we can not guaranty from the test perspective that element will look like we want to. So still you will need to open the browser and check your element works properly, but you are writing tests to avoid it.
By adding this code to Banner.test.js file, it'll check if className called content have inline style property for box-sizing and is it equal to border-box or not. But it'll test only inline styles.
const banner = shallow(<Banner/>);
it("render propperely", () => {
expect(banner).toMatchSnapshot();
expect(banner.find(".content")
.get(0).props.style).toHaveProperty("box-sizing", "border-box");
});

how to import css file in react when generating static html and inject imported css into html head tag?

I am trying to generate static html from react using renderToStaticMarkup method. The problem I am facing right now is that I am not able to import css into react component. I want to import css in my React components like css-modules (import styles from './style.css'). And then inject that loaded css into generated static html head. How can I accomplish that?
P.S. I can't use webpack due to some constraints. If there is any babel plugin availabe for this specific case, then please let me know.
Here is how I am generating static html from react component:
const reactElement = require('react').createElement;
const ReactDomServer = require('react-dom/server');
const renderHTML = Component => {
return ReactDomServer.renderToString(reactElement(Component))
}
You can pass a URL in as a prop and render a <link/> tag. Made an example here, not sure if that would meet your needs or if you need it to be a style tag.
This may be challenging without a lot of custom logic.
If you want to inline the CSS only for the initial render and then fetch the rest after the initial render, styled-components may be a better option because it supports exactly what you're trying to achieve without too much configuration: https://www.styled-components.com/docs/advanced#server-side-rendering
May be I am too late you can also create It like this way.
React.createElement("style", {},[ "body {background-color: powderblue;}
h1 {color: blue;}
p {color: red;}" ])
Output:
<style>
body {background-color: powderblue;}
h1 {color: blue;}
p {color: red;}
</style>
Since createElement take 3 params and last one is children we can put our vanila css inside it as a children. You can put any imported file in the form of string and it will convert to style tag

How to get CSS Modules to work with Reactstrap cssModule propType?

I noticed most form elements in the Reactstrap documentation have a PropType of a cssModule. I would assume that means I could override the default Reactstrap styles and do something like this:
Formtext.module.css
.formtext {
background-color: blue;
border: 2px solid black;
margin: 10px;
}
SimpleForm.jsx
import styles from "./Formtext.module.css";
...
<FormText cssModule={styles.formtext}>
This is some placeholder help text...
</FormText>
```
However, this doesn't seem to work. Checking my react dev tools the cssModule prop evaluates to undefined.
I'm using Using Reactstrap 5.0 and create-react-app 1.1.5
Is there something I'm unaware of that I need to do?
Do I need to eject to be able to use css-modules?
Can someone point me to an example of how to use the Reactstrap's cssModule prop correctly?
For reference here is the proptypes definition from Reactstrap docs
FormText.propTypes = {
children: PropTypes.node,
inline: PropTypes.bool,
tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), // default: 'small'
color: PropTypes.string, // default: 'muted'
className: PropTypes.string,
cssModule: PropTypes.object,
};
On cssModule
It looks like cssModules doesn't behave quite like you would think; the prop doesn't take a single, overriding class - it takes a rejected class and a replacement class.
Reactstrap uses mapToCssModules to get this done. See its documentation here. Take note of the usage example:
<Example tag="div" cssModule={{ 'w-100': 'w-75' }} />
So your case would look something like this:
<FormText cssModule={{ 'form-text' : styles.formtext }} />
This will completely surpress the 'form-text' class though (which in your case, only contributes display: block. If you'd like to override more selectively, see below.
Try this instead
In the FormText source code, it looks like you may be able to do your overrides in a different way:
If you want to omit the form-text class altogether, include inline as a prop,
If you want to omit any color-related bootstrap classes, set 'color' to false (or something falsy),
set the className prop to your CSS Module object (styles.formtext).
<FormText className={styles.formText} color='' inline>
Test formtext
</FormText>
The most important part here is actually the className prop. You can also further override styling by including a tag prop (again, check the FormText docs).
Hope this was helpful! Happy holidays! 🦃🎅
I did not really get the accepted answer, but I had the same problem recently and in my opinion, cssModule behaves exactly as one would expect.
You just pass an imported module object and then specify classes they will be referenced towards the module.
Here is my example (from create-react-app) how I did fix Navbar to get it's bootstrap styles from my bootstrap module (as I don't import bootstrap globally):
import cx from 'classnames';
import bootstrap from 'bootstrap/dist/css/bootstrap.css';
import navbar from './navbar.css';
let styles = Object.assign({}, bootstrap, navbar);
public render() {
return (<Navbar cssModule={styles} className={cx(styles.navbarExpandSm, styles.navbarToggleableSm, styles.borderBottom, styles.boxShadow, styles.mb3)} light>[your menu here]</Navbar>);
}
This simply says the control to take the styles module and reference all the class names passed in classNames towards it. If you take a look at the mapToCssModules method, it is exactly what it does.
https://github.com/reactstrap/reactstrap/blob/d3cd4ea79dcaf478af5984f760ff1290406f62a5/src/utils.js#L53
In my case, it allows the control to pick up the original bootstrap styles and I can override what I need in my own module.

how to use common less variable with styled component?

Say I have a styled component, in index.jsx
import './index.less';
class Input extends React.Component {
...
}
and my index.less files looks:
.input{
color: #whiteColor;
}
This index.less has to work with the mixin.less that imported in the root project.
So my question is, even though I imported the mixin.less, it prompts variable #whiteColor not found. Any idea to solve this?
I have felt the same pain, why isn't my styled component resolving less variables?
The syntax is simple JavaScript, just do:
.input{
color: ${props => props.whiteColor};
// or
color: ${props => props.theme.whiteColor};
}
But, at my company, we had thousands of less components, and we really thought that the less syntax was cleaner and definitely faster to write. We developed Styless.
It is a babel plugin that parses less and generates javascript code. Add it to your .babelrc file.
{
"plugins": ["babel-plugin-styless"]
}
Then, we can do!!
const Input = styled.input`
#highlight: blue; // can be overwritten by theme or props
background: darken(#highlight, 5%); // make green darken by 5%
`;
Check here to see how to use the theme provider and load variable from your index.less!
You can try import the mixin.less in index.less
I have been trying the same than you.
But then I thought.. it is that what I really want? Because styled-components propose a different approach to having a modular structure for your styles.
https://www.styled-components.com/docs/advanced Check theming, is amazing powerful.
Because in styled components you define the variables with javascript.
And if you want color manipulation like less, sass, you can check https://github.com/erikras/styled-components-theme
Its like forgetting about less, and sass and moving it to a new style modules.
Still, if you want to keep your defined style classes, you can do that:
class MyComponent extends React.Component {
render() {
// Attach the passed-in className to the DOM node
return <div className={`some-global-class ${this.props.className}`} />;
}
}
Check the existing CSS usage from docs:
https://www.styled-components.com/docs/advanced#existing-css

Resources