diff --git a/.eslintrc.js b/.eslintrc.js index 4ece48c8fd..3b4c5f7a67 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,7 +21,7 @@ module.exports = { }, sourceType: 'module', }, - plugins: ['react', 'flowtype', 'import', 'prettier'], + plugins: ['babel', 'react', 'flowtype', 'import', 'prettier'], rules: { // Plugin rules: 'import/no-duplicates': 'error', @@ -66,7 +66,9 @@ module.exports = { 'no-extra-bind': 'error', 'no-extra-label': 'error', 'no-implied-eval': 'error', - 'no-invalid-this': 'error', + // We use the version from the babel plugin so that `this` in a function + // class property doesn't give a false positive. + 'babel/no-invalid-this': 'error', 'no-return-await': 'error', 'no-self-compare': 'error', 'no-throw-literal': 'error', diff --git a/package.json b/package.json index 4c093368f7..093d524763 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,9 @@ "polyfill": false, "regenerator": true } + ], + [ + "transform-class-properties" ] ] }, @@ -99,6 +102,7 @@ "babel-eslint": "^7.2.3", "babel-jest": "^20.0.3", "babel-loader": "^7.1.2", + "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-runtime": "^6.23.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.6.1", @@ -109,6 +113,7 @@ "devtools-license-check": "^0.5.0", "eslint": "^4.12.0", "eslint-config-prettier": "^2.4.0", + "eslint-plugin-babel": "^4.1.2", "eslint-plugin-flowtype": "^2.39.1", "eslint-plugin-import": "^2.8.0", "eslint-plugin-prettier": "^2.2.0", diff --git a/src/components/app/FooterLinks.js b/src/components/app/FooterLinks.js index c5ee1bb789..28845bf07d 100644 --- a/src/components/app/FooterLinks.js +++ b/src/components/app/FooterLinks.js @@ -9,6 +9,10 @@ require('./FooterLinks.css'); type State = {| hide: boolean |}; class FooterLinks extends PureComponent<{||}, State> { + _onClick = () => { + this.setState({ hide: true }); + }; + constructor() { super(); this.state = { @@ -26,7 +30,7 @@ class FooterLinks extends PureComponent<{||}, State> { aria-label="Hide links to legal information" title="Hide links to legal information" className="appFooterLinksClose" - onClick={() => this.setState({ hide: true })} + onClick={this._onClick} > ✕ diff --git a/src/components/app/Home.js b/src/components/app/Home.js index 5bed2a8842..67cb09ba3e 100644 --- a/src/components/app/Home.js +++ b/src/components/app/Home.js @@ -19,6 +19,15 @@ const ADDON_URL = const LEGACY_ADDON_URL = 'https://raw.githubusercontent.com/devtools-html/Gecko-Profiler-Addon/master/gecko_profiler_legacy.xpi'; +function onInstallClick(e: SyntheticEvent) { + if (window.InstallTrigger) { + const name = e.currentTarget.dataset.name; + const xpiUrl = e.currentTarget.href; + window.InstallTrigger.install({ [name]: xpiUrl }); + } + e.preventDefault(); +} + const InstallButton = ({ name, xpiUrl, @@ -29,12 +38,8 @@ const InstallButton = ({ { - if (window.InstallTrigger) { - window.InstallTrigger.install({ [name]: xpiUrl }); - } - e.preventDefault(); - }} + data-name={name} + onClick={onInstallClick} > {children} @@ -54,6 +59,7 @@ type UploadButtonProps = { class UploadButton extends React.PureComponent { _input: HTMLInputElement | null; + _takeInputRef = input => (this._input = input); constructor(props: UploadButtonProps) { super(props); @@ -63,13 +69,7 @@ class UploadButton extends React.PureComponent { render() { return (
- { - this._input = input; - }} - onChange={this._upload} - /> +
); } diff --git a/src/components/calltree/CallTree.js b/src/components/calltree/CallTree.js index 343da8ec65..52a38fb2d8 100644 --- a/src/components/calltree/CallTree.js +++ b/src/components/calltree/CallTree.js @@ -60,6 +60,7 @@ class CallTreeComponent extends PureComponent { _appendageColumn: Column; _appendageButtons: string[]; _treeView: TreeView | null; + _takeTreeViewRef = treeView => (this._treeView = treeView); constructor(props: Props) { super(props); @@ -188,9 +189,7 @@ class CallTreeComponent extends PureComponent { disableOverscan={disableOverscan} appendageButtons={this._appendageButtons} onAppendageButtonClick={this._onAppendageButtonClick} - ref={ref => { - this._treeView = ref; - }} + ref={this._takeTreeViewRef} contextMenuId={'ProfileCallTreeContextMenu'} icons={this.props.icons} /> diff --git a/src/components/header/ThreadStackGraph.js b/src/components/header/ThreadStackGraph.js index 74f4c58592..56afb24ab2 100644 --- a/src/components/header/ThreadStackGraph.js +++ b/src/components/header/ThreadStackGraph.js @@ -10,6 +10,8 @@ import { getSampleCallNodes } from '../../profile-logic/profile-data'; import { BLUE_70, BLUE_40 } from 'photon-colors'; class ThreadStackGraph extends PureComponent { + _takeCanvasRef = canvas => (this._canvas = canvas); + constructor(props) { super(props); this._resizeListener = () => this.forceUpdate(); @@ -146,7 +148,7 @@ class ThreadStackGraph extends PureComponent { `${this.props.className}Canvas`, 'threadStackGraphCanvas' )} - ref={ref => (this._canvas = ref)} + ref={this._takeCanvasRef} onMouseUp={this._onMouseUp} /> diff --git a/src/components/marker-table/index.js b/src/components/marker-table/index.js index 90b810b448..1e32969168 100644 --- a/src/components/marker-table/index.js +++ b/src/components/marker-table/index.js @@ -115,6 +115,8 @@ class MarkerTree { } class MarkerTable extends PureComponent { + _takeTreeViewRef = treeView => (this._treeView = treeView); + constructor(props) { super(props); this._fixedColumns = [ @@ -155,7 +157,7 @@ class MarkerTable extends PureComponent { onExpandedNodesChange={this._onExpandedNodeIdsChange} selectedNodeId={selectedMarker} expandedNodeIds={this._expandedNodeIds} - ref={ref => (this._treeView = ref)} + ref={this._takeTreeViewRef} contextMenuId={'MarkersContextMenu'} /> diff --git a/src/components/shared/IdleSearchField.js b/src/components/shared/IdleSearchField.js index 1e7919b3fc..bd8ba0b9fc 100644 --- a/src/components/shared/IdleSearchField.js +++ b/src/components/shared/IdleSearchField.js @@ -97,6 +97,10 @@ class IdleSearchField extends PureComponent { } } + _onFormSubmit(e: SyntheticEvent) { + e.preventDefault(); + } + componentWillReceiveProps(nextProps: Props) { if (nextProps.defaultValue !== this.props.defaultValue) { this._notifyIfChanged(nextProps.defaultValue || ''); @@ -111,7 +115,7 @@ class IdleSearchField extends PureComponent { return (
e.preventDefault()} + onSubmit={this._onFormSubmit} > { _specialItems: (IndexIntoCallNodeTable | null)[]; _visibleRows: IndexIntoCallNodeTable[]; _list: VirtualList | null; + _takeListRef = (list: VirtualList | null) => (this._list = list); constructor(props: TreeViewProps) { super(props); @@ -556,9 +557,7 @@ class TreeView extends React.PureComponent { specialItems={this._specialItems} disableOverscan={disableOverscan} onCopy={this._onCopy} - ref={ref => { - this._list = ref; - }} + ref={this._takeListRef} /> {contextMenu} diff --git a/src/components/shared/chart/Viewport.js b/src/components/shared/chart/Viewport.js index cb0481c329..ac8d82899e 100644 --- a/src/components/shared/chart/Viewport.js +++ b/src/components/shared/chart/Viewport.js @@ -116,7 +116,8 @@ export default function withChartViewport( shiftScrollId: number; zoomRangeSelectionScheduled: boolean; zoomRangeSelectionScrollDelta: number; - _container: ?HTMLElement; + _container: HTMLElement | null; + _takeContainerRef = container => (this._container = container); constructor(props: Props) { super(props); @@ -131,6 +132,7 @@ export default function withChartViewport( this.shiftScrollId = 0; this.zoomRangeSelectionScheduled = false; this.zoomRangeSelectionScrollDelta = 0; + this._container = null; this.state = this.getDefaultState(props); } @@ -477,9 +479,7 @@ export default function withChartViewport( className={viewportClassName} onWheel={this._mouseWheelListener} onMouseDown={this._mouseDownListener} - ref={container => { - this._container = container; - }} + ref={this._takeContainerRef} > @@ -386,6 +387,7 @@ exports[`app/Home renders the install screen for the legacy add-on 1`] = ` diff --git a/yarn.lock b/yarn.lock index c89ad473d7..c3a8414117 100644 --- a/yarn.lock +++ b/yarn.lock @@ -593,6 +593,10 @@ babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + babel-plugin-syntax-exponentiation-operator@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" @@ -621,6 +625,15 @@ babel-plugin-transform-async-to-generator@^6.22.0: babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.22.0" +babel-plugin-transform-class-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" @@ -2354,6 +2367,10 @@ eslint-module-utils@^2.1.1: debug "^2.6.8" pkg-dir "^1.0.0" +eslint-plugin-babel@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-4.1.2.tgz#79202a0e35757dd92780919b2336f1fa2fe53c1e" + eslint-plugin-flowtype@^2.39.1: version "2.39.1" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.39.1.tgz#b5624622a0388bcd969f4351131232dcb9649cd5"