Why child Styled-components is not applying styles? - reactjs

I have a Notification component :
import React, { useContext } from "react"
import Wrapper from "./Wrapper"
import context from "./context"
import Close from "./Close"
const Notification = ({ children }) => {
const { type } = useContext(context)
return (<Wrapper type={type}>
<Close /> {// This component does not apply styles !!}
{children}
</Wrapper>)
}
export default Notification
My Wrapper :
import styled from "styled-components"
const Wrapper = styled.div`
position:absolute;
bottom:0;
right:0;
margin-right:1.2em;
margin-bottom:1.2em;
background-color: #fff;
padding:15px;
min-width:350px;
min-height:100px;
border-radius:20px;
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)}}`
const pickShadowColor = (type) => {
switch (type) {
case "info": return "rgba(51,153,255,0.3)";
case "warning": return "rgba(255,157,0,0.3)";
case "success": return "rgba(0,216,0,0.3)";
case "error": return "rgba(255,0,0,0.3)";
default: return "rgba(190,190,190,0.3)";
}
}
export default Wrapper
The result is :
The problem is with Close component while it has a custom style but it still shows the default style of button.
Close component :
import React from 'react';
import { Button } from './Button';
const Close = () => (
<Button>close</Button>
)
export default Close;
and the styled Button :
import styled from 'styled-components';
export const Button = styled.button({
'background-color': 'red'
})
Expected Behavior
The button should be red.
Actual Behavior
The button has a default style.
Additional Info
Navigator: Chrome.
The button does not accept any styles.

The problem its on box-shadow. You are missing a ; and have an extra {
Change it FROM:
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)}}`;
TO:
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)};`;
The complete style:
const Wrapper = styled.div`
position: absolute;
bottom: 0;
right: 0;
margin-right: 1.2em;
margin-bottom: 1.2em;
background-color: #fff;
padding: 15px;
min-width: 350px;
min-height: 100px;
border-radius: 20px;
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)};
`;

Related

Implementing animation when removing Toast

I have a working ToastList that enables me to click a button multiple times and generate a toast each time. On entry, I have an animation, but when I remove the toast, I do not get an animation. I am using Typescript and functional components.
My component is as follows:
import React, { useCallback, useEffect, useState } from 'react';
import * as Styled from './Toast.styled';
export interface ToastItem {
id: number;
title: string;
description: string;
backgroundColor: string;
}
export interface ToastProps {
toastList: ToastItem[];
setList: React.Dispatch<React.SetStateAction<ToastItem[]>>;
}
export default function Toast(props: ToastProps) {
const deleteToast = useCallback(
(id: number) => {
const toastListItem = props.toastList.filter((e) => e.id !== id);
props.setList(toastListItem);
},
[props.toastList, props.setList]
);
useEffect(() => {
const interval = setInterval(() => {
if (props.toastList.length) {
deleteToast(props.toastList[0].id);
}
}, 2000);
return () => {
clearInterval(interval);
};
}, [props.toastList, deleteToast]);
return (
<Styled.BottomRight>
{props.toastList.map((toast, i) => (
<Styled.Notification
key={i}
style={{ backgroundColor: toast.backgroundColor }}
>
<button onClick={() => deleteToast(toast.id)}>X</button>
<div>
<Styled.Title>{toast.title}</Styled.Title>
<Styled.Description>{toast.description}</Styled.Description>
</div>
</Styled.Notification>
))}
</Styled.BottomRight>
);
}
And my styling is done using styled-components and is as follows:
import styled, { keyframes } from 'styled-components';
export const Container = styled.div`
font-size: 14px;
position: fixed;
z-index: 10;
& button {
float: right;
background: none;
border: none;
color: #fff;
opacity: 0.8;
cursor: pointer;
}
`;
const toastEnter = keyframes`
from {
transform: translateX(100%);
}
to {
transform: translateX(0%);
}
}
`;
export const BottomRight = styled(Container)`
bottom: 2rem;
right: 1rem;
`;
export const Notification = styled.div`
width: 365px;
color: #fff;
padding: 15px 15px 10px 10px;
margin-bottom: 1rem;
border-radius: 4px;
box-shadow: 0 0 10px #999;
opacity: 0.9;
transition .1s ease;
animation: ${toastEnter} 0.5s;
&:hover {
box-shadow: 0 0 12px #fff;
opacity: 1;
}
`;
export const Title = styled.p`
font-weight: 700;
font-size: 16px;
text-align: left;
margin-bottom: 6px;
`;
export const Description = styled.p`
text-align: left;
`;
When I click a button, I just add an element to the state list, like:
toastProps = {
id: list.length + 1,
title: 'Success',
description: 'Sentence copied to clipboard!',
backgroundColor: '#5cb85c',
};
setList([...list, toastProps]);
My component is rendered like:
<Toast toastList={list} setList={setList}></Toast>
I would like to add animation when a toast exits, but do not know how. I have tried changing the style according to an additional prop I would send to the styled components, but this way all the toasts animate at the same time. My intuition is that I should use useRef(), but I am not sure how. Thanks in advance for any help you can provide.

What is the reason why the pagination style is not getting applied? I am trying to display lists in row but it is not working

Pagination.jsx
import { Box } from "#chakra-ui/react";
import React, { useEffect, useState } from "react";
import ReactPaginate from "react-paginate";
import "./Pagination.css";
export function ReactPagination({ lastPage, filterData }) {
const [pageCount, setPageCount] = useState(0);
useEffect(() => {
setPageCount(lastPage);
}, [lastPage]);
// Invoke when user click to request another page.
const handlePageClick = (event) => {
filterData(event.selected + 1);
};
return (
<Box w="full">
<ReactPaginate
breakLabel="..."
nextLabel="next >"
onPageChange={handlePageClick}
pageRangeDisplayed={4}
pageCount={pageCount}
previousLabel="< previous"
renderOnZeroPageCount={null}
containerClassName="pagination"
previousLinkClassName="pagination__link"
nextLinkClassName="pagination__link"
disabledClassName="pagination__link--disabled"
activeClassName="pagination__link--active"
/>
</Box>
);
}
Pagination.css
//css for react paginate
.pagination {
list-style: none;
display: flex !important;
gap: 4px;
justify-content: space-between;
background: red;
}
.pagination a {
padding: 10px;
border-radius: 5px;
border: 1px solid #6c7ac9;
color: #6c7ac9;
}
.pagination__link {
font-weight: bold;
}
.pagination__link--active a {
color: #fff;
background: #6c7ac9;
}
.pagination__link--disabled a {
color: rgb(198, 197, 202);
border: 1px solid rgb(198, 197, 202);
}
The pagination className in containerClassName is not getting applied but it's strange that other styles like disabledClassName, activeClassName are applied. I am trying to display lists in row but it is not working. Is there any reason or any package updates that is causing this issue?

Styled-components extends props

I have a ready button like this , These are the features I want every button to have.
If I send props for the icon, I don't want the icon to be created, if I don't, I don't want it to be created.
import Icon from 'components/icons/Icon';
import styled from 'styled-components';
const Button = styled.button`
border-radius: 8px;
color: white;
cursor: pointer;
outline: none;
border: none;
display: flex;
font-family: 'DM Sans', sans-serif;
font-weight: 700;
`;
const DefaultButton = ({ name, children }) => {
return (
<Button>
{name && <Icon name={name}></Icon>}
{children}
</Button>
);
};
export default DefaultButton;
I have a button component that I have customized like this.
I want it to inherit its properties from the default button.
But Primary's features do not appear on my button.
I wonder what should I fix, thanks friend
import styled from 'styled-components';
import { sizes } from 'constants/components/button';
import { colors } from 'theme';
import DefaultButton from './DefaultButton';
const PrimaryButton = styled(DefaultButton)`
padding: ${({ size }) => sizes[size]} 48px;
background-color: ${colors.primary.color};
font-size: 14px;
font-style: bold;
&:focus {
border: 1px solid #ffffff;
box-sizing: border-box;
box-shadow: 0px 0px 0px 3px rgba(24, 207, 152, 1);
}
&:hover {
background-color: ${colors.primary.pressed};
box-shadow: 0px 14px 24px rgba(23, 207, 151, 0.3);
color: white;
}
&:disabled {
background-color: ${colors.primary.disabled};
cursor: not-allowed;
pointer-events: none;
color: white;
}
`;
export default PrimaryButton;
This is how I use my component and I want it to be like this
<PrimaryButton size="md" name="test">
Sign in
</PrimaryButton>
If you want to extend/override the styles of another component, you need to pass down the className prop, as mentioned in the docs.
The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element.
const DefaultButton = ({ name, children, className }) => {
return (
<Button className={className}>
{name && <Icon name={name}></Icon>}
{children}
</Button>
);
};
I find #svrbst solution very effective. I extended it to add other dependencies as shown is the question and it works fine. Here is the forked codesandbox

How to interpolate a CSS property in styled-components?

I am trying to create a Styled Component which can receive a prop for the type of border. The Section can either have border-top or border-bottom based on the prop passed to it.
Here is the code
type TSectionBorder = 'top' | 'bottom';
<Section edge="top">
{children}
</Section>
How can I achieve this in styled components, something along these lines -
const Section = styled.section<{edge: TSectionBorder}>`
border-${edge}: 1px solid black;
`;
const Section = styled.section<{edge: TSectionBorder}>`
${({ edge }) => `border-${edge}: 1px solid black`};
`;
I deconstructed the props just to keep your syntax, however, this is one way to do so. They also have a css helper to look into.
Documentation:
https://styled-components.com/docs/basics#passed-props
https://styled-components.com/docs/api#css
First you will import:
import styled from 'styled-components';
Here is the function ButtonBlock:
function ButtonBlock(props) {
return (
<BtnBlock type="primary" {...props}>
{props.children}
</BtnBlock>
);
}
const handleColorType = (color) => {
switch (color) {
case 'grey':
return '#D4D5D6';
default:
return '#6633CC';
}
};
const hoverColorType = (color) => {
switch (color) {
case 'grey':
return '#6a7581';
default:
return '#99CC00';
}
};
Styled Component:
const BtnBlock = styled(Button)`
&& {
max-width: none;
width: 100%;
text-align: center;
display: block;
font-size: 14px;
font-weight: 600;
line-height: 50px;
height: 50px;
text-transform: uppercase;
border-radius: 0;
border: 0px;
padding: 0;
background: ${({ color }) => handleColorType(color)};
&[disabled] {
background-color: #c6c6c6;
}
:hover {
background-color: ${({ color }) => hoverColorType(color)};
}
&[disabled]:hover {
background-color: #c6c6c6;
}
}
`;

How can I add custom CSS to my React component and override the default CSS value?

I have a component for my button with its own css file, but I'm trying to figure out how to add a className directly to the component and change the CSS for mobile view. Right now, the Button.css file overrides everything and my new className added to the component doesn't even show up in the html.
Here is the Button.js file
import React from 'react';
import './Button.css'
const STYLES = [
'btn--primary',
'btn--outline'
]
const SIZES = [
'btn--medium',
'btn--large'
]
export const Button = ({
children,
type,
onClick,
buttonStyle,
buttonSize
}) => {
const checkButtonStyle = STYLES.includes(buttonStyle) ? buttonStyle : STYLES[0]
const checkButtonSize = SIZES.includes(buttonSize) ? buttonSize : SIZES[0]
return (
<button className={`btn ${checkButtonStyle} ${checkButtonSize}`} onClick={onClick} type={type}>
{children}
</button>
)
}
Here is the Button.css
:root {
--primary: #3acbf7;
}
.btn {
padding: 8px 20px;
border-radius: 4px;
outline: none;
border: none;
cursor: pointer;
}
.btn:hover {
background-color: transparent;
color: #fff;
padding: 8px 20px;
border-radius: 4px;
border: solid 2px var(--primary);
transition: all 0.3s ease-out;
}
.btn--primary {
background-color: var(--primary);
}
.btn--outline {
background-color: transparent;
color: #fff;
padding: 8px 20px;
border-radius: 4px;
border: solid 1px var(--primary);
transition: all 0.3s ease-out;
}
.btn--medium {
padding: 8px 20px;
border-radius: 4px;
font-size: 18px;
color: #fff;
}
.btn--large {
padding: 12px 26px;
border-radius: 4px;
font-size: 20px;
color: #fff;
}
And here is the main component where I'm trying to add a custom className to override the code above. Note this code is inside of a Navbar component.
import React, { Component } from 'react';
import { MenuItems } from "./MenuItems"
import { Button } from "../Button"
import './Navbar.css'
class Navbar extends Component {
render() {
return (
<nav className="NavbarItems">
<Button className="btn-mobile">Sign Up</Button>
</nav>
)
}
}
If I try to add .btn-mobile to my Navbar.css, none of the properties show up in the html. I tried to add an id and still didn't work. Not sure how to custom the button since it's a component with preset properties
The way that I generally accomplish this is to accept className as a prop and spread it into the default classes of the element, like so:
const Button = ({className='', text, ...rest}) => (
<button className={`my-button-class ${className}`} {...rest}>{text}</button>
)
Then you could use it like so:
<Button className="my-custom-class" text="press me" id="my-id" />
and it will render as
<button class="my-button-class my-custom-class" id="my-id">press me</button>
By defaulting the className prop to an empty string, it won't be added as the string of "undefined" if the implementation doesn't pass a className. Note that we also spread in any other props that were passed.

Resources