Animation not working in styled-component using {keyframes} - reactjs

I have a base component - Emoji.jsx
import styled from 'styled-components';
const StyledEmoji = styled.div`
font-size: 6rem;
cursor: pointer;
&:not(:last-child) {
margin-right: 3rem;
}
`;
function Emoji({ content, handleClick }) {
return (
<StyledEmoji onClick={() => handleClick(content)}>{content}</StyledEmoji>
);
}
export default Emoji;
I am extending this component and applying anmimation to it in EmojiBubble.jsx
import Emoji from './Emoji';
import styled, { keyframes } from 'styled-components';
const Bubble = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const EmojiBubble = styled(Emoji)`
animation: ${Bubble} 6s ease-in-out;
`;
export default EmojiBubble;
But, the animation is not working when I am using EmojiBubble component
<EmojiBubble content={emoji} /> //Emoji not rotating
What the issue here?

function Emoji({ content, handleClick })... doesn't pass the class name down to StyledEmoji, that's why styles created by EmojiBubble are not applied. All you need to do: pass the class name:
function Emoji({ content, handleClick, className }) {
return (
<StyledEmoji className={className} onClick={() => handleClick(content)}>
{content}
</StyledEmoji>
);
}
Working example

Related

Override them with styled components

I want to creare a template theme in React for my software suite and allow developers to customize the theme for each software they develop.
The theme will be shipped in a library using styled components
Here is an example of the code:
import styled from 'styled-components'
const ButtonStyled = styled.button`
font-size: 1.5em;
text-align: center;
color: green;
`;
const TomatoButton = styled(ButtonStyled)`
color: tomato;
`;
//This is the default template
const DefaultTemplate = () => {
return <div>
<ButtonStyled className='button'>Button</ButtonStyled>
<TomatoButton className='tomato-button'>Button II</TomatoButton>
</div>
}
//This is the template styled by developers
const DefaultTemplateStyled = styled(DefaultTemplate)`
color: white;
&.button{
color: violet
}
&.tomato-button{
color: black;
}
`;
function App() {
return (<DefaultTemplateStyled />);
}
export default App;
In this app I cannot see the override of the styles, am I missing something?
In styled-components what you should do is pass the className propm like this:
//This is the default template
const DefaultTemplate = ({ className }) => {
return (
<div>
<ButtonStyled className={`button ${className}`}>Button</ButtonStyled>
<TomatoButton className={`tomato-button ${className}`}>
Button II
</TomatoButton>
</div>
);
};
All the other code is fine

React useState with className and styled components hover out

I want to implement an animation both on the hover in and out. For hover in I use a normal css :hover. For hover out I tried to make a default state outside of the :hover but this means that when the page loads it already applies it. This is what I tried.
const Container = styled.div`
animation-name: move-card-down;
animation-duration: 0.5s;
animation-fill-mode: forwards;
:hover {
animation-name: move-card-up;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
#keyframes move-card-up {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-1rem);
}
}
#keyframes move-card-down {
0% {
transform: translateY(-1rem);
}
100% {
transform: translateY(0);
}
}
`
So I figured I could instead apply a class to the div whenever a user hovers over it for the first time instead on a page load.
const [hover, setHover] = useState(false);
const onMouseOverCard = (): void => {
setHover(true);
};
const hovered = true === hover ? 'hovered' : '';
<Container onMouseOver={() => onMouseOverCard()} className={`${hovered}`}>
</Container>
const Container = styled.div`
.hovered {
animation-name: move-card-down;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
`
It applies my class to the section but it doesn't load the styles for it. Can someone tell me why this is?
I'm not sure about the transition part, but the toggling of classes should work fine, example: https://codesandbox.io/s/silent-butterfly-qocvd?file=/src/App.js:0-291
import { useState } from "react";
import styled from "styled-components";
import "./styles.css";
const MyDiv = styled.div`
color: red;
&.foobar {
outline: dashed;
}
`;
export default function App() {
const [klass, setKlass] = useState("");
return (
<MyDiv
className={klass}
onMouseEnter={() => setKlass("foobar")}
onMouseLeave={() => setKlass("")}
>
<h1>Hello CodeSandbox</h1>
</MyDiv>
);
}

React, Styled-component: Override child component style

I would like to override Button component style but it doesn't work. Could you tell me how to override it or should I give all style to the child button component?
Button.js
const StyledButton = styled.button`
color: white;
background-color: yellow;
`;
const Button = ({text}) => {
return <StyledButton>{text}</StyledButton>;
};
export default Button;
Parent.js
import Button from "./Button";
const OverrideButton = styled(Button)`
&& {
color: blue;
background-color: green;
}
`;
const Parent = () => {
return <OverrideButton text="Parent" />;
};
your class is created but not applied. You need to pass it on.
const Button = ({text, className}) => {
return <StyledButton className={className}>{text}</StyledButton>;
};
It was answered here: https://stackoverflow.com/a/54113434/12959556
Example: https://stackblitz.com/edit/react-a5mdpo?embed=1&file=index.js

React & Typescript, styled components & children

I've tried so many different combinations to get this to work, but it's not applying the styles I've written for the StyledLines component, by them selves they work fine! Using the StyledLines component as a child, it doesn't work. The Target component styles work as expected.
import React, { Fragment, FunctionComponent } from 'react';
import styled from '#emotion/styled';
interface Crosshair {
size: number;
thickness: number;
}
const Target = styled.div<Crosshair>`
position:absolute;
&:before {
content: '';
display:block;
position:absolute;
top:50%;
left:50%;
background-color:transparent;
border-color:#2fd5d5;
margin-left:-${({size}) => size / 4}px;
margin-top:-${({thickness}) => thickness / 4}px;
width:${({size}) => size / 2}px;
height:${({thickness}) => thickness / 2}px;
}
`;
const Lines: FunctionComponent = ({children}) => <div className="line">{children}</div>;
const StyledLines = styled(Lines)<Crosshair>`
position:absolute;
&:nth-of-type(1) {
top:0;
left:0;
}
&:nth-of-type(2) {
top:0;
right:0;
}
&:nth-of-type(3) {
bottom:0;
right:0;
}
&:nth-of-type(4) {
bottom:0;
left:0;
}
&:after, &:before {
content: '';
display:block;
position:absolute;
top:50%;
left:50%;
background:#2fd5d5;
margin-left:-${({size = 2}) => size / 2}px;
margin-top:-${({thickness = 24}) => thickness / 2}px;
width:${({size = 2}) => size}px;
height:${({thickness = 24}) => thickness}px;
}
&:before {
transform: rotateZ(90deg);
}
`;
export default function Crosshairs() {
return <Fragment>
<div>
{[0,1,2,3].map(i => <StyledLines key={i} size={24} thickness={2}>
<Target size={24} thickness={2} />
</StyledLines>)}
</div>
</Fragment>;
}
Lines is a plain React component, not a styled component, so you have to pass the className prop to the DOM part you want to style:
const Lines: FunctionComponent = ({children, className}) => <div className={`line ${className}`}>{children}</div>;

React testing toHaveStyleRule property not found in style rules

I am trying to test that ThemeProvider is providing theme to my component. It is properly providing default & custom theme to the base class and passing tests, but when I test for the condition class it does not find any of the styles. This is even while passing through the class directly. This is my first venture into Styled-Components and testing as a whole.
I have tested using the optional { css } import from styled-components, tried passing the class directly, and removing the default class entirely. I also tried setting a default style directly in the styled-component. The toBeTruthy() does pass, so it's at least seeing it with the class I would think?
// This is being called globally
export const mountWithTheme = (Component, customTheme) => {
const theme = customTheme || defaultTheme
return mount(<ThemeProvider theme={theme}>{Component}</ThemeProvider>)
}
import React from 'react';
import { shallow, mount } from 'enzyme';
import { MemoryRouter, Route, Link } from 'react-router-dom';
import { css } from 'styled-components';
import 'jest-styled-components';
import HeaderLinkA from './HeaderLinkA.jsx';
describe('HeaderLinkA', () => {
it('renders color /w prop', () => {
const wrapper = mount(
<MemoryRouter initialEntries={['/about']} >
<Route component={props => <HeaderLinkA {...props} name='about' test='about' theme={{ primarycolor: 'white', secondarycolor: 'black' }} /> } path='/about' />
</MemoryRouter>
)
expect(wrapper.find('Link')).toHaveStyleRule('color', 'white');
expect(wrapper.find('Link')).toHaveStyleRule('color', 'white', {
modifier: `:hover`,
});
});
it('is themed with default styles, when theme is missing', () => {
const wrapper = global.StyledComponents.mountWithTheme(
<MemoryRouter initialEntries={['/about']} >
<React.Fragment>
<HeaderLinkA name='about' testclass='section-link-active' />,
</React.Fragment>
</MemoryRouter>
)
expect(wrapper.find('Link')).toHaveStyleRule('color', '#FFF')
expect(wrapper.find('Link')).toHaveStyleRule('color', '#FFF', {
modifier: `:hover`
});
expect(wrapper.find('Link.section-link-active')).toBeTruthy();
expect(wrapper.find('Link')).toHaveStyleRule('border-bottom', '1px solid #95d5d2');
});
});
import React from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
const StyledHeaderLink = styled(Link)`
text-decoration: none;
color: ${ props => props.theme.primarycolor };
padding-bottom: 2px;
overflow-x: hidden;
position: relative;
display: inline-flex;
&:active,
&:hover {
color: ${ props => props.theme.primarycolor };
}
&.section-link {
&:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
height: 1px;
background: ${ props => props.theme.secondarycolor };
width: 100%;
transform: translate3d(-110%, 0, 0);
-webkit-transform: translate3d(-110%, 0, 0);
transition: transform .3s ease-in-out;
-webkit-transition: transform .3s ease-in-out;
}
&:hover:after {
transform: translate3d(0%, 0, 0);
-webkit-transform: translate3d(0%, 0, 0);
}
}
&.section-link-active {
border-bottom: 1px solid ${ props => props.theme.secondarycolor || '#95d5d2' };
}
`;
const HeaderLinkA = ( props ) => {
const page = props.name.toLowerCase();
return (
<StyledHeaderLink {...props } to={`/${ page }`}
className={props.testclass || window.location.pathname === `/${ page }` ? // refactor this to be controlled by HeaderO and pass down the prop.
'section-link-active'
: 'section-link'} >
{props.name}
</StyledHeaderLink>
)
}
export default HeaderLinkA;
All of the tests pass up until the final one which I'm stuck on.
"Property 'border-bottom' is not found in style rules"
Expected:
"border-bottom: 1px solid #95d5d2"
Received:
"border-bottom: undefined"

Resources