Styling Components (styled-components) | Superimposing one on top of the other - reactjs

The problem is this:
There is a created UiInput React component using styled-components, above it there is also a UiInputExt React component, which should override some of the styles defined in UiInput, but for some reason this does not happen, apparently it does not even add the corresponding class...
I attach the code below:
const StyledInput = styled.input`
color: ${(props) => props.styles.color};
::placeholder,
::-webkit-input-placeholder {
color: ${(props) => props.styles.color};
}
:-moz-placeholder {
color: ${(props) => props.styles.color};
opacity: 1;
}
::-moz-placeholder {
color: ${(props) => props.styles.color};
opacity: 1;
}
:-ms-input-placeholder {
color: ${(props) => props.styles.color};
}
`;
<StyledInput
id={id}
className={cn(styles.input, classes)}
type={type}
placeholder={placeholder}
styles={isTheme.styles}
>
</StyledInput>
And the corresponding override (which doesn't work!)
import UiInput from '../';
const StyledUiInputExt = styled(UiInput)`
color: ${(props) => props.styles.color_uiinputext};
::placeholder,
::-webkit-input-placeholder {
color: ${(props) => props.styles.color_uiinputext};
}
:-moz-placeholder {
color: ${(props) => props.styles.color_uiinputext};
opacity: 1;
}
::-moz-placeholder {
color: ${(props) => props.styles.color_uiinputext};
opacity: 1;
}
:-ms-input-placeholder {
color: ${(props) => props.styles.color_uiinputext};
}
`;
<StyledUiInputExt classes={cn(styles.input, classes)} {...props} styles={isTheme.styles}></StyledUiInputExt>

You are not exporting the styled component StyledInput, rather you are exporting a plain React component that is rendering StyledInput.
const StyledInput = styled.input`
color: blue;
`;
const UiInput = () => {
return <StyledInput />;
};
export default UiInput;
UiInputExt
import UiInput from "./UiInput";
const StyledInputExt = styled(UiInput)`
color: green;
`;
const UiInputExt = () => {
return <StyledInputExt />;
};
The style you are applying to UiInput isn't applied to the JSX it renders, i.e. it isn't passed through to StyledInput.
You can only override other styled-components. For this you should export/import StyledInput (renamed to UiInput) directly.
const UiInput = styled.input`
color: blue;
`;
export default UiInput;
An alternative would be to export UiInput as a styled component and ensure that the className prop is passed through to the StyledInput component.
const StyledInput = styled.input`
color: blue;
`;
const UiInput = ({ className }) => {
return <StyledInput className={className} />;
};
export default styled(UiInput)``;

Related

Why function is empty that's passed form ThemeContext

I am trying to call toggleTheme function from themeContext. But it is empty function.
themeContext.js
import React, { useState } from 'react';
export const ThemeContext = React.createContext({
theme: 'light',
toggleTheme: () => {}
});
export function ThemeContextProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
console.log("ToggleTheme")
setTheme(theme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
//App.js
import React, { useContext } from "react";
import { ThemeContext, ThemeContextProvider } from "./theme/themeContext";
import styled from "styled-components";
const AppContainer = styled.div`
color: ${({ theme }) => theme.textPrimary};
background-color: ${({ theme }) => theme.background};
font-size: large;
font-weight: bold;
padding: 20px;
height: calc(100vh - 40px);
transition: all 0.5s;
`;
const Button = styled.button`
color: ${({ theme }) => theme.textPrimary};
background-color: ${({ theme }) => theme.background};
border: 2px ${({ theme }) => theme.textPrimary} solid;
float: right;
transition: all 0.5s;
cursor: pointer;
`;
function App() {
const { theme, toggleTheme } = useContext(ThemeContext);
const handleClick = () => {
console.log(theme);
console.log(toggleTheme);
toggleTheme();
};
return (
<ThemeContextProvider>
<AppContainer theme={theme}>
<Button onClick={() => handleClick()}>
Switch to {theme === "light" ? "Dark" : "Light"}
</Button>
{/* rest of your component code */}
</AppContainer>
</ThemeContextProvider>
);
}
export default App;

React - Typescript - How to function as prop to another component

There are three components Toggle, ToggleMenu, Wrapper
The toggle should be universal, and used for different functions
The Wrapper should only change background color when toggle is on
The question is how to pass the function that responcible for changing color to togglemenu, that it executs when switch toggle
App
import React from 'react';
import { Wrapper } from './containers/wrapper';
import { Button } from './components/buttons/button';
import { ToggleMenu } from './components/settings/settings';
const App = () => {
return (
<>
<Wrapper>
<Button />
<ToggleMenu />
</Wrapper>
</>
)
}
export default App;
ToggleMenu
import styled from "styled-components";
import { Toggle } from "../buttons/toggle";
import { WrapperProps } from "../../containers/wrapper";
import { test } from "./test"
const SettingsContainer = styled.div`
margin: auto;
width: 50%;
height: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: white;
`;
const Container = styled.div`
height: 50%;
width: 50%;
display: flex;
justify-content: center;
flex-direction: column;
background-color: lime;
`;
const TogCon = styled.div`
margin: 0.5em;
`;
export const ToggleMenu = (props: WrapperProps) => {
return (
<SettingsContainer>
<Container>
<TogCon>
<Toggle toggleText={"Dark mode"} onChange={props.handleTheme}/>
</TogCon>
<TogCon>
<Toggle toggleText={"Sth"} onChange={() => {console.log("Sth")}}/>
</TogCon>
</Container>
</SettingsContainer>
);
};
Wrapper
import React, { useState } from "react";
import styled from "styled-components";
export type WrapperProps = {
children?: React.ReactNode;
color?: string;
handleTheme?: () => void;
};
const Container = styled.div<WrapperProps>`
width: 100%;
height: 100%;
top: 0;
left: 0;
position: fixed;
background-color: ${props => props.color }
`
export const Wrapper = ({ children }: WrapperProps) => {
const [theme, setTheme] = useState("black")
const handleTheme = () => {
theme === "black" ? setTheme("white"): setTheme("black")
}
return(
<Container
color={theme}
handleTheme={handleTheme}
> { children }
</Container>
);
}
Was solved with React.cloneElement
export const Wrapper = (props: WrapperProps) => {
const [theme, setTheme] = useState("black")
const handleTheme = () => {
theme === "black" ? setTheme("white"): setTheme("black")
}
const childrenWithProps = React.Children.map(props.children, child => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { handleTheme });
}
return child;
});
return(
<Container color={theme} >
{ childrenWithProps }
</Container>
);
}

Explain example from Material UI v5 [duplicate]

Is it possible to use the spread operator with a styled component in React Native?
I have this component:
const StyledHeaderText = styled.Text`
fontFamily: ${props => props.theme.font};
fontSize: ${props => props.theme.fontSizeSubtitle};
color: ${props => (props.lightMode) ? props.theme.fontColor : props.theme.fontPrimaryColor}
`;
But lets say that in my theme, I have an object that has both the fontFamily and the fontSize, and I re use all over the app. I would like to be able to know if I can do something like this, which currently it is not working:
const StyledHeaderText = styled.Text`
...${props => props.theme.fontHeader};
color: ${props => (props.lightMode) ? props.theme.fontColor : props.theme.fontPrimaryColor}
`;
This would be useful too setting up elevation in iOS for example, since I have to setup 4 styles.
Thanks
You can use the css helper function to generate the specific css and return it as a template literal.
import styled, {css} from 'styled-components/native'
const GlobalStyles = css`
fontFamily: ${props => props.theme.font};
fontSize: ${props => props.theme.fontSizeSubtitle};
`
const StyledHeaderText = styled.Text`
${GlobalStyles}
// Other Styles
`
or conditionally as
const StyledHeaderText = styled.Text`
${props => props.theme.fontHeader ? GlobalStyles : 'fontSize: 14'}
// Other Styles
`
In React.js you can use the spread operator override multiple styles
Here is a link to a code example
const StyledText = styled.div`
/* Normal styles */
color: blue,
background-color: white,
/* Fields to override with object from props */
${({ styleOverrides }) => ({ ...styleOverrides })}
`;
You can also return objects directly from interpolations function, and they'll be treated as inline styles.
const StyledHeaderText = styled.Text`
${props => ({...props.theme.fontHeader})};
color: ${props => (props.lightMode) ? props.theme.fontColor props.theme.fontPrimaryColor}
`;
or
const StyledHeaderText = styled.Text`
${props => props.theme.fontHeader};
color: ${props => (props.lightMode) ? props.theme.fontColor props.theme.fontPrimaryColor}
`;
for more details : reference
To expand on previous answers you can also do the following:
import styled, {css} from 'styled-components/native'
// with theme
const GlobalStyles = css`
fontFamily: ${ ({theme}) => theme.font };
fontSize: ${ ({theme}) => theme.fontSizeSubtitle };
`
// Without theme using inherited props
const GlobalStyles = css`
fontFamily: ${ ({font}) => font };
fontSize: ${ ({fontSizeSubtitle}) => fontSizeSubtitle };
`
// if you wanted it to be conditional
const GlobalStyles = css`
fontFamily: ${ ({font}) => font || 'roboto' };
fontSize: ${ ({fontSizeSubtitle}) => fontSizeSubtitle || '14px' };
`
const StyledHeaderText = styled.Text`
${GlobalStyles}
// Other Styles
`
// same can also be done with regular styled components:
export const HeaderText = styled.p`
fontSize: ${ ({fontSizeSubtitle}) => fontSizeSubtitle || '14px' };
`
// Useage would be in your component would be like:
import react from react;
import { HeaderText } from '../component/styles'
export const FooComponent = memo(({ destructuredProps }) => {
// some other code
return (
<>
<HeaderText className={fooClass} fontSize='18px'> Here's my sample text! </HeaderText>
<>
)});

How to add "active" class to styled components in React

I'm learning React and i just founded about styled-components and i would like to refactor my code into styled components.
Maybe i didn't understand it at all but i'm trying to make add/remove class on "toggle"
For example:
button--active
Here is what i've tried so far:
render() {
const {active, toggleHamburgerActive } = this.props;
return (
<div>
<HamburgerButtonContainer active={active} onClick={toggleHamburgerActive}/>
</div>
styles:
const buttonStyles = css`
border: 1px solid red;
`;
const getButtonStyles = active => {
if (active) {
return hamburgerActiveStyles;
}
return buttonStyles;
};
const hamburgerActiveStyles = css`
border: 10px solid green;
`;
export const HamburgerButtonContainer = styled.button`
${getButtonStyles}
`;
#EDIT
To make it more clear. active is changing on click but styles are not.
I think you just forgot to destructure your props:
const getButtonStyles = /* this here ---> */({active}) => {
if (active) {
return hamburgerActiveStyles;
}
return buttonStyles;
};
const getButtonStyles = /* props is passed here, not only active ---> */props =>
You can access props by passing a callback in an expression as you did.
styled.button`
${props => ...} // you passed the callback here, so props is passed to it, not only active
`
Or if you prefer
styled.button`
${props => getButtonStyles(props.active)}
`
You haven't called your function using active as a param. And You should organise your code like below to pass the active as a prop.
const HamburgerButtonContainer = ({ onClick, active }) => {
const buttonStyles = `
border: 1px solid red;
`;
const getButtonStyles = (active) => {
if (active) {
return hamburgerActiveStyles;
}
return buttonStyles;
};
const hamburgerActiveStyles = `
border: 10px solid green;
`;
const StyledHamburgerButtonContainer = styled.button`
${getButtonStyles(active)}
`;
return <StyledHamburgerButtonContainer onClick={onClick} />;
};

React Storybook Checkbox change handler not working

I am creating a checkbox component using React, Typescript, Storybook and Styled-Components, after following this tutorial: building-a-checkbox-component-with-react-and-styled-components. I have had to adjust the code as I am using a FunctionComponent, but I am facing an issue with the change handler. I cannot check or uncheck the Checkbox which seems to be readonly, and I'm not sure where I am going wrong. Here is my code:
Checkbox.tsx
import React from 'react';
import styled from 'styled-components';
import { FunctionComponent } from 'react';
type CheckboxProps = {
checked?: boolean;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
};
const CheckboxContainer = styled.div`
display: inline-block;
vertical-align: middle;
`;
const Icon = styled.svg`
fill: none;
stroke: white;
stroke-width: 2px;
`;
const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
border: 0;
clip: rect(0 0 0 0);
clippath: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
`;
const StyledCheckbox = styled.div`
display: inline-block;
width: 16px;
height: 16px;
background: ${(props: CheckboxProps) => (props.checked ? 'green' : 'white')};
border-color: 'dark gray';
border-width: 1px;
border-style: solid;
border-radius: 2px;
${HiddenCheckbox}:focus + & {
box-shadow: 0 0 0 3px grey;
}
${Icon} {
visibility: ${(props: CheckboxProps) => (props.checked ? 'visible' : 'hidden')};
}
`;
const Checkbox: FunctionComponent<CheckboxProps> = ({ onChange, checked, ...props }) => {
return (
<CheckboxContainer>
<HiddenCheckbox checked={checked} {...props} onChange={onChange} />
<StyledCheckbox data-testid="styledcheckbox" checked={checked} {...props} onChange={onChange}>
<Icon viewBox="0 0 24 24">
<polyline points="20 6 9 17 4 12" />
</Icon>
</StyledCheckbox>
</CheckboxContainer>
);
};
export default Checkbox;
Checkbox.stories.js
// Checkbox.stories.js
import React, { useState, useRef } from 'react';
import Checkbox from '#components/Checkbox/Checkbox';
import { action } from '#storybook/addon-actions';
import { storiesOf } from '#storybook/react';
// eslint-disable-next-line #typescript-eslint/explicit-function-return-type
const CheckboxStateful = props => {
const [value, setValue] = useState(props);
const valRef = useRef(value);
// eslint-disable-next-line #typescript-eslint/explicit-function-return-type
const onChange = event => {
setValue(event.target.value);
valRef.current = event.target.value;
};
return (
<Checkbox
value={value}
onChange={event => {
onChange(event);
}}
></Checkbox>
);
};
storiesOf('Checkbox', module)
.add('with checked', () => {
const value = true;
// eslint-disable-next-line #typescript-eslint/explicit-function-return-type
const onChange = event => setValue(event.target.value);
return <CheckboxStateful value={value} onChange={onChange}></CheckboxStateful>;
})
.add('with unchecked', () => {
const value = false;
// eslint-disable-next-line #typescript-eslint/explicit-function-return-type
const onChange = event => setValue(event.target.value);
return <CheckboxStateful value={value} onChange={onChange}></CheckboxStateful>;
});
As I am a novice at both React and Storybook I may require some expert input into whether my change handler code is correct or not. I have looked at other examples, but none of them use Typescript. Any help would be appreciated.
Solution is to change onChange to onClick in CheckboxProps in Checkbox.tsx, as follows:
onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
and to simplify CheckboxStateful in Checkbox.stories.js as follows:
const CheckboxStateful = () => {
const [value, setValue] = useState(false);
return <Checkbox checked={value} onClick={() => setValue(!value)}></Checkbox>;
};
The complete updated code is now as follows:
Checkbox.tsx
import * as React from 'react';
import styled from 'styled-components';
import { FunctionComponent } from 'react';
type CheckboxProps = {
checked?: boolean;
onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
};
const CheckboxContainer = styled.div`
display: inline-block;
vertical-align: middle;
`;
const Icon = styled.svg`
fill: none;
stroke: white;
stroke-width: 2px;
`;
const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
border: 0;
clip: rect(0 0 0 0);
clippath: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
`;
const StyledCheckbox = styled.div`
display: inline-block;
width: 16px;
height: 16px;
background: ${(props: CheckboxProps) => (props.checked ? 'green' : 'white')};
border-color: 'dark gray';
border-width: 1px;
border-style: solid;
border-radius: 2px;
${HiddenCheckbox}:focus + & {
box-shadow: 0 0 0 3px grey;
}
${Icon} {
visibility: ${(props: CheckboxProps) => (props.checked ? 'visible' : 'hidden')};
}
`;
const Checkbox: FunctionComponent<CheckboxProps> = ({ onClick, checked, ...props }) => {
return (
<CheckboxContainer>
<HiddenCheckbox checked={checked} {...props} />
<StyledCheckbox checked={checked} {...props} onClick={onClick} data-testid="styledcheckbox">
<Icon viewBox="0 0 24 24">
<polyline points="20 6 9 17 4 12" />
</Icon>
</StyledCheckbox>
</CheckboxContainer>
);
};
export default Checkbox;
Checkbox.stories.js
/* eslint-disable #typescript-eslint/explicit-function-return-type */
// Checkbox.stories.js
import * as React from 'react';
import { useState } from 'react';
import Checkbox from '#components/Checkbox/Checkbox';
import { action } from '#storybook/addon-actions';
import { storiesOf } from '#storybook/react';
export default {
title: 'Checkbox',
component: Checkbox,
};
const CheckboxStateful = () => {
const [value, setValue] = useState(false);
return <Checkbox checked={value} onClick={() => setValue(!value)}></Checkbox>;
};
storiesOf('Checkbox', module)
.add('with checked', () => {
const value = true;
return <Checkbox checked={value} onClick={action('checked')}></Checkbox>;
})
.add('with unchecked', () => {
const value = false;
return <Checkbox checked={value} onClick={action('checked')}></Checkbox>;
})
.add('stateful', () => {
return <CheckboxStateful />;
});
Once you create the React project and add Storybook, just run yarn storybook and the checkboxes will show up correctly.
Instead of creating a new Stateful component a much better approach is to use state in the components template.
An example:
const Template: ComponentStory<typeof MyComponent> = (args) => {
const [state, setState] = useState('');
return (
<MyComponent
prop1={args.prop1}
prop2={args.prop2}
onChange={() => setState('Change')}
/>
);
};
Here's a link to the original answer I got this approach from:
https://stackoverflow.com/a/64722559/10641401

Resources