Material UI Tabs no selected tab - material-ui

I'm trying to use Material UI Tabs for navigation. However, there are routes in my application that match none of the tabs. When I pass a value to the Tabs component that does not match any of the child tab values, I get a warning about an invalid value.
I created a hidden tab will a value of null as a work-around.
Is it possible to disable this warning about an invalid tab value?
Can tabs in Material UI have no selection?
Thanks

The value of the currently selected Tab. If you don't want any selected Tab, you can set this property to false.
From: https://material-ui.com/api/tabs/
What I ended up doing is creating a switch statement with valid tab values, and if windows.location.pathname doesn't match any of them have the default return false.
Example Routes:
class Routes extends Component {
render() {
return (
<Switch>
<Route path={'/test2'} component={Test2} />
<Route path={'/test3'} component={Test3} />
<Route exact path={'/'} component={Test} />
</Switch>
);
}
}
Example NavBar:
checkPathnameValue() {
const { pathname } = window.location;
switch (pathname) {
case '/test2':
case '/test3':
break;
default:
return false;
}
return pathname;
}
render() {
const { classes } = this.props;
const tabValue = this.checkPathnameValue();
return (
<div className={classes.root}>
<AppBar position={'static'}>
<Toolbar>
<Tabs value={tabValue}>
<Tab
label={'Test 2'}
value={'/test2'}
to={'/test2'}
component={Link}
/>
<Tab
label={'Test 3'}
value={'/test3'}
to={'/test3'}
component={Link}
/>
</Tabs>
</Toolbar>
</AppBar>
</div>
);
}

Seems like setting the value property of Tabs to false will not show any warning and will deselect all the tabs correctly.
Philip's solution works perfectly, here I am just removing the need for the switch case.
In my case, I only had one tab (Login) where I wanted no tab to be selected since it is a button rather than a tab.
Here's what I did to solve this:
<Tabs value={this.state.content !== "login" ? this.state.content : false} onChange={(event, newValue) => { this.setState({content: newValue}) }}>
<Tab value="home" label="Home" wrapped />
<Tab value="tab1" label="Tab 1" />
<Tab value="tab2" label="Tab 2" />
</Tabs>
on another part of my AppBar I had a Login button:
<Button onClick={(event, newValue) => { this.setState({content: "login"}) }}>Login</Button >
Similarly to Philips's answer, the key is in {this.state.content !== "login" ? this.state.content : false} which prevents Tabs from being rendered with "login" value. Instead, it is given the value "false" which is allowed and does not invoke the warning.

I also experienced this issue a while back and follow the same pattern.
E.g.,
return <Tabbar value={value ?? false} onChange={(event: React.ChangeEvent<{}>, value: any) => onChange(value)}>{tabs}</Tabbar>

Toggle Effect
To get a toggle effect the listener will need to be placed on the individual <Tab onClick/> events as <Tabs onChange> will not trigger when clicking the same button multiple times.
const Container = ()=>{
const [currentTab,setCurrentTab] = React.useState<string|false>('a')
const handleChange = (val: string) =>
setCurrentTab(val === currentTab ? false : val)
return <Tabs value={currentTab}>
<Tab value='a' label='a' onClick={()=>handleChange('a')}/>
<Tab value='b' label='b' onClick={()=>handleChange('b')}/>
</Tabs>
}

Related

Unable to display menu on right click react-big-calendar

I am trying to display menu on right click with react-big-calendar and material ui,
the issue that menu isnt display correctly on html its going on top right corner,
My code is:
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
return (
<>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={() => redirectToEvent(selectedEvent)}>
<ImportContactsTwoToneIcon
color="primary"
style={{ marginLeft: "15px" }}
/>{" "}
פתח אירוע
</MenuItem>
</Menu>
<Calendar
localizer={localizer}
events={events}
step={60}
views={["month", "day"]}
onSelectEvent={(event, e) => {
redirectToEvent(event);
}}
components={
{
eventWrapper: ({ event, children }) => (
<div
onContextMenu={
e => {
setSelectedEvent(event);
//think this is the issue
setAnchorEl(e);
e.preventDefault();
}
}
>
{children}
</div>
)
}
}
Material-UI has an example of providing a Context Menu, and it doesn't seem to use an anchorEl prop, or take the bare 'event' target object, placing a different object in state.
const handleContextMenu = (event) => {
event.preventDefault();
setContextMenu(
contextMenu === null
? {
mouseX: event.clientX - 2,
mouseY: event.clientY - 4,
}
: // repeated contextmenu when it is already open closes it with Chrome 84 on Ubuntu
// Other native context menus might behave different.
// With this behavior we prevent contextmenu from the backdrop to re-locale existing context menus.
null,
);
};
const handleClose = () => {
setContextMenu(null);
};
From that part of the example, it would seem you need to update your onContextMenu accordingly. Since you're setting multiple state values for your menu, both for it's positioning and the referenced selectedEvent, you may want to use a reducer for state instead.
Then, on the <Menu> itself, it also mutates that state.
<Menu
open={contextMenu !== null}
onClose={handleClose}
anchorReference="anchorPosition"
anchorPosition={
contextMenu !== null
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
: undefined
}
>
// menu items
</Menu>
And, since you've placed the <Menu> inside of your <Calendar>'s container object, what sort of styling is on your container and could that effect it's layout as well? (I don't know if Material-UI automatically portals it's menu in this scenario or not)

Material UI Autocomplete + Infinite Scrolling together?

Problem : Getting double Scrollbars - Removing Paper Scrollbar makes the autocomplete content not scrollable hence showing ONLY the contents in the visible height of the dropdown.
If I hide the other scroll then the Infinite Scroll API does not get invoked. How can I get it working :
Description -
I am trying to create a Infinite Scroll with Material UI Autocomplete for which I am using react-infinite-scroll-component attached link for reference
The way I implemented is :
As we need to attach the Infinite Scroll to the Popper that renders the list items; hence I have written my custom PAPER Component (as per documentation it is responsible for rendering items in the dropdown )
PaperComponent={myCustomComponent}
My InfiniteScrollAutoComplete definition is attached below :
<Autocomplete
options={list.data && list.data !== null ? list.data : []}
getOptionLabel={(option) => option.name}
PaperComponent={(param) => (
<InfiniteScroll
height={200}
dataLength={list.total}
next={this.handleFetchNext.bind(this)}
hasMore={list.data.length < list.total ? true : false}
loader={
<p style={{ textAlign: "center", backgroundColor: "#f9dc01" }}>
<b>Loading...</b>
</p>
}
endMessage={
<p style={{ textAlign: "center", backgroundColor: "#f9dc01" }}>
<b>Yay! You have seen it all</b>
</p>
}
>
<Paper {...param} />
</InfiniteScroll>
)}
renderInput={(params) => (
<TextField {...params} label="" variant="outlined" />
)}
/>
const observer = useRef();
const lastOptionElementRef = useCallback((node) => {
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver(async (entries) => {
if (entries[0].isIntersecting && props.hasMore) {
setPageNumber((pageNumber) => pageNumber + 1);
}
});
if (node) observer.current.observe(node);
}, [props.loader]);
you can add this lastOptionElementRef to the last element of the options using the render option prop. This will trigger an function whenever the last option is visible in the viewport. Also, it avoids the scrolling issue

React-Admin: How do I hide the "Checkbox" with the Datagrid <row>?

Within my App, we have permissions like ADD/DELETE based on roles.
Admin has all permissions to add, delete, edit the records
customers don't have the delete permissions.
I therefore want to hide the delete checkbox within the <Datagrid> for customers.
//RoleList.js
import React from "react";
import { List, Datagrid, TextField, SingleFieldList, ChipField, EditButton, DeleteButton, ReferenceArrayField, Loading } from "react-admin";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles(theme => ({
chips: {
backgroundColor: theme.palette.secondary.light
}
}));
const RoleList = ({ permissions, ...props }) => {
const classes = useStyles();
if (!permissions) return <Loading />;
const permissionsList = permissions.split(",");
return (
<List {...props} title="roles">
<Datagrid rowClick="show">
<TextField source="name" />
<ReferenceArrayField reference="permissions" source="permissions">
<SingleFieldList>
<ChipField source="name" className={classes.chips} />
</SingleFieldList>
</ReferenceArrayField>
{permissionsList.includes("edit_roles") && <EditButton />}
{permissionsList.includes("delete_roles") && <DeleteButton />}
</Datagrid>
</List>
);
};
export default RoleList;
This is my component where I hide edit and delete button based on permissions, but the checkbox is still functional.
Am I missing anything from react-admin?
To hide the checkboxes:
<List
{...props}
bulkActionButtons={false}
>
...
</List>
To hide those checkboxes in RA v4 or greater:
<Datagrid
{...props}
bulkActionButtons={false}
>
...
</Datagrid>
The bulkActionButtons prop was moved from List component to Datagrid component in RA v4.

ag-Grid Switch goes from checked to unchecked upon down sroll

Getting this weird error where any checked material UI switch becomes unchecked as I scroll down out of that data view.
Below is the Switch return with conditional rendering.
let freeTier = props.params.data.tier;
return freeTier === "FREE" ? (
<FormGroup>
<FormControlLabel
control={
<Switch
disableRipple
focusVisibleClassName={classes.focusVisible}
classes={{
root: classes.root,
switchBase: classes.switchBase,
thumb: classes.thumb,
track: classes.track,
checked: classes.checked
}}
{...props}
/>
}
/>
</FormGroup>
) : null;
And this is where I call the above into cellRendererFramework
cellRendererFramework: params => {
const handleClick = params => {
console.log(params.data);
};
return (
<PaypalSwitch
params={params}
data={params.data}
otherProps={this.props}
onClick={() => handleClick(params)}
/>
);
}
From what it seems like the grid re-renders? I'm getting this error.
ag-Grid: React Component 'cellRendererFramework' not created within
1000ms

Getting a dialog on click of Edit Button on admin-on-rest

I am working on an application using admin-on-rest framework. For editing an entry on a Resource we provide XXXEdit, XXXShow, XXXCreate props to it. My requirement is that when I click on an Edit button in List view on any entry I should get a Dialog box with the parameters in XXXEdit instead of going to a new page. I tried doing this by using a Dialog in XXXEdit component
<Edit title={<RoleTitle />} {...props}>
<SimpleForm>
<Dialog
title="Dialog With Actions"
actions={actions}
modal={false}
open={true}
>
<TextInput source="id" />
<TextInput source="name" validate={required} />
.
.//some more fields
</Dialog>
</SimpleForm>
</Edit>
I get errors like The TextInput component wasn't called within a redux-form
If I use a DisabledInput then I get an error cannot read value of undefined
How do I go on with this?
I do not think you can use Simpleform for this. You will need to create a custom Form using Redux-Form. Look at the bottom answer that documents the final answer.
This might help you
How to richly style AOR Edit page
Instead of creating a page. You are creating a component that connects to the Redux state and displays as a dialog box.
I tried to resolve this using HOC and react-router.
I created a button using AOR button and provided a containerElement
containerElement={
<Link
key={record.id}
to={{
...{
pathname: `${basePath}/${encodeURIComponent(record.id)}`
},
...{ state: { modal: true } }
}}
/>
}
I created a route like this where DialogRoleEdit is an AOR edit component wrapped with a dialog HOC below .
<Route
exact
path="/roles/:id"
render={routeProps => {
return !!(
routeProps.location.state && routeProps.location.state.modal
) ? (
<Restricted authClient={authClient} location={routeProps.location}>
<div>
<RoleList resource={"roles"} {...routeProps} />
<DialogRoleEdit resource={"roles"} {...routeProps} />
</div>
</Restricted>
) : (
<Restricted authClient={authClient} location={routeProps.location}>
<RoleEdit resource={"roles"} {...routeProps} />
</Restricted>
);
}}
/>
Finally an HOC
handleClose = () => {
this.props.history.goBack();
};
render() {
const actions = [
<FlatButton label="Cancel" primary={true} onClick={this.handleClose} />
];
return (
<Dialog>
<WrappedComponent/>
</Dialog>
)
}
We need to provide edit prop for this resource in App.js
edit={DialogUserEdit}