♻️ (typescript) port some common components to strict TS (#2481)

This commit is contained in:
Matiss Janis Aboltins
2024-03-23 09:35:20 +00:00
committed by GitHub
parent e44fbb3847
commit afaee6bc16
14 changed files with 86 additions and 57 deletions

View File

@@ -1,5 +1,4 @@
// @ts-strict-ignore
import { type ComponentProps } from 'react';
import { type ComponentProps, type ReactNode } from 'react';
import { type CSSProperties } from '../../style';
@@ -7,8 +6,8 @@ import { Block } from './Block';
import { View } from './View';
type AlignedTextProps = ComponentProps<typeof View> & {
left;
right;
left: ReactNode;
right: ReactNode;
style?: CSSProperties;
leftStyle?: CSSProperties;
rightStyle?: CSSProperties;

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import React, { forwardRef, type ElementType, type HTMLProps } from 'react';
import { css } from 'glamor';
@@ -31,7 +30,9 @@ type ButtonType =
| 'menu'
| 'menuSelected';
const backgroundColor = {
const backgroundColor: {
[key in ButtonType | `${ButtonType}Disabled`]?: string;
} = {
normal: theme.buttonNormalBackground,
normalDisabled: theme.buttonNormalDisabledBackground,
primary: theme.buttonPrimaryBackground,
@@ -43,7 +44,7 @@ const backgroundColor = {
link: theme.buttonBareBackground,
};
const backgroundColorHover = {
const backgroundColorHover: Record<ButtonType, string> = {
normal: theme.buttonNormalBackgroundHover,
primary: theme.buttonPrimaryBackgroundHover,
bare: theme.buttonBareBackgroundHover,
@@ -52,7 +53,9 @@ const backgroundColorHover = {
link: theme.buttonBareBackground,
};
const borderColor = {
const borderColor: {
[key in ButtonType | `${ButtonType}Disabled`]?: string;
} = {
normal: theme.buttonNormalBorder,
normalDisabled: theme.buttonNormalDisabledBorder,
primary: theme.buttonPrimaryBorder,
@@ -62,7 +65,9 @@ const borderColor = {
link: theme.buttonBareBackground,
};
const textColor = {
const textColor: {
[key in ButtonType | `${ButtonType}Disabled`]?: string;
} = {
normal: theme.buttonNormalText,
normalDisabled: theme.buttonNormalDisabledText,
primary: theme.buttonPrimaryText,
@@ -74,7 +79,9 @@ const textColor = {
link: theme.pageTextLink,
};
const textColorHover = {
const textColorHover: {
[key in ButtonType]?: string;
} = {
normal: theme.buttonNormalTextHover,
primary: theme.buttonPrimaryTextHover,
bare: theme.buttonBareTextHover,
@@ -87,7 +94,10 @@ const linkButtonHoverStyles = {
boxShadow: 'none',
};
const _getBorder = (type, typeWithDisabled) => {
const _getBorder = (
type: ButtonType,
typeWithDisabled: keyof typeof borderColor,
): string => {
switch (type) {
case 'bare':
case 'link':
@@ -98,7 +108,7 @@ const _getBorder = (type, typeWithDisabled) => {
}
};
const _getPadding = type => {
const _getPadding = (type: ButtonType): string => {
switch (type) {
case 'bare':
return '5px';
@@ -109,7 +119,7 @@ const _getPadding = type => {
}
};
const _getActiveStyles = (type, bounce) => {
const _getActiveStyles = (type: ButtonType, bounce: boolean): CSSProperties => {
switch (type) {
case 'bare':
return { backgroundColor: theme.buttonBareBackgroundActive };
@@ -120,7 +130,7 @@ const _getActiveStyles = (type, bounce) => {
};
default:
return {
transform: bounce && 'translateY(1px)',
transform: bounce ? 'translateY(1px)' : undefined,
boxShadow: `0 1px 4px 0 ${
type === 'primary'
? theme.buttonPrimaryShadow
@@ -150,7 +160,9 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
},
ref,
) => {
const typeWithDisabled = disabled ? type + 'Disabled' : type;
const typeWithDisabled: ButtonType | `${ButtonType}Disabled` = disabled
? `${type}Disabled`
: type;
hoveredStyle = {
...(type !== 'bare' && styles.shadow),

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import {
type ReactElement,
type Ref,
@@ -8,14 +7,16 @@ import {
} from 'react';
type InitialFocusProps = {
children?: ReactElement | ((node: Ref<HTMLInputElement>) => ReactElement);
children:
| ReactElement<{ inputRef: Ref<HTMLInputElement> }>
| ((node: Ref<HTMLInputElement>) => ReactElement);
};
export function InitialFocus({ children }: InitialFocusProps) {
const node = useRef(null);
const node = useRef<HTMLInputElement>(null);
useEffect(() => {
if (node.current && !global.IS_DESIGN_MODE) {
if (node.current) {
// This is needed to avoid a strange interaction with
// `ScopeTab`, which doesn't allow it to be focused at first for
// some reason. Need to look into it.

View File

@@ -1,9 +1,8 @@
// @ts-strict-ignore
import React, {
useRef,
type InputHTMLAttributes,
type KeyboardEvent,
type Ref,
type InputHTMLAttributes,
useRef,
} from 'react';
import mergeRefs from 'react-merge-refs';
@@ -42,7 +41,7 @@ export function Input({
focused,
...nativeProps
}: InputProps) {
const ref = useRef();
const ref = useRef<HTMLInputElement>(null);
useProperFocus(ref, focused);
return (

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { useState, type ComponentProps, type ReactNode } from 'react';
import { type CSSProperties, theme } from '../../style';
@@ -23,7 +22,7 @@ export function InputWithContent({
getStyle,
...props
}: InputWithContentProps) {
const [focused, setFocused] = useState(props.focused);
const [focused, setFocused] = useState(props.focused ?? false);
return (
<View
@@ -37,7 +36,7 @@ export function InputWithContent({
(focusStyle ?? {
boxShadow: '0 0 0 1px ' + theme.formInputShadowSelected,
})),
...(getStyle && getStyle(focused)),
...getStyle?.(focused),
}}
>
{leftContent}

View File

@@ -1,5 +1,8 @@
// @ts-strict-ignore
import React, { type ReactNode, type ComponentProps } from 'react';
import React, {
type ComponentProps,
type MouseEvent,
type ReactNode,
} from 'react';
import { NavLink, useMatch } from 'react-router-dom';
import { css } from 'glamor';
@@ -31,7 +34,7 @@ const ButtonLink = ({
const navigate = useNavigate();
const match = useMatch({ path: to });
const handleClick = e => {
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
onClick?.(e);
navigate(to);
};

View File

@@ -1,5 +1,6 @@
// @ts-strict-ignore
import {
type FunctionComponent,
type ReactElement,
type ReactNode,
createElement,
useEffect,
@@ -13,6 +14,10 @@ import { Text } from './Text';
import { Toggle } from './Toggle';
import { View } from './View';
const MenuLine: unique symbol = Symbol('menu-line');
Menu.line = MenuLine;
Menu.label = Symbol('menu-label');
type KeybindingProps = {
keyName: ReactNode;
};
@@ -29,7 +34,12 @@ type MenuItem = {
type?: string | symbol;
name: string;
disabled?: boolean;
icon?;
// eslint-disable-next-line @typescript-eslint/ban-types
icon?: FunctionComponent<{
width: number;
height: number;
style: CSSProperties;
}>;
iconSize?: number;
text: string;
key?: string;
@@ -53,21 +63,21 @@ export function Menu<T extends MenuItem>({
onMenuSelect,
style,
}: MenuProps<T>) {
const elRef = useRef(null);
const elRef = useRef<HTMLDivElement>(null);
const items = allItems.filter(x => x);
const [hoveredIndex, setHoveredIndex] = useState(null);
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
useEffect(() => {
const el = elRef.current;
el.focus();
el?.focus();
const onKeyDown = e => {
const onKeyDown = (e: KeyboardEvent) => {
const filteredItems = items.filter(
item => item && item !== Menu.line && item.type !== Menu.label,
);
const currentIndex = filteredItems.indexOf(items[hoveredIndex]);
const currentIndex = filteredItems.indexOf(items[hoveredIndex || 0]);
const transformIndex = idx => items.indexOf(filteredItems[idx]);
const transformIndex = (idx: number) => items.indexOf(filteredItems[idx]);
switch (e.key) {
case 'ArrowUp':
@@ -90,7 +100,7 @@ export function Menu<T extends MenuItem>({
break;
case 'Enter':
e.preventDefault();
const item = items[hoveredIndex];
const item = items[hoveredIndex || 0];
if (hoveredIndex !== null && item !== Menu.line) {
onMenuSelect?.(item.name);
}
@@ -99,10 +109,10 @@ export function Menu<T extends MenuItem>({
}
};
el.addEventListener('keydown', onKeyDown);
el?.addEventListener('keydown', onKeyDown);
return () => {
el.removeEventListener('keydown', onKeyDown);
el?.removeEventListener('keydown', onKeyDown);
};
}, [hoveredIndex]);
@@ -203,6 +213,7 @@ export function Menu<T extends MenuItem>({
style={{ marginLeft: 5, ...item.style }}
onToggle={() =>
!item.disabled &&
onMenuSelect &&
item.toggle !== undefined &&
onMenuSelect(item.name)
}
@@ -217,7 +228,3 @@ export function Menu<T extends MenuItem>({
</View>
);
}
const MenuLine: unique symbol = Symbol('menu-line');
Menu.line = MenuLine;
Menu.label = Symbol('menu-label');

View File

@@ -1,9 +1,14 @@
// @ts-strict-ignore
import React from 'react';
import React, { type ReactNode } from 'react';
import { Tooltip } from '../tooltips';
export function MenuTooltip({ width, onClose, children }) {
type MenuTooltipProps = {
width: number;
onClose: () => void;
children: ReactNode;
};
export function MenuTooltip({ width, onClose, children }: MenuTooltipProps) {
return (
<Tooltip
position="bottom-right"

View File

@@ -103,7 +103,7 @@ export const Modal = ({
isOpen={true}
onRequestClose={onClose}
shouldCloseOnOverlayClick={true}
shouldFocusAfterRender={!global.IS_DESIGN_MODE}
shouldFocusAfterRender
shouldReturnFocusAfterClose={focusAfterClose}
appElement={document.querySelector('#root') as HTMLElement}
parentSelector={parent && (() => parent)}

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { type Ref } from 'react';
import { SvgRemove, SvgSearchAlternate } from '../../icons/v2';
@@ -10,7 +9,7 @@ import { InputWithContent } from './InputWithContent';
type SearchProps = {
inputRef?: Ref<HTMLInputElement>;
value: string;
onChange: (value: string) => unknown;
onChange: (value: string) => void;
placeholder: string;
isInModal?: boolean;
width?: number;
@@ -30,12 +29,12 @@ export function Search({
style={{
width,
flex: '',
borderColor: isInModal ? null : 'transparent',
backgroundColor: isInModal ? null : theme.formInputBackground,
borderColor: isInModal ? undefined : 'transparent',
backgroundColor: isInModal ? undefined : theme.formInputBackground,
}}
focusStyle={
isInModal
? null
? undefined
: {
boxShadow: '0 0 0 1px ' + theme.formInputShadowSelected,
backgroundColor: theme.formInputBackgroundSelected,

View File

@@ -55,7 +55,7 @@ export function AmountInput({
useEffect(() => setValue(initialValueAbsolute), [initialValueAbsolute]);
const buttonRef = useRef();
const ref = useRef<HTMLInputElement>();
const ref = useRef<HTMLInputElement>(null);
const mergedRef = useMergedRefs<HTMLInputElement>(inputRef, ref);
useEffect(() => {

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { useMemo } from 'react';
import type { MutableRefObject, Ref, RefCallback } from 'react';
@@ -7,7 +6,7 @@ export function useMergedRefs<T>(
ref2: RefCallback<T> | MutableRefObject<T>,
): Ref<T> {
return useMemo(() => {
function ref(value) {
function ref(value: T) {
[ref1, ref2].forEach(ref => {
if (typeof ref === 'function') {
ref(value);

View File

@@ -77,7 +77,7 @@ export function AvoidRefocusScrollProvider({
export function useProperFocus(
ref: RefObject<HTMLElement>,
shouldFocus: boolean,
shouldFocus = false,
): void {
const context = useContext(AvoidRefocusScrollContext);
const prevShouldFocus = useRef(null);

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [MatissJanis]
---
Convert most common components to strict TypeScript