Improve typing in various UI components. (#4842)

This commit is contained in:
John Chadwick 2022-06-08 15:17:02 +00:00 committed by GitHub
parent f1080d6029
commit 325abe1d00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 109 additions and 94 deletions

View File

@ -6,8 +6,8 @@ import { debounce } from '../../../common/misc';
interface Props {
onChange: (value: string) => void;
onFocus?: Function;
onBlur?: Function;
onFocus?: (e: React.FocusEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
onBlur?: (e: React.FocusEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
textarea?: boolean;
delay?: number;
placeholder?: string;

View File

@ -19,7 +19,7 @@ interface Props {
@autoBindMethodsForReact(AUTOBIND_CFG)
export class DropdownItem extends PureComponent<Props> {
_handleClick(e) {
_handleClick(e: React.MouseEvent) {
const { stayOpenAfterClick, onClick, disabled } = this.props;
if (stayOpenAfterClick) {

View File

@ -60,7 +60,7 @@ export class Dropdown extends PureComponent<DropdownProps, State> {
this._node = n;
}
_handleCheckFilterSubmit(e) {
_handleCheckFilterSubmit(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.key === 'Enter') {
// Listen for the Enter key and "click" on the active list item
const selector = `li[data-filter-index="${this.state.filterActiveIndex}"] button`;
@ -232,7 +232,7 @@ export class Dropdown extends PureComponent<DropdownProps, State> {
}
}
_handleClick(e) {
_handleClick(e: React.MouseEvent<HTMLDivElement>) {
e.preventDefault();
e.stopPropagation();
this.toggle();
@ -256,7 +256,8 @@ export class Dropdown extends PureComponent<DropdownProps, State> {
}
}
_getFlattenedChildren(children) {
// TODO: children should not be 'any'.
_getFlattenedChildren(children: any) {
let newChildren: ReactNode[] = [];
// Ensure children is an array
children = Array.isArray(children) ? children : [children];

View File

@ -16,9 +16,9 @@ interface Props {
id?: string;
type?: string;
mode?: string;
onBlur?: (e: FocusEvent) => void;
onKeyDown?: (e: KeyboardEvent, value?: any) => void;
onFocus?: (e: FocusEvent) => void;
onBlur?: (e: FocusEvent | React.FocusEvent) => void;
onKeyDown?: (e: KeyboardEvent | React.KeyboardEvent, value?: any) => void;
onFocus?: (e: FocusEvent | React.FocusEvent) => void;
onChange?: CodeEditorOnChange;
onPaste?: (e: ClipboardEvent) => void;
getAutocompleteConstants?: () => string[] | PromiseLike<string[]>;
@ -161,8 +161,9 @@ export class OneLineEditor extends PureComponent<Props, State> {
this._convertToInputIfNotFocused();
}
_handleEditorFocus(e) {
const focusedFromTabEvent = !!e.sourceCapabilities;
_handleEditorFocus(e: FocusEvent) {
// TODO: unclear why this is missing in TypeScript DOM.
const focusedFromTabEvent = !!(e as any).sourceCapabilities;
if (focusedFromTabEvent) {
this._editor?.focusEnd();
@ -179,7 +180,7 @@ export class OneLineEditor extends PureComponent<Props, State> {
this.props.onFocus?.(e);
}
_handleInputFocus(e) {
_handleInputFocus(e: React.FocusEvent<HTMLInputElement>) {
// If we're focusing the whole thing, blur the input. This happens when
// the user tabs to the field.
const start = this._input?.getSelectionStart();
@ -203,19 +204,19 @@ export class OneLineEditor extends PureComponent<Props, State> {
this.props.onFocus?.(e);
}
_handleInputChange(value) {
_handleInputChange(value: string) {
this._convertToEditorPreserveFocus();
this.props.onChange?.(value);
}
_handleInputKeyDown(event) {
_handleInputKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
if (this.props.onKeyDown) {
this.props.onKeyDown(event, event.target.value);
this.props.onKeyDown(event, event.currentTarget.value);
}
}
_handleInputBlur(e: FocusEvent) {
_handleInputBlur(e: React.FocusEvent<HTMLTextAreaElement | HTMLInputElement>) {
// Set focused state
this._input?.removeAttribute('data-focused');
@ -245,10 +246,11 @@ export class OneLineEditor extends PureComponent<Props, State> {
}
// @TODO Refactor this event handler. The way we search for a parent form node is not stable.
_handleKeyDown(event) {
_handleKeyDown(event: KeyboardEvent) {
// submit form if needed
if (event.keyCode === 13) {
let node = event.target;
// TODO: This can be NULL, or not an HTMLElement.
let node = event.target as HTMLElement;
for (let i = 0; i < 20 && node; i++) {
if (node.tagName === 'FORM') {
@ -258,7 +260,8 @@ export class OneLineEditor extends PureComponent<Props, State> {
break;
}
node = node.parentNode;
// TODO: This can be NULL.
node = node.parentNode as HTMLElement;
}
}
@ -310,7 +313,7 @@ export class OneLineEditor extends PureComponent<Props, State> {
return;
}
if (this._mayContainNunjucks(this.getValue())) {
if (this._mayContainNunjucks(this.getValue() || '')) {
return;
}
@ -327,7 +330,7 @@ export class OneLineEditor extends PureComponent<Props, State> {
this._input = n;
}
_mayContainNunjucks(text) {
_mayContainNunjucks(text: string) {
// Not sure, but sometimes this isn't a string
if (typeof text !== 'string') {
return false;

View File

@ -12,7 +12,7 @@ const LOCALSTORAGE_KEY = 'insomnia.httpMethods';
const GRPC_LABEL = 'gRPC';
interface Props {
onChange: Function;
onChange: (method: string) => void;
method: string;
right?: boolean;
showGrpc?: boolean;
@ -28,12 +28,11 @@ export class MethodDropdown extends PureComponent<Props> {
}
_handleSetCustomMethod() {
let recentMethods;
let recentMethods: string[];
try {
const v = window.localStorage.getItem(LOCALSTORAGE_KEY);
// @ts-expect-error -- TSCONVERSION don't try parse if no item found
recentMethods = JSON.parse(v) || [];
recentMethods = v ? JSON.parse(v) || [] : [];
} catch (err) {
recentMethods = [];
}
@ -74,7 +73,7 @@ export class MethodDropdown extends PureComponent<Props> {
});
}
_handleChange(method) {
_handleChange(method: string) {
this.props.onChange(method);
}

View File

@ -10,7 +10,7 @@ import { DropdownButton } from '../base/dropdown/dropdown-button';
import { DropdownItem } from '../base/dropdown/dropdown-item';
import { Lazy } from '../base/lazy';
import { PromptButton } from '../base/prompt-button';
import { Row } from './row';
import { AutocompleteHandler, Pair, Row } from './row';
const NAME = 'name';
const VALUE = 'value';
@ -25,8 +25,8 @@ const RIGHT = 39;
interface Props {
onChange: Function;
pairs: any[];
handleGetAutocompleteNameConstants?: Function;
handleGetAutocompleteValueConstants?: Function;
handleGetAutocompleteNameConstants?: AutocompleteHandler;
handleGetAutocompleteValueConstants?: AutocompleteHandler;
allowFile?: boolean;
allowMultiline?: boolean;
sortable?: boolean;
@ -83,10 +83,10 @@ export class KeyValueEditor extends PureComponent<Props, State> {
}
}
_handlePairChange(pair) {
_handlePairChange(pair: Pair) {
const i = this._getPairIndex(pair);
const pairs = [
const pairs: Pair[] = [
...this.state.pairs.slice(0, i),
Object.assign({}, pair),
...this.state.pairs.slice(i + 1),
@ -99,7 +99,7 @@ export class KeyValueEditor extends PureComponent<Props, State> {
this._onChange([]);
}
_handleMove(pairToMove, pairToTarget, targetOffset) {
_handleMove(pairToMove: Pair, pairToTarget: Pair, targetOffset: 1 | -1) {
if (pairToMove.id === pairToTarget.id) {
// Nothing to do
return;
@ -122,7 +122,7 @@ export class KeyValueEditor extends PureComponent<Props, State> {
this._onChange(pairs);
}
_handlePairDelete(pair) {
_handlePairDelete(pair: Pair) {
const i = this.state.pairs.findIndex(p => p.id === pair.id);
this._deletePair(i, true);
@ -140,28 +140,31 @@ export class KeyValueEditor extends PureComponent<Props, State> {
this._focusedField = null;
}
_handleFocusName(pair) {
_handleFocusName(pair: Pair) {
this._setFocusedPair(pair);
this._focusedField = NAME;
this._rows[pair.id].focusNameEnd();
// TODO: this may be a bug; pair.id is not an index into _rows
this._rows[pair.id as any].focusNameEnd();
}
_handleFocusValue(pair) {
_handleFocusValue(pair: Pair) {
this._setFocusedPair(pair);
this._focusedField = VALUE;
this._rows[pair.id].focusValueEnd();
// TODO: this may be a bug; pair.id is not an index into _rows
this._rows[pair.id as any].focusValueEnd();
}
_handleFocusDescription(pair) {
_handleFocusDescription(pair: Pair) {
this._setFocusedPair(pair);
this._focusedField = DESCRIPTION;
this._rows[pair.id].focusDescriptionEnd();
// TODO: this may be a bug; pair.id is not an index into _rows
this._rows[pair.id as any].focusDescriptionEnd();
}
_handleAddFromName() {
@ -183,7 +186,7 @@ export class KeyValueEditor extends PureComponent<Props, State> {
this._addPair();
}
_handleKeyDown(_pair, e, value) {
_handleKeyDown(_pair: Pair, e: KeyboardEvent, value?: any) {
if (e.metaKey || e.ctrlKey) {
return;
}
@ -209,7 +212,7 @@ export class KeyValueEditor extends PureComponent<Props, State> {
}
}
_onChange(pairs) {
_onChange(pairs: Pair[]) {
this.setState(
{
pairs,
@ -235,7 +238,7 @@ export class KeyValueEditor extends PureComponent<Props, State> {
}
position = position === undefined ? numPairs : position;
const pair = {
const pair: Pair = {
id: '',
name: '',
value: '',
@ -260,7 +263,7 @@ export class KeyValueEditor extends PureComponent<Props, State> {
this.props.onCreate?.();
}
_deletePair(position, breakFocus = false) {
_deletePair(position: number, breakFocus = false) {
if (this.props.disableDelete) {
return;
}
@ -385,7 +388,7 @@ export class KeyValueEditor extends PureComponent<Props, State> {
}
}
_getPairIndex(pair) {
_getPairIndex(pair: Pair) {
if (pair) {
return this.state.pairs.findIndex(p => p.id === pair.id);
} else {
@ -401,7 +404,7 @@ export class KeyValueEditor extends PureComponent<Props, State> {
return this.state.pairs.find(p => p.id === this._focusedPairId) || null;
}
_setFocusedPair(pair) {
_setFocusedPair(pair: Pair) {
if (pair) {
this._focusedPairId = pair.id;
} else {

View File

@ -2,7 +2,7 @@
import { autoBindMethodsForReact } from 'class-autobind-decorator';
import classnames from 'classnames';
import React, { forwardRef, ForwardRefRenderFunction, PureComponent } from 'react';
import { ConnectDragPreview, ConnectDragSource, ConnectDropTarget, DragSource, DropTarget } from 'react-dnd';
import { ConnectDragPreview, ConnectDragSource, ConnectDropTarget, DragSource, DropTarget, DropTargetMonitor } from 'react-dnd';
import ReactDOM from 'react-dom';
import { AUTOBIND_CFG } from '../../../common/constants';
@ -18,32 +18,39 @@ import { OneLineEditor } from '../codemirror/one-line-editor';
import { CodePromptModal } from '../modals/code-prompt-modal';
import { showModal } from '../modals/index';
export interface Pair {
id: string;
name: string;
value: string;
description: string;
fileName?: string;
type?: string;
disabled?: boolean;
multiline?: boolean | string;
}
export type AutocompleteHandler = (pair: Pair) => string[] | PromiseLike<string[]>;
type DragDirection = 0 | 1 | -1;
interface Props {
onChange: Function;
onDelete: Function;
onFocusName: Function;
onFocusValue: Function;
onFocusDescription: Function;
onChange: (pair: Pair) => void;
onDelete: (pair: Pair) => void;
onFocusName: (pair: Pair, e: FocusEvent) => void;
onFocusValue: (pair: Pair, e: FocusEvent) => void;
onFocusDescription: (pair: Pair, e: FocusEvent) => void;
displayDescription: boolean;
index: number;
pair: {
id: string;
name: string;
value: string;
description: string;
fileName: string;
type: string;
disabled: boolean;
};
pair: Pair;
readOnly?: boolean;
onMove?: Function;
onKeyDown?: Function;
onBlurName?: Function;
onBlurValue?: Function;
onBlurDescription?: Function;
onMove?: (pairToMove: Pair, pairToTarget: Pair, targetOffset: 1 | -1) => void;
onKeyDown?: (pair: Pair, e: KeyboardEvent, value?: any) => void;
onBlurName?: (pair: Pair, e: FocusEvent) => void;
onBlurValue?: (pair: Pair, e: FocusEvent) => void;
onBlurDescription?: (pair: Pair, e: FocusEvent) => void;
enableNunjucks?: boolean;
handleGetAutocompleteNameConstants?: Function;
handleGetAutocompleteValueConstants?: Function;
handleGetAutocompleteNameConstants?: AutocompleteHandler;
handleGetAutocompleteValueConstants?: AutocompleteHandler;
namePlaceholder?: string;
valuePlaceholder?: string;
descriptionPlaceholder?: string;
@ -66,7 +73,7 @@ interface Props {
}
interface State {
dragDirection: number;
dragDirection: DragDirection;
}
@autoBindMethodsForReact(AUTOBIND_CFG)
@ -94,7 +101,7 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
this._descriptionInput?.focusEnd();
}
setDragDirection(dragDirection) {
setDragDirection(dragDirection: DragDirection) {
if (dragDirection !== this.state.dragDirection) {
this.setState({
dragDirection,
@ -106,23 +113,23 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
this._descriptionInput = n;
}
_sendChange(patch) {
_sendChange(patch: Partial<Pair>) {
const pair = Object.assign({}, this.props.pair, patch);
this.props.onChange?.(pair);
}
_handleNameChange(name) {
_handleNameChange(name: string) {
this._sendChange({
name,
});
}
_handleValuePaste(e) {
_handleValuePaste(e: ClipboardEvent) {
if (!this.props.allowMultiline) {
return;
}
const value = e.clipboardData.getData('text/plain');
const value = e.clipboardData?.getData('text/plain');
if (value?.includes('\n')) {
e.preventDefault();
@ -147,25 +154,25 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
}
}
_handleValueChange(value) {
_handleValueChange(value: string) {
this._sendChange({
value,
});
}
_handleFileNameChange(fileName) {
_handleFileNameChange(fileName: string) {
this._sendChange({
fileName,
});
}
_handleDescriptionChange(description) {
_handleDescriptionChange(description: string) {
this._sendChange({
description,
});
}
_handleTypeChange(def) {
_handleTypeChange(def: Partial<Pair>) {
// Remove newlines if converting to text
// WARNING: props should never be overwritten!
let value = this.props.pair.value || '';
@ -181,31 +188,31 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
});
}
_handleDisableChange(disabled) {
_handleDisableChange(disabled: boolean) {
this._sendChange({
disabled,
});
}
_handleFocusName(e) {
_handleFocusName(e: FocusEvent) {
this.props.onFocusName(this.props.pair, e);
}
_handleFocusValue(e) {
_handleFocusValue(e: FocusEvent) {
this.props.onFocusValue(this.props.pair, e);
}
_handleFocusDescription(e) {
_handleFocusDescription(e: FocusEvent) {
this.props.onFocusDescription(this.props.pair, e);
}
_handleBlurName(e) {
_handleBlurName(e: FocusEvent) {
if (this.props.onBlurName) {
this.props.onBlurName(this.props.pair, e);
}
}
_handleBlurValue(e) {
_handleBlurValue(e: FocusEvent) {
if (this.props.onBlurName) {
this.props.onBlurValue?.(this.props.pair, e);
}
@ -223,7 +230,7 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
}
}
_handleKeyDown(e, value) {
_handleKeyDown(e: KeyboardEvent, value?: any) {
if (this.props.onKeyDown) {
this.props.onKeyDown(this.props.pair, e, value);
}
@ -235,6 +242,8 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
if (handleGetAutocompleteNameConstants) {
return handleGetAutocompleteNameConstants(this.props.pair);
}
return [];
}
_handleAutocompleteValues() {
@ -243,6 +252,8 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
if (handleGetAutocompleteValueConstants) {
return handleGetAutocompleteValueConstants(this.props.pair);
}
return [];
}
_handleEditMultiline() {
@ -253,9 +264,8 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
defaultValue: pair.value,
onChange: this._handleValueChange,
enableRender: enableNunjucks,
// @ts-expect-error -- TSCONVERSION
mode: pair.multiline || 'text/plain',
onModeChange: mode => {
onModeChange: (mode: string) => {
this._handleTypeChange(
Object.assign({}, pair, {
multiline: mode,
@ -316,7 +326,6 @@ class KeyValueEditorRowInternal extends PureComponent<Props, State> {
onChange={this._handleFileNameChange}
/>
);
// @ts-expect-error -- TSCONVERSION
} else if (pair.type === 'text' && pair.multiline) {
const bytes = Buffer.from(pair.value, 'utf8').length;
return (
@ -543,27 +552,27 @@ const dragSource = {
},
};
function isAbove(monitor, component) {
function isAbove(monitor: DropTargetMonitor, component: any) {
const hoveredNode = ReactDOM.findDOMNode(component);
// @ts-expect-error -- TSCONVERSION
const hoveredTop = hoveredNode.getBoundingClientRect().top;
// @ts-expect-error -- TSCONVERSION
const height = hoveredNode.clientHeight;
const draggedTop = monitor.getSourceClientOffset().y;
const draggedTop = monitor.getSourceClientOffset()?.y;
// NOTE: Not quite sure why it's height / 3 (seems to work)
return hoveredTop > draggedTop - height / 3;
return draggedTop !== undefined ? hoveredTop > draggedTop - height / 3 : false;
}
const dragTarget = {
drop(props, monitor, component) {
drop(props: Props, monitor: DropTargetMonitor, component: any) {
if (isAbove(monitor, component)) {
props.onMove(monitor.getItem().pair, props.pair, 1);
props.onMove?.(monitor.getItem().pair, props.pair, 1);
} else {
props.onMove(monitor.getItem().pair, props.pair, -1);
props.onMove?.(monitor.getItem().pair, props.pair, -1);
}
},
hover(_props, monitor, component) {
hover(_props: Props, monitor: DropTargetMonitor, component: any) {
if (isAbove(monitor, component)) {
component.setDragDirection(1);
} else {