I want to create a react Portal component - reactjs

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);
};

Related

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>
);
}

Property 'isOpen' does not exist on type 'IntrinsicAttributes. ts(2322)

I'm creating a navbar, but I'm not able to show the Wrapper menu when clicked.
The error occurs when inserting isOpen = {isOpen} in <Sidebar />
ERROR
Type '{ isOpen: boolean; toggle: () => void; }' is not assignable to type 'IntrinsicAttribute. Property 'isOpen' does not exist on type 'IntrinsicAttributes. ts(2322)
pages/Home.tsx
import React, { useState } from 'react';
...
import Navbar from '../../components/Navbar';
import Sidebar from '../../components/Sidebar';
interface SidebarProps {
isOpen?: boolean;
}
const Home: React.FC<SidebarProps> = () => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => {
setIsOpen(!isOpen);
};
return (
<>
<Sidebar isOpen={isOpen} toggle={toggle} /> <----------- ERRO
<Navbar />
<HomePage>
...
</HomePage>
components/Sidebar/styles.tsx
import { FaTimes } from 'react-icons/fa';
import { Link as LinkScroll } from 'react-scroll';
import styled from 'styled-components';
interface SidebarProps {
isOpen?: boolean;
}
export const SidebarContainer = styled.aside<SidebarProps>`
position: fixed;
z-index: 999;
width: 100%;
height: 100%;
background: #010311;
display: grid;
align-items: center;
top: 0;
left: 0;
transition: 0.4s ease-in-out;
opacity: ${({ isOpen }) => (isOpen ? '100%' : '0')};
top: ${({ isOpen }) => (isOpen ? '0' : '-100%')};
`;
I think in the styled component you should access the boolean isOpen in the following way:
opacity: ${props => props.isOpen ? '100%' : '0'}

How can React Js make a Border-botom as a function?

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;
`;

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