I'm trying to simulate a scroll down. After that, the state of isVisible should change to true and the Button component should be displayed.
That's working correctly, but I can't simulate it in the test.
Main Component Below
const ScrollToTop = (props: ScrollToTopProps) => {
const {
message = 'Back To Top',
visibilityHeight = 300,
containerClassName = '',
...restProps
} = props;
const [isVisible, setIsVisible] = useState(false);
const classNames = useClassNameWithPrefixCls();
const toggleVisibility = () => {
if (window.pageYOffset > visibilityHeight) {
setIsVisible(true);
} else {
setIsVisible(false);
}
};
const scrollToTopOfPage = () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
});
};
useEffect(() => {
window.addEventListener('scroll', toggleVisibility);
return () => {
window.removeEventListener('scroll', toggleVisibility);
};
}, []);
return (
<>
{isVisible && (
<div
className={`${classNames('scroll-to-top')} ${containerClassName}`}
onClick={scrollToTopOfPage}
>
<Button {...restProps}>{message}</Button>
</div>
)}
</>
);
};
export default ScrollToTop;
Test below
import React from 'react';
import { render, screen, fireEvent } from '#testing-library/react';
import ScrollToTop from '../ScrollToTop';
describe('ScrollToTop', () => {
test('Renders ScrollToTop with aria-label', async () => {
render(<ScrollToTop aria-label="Go to the top" />);
fireEvent.scroll(window, { target: { scrollY: 1000 } });
const testy = await screen.findByLabelText('Go to the top');
expect(testy).toBeDefined();
});
});
Error Below
TestingLibraryElementError: Unable to find a label with the text of: Go to the top
<body
class=""
style=""
>
<div />
</body>
<body
class=""
style=""
>
<div />
</body>Error: Unable to find a label with the text of: Go to the top
<body
class=""
style=""
>
<div />
</body>
7 | render(<ScrollToTop aria-label="Go to the top" />);
8 | fireEvent.scroll(window, { target: { scrollY: 1000 } });
> 9 | const testy = await screen.findByLabelText('Go to the top');
| ^
10 | expect(testy).toBeDefined();
11 | });
12 | });
As you can see, the file is empty even using async/await.
note: Button is a component that just renders a
It's working as expected. I didn't upload that code because it could be confusing.
Thanks
Related
please solve it if you can do, I made a function when I scroll down, I can show the "button" {this is basically an arrow which indicates from bottom to top}. I want to add another function that when I scroll down >500 it will show the button, and if I scroll up it will hide, and if I stop scrolling if my window is scrolled >500 it will show otherwise it will hide.
export default function ScrollToTop() {
const [isVisible, setIsVisible] = useState(false);
const ScrollToTop= () => {
window.scrollTo({
top: 0,
behavior: "smooth"
});
};
useEffect(() => {
// Button is displayed after scrolling for 500 pixels
const toggleVisibility = () => {
if (window.pageYOffset > 500) {
setIsVisible(true);
} else {
setIsVisible(false);
}
};
window.addEventListener("scroll", toggleVisibility);
return () => window.removeEventListener("scroll", toggleVisibility);
}, []);
return (
<div className="scroll-to-top">
{isVisible && (
<div className="top" onClick={scrollToTop}>
<div className="top_img_holder">
<Image src="/uparrow.png" width="16" height="12" alt="" />
</div>
</div>
)}
</div>
)
}
To add the behavior you described, you can try the following:
Add a useRef hook to store a reference to the previous scroll
position.
Add an event listener for the scroll event in the component's
useEffect hook, and update the component's state (using the
setIsVisible function) based on the current and previous scroll
positions.
Return the component's state (isVisible) from the useEffect hook's
callback function, so that the effect is re-run whenever isVisible
changes.
import { useState, useEffect, useRef } from "react";
export default function ScrollToTop() {
const [isVisible, setIsVisible] = useState(false);
const prevScrollPos = useRef(0);
const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: "smooth"
});
};
useEffect(() => {
const toggleVisibility = () => {
const currentScrollPos = window.pageYOffset;
// Button is displayed after scrolling for 500 pixels
if (currentScrollPos > 500 && currentScrollPos > prevScrollPos.current) {
setIsVisible(true);
} else {
setIsVisible(false);
}
prevScrollPos.current = currentScrollPos;
};
window.addEventListener("scroll", toggleVisibility);
return () => window.removeEventListener("scroll", toggleVisibility);
}, [isVisible]);
return (
<div className="scroll-to-top">
{isVisible && (
<div className="top" onClick={scrollToTop}>
<div className="top_img_holder">
<Image src="/uparrow.png" width="16" height="12" alt="" />
</div>
</div>
)}
</div>
);
}
So I have an array of images, which I would like to hide or show on a click of a button.
right now when I try to hide the image, it will hide the entire array.
import "./main.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import React, { useEffect, useState } from "react";
import {
faCircleChevronLeft,
faCircleChevronRight,
faCircleXmark,
} from "#fortawesome/free-solid-svg-icons";
const Main = ({ galleryImages }) => {
const [slideNumber, setSlideNumber] = useState(0);
const [openModal, setOpenModal] = useState(false);
const [pics, setPics] = useState([]);
const [show, toggleShow] = useState(true);
// buttons next to name of diff charts (hide/show chart)
const handleOpenModal = (index) => {
setSlideNumber(index);
setOpenModal(true);
};
const removeImage = (id) => {
setPics((oldState) => oldState.filter((item) => item.id !== id));
};
// const hide = () => {
// setShow(false)
// }
const handleCloseModal = () => {
setOpenModal(false)
}
useEffect(()=> {
setPics(galleryImages)
},[]);
return (
<div>
<button onClick={() => toggleShow(!show)}>toggle: {show ? 'show' : 'hide'}</button>
{show &&
<div>
{pics.map((pic) => {
return (
<div style = {{marginBottom:'100px'}}>
{pic.id}
<img
src={pic.img}
width='500px'
height='500px'
/>
<button onClick ={() => removeImage(pic.id)}>Delete</button>
</div>
)
})}
</div>
I tried making a state component to try to hide and show the images, however it will hide the entire array instead of the individual image
i would add a show var to the galleryImages array and then set it so you get control of each image like this
import { useState } from "react";
import { v4 as uuidv4 } from "uuid";
import "./main.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import React, { useEffect, useState } from "react";
import {
faCircleChevronLeft,
faCircleChevronRight,
faCircleXmark,
} from "#fortawesome/free-solid-svg-icons";
function Main({ galleryImages }) {
const [slideNumber, setSlideNumber] = useState(0);
const [openModal, setOpenModal] = useState(false);
const [pics, setPics] = useState([]);
// buttons next to name of diff charts (hide/show chart)
const toggleShow = ({ id, show }) => {
setPics((oldState) =>
oldState.map((item) => {
if (item.id !== id) return item;
return { ...item, show: !show };
})
);
};
const removeImage = (id) => {
setPics((oldState) => oldState.filter((item) => item.id !== id));
};
useEffect(() => {
setPics(
galleryImages.map((galleryImage) => {
return { ...galleryImage, show: true };
})
);
}, []);
return (
<div>
<div>
{pics.map((pic) => {
return (
<>
<button
onClick={() => toggleShow({ show: pic.show, id: pic.id })}
>
toggle: {pic.show ? "show" : "hide"}
</button>
{pic.show && (
<div style={{ marginBottom: "100px" }}>
{pic.id}
<img src={pic.img} width="500px" height="500px" />
<button onClick={() => removeImage(pic.id)}>Delete</button>
</div>
)}
</>
);
})}
</div>
</div>
);
}
export default Main;
`
If you would like the option to hide individual pics, to accomplish this you are correct in your state component approach.
First you can create a pic component that has its own state with a hide/show button:
export default function Pic({pic}) {
const [showPic, setShowPic] = useState(true);
const handleClick = () => {setShowPic(!showPic)}
return (
<div>
<div style={showPic ? {display : "block"} : {display : "none"}}>
<img
src={pic.img}
width='500px'
height='500px'
/>
</div>
<button onClick={handleClick}>{showPic ? 'Hide' : 'Show'}</button>
</div>
)
}
Next, you can import this component into your main file
import Pic from 'location/Pic.js';
and map each pic to a <Pic> component.
{pics.map((pic) => <Pic pic={pic} key={pic.id}/>)}
Now the images will be shown each with their own Hide/Show button that can toggle their display with the state contained within each <Pic/> component. This is good practice because toggling one image will not cause a re-render of the entire image gallery.
Hi I am creating an app where the user can search for books by title. The user can search and each book result has a dropdown. so I have many dropdowns on a single page (the search results page). I am trying to make a dropdown close when the user clicks outside of the dropdown button (which is a div). Currently I can open the dropdown by clicking on the dropdown button and only close it when clicking on the dropdown button again.
I need the dropdown to close when clicking anywhere except the dropdown options. How would I go about doing this?
ButtonDropDown.js
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { BsFillCaretDownFill } from 'react-icons/bs';
const ButtonDropDown = ({ choices, label }) => {
const [active, setActive] = useState(false);
const toggleClass = () => {
setActive(!active);
};
return (
<div className="dropdown">
<button onClick={toggleClass} type="button" className="dropbtn">
<BsFillCaretDownFill />
</button>
<div
id="myDropdown"
className={`dropdown-content ${active ? `show` : `hide`}`}
>
<div>{label}</div>
{choices.map((choice) => (
<div>{choice}</div>
))}
</div>
</div>
);
};
ButtonDropDown.propTypes = {
choices: PropTypes.arrayOf(PropTypes.string).isRequired,
label: PropTypes.string,
};
ButtonDropDown.defaultProps = {
label: 'Move to...',
};
export default ButtonDropDown;
Book.js
import React from 'react';
import PropTypes from 'prop-types';
import ButtonDropDown from './ButtonDropDown';
const Book = ({ title, authors, thumbnail }) => {
return (
<div className="book">
<img src={thumbnail} alt={title} className="book-thumbnail" />
<div className="book-title">{title}</div>
<div className="book-authors">{authors}</div>
<ButtonDropDown
choices={['Currently Reading', 'Want to Read', 'Read', 'None']}
/>
</div>
);
};
// Move to..., currently reading, want to read, read, none
Book.propTypes = {
thumbnail: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
authors: PropTypes.arrayOf(PropTypes.string),
};
Book.defaultProps = {
authors: [],
};
export default Book;
SearchPage.js
import React, { useEffect, useState } from 'react';
import { BsArrowLeftShort } from 'react-icons/bs';
// import { debounce } from 'debounce';
import SearchBar from '../components/SearchBar';
import { search } from '../api/BooksAPI';
import Book from '../components/Book';
const SearchPage = () => {
const [query, setQuery] = useState('');
const [data, setData] = useState([]);
// const [isLoading, setIsLoading] = useState(true);
const handleChange = (e) => {
setQuery(e.target.value);
};
useEffect(() => {
const bookSearch = setTimeout(() => {
if (query.length > 0) {
search(query).then((res) => {
if (res.length > 0) {
setData(res);
} else setData([]);
});
} else {
setData([]); // make sure data is not undefined
}
}, 1000);
// bookSearch();
// console.log(data); // undefined initially since we didnt search anything
return () => clearTimeout(bookSearch);
// if (data !== []) setIsLoading(false);
// setIsLoading(true);
}, [query]);
return (
<div>
<SearchBar
type="text"
searchValue={query}
placeholder="Search for a book"
icon={<BsArrowLeftShort />}
handleChange={handleChange}
/>
<div className="book-list">
{data !== []
? data.map((book) => (
<Book
key={book.id}
title={book.title}
authors={book.authors}
thumbnail={book.imageLinks.thumbnail}
/>
))
: 'ok'}
</div>
</div>
);
};
export default SearchPage;
Hello I need to setup a progress bar. So when I press on the button progress bar shows, the button and the content inside of it dispears proggress bar starts to go 0 100 and as well shows some text above, and
when it reaches the 100% progress bar disppears and text above, but after that new text shows. Thanks in Advance
import React,{useEffect, useState} from 'react'
import LinearProgress from '#material-ui/core/LinearProgress'
const useStyles = makeStyles({
root: {
width: '100%',
},
});
const Content =(props)=> {
const classes = useStyles();
const[hideContent, setHideContent]= React.useState(false)
const [progress, setProgress] = React.useState(10);
function handleClick12 ()
{setHideEset(true) }
useEffect(() => {
const timer = setInterval(() => {
setProgress((prevProgress) => (prevProgress >= 100 ? 10 : prevProgress + 10));
}, 800);
return () => {
clearInterval(timer);
};
}, []);
return (
{!hideContent &&
<div className='esetNod__info'>
<h3>Hello</h3>
<Button onClick={handleClick12} className='fix__button'variant='outlined'></Button>
<div >
<LinearProgress value={progress} />
</div>
</div>
}
</div>
)
}
export default Content;
I tried to write something for you:
import React, { useState } from "react";
import LinearProgress from "#material-ui/core/LinearProgress";
const Content = () => {
const [isLoading, setIsLoading] = useState(false);
const [hasLoaded, setHasLoaded] = useState(false);
const [progress, setProgress] = useState(0);
const handleClick = () => {
setIsLoading(true);
const interval = setInterval(() => {
setProgress((prevProgress) => {
const next = prevProgress + 10;
if (next === 100) {
clearInterval(interval);
setIsLoading(false);
setHasLoaded(true);
}
return next;
});
}, 800);
};
if (!isLoading && !hasLoaded) {
return (
<div className="esetNod__info">
<h3>Pre load content</h3>
<button onClick={handleClick} className="fix__button">
Load Content
</button>
</div>
);
} else if (isLoading && !hasLoaded) {
return (
<diV>
<h3>Loading content</h3>
<LinearProgress value={progress} />
</diV>
);
} else {
return (
<div>
<h3>Post load content</h3>
</div>
);
}
};
export default Content;
I have a react object that I am trying to convert from a fucntion to a class in order to save the state and bind certain functions that I want to pass to child components. I am getting an error that causes it ot not compile.
React Component that I am trying to create with the state and functions that will be passed into the WidgetTile objects.
import React from "react";
...
export default class WidgetToolbar extends React.Component{
constructor(props){
super(props)
this.state={
targetBox:null,
};
this.dragStart = this.dragStart.bind(this);
this.dragEnd = this.dragEnd.bind(this);
this.drop = this.drop.bind(this);
};
const isOpen = useBehavior(mainStore.isWidgetToolbarOpen);
const themeClass = useBehavior(mainStore.themeClass);
const userDashboards = useBehavior(dashboardStore.userDashboards);
const [filter, setFilter] = useState("");
const [sortOrder, setSortOrder] = useState<SortOrder>("asc");
const userWidgets = useMemo(() => {
let _userWidgets = values(userDashboards.widgets).filter((w) => w.widget.isVisible);
if (sortOrder === "asc") {
_userWidgets.sort((a, b) => a.widget.title.localeCompare(b.widget.title));
} else {
_userWidgets.sort((a, b) => b.widget.title.localeCompare(a.widget.title));
}
if (!isBlank(filter)) {
_userWidgets = _userWidgets.filter((row) => {
return row.widget.title.toLowerCase().includes(filter.toLocaleLowerCase());
});
}
return _userWidgets;
}, [userDashboards, sortOrder, filter]);
dragStart = (e) => {
// this is initiated whenthe drag starts.
console.log(e.target.id);
console.log('drag start');
this.setState({
targetbox: true,
selectedId: e.target.id
});
this.createPopup();
e.dataTransfer.setData("text", e.target.id);
}
dragEnd = (e) => {
// the following is activated when the drag is ended
console.log('ended drag');
this.setState({
targetBox:null
});
this.destroyPopup();
}
drop = (e) => {
console.log("end drag dt: " + e.dataTransfer.getData("text"));
console.log("end drag t:" + e.target.id);
console.log('set start: ' + this.state.iterating);
}
createPopup = () => {
console.log("create Popup");
}
destroyPopup = () => {
console.log("destroy popup");
}
render(){
return(
<Overlay
isOpen={isOpen}
hasBackdrop={false}
canOutsideClickClose={true}
canEscapeKeyClose={true}
onClose={mainStore.closeWidgetToolbar}
className={Classes.OVERLAY_SCROLL_CONTAINER}
>
<div className={classNames(styles.toolbar, className, themeClass)} data-element-id="widgets-dialog">
<h3 className={styles.toolbarTitle}>Widgets</h3>
<div className={styles.toolbarMenu}>
<InputGroup
placeholder="Search..."
leftIcon="search"
round={true}
// TODO - Implement mainstore widget filter
// onChange={handleStringChange(this.mainStore.setWidgetFilter)}
value={filter}
onChange={handleStringChange(setFilter)}
data-element-id="widget-search-field"
/>
<SortButton order={sortOrder} onClick={setSortOrder} />
<Button minimal icon="pin" />
<Button minimal icon="cross" onClick={mainStore.closeWidgetToolbar} />
</div>
<hr />
<div className={Classes.DIALOG_BODY}>
<div className={styles.buttonBar}>
<Button text="Prev" icon="caret-left" small={true} disabled={true} />
<span className={styles.currentPage}>Page 1</span>
<Button text="Next" icon="caret-right" small={true} disabled={true} />
</div>
<ul className={styles.widgetList}>
{userWidgets.map((userWidget) => (
<li key={userWidget.id}>
<UserWidgetTile
userWidget={userWidget}
dragStart={this.dragStart}
dragEnd={this.dragEnd}
drop={this.drop}/>
</li>
))}
</ul>
</div>
</div>
</Overlay>
)
};
};
The compile error that I am getting is the following:
./src/components/widget-toolbar/WidgetToolbar.tsx
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: C:\Users\ojandali\Documents\dev\ozone1\ozone-framework-client\packages\application\src\components\widget-toolbar\WidgetToolbar.tsx: Unexpected token (35:10)
33 |
34 |
> 35 | const isOpen = useBehavior(mainStore.isWidgetToolbarOpen);
| ^
36 | const themeClass = useBehavior(mainStore.themeClass);
37 |
38 | const userDashboards = useBehavior(dashboardStore.userDashboards);
at Object.raise (C:\Users\ojandali\Documents\dev\ozone1\ozone-framework-client\packages\application\node_modules\#babel\parser\lib\index.js:3851:17)
at Object.unexpected (C:\Users\ojandali\Documents\dev\ozone1\ozone-framework-client\packages\application\node_modules\#babel\parser\lib\index.js:5167:16)
constructor(props) {
...
}; // <- semicolon here is invalid syntax in class
// v- const is also invalid syntax in class
const isOpen
Basically you got all wrong with ES6 class syntax. Please look up and get yourself familiar with that syntax.