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"
Related
I'm making a React navbar with a hamburger menu icon, but I cannot figure out why my icon cannot be positioned absolutely to the right. Chrome dev tools indicates that both Top and Right properties (as well as any other margin/padding properties) are invalid property values on my hamburger icon (a div element), though my nav menu itself is able to be positioned absolutely to the right.
Navbar.jsx:
import React, { useState } from 'react';
import NavIcon from './NavIcon';
import NavMenu from './NavMenu';
import './navbar.css';
const Navbar = () => {
const [isOpen, setIsOpen] = useState('false');
return (
<nav>
<NavIcon isOpen={isOpen} setIsOpen={setIsOpen} />
<NavMenu isOpen={isOpen} setIsOpen={setIsOpen} />
</nav>
);
};
export default Navbar;
NavMenu.jsx:
import React from 'react';
import NavItem from './NavItem';
import './navbar.css';
const NavMenu = ({ isOpen }) => {
const slideIn = {
transform: () => isOpen ? 'translateX(1)' : 'translateX(0)'
};
return (
<ul className='Navbar__menu' isOpen={isOpen} style={slideIn}>
<NavItem title='Home' />
<NavItem title='About' />
<NavItem title='Contact' />
</ul>
);
};
export default NavMenu;
NavIcon.jsx:
import React from 'react';
import './navbar.css';
const NavIcon = ({ isOpen, setIsOpen }) => {
const toXTop = {
transform: isOpen ? 'rotate(-45deg) translate(-5.25px, 6px)' : 'none'
};
const toXMid = {
opacity: isOpen ? '0' : '1'
};
const toXbottom = {
transform: isOpen ? 'rotate(45deg) translate(-5.25px, -6px)' : 'none'
};
return (
<div className='Navbar__icon' isOpen={isOpen} onClick={() => setIsOpen(!isOpen)}>
<div className='Navbar__icon--bars' style={toXTop} />
<div className='Navbar__icon--bars' style={toXMid} />
<div className='Navbar__icon--bars' style={toXbottom} />
</div>
);
};
export default NavIcon;
NavItem.jsx:
import React from 'react';
import './navbar.css';
const NavItem = ({ title }) => {
return (
<div className='Navbar__menu--item'>
{ title }
</div>
);
};
export default NavItem;
navbar.css:
.Navbar__icon {
display: block;
position: absolute;
top: 5;
right: 5;
z-index: 1;
cursor: pointer;
}
.Navbar__icon--bars {
width: 25px;
height: 4px;
background-color: #666;
border-radius: 5px;
margin: 4px 0;
transition: 0.2s;
}
.Navbar__menu {
position: absolute;
width: 15em;
height: auto;
right: 0;
list-style: none;
background-color: #000;
}
.Navbar__menu--item {
font: 1rem Arial, sans-serif;
color: #999;
margin: 1em;
cursor: pointer;
}
App.js just renders the Navbar.jsx component.
Any advice is appreciated. Thanks.
It seems that Top/Left/Right/Bottom property values in React do not support unitless numerical values, as this issue was resolved by adding px units to my values.
I'm following a tutorial and everything was going great until I tried to implement Navigation through a search input. For instance, If I am on http://localhost:3000/searched/profile. Typing out an input of 'names' in the search bar should take me to http://localhost:3000/searched/names. In the tutorial it worked that way and I believe I did the same thing but it doesn't work for me
First below is the Search component for the search bar and its input
And then the Pages where my routing is done. My Browser Router is in the App.js.
import styled from "styled-components"
import { FaSearch } from 'react-icons/fa'
import { useState } from 'react'
import {useNavigate} from 'react-router-dom'
function Search() {
const [input, setInput] = useState('');
const navigate = useNavigate();
const submitHandler = (e) => {
e.preventDefault();
navigate('/searched/' + input) (I GUESS THIS IS WHAT IS NOT WORKING)
};
return (
<FormStyle onSubmit={submitHandler}>
<div>
<FaSearch></FaSearch>
<input onChange={(e) => setInput(e.target.value)} type="text" value={input}/>
</div>
<h1>{input}</h1>
</FormStyle>
)
}
const FormStyle = styled.div`
margin: 0 20rem;
div{
width: 100%;
position: relative;
}
input{
border: none;
background: linear-gradient(35deg, #494949, #313131);
border-radius: 1rem;
outline: none;
font-size: 1.5rem;
padding: 1rem 3rem;
color: white;
width: 100%;
}
svg{
position: absolute;
top: 50%;
left: 0%;
transform: translate(100%, -50%);
color: white;
}
`
export default Search
Pages
import Home from "./Home"
import { Route, Routes } from 'react-router-dom'
import Cuisine from "./Cuisine"
import Searched from "./Searched"
function Pages() {
return (
<Routes>
<Route path='/' element={<Home/>} />
<Route path='/cuisine/:type' element={<Cuisine/>} />
<Route path='/searched/:search' element={<Searched/>} />
</Routes>
)
}
export default Pages
The FormStyle component is a styled div element instead of a form element, so the onSubmit handler is meaningless on the div. To resolve you should use the form element so the form submission works as you are expecting.
Search.js Example:
import styled from "styled-components";
import { FaSearch } from "react-icons/fa";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
function Search() {
const [input, setInput] = useState("");
const navigate = useNavigate();
const submitHandler = (e) => {
e.preventDefault();
navigate("/searched/" + input);
};
return (
<FormStyle onSubmit={submitHandler}> // <-- (2) onSubmit works now
<div>
<FaSearch></FaSearch>
<input
onChange={(e) => setInput(e.target.value)}
type="text"
value={input}
/>
</div>
<h1>{input}</h1>
</FormStyle>
);
}
const FormStyle = styled.form` // <-- (1) switch to form element
margin: 0 20rem;
div {
width: 100%;
position: relative;
}
input {
border: none;
background: linear-gradient(35deg, #494949, #313131);
border-radius: 1rem;
outline: none;
font-size: 1.5rem;
padding: 1rem 3rem;
color: white;
width: 100%;
}
svg {
position: absolute;
top: 50%;
left: 0%;
transform: translate(100%, -50%);
color: white;
}
`;
export default Search;
We are using react.
I want to create a Portal component and enclose a Drawer component in the Portal component.
I have created a Poratl component, but the following error occurs.
I don't know how to solve this problem.
errorMesssage
_react.default.createPortal is not a function
code
import ReactDOM from "react";
import createPortal from "react-dom";
export const Portal = ({ children }) => {
const el = document.getElementById("portal");
return ReactDOM.createPortal(children, el);
};
import React from "react";
import styled from "styled-components";
import { Portal } from "./Portal";
type Props = {
isOpen: boolean;
children: React.ReactNode;
onClose: () => void;
};
export const Drawer = (props: Props) => {
return (
<Portal>
<Overlay isOpen={props.isOpen} onClick={props.onClose} />
<DrawerModal {...props}>{props.children}</DrawerModal>
</Portal>
);
};
const DrawerModal = styled.div<Props>`
position: absolute;
overflow: scroll;
top: 0;
right: 0;
height: 100vh;
width: ${({ isOpen }: Props) => (isOpen ? 400 : 0)}px;
background: #ffffff;
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.25);
transition: 200ms cubic-bezier(0.25, 0.1, 0.24, 1);
`;
const Overlay = styled.div<{ isOpen: boolean }>`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: ${(props: { isOpen: boolean }) => (props.isOpen ? 0 : -1)};
`;
Try import ReactDOM from 'react-dom'; instead of import ReactDOM from 'react';
import ReactDOM from 'react-dom'; // Update this line
export const Portal = ({ children }) => {
const el = document.getElementById("portal");
return ReactDOM.createPortal(children, el);
};
I would like to give Border-bottom as a function. What should I do?
This is the code that Border-bottom should appear.๐
import React from "react";
import { Header } from "../BaseLabel";
import { Link, withRouter } from "react-router-dom";
const Header = ({ location: { pathname } }) => {
const getStyle = (path) => {
return {
color: pathname === path ? "#191919" : "#B6B6B6",
borderBottom: pathname === path ? "#191919" : null,
};
};
return (
<>
<ShapMenu>
<ShapLinks to="/covid19" style={getStyle("/covid19")}> //Link
<Header title="์ฝ๋ก๋19" current={pathname === "/covid19"} />
</ShapLinks>
</ShapMenu>
</>
);
}
This is Header Styled-components๐
const ShapMenu = styled.div`
display: flex;
box-sizing: content-box;
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
scroll-behavior: smooth;
scrollbar-color: inherit;
cursor: pointer;
`;
const ShapLinks = styled(Link)``;
This is a reusable component code. This code is not only used on this screen because it is a reuse code.๐
import PropTypes from "prop-types";
import styled from "styled-components";
import React from "react";
export const Header = ({ title, children }) => {
return (
<>
<Title>{title}</Title>
<Items>{children}</Items>
</>
);
};
Header.propTypes = {
title: PropTypes.node,
children: PropTypes.object,
};
const Items = styled.div``;
const Title = styled.div`
margin-right: 14px;
font-size: 20px;
`;
This is the style property that I want to give to the title.๐
border-bottom: 2px solid
${(props) => (props.current ? "#191919" : "transparent")};
transition: border-bottom 0.5s ease-in-out;
The CSS styled rules appear to be correct. You should pass the current prop from Header to Title.
const Header = ({ current, title, children }) => { // <-- destructure current
return (
<>
<Title current={current}>{title}</Title> // <-- pass current prop
<Items>{children}</Items>
</>
);
};
Header.propTypes = {
children: PropTypes.object,
current: PropTypes.bool,
title: PropTypes.node
};
const Title = styled.div`
margin-right: 14px;
font-size: 20px;
border-bottom: 2px solid
${(props) => (props.current ? "#191919" : "transparent")}; // <-- use current prop
transition: border-bottom 0.5s ease-in-out;
`;
I have a react-spring animation that consists in make a component appear while sliding. But during this animation, the component change its margin top before going back to normal. How to fix this?
Here is the code & a sandbox:
import React, { useEffect, useMemo, useState } from "react";
import styled, { css, keyframes } from "styled-components";
import { animated, useTransition } from "react-spring";
const Tabs = styled.div`
display: flex;
margin-bottom: 12px;
`;
const Tab = styled.button<{ active: boolean }>`
margin: 0 4px;
border-bottom: 1px solid transparent;
font-weight: ${({ active }) => active && 600};
cursor: pointer;
background: transparent;
border: 0;
&:focus {
outline: none !important;
}
`;
export default function Inbox() {
const [tab, setTab] = useState(0);
const transitions = useTransition(tab, (p) => p, {
from: { opacity: 0, transform: "translate3d(100%,0,0)" },
enter: { opacity: 1, transform: "translate3d(0%,0,0)" },
leave: { opacity: 0, transform: "translate3d(-50%,0,0)" }
});
const getList = (name: string) => <div>{name}</div>;
const pages = [
({ style }) => (
<animated.div style={style}>{getList("test 1")}</animated.div>
),
({ style }) => (
<animated.div style={style}>{getList("test 2")}</animated.div>
)
];
return (
<>
<Tabs>
<Tab onClick={() => setTab(0)} active={tab === 0}>
Unread
</Tab>
<Tab onClick={() => setTab(1)} active={tab === 1}>
All
</Tab>
</Tabs>
{transitions.map(({ item, props, key }) => {
const Page = pages[item];
return <Page key={key} style={props} />;
})}
</>
);
}
The sandbox: https://codesandbox.io/s/amazing-ride-hwbv2?file=/src/App.tsx
It is not the margin top you see. It is the old component moving to the left side and changing opacity, but it is still there. Finally it is unmounted in that moment the new component is taking its vertical place. Most of the time we use absolute positionig that the old and new components are on top of each other. This way there is no bump at the unmount. Something like this:
from: { opacity: 0, transform: "translate3d(100%,0,0)", position: "absolute" },