Reactjs/Nextjs onClick event not working for external component - javascript

I have a main page, with a button component i create to reuse in my project. My issue is when i add the onClick event to my external component, the click event is not working, but is i create the same button inside my main page, the click event works just fine
Button Component
import React from "react";
import styled from "styled-components";
const BigButton = (props): JSX.Element => {
return <>{props.red ? <BigBtn red={props.red}>{props.val}</BigBtn> : <BigBtn>{props.val}</BigBtn>}</>;
};
export default BigButton;
const BigBtn = styled.button`
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #f5f5f5;
width: 78px;
height: 30px;
background: ${(props) => (props.red ? "#BD2129" : "#2e3034")};
border: ${(props) => (props.red ? "initial" : "1px solid #494b4f")};
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
`;
This works on main page
<button onClick={buttonClose}>Close</button>
Button Component on main page - This doesn't work on main page
<BigButton val="Cancel" onClick={handleClose} />
Close function
const handleClose = (e) => {
e.preventDefault();
props.onClose();
};

Your component does not look correct. You do not have button inside the component and onClick event. You should update like this
import React from "react";
import styled from "styled-components";
const BigButton = (props): JSX.Element => {
const handleClick = () => {
props.onClick()
}
return <>{props.red ?
<BigBtn red={props.red}>
<button onClick={handleClick}>
{props.val}
</button>
</BigBtn> :
<BigBtn>
<button onClick={handleClick}>
{props.val}
</button>
</BigBtn>}
</>
};
export default BigButton;
const BigBtn = styled.button`
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #f5f5f5;
width: 78px;
height: 30px;
background: ${(props) => (props.red ? "#BD2129" : "#2e3034")};
border: ${(props) => (props.red ? "initial" : "1px solid #494b4f")};
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
`;

Related

React js app sidebar selected item not highlighted

I created this react app with a side bar, and it looks pretty good, but one thing I can't figure out is how to mark the item (page link) that was clicked.
Here is the code, I added activeClassName="selected" to the SidebarLink const, and added it to the styled-component, but it's not working.
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const SidebarLink = styled(Link)`
/*border: 3px solid green;*/
display: flex;
color: #e1e9fc;
justify-content: space-between;
align-items: center;
padding: 20px;
list-style: none;
height: 60px;
text-decoration: none;
font-size: 18px;
&:hover {
background: #252831;
border-left: 4px solid #ffbc2e;
cursor: pointer;
}
.selected {
color: red;
}
`;
const SidebarLabel = styled.span`
margin-left: 16px;
`;
/* Used for Training dropdown */
const DropdownLink = styled(Link)`
/*border: 3px solid red;*/
background: #252831;
height: 60px;
padding-left: 3rem;
display: flex;
align-items: center;
text-decoration: none;
color: #f5f5f5;
font-size: 16px;
font-weight: bold;
&:hover {
background: #48526F;
border-left: 4px solid #ffbc2e;
cursor: pointer;
}
`;
const SubMenu = ({ item }) => {
const [subnav, setSubnav] = useState(false);
const showSubnav = () => setSubnav(!subnav);
return (
<>
<SidebarLink to={item.path} onClick={item.subNav && showSubnav} activeClassName="selected" exact>
<div className="sidebarTextContainer">
<div className="sidebarIcon">{item.icon}</div>
<div className="sidebarText"><SidebarLabel>{item.title}</SidebarLabel></div>
</div>
<div>
{item.subNav && subnav
? item.iconOpened
: item.subNav
? item.iconClosed
: null}
</div>
</SidebarLink>
{subnav &&
item.subNav.map((item, index) => {
return (
<DropdownLink to={item.path} key={index}>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</DropdownLink>
);
})}
</>
);
};
export default SubMenu;
Anyone got any idea please on how to fix this?
Even though you're asking about remembering which item was clicked, I think what you actually want is that the link corresponding to the current page be highlighted as active.
If you're using react-router v6, you should use NavLink instead of Link (docs).
A <NavLink> is a special kind of <Link> that knows whether or not it is "active".
By default, an active class is added to a <NavLink> component when it is active.
You should update the imported component and remove the activeClassName.
const SidebarLink = styled(NavLink)`
// ...
&.active {
color: red;
}
`
// ...
<SidebarLink to={item.path} onClick={item.subNav && showSubnav} exact>
...
</SidebarLink>
If you're using react-router v5, then you should also use NavLink but you have to pass the activeClassName, like you do in your example. In this case you simply need to substitute Link for NavLink.
Also note that you nested CSS syntax is incorrect. It is applied to the children of SidebarLink, not on SidebarLink itself. You'd want to replace .selected { color: red; } with &.selected { color: red }.

How can I center text in a button component using styled-components?

I have made a button component using styled-components like below.
And I am using this button component in another component, even made a parent component to the button component, hoping that if the parent component has "text-align: center" attribute, it'd center texts in my button, which turned out not... maybe I did not do it right?
Below is my code for Button.js
import React from "react";
import styled, { css } from "styled-components";
const Container = styled.div`
text-align: center;
`;
const colorStyles = css`
${(props) =>
props.color === "blue" &&
css`
background: #588ced;
color: #ffffff;
&:hover {
background: #3866bc;
}
`}
${(props) =>
props.color === "gray" &&
css`
background: #eeeef2;
color: #65636a;
&:hover {
background: #dbdadf;
}
`}
`;
const sizeStyles = css`
${(props) =>
props.size === "normal" &&
css`
width: 181px;
height: 48px;
`}
${(props) =>
props.size === "small" &&
css`
width: 100px;
height: 40px;
`}
`;
const shapeStyles = css`
${(props) =>
props.shape === "round" &&
css`
border-radius: 50px;
`}
${(props) =>
props.shape === "squared" &&
css`
border-radius: 10px;
`}
`;
const StyledButton = styled.button`
/* common styles */
display: flex;
outline: none;
border: none;
font-family: Roboto;
font-style: normal;
font-weight: bold;
font-size: 16px;
line-height: 19px;
cursor: pointer;
align-items: center;
text-align: center;
/* sizes */
${sizeStyles}
/* colors */
${colorStyles}
/* shapes */
${shapeStyles}
/* etc */
// & + & {
// margin-left: 1rem;
// }
`;
function Button({ children, color, size, shape, ...rest }) {
return (
<Container>
<div>
<StyledButton color={color} size={size} shape={shape} {...rest}>
{children}
</StyledButton>
</div>
</Container>
);
}
export default Button;
AND this is a part from the other component where the button component is used.
import styles from "./Page.module.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import Button from "../button/Button";
export default function Page({ data, setModalVisibleState }) {
const addButtonHandler = () => {
setModalVisibleState(true);
};
return (
<div className={styles.container}>
<div className={styles.titleBox}>
<FontAwesomeIcon icon={faGraduationCap} />
<p>{data.title}</p>
<div className={styles.buttonWrap}>
<Button size="normal" shape="squared" color="blue" onClick={addButtonHandler}>{data.button}</Button>
</div>
</div>
//...and more...
And under "./Page.module.css" I have done this.
.buttonWrap {
display: flex;
text-align: center;
align-items: center;
}
This is how my buttons look right now. Before I used text-align and align-items attributes the text was located on the upper left side of the button. Is there an way to center texts in the button?
I am wondering if the purple box on the right has anything to do with centering the text..
Thank you very much in advance for reading and kindly answering my question. :)
Just add: justify-content: center; Aligning Items in a Flex Container
To center our box we use the align-items property to align our item on the cross axis, which in this case is the block axis running vertically. We use justify-content to align the item on the main axis, which in this case the inline axis running horizontally.
DEMO
button{
display: flex;
outline: none;
border: none;
font-family: Roboto;
font-style: normal;
font-weight: bold;
font-size: 16px;
line-height: 19px;
cursor: pointer;
align-items: center;
text-align: center;
border-radius: 50px;
background: #588ced;
color: #ffffff;
width:189px;
height:48px;
}
<button>button</button>
<button style="justify-content: center;">button</button>

typescript props type for styled input component

i am new to TS and I have been asked to move existing JS code base to ts. I have styled component which looks like this (style.js)
import styled from "styled-components";
export const Container = styled.div`
${({ flex }) => flex && `flex: ${flex};`}
${({ flex }) => flex && `display: flex;`}
flex-direction: column;
`;
export const Label = styled.label`
display: inline-block;
color: #95aac9;
font-size: 12px;
`;
export const DefaultInput = styled.input`
border: 1px solid #dfe0eb;
background-color: transparent;
padding: 10px 12px;
border-radius: 4px;
height: 35px;
color: #888888;
font-weight: 500;
&:focus {
outline: none;
}
`;
export const GrayInput = styled.input`
border: none;
background-color: #ebf2fb;
border: 1px solid #e1e9f5;
border-radius: 5px;
padding: 5px 10px;
&:focus {
outline: none;
}
`;
I was writting types for the file where I am importing this
import React from "react";
import Label from "components/atoms/Label";
import { DefaultInput, GrayInput, NormalInput, Container } from "./styles";
export default function Input({flex, theme, ...props}:{flex:string, theme}) {
return (
<Container flex={props.flex}>
{props.label && <Label>{props.label}</Label>}
{(!props.theme || props.theme === "light") && <DefaultInput {...props} />}
{props.theme === "gray" && <GrayInput {...props} />}
{props.theme === "normal" && <NormalInput {...props} />}
</Container>
);
}
but I can't figure the type for the {...props] in export default function Input({flex, theme, ...props}:{flex:string, theme}) { and how to write it
I think this should do the work:
export default function Input({flex, theme, ...props}:{flex:string; theme: string; [rest: string]: any })
You should also use flex and theme variables directly instead of props.flex and props.theme, because you already destructured them from the passed object (props) and therefore they are not available(defined) there anymore.

Need single block for all react toast messages either failure or success in react toastify

By default in react we receive react-toastify like in attached image below.
But now project demands all messages in one block or div, with one cross button and notification count on top with one timer and with a scrollbar to see all messages.
I have tried with redux but now need to remove redux. Please see if anyone can help in this by using only react. Or if react-toastify giving this functionality.
import React, { useContext, useState, useReducer } from 'react';
import { ToastContext } from './App';
import { closeToast, showToast } from '../store/toast/action';
import { connect } from 'react-redux';
const ToastContainer = styled.div`
width: 350px;
position: absolute;
top: 100px;
right: 16px;
background: #c12335;
z-index: 9999;
color: white;
padding: 12px;
display: ${props => (props.hide ? 'none' : '')};
`;
const ViewMore = styled.div`
text-align: right;
margin-top: 4px;
text-decoration: underline;
cursor: pointer;
`;
const CloseButton = styled.div`
color: #fff;
font-weight: bold;
font-size: 14px;
background: transparent;
outline: none;
border: none;
padding: 0;
cursor: pointer;
text-align: right;
`;
const ToastList = styled.div`
margin-top: 8px;
max-height: 160px;
overflow: auto;
`;
const CustomToast = props => {
const { toastList, closeToast } = props;
const [showAll, setShowAll] = useState(false);
const toastCount = toastList.length;
const list = showAll ? toastList : toastList.slice(0, 5);
return (
<ToastContainer hide={toastCount == 0}>
<div>
<CloseButton
onClick={() => {
setShowAll(false);
closeToast();
}}
>
x
</CloseButton>
{toastCount} New Notifications
</div>
<PerfectScrollbar>
<ToastList>
{list.map((toastContent, index) => (
<div key={'toast-' + index}>
<span class="fa fa-exclamation-triangle"></span> {toastContent}
</div>
))}
</ToastList>
</PerfectScrollbar>
{toastList.length > 5 && !showAll && (
<ViewMore onClick={() => setShowAll(true)}>View More....</ViewMore>
)}
</ToastContainer>
);
};
const mapStateToProps = state => {
return {
toastList: state.toast.toastList,
};
};
const mapDispatchToProps = {
showToast,
closeToast,
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(CustomToast);

Change inline styles to styled-components

I have this code:
import React, {useState} from 'react';
import PropTypes from 'prop-types';
import styled from "styled-components";
import ArrowTemplate from "./ArrowTemplate";
const AccordionBtn = styled.button`
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
display: flex;
align-items: center;
border: none;
outline: none;
transition: background-color 0.6s ease;
:hover,
:focus,
:active {
background-color: #ccc;
}
`;
const AccordionTitle = styled.p`
font-family: "Open Sans", sans-serif;
font-weight: 600;
font-size: 14px;
`;
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
const AccordionText = styled.div`
font-family: "Open Sans", sans-serif;
font-weight: 400;
font-size: 14px;
padding: 18px;
`;
const AccordionSection = styled.div`
display: flex;
flex-direction: column;
`;
Accordion.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.node.isRequired,
id: PropTypes.string.isRequired,
};
function Accordion(props) {
const [isAccordionExpanded, setIsAccordionExpanded] = useState(false);
const toggleAccordion = () => {
setIsAccordionExpanded(!isAccordionExpanded);
};
return (
<AccordionSection>
<AccordionBtn onClick={toggleAccordion}>
<AccordionTitle>
{props.title}
</AccordionTitle>
<ArrowTemplate
color={'black'}
direction={isAccordionExpanded === true ? 'up' : 'down'}
onClick={toggleAccordion}
/>
</AccordionBtn>
<AccordionContent
style={{height: isAccordionExpanded === true ? "100px" : "0"}}
>
<AccordionText>
{props.content}
</AccordionText>
</AccordionContent>
</AccordionSection>
);
}
export default Accordion;
What this code does, is extends the accordeon on click. Preety simple. But now, I want to move this height:
<AccordionContent
style={{height: isAccordionExpanded === true ? "100px" : "0"}}
>
here:
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
The problem is I need to use state, and if I declare it in the function, it will get re-rendered, and not run the animation. How can I pass the state to the styled-component?
You can just pass the isAccordionExpanded directly to the styled component. Change your style from this:
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
to include this
height: ${({ isAccordionExpanded }) => (isAccordionExpanded ? "100px" : "0"};
Another way to do animation in React is using React Transition Group, Quite good

Categories