onHover event on material ui component is not rendering icon - javascript

I am currently trying to render a delete icon on an Material UI component, to be more specific, the MenuItem component. I have created a state field in the component's state and set it to false(this.state.isHovering). The idea is that when I hover over the account item, I want to render the delete icon, and when I hover off, it is supposed to go away. I impleneted these event listeners using onMouseEnter and onMouseLeave and attached them over to the MenuItem. I then made it so that the icon only shows when this.state.isHovering is true. However, when I hover over the MenuItem, it does not render. However, if I just implemented the icon without having the state get involved, it renders. I also console logged in the event handlers to make sure that the event is being registered and it does console log both when hovering in and out. Please see my code. Thanks!
handleMouseEnter = () => {
this.setState({
isHovering: true,
})
}
handleMouseLeave = () => {
this.setState({
isHovering: true,
})
}

I think there are a few issues with your code:
the state is never defined
you don't properly update the state
You don't properly conditionally check/use the state
Lots of bugs in the code you provided (looks like an incomplete copy paste)
You can use reactHooks to help define your state and setState functions
This is my best answer, but the code you provided is missing the account variable and a lot of the referenced functions, so it is incomplete. But it should help with your state issues
import React, { Component, Fragment, useState } from 'react';
import { Typography, withStyles } from '#material-ui/core';
import PropTypes from 'prop-types';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import MenuItem from '#material-ui/core/MenuItem';
import Icon from '#material-ui/core/Icon';
import AccountTypeIcon from './AccountTypeIcon';
class PortDashSideBar extends Component {
const [isHovering, setIsHovering] = useState(false);
handleMouseEnter = () => {
setIsHovering(true);
}
handleMouseLeave = () => {
setIsHovering(false);
}
return <MenuItem
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={account.id === currentAccountId ? classes.selectedAccount : classes.unselectedAccount}
hover
name={account.name}
key={account.id}
onClick={ () => this.setCurrentAccount(account.id) }>
<TableCell className={classes.cellAccountIcon}>
<AccountTypeIcon type={this.getAccountById(account.id).type} />
</TableCell>
<TableCell className={classes.cellName}>
<Typography className={classes.accountName}>
{account.label}
</Typography>
<Typography>
<span>
{this.getHoldingsForAccount(account.id)}
</span>
{
isHovering ? <Icon>delete</Icon> : null
}
</Typography>
</TableCell>
</MenuItem>;
}

Related

Finding the buttons on the screen that have no text for the test

I am trying to write the tests for the NavBar component (using react-native-testing-library) that has several buttons that are basically just icons (using ui-kitten for react native). So I can't get these buttons by text (as there is none) but other methods didn't work for me either (like adding accesibilityLabel or testID and then getting by the label text / getting by test ID). Any ideas what I am doing wrong?
// NavBar.tsx
import React from 'react';
import {View, StyleSheet} from 'react-native';
import {HomeBtn, SaveBtn} from '../components/buttons';
import UserSignOut from './UserSignOut';
const NavBar = ({
navigation,
pressHandlers,
}) => {
return (
<View style={styles.navBar}>
<View>
<HomeBtn navigation={navigation} />
<SaveBtn pressHandler={pressHandlers?.saveBtn ?? undefined} />
</View>
<UserSignOut />
</View>
);
};
export default NavBar;
// HomeBtn.tsx
import React from 'react';
import {Button} from '#ui-kitten/components';
import {HomeIcon} from '../shared/icons';
import styles from './Btn.style';
export const HomeBtn = ({navigation}: any) => {
return (
<Button
accesibilityLabel="home button"
style={styles.button}
accessoryLeft={props => HomeIcon(props, styles.icon)}
onPress={() => navigation.navigate('Home')}
/>
);
};
// NavBar.test.tsx
import React from 'react';
import {render, screen} from '#testing-library/react-native';
import * as eva from '#eva-design/eva';
import {RootSiblingParent} from 'react-native-root-siblings';
import {EvaIconsPack} from '#ui-kitten/eva-icons';
import {ApplicationProvider, IconRegistry} from '#ui-kitten/components';
import NavBar from '../../containers/NavBar';
describe('NavBar', () => {
const navBarContainer = (
<RootSiblingParent>
<IconRegistry icons={EvaIconsPack} />
<ApplicationProvider {...eva} theme={eva.light}>
<NavBar />
</ApplicationProvider>
</RootSiblingParent>
);
it('should render the buttons', async () => {
render(navBarContainer);
// this test fails (nothing is found with this accesibility label)
await screen.findByLabelText('home button');
});
});
Query predicate
The recommended solution would be to use:
getByRole('button', { name: "home button" })
As it will require both the button role, as well as check accessibilityLabel with name option.
Alternative, but slightly less expressive way would be to use:
getByLabelText('home button')
This query will only check accessibilityLabel prop, which also should work fine.
Why is query not matching
Since you're asking why the query is not working, that depends on your test setup. It seems that you should be able to use sync getBy* query and do not need to await findBy* query, as the HomeBtn should be rendered without waiting for any async action.
What might prevent that test from working could be incorrect mocking of any of the wrapping components: RootSiblingParent, ApplicationProvider, they might be "consuming" children prop without rendering it. In order to diagnose the issue you can use debug() function from RNTL to inspect the current state of rendered components. You can also run your tests on render(<NavBar />) to verify that.
Does await screen.findByA11yLabel('home button') work? It should match the accessibilityLabel prop.

should I use state in addling a new component on click React?

I am new to react and trying to refactor some es6 js code in to react I have a component that I need to create once I click on an icon similar to insert adjacent html is vanilla js any idea how can I achieve this.
import React, {useState} from 'react'
import Item from './Item';
import { icon, library } from '#fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faPlusCircle} from '#fortawesome/free-solid-svg-icons';
library.add(faPlusCircle)
function row(props) {
const [item] = useState(<Item />)
return (
<ul className="global">
item
<FontAwesomeIcon onClick={()=>{return <ChangeableItem/>}} con={["fas", "plus-circle"]}/>
<ul/>
)
}
This doesn't do anything:
onClick={()=>{return <ChangeableItem/>}}
The click handler isn't expecting a returned React node and won't do anything with it.
should I use state
Yes. Track in state whether or not this component is displayed. For example:
const [showChangeableItem, setShowChangeableItem] = useState(false);
The state now says not to show the "changeable item". Within the rendering, conditionally render that element based on state. For example:
{ showChangeableItem ? <ChangeableItem/> : null }
Then in your click handler you'd just update the state:
<FontAwesomeIcon onClick={() => setShowChangeableItem(true)} con={["fas", "plus-circle"]}/>
Basically, don't think of it as trying to add elements. All of the elements you need should already be specified in the render, some can just be wrapped in conditional logic. State drives the rendering. Events update state.
You would use a state variable for that.
import React, {useState} from 'react'
import Item from './Item';
import { icon, library } from '#fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faPlusCircle} from '#fortawesome/free-solid-svg-icons';
library.add(faPlusCircle)
function row(props) {
const [showItem, set_showItem] = useState
return (
<> // use react fragment here, otherwise you will get an error if you try to return several React Elements
<ul className="global">
{showItem? <Item> : null} // return the Item if you want to show it, otherwise return null
<FontAwesomeIcon
onClick={()=> set_showItem(true)}
con={["fas", "plus-circle"]}
/>
<ul/>
</> //ent of React fragment
)
}

Delete images if the user is leaving the component React

I have form with a drag and drop component where I can upload images, after this I send these pictures with axios to my backend and save it on server side and then re-render it in a preview mode. My problem is, that if a user uploads some pictures and after that he switches to another page without submitting the form the added pictures are staying on my server side for no reason.
So the question: I want to check inside my component if a user is leaving, show a prompt and if he clicks on the OK button I want to delete all the added pictures from my backend before leaving. How can I detect this?
My simplified snippet:
function MyComp(props) {
const [Images,setImages] = useState([]) // in this array I store the recieved image's URL
const [isBlocking,setIsBlocking] = useState(true) // used for prompt
const handleSubmit = (event) => {
event.preventDefault();
setIsBlocking(false)
}
return(
<Grid container className={classes.mainGrid} direction="row" spacing={2}>
<Grid item xs={4} xl={4}>
<Prompt when={isBlocking} message="There are unsaved datas. Are you sure you want to leave?"/>
<form className={classes.form} onSubmit={handleSubmit}>
... somecode
</Grid>
</Grid>
)
}
export default MyComp
Thanks in advance
Inside React Function Component you can call the prompt when the user is trying to leave , i.e when the component is unmounting
In Class Component of React you can use React componentWillUnmount() and in Function Component You can use useEffect cleanup function
Code for Function Component
import React, { useEffect } from "react";
import { Link } from "react-router-dom";
export default function Home(props) {
useEffect(() => {
return function cleanup() {
alert("unmounting");
//call api and execute your code here
};
}, []);
return (
<div>
<Link to="/home">
On Click I will unmount this component and go to /home
</Link>
</div>
</Link>
);
}
Code for Class Component
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export default class Test extends Component {
componentWillUnmount() {
alert('unmounting component');
//call your code here
}
render() {
return (
<div>
<Link to='/home'>
On Click I will unmount this component and go to /home
</Link>
</div>
);
}
}
You can check this codesandbox if you want any ref
When you leave the page, the component method componentWillUnmount() fires. I don't recall how this behaves if you were to simply close the browser window as opposed to just navigating away, nor do I recall how you can escape it and stay on the component, but that should at least be a good starting point for you. Obviously you'd have to do a class extending React.Component for this one instead of a straight function.

Show a Modal on react functional component using redux toolkit

I have a dashboard page that shows a table list of all the posts, for each post I have and an edit button.
I'm trying to pop a modal when the edit button is clicked.
So I created a Modal component, which is rendered by the Dashboard component (this is a high order component equal to the App compo)
and I added a modal slice with redux toolkit and I successfully managed to change the modal state when the edit button is clicked but the modal doesn't show up.
I hope that I was thorough enough with what I'm trying to achieve, I also hope that you will help me guys, and now I'll share with you some of the code.
EditPostModal.jsx
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { makeStyles } from '#material-ui/core/styles'
import Modal from '#material-ui/core/Modal'
import editPostSlice, {
getPostToEditModal,
} from '../../store/slices/editPost'
const useStyles = makeStyles((theme) => ({.....}))
export default function SimpleModal() {
const classes = useStyles()
const modal = useSelector(getPostToEditModal)
const dispatch = useDispatch()
console.log('HEYYYY', modal) // modal is undefined
const handleClose = () => {
dispatch(editPostSlice.actions.closeModal())
}
if (!modal) return null
return (
<Modal
className={classes.modal}
open
onClose={handleClose}
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
>
<h1>I AM THE MODAL</h1>
</Modal>
)
}
The first step to debug is to check if the modal will open without redux toolkit slice.
Also, can you confirm that the modal variable always return something other than a falsy value?

Material UI v0 ListItem Alignment Issue when using Custom Avatar Component

Material UI version: v0.20.0
I am trying to assign leftAvatar value via CustomAvatar component but it is not aligning as you can see in attached screenshot. Please help.
CustomAvatar: This component is working on some condition bases like if image is available the its
MemberList/index.js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import {List, ListItem} from 'material-ui/List';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import CustomAvatar from 'routes/CustomAvatar';
class MemberList extends Component {
render(){<MuiThemeProvider>
<List>
<ListItem
leftAvatar={<CustomAvatar avatarPic={false}/>}
primaryText={"Mike Tailor"}
secondaryText={"This is first text"}
secondaryTextLines={1}
rightIconButton={<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem>Add friend</MenuItem>
<MenuItem>Chat</MenuItem>
</IconMenu>}/>
<ListItem
leftAvatar={<CustomAvatar avatarPic={true}/>}
primaryText={"Kory Becker"}
secondaryText={"This is second text"}
secondaryTextLines={1}
rightIconButton={<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem>Add friend</MenuItem>
<MenuItem>Chat</MenuItem>
</IconMenu>}/>
</List>
</MuiThemeProvider>}
}
export default withRouter(MemberList);
CustomAvatar/index.js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import Avatar from 'material-ui/Avatar';
class CustomAvatar extends Component {
render(){
if(this.props.avatarPic){
return(<Avatar size={40} src={"http://www.example.com/myimage.png"} />)
}else{
return(<Avatar size={40}>A</Avatar>)
}
}
}
export default withRouter(CustomAvatar);
The cause of your problem
Your problem is caused because the material-ui v0 library expects the leftAvatar prop to be an Avatar component, and so relies on internal values of Avatar behind the scenes. Since your CustomAvatar is not directly an Avatar, material-ui does not find these internals and so the styling does not work.
Specifically, if you take a look at the source of ListItem, you'll notice a pushElement function that takes child components like leftAvatar and assigns styling by setting the style prop:
pushElement(children, element, baseStyles, additionalProps) {
if (element) {
const styles = Object.assign({}, baseStyles, element.props.style);
children.push(
React.cloneElement(element, {
key: children.length,
style: styles,
...additionalProps,
})
);
}
}
Your CustomAvatar makes no use of this style prop, so you never receive the necessary CSS styling. That's the cause of your layout issues.
You have a couple of options to fix this depending on whether you are willing to upgrade to v1 or not.
Code that fixes it
class CustomAvatar extends Component {
render() {
const { showPicture, ...other } = this.props;
if (showPicture) {
return (<Avatar size={40} {...other} src={"http://www.example.com/myimage.png"} />);
} else {
return (<Avatar size={40} {...other}>A</Avatar>);
}
}
}
As discussed above, the pushElement function sets the style prop. Right now, you're not using it, so your Avatars get no styling. The {...other} spreads this prop down into your Avatars so that they get the right styling and your layout works.
But, you should upgrade to v1
v1 should have a stable release in the early quarters of 2018, and it fixes a lot of these kinds of problems. Instead of spending time working through these kinds of issues and working with v0, you should upgrade and learn the new (and, imho, improved) way.
I also have faced same problem you could fix this by wrap your custom compoent into PAPER component of material-ui. Please see code below:
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import {List, ListItem} from 'material-ui/List';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import CustomAvatar from 'routes/CustomAvatar';
import Paper from 'material-ui/Paper';
class MemberList extends Component {
render(){<MuiThemeProvider>
<List>
<ListItem
leftAvatar={<Paper zDepth={2} circle={true}><CustomAvatar avatarPic={false}/></Paper>}
primaryText={"Mike Tailor"}
secondaryText={"This is first text"}
secondaryTextLines={1}
rightIconButton={<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem>Add friend</MenuItem>
<MenuItem>Chat</MenuItem>
</IconMenu>}/>
<ListItem
leftAvatar={<Paper zDepth={2} circle={true}><CustomAvatar avatarPic={true}/></Paper>}
primaryText={"Kory Becker"}
secondaryText={"This is second text"}
secondaryTextLines={1}
rightIconButton={<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem>Add friend</MenuItem>
<MenuItem>Chat</MenuItem>
</IconMenu>}/>
</List>
</MuiThemeProvider>}
}
export default withRouter(MemberList);

Categories

Resources