How can I reuse other external styled-components - css

I want to load an external styled component and use extending styles, but it doesn't work. How can I solve this problem?
common/Button.js
import styled from 'styled-components';
const StyledButton = styled.button`
padding: 1rem 2rem;
border: none;
border-radius: 8px;
`;
const Button = ({ children, onClick }) => {
return <StyledButton onClick={onClick}>{children}</StyledButton>;
};
export default Button;
pages/Login.js
import Button from '../Components/common/Button';
import styled from 'styled-components';
const StyledButton = styled(Button)`
// not working
color: red;
`;
const Login = () => {
return (
<div>
<StyledButton>Submit</StyledButton>
</div>
);
}
export default Login;
enter image description here

Component can be styled only if it passes className property to it's child. So you should make your Button component like this:
const Button = ({ children, onClick, className }) => {
return <StyledButton onClick={onClick} className={className}>{children}</StyledButton>;
};

Related

Why does #emotion/styled not render the styled component on screen?

I am trying to use #emotion/styled. But, I cannot get the components to render on the screen, whereas if I am using the HTML element it is working fine.
import styled from "#emotion/styled";
export const Button = styled.button`
color: red;
background-color: green;
`;
import { Button } from "../styles/Button";
const Test = () => {
return (
<div>
<Button>Hello</Button>
</div>
);
};
export default Test;
Does anyone has any idea where things are going wrong?
It is working in my sandbox here
sandbox
import "./styles.css";
import styled from "#emotion/styled";
const Button = styled.button`
color: red;
background-color: green;
`;
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Button>Hello</Button>
</div>
);
}
try to import this on top of your component where you use emotion css.
This fixed my problems with not loading emotion css. Cheers!
/** #jsxImportSource #emotion/react */
You can use styled with css function to make a new component by updating some styles of existing component.
import styled from "#emotion/styled";
import { css } from "#emotion/react";
import { Button as MUIButton } from "#mui/material";
export const Button = styled(MUIButton)(
css({
backgroundColor: "green",
color: "red",
})
);

Shared css declarations to styled component with props

I have some css declarations shared with multiple style-components.
For example:
margin-top:${props.marginTop};
margin-bottom:${props.marginBottom};
So i know can add file with:
export const baseStyles = css`
margin-top: 10px;
margin-bottom: 10px;
`;
and then import it and use:
const MyComponent = styled.div`
${(props: Props) => {
return css`
${baseStyles};
`;
}}
`;
But is there a way to this and still have the current component props used inside ${baseStyled} ?
props are automatically provided inside of mixins.
export const baseStyles = css`
margin-top: ${(props) => props.marginTop};
margin-bottom:${(props) => props.marginBottom};
`;
const MyComponent = styled.div`
${baseStyles}
`;
const App = () => {
return (
<MyComponent marginTop="12px" marginBottom="16px" />
)
};

Styled Components - How to style a component passed as a prop?

I am trying to build this simple component which takes title and Icon Component as props and renders them. The icons I use here are third party components like the ones from Material UI.
option.component.jsx
import { Wrapper } from './option.styles';
function Option({ title, Icon }) {
return (
<Wrapper>
{Icon && <Icon />}
<h4>{title}</h4>
</Wrapper>
);
}
option.styles.js
import styled from 'styled-components';
export const Wrapper = styled.div`
display: flex;
color: grey;
&:hover {
color: white;
}
`;
// export const Icon =
I've organized all my styles in a separate file and I intend to keep it that way.
I want to style <Icon /> , but I don't want to do it inside Option Component like this.
import styled from 'styled-components';
import { Wrapper } from './option.styles';
function Option({ title, Icon }) {
const IconStyled = styled(Icon)`
margin-right: 10px;
`;
return (
<Wrapper>
{Icon && <IconStyled />}
<h4>{title}</h4>
</Wrapper>
);
}
What is the best way to style a component passed as a prop while maintaining this file organization?
I've looked through the documentation and I wasn't able find anything related to this. Any help would be appreciated.
You can do this in 2 ways:
1. As a SVG Icon (svg-icon):
option.styles.js as:
import styled from "styled-components";
import SvgIcon from "#material-ui/core/SvgIcon";
export const Wrapper = styled.div`
display: flex;
color: grey;
&:hover {
color: black;
}
`;
export const IconStyled = styled(SvgIcon)`
margin-right: 10px;
`;
And in your component, do like that:
import { Wrapper, IconStyled } from "./option.styles";
function Option({ title, Icon }) {
return (
<Wrapper>
{Icon && <IconStyled component={Icon} />}
<h4>{title}</h4>
</Wrapper>
);
}
const App = () => {
return (
<>
<Option title="title" Icon={HomeIcon}></Option>
<Option title="title" Icon={AccessAlarmIcon}></Option>
</>
);
};
2. As a Font Icon (font-icons):
Import material icons in <head>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
option.styles.js as:
import styled from "styled-components";
import Icon from "#material-ui/core/Icon";
export const Wrapper = styled.div`
display: flex;
color: grey;
&:hover {
color: black;
}
`;
export const IconStyled = styled(Icon)`
margin-right: 10px;
`;
And in your component, do like that:
import { Wrapper, IconStyled } from "./option.styles";
function Option({ title, icon }) {
return (
<Wrapper>
{icon && <IconStyled>{icon}</IconStyled>}
<h4>{title}</h4>
</Wrapper>
);
}
const App = () => {
return (
<>
<Option title="title" icon='star'></Option>
<Option title="title" icon='home'></Option>
</>
);
};

React: Styled-components loading button

I am using React- typescript for my app. For Styling I am using Styled-components. I have created one global button component. Inside the button component i used loader. My target is when user will click the button it will display loading. From the parent component I have created one fake api call and in there I added settime-out 5s. But when I click the button it does not display the loader which was in Button component. From the parent component set-time out works and it display in my console. I don't know why it does not the display the loading ..... Also i added the disabled option when fake api will call button should be disabled. That logic also does not work.
Here is the Button component
import * as React from "react";
import styled, { css } from "styled-components";
interface IButtonProps {
children?: React.ReactChild;
className?: string;
size?: "small" | "medium" | "big";
themes?: "primary" | "secondary" | "dark" | "light";
disabled?: boolean;
loading?: boolean;
style?: React.CSSProperties;
onClick?: () => void;
onSubmit?: () => void;
}
const Button = ({
children,
className,
size,
themes,
disabled,
loading,
style,
onClick,
onSubmit
}: IButtonProps) => (
<button
className={className}
onClick={onClick}
onSubmit={onSubmit}
style={
disabled && disabled
? { opacity: 0.5, pointerEvents: `none` }
: loading ? { ...style, pointerEvents: `none` } : //This is my disabled condition.
style
}
>
{loading ? <p>loading...</p> : children} //This is my loading condition.
</button>
);
const sizes = {
small: css`
padding: 5px 20px;
font-size: 12px;
`,
medium: css`
padding: 10px 30px;
font-size: 14px;
`,
big: css`
padding: 15px 40px;
font-size: 18px;
`
};
const ButtonThemes = {
primary: css`
border: 1px solid tomato;
background: tomato;
color: white;
`,
secondary: css`
border: 1px solid palevioletred;
background: palevioletred;
color: white;
`,
dark: css`
border: 1px solid #273444;
background: #273444;
color: white;
`,
light: css`
border: 1px solid #eff2f7;
background: #f9fafc;
color: #273444;
`
};
const StyledButton = styled(Button)`
${({ size = "small" }) => sizes[size]};
${({ themes = "primary" }) => ButtonThemes[themes]};
outline: none;
border-radius: 5px;
cursor: pointer;
`;
export default StyledButton;
This is my parent component. Where I used settimeout and import Button component.
const handleClick = () => {
setTimeout(() => {
console.log("check it out") //It does display when the button click
}, 5000);
}
//This is the Button
<Button size="medium" themes="primary" onClick={handleClick}>click</Button>
You should create a loading state by using useState hook if your parent component is function component, if using class component you can define a state in constructor like this this.state = { loading: false } in your parent component, and set it to true in handleClick and pass the state as prop to your Button component:
// In case function component
const [loading, setLoading] = useState(false);
const handleClick = () => {
setLoading(true);
setTimeout(() => {
setLoading(false); // When finish, set it to false
console.log("check it out") //It does display when the button click
}, 5000);
}
//This is the Button
<Button
size="medium"
themes="primary"
onClick={handleClick}
loading={loading}
>
click
</Button>

Why is the flex-value not updating in the styled-component?

I have a sidebar and some main content. The main content takes up the majority of the screen whilst the sidebar should only take a small portion. I've got a parent container that is a flexbox. The two children (sidebar and main content) are both div elements.
The sidebar is closed by default
The Issue: Toggling the sidebar does not expand the sidebar as expected
Things I've Checked:
Flex values in the sidebar css are being updated correctly
Sidebar events are being fired and the isOpen hook is being updated correctly
// main.ts
import styled from 'styled-components';
export const Content = styled.div`
flex: 4;
margin-top: 1em;
margin-bottom: 2em;
height: 100vh;
`;
export const Container = styled.div`
display: flex;
`;
// sidebar/styles.ts
import styled from 'styled-components';
interface RootProps {
isOpen: boolean;
}
export const Root = styled.div<RootProps>`
padding: 1em;
background-color: ${DARK};
flex: ${({ isOpen }: RootProps) => (isOpen ? 1 : 0)};
`;
// sidebar/index.tsx
export const Sidebar: React.FC = () => {
const dispatch = useDispatch();
const isOpen = useSidebar();
const handleOpen = () => dispatch(toggleSidebar());
return (
<Root isOpen={isOpen}>
<Button onClick={handleOpen}>
CLICK ME
</Button>
</Root>
);
};
// Usage
// Sidebar styles mirror what's in the sidebar styled component file
<Container>
<Sidebar />
<Content />
</Container>
Expected outcome is that the sidebar expands and collapses when toggling the button. No error messages are displayed and the sidebar flex values are being updated correctly.
JSFiddle with just HTML / CSS but essentially the desired effect: https://jsfiddle.net/5dLk9ex3/3/
The problem with your code is somewhere in the reducer scope (your question is incomplete), your Sidebar doesn't rerender after you dispatching an action.
Working example:
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
const Container = styled.div`
display: flex;
`;
const First = styled.div`
flex: ${({ isOpen }) => (isOpen ? 1 : 0)};
background-color: red;
height: 20px;
`;
const Second = styled.div`
flex: 4;
background-color: green;
height: 20px;
`;
const DEFAULT_INITIAL = false;
const App = () => {
const [isOpen, setIsOpen] = useState(DEFAULT_INITIAL);
const onClick = () => {
console.log('Toggle Sidebar');
setIsOpen(p => !p);
};
return (
<>
<Container>
<First isOpen={isOpen}>FIRST</First>
<Second>SECOND</Second>
</Container>
<button onClick={onClick}>OpenSider</button>
</>
);
};
Please refer to the first comment of this answer
I'm unsure as to why but setting the main content div to min-width: 0 resolved the issue. See this for more: Flex items not shrinking when window gets smaller

Resources