2
votes

I was dealing with some problem these days ago. The problem is related with event bubbling with react.

I have implemented a drop-down with some react components. This is made with a div containing a ul and some li elements. I need to make the drop down accessible by keyboard so I fire the onblur, onfocus, onkeydown and onclick elements to show and hide the drop down and to use it with keyboard.

I fire a function passed by props to real with the show/hide stuff and when the div is focused or clicked I show the drop down and when is onblur I hide it changing the state of the component. The problem is that I have some li elements with onclick functions to select the desired option. However, when I click on an option, onblur event of parent fires, it changes the state and onclick event of the li element doesn't fire so I cannot choose any option.

I'm trying to solve this using event bubbling or propagation but I couldn't find any solution. Could you please help me?

Thanks a lot!

EDIT: Code of the problem:

const Filter = (props: FilterProps) => {
...
<div onBlur={(e) =>
   {props.handleDropdown(e, props.isOpen)}} onKeyDown={(e) => {props.handleKeyDown(e)}} onFocus={(e) => props.handleDropdown(e, props.isOpen)} className={props.isOpen ? "Dropdown Dropdown--multiselection is-open" : "Dropdown Dropdown--multiselection"}>
   <Button className="FilterField Dropdown__trigger Button--secondary" onClick={(e) => props.handleDropdown(e, props.isOpen)}>
   <span className="Dropdown__label">{setLabels(ASSETS, props.selectedAssets)}</span>
   <span className="Dropdown__caret"></span>
   </Button>
   <ul className="Dropdown__menu">
      <li className={checkSelectedAsset(-1, props.selectedAssets).class} onClick={(e) => props.selectAsset(e, -1)}>
      <Translate id="all"/>
      {checkSelectedAsset(-1, props.selectedAssets).isSelected && 
      <span className="Dropdown__menu-item-icon">
         <IconCheck/>
      </span>
      }
      </li>
      <li className="Dropdown__menu-divider"></li>
      {
      (props.assetClasses && props.assetClasses.length > 0) &&
      props.assetClasses.map((asset) => {
      return (
      <li className={checkSelectedAsset(asset, props.selectedAssets).class} onClick={(e) => props.selectAsset(e, asset)}>
      {
      <span>
         <Translate id={`products.${Helper.getType(asset)}`}/>
      </span>
      }{checkSelectedAsset(asset, props.selectedAssets).isSelected && 
      <span className="Dropdown__menu-item-icon">
         <IconCheck/>
      </span>
      }
      </li>
      );
      })
      }
   </ul>
</div>

interface PositionsContainerState {
...
isOpen: boolean;
}   

class Container extends 
React.Component<ContainerProps, ContainerState> {
openCloseDropdown = (event, isOpen: boolean) => {
event.stopPropagation();
if (event.type === "focus") {
this.setState({
dropdownExpanded: true,
focusTriggered: true
});
}
else if (event.type === "blur") {
this.setState({
dropdownExpanded: false,
focusTriggered: false
});
}
else if (event.type === "click") {
if (this.state.focusTriggered) {
this.setState({
dropdownExpanded: isOpen,
focusTriggered: false
});
} 
else {
this.setState({
dropdownExpanded: !isOpen,
});
}
}
};
selectAsset = (event, asset: number) => {
//event.detail.keyboardEvent.preventDefault();
if (asset < 0) {
this.props.dispatch(setFilterAssets([]));
}
else {
let auxSelectedAssets = assign([], this.props.selectedAssets);
if (auxSelectedAssets.indexOf(asset) === -1)
auxSelectedAssets.push(asset);
else
auxSelectedAssets.splice(auxSelectedAssets.indexOf(asset), 1);
this.props.dispatch(setFilterAssets(auxSelectedAssets));
}
}
render() {
return (
<Filter
   handleDropdown={props.openCloseDropdown}
   isOpen={props.isOpen}
   selectAsset={props.selectAsset}
   />
)
};
1
A related code snippet will be helpful in debugging your issueShubham Khatri
There you have some code of my problem, as you can see the main problem is about getting to onclick of li elements, I cannot reach that event.lbarrous

1 Answers

0
votes

I think u should lift the state and event handlers of the menu up to the parent component and make the child ones stateless. so when u fire an event on the child to will trigger the handler on the parent through the prop so now you can put some flags to handle the events (like that)

parent (onBlur) (add flag on the state to check if it's blur and not clicked and vice-versa)

-child (click)
-child (keyboard).

please ping me if the answer not clear enough.