Take the following React component. It renders some text and an accordion.
const AccountDetails = ({
accountNumber,
accountType,
accordionOpen,
accordionState,
productName,
toggle,
}) => {
if (accordionState) {
return (
<div className="transaction-card-account-info">
<div className={`icon ${accountTypeIcons[accountType]}`} />
<div className="account-details">
<div className="account-name">{productName}</div>
{accountNumber && <div>{`Account ${accountNumber}`}</div>}
</div>
</div>
);
} else {
return (
<div className="transaction-card-account-info">
<div className={`icon ${accountTypeIcons[accountType]}`} />
<div className="account-details">
<div className="account-name">
{productName}
<ToggleAccordionButton
onClick={toggle}
>
{accordionOpen ? <CloseAccordionIcon /> : <OpenAccordionIcon />}
</ToggleAccordionButton>
</div>
{accountNumber && <div>{`Account ${accountNumber}`}</div>}
</div>
</div>
);
}
};
AccountDetails.propTypes = AccountDetailsPropTypes;
export default AccountDetails;
`openAccordionIcon` renders the following HTML
`
<button type="button" class="sc-hBURRC jZGdVy">
<i class="sc-fotPbf bdlDTo fas fa-chevron-right"></i>
</button>
`
I'm trying to test that the className of 'fa-chevron-right' is present. I've looked at several Stackoverflow posts as well as documentation for React Testing Library, Enzyme, and Jest and nothing seems to work. Below is my test file.
describe('<AccountDetails />', () => {
let defaultProps;
beforeEach(() => {
defaultProps = {
accountNumber: '4571184999',
accountType: 'generic',
accordionOpen: true,
accordionState: false,
productName: 'Alex Credit',
toggle: PropTypes.func,
};
});
test('should render correctly', () => {
const { getAllByText } = render(<AccountDetails {...defaultProps} />);
expect(getAllByText('Alex Credit').length).toBe(1);
});
test('should call toggle correctly', () => {
// this test fails
const wrapper = render(<AccountDetails {...defaultProps} />);
expect(wrapper.getElementsByClassName("fa-chevron-right").length).toBe(1);
});
How can I test that the component renders fa-chevron-right?
You could use by adding jest-dom to your project.
It becomes:
expect(container).toHaveClass('fa-chevron-right');
I solved it by adding a data-testid='down' to my component. I don't love that I need to add an attribute just to test it, but it's the only way I could solve it. I used React's Testing-library.
return (
<div className="transaction-card-account-info">
<div className={`icon ${accountTypeIcons[accountType]}`} />
<div className="account-details">
<div className="account-name">
{productName}
<ToggleAccordionButton
onClick={toggle}
>
{accordionOpen ? <CloseAccordionIcon data-testid="down" /> : <OpenAccordionIcon />}
</ToggleAccordionButton>
</div>
{accountNumber && <div>{`Account ${accountNumber}`}</div>}
</div>
</div>
);
// test
test('should call toggle correctly', () => {
const { container } = render(<AccountDetails {...defaultProps} />);
expect(getByTestId(container, 'down')).toBeTruthy();
});
Related
on my Project I have a banner on top of my site with 2 buttons. when I click the button profile I want it to change the css style of a div in another component.
this is my code for the banner:
import Profile from "./Profile";
function Banner() {
const invis=false;
return (
<div className="banner">
<span className="bannerbtnsettings">
<button className="btnbannersettings">Settings</button>
</span>
<span className="bannerbtnprofile">
<button className="btnbannerprofile" onClick={Profile.changeStyle}>Profile</button>
</span>
</div>
);
}
export default Banner;
this is my code for the div in the other component:
import "../index.css";
import React, { useState } from "react";
const Profile = () => {
const [style, setStyle] = useState("profile-hidden");
const changeStyle = () => {
console.log("you just clicked");
setStyle("profile-displayed");
};
return (
<div>
<div className={style}> hellllo</div>
</div>
);
};
export default Profile;
I can only find information about this with parent-child components.
They said I should use a usestate import but I can't seem to get it working. what's the proper way to do this?
All you need is lift your state to parent component, if you have a long trip to your common ancestor you can try to use a context. Attached a working example. Hope it helps!
const Banner = ({ onClickHandler }) => {
return (
<div className="banner">
<span className="bannerbtnsettings">
<button className="btnbannersettings">Settings</button>
</span>
<span className="bannerbtnprofile">
<button className="btnbannerprofile" onClick={() => onClickHandler()}>Profile</button>
</span>
</div>
)}
const Profile = ({ style }) => {
return (
<div>
<div className={style}>I'm your profile :)</div>
</div>
);
};
const App = () => {
// We lift the state
const [style, setStyle] = React.useState("profile-hidden");
const profileHandler = () => {
setStyle(style === 'profile-hidden'
? 'profile-displayed'
: 'profile-hidden')
}
return(
<div>
<Banner onClickHandler={profileHandler} />
<Profile style={style} />
</div>
)
}
// Render
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
.profile-hidden {
display: none;
}
.profile-displayed {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
<div id="root"></div>
You cannot use this syntax for React Components COMPONENT.method
, in your case onClick={Profile.changeStyle} !
Instead you should make Banner parent component and use Profile component as child inside it or vise versa !
then You should pass the state style as props so then you will be able to use its value.
your code should look like this :
function Banner() {
const [style, setStyle] = useState("profile-hidden");
const changeStyle = () => {
console.log("you just clicked");
setStyle("profile-displayed");
};
return (
<div className="banner">
<span className="bannerbtnsettings">
<button className="btnbannersettings">Settings</button>
</span>
<span className="bannerbtnprofile">
<button className="btnbannerprofile" onClick={changeStyle}>Profile</button>
</span>
<Profile style={style} />
</div>
);
}
export default Banner;
and your Profile component :
const Profile = (props) => {
return (
<div>
<div className={props.style}> hellllo</div>
</div>
)
}
I have two React components
class App extends React.Component {
render() {
return (
<div id="appWrapper">
<ConfigureWindow />
<button id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
}
const ConfigureWindow = () => (
<div id="configureWindowWrapper">
<div id="configureWindow">
<section id="addCitySection">TODO: adding a city</section>
<div id="verticalLine"></div>
<section id="listOfCities">
<header>
<h1>Available cities</h1>
<div id="closeConfigureWindowWrapper">
<img src="..\src\images\exit.png" id="closeConfigureWindow" alt="" />
</div>
</header>
<section id="availableCities"></section>
</section>
</div>
</div>
);
I want "ConfigureWindow" to be shown when "configureClocksButton". I tried to execute it with props, state and a function but got errors. It also would be nice if you explain me how to create new React components with React functions?
You probably want to use the React.JS event onClick (https://reactjs.org/docs/handling-events.html), and a state to store the action. To create a function component, you just have to return the JSX you want to render, and use hooks (https://reactjs.org/docs/hooks-intro.html) and then do a conditional rendering (https://reactjs.org/docs/conditional-rendering.html):
const App = () => {
const [toggleConfiguration, setToggleConfiguration] = useState(false)
return (
<div id="appWrapper">
{toggleConfiguration && <ConfigureWindow />}
<button onClick{() => setToggleConfiguration(true)} id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
It's a bit difficult to understand your post, but I gather you want to click the button with id="configureClocksButton" and conditionally render the ConfigureWindow component.
You can accomplish this with some boolean state, a click handler to toggle the state, and some conditional rendering.
class App extends React.Component {
this.state = {
showConfigureWindow: false,
}
toggleShowConfigureWindow = () => this.setState(prevState => ({
showConfigureWindow: !prevState.showConfigureWindow,
}))
render() {
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={this.toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}
}
A function component equivalent:
const App = () => {
const [showConfigureWindow, setShowConfigureWindow] = React.useState(false);
const toggleShowConfigureWindow = () => setShowConfigureWindow(show => !show);
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}
I am new to ReactJs and I'm developing a social media web application. Here I have template where I have to implement a Three-dot more options menu. I tried using Bootstrap menu and react Bootstrap component menu. Both didn't work for me. What is the best way to implement this feature without using a library?
I did till toggling the menu. But on click, all the menus toggle altogether. I am not able do the toggling individually.
Here's the piece of code I did:
post.jsx
class UserPost extends Component {
state = {
overFlowMenuActive: false
};
toggleOverflowMenu = () => {
this.setState((prevState) => ({ overFlowMenuActive:
!prevState.overFlowMenuActive }));
};
closeOverflowMenu = () => {
this.setState({ overFlowMenuActive: false });
};
render() {
return (
<React.Fragment>
{this.props.posts.map((post, index) =>(
<div>
<div tabIndex='0' onBlur={this.closeOverflowMenu}>
<img src={require('../../assets/images/more.svg')} alt='' onClick={this.toggleOverflowMenu}/>
</div>
<MoreBtn options={this.state.options} overFlowMenuActive={this.state.overFlowMenuActive} />
</div>
))}
</React.Fragment>
);
}
MoreBtn.jsx
<div className={`${classes['popup-more']} ${this.props.overFlowMenuActive
? classes.expand
: classes.collapse}`}>
{this.props.options.map((option, index) => (
<div key={index}>
<img src={option.url} alt='' />
<p>{option.name}</p>
</div>
))}
</div>
You are maintaining only a single state for all UserPosts
To have each of these toggle seperately, these states should be moved into the component.
class SinglePost extends Component {
state = {
overFlowMenuActive: false
};
toggleOverflowMenu = () => {
this.setState((prevState) => ({ overFlowMenuActive:
!prevState.overFlowMenuActive }));
};
closeOverflowMenu = () => {
this.setState({ overFlowMenuActive: false });
};
render() {
return (
<div>
<div tabIndex='0' onBlur={this.closeOverflowMenu}>
<img src={require('../../assets/images/more.svg')} alt='' onClick={this.toggleOverflowMenu}/>
</div>
<MoreBtn options={this.state.options} overFlowMenuActive={this.state.overFlowMenuActive} />
</div>
);
}
class UserPost extends Component {
render() {
return (
<React.Fragment>
{this.props.posts.map((post, index) =>(
<SinglePost post={post} />
))}
</React.Fragment>
);
}
This way, the button for only one component is toggled at a time
I have this presentational component that includes a LoginForm which uses redux connect... when i try to see if the component is there by using wrapper.debug(), instead of the component i see: <Connect(Component) />
What do i have to do in order to see the actual LoginForm and test its length?
This is my component:
const LoginSection = ({ intl }) => (
<div className={styles.loginSection}>
<div className={styles.wrapper}>
<div className={styles.form}>
<p className={styles.title}>
<FormattedMessage
id="Dashboard.login.title"
defaultMessage="Login to an account"
/>
</p>
<LoginForm />
<p className={styles.createAccountWrapper}>
<span className={styles.dontHaveAccount}>
<FormattedMessage
id="Dashboard.login.subline"
defaultMessage="Dont have an account?"
/>
</span>
<a
className={styles.createAccount}
href={`${localeToDomainMap[intl.locale]}/register`}
>
<span className={styles.createOneHere}>
<FormattedMessage
id="Dashboard.login.createAccount"
defaultMessage="Create one here."
/>
</span>
</a>
</p>
</div>
</div>
</div>
);
and this is my test:
const setup = (newProps) => {
const props = {};
const wrapper = shallow(<LoginSection {...props} />);
return {
wrapper,
props,
};
};
describe('LoginSection', () => {
test('that it contains LoginForm', () => {
const { wrapper } = setup();
console.log(wrapper.debug());
expect(wrapper.find('.loginFrom')).toEqual(1);
});
});
and this is the result of wrapper.debug():
<div className="loginSection">
... other stuff here ...
<Connect(Component) />
... other stuff here ...
</div>
I am using React JSX. I have a div with className="shadow" as shown below.
<div className="main">
<div className="shadow" style={{backgroundColor: "#FFFFFF"}}>
<div id="wrapper">
Hello
</div>
</div>
</div>
Based on a certain condition being true or false, I want to remove the div with className="shadow", but want to keep every div including the div with id="wrapper" intact. Something like unwrap() method of jQuery.
Something to the effect of what is written below, but without so many lines of code.
if ( currentPage==="login") {
<div className="main">
<div id="wrapper">
Hello
</div>
</div>
}
else {
<div className="main">
<div className="shadow" style={{backgroundColor: "#FFFFFF"}}>
<div id="wrapper">
Hello
</div>
</div>
</div>
}
I checked React.js: Wrapping one component into another and How to pass in a react component into another react component to transclude the first component's content?, but didn't get what I am looking for.
Maybe what can help you is to change the className based on the page you are on, because what you try to do would be better using react-router to display different components based on the path you are on.
Use something similar to this code, I hope I can help you.
const App = React.createClass({
changePage(nextPage) {
this.setState({ page: nextPage })
},
getInitialState() {
return({
page: 'Login'
})
},
render() {
return(
<div className="main">
<div className={ this.state.page === 'Login' ? 'shadow' : '' }>
<div id="wrapper">
Hello from { this.state.page } page.
</div>
<button onClick={ this.changePage.bind(null, 'Login') }>Go to Login page.</button>
<button onClick={ this.changePage.bind(null, 'Home') }>Go to Home page.</button>
</div>
</div>
)
}
})
ReactDOM.render(<App />, document.getElementById('app'))
div.shadow{ background-color: #000000; color: #ffffff}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'></div>
This is a great use case for Higher Order Components!
const HOC = (currentPage, Shadow, Wrapper) => (
() => <div className="main">
{
currentPage === 'login'
? <Shadow {...this.props} />
: <Shadow {...this.props}><Wrapper {...this.props}>{this.props.children}</Wrapper></Shadow>
}
</div>
)
Usage:
render () {
const Shadow = props => <div className="shadow" style={{backgroundColor: '#FFFFFF'}}>{props.children}</div>
const Wrapper = props => <div id="wrapper">Hello</div>
const Example = HOC(
currentPage,
Shadow,
Wrapper
)
return (
<Example />
)
}
Update:
To render the children of <Wrapper />, use {this.props.children} and use the class syntax in HOC:
const HOC = (currentPage, Shadow, Wrapper) => (
class extends Component {
render () {
return (
<div className="main">
{
currentPage === 'login'
? <Shadow />
: <Shadow><Wrapper>{this.props.children}</Wrapper></Shadow>
}
</div>
)
}
}
)
If you needed to capture props on <Shadow /> and <Wrapper />, then do something like the following. Note: I don't think you can pass props into normal DOM elements like <div> tags. But if they were other components with a Capital starting letter, then I believe passing props through with {...this.props} would work nicely.
const HOC = (currentPage, Shadow, Wrapper) => (
class extends Component {
render () {
return (
<div className="main">
{
currentPage === 'login'
? <Shadow {...this.props} />
: <Shadow {...this.props}><Wrapper {...this.props}>{this.props.children}</Wrapper></Shadow>
}
</div>
)
}
}
)