React styled component specificity issue for modal calling custom styled modal - reactjs

I am working with styled component in React. I want to style a modal with the following style inside my component,
const StyledModal = styled(Modal)`
.modal-container-header {
display: none;
}
.modal-container-footer {
display: none ;
}
.modal-container-close {
display: none;
}
`;
That is, I want to omit the global footer, header for my custom modal. It is working fine for one case but not working for another.
Here is the entire component,
interface LoaderInterface {
loading: boolean;
loadingText?: string;
}
const StyledModal = styled(Modal)`
.modal-container-header {
display: none;
}
.modal-container-footer {
display: none ;
}
.modal-container-close {
display: none;
}
`;
const Loader: FC<LoaderInterface> = ({ loading, loadingText }) => {
console.log("this modal is working");
return (
<StyledModal onClose={() => loading === false} open={loading}>
<div>
<Loading size="large" />
<Heading type="h4">{loadingText}</Heading>
</div>
</StyledModal>
);
};
export default Loader;
It is working when a general React functional component is calling the custom styled modal
but it is not working when a modal calling this custom modal. When the modal is triggering the custom modal then the style of the calling modal is getting effected. But I closed the calling modal before calling this custom modal.
In DEV tool I'm always seeing, for example, for footer,
.hPyTDi modal-container-footer {
border-top: 1px solid rgba(0,0,0,0.06);
padding: 16px 24px;
text-align: right;
}
That is the global style. Not my custom style. when I apply display: none inside the above code in the dev tool obviously it is working.
But this issue doesn't persist when my custom modal is called by a React functional component. Only this issue appears when a modal (which is also a React component) calls this custom modal.
I also tried '&' to increase specificity but it doesn't work.
I did,
&.modal-container-footer {
display: none;
}
It doesn't work. If I move the custom modal style in a global place it works. But I don't want that as then this is applied for all modal where I want the header/footer to be visible.
Really struggling with this. Any help is much appreciated.

I've run into so many issues with the way Styled Components selectors, and come to realise that it's always 'safer' to pass props into them. This is because of the way SC dynamically assigns class names. Might this help?
const StyledModal = styled(Modal)`
${ ({ hidden }) => hidden && `
.modal-container-header, .modal-container-footer, .modal-container-close {
display: none;
}
`}
`;
const Example = () => (
<StyledModal hidden={()=>someFunction} />
)
For TypeScript, the syntax is similar:
const StyledModal = styled.div<{hidden: boolean}>(...)

Related

Conditional styled components effect all instances when nested

If i use a prop condition on the main component it will work per instance.
So for example if i have:
const Div = styled.div<Props>`
${(props) => {
return css`
${props.test && "border: 5px solid red;"};
`;
}}
`;
only components which have the test prop will have this ugly border :)
But if i use this same condition on a nested css rule like that:
const Div = styled.div<Props>`
${(props) => {
return css`
.tabletScreen & {
${props.test && "border: 5px solid red;"};
}
`;
}}
`;
All instances of this component will have this ugly border if one of the components has this test prop.
When inspect it i see that all instances of the component gets the same class generated, so the this:
.tabletScreen .sc-jcFjpl {
border: 5px solid red;
}
Is implemented to all instances.
But on the first case (when the condition is not nested) the component with the test prop will get another class so it won't override the others.
How can i fix this?
Use && instead of & and it'll be scoped to that stylistic instance of the component. Single ampersand refers to the "static component class".
More info here: https://styled-components.com/docs/basics#pseudoelements-pseudoselectors-and-nesting
import React from "react";
import { render } from "react-dom";
import styled, { css } from "styled-components";
import Wrapper from "./Wrapper";
import Title from "./Title";
// Render these styled components like normal react components.
// They will pass on all props and work
// like normal react components – except they're styled!
const Div = styled.div`
${(props) => {
return css`
.tabletScreen {
border: ${props.test && '5px solid red'};
}
`;
}}
`;
const App = () => (
<Wrapper>
<Div test>
Without Test
<div className="tabletScreen">TabletScreen</div>
</Div>
</Wrapper>
);
render(<App />, document.getElementById("root"));

Access props inside the styled component using MUI

I'm trying to create a custom button component using MUI on a React JS project. I've checked the docs and I discovered that I can do this using styled components. All worked well with the code presented below. The problem is that I want to create a more "customizable" component. I have inside my theme file 2 sets of colors (primary and secondary). The fact is that I want to create a button that is able to take a prop for this set of colors (primary / secondary).
import * as React from 'react';
import ButtonUnstyled, {
buttonUnstyledClasses,
ButtonUnstyledProps,
} from '#mui/core/ButtonUnstyled';
import { styled } from '#mui/system';
import { theme } from '../../theme';
const CustomButtonRoot = styled('button')(`
background-color: ${theme.palette[props.type].main};
padding: 15px 20px;
border-radius: 10px;
color: #fff;
`);
interface TodoButtonProps {
unstyledProps: ButtonUnstyledProps,
type?: 'primary' | 'secondary'
}
function CustomButton(props: TodoButtonProps) {
return <ButtonUnstyled {...props} component={CustomButtonRoot} />;
}
export default CustomButton
The question is: How I can include this prop inside the styled component code?
Pass a callback. In this callback the first argument is the props of the styled component. You can also use the style object instead of template string. More detail in this answer.
const CustomButtonRoot = styled("button")(
({ theme, myColor }) => `
padding: 15px 20px;
border-radius: 10px;
color: ${myColor};
`
);
<CustomButton myColor="red">abc</CustomButton>

styled-components with custom React components: passing props

I have a custom component that takes in a property icon and sets the button's inner image to the corresponding icon as follows:
<custom-button-icon ref={showIcon} slot="right" icon="tier1:visibility-show" onClick={() => isPasswordVisible(false)} />
I would like to apply custom styling to a couple of instances of this component using styled-components. How can I pass the icon prop in so that it still functions (gets passed in as a pop of custom-button-icon)? This is what I have so far but it returns an empty button (with no icon):
export const d2lButtonIcon = ({ icon }) => {
const buttonIcon = styled(`d2l-button-icon`)`
display: flex;
align-items: center;
justify-content: center;
padding: 100px;
margin: 1px;
`
return <buttonIcon icon={icon} />
}
Thanks!
This is a really unusually case because this d2l-button-icon that you are dealing with a custom HTML element rather than a React component.
styled-components can style any React component and any built-in DOM element (a, div, etc.), but I don't think it knows how to deal with a custom DOM element because that's just not the way that we build things in React.
At first I tried passing the style to the d2l-button-icon element, and I got the icon prop to pass through but the style was ignored.
The way that I got this to work is to apply the styles to a div around the element and pass the other props to the d2l-button-icon element itself.
// function component adds styles around the icon button
const UnstyledD2L = ({ className, ...props }) => {
return (
<div className={className}>
<d2l-button-icon {...props} />
</div>
);
};
// can use styled-component to style this wrapper component
const StyledButtonIcon = styled(UnstyledD2L)`
display: flex;
align-items: center;
justify-content: center;
padding: 100px;
margin: 1px;
`;
// example usage with props
export default () => <StyledButtonIcon text="My Button" icon="tier1:gear" />;
However if you aren't super attached to this Brightspace package, I would recommend switching to a UI library that is designed for React.

How to target backdrop color property of Material UI Dialog using styled components?

I'm looking to change the color of the backdrop of the Dialog component of Material UI using styled-components.
I found this thread on how to do exactly that but I'm not sure how to apply this to styled-components.
I currently haved a StyledDialog as such:
const StyledDialog = styled(Dialog).attrs({
classes: { paper: 'container' },
keepMounted: true,
'aria-labelledby': 'alert-dialog-slide-title',
'aria-describedby': 'alert-dialog-slide-description'
})`
.container {
border-radius: 0;
}
`;
<Dialog BackdropProps={{style: {backgroundColor: 'white'}}}/>
https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Dialog/Dialog.js
You can reference the backdrop via its global class ("MuiBackdrop-root") in the following manner:
const StyledDialog = styled(Dialog)`
.MuiBackdrop-root {
background-color: lightgreen;
}
`;
Relevant Styled Components documentation: https://www.styled-components.com/docs/basics#pseudoelements-pseudoselectors-and-nesting

Styled Components & React: Click event

I am trying to create an onClick for a Styled Components Component, but it is not working. It is not logging hello in the console.
<Component onClick={this.handleClick} />
The handleClick:
handleClick(e) {
console.log("hello");
}
This is also in the constructor:
this.handleClick = this.handleClick.bind(this);
The component:
const Component = styled.span`
/* Regular CSS */
${css};
`;
Thanks in advance!
EDIT:
The component is nested in a Button component. When I add the onClick to the Button Component hello is shown in the console. However I want the child to have this function not the parent.
EDIT 2:
Ok so when I change the CSS it works. I have no idea why. This is my css:
top: 0;
left: 0;
width: 10px;
height: 10px;
display: block;
z-index: 0;
position: absolute;
overflow: hidden;
border-radius: inherit;
We probably need more code. Anyway the cleanest way to bind is by arrow functions:
handleClick = () => console.log('click')
If your Button is INSIDE you have to use another props as onClick is binded to onClick event.
Try
// In Parent
<Parent action={this.handleClick} />
// In Child
<Child onClick={this.props.action} />
Your span is rendered with a css value of display: inline. If there is no actual content in it to expand, it will be 0 pixels in width, and therefore you are not actually clicking on it.
I ran into the same issues and in Styled Components I couldn't make it work with a forced display: block. You should use a clickable item, like styled.button...
You might want to try this:
handleClick = e => {
console.log('hello');
}
...
<Component onClick={(e) => this.handleClick(e)} />
or
<Component onClick={this.handleClick.bind(e)}/>

Resources