Material-ui transition between old LeftNav menu style and the composability one with Reactjs - javascript

Ok, so I am very confused about the way of making a LeftNav menu with material-ui.
I am new on the project and I updated reactjs and material-ui.
So, a lot of stuff have been deprecated about LeftNav from material-ui and I am trying to fix it.
Here is the menu as it was when I opened the project (with all the console warning):
<LeftNav ref="leftNav"
docked={false}
style={{opacity: '0.9'}}
menuItems={menuItems}
onChange={this.leftNavOnChange} />
From this array:
var menuItems = [
{ route: '/', text: 'Home' },
{ type: 'SUBHEADER', text: 'Connect' },
{ route: '/categories', text: 'Categories' },
{ route: '/icons', text: 'Icons'},
{ route: '/Tmp', text: 'Tmp', disabled: !Permissions['connect_v2_list_tmp']['isPermitted'] },
{ route: '/wizard', text: 'Wizard', disabled: !Permissions['connect_v2_analyze_spreadsheet']['isPermitted'] },
{ route: '/linkshortener', text: 'Link shortener'},
{ type: 'SUBHEADER', text: 'Visual search' },
{ route: '/whitelist', text: 'Whitelist', disabled: !Permissions['connect_v2_list_whitelist']['isPermitted'] },
{ route: '/blacklist', text: 'Blacklist', disabled: !Permissions['connect_v2_list_blacklist']['isPermitted'] },
{ type: 'SUBHEADER', text: 'Tmp-wise' },
{ route: '/viewer', text: 'Viewer', disabled: !Permissions['connect_v2_view_bw_entity']['isPermitted']},
];
And here is what I did from what I understood about the way of doing it:
<LeftNav ref="leftNav"
docked={false}
style={{opacity: '0.9'}}
//menuItems={menuItems}
//onChange={this.leftNavOnChange}
>
{menuItems.map(function(items, i) {
if (items.route) {
return <MenuItem linkButton={true} href={items.route} key={i}>{items.text}</MenuItem>;
} else {
return <MenuItem data={items.type} key={i}>{items.text}</MenuItem>;
}
})}
</LeftNav>
So, less warning except one : using methods on left nav has been deprecated. Please refer to documentations.
but not such a big deal.
My problem here, is that my links are not working. I am staying on the same page.
And my other main problem: all the style it had is gone.
So, my question is:
am I doing it right?
Or am I missing something owned by reactjs and / or material-ui?
Thanks a lot in advance for the time spent on my request.

This is what I do (I am using react-router):
import { browserHistory } from 'react-router';
handleLeftNav: function (route) {
browserHistory.push(route);
this.setState({
leftNavOpen: false
});
},
<MenuItem onTouchTap={() => { return this.handleLeftNav('/route/'); }}>Route</MenuItem>

If you move your map outside of the LeftNav then you should no longer receive this warning. When I compose my LeftNav I follow this pattern and I don't get the error you're reporting. Hope this helps.
let menuItems = menuItems.map(function(items, i) {
if (items.route) {
return <MenuItem linkButton={true} href={items.route} key={i}>{items.text}</MenuItem>;
} else {
return <MenuItem data={items.type} key={i}>{items.text}</MenuItem>;
}
});
<LeftNav ref="leftNav"
docked={false}
style={{opacity: '0.9'}}
//menuItems={menuItems}
//onChange={this.leftNavOnChange}
>
{ menuItems }
</LeftNav>

Related

Mapping an array in my Next JS app not working

In my Next JS app I'm getting array and mapping them for the nav bar. but when i do it throws an error of TypeError: _utils_navigation__WEBPACK_IMPORTED_MODULE_6___default(...).map is not a function. my code is as follows.
{NavRoutes.map((navs, index) => {
console.log(navs.title);
console.log('bhsjvhjvs');
return (
<Link href={navs.link}>
<div className={`${
router.asPath === navs.link
? "text-active-red"
: ""
}`}>{navs.title}</div>
</Link>
)
})}
and the array
const NavRoutes = [
{
title: "HOME",
link: "/"
},
{
title: "NEWS",
link: "/news"
},
{
title: "CHANNEL",
link: "/channels"
},
{
title:"SHOWS",
link: "/shows"
},
{
title: "SCHEDULE",
link: "/schedules"
},
{
title: "CONTACT",
link: "/contact"
},
]
export default NavRoutes;
so how can i work through this?
check how you have imported it.
It should be like this
import NavRoutes from "../utils/navigations.js";
And apart from this, try to console NavRoutes and check you are getting expected value or not.

Map function is not working properly when I hit onClick

So I am making this project in ReactJs, which has a sidebar, where I am trying to implement dropdown menu.
Required Behavior
If I click in any of the option of the sidebar, if it has a submenu, it will show. And close upon again clicking.
Current Behavior
If I click any of the options, all the submenus are showing at once.
For example if I click publications option, it shows me all the options, such as featured publications, journal publications.
How do I fix that?
My sidebarItems array
const sidebarItems = [
{
title: "Publications",
url: "#",
subMenu: [
{
title: "Journal Publications",
url: "#",
},
{
title: "Featured Publications",
url: "#",
},
],
},
{
title: "Team Members",
url: "#",
subMenu: [
{
title: "Current Members",
url: "#",
},
{
title: "Lab Alumni",
url: "#",
},
],
},
{
title: "Projects",
url: "#",
subMenu: [
{
title: "Project 1",
url: "#",
},
{
title: "Project 2",
url: "#",
},
{
title: "Project 3",
url: "#",
},
],
},
{
title: "News",
url: "#",
},
{
title: "Contact Us",
url: "#",
},
];
export default sidebarItems;
The Sidebar Component
import { useState } from "react";
import { Box, Text } from "#chakra-ui/react";
import sidebarItems from "./sidebarItems";
export default function Sidebar() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<Box>
{sidebarItems.map((items) => {
return (
<Box
width='200px'
height='40px'
textAlign='center'
cursor='pointer'
onClick={() => {
setIsOpen(!isOpen);
}}
>
<Text>{items.title}</Text>
{isOpen
? items.subMenu?.map((item) => {
return <Text>{item.title}</Text>;
})
: ""}
</Box>
);
})}
</Box>
</div>
);
}
You have to use an array state variable. Your single state variable isOpen is dictating all the subMenus here:
{isOpen
? items.subMenu?.map((item) => {
return <Text>{item.title}</Text>;
})
: ""}
You need to have an array state variable here, so each sidebar item has a corresponding boolean to dictate opening/closing of value.
const [isOpen, setIsOpen] = useState(Array(sidebarItems.length).fill(false));
Now you have to ensure that you are setting it correctly and manipulating the right array element.
onClick={() => {
let newIsOpen = [...isOpen];
newIsOpen[index] = !isOpen[index];
setIsOpen(newIsOpen);
}}
I hope this helps you reach your solution
This is happening because you are using wrong logic and you don't specify which submenu should be shown.
First, delete the current state and dont use it.
Then, you should define another state like:
const [selectedMenu, setSelectedMenu] = useState("");
then, define this function:
const handleClick = (title) => {
setSelectedMenu(title);
}
after that, once you click on Box, you should invoke function like this:
onClick={() => handleClick(item.title)}
consequently, you should write your logic like this:
<Text>{items.title}</Text>
{item.title === selectedMenu
? items.subMenu?.map((item) => {
return <Text>{item.title}</Text>;
})
: ""}
I think the problem is occurring because you have only 1 state variable set for every sidebar option. Every sidebar option should have its own variable keeping track of whether its submenu should open or not.
In your code, when the isOpen state variable is set to true then when the function maps over all the options the variable's value will always be true.
Try setting a variable for each of the menu options which contains whether the submenu should open or not.

Using VMenu from vuetify with render function (scoped slot)

I'm trying to use Vuetify's VMenu component and I would like that when a user clicks the button the VMenu shows up. As far as the docs goes it says we should add a scoped slot. Doing with a normal template it works but when I switch to a render function approach it never renders the button.
I have been following the Vue's docs and ended up with:
h(VMenu, { props: { value: isMenuOpen.value } }, [
h(
"template",
{
scopedSlots: {
activator: ({ on, attrs }) => {
debugger; // it never reaches this debugger
return h(VButton, { on, attrs }, 'click me');
}
},
},
[]
),
h(VList, [h(VListItem, [h(VListItemTitle, ["Logout"])])]),
]),
I have tried using a non-arrow function as well:
scopedSlots: { activator: function({ on, attrs }) { return h('div', 'click me'); } }
and return a simple h('div', 'click me') in both non-arrow function and arrow function and nothing seems to work.
How can I pass the scoped slot activator to VMenu component?
Scoped slots are passed through the scopedSlots property of createElement's 2nd argument in the form of { name: props => VNode | Array<VNode> }. In your case, scopedSlots should have two entries: one for activator, and another for default:
import { VMenu, VList, VListItem, VBtn } from 'vuetify/lib'
export default {
render(h) {
return h(VMenu, {
scopedSlots: {
activator: props => h(VBtn, props, 'Open'),
default: () => h(VList, [
h(VListItem, 'item 1'),
h(VListItem, 'item 2'),
h(VListItem, 'item 3'),
]),
},
})
}
}
which is equivalent to this template:
<template>
<v-menu>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on">Open</v-btn>
</template>
<v-list>
<v-list-item>item 1</v-list-item>
<v-list-item>item 2</v-list-item>
<v-list-item>item 3</v-list-item>
</v-list>
</v-menu>
</template>
demo
I wasn't able to fully understand the problem described in my question. This is an answer not to answer the fully original question but to guide future users that may come to this question.
Instead of using a scoped slot I have used the value prop in combination with attach prop. This solution in the end ended up working without no problem.
button(
{
attrs: { "data-account-setting": true },
props: { plain: true, rounded: true, icon: true },
on: { click: onOpenMenuClick },
},
[h(VIcon, ["mdi-account-outline"])]
),
h(
VMenu,
{
props: {
value: isMenuOpen.value,
// waiting on answer on SO
// #see https://stackoverflow.com/questions/67405594/using-vmenu-from-vuetify-with-render-function-scoped-slot
attach: "[data-account-setting]",
minWidth: "300px",
left: true,
offsetY: true,
closeOnContentClick: false,
rounded: true,
},
on: {
input: (value: boolean) => {
isMenuOpen.value = value;
},
},
},
[
h(VList, { props: { dense: true } }, [
h(VListItem, { props: { to: { name: "logout" } } }, [
h(VListItemTitle, { attrs: { 'data-cy-logout': true } }, ["Logout"]),
]),
]),
]
),

ReactJs Accordion Automatic Close Mechanism

I'm currently working on an accordion component in react version 16.3.2, that receives props from another component and displays the accordion accordingly. Unfortunately I cannot use hooks.
This works pretty smoothly, what I want to implement now is a way so that only one accordion can be opened at the same time on the same page. So, once you open one accordion (while another is already opened) it should automatically close the already opened one.
I have an Id (string which describes the current section, e.g 'contact', 'info', etc.) and the state of an accordion gets saved in the state (set to true when you toggle the accordion).
I'm not quite sure on how I could implement this mechanism and am looking for tips on how I could solve this in a smart way. Any pointers?
example:
https://codesandbox.io/s/reactjs-accordion-automatic-close-mechanism-6dys1
(I didn't add all of the styling, animations since this is more about functionality)
You could do something like this, using the state hook in the App component
export default function App() {
const items = [
{ id: 1, title: 'First Accordion', content: 'Hello' },
{ id: 2, title: 'Click me', content: 'Hello 2' },
{ id: 3, title: 'Third Accordion Accordion', content: 'Hello 3' },
]
const [selectedItem, setSelectedItem] = useState(1)
const handleClick = id => {
setSelectedItem(id)
}
return (
<div className="App">
{items.map(x => {
return (
<Accordion
key={x.id}
id={x.id}
title={x.title}
open={x.id === selectedItem}
onClick={handleClick}
>
<p>{x.title}</p>
</Accordion>
)
})}
</div>
);
}
Then your accordion component is a bit simpler
class Accordion extends React.Component {
accToggle() {
this.props.onClick(this.props.id);
}
sectionClasses() {
let classes = "accordion";
classes += this.props.open ? " sec-on" : "";
classes += "sec-underway";
return classes.trim();
}
render() {
return (
<section className={this.sectionClasses()} id={this.props.id}>
<div className="acc-title" onClick={this.accToggle.bind(this)}>
<h3 className="acc-text">{this.props.title}</h3>
<div className="acc-nav">
<span className="acc-toggle" />
</div>
</div>
<div className="acc-content">{this.props.children}</div>
</section>
);
}
}
Accordion.defaultProps = {
open: false
};
Accordion.propTypes = {
id: PropTypes.number.isRequired,
children: PropTypes.any,
onFocus: PropTypes.func,
progress: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
PropTypes.bool
]),
title: PropTypes.string,
open: PropTypes.bool
};
export default Accordion;
The accordion calls a function on the app component, which updates the state and the display logic is passed down in the props
You can find solution for your problem in the following codesandbox
https://codesandbox.io/s/reactjs-accordion-automatic-close-mechanism-yejc0
Change prop names as it fits your code base, but the logic is solid

How do I make sure my menu refreshes when a new user logs in? Trying to show the proper items (admin navigation for admin users)

My SideMenu.js basically does a check to see if user is admin or not before building the proper routes to render, my issue is that when i sign out and sign back in, it keeps the same menu from the previous user.
It looks something like this:
class SideMenu extends Component {
constructor () {
super();
this.state = {
isAdmin: false,
isLoading: true,
footer: [
{ label: 'Settings', url: 'settingsUrl', icon: settingsIcon, id: '1' },
{ label: 'Log Out', url: 'logOutUrl', icon: signoutIcon, id: '2' },
]
}
this.checkVerification();
}
checkVerification = () => {
var isLoggedIn = Firebase.isLoggedIn();
var isAdmin = Firebase.isAdmin();
Promise.all([ isLoggedIn, isAdmin ]).then((responses) => {
isLoggedIn = responses[0];
isAdmin = responses[1];
if(isLoggedIn){
if(isAdmin){
this.setState({
isAdmin: true,
isLoading: false,
routes: [
{ label: 'Screen1', url: 'screenUrl1', icon: screenIcon1, id: '1' },
{ label: 'Screen2', url: 'screenUrl2', icon: screenIcon2, id: '2' },
{ label: 'AdminScreen3', url: 'screenUrl3', icon: screenIcon3, id: '3' },
]
})
}else{
this.setState({
isLoading: false,
routes: [
{ label: 'Screen1', url: 'screenUrl1', icon: screenIcon1, id: '1' },
{ label: 'Screen2', url: 'screenUrl2', icon: screenIcon2, id: '2' },
]
})
}
}else{
this.props.navigation.navigate('Login')
}
})
}
Then I render the menu at the end:
render () {
const { isLoading } = this.state;
return(
<View style={styles.container}>
{isLoading && this.renderLoading()}
{!isLoading && this.renderMenu()}
</View>
)
}
The Menu only loads once and never refreshes when a user signs out and back in with a different account. I tried a bunch of checks in the sidemenu render but it resulted in functions being spammed and didnt work either way. Then I found a post of someone mentioning to use StackActions.reset, which I did in my Login Screen, which works, the code is as follows:
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Init' })],
});
this.props.navigation.dispatch(resetAction);
This works, but I get the warning:
'Warning: Can\'t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s', 'the componentWillUnmount method', '\n in Login (at SceneView.js:9)
What's the proper way to go about refreshing my menu on new user? I've been trying to fix it for quite a while with no proper result. I'm not sure if it helps to know that I load my SideMenu from my Init Page:
import { AppRegistry, Dimensions, YellowBox } from 'react-native';
import { createDrawerNavigator , createAppContainer } from 'react-navigation';
import SideMenu from './pages/menu/SideMenu'
import Routes from './Routes';
const DrawerNav = createDrawerNavigator({
Screen: {
screen: Routes,
}
}, {
contentComponent: SideMenu,
drawerWidth: Dimensions.get('window').width/1.7,
overlayColor: 'rgba(0, 0, 0, 0.7)',
minSwipeDistance: 5,
});
const Init = createAppContainer(DrawerNav);
export default Init;
Any help would be greatly appreciated, thank you in advance.
I figured it out. First of all I had a bunch of promises that weren't getting resolved when signing out that were giving me errors, after resolving them properly, I ended up changing my logOut.js to:
componentDidMount(){
Firebase.logOut().then((response) => {
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Login' })],
});
this.props.navigation.dispatch(resetAction);
})
}
That did the trick for me, when I tried that in my SideMenu.js when route == 'LogOut' it gave me warnings. It seems to be working this way though. Hope this helps someone struggling with the same issue out if they come across this.

Categories

Resources