Material UI IconButton onClick doesn't let to handle event - material-ui

I installed "#material-ui/core": "^4.9.2" and "#material-ui/icons": "^4.9.1".
In my form i have several rows, each row has an add button and a remove button. I want the remove button to remove the row from it was clicked. It works fine with regular Button with a "-" character in it. But i want it fancy, so i replaced my Button from an IconButton, and imported the icons to use
import {AddCircleOutline,RemoveCircleOutlineOutlined} from "#material-ui/icons";
And my IconButton looks like this:
<IconButton
onClick={props.onRemoveClick}
className="align-self-center"
color="info"
size="sm"
disabled={props.index > 0 ? false : true}
<RemoveCircleOutlineOutlined/>
</IconButton>
When the IconButton is hit, the onClick method is called (i know because of logs in my console) but i can't handle the event because it is now undefined.
The funny thing is that if i click on the button area that doesn't correspond to the icon, it works. But obviously i need it to work in the whole area of the button.
It is not a binding issue because i already tested it.
Any ideas?

Props that are not cited in the documentation are inherited to their internal <EnhancedButton />, so you need to use a wrapper.
<IconButton
onClick={(e) => props.onRemoveClick(e)}
className="align-self-center"
color="info"
size="sm"
disabled={props.index > 0 ? false : true}
<RemoveCircleOutlineOutlined/>
</IconButton>

Well you gave an idea. Since i needed an index to identify the row's button, i sended the index through a paramater on the onClick method, like this:
onClick={e => props.onRemoveClick(props.index)}
In this way i didn't need to handle the event. I also had to bind my method on the constructor:
constructor(props) {
super(props);
this.handleRemoveClick = this.handleRemoveClick.bind(this);
}
Now i got the behaviour wanted

You can see the github ussue here. There is some problem with typescript definition files but we can work around it.
Solution
I tried to solve it like in the github issue but didn't work. So this works for me.
const onClick = (e: any) => {
// e is of type any so the compiler won't yell at you
}
<IconButton onClick={(e) => onClick(e)}>

I don't know the reason but using e.currentTarget helped me to get the button that I wanted and not the material icon inside it.
onClick={(e) => {
return console.log(e.currentTarget)
}}

Related

Cypress UI - get an element inside double within block

I have the following scenario: I find some text to navigate to a section in my HTML. Then I find some other text to navigate to a subsection. Inside this subsection I have a button when clicked displays a modal dialog (which is placed outside both sections that I'm currently in. If I try to grab the modal dialog from within the sections it does not work. If I go outside the sections, it works.
cy.contains("Some text").parent().within(() => {
cy.contains("Some other text").parent().within(() => {
cy.find("Button that triggers a modal dialog").click();
//does not work
cy.getModalDialog().within(() => {
cy.contains("OK").click();
})
})
})
cy.contains("Some text").parent().within(() => {
cy.contains("Some other text").parent().within(() => {
cy.find("Button that triggers a modal dialog").click();
})
})
//works
cy.getModalDialog().within(() => {
cy.contains("OK").click();
})
Is there a better way how to grab this modal, without going outside the double within blocks?
Cypress provides an option withinSubject that can remove the effect of .within() for that particular command.
Element to search for children in. If null, search begins from root-level DOM element
cy.get("div#1").within(() => {
cy.get("span").within(() => {
// cy.get("div#2"); // ❌ fails
cy.get("div#2", { withinSubject: null }); // ✅ passes
})
})
Test page
<body>
<div id="1">
<span>one</span>
</div>
<div id="2">
<span>two</span>
</div>
</body>
Note this feature was broken in v12.0.2 and fixed again in v12.1.1
Although Cypress does allow things like cy.document() from which I presume you can go down from there, in general whenever I find a pattern where I "find a thing, then inside the thing, find another thing and do stuff", I'm better off never using .within at all. Instead I combine all the "find the thing" into one very large, very specific selector for a single .get
This does mean I need to repeat the long selector multiple times, and the .get is very heavy which seems backward, but it helps me avoid descending into callback heck.
In my current level of understanding, I do believe .within is a trap.

Ionic - Problem using two way binding to change a style. As example I am using background-color

I think I have a very basic problem but I can't resolve it. So what I am trying to do is to implement a button in Ionic that when pressed change the style of a style. To keep it simple for now I try and change the background color of a div. However, it does not work neither does it give an error. (I use console page of browser to view changes, look for errors etc)
The code in the card.page.html page is
<ion-button
(click)="setStyle('red')"
[style.--background]="'pink'"
>
Some Button
</ion-button>
The code in the card.page.ts is
setStyle(value: string): void {
console.log('read More Works');
this.aColor = '#yellow';
console.log('read More still Works');
}
and that is it. Clicking on 'Some Button' button does not do anything except the logging but I am pretty sure it is not two way binding that is the issue as I tried just using for example trying with just some text as being the 'variable' I want to change and that worked fine.
I do appreciate any help :(
Thanks
You can use pre defined CSS styles for that. Something like this:
card.page.scss
#somediv {
&.initial-style {
background: #000;
}
&.dinamic-style {
background: #fff;
}
}
card.page.html
<div id="somediv" [class]="apply_styles ? 'dinamic-style' : 'initial-style'">
styles applied: {{ apply_styles }}
</div>
<ion-button (click)="changeStyle()">Change Style</ion-button>
card.page.ts
apply_styles: boolean = false;
changeStyle() {
this.apply_styles = !this.apply_styles;
}
Of course this is very simple. But I hope it can put you in the right direction.

React-Bootstrap-Typeahead: Capture moment before setting selection into box (to allow conditional rejection)

In React-Boostrap-Typeahead, I need to capture the moment after the mouse has been clicked, but before the menu selection gets loaded into the Typeahead's box.
This is because I need to check items being selected, and for some of them, I need to display an alert with a Yes/No warning. Only if Yes is clicked can I proceed with setting that value into the box. Otherwise I need to reject the selection and keep it whatever it was prior to the mouse click.
I can't use onChange because by that point the selection is already in the box.
I can't use onInputChange because that is for typing rather than for menu selection. I need the post-menu-select, pre-box change.
If there are any workarounds please let me know.
You should be able to achieve what you're after using onChange:
const ref = useRef();
const [selected, setSelected] = useState([]);
return (
<Typeahead
id="example"
onChange={(selections) => {
if (!selections.length || window.confirm('Are you sure?')) {
return setSelected(selections);
}
ref.current.clear();
}}
options={options}
ref={ref}
selected={selected}
/>
);
Working example: https://codesandbox.io/s/objective-colden-w7ko9

Make Upload tab the default in Insert/Edit Image dialog

Using TinyMCE 5.7.0
Is there a way to make the "Upload" tab the default tab displayed in the Insert/Edit Image dialog?
I'm looking for a configuration option or programmatic way to do this so we can continue to easily update TinyMCE when new versions come out.
In TinyMCE (5.7.0 in my case, not the minified version), open plugins/image/plugin.js.
Search for these lines (1462 to 1466):
tabs: flatten([
[MainTab.makeTab(info)],
info.hasAdvTab ? [AdvTab.makeTab(info)] : [],
info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : []
])
Reorder the lines like this:
tabs: flatten([
info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : [],
[MainTab.makeTab(info)],
info.hasAdvTab ? [AdvTab.makeTab(info)] : []
])
We had the same requirement and this is how we did it.
Instead of adding the "Upload Image" option to toolbar, create a keyboard shortcut for opening the image upload modal using addShortcut method. Something like this in reactjs:
editor.addShortcut('ctrl+shift+i', 'Open image upload window', function () {
editor.execCommand('mceImage')
});
Now that we have a code block that runs when pressing the shortcut keys, we can add logic inside that block to initiate a click action on the "Upload" button within the modal like this:
setTimeout(() => {
let element = document.querySelectorAll('.tox-dialog__body-nav-item')[1];
if (element) { element.click() }
}, 0)
The setTimeout is added to make sure that the modal is added to DOM before run the querySelectorAll method on the document object is executed. Timeout even with 0 will make sure the code block only executes after all the synchronous tasks are done, which includes the DOM update.
In the end, the final codeblock will look like this:
editor.addShortcut('ctrl+shift+i', 'Open image upload window', function () {
editor.execCommand('mceImage')
setTimeout(() => {
let element = document.querySelectorAll('.tox-dialog__body-nav-item')[1];
if (element) { element.click() }
}, 0)
});
Edit:
If you notice other elements in the DOM with the same class as "tox-dialog__body-nav-item", you can change the querySelectorAll method to make it more well defined and make sure it only selects the class within image upload modal if found. I haven't yet ran into this issue, so this was enough for my case.

How to deselect row when clicked outside the table? [used: Material-UI DataGrid]

I'm using Material-UI tables for showing my data, and I'm stuck when it comes to deselecting selected row.
So when user clicks outside the table, row should be deselected.
This is my code
const showData2 = (e,data) => {
console.log('selection',e)
}
<div style={{ height: 180, width: "100%", backgroundColor:'white' }}>
<DataGrid
rows={cases}
density='compact'
columns={columnsCases}
pageSize={3}
hideFooterSelectedRowCount={true}
rowHeight={40}
onRowSelected = {(e)=>{showData(e)}}
onSelectionChange= {(e)=>{showData2(e)}}
/>
</div>
Selection works perfectly but it seems impossible to deselect row when clicked outside the table
I would appreciate any idea and help.
Thank you!!
Use ClickAwayListener to detect if a click event happened outside of an element. It listens for clicks that occur somewhere in the document.
https://material-ui.com/components/click-away-listener/
Deselect Row Example using the ClickAwayListener with Typescript
Import GridRowId type in addition to whatever other types or components you need from data-grid
import { DataGrid, GridColDef, GridRowId } from '#material-ui/data-grid';
Set local state for the selectionModel prop on the DataGrid and init as an empty array.
const [selectionModel, setSelectionModel] = useState<GridRowId[]>([]);
Add DataGrid component as child of ClickAwayListener... plus your other DataGrid props.
<ClickAwayListener onClickAway={() => setSelectionModel([])}>
<DataGrid
{...otherProps}
checkboxSelection // <= works with or without checkbox selection
selectionModel={selectionModel}
onSelectionModelChange={({ selectionModel }) =>
setSelectionModel(selectionModel)
}
/>
</ClickAwayListener>
Of course you can just remove the types if you're not using Typescript.
Good Day,
Like #Omar EL KHAL said a ClickAwayListener works perfectly.
Step One: Define your onSelectionModelChange and handleClickAway function.
const handleSelection = (newSelection) => {
if (newSelection)
{
setSelectionModel(newSelection.selectionModel);
}
else
{
setSelectionModel(null);
}
}
const handleClickAway = () => {
handleSelection(null);
};
Step Two: Define your selectionModel as State
For this step I chose to set selectionModel in the component state to allow for other functions to set values.
const [selectionModel, setSelectionModel] = React.useState([]);
Step Three: Set the onSelectionModelChange and selectionModel props
Set the props props in the Data Grid as follows.
<ClickAwayListener onClickAway={handleClickAway}>
<DataGrid
onSelectionModelChange={handleSelection}
selectionModel={eventSelectionModel}
/>
</ClickAwayListener>
Once you save and let NPM do its thing you it should work.
I personally tested this on a single selection model but it should work with multiple as well.
P.S. I also did this with functional components and not classed components. The theory would be the same just instead of using useState you would use this.setState.
Happy Coding!
Gunny
There is no need for extra state. You could simply call the setSelectionModel function on the ClickAwayListener like so:
<ClickAwayListener onClickAway={() => apiRef.current.setSelectionModel([])}>
// your Data Grid
</ClickAwayListener>