title: Problem with keyboard event in ag-grid with react-select as a popup custom cell editor
body: <p>I want to use react-select as a custom cell editor for ag-grid</p>
  
  <p>Here is the <a href="https://codesandbox.io/s/jovial-sunset-uq0bk" rel="nofollow noreferrer">code sandbox link</a></p>
  
  <p>You can download the source <a href="http://www.filedropper.com/sandbox2" rel="nofollow noreferrer">here</a></p>
  
  <pre><code>npm install
  npm start
  </code></pre>
  
  <p>I've removed all the css so it looks so plain, but it still does the job.</p>
  
  <p>Here is my package.json</p>
  
  <pre><code>{
    "name": "Test",
    "version": "1.5.0",
    "private": true,
    "dependencies": {
      "react": "16.8.1",
      "react-dom": "16.8.1",
      "react-select": "^2.4.1",
      "react-scripts": "2.1.5",
      "ag-grid-community": "20.1.0",
      "ag-grid-react": "20.1.0"
    },
    "scripts": {
      "start": "react-scripts start",
      "build": "react-scripts build",
      "test": "react-scripts test --env=jsdom",
      "eject": "react-scripts eject",
      "deploy": "npm run build",
      "lint:check": "eslint . --ext=js,jsx;  exit 0",
      "lint:fix": "eslint . --ext=js,jsx --fix;  exit 0",
      "install:clean": "rm -rf node_modules/ &amp;&amp; rm -rf package-lock.json &amp;&amp; npm install &amp;&amp; npm start"
    },
    "optionalDependencies": {
      "@types/googlemaps": "3.30.16",
      "@types/markerclustererplus": "2.1.33",
      "ajv": "6.9.1",
      "prettier": "1.16.4"
    },
    "devDependencies": {
      "eslint-config-prettier": "4.0.0",
      "eslint-plugin-prettier": "3.0.1"
    },
    "browserslist": [
      "&gt;0.2%",
      "not dead",
      "not ie &lt;= 11",
      "not op_mini all"
    ]
  }
  </code></pre>
  
  <p>index.js</p>
  
  <pre><code>import React from "react";
  import ReactDOM from "react-dom";
  import Grid from './app/AgGrid'
  
  const colDefs = [
      {
          headerName              : "Test",
          field                   : "test",       
          editor                  : "manyToOne",
          editable                : true,
          suppressKeyboardEvent   : function suppressEnter(params) {
              let KEY_ENTER = 13;
              let KEY_LEFT = 37;
              let KEY_UP = 38;
              let KEY_RIGHT = 39;
              let KEY_DOWN = 40;
              var event = params.event;
              var key = event.which;
              var editingKeys = [KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_ENTER];
              var suppress = params.editing &amp;&amp; editingKeys.indexOf(key) &gt;= 0;
              console.log("suppress "+suppress);
              return suppress;
          },
          cellEditor              : "reactSelectCellEditor",
      },
  ]
  
  ReactDOM.render(
  &lt;Grid
      readOnly={false}
      field={null}
      columnDefs={colDefs}
      editable={true}
      editingMode={null}
      rowData={[]}
  /&gt;
  ,
  document.getElementById("root")
  );
  </code></pre>
  
  <p>AgGrid.js</p>
  
  <pre><code>import React, { Component } from 'react'
  import { AgGridReact } from 'ag-grid-react'
  import 'ag-grid-community/dist/styles/ag-grid.css'
  import ReactSelectCellEditor from './ReactSelectCellEditor'
  
  class Grid extends Component {
  
      constructor(props) {
          super(props);
          this.state = {
              columnDefs  : props.columnDefs,
              rowData     : [],
              isFormOpen  : false,
              editedData  : {
                  data        : [],
                  rowIndex    : null,
                  columns     : []
              },
              frameworkComponents:{
                  'reactSelectCellEditor': ReactSelectCellEditor,
              },
          }
      }
  
      updateRowData = (newData,index) =&gt; {
          var unsavedData = this.state.rowData.map((row,i)=&gt;{
              if (i === index) {
                  return newData
              }
              else{
                  return row
              }
          })
          this.setState({rowData:unsavedData})
      }
  
      onGridReady = params =&gt; {       
      }
  
      createData = () =&gt; {
          let fields = this.state.columnDefs.map(column=&gt;{
              return column.field
          })
          let newData = {}
          fields.map(field=&gt;{
              if (field !== "id") {
                  newData[field] = ""
              }           
          })
          newData["delete"] = false
          this.setState({rowData:[...this.state.rowData,newData]})
      }
  
      toggleModal = () =&gt; {
          this.setState({isFormOpen:!this.state.isFormOpen})
      }
  
      setEditedData = (data) =&gt; {
          console.log("editedData",data)
          this.setState({editedData:data})
      }
  
  
      render() {
          var defaultColDef = {
              editable:this.state.editable,
              sortable:false
          }
  
          var initColDefs = this.state.columnDefs.map(colDef=&gt;{
              return {
                  ...colDef,
                  cellEditorParams:{
                      ...colDef.cellEditorParams,
                  }
              }
          })
  
          var renderedColumnDefs = []
          if (this.props.editingMode === "inline") {
              renderedColumnDefs = [
                  ...initColDefs
              ]
          }
          else if (this.props.readOnly) {
              renderedColumnDefs = [...initColDefs]
          }
          else{
              defaultColDef = {
                  editable:false,
                  sortable:false
              }
              renderedColumnDefs = [
                  ...initColDefs,
              ]
          }
          var renderedRowData = []
          this.state.rowData.map(row=&gt;{
              if (row.delete !== true) {
                  renderedRowData.push(row)
              }
          })
          return (
              &lt;React.Fragment&gt;
                  &lt;AgGridReact
                      defaultColDef                   = {defaultColDef}
                      columnDefs                      = {renderedColumnDefs}
                      rowData                         = {renderedRowData}
                      onGridReady                     = {this.onGridReady}
                      onCellValueChanged              = {this.handleChange}
                      frameworkComponents             = {this.state.frameworkComponents}
                      singleClickEdit                 = {true}
                      stopEditingWhenGridLosesFocus   = {true}
                      reactNext={true}
                  /&gt;
                  &lt;div className="addButton" style={{padding:'5px', border: '1px solid black'}} onClick={()=&gt;this.createData()}&gt;Add&lt;/div&gt;
              &lt;/React.Fragment&gt;
          );
      } 
  }
  
  export default Grid
  </code></pre>
  
  <p>And ReactSelectCellEditor.js</p>
  
  <pre><code>import React, { Component } from 'react'
  import CreatableSelect  from "react-select/lib/Creatable"
  import { components } from 'react-select'
  import ReactDOM from 'react-dom'
  
  const colourOptions = [
      { value: 'red', label: 'Red' },
      { value: 'blue', label: 'Blue' },
      { value: 'yellow', label: 'Yellow' }
  ]
  
  class ReactSelectCellEditor extends Component {
  
      constructor(props) {
          super(props);
          this.state = {
              value : null
          }
          this.handleChange = this.handleChange.bind(this)
      }
  
      componentDidMount() {
      }
  
      handleCreateOption = (inputValue:any) =&gt; {
      }
  
      handleChange(selected){
          this.setState({
              value : selected.value
          })
      }
  
      afterGuiAttached(param) {
          let self = this;
          this.Ref.addEventListener('keydown', function (event) {
              // let key = event.which || event.keyCode
              // if (key === 37 || key === 38 || key === 39 || key === 40 || key === 13) {
              //     event.stopPropagation();
              //     console.log("onKeyDown "+key);
              // }
          });
          this.SelectRef.focus()
      }
  
      formatCreate = (inputValue) =&gt; {
          return (&lt;p&gt; Add: {inputValue}&lt;/p&gt;); 
      };
  
      isPopup(){
          return true
      }
  
      getValue() {
          return this.state.value
      }
  
      render() {
          return (
              &lt;div ref = { ref =&gt; { this.Ref = ref }} style={{width: '200px'}}&gt; 
                  &lt;CreatableSelect
                      onChange                = {this.handleChange}
                      options                 = {colourOptions}
                      formatCreateLabel       = {this.formatCreate}
                      createOptionPosition    = {"first"}
                      ref                     = { ref =&gt; { this.SelectRef = ref }}
                  /&gt;
              &lt;/div&gt;
          );
      }
  }
  
  export default ReactSelectCellEditor
  </code></pre>
  
  <p>As you can see, a simple bare bone combined implementation between ag-grid and <strong>popped up</strong> (not in cell) react-select.</p>
  
  <p>It works fine, except the default navigation key event of ag-grid hinders react-select from working flawlessly.</p>
  
  <p>Now I've read about the solutions to this here in <a href="https://www.ag-grid.com/javascript-grid-cell-editor/#keyboard-navigation-while-editing" rel="nofollow noreferrer">ag-grid documentation of keyboard navigation while editing</a></p>
  
  <p>Here is the thing:</p>
  
  <ol>
  <li><p>Firstly, I have tried solution 1 which is to stop event propagation of my custom cell editor. It works, but strangely <strong>although</strong> I stop the event bubbling in the <strong>container</strong> of react-select, not the react-select itself, as you can see from the commented out script in ReactSelectCellEditor.js, somehow React Select Key Event also stops executing. I don't know why react select event which is a child element of the div stops executing when I stop the event propagation in the parent element. Isn't the event supposed to bubble up from child to parent not the other way around? So first solution is a no go.</p></li>
  <li><p>As for the second solution the one that is strange is ag-grid itself. As the documentation says, ag-grid allows us to intercept the key event by defining a function for suppressKeyboardEvent from the column definition. It works if the editing mode is <strong>not</strong> popup. But in my case, I use the popup mode, and in popup mode I find the suppressKeyboardEvent isn't triggered while editing, it isn't even called at all.</p></li>
  </ol>
  
  <p>Now I'm in a deadlock. In the first solution the react-select behaves weirdly and in the second one it is the ag-grid that behaves weirdly. How do I solve this?</p>
  
  <h2>Additional find</h2>
  
  <p>After searching through the internet it looks like React Select uses <code>SyntheticKeyboardEvent</code> which, if I'm not wrong, executes AFTER events have already gone through a first capture/bubbling cycle across the DOM tree. So if I stop the propagation <code>SyntheticKeyboardEvent</code> won't be triggered. But if I don't stop the propagation ag-grid which uses the native event listener will trigger its default navigation function and kill the react-select component. Now is this it for me? A dead end?</p>