I am using Material UI Select: https://material-ui.com/components/selects/
The select needs a bit of custom functionality. I have added this in a CodePen sandbox: https://codesandbox.io/s/material-demo-3nzpv
Some MenuItem might need to show a delete icon.
Selecting an option that has the delete icon (but not clicking the delete icon itself) should not show the delete icon after selection as the selected value. The delete icon should still be a part of the MenuItem though.
Clicking on the delete icon should not select the option but trigger the onClick of the delete icon instead. In essence, the default functionality of MenuItem should not trigger, just the icon onClick.
What's the best way I can achieve this?
You need to use renderValue prop from your Select component, in order cu custom render your selected value.
Add the following line to your Select component
renderValue={value => currencies.find(el => el.value === value).label}
You can check an updated version of your working sandbox HERE
Also you can check more on Select props HERE
LE: Update for point 3
In your <DeleteIcon> handler onClick, you need to stop the propagation of the event further. In the handleDelete() function add the event parameter and use e.stopPropagation() method.
const handleDelete = (e: React.ChangeEvent<HTMLInputElement>) => {
e.stopPropagation();
console.log("Delete");
};
And your component should look like this
<MenuItem key={option.value} value={option.value}>
<div>{option.label}</div>
<div>{option.action && <DeleteIcon onClick={handleDelete}/>}/div>
</MenuItem>
/>
I've also updated a sandbox, with the last version HERE
On your handleChange method, write a condition to change the value only if the value of a menu item does not have the value that does not need to be selected
Related
When I click on Dropdown.Item, Dropdown.Menu hides. I want to prevent this, i.e. leave Dropdown.Menu open after a click, and close it only if there was a click outside of Dropdown at all. I've found similar questions, but there were in original bootstrap using jQuery. How to implement this in react-bootstrap? Thanks
////
<Dropdown.Menu>
<Dropdown.Item>- Pending</Dropdown.Item>
<Dropdown.Item>- Completed</Dropdown.Item>
<Dropdown.Item>- Cancelled</Dropdown.Item>
</Dropdown.Menu>
////
Add autoClose="inside" to the Dropdown component.
By default, the dropdown menu is closed when selecting a menu item or clicking outside of the dropdown menu. This behavior can be changed by using the autoClose property.
By default, autoClose is set to the default value true and behaves like expected. By choosing false, the dropdown menu can only be toggled by clicking on the dropdown button. the inside makes the dropdown disappear only by choosing a menu item and the outside closes the dropdown menu only by clicking outside.
https://react-bootstrap.github.io/components/dropdowns/
You can make use of show prop of Dropdown. Using this you can manually hide and show the dropdown.
So what i did is i added dropdown props state variable to the Dropdown element and then using onToggle function i hide and show dropdown on particular conditions.
<Dropdown {...this.state.dropdownProps} onToggle={(isOpen, event) => this.onToggleFunction(isOpen, event)} />
Try this:
const [isShown, setIsShown] = useState(false);
const onToggleHandler = (isOpen, e, metadata) => {
if (metadata.source != 'select') {
setIsOpen(isOpen);
}
}
<Dropdown
show={isShown}
onToggle={(isOpen, e, metadata) => onToggleHandler(isOpen, e, metadata)}
>
*onSelect method can be used normally
For functional components, simply create the state:
const [ show, setShow ] = useState(false);
Then write your dropdown component like this:
<Dropdown show = {show}>
<Dropdown.Toggle onClick = {() => setShow(!show)}>Toggle Trigger.</Dropdown.Toggle>
</Dropdown>
I have a material-ui Table and have been implementing multi-select functionality on it.
My multi select should behave as follows :
Select checkboxes only appear at the start of a row on hover (when nothing has been selected)
Once one row has been selected, all other checkboxes become permanently visible.
The checkbox in the TableHead will select / deselect all rows in the Table when toggled.
Each row has its own individual unique id.
I maintain a Set called idsOfSelectedRows via a useState hook to keep track of all rows currently selected.
Each TableRow has a Checkbox which looks like this
<Checkbox
className={
idsOfSelectedRows.size === 0 &&
styles.rowSelectionCheckboxStyle
}
onChange={() => handleRowSelectionCheckboxClick}
checked={idsOfSelectedRows.has(row.id)}
/>
and handleRowSelectionCheckboxClick looks like this
const handleRowSelectionCheckboxClick = event => {
const idOfClickedRow = event.target.attributes.getNamedItem("id").value;
//If the checkbox has just become checked then add it to the idOfSelectedRows Set, otherwise remove it.
setIdsOfSelectedRows(
event.target.checked
? new Set(idsOfSelectedRows.add(String(idOfClickedRow)))
: () => {
idsOfSelectedRows.delete(String(idOfClickedRow));
return new Set(idsOfSelectedRows);
}
);
};
My issue is that clicking on the checkboxes on the rows is unresponsive. I can select them all by clicking on the select all Checkbox in the TableHead but clicking on checkboxes in individual rows does not change their state ?
Here's a full CodeSandbox reproducing the exact issue. What do I need to do to make the checkboxes in the rows be toggleable ?
Remove the () => from the onChange assignment for a quick fix.
Why
You're declaring an anonymous inline function, but that function does not call anything (missing () that is syntax to call a function). onChange accepts a function reference. When you give it an inline function, that's the function it will call. The other option is to only pass it the function reference of handleRowSelectionCheckboxClick, and then that function will be called on change instead of the anonymous function middleman.
So your two options are:
onChange={handleRowSelectionCheckboxClick} // Just the reference
OR
onChange={(e) => handleRowSelectionCheckboxClick(e)} // New inline function that calls your function
The second way is unnecessary in most cases. The first is preferred unless you need to pass custom parameters. Here is an example of how it might be useful:
onChange={(e) => handleRowSelectionCheckboxClick(e, id)}
In your sandbox, you map over elements and each gets a change handler. To treat them separately in the handler, you can pass an extra variable to the function like above.
For example, I got your sandbox working by changing the handler and assignment to this:
const handleRowSelectionCheckboxClick = (event, idOfClickedRow) => {
...
onChange={(e) => handleRowSelectionCheckboxClick(e, row.id)}
This line: event.target.attributes.getNamedItem("id").value;
won't give you the required id, since there isn't one passed, change the chekbox like so:
<Checkbox
id={row.id}
onChange={handleRowSelectionCheckboxClick}
checked={idsOfSelectedRows.has(row.id)}
/>
Then, retreive the ID like so:
const idOfClickedRow = event.target.attributes.id.value;
Updated codesandbox:
Like the other answers suggest, change the handleRowSelectionCheckboxClick function call like shown in the example.
If you like your solution with an anonymous function, you should add the (): onChange={() => handleRowSelectionCheckboxClick()}
I am implementing a custom input element similar to React Select. Users will be able to type something into the input element and select a suggestion from a dropdown (in my case, I implemented a component called SuggestionList which serves the same purpose as that dropdown suggestion list).
Currently, the SuggestionList is a sibling React component to the input element that the user types into (I named it CustomInput).
I want SuggestionList to disappear when CustomInput loses focus, so I implemented an onBlur handler which makes SuggestionList either un-mount or have "display" CSS property set to none. However, the problem is that after SuggestionList disappears, its onClick/handleClick event handler doesn't get called, and that event handler is responsible for adding user-selected elements to the selected items list.
Code shown below. There is more explaination after this code.
const AutocompleteInput = ({...}) => {
const [suggestionVisible, setSuggestionVisible] = useState(true);
return (
....
<SystemComponent position="relative">
<SuggestionList p={theme.space[4]}
position="absolute"
visible={suggestionVisible}
value={value}
handleClick={handleSelect}
ref={suggestionBox}
/>
<CustomInput
variant="text"
placeholder={placeholder}
value={value}
onChange={(evt) => handleInputChange(evt.target.value)}
onKeyDown={handleKeyDown}
onFocus={ () => {setSuggestionVisible(true)} }
onBlur={ () => {setSuggestionVisible(false);} }
/>
</SystemComponent>
....
)
}
Therefore, when a user clicks something on the SuggestionList, my CustomInput immediately loses focus, causing SuggestionList to either be unmounted/disappear, and thus its event handler isn't called. As a result, the item the user selects isn't added to the suggestion list.
Having that said, I still want SuggestionList to disappear when the user clicks some other part of the website, thereby causing CustomInput to lose focus. Any suggestions ?
Let's say you are calling onClickHandler() inside your onClick. You can call this inside the onBlur on function.
For example
onBlur={ () => {onClickHandler(); setSuggestionVisible(false);} }
I am using react fontawesome in my project. Under the render function of my component I have the following which maps a school onto the page and then I want a button with an edit icon to perform an action. My code for the button looks as below:
{ this.state.myschools.map( (school) => (
<Row key={school.id}>
<Col><p>{school.name}</p></Col>
{ school.deaths.length > 0 ? <Col md="2"><button id={school.id} className="likelink" onClick={this.goToSchoolDeathList}>View Deaths</button></Col> : <Col md="2"></Col> }
<Col><button id={school.id} onClick={this.editSchool}><FontAwesomeIcon icon={faEdit} /></button></Col>
</Row>
))}
The editSchool function is as:
editSchool(e) {
console.log("School Id");
console.log(e.target.id);
}
If I click on the empty space around the font awesome icon then the id is logged to the console. If I only click on the area where the icon exits then the id is not logged. I want the user to be able to click on any part of the button including the fa icon and to capture the school id.
I have tried adding the onClick event and the id attribute to the "FontAwesomeIcon" component, but that still doesn't work.
Can anyone help?
Well, this is one of the common pitfall you learn during your time with React. Rest assure, we have all fallen into this pit.
There are few AHA moments here.
1st Aha: React doesn't have a usual way to let you get element attribute in an event (an e in your example).
That being said, you can still access to the button element by e.target and use JavaScript to get attribute.
Solution #1 JavaScript getAttribute
editSchool(e) {
console.log("School Id");
console.log(e.target.getAttribute('id'));
}
e.target will be the HTML Element that you click, and we can use JavaScript way to get attribute. (Please Note that, Javascript way is not React way)
But there are another way and you'll see this way more often.
Solution #2 You can define a function directly in a render
<button id={school.id} onClick={e => this.editSchool(school.id)}>
Please be careful about this another pitfall,
2nd Aha: Bind onClick to function, not result of a function.
Don't do this
<button id={school.id} onClick={this.editSchool(scholl.id)}> // This is incorrect
The thing is onClick prop expect a function to call when someone click.
The first one we define a function e => this.editSchool(school.id) and bind onClick with the newly defined function.
While the second, we just bind onClick to a "result" of function this.editSchool(school.id), which is not a function.
I 've created autocomplete suggested input field, with debounce hook, only one issue left, is close drop-down when I select item or click outside? And what is the best solution to show selected item, show it in input value on create some 'redableRenderValue'.
Current behavior : when I click enter or on item itself the drop down close and reopen again
Expected value: close on blur , close on enter and close on select item
https://codesandbox.io/s/usedebounce-ncq2n
Try this:
const [showSuggestions, setShowSuggestions] = useState(true); //make useState initial value to true instead of false.
and, delete below code from useEffect
setShowSuggestions(true); //delete this line from your code
also alter here:
<input
type="text"
placeholder="Find"
onChange={e => setSearchTerm(e.target.value)}
onClick={onInputClick} //add this attribute
onKeyDown={onKeyDown}
// onBlur={onBlur}
value={searchTerm}
autoFocus
/>
and a corresponding function to call:
const onInputClick = () =>{
setShowSuggestions(true);
}
and Run your code.
I try it this will be working fine by doing this. Hope this will help you to.