How to test conditional styling made with Material UI makeStyles()? - javascript

I've been working on a React app for a few weeks and I use Material UI components for most of by base.
I have this component that has its style change depending on the values of its props. To do this, I did something like:
const useStyles = makeStyles({
component: {
...
backgroundColor: getComponentBackgroundColor(),
...
}
});
with getComponentBackgroundColor() being defined as
const getComponentBackgroundColor = () => {
if (props.someProp) {
return "green";
}
return "red";
};
and then by setting the component's className.
My problem is that I want to test this component to ensure that the styling is correctly applied (some getStyle() methods are more complex than juste looking if a prop exists or not).
I'm using the react-testing-library and my first instinct was to check if the rendered component had the right className, but upon further inspection, I realized that makeStyle() assigns some random className like makeStyles-component-12 to each component. I also noticed that components with the same styling had different classNames. So that was a no-go.
Is there an easy way to test conditional styling when using Material UI's makeStyles() ?
Thanks a bunch.

Although it's not recommended to couple your tests with specific class or css style, you could use jest-dom's .toHaveStyle matcher to test if the right style is applied when passing the corresponding props.

Related

How call a method from an other component

I have made an example in Codesandbox
Is there a way to collapse the sidebar by clicking on the button 'Col-2'. The button 'Col-1' is working fine, but it's necessary that is works by clicking on 'Col-2'.
I've tried to call the collapsedButton but this didn't work.
<script>
export default {
name: 'template',
methods: {
setCollapsed () {
this.collapsed = !this.collapsed
},
collapseButton () {
this.$emit('setCollapsed')
this.collapsed = !this.collapsed
}
}
}
</script>
Can someone help me out with this?
You should make collapsed a prop and control it from the parent component. You'll need to listen for the event (this.$emit('setCollapsed')) in the parent component and update collapsed accordingly.
I hope this helps, good luck!
The recommended way to achieve this is to use a store.
The store is a module, external to any component, which can be imported in any number of components. When the state of the store (which is pretty much like the data function of a component) is changed, the change is propagated in every single component where the store is used.
Since you're using Vue 2, you could use a mixin.
Here's a mixin using a basic version of a store, under the hood: https://codesandbox.io/s/vue-forked-tz1yox?file=/src/components/sidebar.vue
In more detail, it uses a reactive object which serves as shared state for sidebar's collapsed status while exposing a writable computed (collapsed) and a toggleSidebar method.
Using the mixin in any component is the equivalent of writing the writable computed and the method into the component.
Note: mixins don't play nice with TypeScript. But the above solution doesn't need a mixin. I only used one so I didn't have to write the same computed and same method in both components.
The key to it working is the shared/external reactive object. In Vue 3 you would achieve this using Composition API.

render with different child components from props

I am currently working in a scenario, where I need to be able to import a component from library, but tell it to choose different components for some of its child components to render with. In this case, it needs to choose different button components, for example. Now I already got this working, as in, it does what it needs to do, but I am wondering if there is maybe a more fitting/appropriate way of doing it.
export const container = ({component, children}) => {
const ButtonComponent = component?.button ?? Button;
return (
<div>
<ButtonComponent size="large">Do something</ButtonComponent>
</div>
)
}
In this case, Buttons are defined in this same library, but on the side of the application where the library is consumed, the buttons are modified, variants are added, some properties are added that are not part of the original component of the library. And I am telling the component to use a different component like this:
<container component={FancyButton} />
As I said this works, but it feels like there might maybe be a more elegant solution to this. This is relevant, because the library uses an atomic design methodology approach, where some of the more complex components use less complex components, but they are being modified for a specific usecase. So all the buttons are being modified to have additional variants, etc. But if I then go a head and for example use the modal, it uses the regular buttons, not the modified buttons. This is the solution that I came up with, that allows me to tell the component to use these modified buttons instead.
Does this make sense? Is this an anti-pattern? Is there a more efficient/elegant solution to this?
€1: Here's a codesandbox demonstrating what this does: https://codesandbox.io/s/practical-ives-jxk3v
const defaultComponents = {
button: BaseButton
}
export const Card = ({ components=defaultComponents, children }) => {
return (
<div className="card">
<h2>Thing</h2>
{children}
<components.button className="card__button">Click me</components.button>
</div>
);
};
I don't know is this help. I will do like this.
use = when you passing props in a function mean the default value of that prop.

Is it good practice to set CSS variable in the React's render method?

I have created a post yestarday and deleted it by error
I have to create a component with specific style. The problem is that the componentDidMount() method creates some king of flickering when rendering the component. It is the time that the CSS became set.
I think my component is ressource's demanding so the componentDidMount() occurs with a little frame or two of lagging. That why I am providing here just a snippet instead of a full demo, my little demo works well with zero specific flickering.
I have then entering the value in the render's method. Like the following:
componentDidMount(){
// the CSS rendering will lag by releasing a flickering :/
}
render(){
// awesome, by setting my style in the render the good style is returned instantly.
if (Math.round(window.scrollY + window.innerHeight +50) >= Math.round(document.body.scrollHeight)) {
document.documentElement.style.setProperty('--background-transition', "")
document.documentElement.style.setProperty('--is-page-bottom', "orange");
document.documentElement.style.setProperty('--background-transition', "all 0.3s ease-out")
}
else{
//setIsPageBottom_CSSVariable("rgba(84, 80, 79,0.85)")
document.documentElement.style.setProperty('--background-transition', "")
document.documentElement.style.setProperty('--is-page-bottom', "purple");
document.documentElement.style.setProperty('--background-transition', "all 0.3s ease-out")
}
Someone yesterday told me that I should use react React Hook to manage this case, or componentDidMount but componentDidMount fails in my case because of the flickering before installing the relevant background color.
I am new to React Hook so if I understand well I can make something like document.documentElement.style.setProperty in useEffect directly and it would be coherent with the design of React hook?
Meanwhile I find my solution is just good because when reading the official React's documentation, it is noted that:
The render() function should be pure, meaning that it does not modify
component state, it returns the same result each time it’s invoked,
and it does not directly interact with the browser.
If you need to interact with the browser, perform your work in
componentDidMount() or the other lifecycle methods instead. Keeping
render() pure makes components easier to think about.
Okay, if I can think about my component with a limpid manner so it is in no way a bad thing to do it isn't it? Just not the best practice I assume.
I have been advised to use an addeventlistener but here it is about instantly rendering my style on the first render component's loop. So addeventlistener is a solution for a different case that the one I am on.
In all case, which alternative would you recommend that avoid flickering when inserting my CSS setting in componentDidMount()? What is the link with the React hook?
This is how you can use useEffect
//componentDidMount replacement
useEffect(() => {
//your code goes here
}, [])
You might also consider useLayoutEffect as it fires synchronously with the DOM changes. If normal useEffect retains the issue, then you should be able to use this instead.
No, it's not good practice - you would be better off changing the class name dynamically using react, dependent on the internal state of the component.
So you could use the useState hook, and have your component in a 'loading state' which would then apply a class.
Then you could add / remove class names and associated styles using react as well, rather than inserting them with JS.
Have a look at the classNames package: https://www.npmjs.com/package/classnames
Your css would then be: .additional-class {
background: red;
}
or whatever you wanted.
Applying styles directly from React is somewhat missing the point of using react, to be honest. If you do want to do that, there are ways but should be limited, really: How to modify the DOM directly in ReactJS
https://reactjs.org/docs/refs-and-the-dom.html

Use Higher Order Component in React to format child components

I am building a wrapper component that renders children components in a grid, either vertically or horizontally by taking the layout config as a prop:
class App extends React {
render() {
return (
<WrapperComponent layout="horizontal">
<ChildComponent1>
<ChildComponent2/>
<ChildComponent3/>
</WrapperComponent/>
}
}
I want to create a HOC that returns either a <VerticalLayout/> or a <HorizontalLayout/> component depending on the configuration passed to <WrapperComponent/>.
So the code pattern should be:
const LayoutComponent = HOC(InputComponent).
I cannot pass <WrapperComponent/> as an input to HOC, as it needs to wrap the <ChildComponents/>, and it is the entry point for this layout:
How can I achieve the desired result with HOC implementation? I know that this can be achieved without HOC, but I specifically want to write this program by implementing HOC, as some of the tasks/code of <VerticalLayout/> and <HorizontalLayout/> will be the same and also because I want to practice writing HOCs.
Don't reinvent the wheel! it's already there and ready to be used, have a look at : https://github.com/acdlite/recompose/blob/master/docs/API.md#branch
Basically you pass your configuration as a condition, something around these lines should do the trick:
branch(
test: ( { config } => (config.isVertical),
left: <VerticalLayout/>,
right: <HorizontalLayout/>
)

Best way to do database-driven styles in react

This is a little bit of a subjective question but I hope SO can help.
I'm making a ReactJS app and I need to customize certain parts of the style (the color scheme mostly) based on a user's settings in a database. The color palette is spread out over a lot of the style sheet so I'd like to avoid setting all those colors by hand in the render() method.
One option is to server-side render the full css file on every request. Would something like SASS be able to do this in real time? I know most preprocessors are designed for compile-time use.
Another option would be to embed the styling in a JavaScript module with something like react-style. Each components "stylesheet" could actually become a function that takes some preferences and returns styles. This strategy has some downsides (as described here) plus the extra inconvenience of having to pas a "styleSettings" prop down to EVERY component so it could know how to request its styles.
Alternatively I could just have a single global or page-wide style sheet so I have to deal with fewer props, but I like the modularity of each component getting its own style.
Are there any other options I'm missing here? Is there a best practice for doing this type of thing?
I'm not sure if this is the best way to do this, but I accomplished something similar using a theme-provider HoC.
class ThemeProvider extends React.Component {
componentDidMount() {
// fetch required data if you don't already have it
// create a <style> node
this.styleTag = document.createElement('style')
this.styleTag.type = 'text/css'
document.head.appendChild(this.styleTag)
}
/* update the styles if the data source has changed */
componentDidUpdate ({ styles: prevStyles }, prevState) {
const { styles: currentStyles } = this.props
if (!isEqual(currentStyles, prevStyles)) {
// generate the css ex
const css = `
.some_selector {
color: ${currentStyles.color}
}
`
// update the <style> node
this.styleTag.textContent = css
}
}
render() {
const { children } = this.props
return children
}
}
you would use it like
<ThemeProvider>
<RootComponent/>
</ThemeProvider>
pros
globally apply CSS from one location
fairly cheap in terms of reconciliation
cons
a little bit of "magic" involved (i.e. where are these styles coming from?)
component is mounted before styles are applied - i.e. after render... meaning the first load might have styles "flash" in
slightly destructive - .some_selector { color: ... } would override hover, etc effects... nothing you can't easily fix, though.
It turns out styled-components does most of what I wanted to do pretty easily. As a bonus, it has built in support for global themes, so I don't have to think about theme setting in my component props.
It sounds like you either intend to or already have made use of inline styles within your React components. I myself use inline styles from time to time even if this is frowned upon by the vox populi of the React community. Sometimes inline styles are actually the most elegant solution to a React problem, however, in this situation I propose that you stick with using class names so you can leave the application of color to your stylesheet. You would still want to use a CSS preprocessor like LESS or SASS to apply the different color themes based on the user's settings, which is what I am currently doing on my project. So you would apply classes such as: color-primary, color-secondary, color-accent to your DOM content and those classes would have their colors set via the user data.

Categories

Resources