We have a requirement, where focus needs to be set on dropdown (not the child elements of dropdown) once the Modal is closed.
We are using ReactModal component, it has prop called 'shouldReturnFocusAfterClose'(when set to true) which will set focus back on button (where focus was set before modal opened) once Modal is closed.
<ReactModal
isOpen={isOpen}
shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
onRequestClose={onRequestClose}
getAppElement={getAppElement}
closeTimeoutMS={200}
contentRef={contentRef}
shouldReturnFocusAfterClose={true} >
</ReactModal>
Above is the code, ReactModal is setting focus as expected (setting focus on button where it was before modal is opened)
*** ReactModal is setting focus as expected (setting focus on button where it was before modal is opened)
*** Once clicked(key event) on the one of the options of dropdown, Modal is opened. Our requirement is...Once it is closed, focus should be set to dropdown button instead of options of dropdown.
okay so lets try using our custom way.
Lets set ref to your select component. And on onRequestClose lets focus on it.
class YourComponent extends React.Component {
refSelect = React.createRef();
openPopup = () => {
this.setState({ isOpen: true });
}
onRequestClose = () => {
this.setState({ isOpen: false });
this.refSelect.focus();
}
render() {
return (
<select ref={ref => this.refSelect = ref} onClick={this.openPopup}>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
</select>
<ReactModal
// your other props...
isOpen={isOpen}
onRequestClose={this.onRequestClose}
shouldReturnFocusAfterClose={false} />
);
}
}
Related
//This is dropdown component
const Dropdown: FC<any> = ({ list, item, title },props) => {
const isDisabled = item && item.users.length > 0 ? false : true;
const [show, setShow] = useState(false);
const toggleMenu = () => {
setShow(!show);
};
return (
<div>
<Button
title={title || "Action"}
onClick={toggleMenu}
iconName="downarrow"
iconPosition="left"
variant="outlined"
color="primary"
/>
)
The action menu is not getting closed even if I click somewhere on page, the menu
remains open until I explicitly click on the Action button again.
If you want the action menu to close when you click anywhere on the page using onClick won't help it. Use onBlur and use it on the button as -
<Button
title={title || "Action"}
onClick={toggleMenu}
onBlur={() => setShow(true)}
iconName="downarrow"
iconPosition="left"
variant="outlined"
color="primary"
/>
Or alternatively, what you can do is -
Create a reference to your outer div.
Add event listener mousedown (or click) to the document whenever this component appears on screen (eg. mount) and also don’t forget to remove the event on unmount too.
Inside the event (handleClick) this.{Any ref name you give}.contains(e.target) will return true if whatever you are clicking is inside the “node” ref.
Now you have it, you can now do whatever you feel like, close the modal, close the dropdown menu list, anything is allowed.
The above 4 points were taken from the article - https://medium.com/#pitipatdop/little-neat-trick-to-capture-click-outside-react-component-5604830beb7f.
I am learning React and I am making a dropdown menu that contains different items. I want to be able to select something from the dropdown menu and then click submit on a button component I have. And then do things based on whatever was in the drop down menu.
class TestDropDown extends React.Component {
state = {selectedOption: null,};
handleChange = selectedOption => {this.setState({ selectedOption });
};
render() {
const { selectedOption } = this.state;
document.write(selectedOption)
return (
<div className="testdropdown">
<Select value={selectedOption} onChange={this.handleChange} options={test_types}/>
</div>);
}
}
This is my dropdown menu component above. Below is my button component.
class ParseButton extends React.Component {
render() {
return (
<div className='parsebutton'>
<Button variant="primary" type='submit'>Submit</Button>{' '}
</div>);
}
}
I am trying to figure out when I click the "Submit" button, how to read whatever was selected in the drop down and then act upon it.
I have a React app with modal, that pop-ups with rules of the game when one clicks a button. What I want to do is make it so when I click anywhere outside this pop up window it will close. i have three files. app.js, dialog.js, and outsidealerter.js . In my main app.js when I click a button it sets a state to visible, so my element takes it and renders based upon it. my outsideralerer.js basicly detects if there is a click outside anything wrapped with specific tags. Now the problem comes that i have a method that changes the state of visibility in app.js, so in order for outsderalerter.js to use it, I pass it to it so it can have access to my main state and change it so that when a click is outside the zone the pop up window disappears. Kind of works except it closes it down even if i click within a pop up window, because when i pass the value to outsidealerter it considers the whole body as a no click zone. My question is how can I prevent it from triggering and just pass it a value, or is it possible to change the state value of app.js from outsidealerter.js
App.js
updateState() {
this.setState({ isOpen: false });
}
<div id='rule-button'>
<button onClick={(e)=>this.setState({isOpen : true})} id="modalBtn" class="button">Open Rules</button>
</div>
<OutsideAlerter updateParent={ this.updateState.bind(this)}/>
<Dialog isOpen={this.state.isOpen} onClose={(e)=>this.setState({isOpen : false})}>
</Dialog>
outsidealerter.js
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
//alert('You clicked outside of me!');
{this.props.updateParent()};
}
}
I think it will be simpler to have the modal take the full space of the window height and width and just make it invisible except for the content of what you want to show.
We can wrap the modal with onClick={hideModal} and wrap the inner content with onClick={e => e.stopPropagation()} which will prevent our wrapper for triggering the hideModal handler.
class ModalWrapper extends React.Component {
state = { isModalOpen: true };
toggleModal = () => {
this.setState(({ isModalOpen }) => ({
isModalOpen: !isModalOpen
}));
};
render() {
const { isModalOpen } = this.state;
return (
<div className="App">
<button onClick={this.toggleModal}>Open Modal</button>
{isModalOpen && <Modal hideModal={this.toggleModal} />}
</div>
);
}
}
function Modal({ hideModal }) {
return (
<div onClick={hideModal} className="modal">
<div onClick={e => e.stopPropagation()} className="modal__content">
Modal content
</div>
</div>
);
}
Working example
I'm building a Modal component. This component takes modal content as children and the button to trigger the modal as a button prop.
This Modal component should render the button. When clicked it has to position a fixed element exactly on top of that button that then animates to a modal dialog. For this the Modal component needs a ref to the button DOM element to measure it's size and position with getBoundingClientRect.
I want the Modal component to be able to receive through button prop both
button DOM element or a custom React element that renders button.
The api of the component looks like this then
const ModalUser = () => (
<div>
<Modal button={<button>Button</button>}/>
<Modal button={<CustomButton>Button</CustomButton>}/>
</div>
)
The render method of Modal looks like this then
class Modal extends React.PureComponent {
render() {
return (
<div>
{React.cloneElement(this.props.button, {
ref: (el) => { this.button = el && el.button ? el.button : el },
onClick: this.onClick,
})}
<span>top: {this.state.top}</span>
<span>left: {this.state.left}</span>
</div>
);
}
}
And thus requires any CustomElement button to expose this.button as a ref to it's containing button.
class CustomButton extends React.PureComponent {
render() {
return (
<button
ref={(el) => { this.button = el }}
onClick={this.props.onClick}
>
<span>Custom</span>
<span>{this.props.children}</span>
</button>
)
}
}
For me this feels not optimal, but it works. I feel like there should be a more elegant solution to this. Does anyone have a suggestion how to do this better.
Here is a working demo
Codepen
I am trying to close from the outside a SelectField. The idea is that I have a multiple selectField (which doesn't close on change by definition) with a "Clear All" item in the drop down menu that deselects all items on click.
The code I have is similar to this:
class MultiSelect extends Component {
state = {values: []}
clear = () => {
this.setState({values: []});
// FixMe: close the DropDownMenu here.
};
handleChange = (e,i,v) => {
this.setState({values: v});
};
render() {
const {title, choices, style} = this.props;
const {values} = this.state
return <div style={{...style}}>
<SelectField
floatingLabelText={title}
multiple
value={values}
onChange={this.handleChange}
>
<MenuItem value={null} primaryText="Clear All" onTouchTap={this.clear}/>
<Divider/>
{choices.map((d,i) =>
<MenuItem key={i} value={d} primaryText={d} checked={values.includes(d)}/>
)}
</SelectField>
</div>
}
}
The best way of achieving this would be to call the DropDownMenu.close() function. Using the newly introduced dropDownMenuProps prop of SelectField I thought I could do this, however the function apparently gets overwritten in DropDownMenu.
So, is there another way to achieve this or should I implement this component from scratch using the DropDownMenu directly?