0

I used an editable data Table of prime-react , and it worked mouse click. But Problem is tab key not work.I want to enable editable mode one cell to another cell using tab key. I use Prime react version 1.4.0

A full code are given below:-

index.js where my editable table code contains

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import reducer from './reducer';
import saga from './saga';
import messages from './messages';
import { Messages } from 'primereact/components/messages/Messages';
import { Growl } from 'primereact/components/growl/Growl';
import { Panel } from 'primereact/components/panel/Panel';
import { Button } from 'primereact/components/button/Button';
import { DataTable } from 'primereact/components/datatable/DataTable';
import { Column } from 'primereact/components/column/Column';
import { InputText } from 'primereact/components/inputtext/InputText';
import { Dropdown } from 'primereact/components/dropdown/Dropdown';
import CustomDataTable from 'components/CustomDataTable';
import { makeSelectSelectedStudent, makeSelectSectionName, makeSelectStudentUpdateBasicInformation, makeSelectSectionList, makeSelectStdBasicInfo, makeSelectEditorStudent, makeSelectSetMessage, makeSelectSetErrMessage, makeSelectLoaderOff, makeSelectLoaderOn } from './selectors';
import { selectStudent, setEditorData, submitUpdate, getMessage, getErrMessage, submitForm, changeSectionName, getLoaderOn, getLoaderOff } from './actions';
import AppPrivateLayout from '../AppPrivateLayout';

export class StudentUpdateBasicInformation extends React.Component {
  componentDidUpdate() {
    this.props.changeMessage('');
  }

  rollBody(rowData) {
    return <InputText type="text" value={rowData.studentRoll} />;
  }

  nameBody(rowData) {
    return <InputText type="text" value={rowData.studentName} />;
  }

  fatherNameBody(rowData) {
    return <InputText type="text" value={rowData.fatherName} />;
  }

  motherNameBody(rowData) {
    return <InputText type="text" value={rowData.motherName} />;
  }

  contactBody(rowData) {
    return <InputText type="text" value={rowData.guardianMobile} />;
  }

  genderBody(rowData) {
    let gender = [
      { label: 'Male', value: 'Male' },
      { label: 'Female', value: 'Female' },
      { label: 'Others', value: 'Others' }
    ];

    return <Dropdown value={rowData.studentGender} options={gender} style={{ width: '100%' }} placeholder="Select" />
  }

  religionBody(rowData) {
    let religion = [
      { label: 'Islam', value: 'Islam' },
      { label: 'Hindu', value: 'Hindu' },
      { label: 'Buddhist', value: 'Buddhist' },
      { label: 'Christian', value: 'Christian' },
      { label: 'Others', value: 'Others' }
    ];
    return <Dropdown value={rowData.studentReligion} options={religion} style={{ width: '100%' }} placeholder="Select" />
  }

  bloodBody(rowData) {
    let blood = [
      { label: 'A+', value: 'A+' },
      { label: 'A-', value: 'A-' },
      { label: 'B+', value: 'B+' },
      { label: 'B-', value: 'B-' },
      { label: 'O+', value: 'O+' },
      { label: 'O-', value: 'O-' }

    ];
    return <Dropdown value={rowData.bloodGroup} options={blood} style={{ width: '100%' }} placeholder="Select" />
  }

  render() {
    let rollEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Roll" value={row.rowData.studentRoll} />
    }

    let nameEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Student name" value={row.rowData.studentName} />
    }

    let fatherNameEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Father name" value={row.rowData.fatherName} />
    }

    let motherNameEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Mother name" value={row.rowData.motherName} />
    }

    let contactEditor = (row) => {
      return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Contact Number" value={row.rowData.guardianMobile} />
    }

    let genderEditor = (row) => {
      let gender = [
        { label: 'Male', value: 'Male' },
        { label: 'Female', value: 'Female' },
        { label: 'Others', value: 'Others' }
      ];

      return <Dropdown value={row.rowData.gender} options={gender} onChange={(evt) => this.props.onEditorValueChange(row, evt.value)} style={{ width: '100%' }} placeholder="Select" />
    }

    let religionEditor = (row) => {
      let religion = [
        { label: 'Islam', value: 'Islam' },
        { label: 'Hindu', value: 'Hindu' },
        { label: 'Buddhist', value: 'Buddhist' },
        { label: 'Christian', value: 'Christian' },
        { label: 'Others', value: 'Others' }
      ];
      return <Dropdown value={row.rowData.religion} options={religion} onChange={(evt) => { this.props.onEditorValueChange(row, evt.value); }} style={{ width: '100%' }} placeholder="Select" />
    }

    let bloodEditor = (row) => {
      let blood = [
        { label: 'A+', value: 'A+' },
        { label: 'A-', value: 'A-' },
        { label: 'B+', value: 'B+' },
        { label: 'B-', value: 'B-' },
        { label: 'O+', value: 'O+' },
        { label: 'O-', value: 'O-' }

      ];
      return <Dropdown value={row.rowData.blood} options={blood} onChange={(evt) => { this.props.onEditorValueChange(row, evt.value); }} style={{ width: '100%' }} placeholder="Select" />
    }

    let msg = "";
    if (this.props.setMessage) {
      msg = { severity: 'success', detail: this.props.setMessage.message };
      this.growl.show(msg);
    }
    else if (this.props.setErrMessage) {
      msg = { severity: 'error', summary: 'Failed', detail: this.props.setErrMessage };
      this.growl.show(msg);
    }
    if(this.props.loaderOn){
      if(this.props.loaderOn === 'On') {
      $('.loaderDiv').show();
    } else if(this.props.loaderOn === 'Off'){
      $('.loaderDiv').hide();
    }
  }
    let content = '';
    if (this.props.stdBasicInfo && this.props.stdBasicInfo.length) {
      $('#UpdateBtnID').show();
      let selectedStudentArr = [];
      if (this.props.selectedStudent.length) {
        Array.prototype.push.apply(selectedStudentArr, this.props.selectedStudent);
      }
      let columnData = [
        <Column selectionMode="multiple" header="Mark" style={{ width: '3em' }} />,
        <Column field="studentRoll" header="Roll No." editor={rollEditor} body={this.rollBody} style={{ width: '55px' }} />,
        <Column field="studentName" header="Name" editor={nameEditor} body={this.nameBody} style={{ width: '170px' }} />,
        <Column field="fatherName" header="Father Name" editor={fatherNameEditor} body={this.fatherNameBody} style={{ width: '145px' }} />,
        <Column field="motherName" header="Mother Name" editor={motherNameEditor} body={this.motherNameBody} style={{ width: '145px' }} />,
        <Column field="guardianMobile" header="Contact No." editor={contactEditor} style={{ width: '100px' }} body={this.contactBody} />,
        <Column field="studentGender" header="Gender" editor={genderEditor} body={this.genderBody} style={{ width: '85px' }} />,
        <Column field="studentReligion" header="Religion" editor={religionEditor} body={this.religionBody} style={{ width: '85px' }} />,
        <Column field="bloodGroup" header="Blood Group" editor={bloodEditor} style={{ width: '80px' }} body={this.bloodBody} />
      ];
      content = <CustomDataTable
        info={this.props.stdBasicInfo}
        onSelectionChange={this.props.onSelectionChange}
        selectedData={selectedStudentArr}
        columnData={columnData}
        isSelectionOn={true}
        editable={true}
        header={'Student List'}
        rows={10}
      />
    }

    //FOR SECTION LIST
    let sectionListOptions = [];
    if (this.props.sectionList && this.props.sectionList.length) {
      sectionListOptions = this.props.sectionList.map((item) => ({
        value: item.classConfigId,
        label: item.classShiftSection,
      }))
    }

    return (
      <div>
        <AppPrivateLayout>
        <Panel header="Student Information Update">
          <form method="post" onSubmit={this.props.onSubmitForm} >
            <div className='ui-g form-group'>

              <div className='ui-g-2 ui-lg-2 ui-md-2'></div>
              <div className='ui-g-2 ui-lg-2 ui-md-2 ui-sm-12 netiLabel'>
                <label> Section <span className="required"> * </span></label>
              </div>
              <div className='ui-g-3 ui-lg-3 ui-md-4 ui-sm-12 ui-fluid'>
                <Dropdown value={this.props.sectionname} onChange={this.props.onChangeSectionList} options={sectionListOptions} placeholder="Select Section" autoWidth={false} />
              </div>
              <div className='ui-g-2 ui-lg-2 ui-md-3 ui-sm-12 ui-fluid'>
                <Button icon="ui-icon-search" title="Search" label='Search'></Button>
              </div>
              <div className='ui-g-2 ui-lg-2 ui-fluid'></div>
            </div>
          </form>
          {content}
          <div className='ui-g'>
            <Growl ref={(el) => this.growl = el} />
            <div className='ui-g-4 ui-lg-4 ui-md-4 ui-sm-12 ui-fluid'>

            </div>
            <div className='ui-g-6 ui-lg-6 ui-md-5 ui-sm-12 ui-fluid'></div>
            <div className='ui-g-2 ui-lg-2 ui-md-3 ui-sm-12 ui-fluid'>
              <Button id="UpdateBtnID" style={{ display: 'none' }} onClick={this.props.onUpdate} icon='ui-icon-autorenew' label='Update'></Button>
            </div>
          </div>
        </Panel>
        <div class="loaderDiv" style={{display: 'none'}}>
            <img className="sticky" src="https://loading.io/spinners/harmony/lg.harmony-taiji-spinner.gif" />
          </div>
        </AppPrivateLayout>
      </div>
    );
  }
}

StudentUpdateBasicInformation.propTypes = {
  stdBasicInfo: PropTypes.any,
  onSubmitForm: PropTypes.func,
  onEditorValueChange: PropTypes.func,
  value: PropTypes.any,
  onUpdate: PropTypes.func,
  setMessage: PropTypes.any,
  setErrMessage: PropTypes.any,
  changeMessage: PropTypes.func,
  sectionList: PropTypes.any,
  onChangeSectionList: PropTypes.func,
  sectionname: PropTypes.any,
  loaderOn: PropTypes.any,
};

const mapStateToProps = createStructuredSelector({
  stdBasicInfo: makeSelectStdBasicInfo(),
  selectedStudent: makeSelectSelectedStudent(),
  value: makeSelectEditorStudent(),
  setMessage: makeSelectSetMessage(),
  setErrMessage: makeSelectSetErrMessage(),
  sectionList: makeSelectSectionList(),
  sectionname: makeSelectSectionName(),
  loaderOn: makeSelectLoaderOn(),
});

function mapDispatchToProps(dispatch) {
  return {
    changeMessage: (evt) => {
      dispatch(getMessage());
      dispatch(getErrMessage());
      dispatch(getLoaderOn(evt));
    },

    onSubmitForm: (evt) => {
      if (evt !== undefined && evt.preventDefault)
        evt.preventDefault();
      dispatch(submitForm());
    },
    onSelectionChange: (evt) => dispatch(selectStudent(evt.data)),
    onEditorValueChange: (row, value) => {
      dispatch(setEditorData(row, value));
    },
    onUpdate: (evt) => dispatch(submitUpdate()),
    onChangeSectionList: (evt) => dispatch(changeSectionName(evt.value)),
  };
}

const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({ key: 'studentUpdateBasicInformation', reducer });
const withSaga = injectSaga({ key: 'studentUpdateBasicInformation', saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(StudentUpdateBasicInformation);

constants.js

export const DEFAULT_ACTION = 'app/StudentUpdateBasicInformation/DEFAULT_ACTION';

export const SUBMIT_FORM = 'app/StudentUpdateBasicInformation/SUBMIT_FORM';
export const SET_STD_BASIC_INFO = 'app/StudentUpdateBasicInformation/SET_STD_BASIC_INFO';
export const SELECT_STUDENT = 'app/StudentUpdateBasicInformation/SELECT_STUDENT';
export const SET_EDITOR_DATA = 'app/StudentUpdateBasicInformation/SET_EDITOR_DATA';
export const GET_MESSAGE = 'app/StudentUpdateStudentId/GET_MESSAGE';
export const GET_ERR_MESSAGE = 'app/StudentUpdateStudentId/GET_ERR_MESSAGE';
export const SUBMIT_UPDATE = 'app/StudentUpdateBasicInformation/SUBMIT_UPDATE';
export const SET_SECTION_LIST = 'app/StudentUpdateBasicInformation/SET_SECTION_LIST';
export const CHANGE_SECTIONNAME = 'app/StudentUpdateBasicInformation/CHANGE_SECTIONNAME';
export const GET_LOADER_ON = 'app/StudentUpdateBasicInformation/GET_LOADER_ON';

actions.js

import {
  DEFAULT_ACTION, SUBMIT_FORM, SET_STD_BASIC_INFO, SELECT_STUDENT, SET_EDITOR_DATA, SUBMIT_UPDATE, GET_MESSAGE, GET_ERR_MESSAGE, CHANGE_SECTIONNAME, SET_SECTION_LIST, GET_LOADER_OFF, GET_LOADER_ON
} from './constants';

export function defaultAction() {
  return {
    type: DEFAULT_ACTION,
  };
}

export function setStdBasicInfo(item) {
  return {
    type: SET_STD_BASIC_INFO,
    item,
  }
}

export function selectStudent(data) {
  return {
    type: SELECT_STUDENT,
    data,
  };
}

export function setEditorData(row, value) {
  return {
    type: SET_EDITOR_DATA,
    row, 
    value,
  };
}


export function submitForm() {
  return {
    type: SUBMIT_FORM,
  };
}

export function submitUpdate() {
  return {
    type: SUBMIT_UPDATE,
  };
}

export function getMessage(message) {
  return {
    type: GET_MESSAGE,
    message,
  }
}

export function getErrMessage(errmessage) {
  return {
    type: GET_ERR_MESSAGE,
    errmessage,
  }
}

export function setSectionList(sectionList) {
  return {
    type: SET_SECTION_LIST,
    sectionList,
  };
}
export function changeSectionName(sectionname) {
  return {
    type: CHANGE_SECTIONNAME,
    sectionname,
  };
}


export function getLoaderOn(loaderOn){
  return{
    type: GET_LOADER_ON,
    loaderOn,
  }
}

reducer.js

import { fromJS } from 'immutable';
import {
  DEFAULT_ACTION, SET_STD_BASIC_INFO, SELECT_STUDENT, SET_EDITOR_DATA, GET_MESSAGE, GET_ERR_MESSAGE, SET_SECTION_LIST, CHANGE_SECTIONNAME, GET_LOADER_ON, GET_LOADER_OFF
} from './constants';

const initialState = fromJS({
  selectedStudent: [],
  value: [],
  sectionList: {},
  sectionname: '',
});

function studentUpdateBasicInformationReducer(state = initialState, action) {
  switch (action.type) {
    case DEFAULT_ACTION:
      return state;
    case SET_STD_BASIC_INFO:
      return state.set('stdBasicInfo', action.item);
    case SELECT_STUDENT:
      return state.set('selectedStudent', action.data);
    case SET_EDITOR_DATA:
      let updatedInfost = [...action.row.value];
      updatedInfost = [...action.row.value];
      updatedInfost[action.row.rowIndex][action.row.field] = action.value;
      return state.set('stdBasicInfo', updatedInfost);
    case GET_MESSAGE:
      return state.set('setMessage', action.message);
    case GET_ERR_MESSAGE:
      return state.set('setErrMessage', action.errmessage);
    case SET_SECTION_LIST:
      return state.set('sectionList', action.sectionList);
    case CHANGE_SECTIONNAME:
      return state.set('sectionname', action.sectionname);
      case GET_LOADER_ON:
      return state.set('loaderOn', action.loaderOn);
      // case GET_LOADER_OFF:
      // return state.set('loaderOff', action.loaderOff);
    default:
      return state;
  }
}

export default studentUpdateBasicInformationReducer;

selectors.js

import { createSelector } from 'reselect';

const selectStudentUpdateBasicInformationDomain = (state) => state.get('studentUpdateBasicInformation');

const makeSelectStudentUpdateBasicInformation = () => createSelector(
  selectStudentUpdateBasicInformationDomain,
  (substate) => substate.toJS()
);

const makeSelectStdBasicInfo = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('stdBasicInfo'));
const makeSelectSelectedStudent = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('selectedStudent'));
const makeSelectEditorStudent = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('value'));
const makeSelectSetMessage = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('setMessage'));
const makeSelectSetErrMessage = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('setErrMessage'));
const makeSelectSectionName = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('sectionname'));
const makeSelectSectionList = () => createSelector(selectStudentUpdateBasicInformationDomain,(substate) => substate.get('sectionList'));
const makeSelectLoaderOn = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('loaderOn'));

export {
  selectStudentUpdateBasicInformationDomain,
  makeSelectStdBasicInfo,
  makeSelectSelectedStudent,
  makeSelectEditorStudent,
  makeSelectSetMessage,
  makeSelectSetErrMessage, makeSelectSectionName, makeSelectSectionList, makeSelectLoaderOn, 
  //makeSelectLoaderOff
};

saga.js

import { take, call, put, select, takeLatest } from 'redux-saga/effects';
import { BASE_URL, FETCH_BASIC_INFO_LIST, UPDATE_ID, GET_CLASS_CONFIGURATION_URL, STUDENT_CONFIG_LIST } from '../../utils/serviceUrl';
import { setStdBasicInfo, getMessage, getErrMessage, selectStudent, setSectionList, changeSectionName, getLoaderOn } from './actions';
import request from '../../utils/request';
import { SUBMIT_FORM, SUBMIT_UPDATE } from './constants';
import { makeSelectEditorStudent, makeSelectSelectedStudent, makeSelectSectionList, makeSelectSectionName  } from './selectors';
import { getTokenData } from '../../utils/authHelper';

//FOR SECTION LIST
export function* fetchSectionList() {
  const tokenData = JSON.parse(getTokenData());
  const requestUrl = BASE_URL.concat(GET_CLASS_CONFIGURATION_URL);
  const options = {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': tokenData.token_type+" "+tokenData.access_token,
    },
  };
  try {
    const response = yield call(request, requestUrl, options);
    if (response.item) {
      yield put(setSectionList(response.item));
    }
  } catch (err) {
    console.dir(err);
  }
}

//FOR STUDENT BASIC INFO LIST
export function* fetchStudentBasicInfoList() {
  const tokenData = JSON.parse(getTokenData());
  const classConfigId = yield select(makeSelectSectionName());
  let msgg;
  if (classConfigId == '') {
    let msg = "An error has occured. Please fill up all required fields";
    yield put(getErrMessage(msg));

  }
  else {
    msgg = 'On';
    yield put(getLoaderOn(msgg));
  const requestURL = BASE_URL.concat(STUDENT_CONFIG_LIST).concat('?classConfigId=').concat(classConfigId);
  const options = {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': tokenData.token_type+" "+tokenData.access_token,
    },
  };
  const info = yield call(request, requestURL,options);
  yield put(setStdBasicInfo(info.item));
  msgg = 'Off';
  yield put(getLoaderOn(msgg));
  }
}


//FOR UPDATE STUDENT INFORMATION
export function* updateStdBasicInfo() {
  const tokenData = JSON.parse(getTokenData());
  const selectedCheckData = yield select(makeSelectSelectedStudent());
  let selectedData = [];
  if (selectedCheckData.length === undefined || selectedCheckData.length === 0) {
    const errresult = "An error has occured. Please fill up all required fields";
    yield put(getErrMessage(errresult));
  } else {

    for (const i in selectedCheckData) {
      const DataList = selectedCheckData[i];
      selectedData.push(DataList);
    }

    const requestURL = BASE_URL.concat(UPDATE_ID);
    const options = {
      method: 'PUT',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': tokenData.token_type+" "+tokenData.access_token,
      },

      body: JSON.stringify(selectedData),
    }

    try {
      const result = yield call(request, requestURL, options);
      yield put(selectStudent([]));
      yield fetchStudentBasicInfoList();
      yield put(getMessage(result));

    } catch (err) {
      const errresult = "Something went wrong. Please try again.";
      yield put(setStdBasicInfo(info.item));
      yield put(getErrMessage(errresult));

    }
  }
}


export default function* defaultSaga() {
  yield fetchSectionList();
  yield takeLatest(SUBMIT_FORM, fetchStudentBasicInfoList);
  yield takeLatest(SUBMIT_UPDATE, updateStdBasicInfo);

}
Md. Nasir Uddin
  • 151
  • 1
  • 13

2 Answers2

1

DuĊĦan's answer worked for me but for 2 exceptions:

In onEditorKeyDown instead of

var table = this.dt.container.childNodes[1].childNodes[0].childNodes[1];

use

let table = this.dt.container.childNodes[0].childNodes[0].childNodes[1];

In addition, (event.which === 9) should be used instead of (event.keyCode == 9) and all the functions must be bound in the constructor:

this.inputTextEditor = this.inputTextEditor.bind(this)
this.xxxEditor = this.xxxEditor.bind(this)
this.onEditorKeyDown = this.onEditorKeyDown.bind(this)
0

I have an idea how you can do this:

while you are in editable cell, you need to intercept TAB key and then to simulate click on next cell to the right or, if current cell is the last one, on first cell in next row.

Steps in following example are based on PrimeReact's show case editable page. You can adopt them for your particular case.

FUNCTIONAL EXAMPLE

Step 1:

define onRowClickhandler and add it to DataTable component to be able to catch currently clicked rowIndex (we need to know what is the row index of the cell currently edited)

onRowClick(event) {
    this.rowIndex = event.index;
}
...
<DataTable ref={(el) => this.dt = el} editable={true} onRowClick={(e) => this.onRowClick(e)} ...>

Step 2:

Define column index (ordinal number) to each of column editors: 1st column has index 0, 2nd column index 1, etc. For example

vinEditor(props) {
    return this.inputTextEditor(props, 'vin', 0);
}

yearEditor(props) {
    return this.inputTextEditor(props, 'year', 1);
}

brandEditor(props) {
    return this.inputTextEditor(props, 'brand', 2);
}

where inputTextEditor now look like this

inputTextEditor(props, field, columnIndex) {
    return <InputText type="text" value={props.rowData.year}
     onKeyDown={(e) => this.onEditorKeyDown(e, columnIndex)} onChange={(e) => this.onEditorValueChange(props, e.target.value)} />;
}

Note that I've added onKeyDown handler and passed columnIndex arg to it so that we can recognize the column where some key is typed.

Step 3:

Finally we can define onEditorKeyDown to do the magic (check out code comments for additional explanations)

onEditorKeyDown(event, columnIndex) {
    console.log("onKeyDown", event);
    console.log("key code", event.keyCode);
    console.log("columnIndex", columnIndex);
    console.log("rowIndex", this.rowIndex);

    //change following 2 constants to to fit your case
    const columnCount = 4;
    const rowCount = 10;

    //check if TAB (key code is 9) is pressed on InputText
    if (event.keyCode == 9) {
        //prevent default behaviour on TAB press
        event.preventDefault();

        //this.dt is reference to DataTable element
        //get reference to `table` node only
        var table = this.dt.container.childNodes[1].childNodes[0].childNodes[1];

        //check if we are not in last column
        if (columnIndex < columnCount - 1) {
            //simulate click on next column
            table.childNodes[this.rowIndex].childNodes[columnIndex + 1].click();
        } else {
            //we are in the last column, check if we are not in last row
            if (this.rowIndex < rowCount - 1) {
                //we are not in the last row
                // select next row
                this.rowIndex += 1;

                //simulate click on first column in next row
                table.childNodes[this.rowIndex].childNodes[0].click();
            }
        }
    }
}
Dusan Kovacevic
  • 1,377
  • 1
  • 13
  • 19