How to hide or prevent an antd submenu to be rendered? - reactjs

I have a Menu as bellow:
<Menu>
<SubMenu>
<Menu.Item>Help</Menu.Item>
<Menu.Item>Antd</Menu.Item>
</SubMenu>
</Menu>
Now the problem is that I need to hide SubMenu or Menu.Item for some situations? something like below :
<Menu>
<Acl item="submenu">
<SubMenu>
<Acl item="help">
<Menu.Item>Help</Menu.Item>
</Acl>
<Menu.Item>Antd</Menu.Item>
</SubMenu>
</Acl>
</Menu>
The Acl component will check for user access to the menu and decide to hide or show the text.
Is there any code examples for antd to reach this result or any suggestions?
Note: Already I have implemented Acl as bellow:
import React, { Component } from 'react';
class Acl extends Component {
constructor(props) {
super(props);
}
render() {
const props = this.props;
if( hasAccess(props.item) )
return <div>{props.children}</div>;
return null;
}
}
export default Acl;
But after render I got this error:
Cannot read property 'indexOf' of undefined

I think it could be done as bellow:
render() {
const { getAccessByPath } = this.props; //selector from redux store
<Menu>
{ getAccessByPath('submenu') ?
<SubMenu>
{ getAccessByPath('submenu.help') ?
<Menu.Item>Help</Menu.Item> : ''
}
<Menu.Item>Antd</Menu.Item>
</SubMenu> : ''
}
</Menu>
}
This works now but is so nasty and is not readable if I have more submenus an menu items (currently about 20) but I think there should be a better solution.

You can use the disabled prop of Menu.Item and Menu.Submenu and use it to disable the button according to user type:
<Menu.Item disabled={userAccess()}> // userAccess() should return a boolean
In case you want to totally hide the button, you need to use conditional rendering, of the sort:
<Menu>
<Acl item="submenu">
{ (userAccess()) ? // again should return boolean
<SubMenu>
<Acl item="help">
<Menu.Item>Help</Menu.Item>
</Acl>
<Menu.Item>Antd</Menu.Item>
</SubMenu> : ''
}
</Acl>
</Menu>

Related

How to add conditional statement inside react fragment

Inside of the react fragment I have to add conditional statement. On basis of the conditional statement, return what expected
return (
<React.Fragment>
<Toolbar
pageTitle={i18next.t('TITLE')}
iconButtons={this.state.icons}
{this.props.abc && this.props.abc.operation ?(
moreButton={moreButton}
):null}
/>
if this.props.abc.operation is present then only show morebutton if not show only iconbuttons this is my condition and above is the code i tried. any help would be really appreciated.
<>
<Toolbar
pageTitle={i18next.t('TITLE')}
iconButtons={this.state.icons}
moreButton={this.props.abc && this.props.abc.operation && moreButton}
/>
</>
Try to use this.
Instead of conditional rendering you can do like below.
isAbcOperationExist = (args) => {
if(args && args.operation){
return true;
}
return false;
}
Now inside component props:
<Toolbar
pageTitle={i18next.t('TITLE')}
iconButtons={this.state.icons}
showMoreButton={() => this.isAbcOperationExist(this.props.abc)}
/>
Based on result returned boolean value by method isAbcOperationExist you can show or hide moreButton
More Example:
Assumption this is class based component:
class YourComponent extends React.Component {
constructor(props){
super(props)
}
isAbcOperationExist = (args) => {
if(args && args.operation) {
return true;
}
return false;
}
render (){
return (
<Toolbar
pageTitle={i18next.t('TITLE')}
iconButtons={this.state.icons}
moreButton={moreButton}
showMoreButton={() => this.isAbcOperationExist(this.props.abc)}
/>
)
}
}
For Toolbar Component assuming it as functional base component:
const Toolbar = ({pageTitle, iconButtons, showMoreButton, moreButton}) => {
return(
<div>
{
showMoreButton ? <button onClick={moreButton}>Show More</button> : null
}
</div>
)
}
React Fragment has nothing to do with this. You also can't manipulate component props like this. The idea would be to have a single prop for iconButtons and moreButton and and do the logic what to show inside Toolbar component

How to add a CSS class when page loads and remove it when user clicks on item

I am setting up an active class for my react project using an external library. However, I also want when the page loads to mark the Home tab as active by setting the pink color class. This works, but I am not sure how to remove the pink class after the user clicks on another tab, currently, the Home tab stays pink if the user clicks on another item. I tried to remove the class on the onSelect event but that didn't work. Here is my code:
import React from 'react';
import { withRouter } from 'react-router-dom';
import { Menu, MenuItem } from '#progress/kendo-react-layout';
class MenuNavContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 'pink-class',
};
}
selectedElement = null;
render() {
return (
<div>
<Menu onSelect={this.onSelect}>
<MenuItem
text="Home"
data={{ route: '/' }}
activeClassName="pink-class"
cssClass={this.state.active}
/>
<MenuItem text="Products" data={{ route: '/products' }} />
<MenuItem text="About" data={{ route: '/about' }}>
<MenuItem text="Team" data={{ route: '/about/team' }} />
</MenuItem>
</Menu>
<div style={{ padding: 10 }}>{this.props.children}</div>
</div>
);
}
onSelect = (event) => {
if (this.selectedElement !== null) {
this.selectedElement.classList.remove('pink-class');
}
this.selectedElement = event.nativeEvent.target.parentElement;
this.selectedElement.classList.add('pink-class');
this.props.history.push(event.item.data.route);
};
}
export default withRouter(MenuNavContainer);
and my CSS:
.pink-class {
background-color: pink;
}
And a runnable example in order to showcase this more easily:
https://stackblitz.com/edit/react-zg2pmx-nnzppy?file=app/MenuNavContainer.jsx
How can I mark the Home tab as active when the page loads and then only show the active class once the user has clicked on a different item?
Just simply update your state that holds the class name to be an empty string. No need to set classes using the element on the change handler (this is an anti-pattern anyway).
onSelect = (event) => {
this.setState({active: ''})
this.props.history.push(event.item.data.route);
};
The more I look at this though, the more this seems to be a hack, that you have to provide this class at all..
Now a note more of opinion - I've never used this library before, but for being a "commercial" library that requires a license, I'm very underwhelmed by what I'm seeing. It seems logical to me that there should be a way to specify a MenuItem as selected or focused to accomplish this goal (the other tabs do this automatically once clicked), but I do not see anything to meet these requirements. This does not answer your question, but if possible you may search elsewhere for a UI library with a more robust API...

How can I disabled sub menu item in react?

I create a menu with React.js. But there is a problem for me. There is 2 sub menus and these sub menus have their menu items. When I hover one of them it shows its menu items. But when I click another sub menu, it still shows previous menu items. How can I prevent it ? My code:
My first class : Sider.js
function Sider(props) {
return (
<Menu mode="horizontal">
selectedKeys={[props.current]}
onClick={props.handleClick}
<SubMenu title={<span><Icon type="setting" />Sub menu 1</span>}>
<MenuItem> menu item 1</MenuItem>
<MenuItem> menu item 2</MenuItem>
</SubMenu>
<SubMenu title={<span><Icon type="laptop" />Sub menu 2</span>}>
<MenuItem> menu item 3</MenuItem>
</SubMenu>
</Menu>
);
}
This is my main class which I call Sider function.
Main.js :
import PropTypes from 'prop-types';
class Main extends React.Component {
constructor(props) {
super(props)
this.state = {
current: 'MenuItem'
}
}
handleClick = (e) => {
this.setState({
current: e.key,
});
}
render() {
return (
<div>
<Sider navigation={this.props.navigation} handleClick={this.props.handleClick} current={this.state.current />
</div>
);
}
}
Main.propTypes = {
handleClick: PropTypes.func,
};
You can use key to maintain active state of selected sub menu and use selectedKeys. see the working code below
I have made working menu in codeopen here codepen
import PropTypes from 'prop-types';
class App extends React.Component {
state = {
current: 'menu1:1',
}
handleClick = (e) => {
this.setState({
current: e.key,
});
}
render() {
return (
<div>
<Sider navigation={this.props.navigation} handleClick={this.handeClick} current={this.state.current} />
</div>
);
}
}
App.propTypes = {
handleClick: PropTypes.func,
};
And your Sider function will be like below
function Sider(props) {
return (
<Menu onClick={props.handleClick} selectedKeys={[props.current]}>
<SubMenu title={<span><Icon type="setting" />Sub menu 1</span>}>
<MenuItem key="setting:1"> menu item 1</MenuItem>
<MenuItem key="setting:2"> menu item 2</MenuItem>
</SubMenu>
<SubMenu title={<span><Icon type="laptop" />Sub menu 2</span>}>
<MenuItem key="laptop:1"> menu item 3</MenuItem>
</SubMenu>
</Menu>
);
}
For documentations please read horizontal menu and Vertical Menu
How did you implement the hovering mechanism? Please add more code.
Do you change the inner state of each SubMenu when you hover out?
You should better read the documentation for antd's components.
Go to the following link to better understand how to use these components.

Close SelectField menu with multiple=true

I am trying to close from the outside a SelectField. The idea is that I have a multiple selectField (which doesn't close on change by definition) with a "Clear All" item in the drop down menu that deselects all items on click.
The code I have is similar to this:
class MultiSelect extends Component {
state = {values: []}
clear = () => {
this.setState({values: []});
// FixMe: close the DropDownMenu here.
};
handleChange = (e,i,v) => {
this.setState({values: v});
};
render() {
const {title, choices, style} = this.props;
const {values} = this.state
return <div style={{...style}}>
<SelectField
floatingLabelText={title}
multiple
value={values}
onChange={this.handleChange}
>
<MenuItem value={null} primaryText="Clear All" onTouchTap={this.clear}/>
<Divider/>
{choices.map((d,i) =>
<MenuItem key={i} value={d} primaryText={d} checked={values.includes(d)}/>
)}
</SelectField>
</div>
}
}
The best way of achieving this would be to call the DropDownMenu.close() function. Using the newly introduced dropDownMenuProps prop of SelectField I thought I could do this, however the function apparently gets overwritten in DropDownMenu.
So, is there another way to achieve this or should I implement this component from scratch using the DropDownMenu directly?

Use child components as real components, not stateless child objects

Some component contains a Tabs with Tabs:
class App {
render() {
<div>
<p>Tabs here:</p>
<Tabs>
<Tab name="Page 1"> .. content here .. </Tab>
<Tab name="Page 2"> .. content here .. </Tab>
<Tab name="Page 3"> .. content here .. </Tab>
</Tabs>
</div>
}
}
Tabs is responsible for most of the markup:
class Tabs {
click() {
// I want the Tab component, because I want to tab.setState()
// `this` is not a `Tab` object
}
render() {
return (
( print the tab labels: )
<ul>
{ this.props.children.map(tab =>
// >>> `tab` is not a `Tab` object <<<
<li><a onClick={ this.click.bind(tab) } href="#">{ tab.props.name }</a></li>
) }
</ul>
( print the tab content: )
{ this.props.children }
);
}
}
A Tab itself is very little, maybe nothing:
const Tab = ({children}) => <div>{ children }</div>;
How does Tabs.click know which Tab was clicked. If I click.bind(tab) it's the child object, not the Tab object. If I click.bind(this) it's the Tabs object.
This might be a very longwinded way of asking why props.children don't contain the Component objects, but a kind of proxy (?) child object. It does have the props, but not the methods, state etc.
edit 1:
In this example, I'd like to let Tab decide how to render itself: once as tab link and once as tab content. Tabs would be responsible for calling the render methods, but Tab would know how. As you can see in Tabs.render() there are 2 renders. It'd be nice if it could do this:
<ul>
{ this.props.children.map(tab => tab.renderLink()) }
</ul>
{ this.props.children.map(tab => tab.renderContent()) }
or just
{ this.props.children }
because content is the normal render
But Tabs can't do that, because it doesn't have Tab objects for children... Why?
Well, there are a few methods to achieve this. To start, you're right - since you're using ES6 arrow functions in the map, this isn't rebound - it's "inherited" from the enclosing scope (I think, technically, it's less inherited and more just left alone - unchanged).
First, instead of binding in the render method, bind click in the constructor.
constructor(props) {
super(props);
this.click = this.click.bind(this);
}
or use ES6 Arrow Functions
click = (event) => {...}
Although this doesn't directly solve your issue, it cleans up the context mess that arises from using bound callbacks.
From there, you can get the clicked tab by using event.target.
You can also use partials - the bind method accepts additional parameters which are prepended to the parameters of the bound function. That would look something like this:
render (
<div>
{ this.props.children.map((tab) => {
<a <li><a onClick={ this.click.bind(null, tab) } href="#">{ tab.props.name }</a></li>
}) }
);
you would have to adjust your click method to accept tab as well: click(tab, event)
I also don't think it's wise, or even recommended to mess with a component's state from another component. Why aren't you using the Tab component? Pass in Tabs onClick as a prop and handle click events like this...
class Tab extends React.Component {
onClick = (event) => {
this.setState({...}); // Tab onClick
if (this.props.onClick) this.props.onClick(); // Tabs onClick
}
}
edit: Working example
Tabs class
class Tabs extends React.Component {
onTabClick = (tab) => {
tab.setState({ test: true });
}
render() {
return (
<div>
{ this.props.children.map((tab) => {
return React.cloneElement(tab, {
onClick: this.onTabClick
});
}) }
</div>
);
}
}
Tab class
class Tab extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
onClick = (event) => {
console.log(event);
event.preventDefault();
if (this.props.onClick) {
this.props.onClick(this);
}
}
render() {
return <div>{ !this.state.test && <a href={ this.props.url } onClick={ this.onClick }>{ this.props.label }</a> }</div>;
}
}
example tabs
<Tabs>
<Tab url="http://www.google.com" label="Google" />
<Tab url="http://www.google.com" label="Google" />
</Tabs>
although it begs the question again - why not just set the state inside of the Tab class? Why do you need to set the state of a child from within a parent? Can that state not be maintained by the parent (using this.setState(...) and passed into the child as a prop?

Resources