From f8ece0b57c92aa7597e78db4c9cf91008a482bda Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 6 Dec 2024 16:35:11 +0000 Subject: [PATCH] Stabilise types Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../context_menu/ContextMenuButton.tsx | 13 ++-- .../context_menu/ContextMenuTooltipButton.tsx | 13 ++-- .../roving/RovingAccessibleButton.tsx | 22 +++---- .../auth/InteractiveAuthEntryComponents.tsx | 2 +- .../views/dialogs/spotlight/TooltipOption.tsx | 7 +- .../views/elements/AccessibleButton.tsx | 66 ++++++++++--------- src/components/views/emojipicker/Emoji.tsx | 2 +- .../views/messages/MPollEndBody.tsx | 2 +- .../views/messages/MessageActionBar.tsx | 2 +- .../devices/DeviceExpandDetailsButton.tsx | 15 ++--- src/components/views/spaces/SpacePanel.tsx | 2 +- .../views/spaces/SpaceTreeLevel.tsx | 14 ++-- .../LegacyCallView/LegacyCallViewButtons.tsx | 2 +- .../settings/SetIntegrationManager-test.tsx | 2 +- 14 files changed, 77 insertions(+), 87 deletions(-) diff --git a/src/accessibility/context_menu/ContextMenuButton.tsx b/src/accessibility/context_menu/ContextMenuButton.tsx index d8c7d912c1..9d8b5585e3 100644 --- a/src/accessibility/context_menu/ContextMenuButton.tsx +++ b/src/accessibility/context_menu/ContextMenuButton.tsx @@ -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 = ComponentProps> & { +type Props = ButtonProps & { label?: string; // whether the context menu is currently open isExpanded: boolean; }; // Semantic component for representing the AccessibleButton which launches a -export const ContextMenuButton = forwardRef(function ( - { label, isExpanded, children, onClick, onContextMenu, element, ...props }: Props, - ref: Ref, +export const ContextMenuButton = forwardRef(function ( + { label, isExpanded, children, onClick, onContextMenu, ...props }: Props, + ref: Ref, ) { return ( = ComponentProps> & { +type Props = ButtonProps & { // whether the context menu is currently open isExpanded: boolean; }; // Semantic component for representing the AccessibleButton which launches a -export const ContextMenuTooltipButton = forwardRef(function ( - { isExpanded, children, onClick, onContextMenu, element, ...props }: Props, - ref: Ref, +export const ContextMenuTooltipButton = forwardRef(function ( + { isExpanded, children, onClick, onContextMenu, ...props }: Props, + ref: Ref, ) { return ( = Omit< - ComponentProps>, - "inputRef" | "tabIndex" -> & { - inputRef?: Ref; +type Props = Omit, "tabIndex"> & { + inputRef?: RefObject; focusOnMouseOver?: boolean; }; // Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components. -export const RovingAccessibleButton = ({ +export const RovingAccessibleButton = ({ inputRef, onFocus, onMouseOver, focusOnMouseOver, - element, ...props }: Props): JSX.Element => { - const [onFocusInternal, isActive, ref] = useRovingTabIndex(inputRef); + const [onFocusInternal, isActive, ref] = useRovingTabIndex(inputRef); return ( { + onFocus={(event: React.FocusEvent) => { onFocusInternal(); onFocus?.(event); }} - onMouseOver={(event: React.MouseEvent) => { + onMouseOver={(event: React.MouseEvent) => { if (focusOnMouseOver) onFocusInternal(); onMouseOver?.(event); }} diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.tsx b/src/components/views/auth/InteractiveAuthEntryComponents.tsx index b1360f5560..ae5c07e348 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.tsx +++ b/src/components/views/auth/InteractiveAuthEntryComponents.tsx @@ -910,7 +910,7 @@ export class SSOAuthEntry extends React.Component extends React.Component { protected popupWindow: Window | null; - protected fallbackButton = createRef(); + protected fallbackButton = createRef(); public constructor(props: IAuthEntryProps & T) { super(props); diff --git a/src/components/views/dialogs/spotlight/TooltipOption.tsx b/src/components/views/dialogs/spotlight/TooltipOption.tsx index 1d60fed5b3..ebb0b4cf06 100644 --- a/src/components/views/dialogs/spotlight/TooltipOption.tsx +++ b/src/components/views/dialogs/spotlight/TooltipOption.tsx @@ -13,15 +13,15 @@ import { useRovingTabIndex } from "../../../../accessibility/RovingTabIndex"; import AccessibleButton, { ButtonProps } from "../../elements/AccessibleButton"; import { Ref } from "../../../../accessibility/roving/types"; -type TooltipOptionProps = ButtonProps & { +type TooltipOptionProps = ButtonProps & { + className?: string; endAdornment?: ReactNode; inputRef?: Ref; }; -export const TooltipOption = ({ +export const TooltipOption = ({ inputRef, className, - element, ...props }: TooltipOptionProps): JSX.Element => { const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); @@ -34,7 +34,6 @@ export const TooltipOption = ({ tabIndex={-1} aria-selected={isActive} role="option" - element={element as keyof JSX.IntrinsicElements} /> ); }; diff --git a/src/components/views/elements/AccessibleButton.tsx b/src/components/views/elements/AccessibleButton.tsx index fc91525dac..8b58f251c3 100644 --- a/src/components/views/elements/AccessibleButton.tsx +++ b/src/components/views/elements/AccessibleButton.tsx @@ -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"; @@ -38,23 +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 - */ -type DynamicHtmlElementProps = - JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps : DynamicElementProps<"div">; -type DynamicElementProps = Partial< - Omit -> & - Pick< - InputHTMLAttributes, - "onKeyDown" | "onKeyUp" | "onMouseOver" | "onFocus" | "alt" | "type" | "autoFocus" | "children" - >; +type ElementType = keyof HTMLElementTagNameMap; +const defaultElement = "div"; type TooltipProps = ComponentProps; @@ -63,7 +56,7 @@ type TooltipProps = ComponentProps; * * Extends props accepted by the underlying element specified using the `element` prop. */ -type Props = DynamicHtmlElementProps & { +type Props = { /** * The base element type. "div" by default. */ @@ -108,14 +101,12 @@ type Props = DynamicHtmlElementProps & disableTooltip?: TooltipProps["disabled"]; }; -export type ButtonProps = Props; +export type ButtonProps = Props & Omit, keyof Props>; /** * Type of the props passed to the element that is rendered by AccessibleButton. */ -interface RenderedElementProps extends React.InputHTMLAttributes { - ref?: React.Ref; -} +type RenderedElementProps = React.InputHTMLAttributes & RefProp; /** * AccessibleButton is a generic wrapper for any element that should be treated @@ -127,9 +118,9 @@ interface RenderedElementProps extends React.InputHTMLAttributes { * @param {Object} props react element properties * @returns {Object} rendered react */ -const AccessibleButton = forwardRef(function ( +const AccessibleButton = forwardRef(function ( { - element = "div" as T, + element, onClick, children, kind, @@ -144,10 +135,10 @@ const AccessibleButton = forwardRef(function , - ref: Ref, + }: ButtonProps, + ref: Ref, ): JSX.Element { - const newProps: RenderedElementProps = restProps; + const newProps = restProps as RenderedElementProps; newProps["aria-label"] = newProps["aria-label"] ?? title; if (disabled) { newProps["aria-disabled"] = true; @@ -165,7 +156,7 @@ const AccessibleButton = forwardRef(function { + newProps.onKeyDown = (e: KeyboardEvent) => { const action = getKeyBindingsManager().getAccessibilityAction(e); switch (action) { @@ -181,7 +172,7 @@ const AccessibleButton = forwardRef(function { + newProps.onKeyUp = (e: KeyboardEvent) => { const action = getKeyBindingsManager().getAccessibilityAction(e); switch (action) { @@ -210,7 +201,7 @@ const AccessibleButton = forwardRef(function { + ref?: Ref; +} + +interface ButtonComponent { + // With the explicit `element` prop + (props: { element?: C } & ButtonProps & RefProp): ReactElement; + // Without the explicit `element` prop + (props: ButtonProps<"div"> & RefProp<"div">): ReactElement; +} + +export default AccessibleButton as ButtonComponent; diff --git a/src/components/views/emojipicker/Emoji.tsx b/src/components/views/emojipicker/Emoji.tsx index c3dfb24bd1..a852122b75 100644 --- a/src/components/views/emojipicker/Emoji.tsx +++ b/src/components/views/emojipicker/Emoji.tsx @@ -31,7 +31,7 @@ class Emoji extends React.PureComponent { return ( onClick(ev, emoji)} + onClick={(ev: ButtonEvent) => onClick(ev, emoji)} onMouseEnter={() => onMouseEnter(emoji)} onMouseLeave={() => onMouseLeave(emoji)} className="mx_EmojiPicker_item_wrapper" diff --git a/src/components/views/messages/MPollEndBody.tsx b/src/components/views/messages/MPollEndBody.tsx index 94671fea12..1129b3538e 100644 --- a/src/components/views/messages/MPollEndBody.tsx +++ b/src/components/views/messages/MPollEndBody.tsx @@ -90,7 +90,7 @@ export const MPollEndBody = React.forwardRef(({ mxEvent, ...pro const { pollStartEvent, isLoadingPollStartEvent } = usePollStartEvent(mxEvent); if (!pollStartEvent) { - const pollEndFallbackMessage = M_TEXT.findIn(mxEvent.getContent()) || textForEvent(mxEvent, cli); + const pollEndFallbackMessage = M_TEXT.findIn(mxEvent.getContent()) || textForEvent(mxEvent, cli); return ( <> diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index 9d21b8fa45..579db054e9 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -435,7 +435,7 @@ export default class MessageActionBar extends React.PureComponent this.onPinClick(e, isPinned)} + onClick={(e: ButtonEvent) => this.onPinClick(e, isPinned)} onContextMenu={(e: ButtonEvent) => this.onPinClick(e, isPinned)} key="pin" placement="left" diff --git a/src/components/views/settings/devices/DeviceExpandDetailsButton.tsx b/src/components/views/settings/devices/DeviceExpandDetailsButton.tsx index e7839b71da..a04430a0c2 100644 --- a/src/components/views/settings/devices/DeviceExpandDetailsButton.tsx +++ b/src/components/views/settings/devices/DeviceExpandDetailsButton.tsx @@ -7,23 +7,21 @@ Please see LICENSE files in the repository root for full details. */ import classNames from "classnames"; -import React, { ComponentProps } from "react"; +import React from "react"; import { ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { _t } from "../../../../languageHandler"; -import AccessibleButton from "../../elements/AccessibleButton"; +import AccessibleButton, { ButtonProps } from "../../elements/AccessibleButton"; -type Props = Omit< - ComponentProps>, - "aria-label" | "title" | "kind" | "className" | "onClick" | "element" +type Props = Omit< + ButtonProps, + "aria-label" | "title" | "kind" | "className" | "element" > & { isExpanded: boolean; - onClick: () => void; }; -export const DeviceExpandDetailsButton = ({ +export const DeviceExpandDetailsButton = ({ isExpanded, - onClick, ...rest }: Props): JSX.Element => { const label = isExpanded ? _t("settings|sessions|hide_details") : _t("settings|sessions|show_details"); @@ -36,7 +34,6 @@ export const DeviceExpandDetailsButton = className={classNames("mx_DeviceExpandDetailsButton", { mx_DeviceExpandDetailsButton_expanded: isExpanded, })} - onClick={onClick} > diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index af484445b4..73bb66af38 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -221,7 +221,7 @@ const CreateSpaceButton: React.FC { - const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(); + const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(); useEffect(() => { if (!isPanelCollapsed && menuDisplayed) { diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index cee4cf54ec..38329c39b7 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -30,7 +30,7 @@ import defaultDispatcher from "../../../dispatcher/dispatcher"; import { Action } from "../../../dispatcher/actions"; import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton"; import { toRightOf, useContextMenu } from "../../structures/ContextMenu"; -import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; +import AccessibleButton, { ButtonEvent, ButtonProps as AccessibleButtonProps } from "../elements/AccessibleButton"; import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState"; import { NotificationLevel } from "../../../stores/notifications/NotificationLevel"; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; @@ -39,8 +39,8 @@ import SpaceContextMenu from "../context_menus/SpaceContextMenu"; import { useRovingTabIndex } from "../../../accessibility/RovingTabIndex"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; -type ButtonProps = Omit< - ComponentProps>, +type ButtonProps = Omit< + AccessibleButtonProps, "title" | "onClick" | "size" | "element" > & { space?: Room; @@ -52,12 +52,12 @@ type ButtonProps = Omit< notificationState?: NotificationState; isNarrow?: boolean; size: string; - innerRef?: RefObject; + innerRef?: RefObject; ContextMenuComponent?: ComponentType>; onClick?(ev?: ButtonEvent): void; }; -export const SpaceButton = ({ +export const SpaceButton = ({ space, spaceKey: _spaceKey, className, @@ -72,8 +72,8 @@ export const SpaceButton = ({ ContextMenuComponent, ...props }: ButtonProps): JSX.Element => { - const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(innerRef); - const [onFocus, isActive, ref] = useRovingTabIndex(handle); + const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(innerRef); + const [onFocus, isActive, ref] = useRovingTabIndex(handle); const tabIndex = isActive ? 0 : -1; const spaceKey = _spaceKey ?? space?.roomId; diff --git a/src/components/views/voip/LegacyCallView/LegacyCallViewButtons.tsx b/src/components/views/voip/LegacyCallView/LegacyCallViewButtons.tsx index bdcd3713cb..105736d04e 100644 --- a/src/components/views/voip/LegacyCallView/LegacyCallViewButtons.tsx +++ b/src/components/views/voip/LegacyCallView/LegacyCallViewButtons.tsx @@ -69,7 +69,7 @@ interface IDropdownButtonProps extends ButtonProps { } const LegacyCallViewDropdownButton: React.FC = ({ state, deviceKinds, ...props }) => { - const [menuDisplayed, buttonRef, openMenu, closeMenu] = useContextMenu(); + const [menuDisplayed, buttonRef, openMenu, closeMenu] = useContextMenu(); const [hoveringDropdown, setHoveringDropdown] = useState(false); const classes = classNames("mx_LegacyCallViewButtons_button", "mx_LegacyCallViewButtons_dropdownButton", { diff --git a/test/unit-tests/components/views/settings/SetIntegrationManager-test.tsx b/test/unit-tests/components/views/settings/SetIntegrationManager-test.tsx index 888499d524..5c77e88d93 100644 --- a/test/unit-tests/components/views/settings/SetIntegrationManager-test.tsx +++ b/test/unit-tests/components/views/settings/SetIntegrationManager-test.tsx @@ -35,7 +35,7 @@ describe("SetIntegrationManager", () => { deleteThreePid: jest.fn(), }); - let stores: SdkContextClass; + let stores!: SdkContextClass; const getComponent = () => (