Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to React 16.3+ lifecycle methods #3702

Merged
merged 53 commits into from
Sep 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
66545e7
Refactor abstract components for new lifecycle methods
Aug 14, 2019
d1f576a
Update numericInput component to new lifecycle methods
Aug 15, 2019
c636c23
Update multi-slider to new lifecycle methods
Aug 15, 2019
aab8cf3
Update alert to new lifecycle methods
Aug 15, 2019
e76e438
Update collapse to new lifecycle methods
Aug 15, 2019
8c437bd
Update hotkeys to new lifecycle methods
Aug 15, 2019
4b187f6
Increase timeout to fix flakyness of test
Aug 15, 2019
53eb7f7
Update tagInput to new lifecycle methods
Aug 15, 2019
b038062
Update popover to new lifecycle methods
Aug 15, 2019
9d4b6f9
Lint changed files
Aug 15, 2019
cbb823c
Refactor dateInput to new lifeccycle methods
Aug 15, 2019
5bc4d67
Refactor datePicker to new lifeccycle methods
Aug 15, 2019
67d17d7
Refactor dateRangeInput to new lifeccycle methods
Aug 15, 2019
c2efafc
Refactor dateRangePicker to new lifeccycle methods
Aug 15, 2019
8d7bccd
Update dom in dateTimePicker tests after changing props
Aug 15, 2019
b2f7512
Fix signature of componentDidUpdate to allow to receive args
Aug 15, 2019
211cc8f
Only set hotkey if events are already registered
Aug 15, 2019
545a81e
Fix Table component to correctly extend AbstractComponent
Aug 15, 2019
c7a2f56
Refactor timezonePicker to use new lifecycle methods
Aug 15, 2019
73d8763
Add lifecycle polyfill to core package
Aug 15, 2019
76bf418
Refactor lifecycle method in timePicker component
Aug 15, 2019
54979cb
Refactor lifecycle method in Resizable component
Aug 15, 2019
469cd65
Refactor lifecycle method in EditableCell component
Aug 15, 2019
28daf23
Refactor lifecycle method in EditableNane component
Aug 15, 2019
73fdeec
Refactor lifecycle method in LoadableContent component
Aug 15, 2019
ca99bf1
Refactor lifecycle method in Header component
Aug 15, 2019
77438a1
Refactor lifecycle method in Suggest component
Aug 15, 2019
80dabc4
Remove remaining old lifecycle methods in core package
Aug 15, 2019
e7eefc3
Refactor example component to new lifecycle method
Aug 15, 2019
51192c0
Refactor dateTimePicker component to new lifecycle methods
Aug 15, 2019
b74069d
Fix jsdocs
Aug 15, 2019
7323f55
Count items rendered instead of render method calls to account for ad…
Aug 15, 2019
f4c796c
Refactor QueryList to use new lifecycle methods
Aug 15, 2019
0bf9ff8
Refactor Table to use new lifecycle methods
Aug 15, 2019
cb94e0c
Merge branch 'develop' of https://github.com/palantir/blueprint into …
Sep 21, 2019
ea315c5
Incorporate changes from different PR
Sep 20, 2019
a7cbd2b
Remove unneeded code
Sep 21, 2019
42df21b
Polyfill tabs component
Sep 21, 2019
e6755a2
Fix DateRangeInput tests
Sep 21, 2019
24f937e
Pass forceValidation parameter in constructor to use by implementations
Sep 21, 2019
bc21f72
Use abstractComponent implementation of validateProps invocations
Sep 21, 2019
97bd162
Add comment to explain validateProps change in table component
Sep 21, 2019
7a523f4
Rename new abstract components to keep name for legacy components
Sep 21, 2019
631be23
Fix up abstract components, rename to "v2"
adidahiya Sep 24, 2019
e7fb9bb
update usage, make sure everything is polyfilled
adidahiya Sep 24, 2019
cb6d630
Fix some argument names
adidahiya Sep 24, 2019
f79460f
remove unnecessary test logic
adidahiya Sep 30, 2019
3f6e21e
Clean up resizable.tsx
adidahiya Sep 30, 2019
32eb87c
remove forceValidate param, simplify Table#validateProps
adidahiya Sep 30, 2019
fa76c0a
skip some tests
adidahiya Sep 30, 2019
b2483d5
Remove unused import
adidahiya Sep 30, 2019
7c5251c
Revert some semantic changes in Table#validateProps
adidahiya Sep 30, 2019
6504b65
Swap order of Table decorators
adidahiya Sep 30, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@types/mocha": "5.2.7",
"@types/prop-types": "15.7.1",
"@types/react": "16.8.23",
"@types/react-lifecycles-compat": "^3.0.1",
"@types/react-dom": "16.8.5",
"@types/react-transition-group": "4.2.0",
"@types/sinon": "7.0.13",
Expand Down
1 change: 1 addition & 0 deletions packages/core/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = function (config) {
// not worth full coverage
"src/accessibility/*",
"src/common/abstractComponent*",
"src/common/abstractPureComponent*",
],
});
config.set(baseConfig);
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"dom4": "^2.1.5",
"normalize.css": "^8.0.1",
"popper.js": "^1.15.0",
"react-lifecycles-compat": "^3.0.4",
"react-popper": "^1.3.3",
"react-transition-group": "^2.9.0",
"resize-observer-polyfill": "^1.5.1",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/common/abstractComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { isNodeEnv } from "./utils";
/**
* An abstract component that Blueprint components can extend
* in order to add some common functionality like runtime props validation.
* @deprecated componentWillReceiveProps is deprecated in React 16.9; use AbstractComponent2 instead
*/
export abstract class AbstractComponent<P, S> extends React.Component<P, S> {
/** Component displayName should be `public static`. This property exists to prevent incorrect usage. */
Expand Down
83 changes: 83 additions & 0 deletions packages/core/src/common/abstractComponent2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2019 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as React from "react";
import { isNodeEnv } from "./utils";

/**
* An abstract component that Blueprint components can extend
* in order to add some common functionality like runtime props validation.
*/
export abstract class AbstractComponent2<P, S = {}, SS = {}> extends React.Component<P, S, SS> {
/** Component displayName should be `public static`. This property exists to prevent incorrect usage. */
protected displayName: never;

// Not bothering to remove entries when their timeouts finish because clearing invalid ID is a no-op
private timeoutIds: number[] = [];

constructor(props?: P, context?: any) {
super(props, context);
if (!isNodeEnv("production")) {
this.validateProps(this.props);
}
}

public componentDidUpdate(_prevProps: P, _prevState: S, _snapshot?: SS) {
if (!isNodeEnv("production")) {
this.validateProps(this.props);
}
}

public componentWillUnmount() {
this.clearTimeouts();
}

/**
* Set a timeout and remember its ID.
* All stored timeouts will be cleared when component unmounts.
* @returns a "cancel" function that will clear timeout when invoked.
*/
public setTimeout(callback: () => void, timeout?: number) {
const handle = window.setTimeout(callback, timeout);
this.timeoutIds.push(handle);
return () => window.clearTimeout(handle);
}

/**
* Clear all known timeouts.
*/
public clearTimeouts = () => {
if (this.timeoutIds.length > 0) {
for (const timeoutId of this.timeoutIds) {
window.clearTimeout(timeoutId);
}
this.timeoutIds = [];
}
};

/**
* Ensures that the props specified for a component are valid.
* Implementations should check that props are valid and usually throw an Error if they are not.
* Implementations should not duplicate checks that the type system already guarantees.
*
* This method should be used instead of React's
* [propTypes](https://facebook.github.io/react/docs/reusable-components.html#prop-validation) feature.
* Like propTypes, these runtime checks run only in development mode.
*/
protected validateProps(_props: P) {
// implement in subclass
}
}
3 changes: 2 additions & 1 deletion packages/core/src/common/abstractPureComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { isNodeEnv } from "./utils";
/**
* An abstract component that Blueprint components can extend
* in order to add some common functionality like runtime props validation.
* @deprecated componentWillReceiveProps is deprecated in React 16.9; use AbstractPureComponent2 instead
*/
export abstract class AbstractPureComponent<P, S = {}> extends React.PureComponent<P, S> {
/** Component displayName should be `public static`. This property exists to prevent incorrect usage. */
Expand Down Expand Up @@ -77,7 +78,7 @@ export abstract class AbstractPureComponent<P, S = {}> extends React.PureCompone
* [propTypes](https://facebook.github.io/react/docs/reusable-components.html#prop-validation) feature.
* Like propTypes, these runtime checks run only in development mode.
*/
protected validateProps(_: P & { children?: React.ReactNode }) {
protected validateProps(_props: P & { children?: React.ReactNode }) {
// implement in subclass
}
}
86 changes: 86 additions & 0 deletions packages/core/src/common/abstractPureComponent2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2019 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as React from "react";
import { isNodeEnv } from "./utils";

/**
* An abstract component that Blueprint components can extend
* in order to add some common functionality like runtime props validation.
*/
export abstract class AbstractPureComponent2<P, S = {}, SS = {}> extends React.PureComponent<P, S, SS> {
// unsafe lifecycle method
public componentWillReceiveProps: never;

/** Component displayName should be `public static`. This property exists to prevent incorrect usage. */
protected displayName: never;

// Not bothering to remove entries when their timeouts finish because clearing invalid ID is a no-op
private timeoutIds: number[] = [];

constructor(props?: P, context?: any) {
super(props, context);
if (!isNodeEnv("production")) {
this.validateProps(this.props);
}
}

public componentDidUpdate(_prevProps: P, _prevState: S, _snapshot?: SS) {
if (!isNodeEnv("production")) {
this.validateProps(this.props);
}
}

public componentWillUnmount() {
this.clearTimeouts();
}

/**
* Set a timeout and remember its ID.
* All stored timeouts will be cleared when component unmounts.
* @returns a "cancel" function that will clear timeout when invoked.
*/
public setTimeout(callback: () => void, timeout?: number) {
const handle = window.setTimeout(callback, timeout);
this.timeoutIds.push(handle);
return () => window.clearTimeout(handle);
}

/**
* Clear all known timeouts.
*/
public clearTimeouts = () => {
if (this.timeoutIds.length > 0) {
for (const timeoutId of this.timeoutIds) {
window.clearTimeout(timeoutId);
}
this.timeoutIds = [];
}
};

/**
* Ensures that the props specified for a component are valid.
* Implementations should check that props are valid and usually throw an Error if they are not.
* Implementations should not duplicate checks that the type system already guarantees.
*
* This method should be used instead of React's
* [propTypes](https://facebook.github.io/react/docs/reusable-components.html#prop-validation) feature.
* Like propTypes, these runtime checks run only in development mode.
*/
protected validateProps(_props: P) {
// implement in subclass
}
}
2 changes: 2 additions & 0 deletions packages/core/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/

export * from "./abstractComponent";
export * from "./abstractComponent2";
export * from "./abstractPureComponent";
export * from "./abstractPureComponent2";
export * from "./alignment";
export * from "./boundary";
export * from "./colors";
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

import classNames from "classnames";
import * as React from "react";
import { polyfill } from "react-lifecycles-compat";

import { AbstractPureComponent, Classes, DISPLAYNAME_PREFIX, Intent, IProps, MaybeElement } from "../../common";
import { AbstractPureComponent2, Classes, DISPLAYNAME_PREFIX, Intent, IProps, MaybeElement } from "../../common";
import {
ALERT_WARN_CANCEL_ESCAPE_KEY,
ALERT_WARN_CANCEL_OUTSIDE_CLICK,
Expand Down Expand Up @@ -117,7 +118,8 @@ export interface IAlertProps extends IOverlayLifecycleProps, IProps {
onClose?(confirmed: boolean, evt?: React.SyntheticEvent<HTMLElement>): void;
}

export class Alert extends AbstractPureComponent<IAlertProps, {}> {
@polyfill
export class Alert extends AbstractPureComponent2<IAlertProps, {}> {
public static defaultProps: IAlertProps = {
canEscapeKeyCancel: false,
canOutsideClickCancel: false,
Expand Down
9 changes: 4 additions & 5 deletions packages/core/src/components/breadcrumbs/breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@

import classNames from "classnames";
import * as React from "react";
import { polyfill } from "react-lifecycles-compat";

import { Boundary } from "../../common/boundary";
import * as Classes from "../../common/classes";
import { Position } from "../../common/position";
import { IProps } from "../../common/props";
import { AbstractPureComponent2, Boundary, Classes, IProps, Position } from "../../common";
import { Menu } from "../menu/menu";
import { MenuItem } from "../menu/menuItem";
import { IOverflowListProps, OverflowList } from "../overflow-list/overflowList";
Expand Down Expand Up @@ -76,7 +74,8 @@ export interface IBreadcrumbsProps extends IProps {
popoverProps?: IPopoverProps;
}

export class Breadcrumbs extends React.PureComponent<IBreadcrumbsProps> {
@polyfill
export class Breadcrumbs extends AbstractPureComponent2<IBreadcrumbsProps> {
public static defaultProps: Partial<IBreadcrumbsProps> = {
collapseFrom: Boundary.START,
};
Expand Down
16 changes: 6 additions & 10 deletions packages/core/src/components/button/abstractButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@
import classNames from "classnames";
import * as React from "react";

import { Alignment } from "../../common/alignment";
import * as Classes from "../../common/classes";
import * as Keys from "../../common/keys";
import { IActionProps, MaybeElement } from "../../common/props";
import { isReactNodeEmpty, safeInvoke } from "../../common/utils";
import { AbstractPureComponent2, Alignment, Classes, IActionProps, Keys, MaybeElement, Utils } from "../../common";
import { Icon, IconName } from "../icon/icon";
import { Spinner } from "../spinner/spinner";

Expand Down Expand Up @@ -79,7 +75,7 @@ export interface IButtonState {
isActive: boolean;
}

export abstract class AbstractButton<H extends React.HTMLAttributes<any>> extends React.PureComponent<
export abstract class AbstractButton<H extends React.HTMLAttributes<any>> extends AbstractPureComponent2<
IButtonProps & H,
IButtonState
> {
Expand All @@ -91,7 +87,7 @@ export abstract class AbstractButton<H extends React.HTMLAttributes<any>> extend
protected refHandlers = {
button: (ref: HTMLElement) => {
this.buttonRef = ref;
safeInvoke(this.props.elementRef, ref);
Utils.safeInvoke(this.props.elementRef, ref);
},
};

Expand Down Expand Up @@ -142,7 +138,7 @@ export abstract class AbstractButton<H extends React.HTMLAttributes<any>> extend
}
}
this.currentKeyDown = e.which;
safeInvoke(this.props.onKeyDown, e);
Utils.safeInvoke(this.props.onKeyDown, e);
};

protected handleKeyUp = (e: React.KeyboardEvent<any>) => {
Expand All @@ -151,15 +147,15 @@ export abstract class AbstractButton<H extends React.HTMLAttributes<any>> extend
this.buttonRef.click();
}
this.currentKeyDown = null;
safeInvoke(this.props.onKeyUp, e);
Utils.safeInvoke(this.props.onKeyUp, e);
};

protected renderChildren(): React.ReactNode {
const { children, icon, loading, rightIcon, text } = this.props;
return [
loading && <Spinner key="loading" className={Classes.BUTTON_SPINNER} size={Icon.SIZE_LARGE} />,
<Icon key="leftIcon" icon={icon} />,
(!isReactNodeEmpty(text) || !isReactNodeEmpty(children)) && (
(!Utils.isReactNodeEmpty(text) || !Utils.isReactNodeEmpty(children)) && (
<span key="text" className={Classes.BUTTON_TEXT}>
{text}
{children}
Expand Down
8 changes: 5 additions & 3 deletions packages/core/src/components/button/buttonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

import classNames from "classnames";
import * as React from "react";
import { Alignment } from "../../common/alignment";
import * as Classes from "../../common/classes";
import { polyfill } from "react-lifecycles-compat";

import { AbstractPureComponent2, Alignment, Classes } from "../../common";
import { DISPLAYNAME_PREFIX, HTMLDivProps, IProps } from "../../common/props";

export interface IButtonGroupProps extends IProps, HTMLDivProps {
Expand Down Expand Up @@ -56,7 +57,8 @@ export interface IButtonGroupProps extends IProps, HTMLDivProps {

// this component is simple enough that tests would be purely tautological.
/* istanbul ignore next */
export class ButtonGroup extends React.PureComponent<IButtonGroupProps, {}> {
@polyfill
export class ButtonGroup extends AbstractPureComponent2<IButtonGroupProps> {
public static displayName = `${DISPLAYNAME_PREFIX}.ButtonGroup`;

public render() {
Expand Down
18 changes: 14 additions & 4 deletions packages/core/src/components/callout/callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,20 @@

import classNames from "classnames";
import * as React from "react";
import { polyfill } from "react-lifecycles-compat";

import { Classes, DISPLAYNAME_PREFIX, HTMLDivProps, IIntentProps, Intent, IProps, MaybeElement } from "../../common";
import { Icon } from "../../index";
import {
AbstractPureComponent2,
Classes,
DISPLAYNAME_PREFIX,
HTMLDivProps,
IIntentProps,
Intent,
IProps,
MaybeElement,
} from "../../common";
import { H4 } from "../html/html";
import { IconName } from "../icon/icon";
import { Icon, IconName } from "../icon/icon";

/** This component also supports the full range of HTML `<div>` props. */
export interface ICalloutProps extends IIntentProps, IProps, HTMLDivProps {
Expand Down Expand Up @@ -50,7 +59,8 @@ export interface ICalloutProps extends IIntentProps, IProps, HTMLDivProps {
title?: string;
}

export class Callout extends React.PureComponent<ICalloutProps, {}> {
@polyfill
export class Callout extends AbstractPureComponent2<ICalloutProps> {
public static displayName = `${DISPLAYNAME_PREFIX}.Callout`;

public render() {
Expand Down
Loading