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

Make tsc faster again #28678

Merged
merged 6 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
13 changes: 6 additions & 7 deletions src/accessibility/context_menu/ContextMenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,24 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/

import React, { ComponentProps, forwardRef, Ref } from "react";
import React, { forwardRef, Ref } from "react";

import AccessibleButton from "../../components/views/elements/AccessibleButton";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton";

type Props<T extends keyof JSX.IntrinsicElements> = ComponentProps<typeof AccessibleButton<T>> & {
type Props<T extends keyof HTMLElementTagNameMap> = ButtonProps<T> & {
label?: string;
// whether the context menu is currently open
isExpanded: boolean;
};

// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
export const ContextMenuButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
{ label, isExpanded, children, onClick, onContextMenu, element, ...props }: Props<T>,
ref: Ref<HTMLElement>,
export const ContextMenuButton = forwardRef(function <T extends keyof HTMLElementTagNameMap>(
{ label, isExpanded, children, onClick, onContextMenu, ...props }: Props<T>,
ref: Ref<HTMLElementTagNameMap[T]>,
) {
return (
<AccessibleButton
{...props}
element={element as keyof JSX.IntrinsicElements}
onClick={onClick}
onContextMenu={onContextMenu ?? onClick ?? undefined}
aria-label={label}
Expand Down
13 changes: 6 additions & 7 deletions src/accessibility/context_menu/ContextMenuTooltipButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,23 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/

import React, { ComponentProps, forwardRef, Ref } from "react";
import React, { forwardRef, Ref } from "react";

import AccessibleButton from "../../components/views/elements/AccessibleButton";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton";

type Props<T extends keyof JSX.IntrinsicElements> = ComponentProps<typeof AccessibleButton<T>> & {
type Props<T extends keyof HTMLElementTagNameMap> = ButtonProps<T> & {
// whether the context menu is currently open
isExpanded: boolean;
};

// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
export const ContextMenuTooltipButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
{ isExpanded, children, onClick, onContextMenu, element, ...props }: Props<T>,
ref: Ref<HTMLElement>,
export const ContextMenuTooltipButton = forwardRef(function <T extends keyof HTMLElementTagNameMap>(
{ isExpanded, children, onClick, onContextMenu, ...props }: Props<T>,
ref: Ref<HTMLElementTagNameMap[T]>,
) {
return (
<AccessibleButton
{...props}
element={element as keyof JSX.IntrinsicElements}
onClick={onClick}
onContextMenu={onContextMenu ?? onClick ?? undefined}
aria-haspopup={true}
Expand Down
22 changes: 8 additions & 14 deletions src/accessibility/roving/RovingAccessibleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,33 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/

import React, { ComponentProps } from "react";
import React, { RefObject } from "react";

import AccessibleButton from "../../components/views/elements/AccessibleButton";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton";
import { useRovingTabIndex } from "../RovingTabIndex";
import { Ref } from "./types";

type Props<T extends keyof JSX.IntrinsicElements> = Omit<
ComponentProps<typeof AccessibleButton<T>>,
"inputRef" | "tabIndex"
> & {
inputRef?: Ref;
type Props<T extends keyof HTMLElementTagNameMap> = Omit<ButtonProps<T>, "tabIndex"> & {
inputRef?: RefObject<HTMLElementTagNameMap[T]>;
focusOnMouseOver?: boolean;
};

// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components.
export const RovingAccessibleButton = <T extends keyof JSX.IntrinsicElements>({
export const RovingAccessibleButton = <T extends keyof HTMLElementTagNameMap>({
inputRef,
onFocus,
onMouseOver,
focusOnMouseOver,
element,
...props
}: Props<T>): JSX.Element => {
const [onFocusInternal, isActive, ref] = useRovingTabIndex(inputRef);
const [onFocusInternal, isActive, ref] = useRovingTabIndex<HTMLElementTagNameMap[T]>(inputRef);
return (
<AccessibleButton
{...props}
element={element as keyof JSX.IntrinsicElements}
onFocus={(event: React.FocusEvent) => {
onFocus={(event: React.FocusEvent<never, never>) => {
onFocusInternal();
onFocus?.(event);
}}
onMouseOver={(event: React.MouseEvent) => {
onMouseOver={(event: React.MouseEvent<never, never>) => {
if (focusOnMouseOver) onFocusInternal();
onMouseOver?.(event);
}}
Expand Down
7 changes: 1 addition & 6 deletions src/components/structures/auth/SoftLogout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
value={this.state.password}
disabled={this.state.busy}
/>
<AccessibleButton
onClick={this.onPasswordLogin}
kind="primary"
type="submit"
disabled={this.state.busy}
>
<AccessibleButton onClick={this.onPasswordLogin} kind="primary" disabled={this.state.busy}>
{_t("action|sign_in")}
</AccessibleButton>
<AccessibleButton onClick={this.onForgotPassword} kind="link">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn

export class FallbackAuthEntry<T = {}> extends React.Component<IAuthEntryProps & T> {
protected popupWindow: Window | null;
protected fallbackButton = createRef<HTMLButtonElement>();
protected fallbackButton = createRef<HTMLDivElement>();

public constructor(props: IAuthEntryProps & T) {
super(props);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ const SettingsList: React.FC<ISettingsListProps> = ({ onBack, onView, onEdit })
<code>{i}</code>
</AccessibleButton>
<AccessibleButton
alt={_t("devtools|edit_setting")}
title={_t("devtools|edit_setting")}
dbkr marked this conversation as resolved.
Show resolved Hide resolved
onClick={() => onEdit(i)}
className="mx_DevTools_SettingsExplorer_edit"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1253,7 +1253,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
<span>{filterToLabel(filter)}</span>
<AccessibleButton
tabIndex={-1}
alt={_t("spotlight_dialog|remove_filter", {
title={_t("spotlight_dialog|remove_filter", {
filter: filterToLabel(filter),
})}
className="mx_SpotlightDialog_filter--close"
Expand Down
7 changes: 3 additions & 4 deletions src/components/views/dialogs/spotlight/TooltipOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ import { useRovingTabIndex } from "../../../../accessibility/RovingTabIndex";
import AccessibleButton, { ButtonProps } from "../../elements/AccessibleButton";
import { Ref } from "../../../../accessibility/roving/types";

type TooltipOptionProps<T extends keyof JSX.IntrinsicElements> = ButtonProps<T> & {
type TooltipOptionProps<T extends keyof HTMLElementTagNameMap> = ButtonProps<T> & {
className?: string;
endAdornment?: ReactNode;
inputRef?: Ref;
};

export const TooltipOption = <T extends keyof JSX.IntrinsicElements>({
export const TooltipOption = <T extends keyof HTMLElementTagNameMap>({
inputRef,
className,
element,
...props
}: TooltipOptionProps<T>): JSX.Element => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
Expand All @@ -34,7 +34,6 @@ export const TooltipOption = <T extends keyof JSX.IntrinsicElements>({
tabIndex={-1}
aria-selected={isActive}
role="option"
element={element as keyof JSX.IntrinsicElements}
/>
);
};
2 changes: 1 addition & 1 deletion src/components/views/directory/NetworkDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
adornment: (
<AccessibleButton
className="mx_NetworkDropdown_removeServer"
alt={_t("spotlight|public_rooms|network_dropdown_remove_server_adornment", { roomServer })}
title={_t("spotlight|public_rooms|network_dropdown_remove_server_adornment", { roomServer })}
onClick={() => setUserDefinedServers(without(userDefinedServers, roomServer))}
/>
),
Expand Down
63 changes: 34 additions & 29 deletions src/components/views/elements/AccessibleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/

import React, { ComponentProps, forwardRef, FunctionComponent, HTMLAttributes, InputHTMLAttributes, Ref } from "react";
import React, {
ComponentProps,
ComponentPropsWithoutRef,
forwardRef,
FunctionComponent,
ReactElement,
KeyboardEvent,
Ref,
} from "react";
import classnames from "classnames";
import { Tooltip } from "@vector-im/compound-web";

Expand Down Expand Up @@ -38,20 +46,8 @@ export type AccessibleButtonKind =
| "icon_primary"
| "icon_primary_outline";

/**
* This type construct allows us to specifically pass those props down to the element we’re creating that the element
* actually supports.
*
* e.g., if element is set to "a", we’ll support href and target, if it’s set to "input", we support type.
*
* To remain compatible with existing code, we’ll continue to support InputHTMLAttributes<Element>
*/
type DynamicHtmlElementProps<T extends keyof JSX.IntrinsicElements> =
JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps<T> : DynamicElementProps<"div">;
type DynamicElementProps<T extends keyof JSX.IntrinsicElements> = Partial<
Omit<JSX.IntrinsicElements[T], "ref" | "onClick" | "onMouseDown" | "onKeyUp" | "onKeyDown">
> &
Omit<InputHTMLAttributes<Element>, "onClick">;
type ElementType = keyof HTMLElementTagNameMap;
const defaultElement = "div";

type TooltipProps = ComponentProps<typeof Tooltip>;

Expand All @@ -60,7 +56,7 @@ type TooltipProps = ComponentProps<typeof Tooltip>;
*
* Extends props accepted by the underlying element specified using the `element` prop.
*/
type Props<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> & {
type Props<T extends ElementType = "div"> = {
/**
* The base element type. "div" by default.
*/
Expand Down Expand Up @@ -105,14 +101,12 @@ type Props<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> &
disableTooltip?: TooltipProps["disabled"];
};

export type ButtonProps<T extends keyof JSX.IntrinsicElements> = Props<T>;
export type ButtonProps<T extends ElementType> = Props<T> & Omit<ComponentPropsWithoutRef<T>, keyof Props<T>>;

/**
* Type of the props passed to the element that is rendered by AccessibleButton.
*/
interface RenderedElementProps extends React.InputHTMLAttributes<Element> {
ref?: React.Ref<Element>;
}
type RenderedElementProps<T extends ElementType> = React.InputHTMLAttributes<Element> & RefProp<T>;

/**
* AccessibleButton is a generic wrapper for any element that should be treated
Expand All @@ -124,9 +118,9 @@ interface RenderedElementProps extends React.InputHTMLAttributes<Element> {
* @param {Object} props react element properties
* @returns {Object} rendered react
*/
const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
const AccessibleButton = forwardRef(function <T extends ElementType = typeof defaultElement>(
{
element = "div" as T,
element,
onClick,
children,
kind,
Expand All @@ -141,10 +135,10 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
onTooltipOpenChange,
disableTooltip,
...restProps
}: Props<T>,
ref: Ref<HTMLElement>,
}: ButtonProps<T>,
ref: Ref<HTMLElementTagNameMap[T]>,
): JSX.Element {
const newProps: RenderedElementProps = restProps;
const newProps = restProps as RenderedElementProps<T>;
newProps["aria-label"] = newProps["aria-label"] ?? title;
if (disabled) {
newProps["aria-disabled"] = true;
Expand All @@ -162,7 +156,7 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
// And divs which we report as role button to assistive technologies.
// Browsers handle space and enter key presses differently and we are only adjusting to the
// inconsistencies here
newProps.onKeyDown = (e) => {
newProps.onKeyDown = (e: KeyboardEvent<never>) => {
const action = getKeyBindingsManager().getAccessibilityAction(e);

switch (action) {
Expand All @@ -178,7 +172,7 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
onKeyDown?.(e);
}
};
newProps.onKeyUp = (e) => {
newProps.onKeyUp = (e: KeyboardEvent<never>) => {
const action = getKeyBindingsManager().getAccessibilityAction(e);

switch (action) {
Expand Down Expand Up @@ -207,7 +201,7 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
});

// React.createElement expects InputHTMLAttributes
const button = React.createElement(element, newProps, children);
const button = React.createElement(element ?? defaultElement, newProps, children);

if (title) {
return (
Expand All @@ -233,4 +227,15 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
};
(AccessibleButton as FunctionComponent).displayName = "AccessibleButton";

export default AccessibleButton;
interface RefProp<T extends ElementType> {
ref?: Ref<HTMLElementTagNameMap[T]>;
}

interface ButtonComponent {
// With the explicit `element` prop
<C extends ElementType>(props: { element?: C } & ButtonProps<C> & RefProp<C>): ReactElement;
// Without the explicit `element` prop
(props: ButtonProps<"div"> & RefProp<"div">): ReactElement;
}

export default AccessibleButton as ButtonComponent;
7 changes: 1 addition & 6 deletions src/components/views/elements/EditableItemList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,7 @@ export default class EditableItemList<P = {}> extends React.PureComponent<IProps
onChange={this.onNewItemChanged}
list={this.props.suggestionsListId}
/>
<AccessibleButton
onClick={this.onItemAdded}
kind="primary"
type="submit"
disabled={!this.props.newItem}
>
<AccessibleButton onClick={this.onItemAdded} kind="primary" disabled={!this.props.newItem}>
{_t("action|add")}
</AccessibleButton>
</form>
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/emojipicker/Emoji.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Emoji extends React.PureComponent<IProps> {
return (
<RovingAccessibleButton
id={this.props.id}
onClick={(ev) => onClick(ev, emoji)}
onClick={(ev: ButtonEvent) => onClick(ev, emoji)}
onMouseEnter={() => onMouseEnter(emoji)}
onMouseLeave={() => onMouseLeave(emoji)}
className="mx_EmojiPicker_item_wrapper"
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/messages/MPollEndBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const MPollEndBody = React.forwardRef<any, IBodyProps>(({ mxEvent, ...pro
const { pollStartEvent, isLoadingPollStartEvent } = usePollStartEvent(mxEvent);

if (!pollStartEvent) {
const pollEndFallbackMessage = M_TEXT.findIn(mxEvent.getContent()) || textForEvent(mxEvent, cli);
const pollEndFallbackMessage = M_TEXT.findIn<string>(mxEvent.getContent()) || textForEvent(mxEvent, cli);
return (
<>
<PollIcon className="mx_MPollEndBody_icon" />
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/messages/MessageActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
<RovingAccessibleButton
className="mx_MessageActionBar_iconButton"
title={isPinned ? _t("action|unpin") : _t("action|pin")}
onClick={(e) => this.onPinClick(e, isPinned)}
onClick={(e: ButtonEvent) => this.onPinClick(e, isPinned)}
onContextMenu={(e: ButtonEvent) => this.onPinClick(e, isPinned)}
key="pin"
placement="left"
Expand Down
1 change: 0 additions & 1 deletion src/components/views/settings/SetIdServer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,6 @@ export default class SetIdServer extends React.Component<IProps, IState> {
forceValidity={this.state.error ? false : undefined}
/>
<AccessibleButton
type="submit"
kind="primary_sm"
onClick={this.checkIdServer}
disabled={!this.idServerChangeEnabled()}
Expand Down
Loading
Loading