How to add multiple style attributes to a react element? [duplicate] - css

This question already has answers here:
How to combine multiple inline style objects?
(20 answers)
Closed 6 years ago.
How would I go about adding multiple style attributes to my React element?
Some of the components in my app are using the same styles throughout with minor variations in styles. I am trying to accomplish something along the lines of <div style={this.styles.mainStyle, this.styles.variationInStyle}></div>.
These styles are in a file called styles.js hence the this.styles.x. It works with only one style in it. The closest I found to this solution was in a Reddit Post. The solution was <div style={$.extend({}, style1, style2)}></div> but of course, it doesn't work nor does the variation <div style={style1, style2)}></div>.
Any insight would be greatly appreciated! I will also be posting in Reddit and the Reactiflux Discord group if the answer should come from either source, I will post the answer here.

style is just an Object, with css value turn to camelCase, so you could use any way to merge two object, and it should work.
ES6: style={Object.assign({}, style1, style2)}
ES7: style={{...style1, ...style2}}
lodash: style={_.merge({}, style1, style2)}

as #michealmuxica said, the style prop is is just a JS object with camel casing for the keys. So you can set your style on your components as such:
<MyComponent style={{height:"100%", marginLeft:"70%"}} />
I prefer to create another JS file per component to contain the style objects, then import them into the component's file. I feel like this keeps the code more clean and modular:
//in MyComponentStyles.js
var style = {
base:{
height:"100%",
width: "100%",
marginLeft: "auto",
marginRight: "auto"
},
//...other styles...
};
export default styles;
//in MyComponent.js
import {default as MyComponentStyles} from "./<path to styles>/MyComponentStyles.js;
var App = React.createClass({
render: function() {
return ( <MyComponent style={MyComponentStyles.base} /> );
}
});

Related

How to use components in different ways (React)

I want to use simple components in different way and different ui rendering
For example a dropdown rendering a list may have several ui according to the page or context (=> padding, margins, font size and other css properties might change)
should I:
implement it by overwriting in the parent component (target css properties of the child component and apply them my css needs - at cost that if change happens in the child component like change in classname or what might break the parent design)
Pass flags to the component to handle those design and at cost that each component handle the design of each parent
There are different approaches to this and everybody has his own preferences.
I usually solve this by supporting the className property. The class is accepted as a prop and applied to the root. So it is easy to change things like outer margins or the background-color. I usually discourage modifications of deeply nested elements.
Example:
import classnames from 'clsx';
import style from './button.module.scss';
export const Button = ({ content, onClick, className }) => {
return (
<div
className={classnames(style.buttonRoot, className)}
onClick={onClick}>
{content}
</div>
);
};
and if I want to modify it anywhere I can do it thus:
import { Button } from './Button';
import style from './productView.module.scss';
// ...
<Button content={'Show products'} className={style.showProdButton} onClick={showProd} />
and
.show-prod-button {
background-color: #562873;
margin-left: 32px;
}

How to dynamically add classes to NextJS component using class names from CMS?

I am looking for a solution that will allow some styling options in a CMS that can dynamically change the classes of specific components in my NextJS app. My code looks like this:
pages/index.js:
...
import client from "../lib/client";
const Home = ({ headerConfig }) => {
return (
<>
<Header headerConfig={headerConfig} />
...
</>
);
};
export const getServerSideProps = async () => {
const headerConfig = await client.getDocument("headerConfig");
return {
props: { headerConfig },
};
};
export default Home;
components/Header.jsx:
const Header = ({ headerConfig }) => {
return (
<nav className={`relative ... ${headerConfig.bgColour}`}>
...
</nav>
);
}
export default Header
However, the styling does not apply and the background colour remains unchanged although the class does seem to be injected into the class attribute on the browser.
I know my current method is incorrect but I am clueless as to how to fix this. Could someone help point me in the right direction?
I assume that you are using tailwind. If so, you cannot inject classnames into an html element. This is because tailwind only includes classes that are explicitly declared somewhere within your code (it will find classes within any string in your project depending on the configuration). You can get around this problem by adding classes to the safelist array in your tailwind.config.js file. You can also safelist classes with regex to allow all variants of certain utilities.
However, safelisting only works if there are a specific set of classes that could potentially be injected. One option, which will be guaranteed to work but NOT RECOMMENDED, is to add a <link> in your html to the tailwind cdn. However this will include every single tailwind class in your css bundle, making it MUCH larger and your website slower.
Another solution is to use inline styles which are calculated with javascript depending on the classes you need to inject. If you are dealing with only simple parts of tailwind (like padding, margin, or other sizing units), this may be a good approach. For example a class like p-4 would get converted to padding: 1rem in your inline styles.
Depending on the needs of your application, one of these three approaches is probably the way to go. Hope this helps!

How would I apply Material-UI managed styles to non-material-ui, non-react elements?

I have an application where I'm using Material UI and its theme provider (using JSS).
I'm now incorporating fullcalendar-react, which isn't really a fully fledged React library - it's just a thin React component wrapper around the original fullcalendar code.
That is to say, that I don't have access to things like render props to control how it styles its elements.
It does however, give you access to the DOM elements directly, via a callback that is called when it renders them (eg. the eventRender method).
Here's a basic demo sandbox.
Now what I'm wanting to do is make Full Calendar components (eg, the buttons) share the same look and feel as the rest of my application.
One way to do this, is that I could manually override all of the styles by looking at the class names it's using and implementing the style accordingly.
Or - I could implement a Bootstrap theme - as suggested in their documentation.
But the problem with either of these solutions, is that that:
It would be a lot of work
I would have synchronisation problems, if I made changes to my MUI theme and forgot to update the calendar theme they would look different.
What I would like to do is either:
Magically convert the MUI theme to a Bootstrap theme.
Or create a mapping between MUI class names and the calendar class names, something like:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary
I wouldn't mind having to massage the selectors etc to make it work (ie. For example - MUI Buttons have two internal spans, whereas Full Calendar have just one). It's mostly about when I change the theme - don't want to have to change it in two places.
Using something like Sass with its #extend syntax would is what I have in mind. I could create the full-calendar CSS with Sass easily enough - but how would Sass get access to the MuiTheme?
Perhaps I could take the opposite approach - tell MUI 'Hey these class names here should be styled like these MUI classes'.
Any concrete suggestions on how I would solve this?
Here is my suggestion (obviously, it's not straight forward). Take the styles from the MUI theme and generate style tag based on it using react-helmet. To do it event nicely, I created a "wrapper" component that do the map. I implemented only the primary rule but it can be extended to all the others.
This way, any change you will do in the theme will affect the mapped selectors too.
import React from "react";
import { Helmet } from "react-helmet";
export function MuiAdapter({ theme }) {
if (!theme.palette) {
return <></>;
}
return (
<Helmet>
<style type="text/css">{`
.fc-button-primary {
background: ${theme.palette.primary.main}
}
/* more styles go here */
`}</style>
</Helmet>
);
}
And the use of the adapter
<MuiAdapter theme={theme} />
Working demo: https://codesandbox.io/s/reverent-mccarthy-3o856
You could create a mapping between MUI class names and the calendar class names by going through ref's. It's possible that this is not what some would call "best practice"...but it's a solution :). Note that I updated your component from a functional component to a class component, but you could accomplish this with hooks in a functional component.
Add refs
Add a ref to the MUI element you want to set as a reference, in your case the Button.
<Button
color="primary"
variant="contained"
ref={x => {
this.primaryBtn = x;
}}
>
And a ref to a wrapping div around the component you want to map to. You can't add it directly to the component since that wouldn't give us access to children.
<div
ref={x => {
this.fullCal = x;
}}
>
<FullCalendar
...
/>
</div>
Map classes
From componentDidMount() add whatever logic you need to target the correct DOM node (for your case, I added logic for type and matchingClass). Then run that logic on all FullCalendar DOM nodes and replace the classList on any that match.
componentDidMount() {
this.updatePrimaryBtns();
}
updatePrimaryBtns = () => {
const children = Array.from(this.fullCal.children);
// Options
const type = "BUTTON";
const matchingClass = "fc-button-primary";
this.mapClassToElem(children, type, matchingClass);
};
mapClassToElem = (arr, type, matchingClass) => {
arr.forEach(elem => {
const { tagName, classList } = elem;
// Check for match
if (tagName === type && Array.from(classList).includes(matchingClass)) {
elem.classList = this.primaryBtn.classList.value;
}
// Run on any children
const next = elem.children;
if (next.length > 0) {
this.mapClassToElem(Array.from(next), type, matchingClass);
}
});
};
This is maybe a little heavy handed, but it meets your future proof requirement for when you updated update Material UI. It would also allow you to alter the classList as you pass it to an element, which has obvious benefits.
Caveats
If the 'mapped-to' component (FullCalendar) updated classes on the elements you target (like if it added .is-selected to a current button) or adds new buttons after mounting then you'd have to figure out a way to track the relevant changes and rerun the logic.
I should also mention that (obviously) altering classes might have unintended consequences like a breaking UI and you'll have to figure out how to fix them.
Here's the working sandbox: https://codesandbox.io/s/determined-frog-3loyf

Add className attribute to dangerouslySetInnerHTML contents

How to specify className attribute for div which contains 'hello world':
<div dangerouslySetInnerHTML={{__html: '<div>hello world</div>'}} />
One way is to set is to outer div like so:
<div dangerouslySetInnerHTML={{__html: '<div>hello world</div>'}} className='class-name'/>
and then in css style the child:
.class-name div {
(css stuff here)
}
But I want to set className directly to the div with 'hello world'
EDIT: Adding class name to injected html does not solve the problem, for example:
let content = '<div class="class-name">hello world</div>'
<div dangerouslySetInnerHTML={{__html: content}}
does not work, because in case same content is used many times then CSS collisions occur (I am using style-loader with CSS modules on)
I came across this question after 2 years and I wanted to achieve the exact same results. I didn't find any accurate answers here but I came up with a solution thanks to #jash8506 due to the brilliant answer to this question.
We have to utilize two react hooks
useRef
useLayoutEffect
According to the documentation basically, useRef can be used to access the DOM elements in React and useLayoutEffect can be used to read layout from the DOM and synchronously re-render.
We can access the firstChildElement in the container div and add classes to it's classList
Here is how the completed code looks like
const containerRef = useRef();
useLayoutEffect(()=>{
if (containerRef.current){
containerRef.current.firstElementChild.classList.add('class-name');
}
});
return (
<div ref={elRef} dangerouslySetInnerHTML={{__html: '<div>hello world</div>'}} />
)
<div className="post-excerpt" dangerouslySetInnerHTML={{ __html: post.excerpt}}/>

Unable to override Material-UI theme styles

I'm using Material-UI in a project and I am trying to override the default theme style of textTransform:"uppercase", and instead, replace it with textTransform:"capitalize".
Consulting the docs on custom styling informed me that I should use inline styles or a custom class.
Adding className="capitalize" (which has as a text-transform property in the class) or adding style={{textTransform: "capitalize"}} produces the same result. The parent div is passed the CSS property, but is ultimately overridden by a child span.
Is this intended behavior, or am I doing something wrong?
You can use a custom theme to override the textTransform:
const App = () => {
const customTheme = { button: { textTransform: 'capitalize' } };
return (
<MuiThemeProvider muiTheme={getMuiTheme(customTheme) }>
<Example />
</MuiThemeProvider>
)
};
Working jsFiddle: https://jsfiddle.net/88uq8751/7/
Please give more info in you question.
However,I think this is not intended behaviour. I guess, check your other props, maybe with those props the effect of style props is getting overrided.
Still if that is not the cause, check the material-ui repo codebase on GitHub. From my experience working with material-ui, many problems I solved by going through their codebase and not using their doc. Hope the info helps.

Resources