Material UI > Backdrop > only for some subcomponent of the page - material-ui

Is there any way how to enhance a backdrop from example in https://material-ui.com/components/backdrop/ to show loading circle only above the single component (in case some page has more component), not above the whole page?
Thanks for reply.

Backdrop are fixed positioned by default, that's why it covers the whole page.
To achieve the result you want, we have to change its position to absolute and contain it inside an element with relative position — this element can be your component. If you're new in CSS positions check this docs from developer.mozilla.org.
Knowing all that, we can come up with the following codes
const useStyles = makeStyles({
parent: {
position: "relative",
width: 200,
height: 200,
backgroundColor: "red",
zIndex: 0,
},
backdrop: {
position: "absolute"
}
});
export default function App() {
const classes = useStyles();
return (
<div className={classes.parent}>
<Backdrop className={classes.backdrop} open={true}>
<CircularProgress color="inherit" />
</Backdrop>
</div>
);
}
Also we have to define z-index on either parent or backdrop element to make it work. Not sure why though.
I created a codesandbox for you to play with.

The Backdrop component of Material UI is set to position: 'fixed' by default, that's why it covers the whole page.
If you want it to reside and position itself like any other component typically on the DOM, all you have to do is to reset its position back to relative, for instance:
<Backdrop open={true} sx={{ position: 'relative' }}>
<CircularProgress color="inherit" />
</Backdrop>
and you don't need to change the parent component since it should be in your case see to relative by default if you're not changing it. But if you have crazy positions going in your app here and there, then you might consider changing that as well.

i have created a custom componenet that i use if i want to block only part of the UI:
"use strict";
/** external libraries */
import React from "react";
import Backdrop from "#mui/material/Backdrop";
import CircularProgress from "#mui/material/CircularProgress";
const BlockUi = ({open, onClose, children}) => {
return (
<div style={{"position": "relative"}}>
<Backdrop
sx={{color: "#FFFFFF", zIndex: (theme) => theme.zIndex.drawer + 1, "position": "absolute"}}
open={open}
onClick={() => onClose()}
>
<CircularProgress color="inherit"/>
</Backdrop>
{children}
</div>
);
}
export default BlockUi;
and i use it like this:
"use strict";
/** external libraries */
import React from "react";
import BlockUi from "./BlockUi";
const JsonForm = ({fields, onSubmit}) => {
const [loading, setLoading] = React.useState(false)
const stopLoading = () => {
setLoading(false)
}
return (
<div>
<BlockUi open={loading} onClose={stopLoading}>
<button type="submit" onClick={() => {
console.log(loading)
setLoading(true)
}}>Submit
</button>
</BlockUi>
</div>
);
}
export default JsonForm;

Related

Can't figure out how to style material ui datagrid

I'm trying to style material-ui DataGrid component to justify the content in the cells. I am reading the material ui docs about styling but I don't seem to doing it correct and frankly find the docs on styling very confusing.
The doc here: https://material-ui.com/customization/components/#overriding-styles-with-classes implies I should be able to do something like this:
const StyledDataGrid = withStyles({
cellCenter: {
justifyContent: "center",
},
})(DataGrid);
<div style={{ height: 300, width: '100%' }}>
<StyledDataGrid rows={rows} columns={columns} />
</div>
However, when I do this, I don't see the style being added to the MuiDataGrid-cellCenter DOM element. Attaching a screenshot which shows the element classes. In the inspector I see that the style isn't being added (and if I add it manually I get the desired results). Am I not using the withStyles function correctly?
So after a bit more messing around, I believe the issue is that the DataGrid component does not support the classes property (which it seems most of the material ui components do). I believe the withStyles usage about is shorthand for passing the classes via the classes prop. Since the prop isn't listed in the API https://material-ui.com/api/data-grid/ I'm assuming this is why it isn't working. I confirmed that I can get the styles working by using a combination of the className parameter with descendant selection.
If someone determines I'm wrong and there is a way to get withStyles working on this component please comment.
const useStyles = makeStyles({
root: {
"& .MuiDataGrid-cellCenter": {
justifyContent: "center"
}
}
});
...
export default function X() {
const classes = useStyles();
return (
...
<DataGrid className={classes.root} checkboxSelection={true} rows={rows} columns={columns} />
...
)
}
ALTERNATIVE SOLUTION: (for others with similar issues)
If you are working within a class and cannot use hooks...
<div>
<DataGrid
rows={rows}
columns={columns}
sx={{
'&.MuiDataGrid-root .MuiDataGrid-cell:focus': {
outline: 'none',
},
}}
/>
</div>

How to fix 'missing form label' error in Material UI Select using chrome WAVE tool

I am using material UI Select and we are using chrome WAVE tool to fix ADA issues. An error of 'Missing form label' is coming on material UI Select like in the below screenshot. Can anyone help me to solve this issue. Thanks in advance.
wave tool: https://chrome.google.com/webstore/detail/wave-evaluation-tool/jbbplnpkjmmeebjpijfedlgcdilocofh
code:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormHelperText from '#material-ui/core/FormHelperText';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
const useStyles = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
export default function SimpleSelect() {
const classes = useStyles();
const [age, setAge] = React.useState('');
const handleChange = (event) => {
setAge(event.target.value);
};
return (
<div>
<FormControl className={classes.formControl}>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
onChange={handleChange}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
);
}
screenshot:
I have found one solution for it. Just add htmlFor="demo-simple-select-placeholder-label" in InputLabel tag to rmove the 'missing form label' error.
The error says that the input element, which by the way is aria-hidden="true", has no label.
I think it's a problem similar to the one reported about TablePagination answered here, and to the one about TextareaAutosize discussed here. The element is always hidden to screen readers and the select behavior is implemented with javascript.
WAVE plugin is reporting the error because they prefer report more than less, in case the hidden element is shown at some point like they explain here.
I was able to get the warning to go away by adding htmlFor={'input-id'} to the InputLabel, and adding inputProps={{id: 'input-id'}} to the Select.

Adding text or description to image component to pure-react-carousel

What is the appropriate way to add a description to each ? Currently I am adding a div under and I am unable to see the text.
Use Image component with tag prop and isBgImage set tot true.
<Slide index={i}>
<Image
tag='div'
isBgImage
src={someSrc}
>
<div className={descriptionClass}>Your description here</div>
</Image>
</Slide>
You can bypass the issue with a wrapper, without using isBgImage or children native props.
Create a wrapper component and use the prop children containing the text
Use the wrapper in the Slide component, pass the image's source as a prop
Add the children content
Use the position attritbute to style your children
It would look like this:
import React from 'react';
import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext, Image } from 'pure-react-carousel';
const BackgroundImage = (props) => {
return (
<>
{props.children}
<Image src={props.img} />
</>
)
};
const childrenStyle = {
position: "absolute",
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)"
}; // to center the text for example
const Carousel = () => {
return(
<div className='home-banner'>
<CarouselProvider
naturalSlideWidth={100}
naturalSlideHeight={30}
totalSlides={4}
>
<Slider>
<Slide index={0}>
<BackgroundImage img={"your_image"} >
<div>Your children text</div>
</BackgroundImage>
</Slide>
// other slides here
</Slider>
<ButtonBack>Back</ButtonBack>
<ButtonNext>Next</ButtonNext>
</CarouselProvider>
</div>
)
};

MaterialUI CardMedia Image with spinner

The following component works perfectly file. I want to show a spinner until the image gets loaded. How do I do that?
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Card from '#material-ui/core/Card';
import Image from 'material-ui-image';
import CardActionArea from '#material-ui/core/CardActionArea';
import CardContent from '#material-ui/core/CardContent';
import CardMedia from '#material-ui/core/CardMedia';
import Typography from '#material-ui/core/Typography';
/**
* Media is a Card, with an image / video and a caption. url of the media is hidden from the user,
* but the user can click it to open it in a new browser
*/
const useStyles = makeStyles({
card: {
margin: '0.5rem',
maxWidth: '25%'
}
});
const Media = ({ url, caption }: any) => {
const classes = useStyles();
return (
<Card className={classes.card}>
<CardActionArea>
<CardMedia component="img" alt={caption} height="140" image={url} title={caption} />
<CardContent>
<Typography variant="body2">{caption}</Typography>
</CardContent>
</CardActionArea>
</Card>
);
};
export default Media;
The image component should be loaded regardless of image is loaded or not. We can not show the image component on the screen by setting the height of the component to 0 and and then setting the height of the image component to desired height once the image is loaded.
Then, the tracking of if the image is loaded can be done using the state.
import React, { useState } from 'react';
// Image component with loading state
export default Image = () => {
const [hasImageLoaded, setHasImageLoaded] = useState(false);
return (
<>
<img src={src} onLoad={() => setHasImageLoaded(true)} className={`${!hasImageLoaded && height-0}`} />
{ !hasImageLoaded && <div> Loading... </div> }
</>
);
}
.height-0 {
height: 0;
}
Ps. This code can be changed according to your use. The code is done considering the general use
The Material-UI CardMedia component displays the image as a background image, so you won't be able to access the onLoad property available on the DOM <img> tag. You could chooses to use an <img> instead, like this:
add to you imports:
import CircularProgress from '#material-ui/core/CircularProgress';
add to your styles:
media: {
width: '100%',
height: 140,
},
progress: {
# center spinner
}
add state and onLoad handler to your component:
const [loaded, setLoaded] = React.useState(false);
function handleImageLoad() {
setLoaded(true);
}
replace your CardMedia with:
{loaded ? (
<img
className={classes.media}
src={url}
alt={caption}
onLoad={handleImageLoad}
/>
) : (
<div className={classes.progress}>
<CircularProgress color="secondary" />
</div>
)}

FOUC when using #material-ui/core with NextJS/React

My simple NextJS page looks like this (results can be viewed at https://www.schandillia.com/):
/* eslint-disable no-unused-vars */
import React, { PureComponent, Fragment } from 'react';
import Head from 'next/head';
import compose from 'recompose/compose';
import Layout from '../components/Layout';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
const styles = {
root: {
textAlign: 'center',
paddingTop: 200,
},
p: {
textTransform: 'uppercase',
color: 'red',
},
};
class Index extends PureComponent {
render() {
const { classes } = this.props;
const title = 'Project Proost';
const description = 'This is the description for the homepage';
return (
<Fragment>
<Head>
<title>{ title }</title>
<meta name="description" content={description} key="description" />
</Head>
<Layout>
<p className={classes.p}>amit</p>
<Button variant="contained" color="secondary">
Secondary
</Button>
</Layout>
</Fragment>
);
}
}
export default withStyles(styles)(Index);
I am importing a bunch of components off the #material-ui/core library to style my items. I also have a local style definition assigned to a style constant.
What seems to be happening here is that my style isn't getting rendered on the server which is why the files being served upon load are sans-style. And then the CSS gets rendered by the client-side code. As a result, there's a flash of unstyled content that lasts almost a second, long enough to be noticable.
Any way to fix this? The entire codebase is up for reference at https://github.com/amitschandillia/proost/tree/master/web.
I ran a similar problem when tried to make a production build of my app, that uses material-ui. I manage to solve by adding a JSS Provider like this:
import JssProvider from "react-jss/lib/JssProvider";
class App extends Component {
render() {
<JssProvider>
*the rest of your material-ui components*
</JssProvider>
}
}
Here's the solution - https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_document.js .
Basically, all you need to do is to sync server-side class names with client-side. The link above shows what you need to do to fix that issue.