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

Commit

Permalink
Merge branch 'release/3.1.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
jansiegel committed Apr 15, 2020
2 parents 386a950 + d16c8e2 commit fe84b51
Show file tree
Hide file tree
Showing 15 changed files with 5,457 additions and 2,194 deletions.
5 changes: 5 additions & 0 deletions .codesandbox/ci.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sandboxes": [
"github/handsontable/examples/tree/master/react/pull-request"
]
}
4 changes: 4 additions & 0 deletions .config/minified.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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';
Expand All @@ -19,6 +20,9 @@ const minConfig = {
}
},
plugins: baseConfig.plugins.concat([
replace({
'process.env.NODE_ENV': JSON.stringify('production')
}),
uglify({
output: {
comments: /^!/
Expand Down
7 changes: 6 additions & 1 deletion .config/umd.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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';
Expand All @@ -18,7 +19,11 @@ const umdConfig = {
handsontable: 'Handsontable'
}
},
plugins: baseConfig.plugins
plugins: baseConfig.plugins.concat([
replace({
'process.env.NODE_ENV': JSON.stringify('production')
})
])
};

addLicenseBanner(umdConfig);
Expand Down
7,315 changes: 5,220 additions & 2,095 deletions package-lock.json

Large diffs are not rendered by default.

25 changes: 14 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@handsontable/react",
"version": "3.1.2",
"version": "3.1.3",
"description": "Best Data Grid for React with Spreadsheet Look and Feel.",
"author": "Handsoncode <hello@handsoncode.net> (https://handsoncode.net)",
"homepage": "https://handsontable.com",
Expand Down Expand Up @@ -43,20 +43,21 @@
"url": "https://github.com/handsontable/react-handsontable/issues"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.3.4",
"@babel/plugin-proposal-class-properties": "^7.3.4",
"@babel/plugin-transform-runtime": "^7.3.4",
"@babel/polyfill": "^7.2.5",
"@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@babel/runtime": "^7.3.4",
"@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": "^2.0.0",
"cross-env": "^5.2.0",
Expand All @@ -65,7 +66,8 @@
"enzyme-adapter-react-16": "^1.14.0",
"enzyme-to-json": "^3.4.0",
"handsontable": "^7.2.2",
"jest": "^24.1.0",
"jest": "^25.1.0",
"prop-types": "^15.7.2",
"react": "^16.10.2",
"react-dom": "^16.10.2",
"react-redux": "^7.1.1",
Expand All @@ -76,6 +78,7 @@
"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",
Expand Down
5 changes: 3 additions & 2 deletions src/baseEditorComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react';
import Handsontable from 'handsontable';
import { HotEditorProps } from './types';

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

if (props.emitEditorInstance) {
props.emitEditorInstance(this.constructor.name, this);
props.emitEditorInstance(this);
}
}

Expand Down
104 changes: 74 additions & 30 deletions src/helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { HotEditorElement } from './types';

let bulkComponentContainer = null;

Expand All @@ -9,6 +10,22 @@ let bulkComponentContainer = null;
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.
*
Expand Down Expand Up @@ -36,26 +53,27 @@ export function getChildElementByType(children: React.ReactNode, type: string):
}

/**
* Get the component node name.
* Get the reference to the original editor class.
*
* @param {React.ReactElement} componentNode
* @returns {String} Provided component's name.
* @param {React.ReactElement} editorElement React element of the editor class.
* @returns {Function} Original class of the editor component.
*/
export function getComponentNodeName(componentNode: React.ReactElement): string {
if (!componentNode) {
export function getOriginalEditorClass(editorElement: HotEditorElement) {
if (!editorElement) {
return null;
}

return (componentNode.type as Function).name || ((componentNode.type as any).WrappedComponent as Function).name;
return editorElement.type.WrappedComponent ? editorElement.type.WrappedComponent : editorElement.type;
}

/**
* Remove editor containers from DOM.
*
* @param [doc] Document to be used.
* @param {Document} [doc] Document to be used.
* @param {Map} editorCache The editor cache reference.
*/
export function removeEditorContainers(doc = document): void {
doc.querySelectorAll('[id^="hot-wrapper-editor-container-"]').forEach((domNode) => {
doc.querySelectorAll(`[class^="${DEFAULT_CLASSNAME}"]`).forEach((domNode) => {
if (domNode.parentNode) {
domNode.parentNode.removeChild(domNode);
}
Expand All @@ -65,23 +83,31 @@ export function removeEditorContainers(doc = document): void {
/**
* 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(editorElement: React.ReactElement): React.ReactPortal {
export function createEditorPortal(doc = document, editorElement: HotEditorElement, editorCache: Map<Function, React.Component>): React.ReactPortal {
if (editorElement === null) {
return;
}

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

let editorContainer = document.querySelector('#hot-wrapper-editor-container-' + componentName);
if (!document.querySelector('#hot-wrapper-editor-container-' + componentName)) {
editorContainer = document.createElement('DIV');
editorContainer.id = 'hot-wrapper-editor-container-' + componentName;
document.body.appendChild(editorContainer);
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);
}

Expand All @@ -92,21 +118,20 @@ export function createEditorPortal(editorElement: React.ReactElement): React.Rea
* @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<string, object>): React.ReactElement | null {
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: (editorName, editorInstance) => {
if (!editorCache.has(editorName)) {
editorCache.set(editorName, editorInstance);
}
emitEditorInstance: (editorInstance) => {
editorCache.set(editorClass, editorInstance);
},
isEditor: true
});
} as object);
}

/**
Expand Down Expand Up @@ -144,23 +169,42 @@ export function createPortal(rElement: React.ReactElement, props, callback: Func
};
}

/**
* 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 {Function} Klass Class to have the methods renamed.
* @returns {Function} Class with the renamed methods.
* @param {Object} instance Instance to have the methods renamed.
*/
export function addUnsafePrefixes<T extends any>(Klass: T): T {
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) {
Klass.prototype.UNSAFE_componentWillUpdate = Klass.prototype.componentWillUpdate;
delete Klass.prototype.componentWillUpdate;
instance.UNSAFE_componentWillUpdate = instance.componentWillUpdate;
instance.componentWillUpdate = void 0;

Klass.prototype.UNSAFE_componentWillMount = Klass.prototype.componentWillMount;
delete Klass.prototype.componentWillMount;
instance.UNSAFE_componentWillMount = instance.componentWillMount;
instance.componentWillMount = void 0;
}

return Klass;
}
28 changes: 22 additions & 6 deletions src/hotColumn.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React, { ReactPortal } from 'react';
import { HotTableProps, HotColumnProps } from './types';
import { addUnsafePrefixes, createEditorPortal, getExtendedEditorElement } from './helpers';
import {
addUnsafePrefixes,
createEditorPortal,
getExtendedEditorElement
} from './helpers';
import { SettingsMapper } from './settingsMapper';
import Handsontable from 'handsontable';

Expand All @@ -16,6 +20,18 @@ class HotColumn extends React.Component<HotColumnProps, {}> {
*/
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.
*
Expand All @@ -41,7 +57,7 @@ class HotColumn extends React.Component<HotColumnProps, {}> {
*/
getSettingsProps(): HotTableProps {
this.internalProps = ['__componentRendererColumns', '_emitColumnSettings', '_columnIndex', '_getChildElementByType', '_getRendererWrapper',
'_getEditorClass', '_getEditorCache', 'hot-renderer', 'hot-editor', 'children'];
'_getEditorClass', '_getEditorCache', '_getOwnerDocument', 'hot-renderer', 'hot-editor', 'children'];

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

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

Expand Down Expand Up @@ -174,5 +191,4 @@ class HotColumn extends React.Component<HotColumnProps, {}> {
}
}

const PrefixedHotColumn = addUnsafePrefixes(HotColumn);
export { PrefixedHotColumn as HotColumn };
export { HotColumn };
Loading

0 comments on commit fe84b51

Please sign in to comment.