Skip to content
This repository was archived by the owner on Mar 14, 2022. It is now read-only.

Commit fe84b51

Browse files
committed
Merge branch 'release/3.1.3'
2 parents 386a950 + d16c8e2 commit fe84b51

15 files changed

+5457
-2194
lines changed

.codesandbox/ci.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"sandboxes": [
3+
"github/handsontable/examples/tree/master/react/pull-request"
4+
]
5+
}

.config/minified.js

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { baseConfig } from './base';
22
import { addLicenseBanner } from './helpers/licenseBanner';
3+
import replace from 'rollup-plugin-replace';
34
import { uglify } from 'rollup-plugin-uglify';
45

56
const minFilename = 'react-handsontable.min.js';
@@ -19,6 +20,9 @@ const minConfig = {
1920
}
2021
},
2122
plugins: baseConfig.plugins.concat([
23+
replace({
24+
'process.env.NODE_ENV': JSON.stringify('production')
25+
}),
2226
uglify({
2327
output: {
2428
comments: /^!/

.config/umd.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { addLicenseBanner } from './helpers/licenseBanner';
22
import { baseConfig } from './base';
3+
import replace from 'rollup-plugin-replace';
34

45
const env = process.env.NODE_ENV;
56
const filename = 'react-handsontable.js';
@@ -18,7 +19,11 @@ const umdConfig = {
1819
handsontable: 'Handsontable'
1920
}
2021
},
21-
plugins: baseConfig.plugins
22+
plugins: baseConfig.plugins.concat([
23+
replace({
24+
'process.env.NODE_ENV': JSON.stringify('production')
25+
})
26+
])
2227
};
2328

2429
addLicenseBanner(umdConfig);

package-lock.json

+5,220-2,095
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+14-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@handsontable/react",
3-
"version": "3.1.2",
3+
"version": "3.1.3",
44
"description": "Best Data Grid for React with Spreadsheet Look and Feel.",
55
"author": "Handsoncode <[email protected]> (https://handsoncode.net)",
66
"homepage": "https://handsontable.com",
@@ -43,20 +43,21 @@
4343
"url": "https://github.com/handsontable/react-handsontable/issues"
4444
},
4545
"devDependencies": {
46-
"@babel/cli": "^7.2.3",
47-
"@babel/core": "^7.3.4",
48-
"@babel/plugin-proposal-class-properties": "^7.3.4",
49-
"@babel/plugin-transform-runtime": "^7.3.4",
50-
"@babel/polyfill": "^7.2.5",
51-
"@babel/preset-env": "^7.3.4",
52-
"@babel/preset-react": "^7.0.0",
53-
"@babel/preset-typescript": "^7.3.3",
54-
"@babel/runtime": "^7.3.4",
46+
"@babel/cli": "^7.8.4",
47+
"@babel/core": "^7.9.0",
48+
"@babel/plugin-proposal-class-properties": "^7.8.3",
49+
"@babel/plugin-transform-runtime": "^7.9.0",
50+
"@babel/polyfill": "^7.8.7",
51+
"@babel/preset-env": "^7.9.0",
52+
"@babel/preset-react": "^7.9.4",
53+
"@babel/preset-typescript": "^7.9.0",
54+
"@babel/runtime": "^7.9.2",
5555
"@types/enzyme": "^3.1.15",
5656
"@types/enzyme-adapter-react-16": "^1.0.3",
5757
"@types/jest": "^24.0.9",
5858
"@types/react": "^16.9.5",
5959
"@types/react-dom": "^16.9.1",
60+
"@types/react-redux": "^7.1.7",
6061
"babel-core": "^7.0.0-bridge.0",
6162
"cpy-cli": "^2.0.0",
6263
"cross-env": "^5.2.0",
@@ -65,7 +66,8 @@
6566
"enzyme-adapter-react-16": "^1.14.0",
6667
"enzyme-to-json": "^3.4.0",
6768
"handsontable": "^7.2.2",
68-
"jest": "^24.1.0",
69+
"jest": "^25.1.0",
70+
"prop-types": "^15.7.2",
6971
"react": "^16.10.2",
7072
"react-dom": "^16.10.2",
7173
"react-redux": "^7.1.1",
@@ -76,6 +78,7 @@
7678
"rollup-plugin-commonjs": "^10.0.1",
7779
"rollup-plugin-json": "^4.0.0",
7880
"rollup-plugin-node-resolve": "^5.2.0",
81+
"rollup-plugin-replace": "^2.2.0",
7982
"rollup-plugin-typescript2": "^0.22.1",
8083
"rollup-plugin-uglify": "^6.0.4",
8184
"typescript": "^3.5.3",

src/baseEditorComponent.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React from 'react';
22
import Handsontable from 'handsontable';
3+
import { HotEditorProps } from './types';
34

4-
class BaseEditorComponent<P = {}, S = {}, SS = any> extends React.Component<P, S, SS> implements Handsontable._editors.Base {
5+
class BaseEditorComponent<P = {}, S = {}, SS = any> extends React.Component<P | HotEditorProps, S> implements Handsontable._editors.Base {
56
name = 'BaseEditorComponent';
67
instance = null;
78
row = null;
@@ -19,7 +20,7 @@ class BaseEditorComponent<P = {}, S = {}, SS = any> extends React.Component<P, S
1920
super(props);
2021

2122
if (props.emitEditorInstance) {
22-
props.emitEditorInstance(this.constructor.name, this);
23+
props.emitEditorInstance(this);
2324
}
2425
}
2526

src/helpers.tsx

+74-30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom';
3+
import { HotEditorElement } from './types';
34

45
let bulkComponentContainer = null;
56

@@ -9,6 +10,22 @@ let bulkComponentContainer = null;
910
export const AUTOSIZE_WARNING = 'Your `HotTable` configuration includes `autoRowSize`/`autoColumnSize` options, which are not compatible with ' +
1011
' the component-based renderers`. Disable `autoRowSize` and `autoColumnSize` to prevent row and column misalignment.';
1112

13+
/**
14+
* Default classname given to the wrapper container.
15+
*/
16+
const DEFAULT_CLASSNAME = 'hot-wrapper-editor-container';
17+
18+
/**
19+
* Logs warn to the console if the `console` object is exposed.
20+
*
21+
* @param {...*} args Values which will be logged.
22+
*/
23+
export function warn(...args) {
24+
if (typeof console !== 'undefined') {
25+
console.warn(...args);
26+
}
27+
}
28+
1229
/**
1330
* Filter out and return elements of the provided `type` from the `HotColumn` component's children.
1431
*
@@ -36,26 +53,27 @@ export function getChildElementByType(children: React.ReactNode, type: string):
3653
}
3754

3855
/**
39-
* Get the component node name.
56+
* Get the reference to the original editor class.
4057
*
41-
* @param {React.ReactElement} componentNode
42-
* @returns {String} Provided component's name.
58+
* @param {React.ReactElement} editorElement React element of the editor class.
59+
* @returns {Function} Original class of the editor component.
4360
*/
44-
export function getComponentNodeName(componentNode: React.ReactElement): string {
45-
if (!componentNode) {
61+
export function getOriginalEditorClass(editorElement: HotEditorElement) {
62+
if (!editorElement) {
4663
return null;
4764
}
4865

49-
return (componentNode.type as Function).name || ((componentNode.type as any).WrappedComponent as Function).name;
66+
return editorElement.type.WrappedComponent ? editorElement.type.WrappedComponent : editorElement.type;
5067
}
5168

5269
/**
5370
* Remove editor containers from DOM.
5471
*
55-
* @param [doc] Document to be used.
72+
* @param {Document} [doc] Document to be used.
73+
* @param {Map} editorCache The editor cache reference.
5674
*/
5775
export function removeEditorContainers(doc = document): void {
58-
doc.querySelectorAll('[id^="hot-wrapper-editor-container-"]').forEach((domNode) => {
76+
doc.querySelectorAll(`[class^="${DEFAULT_CLASSNAME}"]`).forEach((domNode) => {
5977
if (domNode.parentNode) {
6078
domNode.parentNode.removeChild(domNode);
6179
}
@@ -65,23 +83,31 @@ export function removeEditorContainers(doc = document): void {
6583
/**
6684
* Create an editor portal.
6785
*
86+
* @param {Document} [doc] Document to be used.
6887
* @param {React.ReactElement} editorElement Editor's element.
88+
* @param {Map} editorCache The editor cache reference.
6989
* @returns {React.ReactPortal} The portal for the editor.
7090
*/
71-
export function createEditorPortal(editorElement: React.ReactElement): React.ReactPortal {
91+
export function createEditorPortal(doc = document, editorElement: HotEditorElement, editorCache: Map<Function, React.Component>): React.ReactPortal {
7292
if (editorElement === null) {
7393
return;
7494
}
7595

76-
const componentName: string = getComponentNodeName(editorElement);
96+
const editorContainer = doc.createElement('DIV');
97+
const {id, className, style} = getContainerAttributesProps(editorElement.props, false);
7798

78-
let editorContainer = document.querySelector('#hot-wrapper-editor-container-' + componentName);
79-
if (!document.querySelector('#hot-wrapper-editor-container-' + componentName)) {
80-
editorContainer = document.createElement('DIV');
81-
editorContainer.id = 'hot-wrapper-editor-container-' + componentName;
82-
document.body.appendChild(editorContainer);
99+
if (id) {
100+
editorContainer.id = id;
83101
}
84102

103+
editorContainer.className = [DEFAULT_CLASSNAME, className].join(' ');
104+
105+
if (style) {
106+
Object.assign(editorContainer.style, style);
107+
}
108+
109+
doc.body.appendChild(editorContainer);
110+
85111
return ReactDOM.createPortal(editorElement, editorContainer);
86112
}
87113

@@ -92,21 +118,20 @@ export function createEditorPortal(editorElement: React.ReactElement): React.Rea
92118
* @param {Map} editorCache Component's editor cache.
93119
* @returns {React.ReactElement} An editor element containing the additional methods.
94120
*/
95-
export function getExtendedEditorElement(children: React.ReactNode, editorCache: Map<string, object>): React.ReactElement | null {
121+
export function getExtendedEditorElement(children: React.ReactNode, editorCache: Map<Function, object>): React.ReactElement | null {
96122
const editorElement = getChildElementByType(children, 'hot-editor');
123+
const editorClass = getOriginalEditorClass(editorElement);
97124

98125
if (!editorElement) {
99126
return null;
100127
}
101128

102129
return React.cloneElement(editorElement, {
103-
emitEditorInstance: (editorName, editorInstance) => {
104-
if (!editorCache.has(editorName)) {
105-
editorCache.set(editorName, editorInstance);
106-
}
130+
emitEditorInstance: (editorInstance) => {
131+
editorCache.set(editorClass, editorInstance);
107132
},
108133
isEditor: true
109-
});
134+
} as object);
110135
}
111136

112137
/**
@@ -144,23 +169,42 @@ export function createPortal(rElement: React.ReactElement, props, callback: Func
144169
};
145170
}
146171

172+
/**
173+
* Get an object containing the `id`, `className` and `style` keys, representing the corresponding props passed to the
174+
* component.
175+
*
176+
* @param {Object} props Object containing the react element props.
177+
* @param {Boolean} randomizeId If set to `true`, the function will randomize the `id` property when no `id` was present in the `prop` object.
178+
* @returns An object containing the `id`, `className` and `style` keys, representing the corresponding props passed to the
179+
* component.
180+
*/
181+
export function getContainerAttributesProps(props, randomizeId: boolean = true): {id: string, className: string, style: object} {
182+
return {
183+
id: props.id || (randomizeId ? 'hot-' + Math.random().toString(36).substring(5) : void 0),
184+
className: props.className || '',
185+
style: props.style || {},
186+
}
187+
}
188+
147189
/**
148190
* Add the `UNSAFE_` prefixes to the deprecated lifecycle methods for React >= 16.3.
149191
*
150-
* @param {Function} Klass Class to have the methods renamed.
151-
* @returns {Function} Class with the renamed methods.
192+
* @param {Object} instance Instance to have the methods renamed.
152193
*/
153-
export function addUnsafePrefixes<T extends any>(Klass: T): T {
194+
export function addUnsafePrefixes(instance: {
195+
UNSAFE_componentWillUpdate?: Function,
196+
componentWillUpdate: Function,
197+
UNSAFE_componentWillMount?: Function,
198+
componentWillMount: Function
199+
}): void {
154200
const reactSemverArray = React.version.split('.').map((v) => parseInt(v));
155201
const shouldPrefix = reactSemverArray[0] >= 16 && reactSemverArray[1] >= 3;
156202

157203
if (shouldPrefix) {
158-
Klass.prototype.UNSAFE_componentWillUpdate = Klass.prototype.componentWillUpdate;
159-
delete Klass.prototype.componentWillUpdate;
204+
instance.UNSAFE_componentWillUpdate = instance.componentWillUpdate;
205+
instance.componentWillUpdate = void 0;
160206

161-
Klass.prototype.UNSAFE_componentWillMount = Klass.prototype.componentWillMount;
162-
delete Klass.prototype.componentWillMount;
207+
instance.UNSAFE_componentWillMount = instance.componentWillMount;
208+
instance.componentWillMount = void 0;
163209
}
164-
165-
return Klass;
166210
}

src/hotColumn.tsx

+22-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import React, { ReactPortal } from 'react';
22
import { HotTableProps, HotColumnProps } from './types';
3-
import { addUnsafePrefixes, createEditorPortal, getExtendedEditorElement } from './helpers';
3+
import {
4+
addUnsafePrefixes,
5+
createEditorPortal,
6+
getExtendedEditorElement
7+
} from './helpers';
48
import { SettingsMapper } from './settingsMapper';
59
import Handsontable from 'handsontable';
610

@@ -16,6 +20,18 @@ class HotColumn extends React.Component<HotColumnProps, {}> {
1620
*/
1721
private localEditorPortal: ReactPortal = null;
1822

23+
/**
24+
* HotColumn class constructor.
25+
*
26+
* @param {HotColumnProps} props Component props.
27+
* @param {*} [context] Component context.
28+
*/
29+
constructor(props: HotColumnProps, context?: any) {
30+
super(props, context);
31+
32+
addUnsafePrefixes(this);
33+
}
34+
1935
/**
2036
* Get the local editor portal cache property.
2137
*
@@ -41,7 +57,7 @@ class HotColumn extends React.Component<HotColumnProps, {}> {
4157
*/
4258
getSettingsProps(): HotTableProps {
4359
this.internalProps = ['__componentRendererColumns', '_emitColumnSettings', '_columnIndex', '_getChildElementByType', '_getRendererWrapper',
44-
'_getEditorClass', '_getEditorCache', 'hot-renderer', 'hot-editor', 'children'];
60+
'_getEditorClass', '_getEditorCache', '_getOwnerDocument', 'hot-renderer', 'hot-editor', 'children'];
4561

4662
return Object.keys(this.props)
4763
.filter(key => {
@@ -110,10 +126,11 @@ class HotColumn extends React.Component<HotColumnProps, {}> {
110126
* @param {React.ReactNode} [children] Children of the HotTable instance. Defaults to `this.props.children`.
111127
*/
112128
createLocalEditorPortal(children = this.props.children): void {
113-
const localEditorElement: React.ReactElement = getExtendedEditorElement(children, this.props._getEditorCache());
129+
const editorCache = this.props._getEditorCache();
130+
const localEditorElement: React.ReactElement = getExtendedEditorElement(children, editorCache);
114131

115132
if (localEditorElement) {
116-
this.setLocalEditorPortal(createEditorPortal(localEditorElement))
133+
this.setLocalEditorPortal(createEditorPortal(this.props._getOwnerDocument(), localEditorElement, editorCache));
117134
}
118135
}
119136

@@ -174,5 +191,4 @@ class HotColumn extends React.Component<HotColumnProps, {}> {
174191
}
175192
}
176193

177-
const PrefixedHotColumn = addUnsafePrefixes(HotColumn);
178-
export { PrefixedHotColumn as HotColumn };
194+
export { HotColumn };

0 commit comments

Comments
 (0)