Altering multiple component roots for MUI Textfield - reactjs

According to the MUI Texfield API here, Textfield is a simple abstraction on top of the following components
FormControl
Input
InputLabel
FilledInput
OutlinedInput
Input
FormHelperText
And therefore, to change the styling of the Textfield for any of the components above, like for example notchedOutline class, which is a class for OutlinedInput, i can just do the following
import { TextField } from '#material-ui/core';
const style = theme => ({
notchOutline: { /*style in here*/ }
});
<TextField
inputProps={{ notchedOutline : classes.notchedOutline }}
>
</TextField>
All of this can be achieved if that subcomponent classes are unique for that component only.
My question is, how can i style for the more common naming class, like if say i wanna modify the root classes of OutlinedInput, InputLabel, FormHelperText or more subcomponents inside the TextField all at once? I dont think this will work right?
<TextField
FormControlProps={{ root: classes.root }}
OutlinedInputProps={{ root: classes.root, notchedOutline : classes.notchedOutline }}
>
</TextField>
or
<TextField
inputProps={{
root: classes.OutlinedInputRoot,
root : classes.FormHelperTextRoot
}}
>
</TextField>
Need help on how to aim the specific root of a subcomponent of a TextField, without needing to touch on the global MUI theming, or not using the provided TextField at all, instead building the textfield component using those subcomponents on it.

Below is an example showing how to target each of these.
Targeting TextField root is equivalent to targeting FormControl, since FormControl is the "root" component rendered by TextField.
There is no difference in how to target Input, FilledInput, or OutlinedInput -- they are all reached via InputProps.
As a side note, using the className prop for a given component is also equivalent to classes.root.
import React from "react";
import ReactDOM from "react-dom";
import TextField from "#material-ui/core/TextField";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
formControlRoot: {
border: "2px solid lightgreen",
padding: 2,
marginTop: 10
},
inputRoot: {
border: "2px solid blue"
},
inputLabelRoot: {
border: "2px solid pink"
},
formHelperTextRoot: {
border: "2px solid red"
}
});
function App() {
const classes = useStyles();
const [variant, setVariant] = React.useState("standard");
return (
<div>
<TextField
variant={variant}
label={`My Label (${variant})`}
helperText="My Helper Text"
classes={{ root: classes.formControlRoot }}
InputProps={{ classes: { root: classes.inputRoot } }}
InputLabelProps={{ classes: { root: classes.inputLabelRoot } }}
FormHelperTextProps={{ classes: { root: classes.formHelperTextRoot } }}
/>
<br />
<br />
<button onClick={() => setVariant("standard")}>Standard</button>
<button onClick={() => setVariant("outlined")}>Outlined</button>
<button onClick={() => setVariant("filled")}>Filled</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Relevant documentation: https://material-ui.com/api/text-field/#props

answering this will depend specifically on the material ui version you are using, but i'm going to assume you are using version > 3.
All the following should be working in version 3.9 as i have used them myself, but they are also supposed to work fine with version > 4;
input props are used to pass props to the underlying regular html input element directly, you can pass things like style, max, value, onchange. Things that are native to the html input element.
If you want to pass classes to the underlying material ui input you need to pass a classes object to the InputProps.
Here is how
<TextField
variant="outlined"
// this passes props to the html element, root for example here does not mean anything
inputProps={{
style: { textAlign: 'center' },
}
// this passes props to the wrapper material input, can be one of the following: Input, FilledInput, OutlinedInput
// You can pass here anything that the underlying material component uses but we are only interested in two things classes and className, because other props like value and onChange you can pass directly to TextField - thats why they said TextField is an Abstraction over theses Components
InputProps={{
className: styles.slider_filter_input, // usually you dont need className, classes will be sufficient, but wanted to show that you can also use it
classes: {
focused: classes.focused
// the list of keys you pass here depend on your variant, if for example you used variant="outlined" then you need to check the css api of the OutlinedInput
}
}}
/>
Finally here is a working codesandbox showing the ideas above https://codesandbox.io/s/material-ui-drawer-8p6wv

Related

MUI: customize button component with tailwind classes

I'm trying to customise the look of MUI's <Button /> component. As i want to do it by using tailwind classes i tried to use defaultProps instead of styleOverrides.
const lightTheme = createTheme({
components: {
MuiButton: { defaultProps: { classes: { root: 'p-8' } } },
},
});
It is working fine except that i'm not able to add tailwind classes when i use the <Button /> component as this will overwrite the changes i made above.
<Button variant="contained" classes={{ root: 'p-12' }}>
Ok
</Button>
Is there a way to extend the definition of the classes prop rather than overwriting it?

Incorrect classnames in Material UI with styled components inside react frame

I am using react-frame-component to load a custom component I created which is using material ui and styled-components.
The custom component I created is irrelevant but contains the following relevant code:
const StyledCardHeader = styled(({ isRTL, ...rest }) => (
<CardHeader {...rest} />
))`
${({ theme, isRTL }) => `
& .MuiCardHeader-title {
margin-right:${isRTL ? 0 : theme.spacing(2)}px;
margin-left: ${isRTL ? theme.spacing(2) : 0}px;
}
`};
`;
When it renders, the actual classname becomes something else than I expect: MuiCardHeader-title-34 (it adds the 34 as suffix - random number).
Therefore, my custom styled is not being applied.
Here's a sandbox of the above:
https://codesandbox.io/s/sparkling-field-ui82v
You can look into overriding the MUI CardHeader CSS based off of the docs.
<CardHeader
{...rest}
classes={{
title: makeStyles({
customTitleMargin: {
marginRight: `${isRTL ? 0 : theme.spacing(2)}px`,
marginLeft: `${isRTL ? theme.spacing(2) : 0}px`,
}
})().customTitleMargin
}}
/>
I did not want to mess with your code too much so in my example, I just plugged in the makeStyles export from #material-ui/styles, but the logic of future implementations is similar in that you should just override the MUI component itself.
CodeSandBox: https://codesandbox.io/s/lucid-joliot-5mf8n?file=/src/Card.js

Material UI react testing custom styles

I'm struggling to come up with a sensible way of asserting that a given style will be added to a Material-UI component when a given prop is specified
import makeStyles from "#material-ui/core/styles/makeStyles";
const useStyles = makeStyles({
myRoot: props => props.inverse && {
border: "2px solid red",
},
})
const MyTextInput = ({ inverse, ...otherProps }) => {
const classes = useStyles({ inverse });
return (
<TextField
label="Standard secondary"
color="secondary"
classes={{
root: classes.myRoot,
}}
/>
);
}
// with inverse
<MyTextInput
label="Standard secondary"
color="secondary"
inverse={true}
/>
// without inverse prop
<MyTextInput
label="Standard secondary"
color="secondary"
/>
Is there a sensible way to test that the correct class has been applied?
I notice that when I render the component multiple times, the className + a number will be added to all instances e.g. makeStyles-myRoot-*.
Is there way of confirming that the correct style has been applied?
I'm using react-testing-library if that helps.

Material UI - Change Font Size of TextField from Functional Component

I have the following functional component and I would like to change the font size of the textfield, but for some reason I can't figure it out. I know if I have a traditional component I can export it withStyles and set the className or InputProps, but I'm not sure how to do that with my current setup:
Class Definition:
const FormObjectText = ({id, multiline, onBlur, onChange, onFocus, placeholder, value, style, ...additionalProps}) => (
<TextField
{...additionalProps}
fullWidth
id={id}
inputProps={additionalProps.maxLength != null ? {maxLength: additionalProps.maxLength} : {}}
margin="normal"
multiline={multiline}
onBlur={onBlur}
onChange={e => onChange({ value: e.target.value })}
onFocus={onFocus}
placeholder={placeholder}
style={{
...style
}}
value={value !== null ? value : ""}
/>
);
Export from that file:
export const FORM_OBJECT_DICT = {
text: FormObjectTextStyled,
date: FormObjectDate,
// Others
};
Where it is called in another file:
{FORM_OBJECT_DICT["text"]({
value: editing ? value : getFormObjectDisplayValue(configuration, value),
onChange: this.onChange
})}
Firstly, is there any way to change the font size of the TextField using inline styles (not withStyles()), or if not, where/how would i apply withStyles() in this instance?
You can change the font size with inline styles this way:
<TextField inputProps={{ style: { fontSize: "5rem" } }} />
There is nothing about withStyles that cares whether your component is a function component or a class, so if you want to use classes, you could do something like:
const FormObjectTextStyled = withStyles(styles)(FormObjectText);
and then access the classes prop inside FormObjectText.
Here's a sandbox showing both approaches:

How can I make Dialog take 80% of the screen in Material-UI?

I am working with Material-UI Dialog and I want to make it take 80% of the screen.
Ideally, I want something like this.
I am applying a margin to Dialog but it is not working as intended.
For older material-ui versions like 0.20.0:
<Dialog
title="Dialog With 80% Width"
modal={true}
contentStyle={{
width: '80%',
maxWidth: 'none',
}}
open={true}
>
This dialog spans the 80% width of the screen.
</Dialog>
And in material-ui V1 using these props may can help with your needs
fullWidth={true}
maxWidth = {'md'}
Here is examples and other props for Dialog component, or in more advanced way you can take a look into Dialog component code see what is happening there.
The paperFullWidth css class of Dialog component might be helpful. The only condition for using this class is the fullWidth prop of Dialog component should be true. Below is a sample snippet
import React from "react";
import Dialog from "#material-ui/core/Dialog";
import { withStyles } from "#material-ui/core/styles";
const styles = theme => ({
dialogCustomizedWidth: {
'max-width': '80%'
}
});
const DialogExample = ({ classes }) => (
<Dialog
open
fullWidth
classes={{ paperFullWidth: classes.dialogCustomizedWidth }}
>
I'm a Dialog with customized width.
</Dialog>
);
export default withStyles(styles)(DialogExample);

Resources