Check If Prop Exists in Styled Components - reactjs

I am just getting started with styled components and want to create variations for things like buttons, navbars, etc.
For instance, I'd like to create a dark version of a navbar (wherein the background color would become dark and the text color light).
What I'd like to do is simply add a prop of dark on a component as follows:
<Navbar dark>...</Navbar>
I'd like to do this as opposed to something like this:
<Navbar type="dark">...</Navbar>
However, I'm not sure how to do this. That is, how do I style an element just by checking if the prop name exists (without passing the prop any values)?
Any ideas? Thanks in advance.

styled-components supports passing props to styled components, and those props can be accessed in the CSS tagged template literal (the CSS inside of the backticks) of the styled component receiving those props.
For example, say that the <Navbar /> in your example is a styled <nav> element, then we can define <Navbar /> in a way that takes into account a dark prop:
const Navbar = styled.nav`
background: ${props => props.dark ? 'black' : 'white'}
`
In the example above, we check for the existance of the dark prop. If it was passed, then we give the component a black background color. Otherwise (the default), we give the component a white background color:
<Navbar dark /> // produces component with black background
<Navbar /> // produces the default white background

Related

How to style Material UI Dialog with Tailwind?

Material UI Dialog is a portaled component. The way I understood it and saw it in action is that it renders its markup outside the React's root element.
It renders itself before the </body> tag.
Now I have encountered a problem because of this.
When user chooses the dark mode, I set a dark class on top-level element, one beneath the root element.
And on all components I can use dark variant to apply styles, like dark:bg-zinc-700.
But when I apply it the <Dialog /> component, it won't affect its style, though I can see that the class exists in the output.
<Dialog
PaperProps={{
className="dark:bg-zinc-700"
}}
How should I solve this problem? I know I can use sx to apply style. But that means I need to lose consistency and I also don't know how to translate Tailwind to sx. Thus I prefer to keep using Tailwind.
I solve this problem with this Link
MuiDialog: {
defaultProps: {
container: rootElement,
},
},
Check this
#HosseinFallah Looking back at your post I think this won't work because tailwind and material ui handle dark modes differently. However, you can target Material UI css without using sx. You can use the Dialog API classes in your css and apply tailwind colors on them like this in your global css:
.MuiDialog-paperScrollBody {
background-color: theme(colors.dark) !important;
}
Where dark is the custom dark color you've set in your tailwind.config.js
To add to the answer you can also set this for your whole app by wrapping it with the Mui's StyledEngineProvider. This way tailwind will be prioritized when injecting styling.
In your index.tsx
import { StyledEngineProvider } from '#mui/material';
const container: any = document.getElementById('root');
const root = createRoot(container);
root.render(
<Provider store={store}>
<React.StrictMode>
<StyledEngineProvider injectFirst>
<App />
</StyledEngineProvider>
</React.StrictMode>
</Provider>
);
Then this will be possible without needing to specify !important
<Dialog
PaperProps={{
className="dark:bg-zinc-700"
}}
Did you set the "important" property in the Tailwind config (tailwind.config.js) by chance?
If you set it to something like "#root" then it will only match the elements inside the #root element which is bypassed when MUI uses React Portals.
You could change it to something like "#tw" and then set your body's ID to "tw" so it will always match since Portals are always children of the body element.
if you completely interoperate to tailwindcss, see: https://mui.com/material-ui/guides/interoperability/#tailwind-css
i think this mistake occure in tailwindcss.config.js.
by adding another id to wrap dialog container and asign it to "important" property will solve this problem
You can increase the specificity of the TailwindCSS by using !important selector.
You can find more here https://tailwindcss.com/docs/functions-and-directives

Passing empty props changes the Button color in ButtonGroup

I'm using a customized IconButton element with ButtonGroup.
While passing an empty props, the color and hover shape of element is also changed. I want to know why did this happen? For me, an HOC shouldn't have different render effect with an empty prop passed.
I hosted a minimal environment with GitHub Pages. The question is shown on the second and third chapter.
Using a new customized component.
Misbehavior: buttons are white and not in group.
See the hover highlight of second and third "G"
The component is based on IconButton without passing {...props}.
Using a new customized component. All good.
The component is also based on IconButton but passing {...props}.
Although the props is not passed, hence should be empty(?).
Maybe useStyles changed color, but the hover background shape is changed too.
Also I made a related issue on official repo, and in my Learning note
It's the opposite, if you don't pass the props to IconButton:
function IconButtonWrapper(props /* unused props */) {
return (
<IconButton>
<DeleteIcon />
</IconButton>
);
}
Then no styles are applied and the IconButton appears in plain white, so nothing changes.
Although the props is not passed
The props are passed automatically from the ButtonGroup, even if you don't pass anything. The way it works is that ButtonGroup clones the child component and pass its own set of props to them, so when you don't pass anything yourself like this:
<ButtonGroup variant="contained">
<IconButtonWrapper />
<IconButtonWrapper />
<IconButtonWrapper />
</ButtonGroup>
The child component still has props provided by the parent, you can see it by logging the props:
function IconButtonWrapper(props) {
console.log(props);
return (
<IconButton {...props}>
<DeleteIcon />
</IconButton>
);
}

How can I set the Overlay container property when using OverlayTrigger?

I'm using the OverlayTrigger component to get hover behavior for tooltips, and therefore not using the Overlay component directly. The Overlay component has a container property that I'd like to use to remedy the tooltip getting cut off by its natural container element.
I've attempted to pass a popperConfig object, but that's not working. I've also tried adding a container attribute to the OverlayTrigger component.
<Container fluid className='flex-fill' ref={rowContainer}>
...
<OverlayTrigger delay={{show: 500}} overlay={renderUserTooltip}
popperConfig={{container: rowContainer}}>
<FaUser/>
</OverlayTrigger>
How can I set the container for the Overlay when the Overlay component isn't directly used?
React bootstrap doesn't have a container prop or something similar (I mean it has a target prop but as this part of the docs suggests, for the OverlayTrigger the type is null, so it doesn't accept values and I don't think you can trick it to accept (and I don't think it would be wise to try).
But a pretty nice example, that shows some sort of a workaround in my opinion, can also be find in the docs, under customizing-trigger-behavior.
Starting from that example, if you need your trigger to be totally separated from the container an option is to just wrap everything in a big container that receives ({ ref, ...triggerHandler }) and all is left is to give your container the ref, and the trigger to your FaUser component. So something like:
<OverlayTrigger
placement="bottom"
overlay={<Tooltip id="button-tooltip-2">Check out this avatar</Tooltip>}
>
{({ containerRef, ...triggerHandler }) => (
<Container fluid className='flex-fill' ref={containerRef}>
...
<FaUser/>
</Container>
)}
</OverlayTrigger>
I also created a sandbox with a minimal reproducible example.

'Box' component vs css prop for layout, what to choose?

I'm using styled-system and Emotion now and I can't come to an understanding of which approach is more correct to use. I have some "Box" component, which handles different props like mx, my, px, py, bg, color, and many others from https://styled-system.com/api/.
Sometimes I have the following situations :
I have some "Select" component, which doesn't have styled-system props. And I have to add some margins (for example) to this Select. I can do this in following ways :
import {Select} from 'Select'
<Box
as={Select}
my={32}
ml={10}
/>
Or I can use Emotion's css prop with #styled-system/css and do next :
/** #jsxImportSource #emotion/react */
import {Select} from 'Select'
import css from '#styled-system/css'
<Select
css={css({
my: 32,
ml: 10
)}
/>
For me personally, the css option seems more readable, but I already use the "Box" component in many places of the project and I do not know whether to change their syntax or not.before :
<Box mx={1}/>
after :
div css={css({mx=1})}
Which option in which situation would be more correct?
This is pretty subjective. Your box is not the same as a div. Presumably, you have a border-box rule for the Box component. Let's not get the whole reason for using the box confused. If you just want your select to have all the props of a box, well, it should already have that, assuming your Select definition looks like this
const Select = ({ onClick, ...styleProps }) => <Box {...styleProps}><p>select me</p></Box>
export default Select

Material UI inline style not working

In Material UI, I want to set borderRadius on my buttons. Passing the style attribute seem to work for FlatButton but not for RaisedButton.
For RaisedButton, the borderRadius is applied to the parent <div> (which is necessary) but not to <button> itself (which is also necessary)
Is this a bug in Material UI? Or is this behaviour intended? If it's intended, then how do I make a RaisedButton with rounded corners?
import React from 'react';
import RaisedButton from 'material-ui/lib/raised-button';
import FlatButton from 'material-ui/lib/flat-button';
export default class MyButtons extends React.Component {
render() {
return (
<div>
<FlatButton label="flat button" style={{borderRadius: '25px'}}/> {/*works*/}
<RaisedButton label="raised button" style={{borderRadius: '25px'}} /> {/*does not work*/}
</div>
);
};
}
This is the intended behaviour, and says so in the docs. For the record, you would never want a style prop to be passed to multiple children as no styles would make sense across all children - and how deep in nesting would you apply them?
But I think you're mixing concerns here. Using style on a component should only ever effect the root element - and that's assuming the developer chose to pass along the style tag, which they did.
But what you're looking to do is not style the component, but style the elements of the component. What you want to do is use a CSS class:
<RaisedButton label="raised button" className="raised-button--rounded" />
.raised-button--rounded,
.raised-button--rounded button {
border-radius: 25px; /* assuming one is not already defined */
}
NB: The developers do not intend for you to change the component styles that they have not specifically exposed. Through this approach, you will run into issues eventually.

Resources