Material-ui input label out boxed when flexDirection is row - css

This is a little bit weird situation. I have 2 divs in a FormControl and my input label for Select completely fine. However, when I style it in flexDirection: 'row' The label is outboxed as you can see in below picture.
Edit: It works fine when I comment out justifyContent.
When it is ok.
When it is in flexDirection: row
Below is my code
const useStyles = makeStyles((theme) => ({
submitForm: {
display: 'flex',
flexDirection: 'row',
margin: theme.spacing(1),
justifyContent: 'space-evenly'
},
bacsInputs: {
minWidth: '200px'
}
}))
const DemoInventory = () => {
const bacsInfoInputs = () => {
return(
<>
<p><b>Enter BACS information below and click SAVE</b></p>
<FormControl variant="outlined" className={classes.submitForm}>
<div>
<InputLabel clasName={classes.submitForm} id="demo-simple-select-outlined-label">Select BACS Unit</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={bacsUnit}
onChange={onChangeInput}
label="BACS Unit"
className={classes.bacsInputs}
>
<MenuItem value={'Product-1'}>Product-1</MenuItem>
<MenuItem value={'Product-2'}>Product-2</MenuItem>
<MenuItem value={'Product-3'}>Product-3</MenuItem>
<MenuItem value={'Product-4'}>Product-4</MenuItem>
<MenuItem value={'Product-5'}>Product-5</MenuItem>
<MenuItem value={'Product-6'}>Product-6</MenuItem>
<MenuItem value={'Product-7'}>Product-7</MenuItem>
</Select>
<p>Serial number input</p>
<p>Part number input</p>
<p>Location input</p>
<p>description input</p>
</div>
<div>
<p>deployment input</p>
<p>ship Date input</p>
<p>Expected return date input</p>
</div>
<p>Save Button</p>
</FormControl>
</>
)
}
}
return (
<Fragment>
{bacsInfoInputs()}
</Fragment>
)

it is because you've provided a class classes.submitForm to the FormControl element which is changing its default style and thus you're getting a different style in the selectbox.
FormControl elements are used for wrapping the input elements only to get extra control and features, so don't put any other elements other than input elements inside this element. So put only the Select and InputLabel elements only in this case for better result.

Related

Prevent MUI multi-select from expanding parent flexbox past screen size

I have a multi-select MUI element, and when I select multiple items on a small screen, the elements make the parent larger, so you would have to scroll to the right to view everything.
How can I fixe this?
<Box
display={"flex"}
flexDirection={"column"}
rowGap={3}
sx={{
mb: 1,
px: 2,
}}
>
<FormControl>
<InputLabel id="simple-select-label">
Job completed by
</InputLabel>
<Select
label="Job completed by"
name="employeeID"
multiple={true}
id="simple-select"
sx={{
}}
required
value={values.employeeID}
onChange={(e) => {
setValues({
...values,
employeeID: e.target.value,
})
}}
>
{employeesLists?.map((employee) => (
<MenuItem value={employee.id} key={employee.id}>
{employee.firstName} {employee.lastName}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
To fix this, I had to use the override nested component strategy: https://mui.com/material-ui/customization/how-to-customize/#1-one-off-customization
I added this to fix the problem, to override the default styles. It can be placed on <Select>.
sx={{
'& #simple-select': {
whiteSpace: 'normal',
},
}}
``

MUI - Outlined select label is not rendering properly

As per the demo, the label for a MUI outlined select input should sit on top of the top border of the select box.
However, in my application, the z-index of the label seems to be placing it behind the top border and thus it looks like a line is cutting through the label.
I have pretty much taken the code exactly from the documentation, and as far as I know, do not have any styles conflicting with this input element. I have compared the styles in the debugger to what I have and what is present in the documentation, and I do not see any of my first party CSS files causing a different style to be set on the element.
Any idea what might be going wrong here?
Here is the source code:
<FormControl variant='outlined' style={{ width: '100%' }} margin={'1'}>
<InputLabel id='test-select-label'>Label</InputLabel>
<Select
labelId='test-select-label'
id='test-select'
value={'test1'}
onChange={e => setTest(e.target.value)}
size='small'
>
<MenuItem key={1} value={'test'} >Test 1</MenuItem>
<MenuItem key={2} value={'test2'} >Test 2</MenuItem>
</Select>
</FormControl>
Solution 1: Use TextField
This is what TextField is for. It uses FormControl and InputLabel internally and make sure they work well together. You can tell TextField to render select instead input by overriding the select props:
<TextField
value={value}
onChange={(e) => setValue(e.target.value)}
select // tell TextField to render select
label="Label"
>
<MenuItem key={1} value="test">
Test 1
</MenuItem>
<MenuItem key={2} value="test2">
Test 2
</MenuItem>
</TextField>
For more detail about how TextField works, see this answer.
Solution 2: Use Select
If you decide to use Select, you need to write more code to do the same amount of work:
Pass the label text as InputLabel children
<InputLabel id="test-select-label">Label</InputLabel>
This label text will be rendered on the screen as the Select label when put inside FormControl and next to the Select component.
Pass the label text to the label props of the Select component
This label text is hidden and used to override and remove the part of the border-top where the real label is occupied when the Select label is shrinked.
<Select labelId="test-select-label" label="Label">
Putting it together we'll have something like below, note that with this approach we need to set the label in 2 different places which is not very DRY, so I'd prefer the first approach.
<FormControl>
<InputLabel id="test-select-label">Label</InputLabel>
<Select
value={value}
onChange={(e) => setValue(e.target.value)}
labelId="test-select-label"
label="Label"
>
<MenuItem key={1} value="test">
Test 1
</MenuItem>
<MenuItem key={2} value="test2">
Test 2
</MenuItem>
</Select>
</FormControl>
Live Demo
If you add the label property to your select component your problem should disappear.
...
<Select
value={value}
onChange={(e) => setValue(e.target.value)}
label="Label" // add this
>
<MenuItem key={1} value="test">
Test 1
</MenuItem>
<MenuItem key={2} value="test2">
Test 2
</MenuItem>
</Select>
...
Here is a live demo where you can see the difference:
Try this method, it worked for me.
JSX:
<TextField select variant={"outlined"} style={{width: "100%"}} label="Label">
<MenuItem key={1} value="test">
Test 1
</MenuItem>
<MenuItem key={2} value="test2">
Test 2
</MenuItem>
</TextField>
CSS:
.MuiSelect-outlined.MuiSelect-outlined, .MuiSelect-outlined.MuiSelect-outlined:active, .MuiSelect-outlined.MuiSelect-outlined:focus {
background-color: transparent;
text-align: left;
font-family: sans-serif !important;
font-size: 14px !important;
font-weight: 400 !important;
}
I had a similar issue when I tried to set padding on the FormControl component. I had the proper id and label, but it was still an issue. CSS is not my strong suit, but I noticed that if I could replicate the layout I wanted using margin on the FormControl component, the label aligned appropriately. See image with padding instead of margin:

Prevent Menu From Closing When User Clicks the Input

Is there a way to make it so that the <Input /> doesn't close on click? Currently, if I click inside the input, the menu just closes. I have the same setup as the original poster. I've tried playing around with closeOnSelect="false" and that didn't seem to work. Any advice would be greatly appreciated.
Here is the example in CodeSandbox form:
https://codesandbox.io/s/chakra-menuitem-as-input-forked-9ue4n
import {
Box,
Button,
ChakraProvider,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
useMenuItem,
} from '#chakra-ui/react';
import React from 'react';
const navigationKeys = ['ArrowUp', 'ArrowDown', 'Escape'];
const MenuInput = props => {
const { role, ...rest } = useMenuItem(props);
return (
<Box px="3" role={role}>
<Input
placeholder="Enter value"
size="sm"
{...rest}
onKeyDown={e => {
if (!navigationKeys.includes(e.key)) {
e.stopPropagation();
}
}}
/>
</Box>
);
};
function App() {
return (
<ChakraProvider>
<Menu>
<MenuButton as={Button}>Button</MenuButton>
<MenuList>
<MenuInput />
<MenuItem>Option 1</MenuItem>
<MenuItem>Option 2</MenuItem>
</MenuList>
</Menu>
</ChakraProvider>
);
}
export default App;
Add closeOnSelect={false} to the menu component to stop the menu closing when clicking on the MenuInput.
<Menu closeOnSelect={false}>
<MenuButton as={Button}>Button</MenuButton>
<MenuList>
<MenuInput />
<MenuItem>Option 1</MenuItem>
<MenuItem>Option 2</MenuItem>
</MenuList>
</Menu>

How to have multiple row tabs in Material UI Tabs

I have almost 30 tabs inside Material UI Tabs, the user has to scroll two times to see all the tabs, I would like to show the tabs in two rows with scroll instead of one row with scroll, this will help the user to see most of the tabs in one glance.
How can I do something like this ?, I looked over Material UI document but i couldn't find anything useful, I tried manually giving it CSS style but I wasn't able to achieve my goal (my CSS skills are mediocre).
To show what I mean by multiple row tabs, here is a sample image :
Any help is much appreciated.
I did a little hack:
Use 2 different Tabs components and adjust indexes:
<Box sx={{ display: 'flex',justifyContent: 'center', flexWrap: 'wrap'}}>
<Tabs value={value} onChange={handleChange}>
<Tab label='Precios'/>
<Tab label='Usuarios'/>
<Tab label='Plan'/>
</Tabs>
<Tabs value={value - 3 } onChange={handleChange2}>
<Tab label='Empleados'/>
</Tabs>
</Box>
And manage change for this adjustment:
const handleChange = (event, newValue) => {
setValue(newValue);
};
const handleChange2 = (event, newValue) => {
setValue(newValue + 3);
};
You just change the number 3 for the number of tabs in your first component.
Saludos
I am struggling with similar problem. I have gone throw the documentation and looks like this is not possible using Tabs/Tab features. For now I can see two options:
Use properties variant="scrollable" and scrollButtons="auto" like mentioned above. This will not give you what you expected but at least it is working.
Implement your own tabs. You can use Grid for it. Below example is just to show an approach. It is not redy solution, but it can be easily adjusted.
const useStyles = makeStyles((theme) => ({
navigationLinkContainer: {
// up to you
},
navigationLinkButtonActive: {
color: '#ffffff',
// up to you
},
}));
const NavigationLink = (props) => {
const classes = useStyles();
return (
<Grid item className={classes.navigationLinkContainer}>
<Button
component={Link}
onClick={props.onClick}
>
{props.children}
</Button>
</Grid>
);
};
const NavigationHeaders = (props) => {
const classes = useStyles();
const { headers, className } = props;
const [activeTab, setActiveTab] = React.useState('');
const isActive = (headerId) => headerId === activeTab;
return (
<>
<Grid container >
{headers.map((header) => (
<NavigationLink
className={classnames(isActive(header.id) && classes.navigationLinkButtonActive)}
key={header.id}
onClick={() => setActiveTab(header.id)}
>
{header.title}
</NavigationLink>
))}
</Grid>
{/* some content here shown base on activeTab */}
</>
);
};
I also came to the conclusion that this is not currently possible with Tabs/Tab. I tried using <br />, <hr />, <Divider />, functions to insert breaks, making multiple rows of tabs (which messed up selection), wrapping Tabs in span with max-width (also messed up selection), you name it. I ultimately decided to use scroll on small screens.
I figured out the smallest screen size that would show my tabs properly, then used scroll for any smaller.
const mql = window.matchMedia('(max-width: 2000px)');
const smallScreen = mql.matches;
<Tabs
value={tabValue}
onChange={handleTabChange}
orientation="horizontal"
variant={smallScreen ? 'scrollable' : 'standard'}
centered={!smallScreen}
>
<Tab label="1" />
<Tab label="1" />
<Tab label="3" />
<Tab label="4" />
<Tab label="5" />
</Tabs>
You could add an event handler to change on resize, but was not necessary for my use case
You can set flexWrap: 'wrap' in the tab container component which is a flexbox:
<Tabs
// disable the tab indicator because it doesn't work well with wrapped container
TabIndicatorProps={{ sx: { display: 'none' } }}
sx={{
'& .MuiTabs-flexContainer': {
flexWrap: 'wrap',
},
}}
{...}
>
https://codesandbox.io/s/69733826-material-ui-responsive-tabs-5q57p?file=/demo.js
Try going through the doc of any stuff you use to safe unnecessary problems in future
visit this for more details and full code https://material-ui.com/components/tabs/
<div className={classes.root}>
<AppBar position="static">
<Tabs value={value}
onChange={handleChange}
aria-label="simple tabs example"
indicatorColor="primary"
textColor="primary"
variant="scrollable"
scrollButtons="auto"
aria-label="scrollable auto tabs example"
>
<Tab label="Item One" {...a11yProps(0)} />
<Tab label="Item Two" {...a11yProps(1)} />
<Tab label="Item Three" {...a11yProps(2)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
Item One
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
<TabPanel value={value} index={2}>
Item Three
</TabPanel>
</div>
Edit: please notice the variant in Tabs

Material UI Selected Option Not Showing in Select Box

I have the following:
--- before render ---
const fontArray = [
["Standard", "Standard"], ["Abril FatFace", "'Abril Fatface', cursive"],
["Alfa Slab One", "'Alfa Slab One', cursive"],
["Chonburi", "'Chonburi', cursive"], ["Comfortaa", "'Comfortaa', cursive"],
["Lobster", "'Lobster', cursive"], ["Pacfico", "'Pacifico', cursive"]
]
--- in render ---
<FormControl style={{margin: '10px'}}>
<InputLabel htmlFor="select-font">Font</InputLabel>
<Select
value={this.state.font[0]}
onChange={(evt)=>this.handleFontChange(evt)}
inputProps={{
name: 'font',
id: 'select-font',
}}
>
{fontArray.map((font, index)=>{
return(
<MenuItem key={font} value={font}>
<div style={{fontFamily: `${font[1]}`}}>
{font[0]}
</div>
</MenuItem>
)
})}
</Select>
</FormControl>
And as you can guess the current font is held in state.
--- Here is how I handle select change ---
handleFontChange = (event) => {
this.setState({ font: event.target.value })
};
So what I want is to be able to have a font select, where the font is shown. It almost works. For example, when I click the select I get:
However, the select itself is empty (even when I've confirmed that state is populated:
What am I doing wrong? Maybe material-ui can't handle stylized default text?
EDIT: The two answers below seem close, but not quite right for what I'm trying to do.
If you replace
<MenuItem key={font} value={font}>
with
<MenuItem key={font} value={font[0]}>
It does replace the font with the correct selected value. Great!
...but it also then replaces this.state.font with this.state.font[0]. I'm currently attempting to get this to work by changing the handle function like this:
handleFontChange = (event, fontArray, stateData) => {
let newFont = fontArray.filter(i=>{
if(i[0]==event.target.value){
return i
}
})
this.setState({ font: newFont })
};
Which seems to set this.state.font correctly, but it again doesn't
seem to want to make the select box show the selected font.
Hmmm....
SOLVED
Here is a modification of a solution below:
Using
renderValue = (value) => {
return(
<div style={{fontFamily: `${value[1]}`}}>
{value[0]}
</div>
)
}
and
<...>
<Select
value={this.state.font}
renderValue={() => this.renderValue(this.state.font)}
<...>
Gives...
You can use renderValue to solve this.
renderValue = (value) => {
return value && value[0];
}
in render method
<FormControl style={{margin: 10}}>
<InputLabel htmlFor="select-font">Font</InputLabel>
<Select
value={this.state.font}
renderValue={() => this.renderValue(this.state.font)}
onChange={evt => this.handleFontChange(evt)}
inputProps={{
name: "font",
id: "select-font"
}}
>
{fontArray.map((font, index) => {
return (
<MenuItem key={index} value={font}>
<div style={{fontFamily: `${font[1]}`}}>
{font[0]}
</div>
</MenuItem>
);
})}
</Select>
</FormControl>
<...>
<Select
value={this.state.font?this.state.font :defaultvlue}
renderValue={() => this.renderValue(this.state.font)}
<...>
you can use ternary operator ,if you have data show data else default value

Resources