Alright I have a component called <TestButton />. Inside the <TestButton /> there are two Semantic UI React component, <Button /> and <Header>.
Basically, when the <Button> is clicked, it toggles display: none; to <Header>.
I want to check (I want to learn) on how to assert <Header>'s display: none; when <Button> is clicked.
TestButton.js
const TestButton = (props) => {
return (
<div id='test-button'>
<Header id='please-hide-me' size='huge'>Please Hide Me</Header>
<Button
onClick={
() => {
hiding = !hiding;
let findDOM = document.getElementById(searchForThisID);
if (findDOM) { findDOM.style.display = hiding ? 'none' : ''; }
return hiding;
}
}
>
Sample Toggle
</Button>
</div>
);
};
My unit test is based on How to test style for a React component attribute with Enzyme. It looks like this:
test(`
`, () => {
const wrapper = shallow(<TestButton />);
const button = wrapper.find('Button');
const header = wrapper.find('Header');
const headerStyle = header.get(0).style;
expect(headerStyle).to.have.property('display', '');
wrapper.find('Button').simulate('click');
expect(headerStyle).to.have.property('display', 'none');
}
);
But it has this error:
TypeError: Cannot read property 'have' of undefined
What should I do?
There are a few mistakes in your provided code:
You should not be using DOM element's style property because React does not manage it. Shift the hidden property into the state instead.
I believe headerStyle is a shallow copy of the style object. After you simulate click, it does not get updated. You will have to query the element again for the style object.
to.have.property is not valid Jest syntax. It should be toHaveProperty.
Please refer to the corrected code here. If you paste the following into create-react-app, it should just work.
app.js
import React, { Component } from 'react';
function Header(props) {
return <h1 style={props.style}>Header</h1>;
}
function Button(props) {
return <button onClick={props.onClick}>Click Me</button>;
}
export class TestButton extends React.Component {
constructor(props) {
super(props);
this.state = { hiding: false };
}
render() {
return (
<div>
<Header
id="please-hide-me"
style={{
display: this.state.hiding ? 'none' : '',
}}
>
Please Hide Me
</Header>
<Button
onClick={() => {
this.setState({
hiding: !this.state.hiding,
});
}}
>
Sample Toggle
</Button>
</div>
);
}
}
class App extends Component {
render() {
return (
<div className="App">
<TestButton />
</div>
);
}
}
export default App;
app.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
import { TestButton } from './App';
it('renders without crashing', () => {
const wrapper = shallow(<TestButton />);
expect(wrapper.find('Header').get(0).props.style).toHaveProperty(
'display',
'',
);
wrapper.find('Button').simulate('click');
expect(wrapper.find('Header').get(0).props.style).toHaveProperty(
'display',
'none',
);
});
Related
I'm not sure why my text area isn't rendering if someone could suggest what I'm missing or what I've done wrong I'd be forever grateful.
I'm not sure whether maybe I should try rending it in the App.js file.
I'm also sure I don't need to import the App.js file to the CardCheck.js because it's a child.
import "./App.css";
import React from "react";
import CardCheck from "./CardCheck";
class App extends React.Component() {
state = {
cardNumber: "",
};
handleChange = (event) => {
this.setState({ cardNumber: event.target.value });
};
handleClick = () => {
const { cardNumber } = this.state;
this.setState({
cardNumber: "",
});
};
render() {
const { cardNumber } = this.state;
return (
<div className="App">
<h1>Taken Yo Money</h1>
<CardCheck
cardNumber={cardNumber}
handleChange={this.handleChange}
handleClick={this.handleClick}
/>
</div>
);
}
}
export default App;
function CardCheck(props) {
const { cardNumber, handleChange, handleClick } = props;
return (
<div className="TweetInput">
<div className="bar-wrapper"></div>
<textarea onChange={handleChange} value={cardNumber}></textarea>
<footer>
<button onClick={handleClick}>Enter Card Details</button>
</footer>
</div>
);
}
export default CardCheck;
Remove the parentheses from class App extends React.Component(). It should be
class App extends React.Component {
//rest of the code
}
``
You need to replace class App extends React.Component() with class App extends Component {
The Component is imported from import React, { Component } from "react";
It should fix the rendering issue
Lets say there is 2 component header.js and layout.js
// header.js
import React from 'react';
class Header extends React.Component {
state = { showMenu: false }
toggleMenu = () => {
this.setState({
showMenu: !this.state.showMenu
})
}
render() {
const menuActive = this.state.showMenu ? 'active' : '';
const buttonActive = this.state.showMenu ? 'active' : '';
return (
<button className={`button ${buttonActive}`} onClick={this.toggleMenu}>
Some Button
</button>
<div className={`menu ${menuActive}`}>
...
</div>
)
}
}
export default Header
// layout.js
import React from 'react';
import Header from './header';
const Layout = (props) => {
<div className="foo"> // <==== need to add class from onClick event in header.js
<Header/>
<main>
{props.children}
</main>
</div>
}
export default Layout
motivation
As you can see I have managed to create onClick event in the header.js but in the other hand i also need to pass the event to layout.js so i can add some additional class. Maybe this can be done using props, unfortunately since i new in React i have no idea how to implement it into the code
I have a simple solution.
You can declare the state and function on the parent component.
in your code, you just declare them on Layout.
header.js
import React from 'react';
class Header extends React.Component {
/* state = { showMenu: false }
toggleMenu = () => {
this.setState({
showMenu: !this.state.showMenu
})
}*/
render() {
// const menuActive = this.state.showMenu ? 'active' : '';
// const buttonActive = this.state.showMenu ? 'active' : '';
const menuActive = this.props.showMenu ? 'active' : '';
const buttonActive = this.props.showMenu ? 'active' : '';
return (
<button className={`button ${buttonActive}`} onClick={this.props.toggle}>
Some Button
</button>
<div className={`menu ${menuActive}`}>
...
</div>
)
}
}
export default Header
layout.js
import React from 'react';
import Header from './header';
const Layout = (props) => {
const [showMenu, setShowMenu] = React.useState(false);
const toggle = () => setShowMenu(!showMenu);
return(
<div className={`foo ${showMenu? 'active':''}`}> // <==== need to add class from onClick event in header.js
<Header showMenu={showMenu} toggle={toggle}/>
<main>
{props.children}
</main>
</div>
)
}
export default Layout
And, there're some other options
i.e Redux, Mobx...
If you are going to build a complicated and huge application, you should use one of them.
You should control the state in the Layout component, and pass the toggleMenu callback as a prop to the Header component:
// header.js
import React from 'react';
const Header = ({ menuActive, toggleMenu}) => {
return (
<button className={`button ${menuActive}`} onClick={toggleMenu}>
Some Button
</button>
<div className={`menu ${menuActive}`}>
...
</div>
)
}
export default Header
// layout.js
import React from 'react';
import Header from './header';
class Layout extends React.Component {
state = { showMenu: false }
toggleMenu = () => {
this.setState({
showMenu: !this.state.showMenu
})
}
render() {
const menuActive = this.state.showMenu ? 'active' : '';
return (
<div className={`foo ${menuActive}`}>
<Header menuActive={menuActive} toggleMenu={this.toggleMenu} />
<main>
{this.props.children}
</main>
</div>
)
}
}
export default Layout
I am trying to display/hide one component which is ItemMain and which is imported to the main App component using button in another component which is NavLogoNew. I tried to do this in many different ways but it looks like the button doesn't know if it's clicked, when I change true/false manually it works. In web I found a lot of stuff about situations when only two components are involved, but nothing like this. My code:
App
import React from 'react';
import './App.css';
import { tsPropertySignature } from '#babel/types';
import { statement } from '#babel/template';
import NavBar from './../Components/Navigation/NavBar/NavBar.js';
import ItemMain from './../Components/Item/ItemMain/ItemMain.js';
import ItemList from './../Components/Item/ItemList/ItemList.js';
import NavButtonTop from './../Components/Navigation/NavButton/NavButtonTop/NavButtonTop.js';
import NavLogoNew from './../Components/Navigation/NavButton/NavButtonNew/NavLogoNew.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
visible: !this.visible
})
}
render() {
return (
<div className="App">
<NavBar />
{this.state.visible ? <ItemMain /> : null}
<ItemList />
<NavButtonTop name='UP'/>
</div>
);
}
}
export default App;
NavLogoNew:
import React from 'react';
import './NavLogoNew.css';
import ItemMain from './../../../Item/ItemMain/ItemMain.js'
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={this.props.click}
>
{this.props.name}
</button>
);
}
}
export default NavLogoNew;
Your handleClick function is lacking something
use !this.state.visible so change from the below
handleClick(){
this.setState({
visible: !this.visible
})
}
to
handleClick = () => {
this.setState({
visible: !this.state.visible
})
}
pass the handleClick function to the NavLogoNew as follows
<NavLogoNew onClick = {this.handleClick} />
inside of the NavLogoNew component you should invoke it as follows
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={() => this.props.onClick()}
>
{this.props.name}
</button>
);
}
}
i want to change state different js but i can not , i have a sidebar.js with react-burger-menu
i want to call and change toggleMenu state in header.js
When I click the menu link, i want to toggle react-burger-menu but different js. this is not working.
sidebar.js
import React from "react";
import PropTypes from "prop-types";
import { reveal as Menu } from "react-burger-menu";
import * as FontAwesome from "react-icons/lib/fa";
export default class SidebarMenu extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
}
handleStateChange (state) {
this.setState({menuOpen: state.isOpen})
}
closeMenu () {
this.setState({menuOpen: false})
}
toggleMenu () {
this.setState({menuOpen: !this.state.menuOpen})
}
render () {
return (
<div>
<Menu
isOpen={this.state.menuOpen}
onStateChange={(state) => this.handleStateChange(state)}
>
// menu content
</Menu>
</div>
</div>
)
}
}
header.js have link for react-burger-menu
import React from 'react';
import PropTypes from 'prop-types';
import SidebarMenu from "../SidebarMenu";
export default class Header_Video extends React.Component {
render() {
return (
<Container>
<Row>
<Col md={5} sm={12} xs={12} className="text-left mobile-right">
<div className="bar__module">
<a onClick={this.toggleMenu}>Menu</a>
</div>
</Col>
</Row>
</Container>
);
}
}
thanks for help
note: i have a app.js all files import. I want to run toggleMenu in header.js
app.js
const TemplateWrapper = ({ children }) => (
<div id="outer-container">
<SidebarMenu />
<main id="page-wrap" className="page-wrap">
<HeaderVideo /> {children()}
<Footer />
</main>
</div>
);
menuOpen should be in a parent state of both components.
Example:
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
}
closeMenu = () => {
this.setState({menuOpen: false})
}
toggleMenu = () => {
this.setState({menuOpen: !this.state.menuOpen})
}
render() {
return (
<div>
<SidebarMenu isMenuOpen={this.state.menuOpen} toggleMenu={this.toggleMenu} />
<Header toggleMenu={this.toggleMenu} />
</div>
)
}
}
You may have further errors beyond just this, but the glaring error to me is that toggleMenu() is not bound to the constructor.
https://reactjs.org/docs/handling-events.html
try:
import React from "react";
import PropTypes from "prop-types";
import { reveal as Menu } from "react-burger-menu";
import * as FontAwesome from "react-icons/lib/fa";
export default class SidebarMenu extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
this.toggleMenu = this.toggleMenu.bind(this);
// the above statement binds the function to the object constructor
}
handleStateChange (state) {
this.setState({menuOpen: state.isOpen})
}
closeMenu () {
this.setState({menuOpen: false})
}
toggleMenu () {
this.setState({menuOpen: !this.state.menuOpen})
}
render () {
return (
<div>
<Menu
isOpen={this.state.menuOpen}
onStateChange={(state) => this.handleStateChange(state)}
>
// menu content
</Menu>
</div>
</div>
)
}
}
You'll also want to use an HTML5 button tag instead of a link tag, the correct HTML semantic structure provides a bunch of underlying features and greatly improves accessibility out of the box.
Also, remove the arrow function and pass a reference to the function, not the returned value. This is so react doesn't call the function immediately but stores the function reference to execute upon the click event.
<button onClick={this.toggleMenu}>Menu</button>
// instead of
<a onClick={() => this.toggleMenu()}>Menu</a>
Hope this helps!
I'm building a search engine with React.js, where I can look for GIPHY gifs using their API. Everytime I type a word(any word), it always loads the same gifs and when I erase and write another word, the gifs don't update.
index.js:
import React from 'react'; //react library
import ReactDOM from 'react-dom'; //react DOM - to manipulate elements
import './index.css';
import SearchBar from './components/Search';
import GifList from './components/SelectedList';
class Root extends React.Component { //Component that will serve as the parent for the rest of the application.
constructor() {
super();
this.state = {
gifs: []
}
this.handleTermChange = this.handleTermChange.bind(this)
}
handleTermChange(term) {
console.log(term);
let url = 'http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io';
fetch(url).
then(response => response.json()).then((gifs) => {
console.log(gifs);
this.setState({
gifs: gifs
});
});
};
render() {
return (
<div>
<SearchBar onTermChange={this.handleTermChange} />
<GifList gifs={this.state.gifs} />
</div>
);
}
}
ReactDOM.render( <Root />, document.getElementById('root'));
search.js
import React, { PropTypes } from 'react'
import './Search.css'
class SearchBar extends React.Component {
onInputChange(term) {
this.props.onTermChange(term);
}
render() {
return (
<div className="search">
<input placeholder="Enter text to search for gifs!" onChange={event => this.onInputChange(event.target.value)} />
</div>
);
}
}
export default SearchBar;
Giflist:
import React from 'react';
import GifItem from './SelectedListItem';
const GifList = (props) => {
console.log(props.gifs);
const gifItems = props.gifs && props.gifs.data && props.gifs.data.map((image) => {
return <GifItem key={image.id} gif={image} />
});
return (
<div className="gif-list">{gifItems}</div>
);
};
export default GifList;
GifItem:
import React from 'react';
const GifItem = (image) => {
return (
<div className="gif-item">
<img src={image.gif.images.downsized.url} />
</div>
)
};
export default GifItem;
I can't seem to find where is the issue here. Is it because of this.handleTermChange = this.handleTermChange.bind(this) and there is no "update" state after?
Any help is welcome :) Thanks!
Its because, you are not putting the term value entered by user in the url, all the time you hit the api with static value term, here:
'http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io';
Replace ' by ' (tick), like this:
let url = `http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io`;
Check MDN Doc for more details about Template Literals.