MUI Two Object literals in label for checkbox - reactjs

I am using MUI.
I have checkboxes for which I would like to have a label consisting of two object literals. This is my FormGroup.
<FormGroup>
{data.map((item, index) => {
return (
<FormControlLabel
key={index}
control={<Checkbox />}
// I need {item.title} (item.value) here
label={item.title}
name={item.title}
value={item.title}
onChange={() => handleChange(index)}
disabled={disabledItems[index]}
checked={checkedItems[index]}
color="default"
/>
)
})}
</FormGroup>
My data is as follows:
const data = [{title: 'Something', costs: '1 million'}]
But more of those ;).
I've tried everything I could think of but it seems not to be necessary.
In the end I need a list of checkboxes which shows the name and how much it will cost behind it (e.g. Pizza ($1,50))
Hope somebody knows a solution (or google search option, I cant find anybody else with this issue but maybe I'm using the wrong terminology)

Can't you just use template string?
label={`${item.title} (${item.costs})`}

Related

React-Hook-Form conditional fields in a map function

I'm curious if anyone has used conditional fields in react-hook-from but within a map function. I've got the basic rendering happening, but since it is in a list and they all refer to the same .map() criteria, it is populating the conditional input for all fields, regardless if they are checked. Heres the basic idea for using conditional fields in RHF: https://codesandbox.io/s/react-hook-form-conditional-fields-qgr41
Heres what I've got so far:
{goals && goals.map((goal) => (
<>
<FormControlLabel
control={
<Switch
key={goal.title}
//id..? marked? title?
{...register('marked')}
name='marked'
value={goal.marked}
/>}
label={goal.title}
/>
<br />
{marked && (
<>
<Input
key={goal.title}
{...register('goal.note')}
id="note"
type='text'
label="Progress Note"
name="note"
onChange={(e) => {
e.target.value = e.target.value
}}
/>
<br />
</>
) }
The problem is that from the UI side, when I toggle the switch (or mark a checkbox), that all the fields populated in the map are referring to the same prop in the switch, 'marked'. So when toggling for 1, it populates the additional input field for all elements in the map function.
An additional issue I have yet to look into is passing the data for the individual fields to the data collected by the form for the submit. Right now, my assumption is that all fields would be treated as one value, since the form is recognizing them all together. So ideally if one item in the map is toggled and additional info is provided, then only those values are passed to the form for the submit, rather than all fields (toggled or not) being passed.
UPDATE
So I got the toggle functioning by creating a child component in order to more easily manage the state of the toggle, and leveraging the index within the props so the browser would not treat each conditional field as the same:
parent component:
{goals && goals.map((goal, index) => (
<GoalInput
goal={goal}
index={index}
register={register}
control={control}
errors={errors}
/>
new child component:
function GoalInput({ goal, index, register, control, errors }) {
const [toggle, setToggle] = useState(false)
return (
<>
<FormControlLabel
control={
<Switch
key={index}
{...register(`goals.${index}.marked`)}
checked={toggle}
name={`goals.${index}.marked`}
value={toggle}
onChange={() => setToggle(!toggle)}
/>
}
label={goal.title}
/>
<br />
{toggle ? (
<>
<Controller
control={control}
name={`goals.${index}.note`}
id={`goals.${index}.note`}
render={({field}) => (
<Input
type='text'
index={index}
error={!!errors.note}
value={field.value}
onChange={(e)=>field.onChange(e)}
label="Progress Note"
/>
)}
/>
<br />
</>
) : <></>}
</>
)
}
So the toggles work independently, appropriately record whether the value of the toggle is true/false, and the subsquent conditional input in being tracked as well. Still have some struggles with the data being passed correctly through to the backend, but that is another issue. Hope this helps anyone coming along.

Material-UI Select component with many menu items is slow - Alternative solutions

I am trying to make a select element for cities with material-UI. The problem is that I need to have the same design as the Textfields I have above that element. I used the following
<FormControl variant='outlined' className={classes.city}>
<InputLabel>City</InputLabel>
<Select
label='City'
value={city}
onChange={(event) => setCity(event.target.value)}
>
{cities.map((storeCity, key) => <MenuItem key={key} value={storeCity.id}>{storeCity.name}</MenuItem>)}
</Select>
</FormControl>
Before clicking the element
This gives me the perfect visual output and generally works well. The only problem is that it is slow as the cities array contains more than 200 cities. What should I do to have the same visual effect with faster rendering?

In Next.js, how to scroll to top of window after setting search value on a search bar?

Imagine I have a button somewhere. When clicked, the text on it, now goes to my search bar. How do I make the window scroll to the search bar too after the value is set in the Input element shown below?
<Flex
flexDirection="column"
justifyContent="center"
alignItems="center"
maxWidth="90%"
mt={4}
>
{!filteredBlogPosts.length && 'No posts found.'}
{filteredBlogPosts.map((frontMatter) => (
<>
<BlogPost
key={frontMatter.title + frontMatter.lastPublishedOn}
handleSearch={(anyKey) => setSearchValue(anyKey)}
// Insert your code here, how to scroll after setting the key to search?
{...frontMatter}
/>
</>
))}
</Flex>
And, here is the <Input> field
<Input
aria-label="Search"
borderColor="blue.500"
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search"
value={searchValue}
//or should I do scroll here? How?
autoFocus
onFocus={e => e.currentTarget.select()}
/>
Is this something easy to do? Please present code examples if possible.
Thanks.
If anyone faces this issue in the future, I solved it using this simple function.
const scrollSearch = myKey => {
window.scrollTo(0, 0);
frontMatter.handleSearch(myKey)
};
And passed the scrollSearch function in button onClick.

Label of Multiple Select is crossed out by the outline of the input field

I made a multiple select list with Material-UI. But when I select an item the label should shrink and fit into the outline of the input field. The problem is the outline stays the same and the label shrinks behind it.
I tried looking for a solution in the Material-UI docs. It seems like there isn't any multiple select list outlined variant. So I wonder if it is because there is no outlined variant of the multiple select list or that it is due to something else.
<FormControl
variant="outlined"
fullWidth
>
<InputLabel
ref={ref => {
this.InputLabelRef = ref;
}}
htmlFor="members"
error={this.props.touched.members && Boolean(this.props.errors.members)}
>
Members
</InputLabel>
<Select
onChange={this.change.bind(null, "members")}
multiple
value={this.state.members}
error={this.props.touched.members && Boolean(this.props.errors.members)}
input={
<OutlinedInput
labelWidth={0}
name="members"
id="members"
/>
}
>
<MenuItem value={'Baspa'}>Baspa</MenuItem>
<MenuItem value={'Plorky'}>Plorky</MenuItem>
<MenuItem value={'Rizzels'}>Rizzels</MenuItem>
</Select>
I also made a CodeSandBox:
https://codesandbox.io/s/jnlx1vky39
This is how it looks like:
https://imgur.com/a/Wpf7OE0
You were missing a couple pieces that are shown in the documentation that allow the outline to know the label width:
componentDidMount() {
this.setState({
labelWidth: ReactDOM.findDOMNode(this.InputLabelRef).offsetWidth
});
}
...
<OutlinedInput labelWidth={this.state.labelWidth} name="members" id="members" />
Here's the full code:
The original answer is no longer correct. According to this resolved MUI issue, labelWidth is no longer supported. Instead, set the label on the <OutlinedInput> to match the label on the <InputLabel>. Ex:
<InputLabel id="foo">{tag}</InputLabel>
<Select
input={<OutlinedInput label={tag} />}
Full example in the MUI Docs

React-bootstrap FormControl Select placeholder

Trying to set up a control for a select field but even with placeholder set it still always auto selects the first item in the list.
It looks like this...
export function renderSelector({input, label, placeholder, meta:{touched, warning, error}, title, mandatory, fieldValues, fieldItemKeyFunc, fieldItemLabelFunc, props}) {
const mappedData = fieldValues.map(v => ({Id: fieldItemKeyFunc(v), Name: fieldItemLabelFunc(v)}));
let custom = props || {};
return (
<FormGroup controlId={input.name} validationState={touched && error ? 'error' : null}>
<Col xs={12}>
{renderLabel(title, label, mandatory)}
</Col>
<Col xs={12}>
<FormControl componentClass="select" placeholder={placeholder} name={input.name} {...input} {...custom}>
{
mappedData.map((item, index) => {
return (
<option key={index} value={item.Id}>{item.Name}</option>
)
})
}
</FormControl>
<FormControl.Feedback/>
{renderErrBlock(touched, warning, error)}
</Col>
</FormGroup>
);
}
When this renders the selector always starts with the first item selected, instead of the placeholder. How can I fix this?? I tried just adding a default first option and adjusting the css, but for Accessibility that doesn't work that well because the contrast levels
A bit late, but check if adding this option helps you:
<option key='blankChoice' hidden value />
Make sure to check the form value to a blank choice as well:
<Form.Control
value=''
>
You can do something like this:
Add a
<option disabled value={-1} key={-1}>
, and set your
<FormControl value={-1} ...
for a similar effect.
Just dropping a late answer, in case someone finds this useful.
React Bootstrap's Form as='select' doesn't seem to allow placeholder out of the box. Adding a blank value to Form.Control would render the input useless. Adding an extra option above mappedData.map(), as follows would work:
<option key = 'blankChoice' hidden value> --Your placeholder text-- </option>

Resources