Regular ClassName with Material UI - reactjs

I've read some answers to similar questions but did not fully understand because of materialUI update where #mui/styles are depricated.
According to mui docs we can create custom styling using sx prop, smth like this
<Typography variant="h5" sx={{backgroundColor: 'red' }}>Header</Typography>
But when I use regular classNames in v5 it works well too:
//css
.makeRed {
backgroundColor: red
}
// jsx
<Typography variant="h5" className="makeRed">Header</Typography>
The question is - does using regular classes has pitfalls and I need to rewrite mui classes?
(have started my mUI jorney yesterday)

I've been brushing up on Material for a new job and I've found that if you want to make things more easily changeable and run faster you'll work through the framework. The purpose of Material is for enhanced reusability, so you'll want to work with that purpose and align your style in everything you do within the app towards everything being the same.
If anything your best bet is to do:
const useStyles = makeStyles({
makeRed: {
background-color: 'red'
}
})
export default useStyles;
Then in the actual component just have your classes const as.
const classes = useStyles();
<Typography className = {classes.makeRed} />

Related

Customizing inner components in Material-UI and styled-components

Background:
Our app uses #material-ui/core and we're not ready to upgrade to #mui/material.
Our app uses React 16.14.0 and Node 10.15.3, and we're not ready to upgrade.
We use styled-components and have configured Material UI to work with it.
Now that that's out of the way:
I'm having a lot of trouble targeting the inner components. I want to style the outline of a <TextField variant="outline"/>.
MUI's how-to guide says that the inner components should be discoverable in the devtools with classes like MuiInputBase-input. However, my app is giving me MuiInputBase-input-48, and the number is not guaranteed to stay constant between renders.
So I thought I had it with this, but sometimes I'll refresh and lose it because the classnames will change:
const StyledInput = styled(TextField).attrs(p => ({
inputProps: { inputMode: "numeric", pattern: "[0-9]*", color: "white" },
}))`
.MuiOutlinedInput-root-25 .MuiOutlinedInput-notchedOutline-32 {
border-color: ${colors.lightGrey};
}
`;
const SDCurvedInput = (props) => (
<StyledInput
{...props}
id={props.label}
variant="outlined"
/>
);
As a test I tried replacing StyledInput with simply TextField in the markup because I thought maybe styled-components was doing something to add this number, but no luck.

React/Typescript/mui- Help understanding abstraction over a third party component

We started a project and implemented several pages using material ui's React library. We use React hooks exclusively. I was told to create a wrapper around the mui DataGrid. The purpose behind the decision was that a particular page would need functionality that the DataGrid probably doesn't have. A special case, and one which we don't know the specific needs of at this point. I'm a relative newbie with React and Typescript and I don't even know how you go about creating wrappers, especially with a component as large and feature rich as mui's DataGrid. Where to start? I started like this:
import {
DataGrid as DataGridWrapper,
GridColumns,
GridLocaleText,
GridRowsProp,
GridFilterModel,
GridSortModel,
GridCallbackDetails,
} from "#mui/x-data-grid";
import Tooltip from "#mui/material/Tooltip";
import IconButton from "#mui/material/IconButton";
import AddBoxIcon from "#mui/icons-material/AddBox";
import { esES } from "#mui/x-data-grid";
import { enUS } from "#mui/x-data-grid";
export interface GridProps {
style?: React.CSSProperties;
gridColumns: GridColumns;
gridRows: GridRowsProp;
gridPageSize?: number;
showAddButton?: boolean;
onAddButtonClick?: () => void;
onFilterModelChange?: (model: GridFilterModel, details: GridCallbackDetails) => void;
onSortModelChange?: (model: GridSortModel, details: GridCallbackDetails) => void;
}
function DataGrid({
style,
gridColumns,
gridRows,
gridPageSize = 10,
showAddButton = true,
onAddButtonClick,
onFilterModelChange,
onSortModelChange,
}: GridProps) {
const strings = useLocalizedStrings();
// use text translations from the grid and apply them to all pages
let localeTextTranslations : Partial<GridLocaleText>
if (strings.getLanguage() === "es") {
localeTextTranslations = esES.components.MuiDataGrid.defaultProps.localeText;
} else {
localeTextTranslations = enUS.components.MuiDataGrid.defaultProps.localeText;
}
return (
<>
{showAddButton && (
<div style={{ textAlign: "right" }}>
<Tooltip title="Add">
<IconButton
aria-label="add"
color="primary"
onClick={() => {
onAddButtonClick?.()
}}
>
<AddBoxIcon />
</IconButton>
</Tooltip>
</div>
)}
<div style={{ height: 700, width: "100%" }}>
<div style={{ display: "flex", height: "100%" }}>
<div style={{ flexGrow: 1 }}>
<DataGridWrapper
columns={gridColumns}
rows={gridRows}
style={style}
hideFooterSelectedRowCount={true}
pageSize={gridPageSize}
onFilterModelChange={onFilterModelChange}
onSortModelChange={onSortModelChange}
/>
</div>
</div>
</div>
</>
);
}
export default DataGrid;
At this point I started wondering what the benefit of doing this is, if I'm simply duplicating the mui interface. Plus, I don't know how to handle the typings. Do I create new types that wrap mui's types? That seems like it could get complicated pretty fast as everything in mui has underlying types and so do those types, many levels deep. Or should consumers import the types from mui directly like I have here where I'm typing my props as mui types (but that seems wrong and against the idea of abstraction).
So if I'm not really encapsulating logic and am instead simply renaming props, does this even make sense to do? How does one approach such a task? In the end, this just feels weird so far. To make all pages use something in basically the same way you would use the actual mui grid. I feel like I'm missing something.
Can someone give me an example of some patterns I would use to make this actually be the start of a proper wrapper? And how to handle the typings required by mui and translating those to what's exposed to consumers? Or any thoughts at all? I searched for creating 3rd party wrapper components in React and only saw stuff with classes and higher order components. Nothing with hooks. In the end, I guess my specific questions are:
how to create a basic wrapper?
how to handle typings of the thing you wrap?
any considerations I should keep in mind?
I almost don't know what I don't know so it's difficult to be specific. I wish I could find some examples.

Material UI - Overriding styles using Root & using ClassName

I'm new to material UI. While learning, I came to know out that styles of a material UI can be overridden with the rule name of the classes.
If I have an element - MenuItem where I just need to change the default styling of the text (such as fontFamily, fontWeight, fontSize)
According to the documentation available here https://material-ui.com/api/menu-item/ I used makeStyles hook and have overridden some properties of the root element of Menu-Item
Sample code
const useStyles = makeStyles((theme) => ({
menuItem: {
fontFamily: "Raleway",
}
}));
JSX code: <MenuItem onClick={handleClose} component={Link} to="/services" classes={{root: classes.menuItem}}>Services</MenuItem>
In one more tutorial, I found another way of overriding - with className like
const useStyles = makeStyles((theme) => ({
menuItem: {
fontFamily: "Raleway",
}
}));
JSX code: <MenuItem onClick={handleClose} component={Link} to="/services" className={classes.menuItem}>Services</MenuItem>
My question lies in this part className={classes.menuItem} and classes={{root: classes.menuItem}}
On using root -> I see the css properties getting added to the root element but on using className={classes.menuItem} I see a new class is added to the DOM. But Is there any difference with respect to the behaviour of the app between these 2 methods or is it just another way of doing it?
Thanks
From what I can tell having worked with Material UI, the difference is with what you're trying to do.
className={classes.menuItem} and classes={{root: classes.menuItem}} will both get you the results that you're expecting, as you've already discovered.
The distinctions are important when you start building custom themes, because Material UI allows you to apply classes to the components as a whole, meaning potentially comprised of other components, or pass along styles to the root elements if you need a higher specificity.
For example, consider the Stepper component.
classes={{root: classes.menuItem}} will override the root in the component, applying your styles.
className={classes.menuItem} will add an additional class to the component, leaving the underlying styles intact.

How to pass padding/margin as props in React-Bootstrap components

I am trying to apply margins and paddings with React-Bootstrap as props.
I passed the docs through but haven't found any mention adding padding or margin in there as it is in official bootstrap docs (3th and 4th). I know it doesn't support well Bootstrap 4, so tried with both.
I tried to pass params as p={1}, paddingxs={5} or mt='1' but it doesn't recognize any of them. More over tried to find any Spacing element in React-Bootstrap folder, but failed.
Paddings and margins work as classnames. But I feel there must be a way to it without Bootstrap classes. There must be a kind of property.
First include bootstrap CSS in your src/index.js or App.js
import 'bootstrap/dist/css/bootstrap.min.css';
Then you can style your component by passing desired bootstrap CSS class name as className prop in React, for example:
import React from "react"
import Container from "react-bootstrap/Container";
function MyComponent() {
return (
<Container fluid className="p-0">
<SomeOtherComponent />
</Container>
);
}
export default MyComponent
Above code will add p-0 CSS class to Container.
Reference
React - How do I add CSS classes to components?
React-Bootstrap - Stylesheets
You can add margin and padding by using default React's style:
const divStyle = {
marginLeft: '10px',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}
Refrenced from here
The answer is: there is no props from React Bootstrap to use margins/paddings.
You can use props for col class, but no for margins.
Example:
<Col className="col-6 col-md-3 mb-3 pt-2">
// there you have a Col component from React-Bootstrap 4
// it has some grid system classes, that you can use as props like this:
https://react-bootstrap.github.io/layout/grid/
<Col xs={6} md={3} className="mb-3 pt-2">
// but as you can see, the native classes of Bootstrap 4 like
// mt, mb, pt, pb etc, they have not a props use with
// React-Bootstrap, you have to use them like regular classes
// inside "className"
You'll want to add className="p-5" to the element. This isn't documented in react-bootstrap but it's in the original Bootstrap 4 documentation here: https://getbootstrap.com/docs/4.4/utilities/spacing/#examples
Usually, I'm able to add custom styles to React Bootstrap components by just adding them to the style param. I've put together a short example below, hope this helps.
import React from 'react';
import { Button } from 'react-bootstrap';
const styles = {
myCoolButton: {
paddingTop: "10vh",
paddingBottom: "10vh",
paddingRight: "10vw",
paddingLeft: "10vw"
}
}
const ReactButton = (props) => {
return (
<Button style={styles.myCoolButton} onClick={()=> {
console.log("Do something here!")
}}>Click Me!</Button>
);
}
export default ReactButton
You can also pass custom components (including those from react-bootstrap) into the styled-components constructor if you prefer to do it that way.
None of the jQuery-free implementations of Bootstrap (React Bootstrap, BootstrapVue or ngBootstrap) have implemented utility directives for spacing (margin/padding), simply because Bootstrap have made it unnecessary in the vast majority of cases, by providing a very intuitive set of Spacing utility classes.
All you need to do is apply the desired class.
To apply utility classes selectively, based on responsiveness interval (media queries), you could use a useMedia hook, as demoed here.
In a nutshell:
const interval = useMedia([
"(min-width: 1200px)",
"(min-width: 992px)",
"(min-width: 768px)",
"(min-width: 576px)"
],
["xl", "lg", "md", "sm"],
"xs"
);
(Based on useMedia from useHooks/useMedia).
You can now reuse this hook throughout your app to add media interval based logic.
Example usages:
// interval === 'sm' ? a : b
// ['xs', 'sm'].includes(interval) ? a : b
// negations of the above, etc...
Important: this particular implementation returns the first matching media query in the list.
If you need to map various media queries, to an object/map with true/false values, you'll need to modify getValue fn to return the entire list, along these lines:
const getValue = () => {
const matches = mediaQueryLists.map(mql => mql.matches);
return values.reduce((o, k, i) => ({...o, [k]: matches[i]}), {})
};
Working example here.
Obviously, you could expand on it and add/remove queries. However, be warned each query adds a separate listener so it could impact performance.
In most cases, the return of the first matching query (first example) is enough.
Note: if the above useMedia hook is not enough for your use case, a more robust and heavily tested solution for media-query listeners in JS is enquire.js. It's easy to use, incredibly light and thoroughly tested cross-browser/cross-device. I have no affiliation with it, but I have used it in various projects over the course of more than a decade. In short, I couldn't recommend it more.
Back to Bootstrap 4: in order to customize the $spacer sizes, follow the guide provided under Bootstrap's theming as it's actually about more than what we typically call theming (changing colors), it's about overriding default values of Bootstrap's SASS defaults, including responsivenss breakpoints, spacers, number of columns and many, many others. The one you're interested in is $spacer.
Simply write the overrides into an .scss file and import it in your root component. Example.
Note: a (simpler and more intuitive) option to customize Bootstrap is to do it visually, using bootstrap.build but it's typically a few minor versions behind (i.e. Bootstrap is now at v4.4.1 and the build tool is at v4.3.0).
The build customizer provides intuitive controls and real time visualization.
It allows export as .css or .scss.
Just try this out once according to your input and still if face any issue you can reach out.In below we have increased the .col padding with .px-md-5 and then countered then with .mx-md-n5 on the parent .row.
JSX:
import React from 'react'
import { MDBContainer, MDBRow, MDBCol } from 'mdbreact';
const SpacingPage = () => {
return (
<MDBContainer>
<MDBRow className="mx-md-n5">
<MDBCol size="6" className="py-3 px-md-5">Custom column padding</MDBCol>
<MDBCol size="6" className="py-3 px-md-5">Custom column padding</MDBCol>
</MDBRow>
</MDBContainer>
)
}
export default SpacingPage;
If you still have any kind of doubt on this then feel free to ask .

React component prop style

I am using from UI Materials the
(https://material-ui.com/demos/text-fields/)
component and I want to style the helper text.
I've tried
pstyle = {
"& div p": {
textAlign: "right"
}
}
and then pass it as style={this.pstyle} and it does not work.
Does anyone has a solution?
Thanks!
Update: This is the component and it's props: https://material-ui.com/api/text-field/
What am I trying to style is the helperText prop
first of all welcome!
As OliverRadini said, you cannot use & in the way you proposed, and this makes sense, since you are directly targeting a specific element, it is not required to start making complex selectors.
Talking about your task at hand, remember that TextField component in Material UI library is just a wrapper component for a complete form control including a label, input and help text.
Bare in mind that using Material UI components is aligned with Google's Material Design patterns, so the idea is to stick with their guidelines, but if you want to customize, this is what you can do, either use FormHelperTextProps as prop in the TextField or instead of using the TextField wrapper, just break it into its pieces (FormControl, InputLabel, Label, FormHelperText)
The first option is the easiest way to do it for simple customization as the one you want to achieve. Try something like:
Your CSS class
projDescHelperText: {
textAlign: "right"
}
Your Component
<TextField
id="project-description"
label="Project Description"
helperText="(0/300)"
FormHelperTextProps={className={classes.projDescHelperText}}
value={this.state.description}
/>
If you have some of your code you can share, I would be able to better tailor a response to you
I think you should set { "p": { textAlign: "right" } } instead of { "& div p": { textAlign: "right" } }
You should use:
<div styles={{textAlign: 'right'}}>
<p> 0/300</>
</div
So I've done it like this:
import { withStyles } from "#material-ui/core";
const style = {
smth: {
"& p": {
textAlign: "right !important"
}
}
}
<TextField
className={classes.smth}
/>
export default withStyles(style)(CustomTextField);
seems to be working fine for now. Thanks all for the help and the promptitude and #rdarioduarte your solution helped me a lot. Thanks a lot guys!

Resources