master 7510e01df4b0 cached
44 files
118.9 KB
29.7k tokens
151 symbols
1 requests
Download .txt
Repository: handsontable/react-handsontable
Branch: master
Commit: 7510e01df4b0
Files: 44
Total size: 118.9 KB

Directory structure:
gitextract_fs7xo0sf/

├── .babelrc
├── .codesandbox/
│   └── ci.json
├── .config/
│   ├── base.js
│   ├── commonjs.js
│   ├── es.js
│   ├── factory.js
│   ├── helpers/
│   │   └── licenseBanner.js
│   ├── minified.js
│   └── umd.js
├── .editorconfig
├── .github/
│   ├── ISSUE_TEMPLATE.md
│   └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── package.json
├── rollup.config.js
├── src/
│   ├── baseEditorComponent.tsx
│   ├── helpers.tsx
│   ├── hotColumn.tsx
│   ├── hotTable.tsx
│   ├── index.tsx
│   ├── json.d.ts
│   ├── portalManager.tsx
│   ├── settingsMapper.ts
│   └── types.tsx
├── test/
│   ├── _helpers.tsx
│   ├── autoSizeWarning.spec.tsx
│   ├── componentInternals.spec.tsx
│   ├── hotColumn.spec.tsx
│   ├── hotTable.spec.tsx
│   ├── jestsetup.ts
│   ├── reactContext.spec.tsx
│   ├── reactHooks.spec.tsx
│   ├── reactLazy.spec.tsx
│   ├── reactMemo.spec.tsx
│   ├── reactPureComponent.spec.tsx
│   ├── redux.spec.tsx
│   └── settingsMapper.spec.tsx
├── test-tsconfig.json
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": [
    "@babel/preset-react"
  ],
  "env": {
    "test": {
      "presets": [
        "@babel/env",
        "@babel/preset-typescript"
      ],
      "plugins": [
        "@babel/transform-runtime",
        "@babel/plugin-proposal-class-properties"
      ]
    }
  }
}


================================================
FILE: .codesandbox/ci.json
================================================
{
  "sandboxes": [
    "github/handsontable/examples/tree/master/react/pull-request"
  ]
}


================================================
FILE: .config/base.js
================================================
import nodeResolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import typescript from 'rollup-plugin-typescript2';
import json from 'rollup-plugin-json';
import commonjs from 'rollup-plugin-commonjs';

export const plugins = {
  typescript: typescript(),
  babel: babel({
    babelrc: false,
    exclude: ['/node_modules/', '**.json'],
    extensions: ['.js', '.ts', '.tsx', '.jsx'],
    presets: [
      '@babel/env'
    ],
  }),
  nodeResolve: nodeResolve(),
  json: json({
    include: 'package.json',
    compact: true
  }),
  commonjs: commonjs({
    include: [
      'node_modules/**',
      'src/lib/**'
    ]
  })
};

export const baseConfig = {
  input: 'src/index.tsx',
  plugins: [
    plugins.json,
    plugins.replace,
    plugins.commonjs,
    plugins.typescript,
    plugins.babel,
    plugins.nodeResolve,
  ],
  external: [
    'react',
    'react-dom',
    'handsontable'
  ],
};


================================================
FILE: .config/commonjs.js
================================================
import typescript from 'rollup-plugin-typescript2';
import { baseConfig, plugins } from './base';

const env = process.env.NODE_ENV;
const filename = 'react-handsontable.js';

export const cjsConfig = {
  output: {
    format: env,
    indent: false,
    file: `./commonjs/${filename}`,
    exports: 'named'
  },
  plugins: baseConfig.plugins,
};


================================================
FILE: .config/es.js
================================================
import typescript from 'rollup-plugin-typescript2';
import { baseConfig } from './base';
import { plugins } from './base';

const env = process.env.NODE_ENV;
const filename = 'react-handsontable.js';

export const esConfig = {
  output: {
    format: env,
    indent: false,
    file: `./es/${filename}`
  },
  plugins: [
    plugins.json,
    plugins.replace,
    plugins.commonjs,
    typescript({
      tsconfigOverride: {
        compilerOptions: {
          declaration: true
        }
      },
      useTsconfigDeclarationDir: true,
    }),
    plugins.babel,
    plugins.nodeResolve,
  ]
};


================================================
FILE: .config/factory.js
================================================
import { baseConfig } from './base';
import { cjsConfig } from './commonjs';
import { esConfig } from './es';
import { umdConfig } from './umd';
import { minConfig } from './minified';

export function createConfig() {
  const env = process.env.NODE_ENV;
  const config = baseConfig;
  const newConfigs = {
    cjs: cjsConfig,
    es: esConfig,
    umd: umdConfig,
    min: minConfig,
  };
  const newConfig = newConfigs[env];

  for (let key in newConfig) {
    if (newConfig.hasOwnProperty(key)) {
      if (Array.isArray(config[key]) && Array.isArray(newConfig[key])) {
        config[key] = newConfig[key];

      } else if (typeof config[key] === 'object' && typeof newConfig[key] === 'object') {
        Object.assign(config[key], newConfig[key]);

      } else {
        config[key] = newConfig[key];
      }
    }
  }

  return config;
}


================================================
FILE: .config/helpers/licenseBanner.js
================================================
export function addLicenseBanner(config) {
  const path = require('path');
  const fs = require('fs');
  const packageBody = require(`./package.json`);

  let licenseBody = fs.readFileSync(path.resolve(__dirname, './LICENSE'), 'utf8');
  licenseBody += `\nVersion: ${packageBody.version} (built at ${new Date().toString()})`;

  config.output.banner = `/*!\n${licenseBody.replace(/^/gm, ' * ')}\n */`;

  return config;
}


================================================
FILE: .config/minified.js
================================================
import { baseConfig } from './base';
import { addLicenseBanner } from './helpers/licenseBanner';
import replace from 'rollup-plugin-replace';
import { uglify } from 'rollup-plugin-uglify';

const minFilename = 'react-handsontable.min.js';

const minConfig = {
  output: {
    format: 'umd',
    name: 'Handsontable.react',
    indent: false,
    sourcemap: true,
    exports: 'named',
    file: `./dist/${minFilename}`,
    globals: {
      react: 'React',
      'react-dom': 'ReactDOM',
      handsontable: 'Handsontable'
    }
  },
  plugins: baseConfig.plugins.concat([
    replace({
      'process.env.NODE_ENV': JSON.stringify('production')
    }),
    uglify({
      output: {
        comments: /^!/
      },
      compress: {
        pure_getters: true,
        unsafe: true,
        unsafe_comps: true,
      }
    })
  ])
};

addLicenseBanner(minConfig);

export { minConfig };


================================================
FILE: .config/umd.js
================================================
import { addLicenseBanner } from './helpers/licenseBanner';
import { baseConfig } from './base';
import replace from 'rollup-plugin-replace';

const env = process.env.NODE_ENV;
const filename = 'react-handsontable.js';

const umdConfig = {
  output: {
    format: env,
    name: 'Handsontable.react',
    indent: false,
    sourcemap: true,
    exports: 'named',
    file: `./dist/${filename}`,
    globals: {
      react: 'React',
      'react-dom': 'ReactDOM',
      handsontable: 'Handsontable'
    }
  },
  plugins: baseConfig.plugins.concat([
    replace({
      'process.env.NODE_ENV': JSON.stringify('production')
    })
  ])
};

addLicenseBanner(umdConfig);

export { umdConfig };


================================================
FILE: .editorconfig
================================================
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
### Description
<!--- Tell us what happens and what should happen -->

### Steps to reproduce
<!--- Provide steps to reproduce this issue -->
1.
2.
3.

### Demo
<!--- Provide a link to a live example on JSFiddle or Codepen or fill the following demo with your settings -->
https://jsfiddle.net/handsoncode/c6rgz08x/

### Your environment
* React wrapper version:
* Handsontable version:
* Browser Name and version:
* Operating System:



================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
### Context
<!--- Why is this change required? What problem does it solve? -->

### How has this been tested?
<!--- Please describe in detail how you tested your changes. -->

### Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature or improvement (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)

### Related issue(s):
1.
2.
3.

### Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the code style of this project,
- [ ] My change requires a change to the documentation.


================================================
FILE: .gitignore
================================================
node_modules
bower_components
dist
commonjs
es

.DS_Store
.idea
.rpt2_cache
*.log
/*.d.ts


================================================
FILE: .npmignore
================================================
*

!commonjs/*
!dist/*
!es/*
!package.json
!*.d.ts
!README.md
!LICENSE


================================================
FILE: .travis.yml
================================================
language: node_js

sudo: false

node_js:
  - '8'

before_script:
  - export TZ=Europe/Warsaw

notifications:
  email: false
  slack:
    secure: cDGwvetvROdzjr+rM8KYU48ZQNNwoyeSDI5KnH0y8jrGj/KhbHOsoToNjensduI3afoyouRKc1rbxe7qfmikT0SSiJluyWM4VwXRkdye0I0POnLYISS/fTPFGDulFyiuxOhHzmiQav1+zNkYonGgOVBVt/NqTZkSdc/n6HNdKXIC+3KLdUQ3AI+eaoPaph4jqUdaojSQr26K49Y8us/F7ITI7pPyxG/aVLzLwobGeUaYho+R3PahYBnPlhzNIpeAQ98ZM0Dv4+3ZLzkOZi+/qKWTsaOYFpAlm41QohrcN47XtBCDVLxZ9UzEghkIfHJS68po1MuBDK+ULgm7+LkwEMd6/iTJsKa7ziVmY5ijXdFIc4i+kfMjwsQO7Fm5XAVpm66O+qXlRG5cveCEuLl2NEGBsZ/6caKD7AOqWzF2X5UL+O3qX0deWLT4AOnS/nGnlysPr94/Pthy2QMTt8/lqxpMtBEH9zy0mM7l1ZbeL/eDgUGzNXHxS0rpaE873Ea9Kaq4PZ20AISTGlni11gZbhpAOWhjl9X4sbXzk8ANTvnTf/CeYX8JXCuTEXPPLr4WkHS+1oSq4rfA7DNKrqMgs1LceIy07GogtgAGhJx2b1jFsvk8rO7G0/wOC+E1SIM/8NzQkRxpRmS8y2jkJ4BaQM/6Ee4lpNocC1fsG2VhCw4=


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Handsontable

Your contributions to this project are very welcome. If you want to fix a bug or propose a new feature, you can open a new Pull Request but first make sure it follows these general rules:

1. Sign this [Contributor License Agreement](https://goo.gl/forms/yuutGuN0RjsikVpM2) to allow us to publish your changes to the code.
2. Make your changes on a separate branch. This will speed up the merging process.
3. Always make the target of your pull request the `develop` branch, not `master`.
4. Please review our [coding style](https://github.com/airbnb/javascript) for instructions on how to properly style the code.
5. Add a thorough description of all the changes.

Thank you for your commitment!


================================================
FILE: LICENSE
================================================
(The MIT License)

Copyright (c) Handsoncode sp. z o.o. <hello@handsoncode.net>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# Important information

## We permanently moved this project to the main Handsontable repository at [https://github.com/handsontable/handsontable/tree/master/wrappers/react](https://github.com/handsontable/handsontable/tree/master/wrappers/react)

## It is still available under the same name in npm: [`@handsontable/react`](https://www.npmjs.com/package/@handsontable/react), so you don't have to update your dependency configuration.

---------
<br><br><br>

<div align="center">
  
![Handsontable for React](https://raw.githubusercontent.com/handsontable/static-files/master/Images/Logo/Handsontable/handsontable-react.png)

This is the official wrapper of [**Handsontable**](//github.com/handsontable/handsontable) data grid for React.<br>
It provides data binding, data validation, filtering, sorting and more.<br>

[![npm](https://img.shields.io/npm/dt/@handsontable/react.svg)](//npmjs.com/package/@handsontable/react)
[![npm](https://img.shields.io/npm/dm/@handsontable/react.svg)](//npmjs.com/package/@handsontable/react)
[![Build status](https://travis-ci.org/handsontable/react-handsontable.png?branch=master)](//travis-ci.org/handsontable/react-handsontable)
</div>

<br>

<div align="center">
<a href="//handsontable.com/docs/frameworks-wrapper-for-react-simple-examples.html">
<img src="https://raw.githubusercontent.com/handsontable/static-files/master/Images/Screenshots/handsontable-screenshot-new.png" align="center" alt="A screenshot of a data grid for React"/>
</a>
</div>

<br>

## Installation

Use npm to install this wrapper together with Handsontable.
```
npm install handsontable @handsontable/react
```

You can load it directly from [jsDelivr](//jsdelivr.com/package/npm/@handsontable/react) as well.
```html
<script src="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@handsontable/react/dist/react-handsontable.min.js"></script>

<link href="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.css" rel="stylesheet">
```

The component will be available as `Handsontable.react.HotTable`.

## Usage

Use this data grid as you would any other component in your application. [Options](//handsontable.com/docs/Options.html) can be set as `HotTable` props.

**Styles**
```css
@import '~handsontable/dist/handsontable.full.css';
```

**React Component**
```js
import React from 'react';
import ReactDOM from 'react-dom';
import { HotTable } from '@handsontable/react';

class HotApp extends React.Component {
  constructor(props) {
    super(props);
    this.data = [
      ['', 'Tesla', 'Mercedes', 'Toyota', 'Volvo'],
      ['2019', 10, 11, 12, 13],
      ['2020', 20, 11, 14, 13],
      ['2021', 30, 15, 12, 13]
    ];
  }

  render() {
    return (<HotTable data={this.data} colHeaders={true} rowHeaders={true} width="600" height="300" />);
  }
}
```

##### [See the live demo](//handsontable.com/docs/frameworks-wrapper-for-react-simple-examples.html)

## Features

A list of some of the most popular features:

- Multiple column sorting
- Non-contiguous selection
- Filtering data
- Export to file
- Validating data
- Conditional formatting
- Merging cells
- Custom cell types
- Freezing rows/columns
- Moving rows/columns
- Resizing rows/columns
- Hiding rows/columns
- Context menu
- Comments
- Auto-fill option

## Documentation

- [Developer guides](//handsontable.com/docs/react)
- [API Reference](//handsontable.com/docs/Core.html)
- [Release notes](//handsontable.com/docs/tutorial-release-notes.html)
- [Twitter](//twitter.com/handsontable) (News and updates)

## Support and contribution

We provide support for all users through [GitHub issues](//github.com/handsontable/react-handsontable/issues). If you have a commercial license then you can add a new ticket through the [contact form](//handsontable.com/contact?category=technical_support).

If you would like to contribute to this project, make sure you first read the [guide for contributors](//github.com/handsontable/react-handsontable/blob/master/CONTRIBUTING.md).

## Browser compatibility

Handsontable is compatible with modern browsers such as Chrome, Firefox, Safari, Opera, and Edge. It also supports Internet Explorer 9 to 11 but with limited performance.

## License

This wrapper is released under [the MIT license](//github.com/handsontable/react-handsontable/blob/master/LICENSE) but under the hood it uses [Handsontable](//github.com/handsontable/handsontable), which is dual-licensed. You can either use it for free in all your non-commercial projects or purchase a commercial license.

<table>
  <thead align="center">
    <tr>
      <th width="50%">Free license</th>
      <th width="50%">Paid license</th>
    </tr>    
  </thead>
  <tbody align="center">
    <tr>
      <td>For non-commercial purposes such as teaching, academic research, personal experimentation, and evaluating  on development and testing servers.</td>
      <td>For all commercial purposes</td>
    </tr>
    <tr>
      <td>All features are available</td>
      <td>All features are available</td>
    </tr>
    <tr>
      <td>Community support</td>
      <td>Dedicated support</td>
    </tr>    
    <tr>
      <td><a href="//github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf">Read the license</a></td>
      <td><a href="//handsontable.com/pricing">See plans</a></td>
    </tr>
  </tbody>
</table>

## License key

**The license key is obligatory since [Handsontable 7.0.0](//github.com/handsontable/handsontable/releases/tag/7.0.0) (released in March 2019).**

If you use Handsontable for purposes not intended toward monetary compensation such as, but not limited to, teaching, academic research, evaluation, testing and experimentation, pass a phrase `'non-commercial-and-evaluation'`, as presented below. 

You can pass it in the `settings` object: 

```js
settings: {
  data: data,
  rowHeaders: true,
  colHeaders: true,
  licenseKey: 'non-commercial-and-evaluation'
}
```

Alternatively, you can pass it to a `licenseKey` prop:

```jsx
<HotTable settings={settings} licenseKey="00000-00000-00000-00000-00000" />
```

If, on the other hand, you use Handsontable in a project that supports your commercial activity, then you must purchase the license key at [handsontable.com](//handsontable.com/pricing).

The license key is validated in an offline mode.  No connection is made to any server. [Learn more](//handsontable.com/docs/tutorial-license-key.html) about how it works.

<br>
<br>

Created by [Handsoncode](//handsoncode.net) with ❤ and ☕ in [Tricity](//en.wikipedia.org/wiki/Tricity,_Poland).


================================================
FILE: package.json
================================================
{
  "name": "@handsontable/react",
  "version": "4.0.0",
  "description": "Best Data Grid for React with Spreadsheet Look and Feel.",
  "author": "Handsoncode <hello@handsoncode.net> (https://handsoncode.net)",
  "homepage": "https://handsontable.com",
  "license": "MIT",
  "main": "./commonjs/react-handsontable.js",
  "module": "./es/react-handsontable.js",
  "jsdelivr": "./dist/react-handsontable.min.js",
  "unpkg": "./dist/react-handsontable.min.js",
  "types": "./index.d.ts",
  "keywords": [
    "handsontable",
    "component",
    "grid",
    "data",
    "table",
    "data table",
    "data grid",
    "spreadsheet",
    "sheet",
    "excel",
    "enterprise",
    "sort",
    "formulas",
    "filter",
    "search",
    "conditional",
    "formatting",
    "csv",
    "react",
    "reactjs",
    "react component",
    "react grid",
    "wrapper"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/handsontable/react-handsontable.git"
  },
  "bugs": {
    "url": "https://github.com/handsontable/react-handsontable/issues"
  },
  "devDependencies": {
    "@babel/cli": "^7.8.4",
    "@babel/core": "^7.9.0",
    "@babel/plugin-proposal-class-properties": "^7.8.3",
    "@babel/plugin-transform-runtime": "^7.9.0",
    "@babel/polyfill": "^7.8.7",
    "@babel/preset-env": "^7.9.0",
    "@babel/preset-react": "^7.9.4",
    "@babel/preset-typescript": "^7.9.0",
    "@babel/runtime": "^7.9.2",
    "@types/enzyme": "^3.1.15",
    "@types/enzyme-adapter-react-16": "^1.0.3",
    "@types/jest": "^24.0.9",
    "@types/react": "^16.9.5",
    "@types/react-dom": "^16.9.1",
    "@types/react-redux": "^7.1.7",
    "babel-core": "^7.0.0-bridge.0",
    "cpy-cli": "^3.1.1",
    "cross-env": "^5.2.0",
    "del-cli": "^3.0.1",
    "enzyme": "^3.10.0",
    "enzyme-adapter-react-16": "^1.14.0",
    "enzyme-to-json": "^3.4.0",
    "handsontable": "^8.0.0",
    "jest": "^25.1.0",
    "prop-types": "^15.7.2",
    "react": "^16.10.2",
    "react-dom": "^16.10.2",
    "react-redux": "^7.1.1",
    "redux": "^4.0.4",
    "rollup": "^1.17.0",
    "rollup-plugin-alias": "^1.5.2",
    "rollup-plugin-babel": "^4.3.3",
    "rollup-plugin-commonjs": "^10.0.1",
    "rollup-plugin-json": "^4.0.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-replace": "^2.2.0",
    "rollup-plugin-typescript2": "^0.22.1",
    "rollup-plugin-uglify": "^6.0.4",
    "typescript": "^3.5.3",
    "uglify-js": "^3.4.9"
  },
  "peerDependencies": {
    "handsontable": ">=8.0.0"
  },
  "scripts": {
    "build": "npm run delete-build && npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:min",
    "build:commonjs": "cross-env NODE_ENV=cjs rollup -c",
    "build:umd": "cross-env NODE_ENV=umd rollup -c",
    "build:es": "cross-env NODE_ENV=es rollup -c",
    "build:min": "cross-env NODE_ENV=min rollup -c",
    "delete-build": "del-cli ./es/ && del-cli ./commonjs/ && del-cli ./dist/ && del-cli ./*.d.ts",
    "publish-build": "npm publish",
    "publish-all": "npm run build && npm run publish-build && npm run delete-build",
    "test": "jest",
    "watch:commonjs": "cross-env NODE_ENV=cjs rollup -c --watch",
    "watch:es": "cross-env NODE_ENV=es rollup -c --watch"
  },
  "jest": {
    "testURL": "http://localhost/",
    "setupFiles": [
      "./test/jestsetup.ts"
    ],
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ],
    "transform": {
      "^.+\\.tsx?$": "babel-jest",
      "^.+\\.js$": "babel-jest"
    },
    "testRegex": "(/test/(.*).(test|spec)).(jsx?|tsx?)$",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "jsx",
      "json",
      "node"
    ],
    "globals": {
      "ts-jest": {
        "tsConfig": "test-tsconfig.json",
        "babelConfig": true
      }
    }
  }
}


================================================
FILE: rollup.config.js
================================================
import { createConfig } from './.config/factory';

export default createConfig();


================================================
FILE: src/baseEditorComponent.tsx
================================================
import React from 'react';
import Handsontable from 'handsontable';
import { HotEditorProps } from './types';

class BaseEditorComponent<P = {}, S = {}, SS = any> extends React.Component<P | HotEditorProps, S> implements Handsontable._editors.Base {
  name = 'BaseEditorComponent';
  instance = null;
  row = null;
  col = null;
  prop = null;
  TD = null;
  originalValue = null;
  cellProperties = null;
  state = null;
  hotInstance = null;
  hotCustomEditorInstance = null;
  hot = null;

  constructor(props) {
    super(props);

    if (props.emitEditorInstance) {
      props.emitEditorInstance(this);
    }
  }

  // BaseEditor methods:
  private _fireCallbacks(...args) {
    (Handsontable.editors.BaseEditor.prototype as any)._fireCallbacks.call(this.hotCustomEditorInstance, ...args);
  }

  beginEditing(...args) {
    return Handsontable.editors.BaseEditor.prototype.beginEditing.call(this.hotCustomEditorInstance, ...args);
  }

  cancelChanges(...args) {
    return Handsontable.editors.BaseEditor.prototype.cancelChanges.call(this.hotCustomEditorInstance, ...args);
  }

  checkEditorSection(...args) {
    return Handsontable.editors.BaseEditor.prototype.checkEditorSection.call(this.hotCustomEditorInstance, ...args);
  }

  close(...args) {
    return Handsontable.editors.BaseEditor.prototype.close.call(this.hotCustomEditorInstance, ...args);
  }

  discardEditor(...args) {
    return Handsontable.editors.BaseEditor.prototype.discardEditor.call(this.hotCustomEditorInstance, ...args);
  }

  enableFullEditMode(...args) {
    return Handsontable.editors.BaseEditor.prototype.enableFullEditMode.call(this.hotCustomEditorInstance, ...args);
  }

  extend(...args) {
    return Handsontable.editors.BaseEditor.prototype.extend.call(this.hotCustomEditorInstance, ...args);
  }

  finishEditing(...args) {
    return Handsontable.editors.BaseEditor.prototype.finishEditing.call(this.hotCustomEditorInstance, ...args);
  }

  focus(...args) {
    return Handsontable.editors.BaseEditor.prototype.focus.call(this.hotCustomEditorInstance, ...args);
  }

  getValue(...args) {
    return Handsontable.editors.BaseEditor.prototype.getValue.call(this.hotCustomEditorInstance, ...args);
  }

  init(...args) {
    return Handsontable.editors.BaseEditor.prototype.init.call(this.hotCustomEditorInstance, ...args);
  }

  isInFullEditMode(...args) {
    return Handsontable.editors.BaseEditor.prototype.isInFullEditMode.call(this.hotCustomEditorInstance, ...args);
  }

  isOpened(...args) {
    return Handsontable.editors.BaseEditor.prototype.isOpened.call(this.hotCustomEditorInstance, ...args);
  }

  isWaiting(...args) {
    return Handsontable.editors.BaseEditor.prototype.isWaiting.call(this.hotCustomEditorInstance, ...args);
  }

  open(...args) {
    return Handsontable.editors.BaseEditor.prototype.open.call(this.hotCustomEditorInstance, ...args);
  }

  prepare(row, col, prop, TD, originalValue, cellProperties) {
    this.hotInstance = cellProperties.instance;
    this.row = row;
    this.col = col;
    this.prop = prop;
    this.TD = TD;
    this.originalValue = originalValue;
    this.cellProperties = cellProperties;

    return Handsontable.editors.BaseEditor.prototype.prepare.call(this.hotCustomEditorInstance, row, col, prop, TD, originalValue, cellProperties);
  }

  saveValue(...args) {
    return Handsontable.editors.BaseEditor.prototype.saveValue.call(this.hotCustomEditorInstance, ...args);
  }

  setValue(...args) {
    return Handsontable.editors.BaseEditor.prototype.setValue.call(this.hotCustomEditorInstance, ...args);
  }

  addHook(...args) {
    return (Handsontable.editors.BaseEditor.prototype as any).addHook.call(this.hotCustomEditorInstance, ...args);
  }

  removeHooksByKey(...args) {
    return (Handsontable.editors.BaseEditor.prototype as any).removeHooksByKey.call(this.hotCustomEditorInstance, ...args);
  }

  clearHooks(...args) {
    return (Handsontable.editors.BaseEditor.prototype as any).clearHooks.call(this.hotCustomEditorInstance, ...args);
  }

  getEditedCell(...args) {
    return (Handsontable.editors.BaseEditor.prototype as any).getEditedCell.call(this.hotCustomEditorInstance, ...args);
  }

  getEditedCellsZIndex(...args) {
    return (Handsontable.editors.BaseEditor.prototype as any).getEditedCellsZIndex.call(this.hotCustomEditorInstance, ...args);
  }
}

export default BaseEditorComponent;
export { BaseEditorComponent };


================================================
FILE: src/helpers.tsx
================================================
import React from 'react';
import ReactDOM from 'react-dom';
import { HotEditorElement } from './types';

let bulkComponentContainer = null;

/**
 * Warning message for the `autoRowSize`/`autoColumnSize` compatibility check.
 */
export const AUTOSIZE_WARNING = 'Your `HotTable` configuration includes `autoRowSize`/`autoColumnSize` options, which are not compatible with ' +
  ' the component-based renderers`. Disable `autoRowSize` and `autoColumnSize` to prevent row and column misalignment.';

/**
 * Default classname given to the wrapper container.
 */
const DEFAULT_CLASSNAME = 'hot-wrapper-editor-container';

/**
 * Logs warn to the console if the `console` object is exposed.
 *
 * @param {...*} args Values which will be logged.
 */
export function warn(...args) {
  if (typeof console !== 'undefined') {
    console.warn(...args);
  }
}

/**
 * Filter out and return elements of the provided `type` from the `HotColumn` component's children.
 *
 * @param {React.ReactNode} children HotTable children array.
 * @param {String} type Either `'hot-renderer'` or `'hot-editor'`.
 * @returns {Object|null} A child (React node) or `null`, if no child of that type was found.
 */
export function getChildElementByType(children: React.ReactNode, type: string): React.ReactElement | null {
  const childrenArray: React.ReactNode[] = React.Children.toArray(children);
  const childrenCount: number = React.Children.count(children);
  let wantedChild: React.ReactNode | null = null;

  if (childrenCount !== 0) {
    if (childrenCount === 1 && (childrenArray[0] as React.ReactElement).props[type]) {
      wantedChild = childrenArray[0];

    } else {
      wantedChild = childrenArray.find((child) => {
        return (child as React.ReactElement).props[type] !== void 0;
      });
    }
  }

  return (wantedChild as React.ReactElement) || null;
}

/**
 * Get the reference to the original editor class.
 *
 * @param {React.ReactElement} editorElement React element of the editor class.
 * @returns {Function} Original class of the editor component.
 */
export function getOriginalEditorClass(editorElement: HotEditorElement) {
  if (!editorElement) {
    return null;
  }

  return editorElement.type.WrappedComponent ? editorElement.type.WrappedComponent : editorElement.type;
}

/**
 * Remove editor containers from DOM.
 *
 * @param {Document} [doc] Document to be used.
 * @param {Map} editorCache The editor cache reference.
 */
export function removeEditorContainers(doc = document): void {
  doc.querySelectorAll(`[class^="${DEFAULT_CLASSNAME}"]`).forEach((domNode) => {
    if (domNode.parentNode) {
      domNode.parentNode.removeChild(domNode);
    }
  });
}

/**
 * Create an editor portal.
 *
 * @param {Document} [doc] Document to be used.
 * @param {React.ReactElement} editorElement Editor's element.
 * @param {Map} editorCache The editor cache reference.
 * @returns {React.ReactPortal} The portal for the editor.
 */
export function createEditorPortal(doc = document, editorElement: HotEditorElement, editorCache: Map<Function, React.Component>): React.ReactPortal {
  if (editorElement === null) {
    return;
  }

  const editorContainer = doc.createElement('DIV');
  const {id, className, style} = getContainerAttributesProps(editorElement.props, false);

  if (id) {
    editorContainer.id = id;
  }

  editorContainer.className = [DEFAULT_CLASSNAME, className].join(' ');

  if (style) {
    Object.assign(editorContainer.style, style);
  }

  doc.body.appendChild(editorContainer);

  return ReactDOM.createPortal(editorElement, editorContainer);
}

/**
 * Get an editor element extended with a instance-emitting method.
 *
 * @param {React.ReactNode} children Component children.
 * @param {Map} editorCache Component's editor cache.
 * @returns {React.ReactElement} An editor element containing the additional methods.
 */
export function getExtendedEditorElement(children: React.ReactNode, editorCache: Map<Function, object>): React.ReactElement | null {
  const editorElement = getChildElementByType(children, 'hot-editor');
  const editorClass = getOriginalEditorClass(editorElement);

  if (!editorElement) {
    return null;
  }

  return React.cloneElement(editorElement, {
    emitEditorInstance: (editorInstance) => {
      editorCache.set(editorClass, editorInstance);
    },
    isEditor: true
  } as object);
}

/**
 * Create a react component and render it to an external DOM done.
 *
 * @param {React.ReactElement} rElement React element to be used as a base for the component.
 * @param {Object} props Props to be passed to the cloned element.
 * @param {Function} callback Callback to be called after the component has been mounted.
 * @param {Document} [ownerDocument] The owner document to set the portal up into.
 * @returns {{portal: React.ReactPortal, portalContainer: HTMLElement}} An object containing the portal and its container.
 */
export function createPortal(rElement: React.ReactElement, props, callback: Function, ownerDocument: Document = document): {
  portal: React.ReactPortal,
  portalContainer: HTMLElement
} {
  if (!ownerDocument) {
    ownerDocument = document;
  }

  if (!bulkComponentContainer) {
    bulkComponentContainer = ownerDocument.createDocumentFragment();
  }

  const portalContainer = ownerDocument.createElement('DIV');
  bulkComponentContainer.appendChild(portalContainer);

  const extendedRendererElement = React.cloneElement(rElement, {
    key: `${props.row}-${props.col}`,
    ...props
  });

  return {
    portal: ReactDOM.createPortal(extendedRendererElement, portalContainer, `${props.row}-${props.col}-${Math.random()}`),
    portalContainer
  };
}

/**
 * Get an object containing the `id`, `className` and `style` keys, representing the corresponding props passed to the
 * component.
 *
 * @param {Object} props Object containing the react element props.
 * @param {Boolean} randomizeId If set to `true`, the function will randomize the `id` property when no `id` was present in the `prop` object.
 * @returns An object containing the `id`, `className` and `style` keys, representing the corresponding props passed to the
 * component.
 */
export function getContainerAttributesProps(props, randomizeId: boolean = true): {id: string, className: string, style: object} {
  return {
    id: props.id || (randomizeId ? 'hot-' + Math.random().toString(36).substring(5) : void 0),
    className: props.className || '',
    style: props.style || {},
  }
}

/**
 * Add the `UNSAFE_` prefixes to the deprecated lifecycle methods for React >= 16.3.
 *
 * @param {Object} instance Instance to have the methods renamed.
 */
export function addUnsafePrefixes(instance: {
  UNSAFE_componentWillUpdate?: Function,
  componentWillUpdate: Function,
  UNSAFE_componentWillMount?: Function,
  componentWillMount: Function
}): void {
  const reactSemverArray = React.version.split('.').map((v) => parseInt(v));
  const shouldPrefix = reactSemverArray[0] >= 16 && reactSemverArray[1] >= 3;

  if (shouldPrefix) {
    instance.UNSAFE_componentWillUpdate = instance.componentWillUpdate;
    instance.componentWillUpdate = void 0;

    instance.UNSAFE_componentWillMount = instance.componentWillMount;
    instance.componentWillMount = void 0;
  }
}


================================================
FILE: src/hotColumn.tsx
================================================
import React, { ReactPortal } from 'react';
import { HotTableProps, HotColumnProps } from './types';
import {
  addUnsafePrefixes,
  createEditorPortal,
  getExtendedEditorElement
} from './helpers';
import { SettingsMapper } from './settingsMapper';
import Handsontable from 'handsontable';

class HotColumn extends React.Component<HotColumnProps, {}> {
  internalProps: string[];
  columnSettings: Handsontable.GridSettings;

  /**
   * Local editor portal cache.
   *
   * @private
   * @type {ReactPortal}
   */
  private localEditorPortal: ReactPortal = null;

  /**
   * HotColumn class constructor.
   *
   * @param {HotColumnProps} props Component props.
   * @param {*} [context] Component context.
   */
  constructor(props: HotColumnProps, context?: any) {
    super(props, context);

    addUnsafePrefixes(this);
  }

  /**
   * Get the local editor portal cache property.
   *
   * @return {ReactPortal} Local editor portal.
   */
  getLocalEditorPortal(): ReactPortal {
    return this.localEditorPortal;
  }

  /**
   * Set the local editor portal cache property.
   *
   * @param {ReactPortal} portal Local editor portal.
   */
  setLocalEditorPortal(portal): void {
    this.localEditorPortal = portal;
  }

  /**
   * Filter out all the internal properties and return an object with just the Handsontable-related props.
   *
   * @returns {Object}
   */
  getSettingsProps(): HotTableProps {
    this.internalProps = ['__componentRendererColumns', '_emitColumnSettings', '_columnIndex', '_getChildElementByType', '_getRendererWrapper',
      '_getEditorClass', '_getEditorCache', '_getOwnerDocument', 'hot-renderer', 'hot-editor', 'children'];

    return Object.keys(this.props)
      .filter(key => {
        return !this.internalProps.includes(key);
      })
      .reduce((obj, key) => {
        obj[key] = this.props[key];

        return obj;
      }, {});
  }

  /**
   * Check whether the HotColumn component contains a provided prop.
   *
   * @param {String} propName Property name.
   * @returns {Boolean}
   */
  hasProp(propName: string): boolean {
    return !!this.props[propName];
  }

  /**
   * Get the editor element for the current column.
   *
   * @returns {React.ReactElement} React editor component element.
   */
  getLocalEditorElement(): React.ReactElement | null {
    return getExtendedEditorElement(this.props.children, this.props._getEditorCache());
  }

  /**
   * Create the column settings based on the data provided to the `HotColumn` component and it's child components.
   */
  createColumnSettings(): void {
    const rendererElement: React.ReactElement = this.props._getChildElementByType(this.props.children, 'hot-renderer');
    const editorElement: React.ReactElement = this.getLocalEditorElement();

    this.columnSettings = SettingsMapper.getSettings(this.getSettingsProps());

    if (rendererElement !== null) {
      this.columnSettings.renderer = this.props._getRendererWrapper(rendererElement);
      this.props._componentRendererColumns.set(this.props._columnIndex, true);

    } else if (this.hasProp('renderer')) {
      this.columnSettings.renderer = this.props.renderer;

    } else {
      this.columnSettings.renderer = void 0;
    }

    if (editorElement !== null) {
      this.columnSettings.editor = this.props._getEditorClass(editorElement);

    } else if (this.hasProp('editor')) {
      this.columnSettings.editor = this.props.editor;

    } else {
      this.columnSettings.editor = void 0;
    }
  }

  /**
   * Create the local editor portal and its destination HTML element if needed.
   *
   * @param {React.ReactNode} [children] Children of the HotTable instance. Defaults to `this.props.children`.
   */
  createLocalEditorPortal(children = this.props.children): void {
    const editorCache = this.props._getEditorCache();
    const localEditorElement: React.ReactElement = getExtendedEditorElement(children, editorCache);

    if (localEditorElement) {
      this.setLocalEditorPortal(createEditorPortal(this.props._getOwnerDocument(), localEditorElement, editorCache));
    }
  }

  /**
   * Emit the column settings to the parent using a prop passed from the parent.
   */
  emitColumnSettings(): void {
    this.props._emitColumnSettings(this.columnSettings, this.props._columnIndex);
  }

  /*
  ---------------------------------------
  ------- React lifecycle methods -------
  ---------------------------------------
  */

  /**
   * Logic performed before the mounting of the HotColumn component.
   */
  componentWillMount(): void {
    this.createLocalEditorPortal();
  }

  /**
   * Logic performed after the mounting of the HotColumn component.
   */
  componentDidMount(): void {
    this.createColumnSettings();
    this.emitColumnSettings();
  }

  /**
   * Logic performed before the updating of the HotColumn component.
   */
  componentWillUpdate(nextProps: Readonly<HotColumnProps>, nextState: Readonly<{}>, nextContext: any): void {
    this.createLocalEditorPortal(nextProps.children);
  }

  /**
   * Logic performed after the updating of the HotColumn component.
   */
  componentDidUpdate(): void {
    this.createColumnSettings();
    this.emitColumnSettings();
  }

  /**
   * Render the portals of the editors, if there are any.
   *
   * @returns {React.ReactElement}
   */
  render(): React.ReactElement {
    return (
      <React.Fragment>
        {this.getLocalEditorPortal()}
      </React.Fragment>
    )
  }
}

export { HotColumn };


================================================
FILE: src/hotTable.tsx
================================================
import React from 'react';
import Handsontable from 'handsontable';
import { SettingsMapper } from './settingsMapper';
import { PortalManager } from './portalManager';
import { HotColumn } from './hotColumn';
import * as packageJson from '../package.json';
import {
  HotTableProps,
  HotEditorElement
} from './types';
import {
  AUTOSIZE_WARNING,
  createEditorPortal,
  createPortal,
  getChildElementByType,
  getContainerAttributesProps,
  getExtendedEditorElement,
  getOriginalEditorClass,
  addUnsafePrefixes,
  removeEditorContainers,
  warn
} from './helpers';
import PropTypes from 'prop-types';

/**
 * A Handsontable-ReactJS wrapper.
 *
 * To implement, use the `HotTable` tag with properties corresponding to Handsontable options.
 * For example:
 *
 * ```js
 * <HotTable id="hot" data={dataObject} contextMenu={true} colHeaders={true} width={600} height={300} stretchH="all" />
 *
 * // is analogous to
 * let hot = new Handsontable(document.getElementById('hot'), {
 *    data: dataObject,
 *    contextMenu: true,
 *    colHeaders: true,
 *    width: 600
 *    height: 300
 * });
 *
 * ```
 *
 * @class HotTable
 */
class HotTable extends React.Component<HotTableProps, {}> {
  /**
   * The `id` of the main Handsontable DOM element.
   *
   * @type {String}
   */
  id: string = null;
  /**
   * Reference to the Handsontable instance.
   *
   * @type {Object}
   */
  hotInstance: Handsontable = null;
  /**
   * Reference to the main Handsontable DOM element.
   *
   * @type {HTMLElement}
   */
  hotElementRef: HTMLElement = null;
  /**
   * Class name added to the component DOM element.
   *
   * @type {String}
   */
  className: string;
  /**
   * Style object passed to the component.
   *
   * @type {React.CSSProperties}
   */
  style: React.CSSProperties;

  /**
   * Array of object containing the column settings.
   *
   * @type {Array}
   */
  columnSettings: Handsontable.ColumnSettings[] = [];

  /**
   * Component used to manage the renderer portals.
   *
   * @type {React.Component}
   */
  portalManager: PortalManager = null;

  /**
   * Array containing the portals cashed to be rendered in bulk after Handsontable's render cycle.
   */
  portalCacheArray: React.ReactPortal[] = [];

  /**
   * Global editor portal cache.
   *
   * @private
   * @type {React.ReactPortal}
   */
  private globalEditorPortal: React.ReactPortal = null;

  /**
   * The rendered cells cache.
   *
   * @private
   * @type {Map}
   */
  private renderedCellCache: Map<string, HTMLTableCellElement> = new Map();

  /**
   * Editor cache.
   *
   * @private
   * @type {Map}
   */
  private editorCache: Map<Function, React.Component> = new Map();

  /**
   * Map with column indexes (or a string = 'global') as keys, and booleans as values. Each key represents a component-based editor
   * declared for the used column index, or a global one, if the key is the `global` string.
   *
   * @private
   * @type {Map}
   */
  private componentRendererColumns: Map<number | string, boolean> = new Map();

  /**
   * HotTable class constructor.
   *
   * @param {HotTableProps} props Component props.
   * @param {*} [context] Component context.
   */
  constructor(props: HotTableProps, context?: any) {
    super(props, context);

    addUnsafePrefixes(this);
  }

  /**
   * Package version getter.
   *
   * @returns The version number of the package.
   */
  static get version(): string {
    return (packageJson as any).version;
  }

  /**
   * Prop types to be checked at runtime.
   */
  static propTypes: object = {
    style: PropTypes.object,
    id: PropTypes.string,
    className: PropTypes.string
  };

  /**
   * Get the rendered table cell cache.
   *
   * @returns {Map}
   */
  getRenderedCellCache(): Map<string, HTMLTableCellElement> {
    return this.renderedCellCache;
  }

  /**
   * Get the editor cache and return it.
   *
   * @returns {Map}
   */
  getEditorCache(): Map<Function, React.Component> {
    return this.editorCache;
  }

  /**
   * Get the global editor portal property.
   *
   * @return {React.ReactPortal} The global editor portal.
   */
  getGlobalEditorPortal(): React.ReactPortal {
    return this.globalEditorPortal;
  }

  /**
   * Set the private editor portal cache property.
   *
   * @param {React.ReactPortal} portal Global editor portal.
   */
  setGlobalEditorPortal(portal: React.ReactPortal): void {
    this.globalEditorPortal = portal;
  }

  /**
   * Clear both the editor and the renderer cache.
   */
  clearCache(): void {
    const renderedCellCache = this.getRenderedCellCache();

    this.setGlobalEditorPortal(null);
    removeEditorContainers(this.getOwnerDocument());
    this.getEditorCache().clear();

    renderedCellCache.clear();

    this.componentRendererColumns.clear();
  }

  /**
   * Get the `Document` object corresponding to the main component element.
   *
   * @returns The `Document` object used by the component.
   */
  getOwnerDocument() {
    return this.hotElementRef ? this.hotElementRef.ownerDocument : document;
  }

  /**
   * Set the reference to the main Handsontable DOM element.
   *
   * @param {HTMLElement} element The main Handsontable DOM element.
   */
  private setHotElementRef(element: HTMLElement): void {
    this.hotElementRef = element;
  }

  /**
   * Return a renderer wrapper function for the provided renderer component.
   *
   * @param {React.ReactElement} rendererElement React renderer component.
   * @returns {Handsontable.renderers.Base} The Handsontable rendering function.
   */
  getRendererWrapper(rendererElement: React.ReactElement): Handsontable.renderers.Base | any {
    const hotTableComponent = this;

    return function (instance, TD, row, col, prop, value, cellProperties) {
      const renderedCellCache = hotTableComponent.getRenderedCellCache();

      if (renderedCellCache.has(`${row}-${col}`)) {
        TD.innerHTML = renderedCellCache.get(`${row}-${col}`).innerHTML;
      }

      if (TD && !TD.getAttribute('ghost-table')) {

        const {portal, portalContainer} = createPortal(rendererElement, {
          TD,
          row,
          col,
          prop,
          value,
          cellProperties,
          isRenderer: true
        }, () => {
        }, TD.ownerDocument);

        while (TD.firstChild) {
          TD.removeChild(TD.firstChild);
        }

        TD.appendChild(portalContainer);

        hotTableComponent.portalCacheArray.push(portal);
      }

      renderedCellCache.set(`${row}-${col}`, TD);

      return TD;
    };
  }

  /**
   * Create a fresh class to be used as an editor, based on the provided editor React element.
   *
   * @param {React.ReactElement} editorElement React editor component.
   * @returns {Function} A class to be passed to the Handsontable editor settings.
   */
  getEditorClass(editorElement: HotEditorElement): typeof Handsontable.editors.BaseEditor {
    const editorClass = getOriginalEditorClass(editorElement);
    const editorCache = this.getEditorCache();
    let cachedComponent: React.Component = editorCache.get(editorClass);

    return this.makeEditorClass(cachedComponent);
  }

  /**
   * Create a class to be passed to the Handsontable's settings.
   *
   * @param {React.ReactElement} editorComponent React editor component.
   * @returns {Function} A class to be passed to the Handsontable editor settings.
   */
  makeEditorClass(editorComponent: React.Component): typeof Handsontable.editors.BaseEditor {
    const customEditorClass = class CustomEditor extends Handsontable.editors.BaseEditor implements Handsontable._editors.Base {
      editorComponent: React.Component;

      constructor(hotInstance, row, col, prop, TD, cellProperties) {
        super(hotInstance, row, col, prop, TD, cellProperties);

        (editorComponent as any).hotCustomEditorInstance = this;

        this.editorComponent = editorComponent;
      }

      focus() {
      }

      getValue() {
      }

      setValue() {
      }

      open() {
      }

      close() {
      }
    } as any;

    // Fill with the rest of the BaseEditor methods
    Object.getOwnPropertyNames(Handsontable.editors.BaseEditor.prototype).forEach(propName => {
      if (propName === 'constructor') {
        return;
      }

      customEditorClass.prototype[propName] = function (...args) {
        return editorComponent[propName].call(editorComponent, ...args);
      }
    });

    return customEditorClass;
  }

  /**
   * Get the renderer element for the entire HotTable instance.
   *
   * @returns {React.ReactElement} React renderer component element.
   */
  getGlobalRendererElement(): React.ReactElement {
    const hotTableSlots: React.ReactNode = this.props.children;

    return getChildElementByType(hotTableSlots, 'hot-renderer');
  }

  /**
   * Get the editor element for the entire HotTable instance.
   *
   * @param {React.ReactNode} [children] Children of the HotTable instance. Defaults to `this.props.children`.
   * @returns {React.ReactElement} React editor component element.
   */
  getGlobalEditorElement(children: React.ReactNode = this.props.children): HotEditorElement | null {
    return getExtendedEditorElement(children, this.getEditorCache());
  }

  /**
   * Create the global editor portal and its destination HTML element if needed.
   *
   * @param {React.ReactNode} [children] Children of the HotTable instance. Defaults to `this.props.children`.
   */
  createGlobalEditorPortal(children: React.ReactNode = this.props.children): void {
    const globalEditorElement: HotEditorElement = this.getGlobalEditorElement(children);

    if (globalEditorElement) {
      this.setGlobalEditorPortal(createEditorPortal(this.getOwnerDocument() ,globalEditorElement, this.getEditorCache()))
    }
  }

  /**
   * Create a new settings object containing the column settings and global editors and renderers.
   *
   * @returns {Handsontable.GridSettings} New global set of settings for Handsontable.
   */
  createNewGlobalSettings(): Handsontable.GridSettings {
    const newSettings = SettingsMapper.getSettings(this.props);
    const globalRendererNode = this.getGlobalRendererElement();
    const globalEditorNode = this.getGlobalEditorElement();

    newSettings.columns = this.columnSettings.length ? this.columnSettings : newSettings.columns;

    if (globalEditorNode) {
      newSettings.editor = this.getEditorClass(globalEditorNode);

    } else {
      newSettings.editor = this.props.editor || (this.props.settings ? this.props.settings.editor : void 0);
    }

    if (globalRendererNode) {
      newSettings.renderer = this.getRendererWrapper(globalRendererNode);
      this.componentRendererColumns.set('global', true);

    } else {
      newSettings.renderer = this.props.renderer || (this.props.settings ? this.props.settings.renderer : void 0);
    }

    return newSettings;
  }

  /**
   * Detect if `autoRowSize` or `autoColumnSize` is defined, and if so, throw an incompatibility warning.
   *
   * @param {Handsontable.GridSettings} newGlobalSettings New global settings passed as Handsontable config.
   */
  displayAutoSizeWarning(newGlobalSettings: Handsontable.GridSettings): void {
    if (this.hotInstance.getPlugin('autoRowSize').enabled || this.hotInstance.getPlugin('autoColumnSize').enabled) {
      if (this.componentRendererColumns.size > 0) {
        warn(AUTOSIZE_WARNING);
      }
    }
  }

  /**
   * Sets the column settings based on information received from HotColumn.
   *
   * @param {HotTableProps} columnSettings Column settings object.
   * @param {Number} columnIndex Column index.
   */
  setHotColumnSettings(columnSettings: Handsontable.ColumnSettings, columnIndex: number): void {
    this.columnSettings[columnIndex] = columnSettings;
  }

  /**
   * Handsontable's `beforeRender` hook callback.
   */
  handsontableBeforeRender(): void {
    this.getRenderedCellCache().clear();
  }

  /**
   * Handsontable's `afterRender` hook callback.
   */
  handsontableAfterRender(): void {
    this.portalManager.setState(() => {
      return Object.assign({}, {
        portals: this.portalCacheArray
      });

    }, () => {
      this.portalCacheArray.length = 0;
    });
  }

  /**
   * Call the `updateSettings` method for the Handsontable instance.
   *
   * @param {Object} newSettings The settings object.
   */
  private updateHot(newSettings: Handsontable.GridSettings): void {
    this.hotInstance.updateSettings(newSettings, false);
  }

  /**
   * Set the portal manager ref.
   *
   * @param {React.ReactComponent} pmComponent The PortalManager component.
   */
  private setPortalManagerRef(pmComponent: PortalManager): void {
    this.portalManager = pmComponent;
  }

  /*
  ---------------------------------------
  ------- React lifecycle methods -------
  ---------------------------------------
  */

  /**
   * Logic performed before the mounting of the component.
   */
  componentWillMount(): void {
    this.clearCache();
    this.createGlobalEditorPortal();
  }

  /**
   * Initialize Handsontable after the component has mounted.
   */
  componentDidMount(): void {
    const hotTableComponent = this;
    const newGlobalSettings = this.createNewGlobalSettings();

    this.hotInstance = new Handsontable.Core(this.hotElementRef, newGlobalSettings);

    this.hotInstance.addHook('beforeRender', function (isForced) {
      hotTableComponent.handsontableBeforeRender();
    });

    this.hotInstance.addHook('afterRender', function () {
      hotTableComponent.handsontableAfterRender();
    });

    // `init` missing in Handsontable's type definitions.
    (this.hotInstance as any).init();

    this.displayAutoSizeWarning(newGlobalSettings);
  }

  /**
   * Logic performed before the component update.
   */
  componentWillUpdate(nextProps: Readonly<HotTableProps>, nextState: Readonly<{}>, nextContext: any): void {
    this.clearCache();
    removeEditorContainers(this.getOwnerDocument());
    this.createGlobalEditorPortal(nextProps.children);
  }

  /**
   * Logic performed after the component update.
   */
  componentDidUpdate(): void {
    const newGlobalSettings = this.createNewGlobalSettings();
    this.updateHot(newGlobalSettings);

    this.displayAutoSizeWarning(newGlobalSettings);
  }

  /**
   * Destroy the Handsontable instance when the parent component unmounts.
   */
  componentWillUnmount(): void {
    this.hotInstance.destroy();
    removeEditorContainers(this.getOwnerDocument());
  }

  /**
   * Render the component.
   */
  render(): React.ReactElement {
    const {id, className, style} = getContainerAttributesProps(this.props);
    const isHotColumn = (childNode: any) => childNode.type === HotColumn;
    let children = React.Children.toArray(this.props.children);

    // filter out anything that's not a HotColumn
    children = children.filter(function (childNode: any) {
      return isHotColumn(childNode);
    });

    // clone the HotColumn nodes and extend them with the callbacks
    let childClones = children.map((childNode: React.ReactElement, columnIndex: number) => {
      return React.cloneElement(childNode, {
        _componentRendererColumns: this.componentRendererColumns,
        _emitColumnSettings: this.setHotColumnSettings.bind(this),
        _columnIndex: columnIndex,
        _getChildElementByType: getChildElementByType.bind(this),
        _getRendererWrapper: this.getRendererWrapper.bind(this),
        _getEditorClass: this.getEditorClass.bind(this),
        _getOwnerDocument: this.getOwnerDocument.bind(this),
        _getEditorCache: this.getEditorCache.bind(this),
        children: childNode.props.children
      } as object);
    });

    // add the global editor to the list of children
    childClones.push(this.getGlobalEditorPortal());

    return (
      <React.Fragment>
        <div ref={this.setHotElementRef.bind(this)} id={id} className={className} style={style}>
          {childClones}
        </div>
        <PortalManager ref={this.setPortalManagerRef.bind(this)}></PortalManager>
      </React.Fragment>
    )
  }
}

export default HotTable;
export { HotTable };


================================================
FILE: src/index.tsx
================================================
export * from './hotColumn';
export * from './hotTable';
export * from './types';
export * from './baseEditorComponent';
export { default } from './hotTable';


================================================
FILE: src/json.d.ts
================================================
declare module "*.json" {
  const value: any;
  export default value;
}

declare module "json!*" {
  const value: any;
  export default value;
}


================================================
FILE: src/portalManager.tsx
================================================
import React from 'react';

/**
 * Component class used to manage the renderer component portals.
 */
export class PortalManager extends React.Component<{}, {portals?: React.ReactPortal[]}> {
  constructor(props) {
    super(props);

    this.state = {
      portals: []
    };
  }

  render(): React.ReactNode {
    return (
      <React.Fragment>
        {this.state.portals}
      </React.Fragment>
    )
  }
}


================================================
FILE: src/settingsMapper.ts
================================================
import Handsontable from 'handsontable';
import { HotTableProps } from './types';

export class SettingsMapper {
  /**
   * Parse component settings into Handosntable-compatible settings.
   *
   * @param {Object} properties Object containing properties from the HotTable object.
   * @returns {Object} Handsontable-compatible settings object.
   */
  static getSettings(properties: HotTableProps): Handsontable.GridSettings {
    let newSettings: Handsontable.GridSettings = {};

    if (properties.settings) {
      let settings = properties.settings;
      for (const key in settings) {
        if (settings.hasOwnProperty(key)) {
          newSettings[key] = settings[key];
        }
      }
    }

    for (const key in properties) {
      if (key !== 'settings' && key !== 'children' && properties.hasOwnProperty(key)) {
        newSettings[key] = properties[key];
      }
    }

    return newSettings;
  }
}


================================================
FILE: src/types.tsx
================================================
import Handsontable from 'handsontable';
import React from 'react';
import { ConnectedComponent } from 'react-redux';

/**
 * Type of the editor component's ReactElement.
 */
export type HotEditorElement = React.ReactElement<{}, ConnectedComponent<React.FunctionComponent, any> | any>;

/**
 * Interface for the `prop` of the HotTable component - extending the default Handsontable settings with additional,
 * component-related properties.
 */
export interface HotTableProps extends Handsontable.GridSettings {
  id?: string,
  className?: string,
  style?: React.CSSProperties,
  settings?: Handsontable.GridSettings
  children?: React.ReactNode
}

/**
 * Interface for the props of the component-based editors.
 */
export interface HotEditorProps {
  "hot-editor": any,
  id?: string,
  className?: string,
  style?: React.CSSProperties,
}

/**
 * Properties related to the HotColumn architecture.
 */
export interface HotColumnProps extends Handsontable.GridSettings {
  _componentRendererColumns?: Map<number | string, boolean>;
  _emitColumnSettings?: (columnSettings: Handsontable.GridSettings, columnIndex: number) => void;
  _columnIndex?: number,
  _getChildElementByType?: (children: React.ReactNode, type: string) => React.ReactElement;
  _getRendererWrapper?: (rendererNode: React.ReactElement) => Handsontable.renderers.Base;
  _getEditorClass?: (editorElement: React.ReactElement) => typeof Handsontable.editors.BaseEditor;
  _getEditorCache?: () => Map<Function, React.Component>;
  _getOwnerDocument?: () => Document;
  children?: React.ReactNode;
}


================================================
FILE: test/_helpers.tsx
================================================
import React from 'react';
import { HotTable } from '../src/hotTable';
import { addUnsafePrefixes } from '../src/helpers';
import { BaseEditorComponent } from '../src/baseEditorComponent';

export function sleep(delay = 100) {
  return Promise.resolve({
    then(resolve) {
      if (delay === 0) {
        setImmediate(resolve);
      } else {
        setTimeout(resolve, delay);
      }
    }
  });
}

export function mockElementDimensions(element, width, height) {
  Object.defineProperty(element, 'clientWidth', {
    value: width
  });
  Object.defineProperty(element, 'clientHeight', {
    value: height
  });

  Object.defineProperty(element, 'offsetWidth', {
    value: width
  });
  Object.defineProperty(element, 'offsetHeight', {
    value: height
  });
}

export function simulateKeyboardEvent(type, keyCode) {
  const event = document.createEvent('KeyboardEvent');
  const init = (event as any).initKeyboardEvent !== void 0 ? 'initKeyboardEvent' : 'initKeyEvent';

  event[init](type, true, true, window, false, false, false, false, keyCode, 0);

  document.activeElement.dispatchEvent(event);
}

export function simulateMouseEvent(element, type) {
  const event = document.createEvent('Events');
  event.initEvent(type, true, false);

  element.dispatchEvent(event);
}

class IndividualPropsWrapper extends React.Component<{ref?: string, id?: string}, {hotSettings?: object}> {
  hotTable: typeof HotTable;

  constructor(props) {
    super(props);

    addUnsafePrefixes(this);
  }

  componentWillMount() {
    this.setState({});
  }

  private setHotElementRef(component: typeof HotTable): void {
    this.hotTable = component;
  }

  render(): React.ReactElement {
    return (
      <div>
        <HotTable
          licenseKey="non-commercial-and-evaluation"
          ref={this.setHotElementRef.bind(this)}
          id="hot" {...this.state.hotSettings}
          autoRowSize={false}
          autoColumnSize={false}
        />
      </div>
    );
  }
}

export { IndividualPropsWrapper };

class SingleObjectWrapper extends React.Component<{ref?: string, id?: string}, {hotSettings?: object}> {
  hotTable: typeof HotTable;

  constructor(props) {
    super(props);

    addUnsafePrefixes(this);
  }

  private setHotElementRef(component: typeof HotTable): void {
    this.hotTable = component;
  }

  componentWillMount() {
    this.setState({});
  }

  render(): React.ReactElement {
    return (
      <div>
        <HotTable
          licenseKey="non-commercial-and-evaluation"
          ref={this.setHotElementRef.bind(this)}
          id="hot"
          settings={this.state.hotSettings}
          autoRowSize={false}
          autoColumnSize={false}
        />
      </div>
    );
  }
}

export { SingleObjectWrapper };

export class RendererComponent extends React.Component<any, any> {
  render(): React.ReactElement<string> {
    return (
      <>
        value: {this.props.value}
      </>
    );
  }
}

export class EditorComponent extends BaseEditorComponent<{}, {value?: any}> {
  mainElementRef: any;
  containerStyle: any;

  constructor(props) {
    super(props);

    this.mainElementRef = React.createRef();

    this.state = {
      value: ''
    };

    this.containerStyle = {
      display: 'none'
    };
  }

  getValue() {
    return this.state.value;
  }

  setValue(value, callback) {
    this.setState((state, props) => {
      return {value: value};
    }, callback);
  }

  setNewValue() {
    this.setValue('new-value', () => {
      this.finishEditing();
    })
  }

  open() {
    this.mainElementRef.current.style.display = 'block';
  }

  close() {
    this.mainElementRef.current.style.display = 'none';
  }

  render(): React.ReactElement<string> {
    return (
      <div style={this.containerStyle} ref={this.mainElementRef} id="editorComponentContainer">
        <button onClick={this.setNewValue.bind(this)}></button>
      </div>
    );
  }
}


================================================
FILE: test/autoSizeWarning.spec.tsx
================================================
import React from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  HotColumn
} from '../src/hotColumn';
import {
  mockElementDimensions,
  sleep
} from './_helpers';
import {
  AUTOSIZE_WARNING
} from '../src/helpers';
import Handsontable from 'handsontable';

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

describe('`autoRowSize`/`autoColumns` warning', () => {
  it('should recognize whether `autoRowSize` or `autoColumnSize` is enabled and throw a warning, if a global component-based renderer' +
    'is defined (using the default Handsontable settings - autoColumnSize is enabled by default)', async (done) => {
    console.warn = jasmine.createSpy('warn');

    const RendererComponent = function (props) {
      return <>test</>
    };

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 2)}
                width={300}
                height={300}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <RendererComponent hot-renderer></RendererComponent>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    expect(console.warn).toHaveBeenCalledWith(AUTOSIZE_WARNING);

    wrapper.detach();
    done();
  });

  it('should recognize whether `autoRowSize` or `autoColumnSize` is enabled and throw a warning, if a global component-based renderer' +
    'is defined', async (done) => {
    console.warn = jasmine.createSpy('warn');

    const RendererComponent = function (props) {
      return <>test</>
    };

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 2)}
                width={300}
                height={300}
                autoRowSize={false}
                autoColumnSize={true}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <RendererComponent hot-renderer></RendererComponent>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    expect(console.warn).toHaveBeenCalledWith(AUTOSIZE_WARNING);

    wrapper.detach();
    done();
  });

  it('should recognize whether `autoRowSize` or `autoColumnSize` is enabled and throw a warning, if a component-based renderer' +
    'is defined for any column (using the default Handsontable settings - autoColumnSize enabled by default)', async (done) => {
    console.warn = jasmine.createSpy('warn');

    const RendererComponent = function (props) {
      return <>test</>
    };

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HotColumn/>
        <HotColumn>
          <RendererComponent hot-renderer></RendererComponent>
        </HotColumn>
        <HotColumn/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    expect(console.warn).toHaveBeenCalledWith(AUTOSIZE_WARNING);

    wrapper.detach();
    done();
  });

  it('should recognize whether `autoRowSize` or `autoColumnSize` is enabled and throw a warning, if a component-based renderer' +
    'is defined for any column', async (done) => {
    console.warn = jasmine.createSpy('warn');

    const RendererComponent = function (props) {
      return <>test</>
    };

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                autoColumnSize={false}
                autoRowSize={true}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HotColumn/>
        <HotColumn>
          <RendererComponent hot-renderer></RendererComponent>
        </HotColumn>
        <HotColumn/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    expect(console.warn).toHaveBeenCalledWith(AUTOSIZE_WARNING);

    wrapper.detach();
    done();
  });

  it('should throw a warning, when `autoRowSize` or `autoColumnSize` is defined, and both function-based and component-based renderers are defined', async (done) => {
    console.warn = jasmine.createSpy('warn');

    const RendererComponent = function (props) {
      return <>test</>
    };

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                autoRowSize={false}
                autoColumnSize={true}
                columns={function(columnIndex) {
                  return {
                    renderer: function() {}
                  }
                }}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HotColumn/>
        <HotColumn>
          <RendererComponent hot-renderer/>
        </HotColumn>
        <HotColumn/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    expect(console.warn).toHaveBeenCalledWith(AUTOSIZE_WARNING);

    wrapper.detach();
    done();
  });

  it('should NOT throw any warnings, when `autoRowSize` or `autoColumnSize` is defined, but only global function-based renderers were defined', async (done) => {
    console.warn = jasmine.createSpy('warn');

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                autoRowSize={true}
                autoColumnSize={false}
                renderer={function() {}}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HotColumn/>
        <HotColumn/>
        <HotColumn/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    expect(console.warn).not.toHaveBeenCalled();

    wrapper.detach();
    done();
  });

  it('should NOT throw any warnings, when `autoRowSize` or `autoColumnSize` is defined, but only function-based renderers were defined for columns', async (done) => {
    console.warn = jasmine.createSpy('warn');

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                autoRowSize={true}
                autoColumnSize={true}
                columns={[{renderer: function() {}}]}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HotColumn/>
        <HotColumn/>
        <HotColumn/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    expect(console.warn).not.toHaveBeenCalled();

    wrapper.detach();
    done();
  });

  it('should NOT throw any warnings, when `autoRowSize` or `autoColumnSize` is defined, but only function-based renderers were defined for columns, when ' +
    'the `columns` option is defined as a function', async (done) => {
    console.warn = jasmine.createSpy('warn');

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                autoRowSize={false}
                autoColumnSize={true}
                columns={function(columnIndex) {
                  return {
                    renderer: function() {}
                  }
                }}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HotColumn/>
        <HotColumn/>
        <HotColumn/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    expect(console.warn).not.toHaveBeenCalled();

    wrapper.detach();
    done();
  });
});


================================================
FILE: test/componentInternals.spec.tsx
================================================
import React from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  HotColumn
} from '../src/hotColumn';
import {
  mockElementDimensions,
  sleep,
  RendererComponent,
  EditorComponent
} from './_helpers';
import { BaseEditorComponent } from '../src/baseEditorComponent';
import Handsontable from 'handsontable';

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

describe('Subcomponent state', () => {
  it('should be possible to set the state of the renderer components passed to HotTable and HotColumn', async (done) => {
    class RendererComponent2 extends React.Component<any, any, any> {
      constructor(props) {
        super(props);

        this.state = {
          value: 'initial'
        }
      }

      render(): React.ReactElement<string> {
        return (
          <>
            {this.state.value}
          </>
        );
      }
    }

    let zeroRendererInstance = null;
    let oneRendererInstance = null;
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 2)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                autoRowSize={false}
                autoColumnSize={false}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <RendererComponent2 ref={function (instance) {
          if (instance && instance.props.row === 0 && instance.props.col === 0) {
            zeroRendererInstance = instance;
          }
        }} hot-renderer></RendererComponent2>
        <HotColumn/>
        <HotColumn>
          <RendererComponent2 ref={function (instance) {
            if (instance && instance.props.row === 0 && instance.props.col === 1) {
              oneRendererInstance = instance;
            }
          }} hot-renderer></RendererComponent2>
        </HotColumn>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotTableInstance = wrapper.instance();
    const hotInstance = hotTableInstance.hotInstance;

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>initial</div>');
    expect(hotInstance.getCell(0, 1).innerHTML).toEqual('<div>initial</div>');

    zeroRendererInstance.setState({
      value: 'altered'
    });

    oneRendererInstance.setState({
      value: 'altered as well'
    });

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>altered</div>');
    expect(hotInstance.getCell(0, 1).innerHTML).toEqual('<div>altered as well</div>');

    wrapper.detach();
    done();
  });

  it('should be possible to set the state of the editor components passed to HotTable and HotColumn', async (done) => {
    class RendererEditor2 extends BaseEditorComponent {
      constructor(props) {
        super(props);

        this.state = {
          value: 'initial'
        }
      }

      render(): React.ReactElement<string> {
        return (
          <div id={this.props.editorId}>
            {this.state.value}
          </div>
        );
      }
    }

    let globalEditorInstance = null;
    let columnEditorInstance = null;
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 2)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <RendererEditor2 editorId={'first-editor'} ref={function (instance) {
          globalEditorInstance = instance;
        }} hot-editor></RendererEditor2>
        <HotColumn/>
        <HotColumn>
          <RendererEditor2 editorId={'second-editor'} ref={function (instance) {
            columnEditorInstance = instance;
          }} hot-editor></RendererEditor2>
        </HotColumn>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotTableInstance = wrapper.instance();
    const hotInstance = hotTableInstance.hotInstance;

    expect(document.querySelector('#first-editor').innerHTML).toEqual('initial');
    expect(document.querySelector('#second-editor').innerHTML).toEqual('initial');

    globalEditorInstance.setState({
      value: 'altered'
    });

    columnEditorInstance.setState({
      value: 'altered as well'
    });

    expect(document.querySelector('#first-editor').innerHTML).toEqual('altered');
    expect(document.querySelector('#second-editor').innerHTML).toEqual('altered as well');

    wrapper.detach();
    done();
  });
});

describe('Component lifecyle', () => {
  it('renderer components should trigger their lifecycle methods', async (done) => {
    class RendererComponent2 extends React.Component<any, any, any> {
      constructor(props) {
        super(props);

        rendererCounters.set(`${this.props.row}-${this.props.col}`, {
          willMount: 0,
          didMount: 0,
          willUnmount: 0
        });
      }

      UNSAFE_componentWillMount(): void {
        const counters = rendererCounters.get(`${this.props.row}-${this.props.col}`);
        counters.willMount++;
      }

      componentDidMount(): void {
        const counters = rendererCounters.get(`${this.props.row}-${this.props.col}`);
        counters.didMount++;
      }

      componentWillUnmount(): void {
        const counters = rendererCounters.get(`${this.props.row}-${this.props.col}`);
        counters.willUnmount++;
      }

      render(): React.ReactElement<string> {
        return (
          <>
            test
          </>
        );
      }
    }

    let secondGo = false;
    const rendererRefs = new Map();
    const rendererCounters = new Map();
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 2)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                autoRowSize={false}
                autoColumnSize={false}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <RendererComponent2 ref={function (instance) {
          if (!secondGo && instance) {
            rendererRefs.set(`${instance.props.row}-${instance.props.col}`, instance);
          }
        }} hot-renderer></RendererComponent2>
        <HotColumn/>
        <HotColumn>
          <RendererComponent2 ref={function (instance) {
            if (!secondGo && instance) {
              rendererRefs.set(`${instance.props.row}-${instance.props.col}`, instance);
            }
          }} hot-renderer></RendererComponent2>
        </HotColumn>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotTableInstance = wrapper.instance();
    const hotInstance = hotTableInstance.hotInstance;

    rendererCounters.forEach((counters) => {
      expect(counters.willMount).toEqual(1);
      expect(counters.didMount).toEqual(1);
      expect(counters.willUnmount).toEqual(0);
    });

    secondGo = true;

    hotInstance.render();
    await sleep(300);

    rendererCounters.forEach((counters) => {
      expect(counters.willMount).toEqual(1);
      expect(counters.didMount).toEqual(1);
      expect(counters.willUnmount).toEqual(1);
    });

    wrapper.detach();
    done();
  });

  it('editor components should trigger their lifecycle methods', async (done) => {
    class EditorComponent2 extends BaseEditorComponent {
      constructor(props) {
        super(props);

        editorCounters.set(`${this.props.row}-${this.props.col}`, {
          willMount: 0,
          didMount: 0,
          willUnmount: 0
        });
      }

      UNSAFE_componentWillMount(): void {
        const counters = editorCounters.get(`${this.props.row}-${this.props.col}`);
        counters.willMount++;
      }

      componentDidMount(): void {
        const counters = editorCounters.get(`${this.props.row}-${this.props.col}`);
        counters.didMount++;
      }

      componentWillUnmount(): void {
        const counters = editorCounters.get(`${this.props.row}-${this.props.col}`);
        counters.willUnmount++;
      }

      render(): React.ReactElement<string> {
        return (
          <>
            test
          </>
        );
      }
    }

    let secondGo = false;
    const editorRefs = new Map();
    const editorCounters = new Map();
    const childrenArray = [
      <EditorComponent2 ref={function (instance) {
        if (!secondGo && instance) {
          editorRefs.set(`EditorComponent2`, instance);
        }
      }} hot-editor key={Math.random()}></EditorComponent2>
    ];
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 2)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        {childrenArray}
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotTableInstance = wrapper.instance();

    editorCounters.forEach((counters) => {
      expect(counters.willMount).toEqual(1);
      expect(counters.didMount).toEqual(1);
      expect(counters.willUnmount).toEqual(0);
    });

    secondGo = true;

    childrenArray.length = 0;
    hotTableInstance.forceUpdate();
    await sleep(100);

    editorCounters.forEach((counters) => {
      expect(counters.willMount).toEqual(1);
      expect(counters.didMount).toEqual(1);
      expect(counters.willUnmount).toEqual(1);
    });

    wrapper.detach();
    done();
  });
});


================================================
FILE: test/hotColumn.spec.tsx
================================================
import React from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import Handsontable from 'handsontable';
import { HotTable } from '../src/hotTable';
import { HotColumn } from '../src/hotColumn';
import {
  RendererComponent,
  mockElementDimensions,
  sleep,
  EditorComponent,
  simulateKeyboardEvent,
  simulateMouseEvent
} from './_helpers';

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

describe('Passing column settings using HotColumn', () => {
  it('should apply the Handsontable settings passed as HotColumn arguments to the Handsontable instance', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable
        licenseKey="non-commercial-and-evaluation"
        id="test-hot" data={[[2]]}
        readOnly={false}
      >
        <HotColumn title="test title"></HotColumn>
        <HotColumn readOnly={true}></HotColumn>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);

    let hotInstance = wrapper.instance().hotInstance;

    expect(hotInstance.getSettings().columns[0].title).toEqual('test title');
    expect(hotInstance.getCellMeta(0, 0).readOnly).toEqual(false);

    expect(hotInstance.getSettings().columns[1].title).toEqual(void 0);
    expect(hotInstance.getCellMeta(0, 1).readOnly).toEqual(true);

    expect(hotInstance.getSettings().licenseKey).toEqual('non-commercial-and-evaluation');

    wrapper.detach();

    done();
  });
});

describe('Renderer configuration using React components', () => {
  it('should use the renderer component as Handsontable renderer, when it\'s nested under HotColumn and assigned the \'hot-renderer\' attribute', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(100, 2)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                autoRowSize={false}
                autoColumnSize={false}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HotColumn/>
        <HotColumn>
          <RendererComponent hot-renderer></RendererComponent>
        </HotColumn>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);

    let hotInstance = wrapper.instance().hotInstance;

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('A1');
    expect(hotInstance.getCell(0, 1).innerHTML).toEqual('<div>value: B1</div>');

    hotInstance.scrollViewportTo(99, 0);
    hotInstance.render();

    await sleep(300);

    expect(hotInstance.getCell(99, 0).innerHTML).toEqual('A100');
    expect(hotInstance.getCell(99, 1).innerHTML).toEqual('<div>value: B100</div>');

    wrapper.detach();

    done();
  });
});

describe('Editor configuration using React components', () => {
  it('should use the editor component as Handsontable editor, when it\'s nested under HotTable and assigned the \'hot-editor\' attribute', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 2)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HotColumn/>
        <HotColumn>
          <EditorComponent hot-editor></EditorComponent>
        </HotColumn>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotInstance = wrapper.instance().hotInstance;

    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('none');

    hotInstance.selectCell(0, 1);
    simulateKeyboardEvent('keydown', 13);

    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('block');

    expect(hotInstance.getDataAtCell(0, 1)).toEqual('B1');

    simulateMouseEvent(document.querySelector('#editorComponentContainer button'), 'click');

    expect(hotInstance.getDataAtCell(0, 1)).toEqual('new-value');

    hotInstance.getActiveEditor().close();

    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('none');

    hotInstance.selectCell(0, 0);
    simulateKeyboardEvent('keydown', 13);

    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('none');

    wrapper.detach();

    done();
  });
});

describe('Dynamic HotColumn configuration changes', () => {
  it('should be possible to rearrange and change the column + editor + renderer configuration dynamically', async (done) => {
    function RendererComponent2(props) {
      return (
        <>r2: {props.value}</>
      );
    }

    class WrapperComponent extends React.Component<any, any> {
      constructor(props) {
        super(props);

        this.state = {
          setup: [
            <RendererComponent hot-renderer key={'1'}/>,
            <HotColumn title="test title" className="first-column-class-name" key={'2'}>
              <EditorComponent className="editor-className-1" id="editor-id-1" style={{background: 'red'}} hot-editor/>
            </HotColumn>,
            <HotColumn title="test title 2" key={'3'}>
              <RendererComponent2 hot-renderer></RendererComponent2>
            </HotColumn>
          ]
        }
      }

      render() {
        return (
          <HotTable licenseKey="non-commercial-and-evaluation" id="test-hot"
                    data={Handsontable.helper.createSpreadsheetData(3, 2)}
                    width={300}
                    height={300}
                    rowHeights={23}
                    colWidths={50}
                    readOnly={false}
                    autoRowSize={false}
                    autoColumnSize={false}
                    init={function () {
                      mockElementDimensions(this.rootElement, 300, 300);
                    }}
                    ref={hotTableInstanceRef}>
            {this.state.setup}
          </HotTable>
        );
      };
    }

    let hotTableInstanceRef = React.createRef();

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <WrapperComponent/>
      , {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);

    let hotInstance = (hotTableInstanceRef.current as any).hotInstance;
    let editorElement = document.querySelector('#editorComponentContainer');

    expect(hotInstance.getSettings().columns[0].title).toEqual('test title');
    expect(hotInstance.getSettings().columns[0].className).toEqual('first-column-class-name');
    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>value: A1</div>');
    expect(hotInstance.getCell(1, 0).innerHTML).toEqual('<div>value: A2</div>');
    hotInstance.selectCell(0, 0);
    hotInstance.getActiveEditor().open();
    expect(hotInstance.getActiveEditor().constructor.name).toEqual('CustomEditor');
    expect(hotInstance.getActiveEditor().editorComponent.__proto__.constructor.name).toEqual('EditorComponent');
    expect(editorElement.style.display).toEqual('block');
    expect(editorElement.parentNode.style.background).toEqual('red');
    expect(editorElement.parentNode.id).toEqual('editor-id-1');
    expect(editorElement.parentNode.className.includes('editor-className-1')).toBe(true);

    hotInstance.getActiveEditor().close();

    expect(hotInstance.getSettings().columns[1].title).toEqual('test title 2');
    expect(hotInstance.getSettings().columns[1].className).toEqual(void 0);
    expect(hotInstance.getCell(0, 1).innerHTML).toEqual('<div>r2: B1</div>');
    expect(hotInstance.getCell(1, 1).innerHTML).toEqual('<div>r2: B2</div>');
    hotInstance.selectCell(0, 1);
    expect(hotInstance.getActiveEditor().constructor.name).toEqual('TextEditor');
    expect(hotInstance.getActiveEditor().editorComponent).toEqual(void 0);
    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('none');

    wrapper.instance().setState({
      setup: [
        <EditorComponent className="editor-className-2" id="editor-id-2" style={{background: 'blue'}} hot-editor key={'1'}/>,
        <HotColumn title="test title 2" key={'2'}>
          <RendererComponent2 hot-renderer></RendererComponent2>
        </HotColumn>,
        <HotColumn title="test title" className="first-column-class-name" key={'3'}>
          <RendererComponent hot-renderer/>
        </HotColumn>
      ]
    });

    await sleep(100);

    editorElement = document.querySelector('#editorComponentContainer');

    expect(hotInstance.getSettings().columns[0].title).toEqual('test title 2');
    expect(hotInstance.getSettings().columns[0].className).toEqual(void 0);
    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>r2: A1</div>');
    expect(hotInstance.getCell(1, 0).innerHTML).toEqual('<div>r2: A2</div>');
    hotInstance.selectCell(0, 0);
    hotInstance.getActiveEditor().open();
    expect(hotInstance.getActiveEditor().constructor.name).toEqual('CustomEditor');
    expect(hotInstance.getActiveEditor().editorComponent.__proto__.constructor.name).toEqual('EditorComponent');
    expect(editorElement.style.display).toEqual('block');
    expect(editorElement.parentNode.style.background).toEqual('blue');
    expect(editorElement.parentNode.id).toEqual('editor-id-2');
    expect(editorElement.parentNode.className.includes('editor-className-2')).toBe(true);
    hotInstance.getActiveEditor().close();

    expect(hotInstance.getSettings().columns[1].title).toEqual('test title');
    expect(hotInstance.getSettings().columns[1].className).toEqual('first-column-class-name');
    expect(hotInstance.getCell(0, 1).innerHTML).toEqual('<div>value: B1</div>');
    expect(hotInstance.getCell(1, 1).innerHTML).toEqual('<div>value: B2</div>');
    hotInstance.selectCell(0, 1);
    hotInstance.getActiveEditor().open();
    expect(hotInstance.getActiveEditor().constructor.name).toEqual('CustomEditor');
    expect(hotInstance.getActiveEditor().editorComponent.__proto__.constructor.name).toEqual('EditorComponent');
    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('block');
    hotInstance.getActiveEditor().close();

    expect(hotInstance.getSettings().licenseKey).toEqual('non-commercial-and-evaluation');

    wrapper.detach();

    done();
  });
});


================================================
FILE: test/hotTable.spec.tsx
================================================
import React from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  IndividualPropsWrapper,
  mockElementDimensions,
  RendererComponent,
  EditorComponent,
  SingleObjectWrapper,
  sleep,
  simulateKeyboardEvent,
  simulateMouseEvent
} from './_helpers';
import Handsontable from 'handsontable';

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

describe('Handsontable initialization', () => {
  it('should render Handsontable when using the HotTable component', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable
        id="test-hot"
        data={[[2]]}
        licenseKey="non-commercial-and-evaluation"
      />, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);

    let hotInstance = wrapper.instance().hotInstance;

    expect(hotInstance).not.toBe(null);
    expect(hotInstance).not.toBe(void 0);

    expect(hotInstance.rootElement.id).toEqual('test-hot');

    wrapper.detach();

    done();
  });

  it('should pass the provided properties to the Handsontable instance', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable
        id="test-hot"
        contextMenu={true}
        rowHeaders={true}
        colHeaders={true}
        data={[[2]]}
        licenseKey="non-commercial-and-evaluation"/>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);
    let hotInstance = wrapper.instance().hotInstance;

    expect(hotInstance.getPlugin('contextMenu').enabled).toBe(true);
    expect(hotInstance.getSettings().rowHeaders).toBe(true);
    expect(hotInstance.getSettings().colHeaders).toBe(true);
    expect(JSON.stringify(hotInstance.getData())).toEqual('[[2]]');
    wrapper.detach();

    done();
  });
});

describe('Updating the Handsontable settings', () => {
  it('should call the updateSettings method of Handsontable, when the component properties get updated (when providing properties individually)', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof IndividualPropsWrapper> = mount(
      <IndividualPropsWrapper/>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);
    const hotInstance = wrapper.instance().hotTable.hotInstance;

    let updateSettingsCount = 0;

    hotInstance.addHook('afterUpdateSettings', () => {
      updateSettingsCount++;
    });

    await sleep(300);
    wrapper.instance().setState({hotSettings: {data: [[2]], contextMenu: true, readOnly: true}});

    expect(updateSettingsCount).toEqual(1);
    wrapper.detach();
    done();
  });

  it('should call the updateSettings method of Handsontable, when the component properties get updated (when providing properties as a single settings object)', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof SingleObjectWrapper> = mount(
      <SingleObjectWrapper/>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);

    const hotInstance = wrapper.instance().hotTable.hotInstance;
    let updateSettingsCount = 0;

    hotInstance.addHook('afterUpdateSettings', () => {
      updateSettingsCount++;
    });

    await sleep(300);
    wrapper.instance().setState({hotSettings: {data: [[2]], contextMenu: true, readOnly: true}});

    expect(updateSettingsCount).toEqual(1);
    wrapper.detach();
    done();
  });

  it('should update the Handsontable options, when the component properties get updated (when providing properties individually)', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof IndividualPropsWrapper> = mount(
      <IndividualPropsWrapper/>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);
    const hotInstance = wrapper.instance().hotTable.hotInstance;

    expect(hotInstance.getSettings().contextMenu).toEqual(void 0);
    expect(hotInstance.getSettings().readOnly).toEqual(false);
    expect(JSON.stringify(hotInstance.getSettings().data)).toEqual('[[null,null,null,null,null],[null,null,null,null,null],[null,null,null,null,null],[null,null,null,null,null],[null,null,null,null,null]]');

    await sleep(300);
    wrapper.instance().setState({hotSettings: {data: [[2]], contextMenu: true, readOnly: true}});

    expect(hotInstance.getSettings().contextMenu).toBe(true);
    expect(hotInstance.getSettings().readOnly).toBe(true);
    expect(JSON.stringify(hotInstance.getSettings().data)).toEqual('[[2]]');
    wrapper.detach();

    done();

  });

  it('should update the Handsontable options, when the component properties get updated (when providing properties as a single settings object)', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof SingleObjectWrapper> = mount(
      <SingleObjectWrapper/>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(300);
    const hotInstance = wrapper.instance().hotTable.hotInstance;

    expect(hotInstance.getSettings().contextMenu).toEqual(void 0);
    expect(hotInstance.getSettings().readOnly).toEqual(false);
    expect(JSON.stringify(hotInstance.getSettings().data)).toEqual('[[null,null,null,null,null],[null,null,null,null,null],[null,null,null,null,null],[null,null,null,null,null],[null,null,null,null,null]]');

    await sleep(300);
    wrapper.instance().setState({hotSettings: {data: [[2]], contextMenu: true, readOnly: true}});


    expect(hotInstance.getSettings().contextMenu).toBe(true);
    expect(hotInstance.getSettings().readOnly).toBe(true);
    expect(JSON.stringify(hotInstance.getSettings().data)).toEqual('[[2]]');
    wrapper.detach();

    done();
  });
});

describe('Renderer configuration using React components', () => {
  it('should use the renderer component as Handsontable renderer, when it\'s nested under HotTable and assigned the \'hot-renderer\' attribute', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(100, 100)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                autoRowSize={false}
                autoColumnSize={false}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <RendererComponent hot-renderer></RendererComponent>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    let hotInstance = wrapper.instance().hotInstance;

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>value: A1</div>');

    hotInstance.scrollViewportTo(99, 0);
    // For some reason it needs another render
    hotInstance.render();
    await sleep(100);

    expect(hotInstance.getCell(99, 1).innerHTML).toEqual('<div>value: B100</div>');

    hotInstance.scrollViewportTo(99, 99);
    hotInstance.render();
    await sleep(100);

    expect(hotInstance.getCell(99, 99).innerHTML).toEqual('<div>value: CV100</div>');

    wrapper.detach();

    done();
  });
});

describe('Editor configuration using React components', () => {
  it('should use the editor component as Handsontable editor, when it\'s nested under HotTable and assigned the \'hot-editor\' attribute', async (done) => {
    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <EditorComponent hot-editor></EditorComponent>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotInstance = wrapper.instance().hotInstance;

    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('none');

    hotInstance.selectCell(0,0);
    simulateKeyboardEvent('keydown', 13);

    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('block');

    expect(hotInstance.getDataAtCell(0,0)).toEqual('A1');

    simulateMouseEvent(document.querySelector('#editorComponentContainer button'), 'click');

    expect(hotInstance.getDataAtCell(0,0)).toEqual('new-value');

    hotInstance.getActiveEditor().close();

    expect((document.querySelector('#editorComponentContainer') as any).style.display).toEqual('none');

    done();
  });
});


================================================
FILE: test/jestsetup.ts
================================================
import {
  configure
} from 'enzyme';
import ReactSixteenAdapter from 'enzyme-adapter-react-16';

configure({adapter: new ReactSixteenAdapter()});


================================================
FILE: test/reactContext.spec.tsx
================================================
import React from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  HotColumn
} from '../src/hotColumn';
import {
  mockElementDimensions,
  sleep
} from './_helpers';
import { BaseEditorComponent } from '../src/baseEditorComponent';
import Handsontable from 'handsontable';

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

describe('React Context', () => {
  it('should be possible to declare a context and use it inside both renderers and editors', async (done) => {
    let hotTableInstance = null;
    const TestContext = React.createContext('def-test-val');

    function RendererComponent2() {
      return (
        <TestContext.Consumer>
          {(context) => <>{context}</>}
        </TestContext.Consumer>
      );
    }

    class EditorComponent2 extends BaseEditorComponent {
      render(): React.ReactElement<string> {
        return (
            <TestContext.Consumer>
              {(context) => <>{context}</>}
            </TestContext.Consumer>
        );
      }
    }

    class RendererComponent3 extends React.Component {
      render() {
        return (
          <>
            {this.context}
          </>
        )
      }
    }
    RendererComponent3.contextType = TestContext;

    class EditorComponent3 extends React.Component {
      render() {
        return (
          <>
            {this.context}
          </>
        )
      }
    }
    EditorComponent3.contextType = TestContext;

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <TestContext.Provider value={'testContextValue'}>
        <HotTable licenseKey="non-commercial-and-evaluation"
                  id="test-hot"
                  data={Handsontable.helper.createSpreadsheetData(3, 2)}
                  width={300}
                  height={300}
                  rowHeights={23}
                  colWidths={50}
                  autoRowSize={false}
                  autoColumnSize={false}
                  init={function () {
                    mockElementDimensions(this.rootElement, 300, 300);
                  }}
                  ref={function (instance) {
                    hotTableInstance = instance;
                  }}>
          <HotColumn>
            <RendererComponent2 hot-renderer/>
            <EditorComponent2 hot-editor className="ec2"/>
          </HotColumn>
          <HotColumn>
            <RendererComponent3 hot-renderer/>
            <EditorComponent3 hot-editor className="ec3"/>
          </HotColumn>
        </HotTable>
      </TestContext.Provider>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotInstance = hotTableInstance.hotInstance;

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>testContextValue</div>');
    expect(hotInstance.getCell(1, 0).innerHTML).toEqual('<div>testContextValue</div>');

    expect(document.querySelector('.ec2').innerHTML).toEqual('testContextValue');

    expect(hotInstance.getCell(0, 1).innerHTML).toEqual('<div>testContextValue</div>');
    expect(hotInstance.getCell(1, 1).innerHTML).toEqual('<div>testContextValue</div>');

    expect(document.querySelector('.ec3').innerHTML).toEqual('testContextValue');

    wrapper.detach();
    done();
  });
});


================================================
FILE: test/reactHooks.spec.tsx
================================================
import React, { useState } from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  mockElementDimensions,
  sleep,
  simulateMouseEvent
} from './_helpers';
import Handsontable from 'handsontable';


beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

describe('Using hooks within HotTable renderers', () => {
  it('should be possible to use hook-enabled components as renderers', async (done) => {
    function HookEnabledRenderer(props) {
      const [count, setCount] = useState(0);

      return (
        <div className={'hook-enabled-renderer-container'}>
          <p>{props.value}</p>: <span>{count}</span>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }

    const wrapper: ReactWrapper<{}, {}, typeof HotTable> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                autoRowSize={false}
                autoColumnSize={false}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <HookEnabledRenderer hot-renderer></HookEnabledRenderer>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotInstance = wrapper.instance().hotInstance;

    expect(hotInstance.getCell(0,0).querySelectorAll('.hook-enabled-renderer-container').length).toEqual(1);
    expect(hotInstance.getCell(1,1).querySelectorAll('.hook-enabled-renderer-container').length).toEqual(1);

    simulateMouseEvent(hotInstance.getCell(0,0).querySelector('button'), 'click');
    simulateMouseEvent(hotInstance.getCell(0,0).querySelector('button'), 'click');
    simulateMouseEvent(hotInstance.getCell(0,0).querySelector('button'), 'click');

    expect(hotInstance.getCell(0,0).querySelector('span').innerHTML).toEqual('3');
    expect(hotInstance.getCell(1,1).querySelector('span').innerHTML).toEqual('0');

    wrapper.detach();
    done();
  });
});

/*
 Editor components cannot be used with React Hooks, as they need to be classes derived from BaseEditorComponent.
 */


================================================
FILE: test/reactLazy.spec.tsx
================================================
import React, { Suspense, lazy } from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  mockElementDimensions,
  sleep,
} from './_helpers';
import Handsontable from 'handsontable';

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

describe('React.lazy', () => {
  it('should be possible to lazy-load components and utilize Suspend', async (done) => {
    function RendererComponent2(props) {
      return (
        <>
          lazy value: {props.value}
        </>
      );
    }

    let promiseResolve = null;

    function SuspendedRenderer(props) {
      const customImportPromise = new Promise(function (resolve, reject) {
          promiseResolve = resolve;
        }
      ) as any;

      const LazierRenderer = lazy(() => customImportPromise);

      return (
        <Suspense fallback={<>loading-message</>}>
          <LazierRenderer {...props}></LazierRenderer>
        </Suspense>
      )
    }

    const wrapper: ReactWrapper<{}, {}, any> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(1, 1)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                autoRowSize={false}
                autoColumnSize={false}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <SuspendedRenderer hot-renderer/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotTableInstance = wrapper.instance();
    const hotInstance = hotTableInstance.hotInstance;

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>loading-message</div>');

    promiseResolve({
      default: RendererComponent2,
      __esModule: true
    });

    await sleep(40);

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>lazy value: A1</div>');

    wrapper.detach();

    done();
  });
});


================================================
FILE: test/reactMemo.spec.tsx
================================================
import React from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  mockElementDimensions,
  sleep,
} from './_helpers';
import Handsontable from 'handsontable';

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

/**
 * Worth noting, that although it's possible to use React.memo on renderer components, it doesn't do much, as currently they're recreated on every
 * Handsontable's `render`.
 */
describe('React.memo', () => {
  it('should be possible to use React.memo on renderer components.', async (done) => {
    function RendererComponent2 (props) {
      return (
        <>
        value: {props.value}
        </>
      );
    }

    const MemoizedRendererComponent2 = React.memo(RendererComponent2);

    const wrapper: ReactWrapper<{}, {}, any> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(1, 1)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                autoRowSize={false}
                autoColumnSize={false}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <MemoizedRendererComponent2 hot-renderer/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotTableInstance = wrapper.instance();
    const hotInstance = hotTableInstance.hotInstance;

    hotInstance.render();

    await sleep(100);

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>value: A1</div>');

    wrapper.detach();

    done();
  });

  /*
    Editors cannot use React.memo, as they're derived from the BaseEditorComponent class, thus not being function components.
   */
});


================================================
FILE: test/reactPureComponent.spec.tsx
================================================
import React from 'react';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  mockElementDimensions,
  sleep,
} from './_helpers';
import Handsontable from 'handsontable';

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);
});

/**
 * Worth noting, that although it's possible to use React's Pure Components on renderer components, it doesn't do much, as currently they're recreated on every
 * Handsontable's `render`.
 */
describe('React PureComponents', () => {
  it('should be possible to declare the renderer as PureComponent', async (done) => {
    class RendererComponent2 extends React.PureComponent<any, any> {
      render(): React.ReactElement<string> {
        return (
          <>
            value: {this.props.value}
          </>
        );
      }
    }

    const wrapper: ReactWrapper<{}, {}, any> = mount(
      <HotTable licenseKey="non-commercial-and-evaluation"
                id="test-hot"
                data={Handsontable.helper.createSpreadsheetData(3, 3)}
                width={300}
                height={300}
                rowHeights={23}
                colWidths={50}
                autoRowSize={false}
                autoColumnSize={false}
                init={function () {
                  mockElementDimensions(this.rootElement, 300, 300);
                }}>
        <RendererComponent2 hot-renderer/>
      </HotTable>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    const hotTableInstance = wrapper.instance();
    const hotInstance = hotTableInstance.hotInstance;

    expect(hotInstance.getCell(0, 0).innerHTML).toEqual('<div>value: A1</div>');

    wrapper.detach();

    done();
  });

  /*
    Editors cannot be declared as PureComponents, as they're derived from the BaseEditorComponent class.
   */
});


================================================
FILE: test/redux.spec.tsx
================================================
import React from 'react';
import { createStore, combineReducers } from 'redux';
import { Provider, connect } from 'react-redux';
import {
  mount,
  ReactWrapper
} from 'enzyme';
import {
  HotTable
} from '../src/hotTable';
import {
  mockElementDimensions,
  sleep,
  RendererComponent,
  EditorComponent
} from './_helpers';
import Handsontable from 'handsontable';

const initialReduxStoreState = {
  hexColor: '#fff'
};

const appReducer = (state = initialReduxStoreState, action) => {
  switch (action.type) {
    case 'updateColor':
      const newColor = action.hexColor;

      return Object.assign({}, state, {
        hexColor: newColor
      });
    default:
      return state;
  }
};
const actionReducers = combineReducers({appReducer});
const reduxStore = createStore(actionReducers);

beforeEach(() => {
  let container = document.createElement('DIV');
  container.id = 'hotContainer';
  document.body.appendChild(container);

  reduxStore.dispatch({
    type: 'updateColor',
    hexColor: '#fff'
  });
});

describe('Using Redux store within HotTable renderers and editors', () => {
  it('should be possible to use redux-enabled components as renderers', async (done) => {
    // let reduxStore = mockStore(initialReduxStoreState);

    const ReduxEnabledRenderer = connect(function (state: any) {
        return {
          bgColor: state.appReducer.hexColor
        }
      }, () => {
        return {};
      },
      null,
      {
        forwardRef: true
      })(RendererComponent);
    let rendererInstances = new Map();

    const wrapper: ReactWrapper<{}, {}, any> = mount(
      <Provider store={reduxStore}>
        <HotTable licenseKey="non-commercial-and-evaluation"
                  id="test-hot"
                  data={Handsontable.helper.createSpreadsheetData(3, 3)}
                  width={300}
                  height={300}
                  rowHeights={23}
                  colWidths={50}
                  autoRowSize={false}
                  autoColumnSize={false}
                  init={function () {
                    mockElementDimensions(this.rootElement, 300, 300);
                  }}>
          <ReduxEnabledRenderer ref={function (instance) {
            if (instance === null) {
              return instance;
            }

            rendererInstances.set(`${instance.props.row}-${instance.props.col}`, instance);
          }
          } hot-renderer />
        </HotTable>
      </Provider>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    rendererInstances.forEach((component, key, map) => {
      expect(component.props.bgColor).toEqual('#fff');
    });

    reduxStore.dispatch({
      type: 'updateColor',
      hexColor: '#B57267'
    });

    rendererInstances.forEach((component, key, map) => {
      expect(component.props.bgColor).toEqual('#B57267');
    });

    wrapper.detach();

    done();
  });

  it('should be possible to use redux-enabled components as editors', async (done) => {
    const ReduxEnabledEditor = connect(function (state: any) {
        return {
          bgColor: state.appReducer.hexColor
        }
      }, () => {
        return {};
      },
      null,
      {
        forwardRef: true
      })(EditorComponent);
    let editorInstances = new Map();

    const wrapper: ReactWrapper<{}, {}, any> = mount(
      <Provider store={reduxStore}>
        <HotTable licenseKey="non-commercial-and-evaluation"
                  id="test-hot"
                  data={Handsontable.helper.createSpreadsheetData(3, 3)}
                  width={300}
                  height={300}
                  rowHeights={23}
                  colWidths={50}
                  init={function () {
                    mockElementDimensions(this.rootElement, 300, 300);
                  }}>
          <ReduxEnabledEditor ref={function (instance) {
            if (instance === null) {
              return instance;
            }

            editorInstances.set(`${instance.props.row}-${instance.props.col}`, instance);
          }
          } hot-editor />
        </HotTable>
      </Provider>, {attachTo: document.body.querySelector('#hotContainer')}
    );

    await sleep(100);

    editorInstances.forEach((value, key, map) => {
      expect(value.props.bgColor).toEqual('#fff');
    });

    reduxStore.dispatch({
      type: 'updateColor',
      hexColor: '#B57267'
    });

    editorInstances.forEach((value, key, map) => {
      expect(value.props.bgColor).toEqual('#B57267');
    });

    wrapper.detach();

    done();
  });
});


================================================
FILE: test/settingsMapper.spec.tsx
================================================
import { SettingsMapper } from '../src/settingsMapper';
import { HotTableProps } from '../src/types';

describe('Settings mapper unit tests', () => {
  describe('getSettings', () => {
    it('should return a valid settings object, when provided an object with settings (including the hooks prefixed with "on")', () => {
      const settingsMapper = new SettingsMapper();

      const initial: HotTableProps = {
        width: 300,
        height: 300,
        contextMenu: true,
        columns: [
          {label: 'first label'},
          {label: 'second label'}
        ],
        afterChange: () => {
          return 'works!';
        },
        afterRender: () => {
          return 'also works!';
        }
      };
      const result: {[key: string]: any} = SettingsMapper.getSettings(initial);

      expect(!!result.width && !!result.height && !!result.contextMenu && !!result.columns && !!result.afterChange && !!result.afterRender).toEqual(true);
      expect(Object.keys(initial).length).toEqual(Object.keys(result).length);
      expect(result.width).toEqual(300);
      expect(result.height).toEqual(300);
      expect(result.contextMenu).toEqual(true);
      expect(JSON.stringify(initial.columns)).toEqual(JSON.stringify(result.columns));
      expect(JSON.stringify(result.afterChange)).toEqual(JSON.stringify(initial.afterChange));
      expect(JSON.stringify(result.afterRender)).toEqual(JSON.stringify(initial.afterRender));
      expect(result.afterChange()).toEqual('works!');
      expect(result.afterRender()).toEqual('also works!');
    });

    it('should return a valid settings object, when provided an object with settings inside a "settings" property (including the hooks prefixed with "on")', () => {
      const settingsMapper = new SettingsMapper();
      const initial = {
        settings: {
          width: 300,
          height: 300,
          contextMenu: true,
          columns: [
            {label: 'first label'},
            {label: 'second label'}
          ],
          afterChange: () => {
            return 'works!';
          },
          afterRender: () => {
            return 'also works!';
          }
        }
      };
      const result: {[key: string]: any} = SettingsMapper.getSettings(initial);

      expect(!!result.width && !!result.height && !!result.contextMenu && !!result.columns && !!result.afterChange && !!result.afterRender).toEqual(true);
      expect(Object.keys(initial.settings).length).toEqual(Object.keys(result).length);
      expect(result.width).toEqual(300);
      expect(result.height).toEqual(300);
      expect(result.contextMenu).toEqual(true);
      expect(JSON.stringify(initial.settings.columns)).toEqual(JSON.stringify(result.columns));
      expect(JSON.stringify(result.afterChange)).toEqual(JSON.stringify(initial.settings.afterChange));
      expect(JSON.stringify(result.afterRender)).toEqual(JSON.stringify(initial.settings.afterRender));
      expect(result.afterChange()).toEqual('works!');
      expect(result.afterRender()).toEqual('also works!');
      expect(result.settings).toEqual(void 0);
    });

    it('should return a valid settings object, when provided an object with settings inside a "settings" property as well as individually (including the hooks prefixed with "on")', () => {
      const settingsMapper = new SettingsMapper();
      const initial = {
        width: 300,
        height: 300,
        settings: {
          contextMenu: true,
          columns: [
            {label: 'first label'},
            {label: 'second label'}
          ],
          afterChange: () => {
            return 'works!';
          },
          afterRender: () => {
            return 'also works!';
          }
        }
      };
      const result: {[key: string]: any} = SettingsMapper.getSettings(initial);

      expect(!!result.width && !!result.height && !!result.contextMenu && !!result.columns && !!result.afterChange && !!result.afterRender).toEqual(true);
      expect(Object.keys(initial.settings).length + Object.keys(initial).length - 1).toEqual(Object.keys(result).length);
      expect(result.width).toEqual(300);
      expect(result.height).toEqual(300);
      expect(result.contextMenu).toEqual(true);
      expect(JSON.stringify(initial.settings.columns)).toEqual(JSON.stringify(result.columns));
      expect(JSON.stringify(result.afterChange)).toEqual(JSON.stringify(initial.settings.afterChange));
      expect(JSON.stringify(result.afterRender)).toEqual(JSON.stringify(initial.settings.afterRender));
      expect(result.afterChange()).toEqual('works!');
      expect(result.afterRender()).toEqual('also works!');
      expect(result.settings).toEqual(void 0);
    });
  });
});


================================================
FILE: test-tsconfig.json
================================================
{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "esnext",
    "jsx": "react",
    "module": "esnext",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "baseUrl": "."
  }
}


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "esnext",
    "jsx": "react",
    "module": "esnext",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "sourceMap": true,
    "baseUrl": ".",
    "declarationDir": "."
  },
  "include": [
    "src/*"
  ],
  "exclude": [
    "**/node_modules/*"
  ]
}
Download .txt
gitextract_fs7xo0sf/

├── .babelrc
├── .codesandbox/
│   └── ci.json
├── .config/
│   ├── base.js
│   ├── commonjs.js
│   ├── es.js
│   ├── factory.js
│   ├── helpers/
│   │   └── licenseBanner.js
│   ├── minified.js
│   └── umd.js
├── .editorconfig
├── .github/
│   ├── ISSUE_TEMPLATE.md
│   └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── package.json
├── rollup.config.js
├── src/
│   ├── baseEditorComponent.tsx
│   ├── helpers.tsx
│   ├── hotColumn.tsx
│   ├── hotTable.tsx
│   ├── index.tsx
│   ├── json.d.ts
│   ├── portalManager.tsx
│   ├── settingsMapper.ts
│   └── types.tsx
├── test/
│   ├── _helpers.tsx
│   ├── autoSizeWarning.spec.tsx
│   ├── componentInternals.spec.tsx
│   ├── hotColumn.spec.tsx
│   ├── hotTable.spec.tsx
│   ├── jestsetup.ts
│   ├── reactContext.spec.tsx
│   ├── reactHooks.spec.tsx
│   ├── reactLazy.spec.tsx
│   ├── reactMemo.spec.tsx
│   ├── reactPureComponent.spec.tsx
│   ├── redux.spec.tsx
│   └── settingsMapper.spec.tsx
├── test-tsconfig.json
└── tsconfig.json
Download .txt
SYMBOL INDEX (151 symbols across 17 files)

FILE: .config/factory.js
  function createConfig (line 7) | function createConfig() {

FILE: .config/helpers/licenseBanner.js
  function addLicenseBanner (line 1) | function addLicenseBanner(config) {

FILE: src/baseEditorComponent.tsx
  class BaseEditorComponent (line 5) | class BaseEditorComponent<P = {}, S = {}, SS = any> extends React.Compon...
    method constructor (line 19) | constructor(props) {
    method _fireCallbacks (line 28) | private _fireCallbacks(...args) {
    method beginEditing (line 32) | beginEditing(...args) {
    method cancelChanges (line 36) | cancelChanges(...args) {
    method checkEditorSection (line 40) | checkEditorSection(...args) {
    method close (line 44) | close(...args) {
    method discardEditor (line 48) | discardEditor(...args) {
    method enableFullEditMode (line 52) | enableFullEditMode(...args) {
    method extend (line 56) | extend(...args) {
    method finishEditing (line 60) | finishEditing(...args) {
    method focus (line 64) | focus(...args) {
    method getValue (line 68) | getValue(...args) {
    method init (line 72) | init(...args) {
    method isInFullEditMode (line 76) | isInFullEditMode(...args) {
    method isOpened (line 80) | isOpened(...args) {
    method isWaiting (line 84) | isWaiting(...args) {
    method open (line 88) | open(...args) {
    method prepare (line 92) | prepare(row, col, prop, TD, originalValue, cellProperties) {
    method saveValue (line 104) | saveValue(...args) {
    method setValue (line 108) | setValue(...args) {
    method addHook (line 112) | addHook(...args) {
    method removeHooksByKey (line 116) | removeHooksByKey(...args) {
    method clearHooks (line 120) | clearHooks(...args) {
    method getEditedCell (line 124) | getEditedCell(...args) {
    method getEditedCellsZIndex (line 128) | getEditedCellsZIndex(...args) {

FILE: src/helpers.tsx
  constant AUTOSIZE_WARNING (line 10) | const AUTOSIZE_WARNING = 'Your `HotTable` configuration includes `autoRo...
  constant DEFAULT_CLASSNAME (line 16) | const DEFAULT_CLASSNAME = 'hot-wrapper-editor-container';
  function warn (line 23) | function warn(...args) {
  function getChildElementByType (line 36) | function getChildElementByType(children: React.ReactNode, type: string):...
  function getOriginalEditorClass (line 61) | function getOriginalEditorClass(editorElement: HotEditorElement) {
  function removeEditorContainers (line 75) | function removeEditorContainers(doc = document): void {
  function createEditorPortal (line 91) | function createEditorPortal(doc = document, editorElement: HotEditorElem...
  function getExtendedEditorElement (line 121) | function getExtendedEditorElement(children: React.ReactNode, editorCache...
  function createPortal (line 146) | function createPortal(rElement: React.ReactElement, props, callback: Fun...
  function getContainerAttributesProps (line 181) | function getContainerAttributesProps(props, randomizeId: boolean = true)...
  function addUnsafePrefixes (line 194) | function addUnsafePrefixes(instance: {

FILE: src/hotColumn.tsx
  class HotColumn (line 11) | class HotColumn extends React.Component<HotColumnProps, {}> {
    method constructor (line 29) | constructor(props: HotColumnProps, context?: any) {
    method getLocalEditorPortal (line 40) | getLocalEditorPortal(): ReactPortal {
    method setLocalEditorPortal (line 49) | setLocalEditorPortal(portal): void {
    method getSettingsProps (line 58) | getSettingsProps(): HotTableProps {
    method hasProp (line 79) | hasProp(propName: string): boolean {
    method getLocalEditorElement (line 88) | getLocalEditorElement(): React.ReactElement | null {
    method createColumnSettings (line 95) | createColumnSettings(): void {
    method createLocalEditorPortal (line 128) | createLocalEditorPortal(children = this.props.children): void {
    method emitColumnSettings (line 140) | emitColumnSettings(): void {
    method componentWillMount (line 153) | componentWillMount(): void {
    method componentDidMount (line 160) | componentDidMount(): void {
    method componentWillUpdate (line 168) | componentWillUpdate(nextProps: Readonly<HotColumnProps>, nextState: Re...
    method componentDidUpdate (line 175) | componentDidUpdate(): void {
    method render (line 185) | render(): React.ReactElement {

FILE: src/hotTable.tsx
  class HotTable (line 47) | class HotTable extends React.Component<HotTableProps, {}> {
    method constructor (line 137) | constructor(props: HotTableProps, context?: any) {
    method version (line 148) | static get version(): string {
    method getRenderedCellCache (line 166) | getRenderedCellCache(): Map<string, HTMLTableCellElement> {
    method getEditorCache (line 175) | getEditorCache(): Map<Function, React.Component> {
    method getGlobalEditorPortal (line 184) | getGlobalEditorPortal(): React.ReactPortal {
    method setGlobalEditorPortal (line 193) | setGlobalEditorPortal(portal: React.ReactPortal): void {
    method clearCache (line 200) | clearCache(): void {
    method getOwnerDocument (line 217) | getOwnerDocument() {
    method setHotElementRef (line 226) | private setHotElementRef(element: HTMLElement): void {
    method getRendererWrapper (line 236) | getRendererWrapper(rendererElement: React.ReactElement): Handsontable....
    method getEditorClass (line 280) | getEditorClass(editorElement: HotEditorElement): typeof Handsontable.e...
    method makeEditorClass (line 294) | makeEditorClass(editorComponent: React.Component): typeof Handsontable...
    method getGlobalRendererElement (line 341) | getGlobalRendererElement(): React.ReactElement {
    method getGlobalEditorElement (line 353) | getGlobalEditorElement(children: React.ReactNode = this.props.children...
    method createGlobalEditorPortal (line 362) | createGlobalEditorPortal(children: React.ReactNode = this.props.childr...
    method createNewGlobalSettings (line 375) | createNewGlobalSettings(): Handsontable.GridSettings {
    method displayAutoSizeWarning (line 405) | displayAutoSizeWarning(newGlobalSettings: Handsontable.GridSettings): ...
    method setHotColumnSettings (line 419) | setHotColumnSettings(columnSettings: Handsontable.ColumnSettings, colu...
    method handsontableBeforeRender (line 426) | handsontableBeforeRender(): void {
    method handsontableAfterRender (line 433) | handsontableAfterRender(): void {
    method updateHot (line 449) | private updateHot(newSettings: Handsontable.GridSettings): void {
    method setPortalManagerRef (line 458) | private setPortalManagerRef(pmComponent: PortalManager): void {
    method componentWillMount (line 471) | componentWillMount(): void {
    method componentDidMount (line 479) | componentDidMount(): void {
    method componentWillUpdate (line 502) | componentWillUpdate(nextProps: Readonly<HotTableProps>, nextState: Rea...
    method componentDidUpdate (line 511) | componentDidUpdate(): void {
    method componentWillUnmount (line 521) | componentWillUnmount(): void {
    method render (line 529) | render(): React.ReactElement {

FILE: src/portalManager.tsx
  class PortalManager (line 6) | class PortalManager extends React.Component<{}, {portals?: React.ReactPo...
    method constructor (line 7) | constructor(props) {
    method render (line 15) | render(): React.ReactNode {

FILE: src/settingsMapper.ts
  class SettingsMapper (line 4) | class SettingsMapper {
    method getSettings (line 11) | static getSettings(properties: HotTableProps): Handsontable.GridSettin...

FILE: src/types.tsx
  type HotEditorElement (line 8) | type HotEditorElement = React.ReactElement<{}, ConnectedComponent<React....
  type HotTableProps (line 14) | interface HotTableProps extends Handsontable.GridSettings {
  type HotEditorProps (line 25) | interface HotEditorProps {
  type HotColumnProps (line 35) | interface HotColumnProps extends Handsontable.GridSettings {

FILE: test/_helpers.tsx
  function sleep (line 6) | function sleep(delay = 100) {
  function mockElementDimensions (line 18) | function mockElementDimensions(element, width, height) {
  function simulateKeyboardEvent (line 34) | function simulateKeyboardEvent(type, keyCode) {
  function simulateMouseEvent (line 43) | function simulateMouseEvent(element, type) {
  class IndividualPropsWrapper (line 50) | class IndividualPropsWrapper extends React.Component<{ref?: string, id?:...
    method constructor (line 53) | constructor(props) {
    method componentWillMount (line 59) | componentWillMount() {
    method setHotElementRef (line 63) | private setHotElementRef(component: typeof HotTable): void {
    method render (line 67) | render(): React.ReactElement {
  class SingleObjectWrapper (line 84) | class SingleObjectWrapper extends React.Component<{ref?: string, id?: st...
    method constructor (line 87) | constructor(props) {
    method setHotElementRef (line 93) | private setHotElementRef(component: typeof HotTable): void {
    method componentWillMount (line 97) | componentWillMount() {
    method render (line 101) | render(): React.ReactElement {
  class RendererComponent (line 119) | class RendererComponent extends React.Component<any, any> {
    method render (line 120) | render(): React.ReactElement<string> {
  class EditorComponent (line 129) | class EditorComponent extends BaseEditorComponent<{}, {value?: any}> {
    method constructor (line 133) | constructor(props) {
    method getValue (line 147) | getValue() {
    method setValue (line 151) | setValue(value, callback) {
    method setNewValue (line 157) | setNewValue() {
    method open (line 163) | open() {
    method close (line 167) | close() {
    method render (line 171) | render(): React.ReactElement<string> {

FILE: test/componentInternals.spec.tsx
  class RendererComponent2 (line 29) | class RendererComponent2 extends React.Component<any, any, any> {
    method constructor (line 30) | constructor(props) {
    method render (line 38) | render(): React.ReactElement<string> {
    method constructor (line 172) | constructor(props) {
    method UNSAFE_componentWillMount (line 182) | UNSAFE_componentWillMount(): void {
    method componentDidMount (line 187) | componentDidMount(): void {
    method componentWillUnmount (line 192) | componentWillUnmount(): void {
    method render (line 197) | render(): React.ReactElement<string> {
  class RendererEditor2 (line 102) | class RendererEditor2 extends BaseEditorComponent {
    method constructor (line 103) | constructor(props) {
    method render (line 111) | render(): React.ReactElement<string> {
  class RendererComponent2 (line 171) | class RendererComponent2 extends React.Component<any, any, any> {
    method constructor (line 30) | constructor(props) {
    method render (line 38) | render(): React.ReactElement<string> {
    method constructor (line 172) | constructor(props) {
    method UNSAFE_componentWillMount (line 182) | UNSAFE_componentWillMount(): void {
    method componentDidMount (line 187) | componentDidMount(): void {
    method componentWillUnmount (line 192) | componentWillUnmount(): void {
    method render (line 197) | render(): React.ReactElement<string> {
  class EditorComponent2 (line 265) | class EditorComponent2 extends BaseEditorComponent {
    method constructor (line 266) | constructor(props) {
    method UNSAFE_componentWillMount (line 276) | UNSAFE_componentWillMount(): void {
    method componentDidMount (line 281) | componentDidMount(): void {
    method componentWillUnmount (line 286) | componentWillUnmount(): void {
    method render (line 291) | render(): React.ReactElement<string> {

FILE: test/hotColumn.spec.tsx
  function RendererComponent2 (line 152) | function RendererComponent2(props) {
  class WrapperComponent (line 158) | class WrapperComponent extends React.Component<any, any> {
    method constructor (line 159) | constructor(props) {
    method render (line 175) | render() {

FILE: test/reactContext.spec.tsx
  function RendererComponent2 (line 30) | function RendererComponent2() {
  class EditorComponent2 (line 38) | class EditorComponent2 extends BaseEditorComponent {
    method render (line 39) | render(): React.ReactElement<string> {
  class RendererComponent3 (line 48) | class RendererComponent3 extends React.Component {
    method render (line 49) | render() {
  class EditorComponent3 (line 59) | class EditorComponent3 extends React.Component {
    method render (line 60) | render() {

FILE: test/reactHooks.spec.tsx
  function HookEnabledRenderer (line 25) | function HookEnabledRenderer(props) {

FILE: test/reactLazy.spec.tsx
  function RendererComponent2 (line 23) | function RendererComponent2(props) {
  function SuspendedRenderer (line 33) | function SuspendedRenderer(props) {

FILE: test/reactMemo.spec.tsx
  function RendererComponent2 (line 27) | function RendererComponent2 (props) {

FILE: test/reactPureComponent.spec.tsx
  class RendererComponent2 (line 27) | class RendererComponent2 extends React.PureComponent<any, any> {
    method render (line 28) | render(): React.ReactElement<string> {
Condensed preview — 44 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (129K chars).
[
  {
    "path": ".babelrc",
    "chars": 283,
    "preview": "{\n  \"presets\": [\n    \"@babel/preset-react\"\n  ],\n  \"env\": {\n    \"test\": {\n      \"presets\": [\n        \"@babel/env\",\n      "
  },
  {
    "path": ".codesandbox/ci.json",
    "chars": 91,
    "preview": "{\n  \"sandboxes\": [\n    \"github/handsontable/examples/tree/master/react/pull-request\"\n  ]\n}\n"
  },
  {
    "path": ".config/base.js",
    "chars": 930,
    "preview": "import nodeResolve from 'rollup-plugin-node-resolve';\nimport babel from 'rollup-plugin-babel';\nimport typescript from 'r"
  },
  {
    "path": ".config/commonjs.js",
    "chars": 347,
    "preview": "import typescript from 'rollup-plugin-typescript2';\nimport { baseConfig, plugins } from './base';\n\nconst env = process.e"
  },
  {
    "path": ".config/es.js",
    "chars": 598,
    "preview": "import typescript from 'rollup-plugin-typescript2';\nimport { baseConfig } from './base';\nimport { plugins } from './base"
  },
  {
    "path": ".config/factory.js",
    "chars": 846,
    "preview": "import { baseConfig } from './base';\nimport { cjsConfig } from './commonjs';\nimport { esConfig } from './es';\nimport { u"
  },
  {
    "path": ".config/helpers/licenseBanner.js",
    "chars": 422,
    "preview": "export function addLicenseBanner(config) {\n  const path = require('path');\n  const fs = require('fs');\n  const packageBo"
  },
  {
    "path": ".config/minified.js",
    "chars": 887,
    "preview": "import { baseConfig } from './base';\nimport { addLicenseBanner } from './helpers/licenseBanner';\nimport replace from 'ro"
  },
  {
    "path": ".config/umd.js",
    "chars": 689,
    "preview": "import { addLicenseBanner } from './helpers/licenseBanner';\nimport { baseConfig } from './base';\nimport replace from 'ro"
  },
  {
    "path": ".editorconfig",
    "chars": 117,
    "preview": "[*]\nindent_style = space\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 436,
    "preview": "### Description\n<!--- Tell us what happens and what should happen -->\n\n### Steps to reproduce\n<!--- Provide steps to rep"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 871,
    "preview": "### Context\n<!--- Why is this change required? What problem does it solve? -->\n\n### How has this been tested?\n<!--- Plea"
  },
  {
    "path": ".gitignore",
    "chars": 90,
    "preview": "node_modules\nbower_components\ndist\ncommonjs\nes\n\n.DS_Store\n.idea\n.rpt2_cache\n*.log\n/*.d.ts\n"
  },
  {
    "path": ".npmignore",
    "chars": 71,
    "preview": "*\n\n!commonjs/*\n!dist/*\n!es/*\n!package.json\n!*.d.ts\n!README.md\n!LICENSE\n"
  },
  {
    "path": ".travis.yml",
    "chars": 830,
    "preview": "language: node_js\n\nsudo: false\n\nnode_js:\n  - '8'\n\nbefore_script:\n  - export TZ=Europe/Warsaw\n\nnotifications:\n  email: fa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 729,
    "preview": "# Contributing to Handsontable\n\nYour contributions to this project are very welcome. If you want to fix a bug or propose"
  },
  {
    "path": "LICENSE",
    "chars": 1104,
    "preview": "(The MIT License)\n\nCopyright (c) Handsoncode sp. z o.o. <hello@handsoncode.net>\n\nPermission is hereby granted, free of c"
  },
  {
    "path": "README.md",
    "chars": 6630,
    "preview": "# Important information\n\n## We permanently moved this project to the main Handsontable repository at [https://github.com"
  },
  {
    "path": "package.json",
    "chars": 3788,
    "preview": "{\n  \"name\": \"@handsontable/react\",\n  \"version\": \"4.0.0\",\n  \"description\": \"Best Data Grid for React with Spreadsheet Loo"
  },
  {
    "path": "rollup.config.js",
    "chars": 82,
    "preview": "import { createConfig } from './.config/factory';\n\nexport default createConfig();\n"
  },
  {
    "path": "src/baseEditorComponent.tsx",
    "chars": 4409,
    "preview": "import React from 'react';\nimport Handsontable from 'handsontable';\nimport { HotEditorProps } from './types';\n\nclass Bas"
  },
  {
    "path": "src/helpers.tsx",
    "chars": 7224,
    "preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport { HotEditorElement } from './types';\n\nlet bulkCompon"
  },
  {
    "path": "src/hotColumn.tsx",
    "chars": 5460,
    "preview": "import React, { ReactPortal } from 'react';\nimport { HotTableProps, HotColumnProps } from './types';\nimport {\n  addUnsaf"
  },
  {
    "path": "src/hotTable.tsx",
    "chars": 16067,
    "preview": "import React from 'react';\nimport Handsontable from 'handsontable';\nimport { SettingsMapper } from './settingsMapper';\ni"
  },
  {
    "path": "src/index.tsx",
    "chars": 159,
    "preview": "export * from './hotColumn';\nexport * from './hotTable';\nexport * from './types';\nexport * from './baseEditorComponent';"
  },
  {
    "path": "src/json.d.ts",
    "chars": 145,
    "preview": "declare module \"*.json\" {\n  const value: any;\n  export default value;\n}\n\ndeclare module \"json!*\" {\n  const value: any;\n "
  },
  {
    "path": "src/portalManager.tsx",
    "chars": 414,
    "preview": "import React from 'react';\n\n/**\n * Component class used to manage the renderer component portals.\n */\nexport class Porta"
  },
  {
    "path": "src/settingsMapper.ts",
    "chars": 916,
    "preview": "import Handsontable from 'handsontable';\nimport { HotTableProps } from './types';\n\nexport class SettingsMapper {\n  /**\n "
  },
  {
    "path": "src/types.tsx",
    "chars": 1567,
    "preview": "import Handsontable from 'handsontable';\nimport React from 'react';\nimport { ConnectedComponent } from 'react-redux';\n\n/"
  },
  {
    "path": "test/_helpers.tsx",
    "chars": 3908,
    "preview": "import React from 'react';\nimport { HotTable } from '../src/hotTable';\nimport { addUnsafePrefixes } from '../src/helpers"
  },
  {
    "path": "test/autoSizeWarning.spec.tsx",
    "chars": 9415,
    "preview": "import React from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport {\n  HotTable\n} from '../src/hotTable"
  },
  {
    "path": "test/componentInternals.spec.tsx",
    "chars": 10490,
    "preview": "import React from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport {\n  HotTable\n} from '../src/hotTable"
  },
  {
    "path": "test/hotColumn.spec.tsx",
    "chars": 10868,
    "preview": "import React from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport Handsontable from 'handsontable';\nim"
  },
  {
    "path": "test/hotTable.spec.tsx",
    "chars": 8909,
    "preview": "import React from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport {\n  HotTable\n} from '../src/hotTable"
  },
  {
    "path": "test/jestsetup.ts",
    "chars": 147,
    "preview": "import {\n  configure\n} from 'enzyme';\nimport ReactSixteenAdapter from 'enzyme-adapter-react-16';\n\nconfigure({adapter: ne"
  },
  {
    "path": "test/reactContext.spec.tsx",
    "chars": 3391,
    "preview": "import React from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport {\n  HotTable\n} from '../src/hotTable"
  },
  {
    "path": "test/reactHooks.spec.tsx",
    "chars": 2477,
    "preview": "import React, { useState } from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport {\n  HotTable\n} from '."
  },
  {
    "path": "test/reactLazy.spec.tsx",
    "chars": 2192,
    "preview": "import React, { Suspense, lazy } from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport {\n  HotTable\n} f"
  },
  {
    "path": "test/reactMemo.spec.tsx",
    "chars": 1984,
    "preview": "import React from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport {\n  HotTable\n} from '../src/hotTable"
  },
  {
    "path": "test/reactPureComponent.spec.tsx",
    "chars": 1952,
    "preview": "import React from 'react';\nimport {\n  mount,\n  ReactWrapper\n} from 'enzyme';\nimport {\n  HotTable\n} from '../src/hotTable"
  },
  {
    "path": "test/redux.spec.tsx",
    "chars": 4563,
    "preview": "import React from 'react';\nimport { createStore, combineReducers } from 'redux';\nimport { Provider, connect } from 'reac"
  },
  {
    "path": "test/settingsMapper.spec.tsx",
    "chars": 4706,
    "preview": "import { SettingsMapper } from '../src/settingsMapper';\nimport { HotTableProps } from '../src/types';\n\ndescribe('Setting"
  },
  {
    "path": "test-tsconfig.json",
    "chars": 221,
    "preview": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"target\": \"esnext\",\n    \"jsx\": \"react\",\n    \"module\": \"esne"
  },
  {
    "path": "tsconfig.json",
    "chars": 347,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"jsx\": \"react\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"no"
  }
]

About this extraction

This page contains the full source code of the handsontable/react-handsontable GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 44 files (118.9 KB), approximately 29.7k tokens, and a symbol index with 151 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!