How to correctly remove padding on Components - reactjs

I would like to nest a List inside a Card or an Expansion Panel, the problem is that both, the Expansion Panel and List are adding padding to the sides. The result looks really weird...
<Card>
<CardHeader title="Title" subheader="Subheader"/>
<CardContent>
<List>
<ListItem>
<ListItemText
primary={`<--- too much padding`}
/>
</ListItem>
</List>
</CardContent>
</Card>
Here is a running example that shows the issue: https://codesandbox.io/s/material-demo-djzdz
I would like to get rid of the extra padding, I solved the issue temporarily by using the withStyles thingy like this:
import React from "react";
import Card from "#material-ui/core/Card";
import CardHeader from "#material-ui/core/CardHeader";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import { withStyles } from "#material-ui/core/styles";
import MuiCardContent from "#material-ui/core/CardContent";
const NestedCardContent = withStyles(theme => ({
root: {
padding: 0
}
}))(MuiCardContent);
export default function Color() {
return (
<Card>
<CardHeader title="Title" subheader="Subheader" />
<NestedCardContent>
<List>
<ListItem>
<ListItemText primary={`padding is fine`} />
</ListItem>
</List>
</NestedCardContent>
</Card>
);
}
Run it in codesandbox: https://codesandbox.io/s/material-demo-kyx38
As you can see, it works. But it feels absolutely hackish...
I previously tried that mx, px spacing thing described here: https://material-ui.com/customization/spacing/
Without success either - changed nothing.
Furthermore I need this Kind of NestedCardContent in more than one Component. So how would you do it in a clean way? What is considered as a good practice here?

I recommend just removing the CardContent tag entirely. If you look at the source for CardContent, the only thing it does is padding.
If you don't want that padding just do:
import React from "react";
import Card from "#material-ui/core/Card";
import CardHeader from "#material-ui/core/CardHeader";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
export default function Color() {
return (
<Card>
<CardHeader title="Title" subheader="Subheader" />
<List>
<ListItem>
<ListItemText primary={`padding is fine`} />
</ListItem>
</List>
</Card>
);
}
This solution removes the 24px bottom padding as well which may or may not be what you want. If you want to retain the extra bottom padding, then just move your NestedCardContent component into its own file so you can import/reuse it.

Related

How to display inline Autocomplete component?

I'd like to display inline the Autocomplete component, either alonside other Autocomplete components and/or any other React MUI components.
Starting from the example about countries from the doc:
In index.js my first attempt was the following:
import * as React from "react";
import ReactDOM from "react-dom/client";
import { StyledEngineProvider } from "#mui/material/styles";
import Demo from "./demo";
import TextField from "#mui/material/TextField";
ReactDOM.createRoot(document.querySelector("#root")).render(
<StyledEngineProvider injectFirst>
<Demo />
<Demo />
<TextField />
<TextField />
</StyledEngineProvider>
);
with this outcome:
As you can see, while a component like TextField is automatically displayed inline, we cannot say the same thing for Autocomplete.
So, my second attempt was to force the inline property by a Box as shown in the doc:
import * as React from "react";
import ReactDOM from "react-dom/client";
import { StyledEngineProvider } from "#mui/material/styles";
import Demo from "./demo";
import TextField from "#mui/material/TextField";
import Box from '#mui/material/Box';
ReactDOM.createRoot(document.querySelector("#root")).render(
<StyledEngineProvider injectFirst>
<Box component="div" sx={{ display: 'inline' }}><Demo /></Box>
<Box component="div" sx={{ display: 'inline' }}><Demo /></Box>
<TextField />
<TextField />
</StyledEngineProvider>
);
Result: even in this case nothing changed.
I cannot figure out what is wrong. Am I missing something?
You may give a try to Stack Component available in doc.
<Stack direction="row" spacing={2}>
<Demo />
<Demo />
</Stack>
Edit:
I have looked at your Demo.js and found this solution. It may help you.
Adding this line in <Autocomplete>
sx={{display : 'inline-flex', width : '50%' }}
may solve your problem as it does in sandbox.
Here's sandbox.
Also width may cause problem.

Is it possible to use the ListContext in own components?

I would like to use the dense property of the ListContext to change the ListItemIcon fontSize. When I try to wrap the component in my own and import and use the context, the context is empty. The component is used inside as List/ListItem so it normally should have some values. I suppose it is because the ListContext is only use internally or am I wrong? In the case that I cannot use it what's a possible solution?
import React from 'react';
import MuiListItemIcon, {
ListItemIconProps as MuiListItemIconProps,
} from '#material-ui/core/ListItemIcon';
import ListContext from '#material-ui/core/List/ListContext';
const ListItemIcon: React.ForwardRefExoticComponent<MuiListItemIconProps> = React.forwardRef(
(props, ref) => {
const { children, ...other } = props;
const context = React.useContext(ListContext);
return (
<MuiListItemIcon ref={ref} {...other}>
{context.dense ? React.cloneElement(children, { fontSize: 'small' }) : children}
</MuiListItemIcon>
);
},
);
export default ListItemIcon;
// ...
<List dense>
<ListItem>
<ListItemIcon>
<IcoCreate />
<ListItemIcon>
</ListItem>
<ListItemText primary="Hello world!">
<List>
// ...
I expect the context to have a propery dense that is set to true, but the actual value is empty.
ListContext is considered private. In the Minimizing Bundle Size guide you can find the following:
Be aware that we only support first and second level imports. Anything below is considered private and can cause module duplication in your bundle.
Your import of ListContext is a third-level import (List would be second-level):
import ListContext from '#material-ui/core/List/ListContext';
While it "works" to import it, you are importing a duplicate copy of ListContext rather than the same one used by List which causes this to be a unique context type and is therefore unaffected by the setting of ListContext internal to Material-UI.
There are several ways you could achieve your desired result of dynamically adjusting the icon font-size based on whether or not it is within a dense list:
Create your own List wrapper (in addition to your ListItemIcon wrapper) with its own context type that you use in your ListItemIcon wrapper.
Use CSS (imitating what SvgIcon does internally based on the prop) to change the font-size rather than using the fontSize property as shown in my example below.
import React from "react";
import ReactDOM from "react-dom";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import CreateIcon from "#material-ui/icons/Create";
import { withStyles } from "#material-ui/core/styles";
const SmallIconIfDense = withStyles(theme => ({
root: {
".MuiListItem-dense & .MuiSvgIcon-root": {
fontSize: theme.typography.pxToRem(20)
}
}
}))(ListItemIcon);
function App() {
return (
<>
<List dense>
<ListItem>
<SmallIconIfDense>
<CreateIcon />
</SmallIconIfDense>
<ListItemText primary="Hello dense list!" />
</ListItem>
<ListItem>
<SmallIconIfDense>
<CreateIcon />
</SmallIconIfDense>
<ListItemText primary="Hello second list item!" />
</ListItem>
</List>
<List>
<ListItem>
<SmallIconIfDense>
<CreateIcon />
</SmallIconIfDense>
<ListItemText primary="Hello non-dense list!" />
</ListItem>
<ListItem>
<SmallIconIfDense>
<CreateIcon />
</SmallIconIfDense>
<ListItemText primary="Hello second list item!" />
</ListItem>
</List>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Problem with showing a component which is in another file onclick

I am currently building an website in React with a navigation bar which I use Material-UI for.
My problem is that when I for example click "About" in my navigation bar, I want to show the content/component in About, and when I click Home I want the component Home to be shown and others hidden.
The problem is I am still a beginner in React and want to practice my React skills and now I have the navbar, Home, About in seperate files and not sure on how to pass through state, props and so in this case.
I will show a screen shot on the website and code-snippets to show my code so far.
My website:
File structure of my program:
Here is Code:
App.js:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import NavBar from './Components/Navigationbar'
import Home from './Components/Home'
import About from './Components/About'
class App extends Component {
constructor(props){
super(props);
this.state = {showAbout: true};
this.handleAbout = this.handleAbout.bind(this);
}
handleAbout(){
this.setState({showAbout: true})
}
render() {
return (
<div className="App">
<div className="App-header">
</div>
<NavBar></NavBar>
<p className="App-intro">
<Home></Home>
</p>
{this.state.showAbout ? <About /> : null}
</div>
);
}
}
export default App;
Home.jsx:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Typography from '#material-ui/core/Typography';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(3, 2),
backgroundColor: 'mistyrose'
},
}));
export default function PaperSheet() {
const classes = useStyles();
return (
<div>
<Paper className={classes.root}>
<Typography variant="h5" component="h3">
Home
</Typography>
<Typography component="p">
Welcome Home
</Typography>
</Paper>
</div>
);
}
About.jsx:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Typography from '#material-ui/core/Typography';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(3, 2),
backgroundColor: 'mistyrose'
},
}));
export default function PaperSheet() {
const classes = useStyles();
return (
<div>
<Paper className={classes.root}>
<Typography variant="h5" component="h3">
About
</Typography>
<Typography component="p">
About
</Typography>
</Paper>
</div>
);
}
And finally the navigation bar which is from Material UI:
Navigationbar.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import Button from '#material-ui/core/Button';
import App from '../App';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import About from './About';
const useStyles = makeStyles({
root: {
flexGrow: 1,
},
});
function handleAbout(props){
alert('About');
}
const navBar = (props) => {
return (
<Paper >
<Tabs
//value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
centered
>
<Tab label="Home" />
<Tab label="About" onClick={() => handleAbout(props)} />
<Tab label="Contact" />
</Tabs>
</Paper>
);
}
//ReactDOM.render(<navBar />, document.querySelector('#app'));
export default navBar;
My problem is I want to when I click "About" in the navbar, I want to show the About component(the content in About.jsx) on my website but not sure on how to handle state and props in the case when they are in seperate files.
Would appreciate if someone could help me.
Thanks a lot.
You can use react-router for navigation. How to install and use it is quite nicely shown on the page: https://reacttraining.com/react-router/web/guides/quick-start
Oh boy, this is a big one...
In the simplest case, you pass state though props like this:
<ChildComponent showAbout={this.state.showAbout}/>, and access it in ChildComponent by props.showAbout (or this.props.showAbout if it's a class component).
But things can get complicated as your application scales. Values can only be passed through props downwards inside the component tree; in other words, a component can only see a state that's somewhere above it. You can't use state from a sibling component or a component below it.
And that's the whole reason state management libraries exist. They provide a 'global' state that is available anywhere in the app. Redux is one of them.
You should sit down and learn Redux, as you can't really make a big app without a state management tool.
Another thing you should learn is react-router, for client-side routing.
Those things combined will provide a powerful tool for making useful apps.

Can't include React UI frameworks (Grommet)

I have problems importing UI libraries, I had problem with ant design library so I decided to try different one, now I have problem importing Grommet.
What am I doing wrong? I added dependencies according documentation and added examples included in documentation yet still not working.
I am trying to execute this code from documentation
But it doesn't look the same at all
I work with codesandbox.io, here is link for the code on it
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Heading from "grommet/components/Heading";
import Box from "grommet/components/Box";
import Header from "grommet/components/Header";
import Title from "grommet/components/Title";
import Search from "grommet/components/Search";
import Menu from "grommet/components/Menu";
import Anchor from "grommet/components/Anchor";
import "grommet-css";
class HelloWorldApp extends React.Component {
render() {
return (
<div>
<Header>
<Title>Sample Title</Title>
<Box flex={true} justify="end" direction="row" responsive={false}>
<Search
inline={true}
fill={true}
size="medium"
placeHolder="Search"
dropAlign={{right: "right"}}
/>
<Menu dropAlign={{right: "right"}}>
<Anchor href="#" className="active">
First
</Anchor>
<Anchor href="#">Second</Anchor>
<Anchor href="#">Third</Anchor>
</Menu>
</Box>
</Header>
<Box>
<Heading>
Hello, I'm a Grommet Component styled with
grommet-css
</Heading>
</Box>
</div>
);
}
}
var element = document.getElementById("root");
ReactDOM.render(<HelloWorldApp />, element);
So according to your code:
<Menu dropAlign={{right: "right"}}>
was missing the icon attribute, without which the Menu component directly renders the Anchor, the menu-items component.
add import for the icon of your choosing, i.e: Actions ( from the example )
import Actions from 'grommet/components/icons/base/Actions';
add the icon attribute to the Menu component:
<Menu
icon={<Actions />}
dropAlign={{ right: 'right' }}
>
<Anchor href="#" className="active">First</Anchor>
<Anchor href="#">Second</Anchor>
<Anchor href="#">Third</Anchor>
</Menu>
here's a codesandbox.io link which fixes your issue:
https://codesandbox.io/s/237xo7y48p

CardHeader is not rendering in React

I'm playing with React and material-ui, other stuff have worked but for some reason <CardHeader> is not getting rendered.
Below is the code
import React from "react";
import Card from "#material-ui/core/Card";
import CardHeader from "#material-ui/core/Card";
import CardContent from "#material-ui/core/Card";
import CardActions from "#material-ui/core/Card";
import Button from "#material-ui/core/Button";
import Paper from "#material-ui/core/Paper";
import Typography from "material-ui/Typography";
class PaperCard extends React.Component {
render() {
return (
<Card>
<CardHeader title="CaHellord" subheader="Test ME" />
<CardContent>
<Typography type="title">Word of the day</Typography>
<Typography type="headline" component="h2">
impediments
</Typography>
<Typography component="p">adjective</Typography>
</CardContent>
<CardActions>
<Button dense> Learn More </Button>
</CardActions>
</Card>
);
}
}
export default PaperCard;
Could someone point out the obvious which I have missed.
It's just the case of incorrect import.
Replace the import of CardHeader from
import CardHeader from "#material-ui/core/Card";
to
import CardHeader from "#material-ui/core/CardHeader";

Resources