Bug 1598320 - Add types to the performance-new components; r=julienw

Differential Revision: https://phabricator.services.mozilla.com/D54771

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Greg Tatum 2019-12-04 22:22:42 +00:00
Родитель 246f170a3d
Коммит 086d2b5725
10 изменённых файлов: 220 добавлений и 93 удалений

Просмотреть файл

@ -0,0 +1,8 @@
"use strict";
module.exports = {
rules: {
// Props are checked by TypeScript, so we don't need dynamic type checking here.
"react/prop-types": "off"
}
};

Просмотреть файл

@ -16,6 +16,7 @@ export interface PanelWindow {
gTarget?: any;
gInit(perfFront: any, preferenceFront: any): void;
gDestroy(): void;
gReportReady?(): void
}
/**
@ -55,6 +56,8 @@ export interface PerfFront {
isActive: () => MaybePromise<boolean>;
isSupportedPlatform: () => MaybePromise<boolean>;
isLockedForPrivateBrowsing: () => MaybePromise<boolean>;
on: (type: string, listener: () => void) => void;
off: (type: string, listener: () => void) => void;
/**
* This method was was added in Firefox 72.
*/
@ -356,3 +359,17 @@ export interface PerformancePref {
export interface PopupWindow extends Window {
gResizePopup?: (height: number) => void;
}
/**
* Scale a number value.
*/
export type NumberScaler = (value: number) => number;
/**
* A collection of functions to scale numbers.
*/
export interface ScaleFunctions {
fromFractionToValue: NumberScaler,
fromValueToFraction: NumberScaler,
fromFractionToSingleDigitValue: NumberScaler,
}

Просмотреть файл

@ -1,6 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @ts-check
/**
* @typedef {{}} Props - This is an empty object.
*/
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
@ -13,25 +19,34 @@ const {
/**
* This component provides a helpful description for what is going on in the component
* and provides some external links.
* @extends {React.PureComponent<Props>}
*/
class Description extends PureComponent {
static get propTypes() {
return {};
}
/**
* @param {Props} props
*/
constructor(props) {
super(props);
this.handleLinkClick = this.handleLinkClick.bind(this);
}
/**
* @param {React.MouseEvent<HTMLButtonElement>} event
*/
handleLinkClick(event) {
const { openDocLink } = require("devtools/client/shared/link");
openDocLink(event.target.value);
/** @type HTMLButtonElement */
const target = /** @type {any} */ (event.target);
openDocLink(target.value, {});
}
/**
* Implement links as buttons to avoid any risk of loading the link in the
* the panel.
* @param {string} href
* @param {string} text
*/
renderLink(href, text) {
return button(

Просмотреть файл

@ -1,7 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @ts-check
/**
* @typedef {Object} Props
* @property {string[]} dirs
* @property {() => void} onAdd
* @property {(index: number) => void} onRemove
*/
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const {
div,
@ -12,29 +22,24 @@ const {
const {
withCommonPathPrefixRemoved,
} = require("devtools/client/performance-new/utils");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
// A list of directories with add and remove buttons.
// Looks like this:
//
// +---------------------------------------------+
// | code/obj-m-android-opt |
// | code/obj-m-android-debug |
// | test/obj-m-test |
// | |
// +---------------------------------------------+
//
// [+] [-]
/**
* A list of directories with add and remove buttons.
* Looks like this:
*
* +---------------------------------------------+
* | code/obj-m-android-opt |
* | code/obj-m-android-debug |
* | test/obj-m-test |
* | |
* +---------------------------------------------+
*
* [+] [-]
*
* @extends {React.PureComponent<Props>}
*/
class DirectoryPicker extends PureComponent {
static get propTypes() {
return {
dirs: PropTypes.array.isRequired,
onAdd: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
};
}
/** @param {Props} props */
constructor(props) {
super(props);
this._listBox = null;
@ -43,6 +48,9 @@ class DirectoryPicker extends PureComponent {
this._handleRemoveButtonClick = this._handleRemoveButtonClick.bind(this);
}
/**
* @param {HTMLSelectElement} element
*/
_takeListBoxRef(element) {
this._listBox = element;
}
@ -64,7 +72,7 @@ class DirectoryPicker extends PureComponent {
select(
{
className: "perf-settings-dir-list",
size: "4",
size: 4,
ref: this._takeListBoxRef,
},
dirs.map((fullPath, i) =>

Просмотреть файл

@ -1,6 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @ts-check
/**
* @template P
* @typedef {import("react-redux").ResolveThunks<P>} ResolveThunks<P>
*/
/**
* @typedef {Object} StateProps
* @property {PerfFront} perfFront
* @property {RecordingState} recordingState
* @property {boolean?} isSupportedPlatform
* @property {boolean?} isPopup
* @property {string | null} promptEnvRestart
*/
/**
* @typedef {Object} ThunkDispatchProps
* @property {typeof actions.changeRecordingState} changeRecordingState
* @property {typeof actions.reportProfilerReady} reportProfilerReady
*/
/**
* @typedef {ResolveThunks<ThunkDispatchProps>} DispatchProps
* @typedef {StateProps & DispatchProps} Props
* @typedef {import("../@types/perf").PerfFront} PerfFront
* @typedef {import("../@types/perf").RecordingState} RecordingState
* @typedef {import("../@types/perf").State} StoreState
*/
/**
* @typedef {import("../@types/perf").PanelWindow} PanelWindow
*/
"use strict";
const {
@ -12,7 +46,6 @@ const {
div,
button,
} = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const RecordingButton = createFactory(
require("devtools/client/performance-new/components/RecordingButton.js")
);
@ -38,23 +71,11 @@ const {
*
* 2) It mounts all of the sub components, but is itself very light on actual
* markup for presentation.
*
* @extends {React.PureComponent<Props>}
*/
class Perf extends PureComponent {
static get propTypes() {
return {
// StateProps:
perfFront: PropTypes.object.isRequired,
recordingState: PropTypes.string.isRequired,
isSupportedPlatform: PropTypes.bool,
isPopup: PropTypes.bool,
promptEnvRestart: PropTypes.string,
// DispatchProps:
changeRecordingState: PropTypes.func.isRequired,
reportProfilerReady: PropTypes.func.isRequired,
};
}
/** @param {Props} props */
constructor(props) {
super(props);
this.handleProfilerStarting = this.handleProfilerStarting.bind(this);
@ -103,8 +124,12 @@ class Perf extends PureComponent {
// it will show. This defers the initial visibility of the popup until the
// React components have fully rendered, and thus there is no annoying "blip"
// to the screen when the page goes from fully blank, to showing the content.
if (window.gReportReady) {
window.gReportReady();
/** @type {any} */
const anyWindow = window;
/** @type {PanelWindow} - Coerce the window into the PanelWindow. */
const { gReportReady } = anyWindow;
if (gReportReady) {
gReportReady();
}
});
@ -302,6 +327,10 @@ class Perf extends PureComponent {
}
}
/**
* @param {StoreState} state
* @returns {StateProps}
*/
function mapStateToProps(state) {
return {
perfFront: selectors.getPerfFront(state),
@ -312,6 +341,7 @@ function mapStateToProps(state) {
};
}
/** @type {ThunkDispatchProps} */
const mapDispatchToProps = {
changeRecordingState: actions.changeRecordingState,
reportProfilerReady: actions.reportProfilerReady,

Просмотреть файл

@ -1,9 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @ts-check
/**
* @typedef {import("../@types/perf").ScaleFunctions} ScaleFunctions
*/
/**
* @typedef {Object} Props
* @property {number} value
* @property {string} label
* @property {string} id
* @property {ScaleFunctions} scale
* @property {(value: number) => unknown} onChange
* @property {(value: number) => React.ReactNode} display
*/
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
div,
input,
@ -12,28 +26,22 @@ const {
/**
* Provide a numeric range slider UI that works off of custom numeric scales.
* @extends React.PureComponent<Props>
*/
class Range extends PureComponent {
static get propTypes() {
return {
value: PropTypes.number.isRequired,
label: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
scale: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
display: PropTypes.func.isRequired,
};
}
/** @param {Props} props */
constructor(props) {
super(props);
this.handleInput = this.handleInput.bind(this);
}
handleInput(e) {
e.preventDefault();
/**
* @param {React.ChangeEvent<HTMLInputElement>} event
*/
handleInput(event) {
event.preventDefault();
const { scale, onChange } = this.props;
const frac = e.target.value / 100;
const frac = Number(event.target.value) / 100;
onChange(scale.fromFractionToSingleDigitValue(frac));
}

Просмотреть файл

@ -1,6 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @ts-check
/**
* @template P
* @typedef {import("react-redux").ResolveThunks<P>} ResolveThunks<P>
*/
/**
* @typedef {Object} StateProps
* @property {RecordingState} recordingState
* @property {boolean} isSupportedPlatform
* @property {boolean} recordingUnexpectedlyStopped
* @property {boolean} isPopup
*/
/**
* @typedef {Object} ThunkDispatchProps
* @property {typeof actions.startRecording} startRecording
* @property {typeof actions.getProfileAndStopProfiler} getProfileAndStopProfiler
* @property {typeof actions.stopProfilerAndDiscardProfile} stopProfilerAndDiscardProfile
*/
/**
* @typedef {ResolveThunks<ThunkDispatchProps>} DispatchProps
* @typedef {StateProps & DispatchProps} Props
* @typedef {import("../@types/perf").RecordingState} RecordingState
* @typedef {import("../@types/perf").State} StoreState
*/
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
@ -10,7 +40,6 @@ const {
span,
img,
} = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const actions = require("devtools/client/performance-new/store/actions");
const selectors = require("devtools/client/performance-new/store/selectors");
@ -19,29 +48,30 @@ const selectors = require("devtools/client/performance-new/store/selectors");
* This component is not responsible for the full life cycle of recording a profile. It
* is only responsible for the actual act of stopping and starting recordings. It
* also reacts to the changes of the recording state from external changes.
*
* @extends {React.PureComponent<Props>}
*/
class RecordingButton extends PureComponent {
static get propTypes() {
return {
// StateProps
recordingState: PropTypes.string.isRequired,
isSupportedPlatform: PropTypes.bool,
recordingUnexpectedlyStopped: PropTypes.bool.isRequired,
isPopup: PropTypes.bool.isRequired,
// DispatchProps
startRecording: PropTypes.func.isRequired,
getProfileAndStopProfiler: PropTypes.func.isRequired,
stopProfilerAndDiscardProfile: PropTypes.func.isRequired,
};
}
/** @param {Props} props */
constructor(props) {
super(props);
this._getProfileAndStopProfiler = () =>
this.props.getProfileAndStopProfiler(window);
}
/** @param {{
* disabled?: boolean,
* label?: React.ReactNode,
* onClick?: any,
* additionalMessage?: React.ReactNode,
* isPrimary?: boolean,
* isPopup?: boolean,
* additionalButton?: {
* label: string,
* onClick: any,
* },
* }} buttonSettings
*/
renderButton(buttonSettings) {
const {
disabled,
@ -70,7 +100,6 @@ class RecordingButton extends PureComponent {
button(
{
className: `perf-photon-button perf-photon-button-${buttonClass} perf-button`,
"data-standalone": true,
disabled,
onClick,
},
@ -80,7 +109,6 @@ class RecordingButton extends PureComponent {
? button(
{
className: `perf-photon-button perf-photon-button-default perf-button`,
"data-standalone": true,
onClick: additionalButton.onClick,
disabled,
},
@ -184,6 +212,10 @@ class RecordingButton extends PureComponent {
}
}
/**
* @param {StoreState} state
* @returns {StateProps}
*/
function mapStateToProps(state) {
return {
recordingState: selectors.getRecordingState(state),
@ -195,6 +227,7 @@ function mapStateToProps(state) {
};
}
/** @type {ThunkDispatchProps} */
const mapDispatchToProps = {
startRecording: actions.startRecording,
stopProfilerAndDiscardProfile: actions.stopProfilerAndDiscardProfile,

Просмотреть файл

@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @ts-check
/* eslint-disable react/prop-types */
/**
* @typedef {Object} StateProps
@ -34,7 +33,6 @@
*/
/**
* @typedef {import("react")} React
* @typedef {import("../@types/perf").PopupWindow} PopupWindow
* @typedef {import("../@types/perf").State} StoreState
* @typedef {StateProps & DispatchProps} Props

Просмотреть файл

@ -2,6 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @ts-check
/**
* @typedef {import("./@types/perf").NumberScaler} NumberScaler
* @typedef {import("./@types/perf").ScaleFunctions} ScaleFunctions
*/
"use strict";
// @ts-ignore
@ -64,25 +68,13 @@ function formatFileSize(num) {
return (neg ? "-" : "") + numStr + " " + unit;
}
/**
* Scale a number value.
*
* @callback NumberScaler
* @param {number} value
* @returns {number}
*/
/**
* Creates numbers that scale exponentially.
*
* @param {number} rangeStart
* @param {number} rangeEnd
*
* @returns {{
* fromFractionToValue: NumberScaler,
* fromValueToFraction: NumberScaler,
* fromFractionToSingleDigitValue: NumberScaler,
* }}
* @returns {ScaleFunctions}
*/
function makeExponentialScale(rangeStart, rangeEnd) {
const startExp = Math.log(rangeStart);

Просмотреть файл

@ -244,6 +244,24 @@ class ActorReadyGeckoProfilerInterface {
}
return Services.profiler.GetFeatures();
}
/**
* @param {string} type
* @param {() => void} listener
*/
on(type, listener) {
// This is a stub for TypeScript. This function is assigned by the EventEmitter
// decorator.
}
/**
* @param {string} type
* @param {() => void} listener
*/
off(type, listener) {
// This is a stub for TypeScript. This function is assigned by the EventEmitter
// decorator.
}
}
exports.ActorReadyGeckoProfilerInterface = ActorReadyGeckoProfilerInterface;