Material-UI Select (or Select styling) with a custom Popper? - css

Problem:
I've been trying to figure out the right/best way to use a Material-UI Select component with a customized Popper for its dropdown menu. What I'm hoping to do is make something that looks like the following (but with Material-UI components or keeping the styling of a Material-UI Select component with the 'outlined' variant):
Options I've considered:
1. Simply customize the children of the Select component.
I've tried the following, however, it seems like the Popper component still closes if it is clicked on anywhere and I can't seem to figure out how to stop that.
import * as React from "react";
import { FormControl, InputLabel, Select } from "#mui/material";
export default function BasicSelect() {
return (
<FormControl sx={{ minWidth: 120 }}>
<InputLabel id="demo-simple-select-label">Dropdown</InputLabel>
<Select
label="Dropdown"
MenuProps={{
sx: { marginLeft: "-8px" },
onClick: (e) => { e.preventDefault(); e.stopPropagation(); }
}}
>
<div onClick={(e) => { e.preventDefault(); e.stopPropagation(); }}>
Test
</div>
</Select>
</FormControl>
);
}
2. Apply Select component css components/styles to a button or div that opens a Popper.
To start with, I tried the following but it didn't seem to apply the MUI select styling to the div:
import * as React from "react";
export default function Test() {
return (
<div className={`MuiSelect-select`}>
Test
</div>
);
}
Probably for obvious reasons that the element the class is being applied to is not a select (but I don't want it to be).
3. Write out css from scratch that tries to mimic the Select styling as close as possible.
Would be cumbersome, but potentially doable, I suppose.
What would you suggest or recommend? Thanks!

Related

Toggle Function Using 2 Separate Buttons in 2 Different Components for Mobile Navigation

I have a button in the Header component to toggle the mobile nav open and that is working fine. I have a instance of this button within the mobile menu that I want to close the mobile nav. Clicking the 2nd instance of the mobile nav toggle button in the mobile nav gives an error: Typeerror toggleMobile is not a function
I know I can make this work with a single instance of the button, but would really like to figure out how to get this working with two instances of the button.
Here is simplified code from the 3 components involved.
Simplified Header.js - this part is working as expected
export default function Header() {
const [mobileOpen, setMobileOpen] = useState(false)
const toggleMobile = (toggleValue) => {
setMobileOpen(toggleValue)
}
return (
<Box>
{mobileOpen && <MobileNav toggleMobile={toggleMobile} />}
<MobileIcon toggleMobile={toggleMobile} toggleValue={true} />
</Box>
)
}
MobileIcon component - this works when triggered from the Header component
function MobileIcon({ toggleMobile, toggleValue }) {
return (
<Link href="#">
<Icon
onClick={() => toggleMobile(toggleValue)}
display={{base: 'flex', md: 'none'}}
as={RiMenu2Line} w={8} h={8} mr={8}>
</Icon>
</Link>
)
}
export default MobileIcon;
Simplified MobileNav component - this is the problem. Both Header and MobileNav components are rendering the same MobileIcon component
function MobileNav( toggleMobile ) {
return (
<MobileIcon toggleMobile={toggleMobile} toggleValue={false}/>
)
}
export default MobileNav;
Clicking this instance of the MobileIcon gives the error: 'TypeError: toggleMobile is not a function' and references the toggleMobile function in the MobileIcon component.
I'm guessing this has something to do with how I'm passing the function reference twice in Header. I've tried a lot of different solutions, and can't figure it out.

using material-ui for react in a wordpress plugin: css specificity problems

Im trying to properly use material-ui for react in a wordpress plugin.
Problems arise when using inputs for forms and the styling being overwritten by wordpress own styling in the forms.css file.
Im struggling to find a solution for material-ui writing css that has better specificity than forms.css without having to rewrite the styling(I dont wont my own custom styling for material-ui components: I want to use the default one)
Here is an example(text has been edited for readability(removed selectors that dont matter and styling):
react jsx:
<div id='ad-campaign-edit' style={modalStyle} className={classes.root}>
<form
className={classes.form}
noValidate
autoComplete='off'
>
<TextField
id='ad-campaign-name'
className={classes.textField}
variant='filled'
label='Navn på kampanjen'
/>
<TextField
id='ad-campaign-sub-title-text'
className={classes.textField}
variant='filled'
label='Undertekst'
/>
<TextField
id='ad-campaign-sub-price'
className={classes.textField}
variant='filled'
label='Pris'
/>
<Checkbox
defaultChecked
className={classes.textField}
color='primary'
inputProps={{ 'aria-label': 'secondary checkbox' }}
/>
</form>
</div>
forms.css(WP):
input[type="text"], {
...
}
generated css from material-ui:
.MuiInputBase-input {
...
}
other things to note: the css from material-ui is generated after the styling from wordpress in the dom.
Looking at the rules for css specificity(ref www.specifishity.com) forms.css is (0-1-1) and material-ui is (0-1-0).
My question is: how can I make material-ui take presedence without having to rewrite all the css with something like the makeStyles hook?(Can I add better specificity to material-ui in general?)
edit: Forgot to add html/jsx
edit2: rewrote specificity understanding.
I solved it, it's working but not completely.
This solution can be a starting point.
You need to install this npm
https://www.npmjs.com/package/jss-increase-specificity
Then you have to create a <StyleProvider> like described here
https://material-ui.com/styles/advanced/#jss-plugins
What I did
import { create } from 'jss';
import { StylesProvider, jssPreset } from '#material-ui/core/styles';
import increaseSpecificity from 'jss-increase-specificity';
const jss = create({
plugins: [...jssPreset().plugins, increaseSpecificity({ repeat: 1 })],
});
export default function App() {
return (
<StylesProvider jss={jss}>
...
</StylesProvider>
);
}

ReactJS advanced custom styling

Using reactjs only, is it possible to do advanced styling similar to
#primary-nav .list-group-item.active {}
// or
#secondary-nav .list-group-item:hover>.background-item {}
in the first example I could do some rather simple javascript logic to figure out if the component is "active" but on the second example it's just so much simpler with css.
Is there a clear react+js solution for these situations that comes close to the simplicity of css?
className is applied exactly like a regular HTML class. So to correctly target .background-image like in
.list-group-item:hover>.background-item
Your jsx structure should look like
import './index.css'
const Component = () =>{
return(
<div className='list-group-item'>
<div className='background-item' />
<span>
<div className='background-item' /> /*Targeting nested items '>' */
</span>
</div>
)
}
You can use jss and clsx to have dynamic and conditional styles. Here is an example using MUI styles(hooks API), but you can use styled components, react-jss or implement you're own style's solution based on jss.
import { makeStyles } from '#material-ui/styles'
import clsx from 'clsx'
const styles = {
root:{
color: 'white',
'&:active':{
color: 'red'
}
},
hidden:{
opacity: 0
}
}
const useStyles = makeStyles(styles)
const Component = ({ open }) =>{
const classes = useStyles()
const rootStyle = clsx({
[classes.root] : true,
[classes.hidden] : !open
})
return <div classsName={rootStyle} />
}
jss also have lots of cool features like theming support, styles interpolation (a personal favorite), nested selectors, style's rules,etc. Definitely worth taking a look.
Notice that clsx doesn't require jss to work, it's just a helper to conditionally apply classes. You can use it like clsx({'foo' : true, 'bar': false})

Material UI - Facing an issue that drop down options are coming below the modal window footer

Hi I am using material ui and react select . I am facing an issue that my drop-drown options are showing below the modal window.
Here is the codesandbox link
I tried z-index and change the position value to absolute but did not get the success. Please help.
This happens because of overflow-y rule in two places: the dialog paper, and the dialog content.
simply use material-ui styling to override this rules:
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
paperFullWidth: {
overflowY: 'visible'
},
dialogContentRoot: {
overflowY: 'visible'
}
});
And than apply this classes to your component:
const classes = useStyles();
...
<Dialog
...
fullWidth={true}
classes={{
paperFullWidth: classes.paperFullWidth
}}
>
...
<DialogContent
classes={{
root: classes.dialogContentRoot
}}
You can refer this CodeSandbox demo

react-transition-group transition initial render without props/state

I'm currently implementing a Modal component within my app, using Portals. What I'm trying to achieve is that when the Modal component is rendered, it should fade in and when it's no longer rendered it should fade out.
Looking at the react-transition-group docs it appears that props have to be used in order to achieve this, however I'd like a solution without any form of state or props. Consider:
<App>
<Modal />
</App>
App should load as normal, however Modal should fade in. When Modal is no longer rendered based on some condition way above these components, it should fade out.
Here's the actual code for
import Fade from "../Fade";
import { TransitionGroup } from "react-transition-group";
const Modal = ({ children, ...other }) => {
return (
<Portal>
<TransitionGroup>
<Fade>
{children}
</Fade>
</TransitionGroup>
</Portal>
);
};
And for the Fade component:
import { CSSTransition } from 'react-transition-group';
const Fade = ({ children, ...other }) => (
<CSSTransition
{...other}
timeout={500}
classNames={{
//my classes here
}}
>
{children}
</CSSTransition>
);
Any suggestion? I'm sure this used to work, before React 16 and react-css-transitions changed to react-transition-group but I'm having a very hard time figuring this out.
To be clear, I can transition using state/props and have a working example of this but that is not what I'm trying to achieve. I'd like to fade in when it is rendered, and fade out when it is un-rendered...
Thanks!
appear={true}
Seems to fix this for now, although there's no way to animate the reverse (component being un-mounted).

Resources