Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
24 changes: 24 additions & 0 deletions src/@types/react.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { PropsWithChildren } from "react";

declare module "react" {
// Fix forwardRef types for Generic components - https://stackoverflow.com/a/58473012
function forwardRef<T, P = {}>(
render: (props: PropsWithChildren<P>, ref: React.ForwardedRef<T>) => React.ReactElement | null,
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}
23 changes: 10 additions & 13 deletions src/accessibility/context_menu/ContextMenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,21 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

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

import AccessibleButton from "../../components/views/elements/AccessibleButton";
import AccessibleButton, { Props as AccessibleButtonProps } from "../../components/views/elements/AccessibleButton";

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

// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
Comment thread
t3chguy marked this conversation as resolved.
export const ContextMenuButton: React.FC<IProps> = ({
label,
isExpanded,
children,
onClick,
onContextMenu,
...props
}) => {
export const ContextMenuButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
{ label, isExpanded, children, onClick, onContextMenu, ...props }: Props<T>,
ref: Ref<HTMLElement>,
) {
return (
<AccessibleButton
{...props}
Expand All @@ -44,8 +40,9 @@ export const ContextMenuButton: React.FC<IProps> = ({
aria-label={label}
aria-haspopup={true}
aria-expanded={isExpanded}
ref={ref}
>
{children}
</AccessibleButton>
);
};
});
24 changes: 12 additions & 12 deletions src/accessibility/context_menu/ContextMenuTooltipButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

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

import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
import AccessibleTooltipButton, {
Props as AccessibleTooltipButtonProps,
} from "../../components/views/elements/AccessibleTooltipButton";

interface IProps extends React.ComponentProps<typeof AccessibleTooltipButton> {
type Props<T extends keyof JSX.IntrinsicElements> = AccessibleTooltipButtonProps<T> & {
// whether or not the context menu is currently open
isExpanded: boolean;
}
};

// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
export const ContextMenuTooltipButton: React.FC<IProps> = ({
isExpanded,
children,
onClick,
onContextMenu,
...props
}) => {
export const ContextMenuTooltipButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
{ isExpanded, children, onClick, onContextMenu, ...props }: Props<T>,
ref: Ref<HTMLElement>,
) {
return (
<AccessibleTooltipButton
{...props}
Expand All @@ -41,8 +40,9 @@ export const ContextMenuTooltipButton: React.FC<IProps> = ({
aria-haspopup={true}
aria-expanded={isExpanded}
forceHide={isExpanded}
ref={ref}
>
{children}
</AccessibleTooltipButton>
);
};
});
2 changes: 1 addition & 1 deletion src/accessibility/roving/RovingAccessibleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const RovingAccessibleButton: React.FC<IProps> = ({
if (focusOnMouseOver) onFocusInternal();
onMouseOver?.(event);
}}
inputRef={ref}
ref={ref}
tabIndex={isActive ? 0 : -1}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const RovingAccessibleTooltipButton: React.FC<IProps> = ({ inputRef, onFo
onFocusInternal();
onFocus?.(event);
}}
inputRef={ref}
ref={ref}
tabIndex={isActive ? 0 : -1}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/structures/GenericDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export function GenericDropdownMenu<T>({
<>
<ContextMenuButton
className="mx_GenericDropdownMenu_button"
inputRef={button}
ref={button}
isExpanded={menuDisplayed}
onClick={(ev: ButtonEvent) => {
openMenu();
Expand Down
3 changes: 2 additions & 1 deletion src/components/structures/InteractiveAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { logger } from "matrix-js-sdk/src/logger";

import getEntryComponentForLoginType, { IStageComponent } from "../views/auth/InteractiveAuthEntryComponents";
import Spinner from "../views/elements/Spinner";
import { AccessibleButtonKind } from "../views/elements/AccessibleButton";

export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");

Expand Down Expand Up @@ -59,7 +60,7 @@ export interface InteractiveAuthProps<T> {
continueIsManaged?: boolean;
// continueText and continueKind are passed straight through to the AuthEntryComponent.
continueText?: string;
continueKind?: string;
continueKind?: Extract<AccessibleButtonKind, "primary" | "danger">;
// callback
makeRequest(auth: IAuthDict | null): Promise<T>;
// callback called when the auth process has finished,
Expand Down
5 changes: 3 additions & 2 deletions src/components/structures/SpaceHierarchy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.
*/

import React, {
ComponentProps,
Dispatch,
KeyboardEvent,
KeyboardEventHandler,
Expand Down Expand Up @@ -349,7 +350,7 @@ const Tile: React.FC<ITileProps> = ({
})}
onClick={hasPermissions && onToggleClick ? onToggleClick : onPreviewClick}
onKeyDown={onKeyDown}
inputRef={ref}
ref={ref}
onFocus={onFocus}
tabIndex={isActive ? 0 : -1}
>
Expand Down Expand Up @@ -664,7 +665,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
const disabled = !selectedRelations.length || removing || saving;

let Button: React.ComponentType<React.ComponentProps<typeof AccessibleButton>> = AccessibleButton;
let props = {};
let props: Partial<ComponentProps<typeof AccessibleTooltipButton>> = {};
if (!selectedRelations.length) {
Button = AccessibleTooltipButton;
props = {
Expand Down
6 changes: 3 additions & 3 deletions src/components/structures/SpaceRoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ limitations under the License.

import { EventType, RoomType, JoinRule, Preset, Room, RoomEvent } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import React, { RefObject, useCallback, useContext, useRef, useState } from "react";
import React, { useCallback, useContext, useRef, useState } from "react";

import MatrixClientContext from "../../contexts/MatrixClientContext";
import createRoom, { IOpts } from "../../createRoom";
Expand Down Expand Up @@ -196,7 +196,7 @@ const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
<>
<ContextMenuButton
kind="primary"
inputRef={handle}
ref={handle}
onClick={openMenu}
isExpanded={menuDisplayed}
label={_t("action|add")}
Expand Down Expand Up @@ -499,7 +499,7 @@ const SpaceSetupPrivateInvite: React.FC<{
const [busy, setBusy] = useState(false);
const [error, setError] = useState("");
const numFields = 3;
const fieldRefs = [useRef(), useRef(), useRef()] as RefObject<Field>[];
const fieldRefs = [useRef<Field>(null), useRef<Field>(null), useRef<Field>(null)];
const [emailAddresses, setEmailAddress] = useStateArray(numFields, "");
const fields = new Array(numFields).fill(0).map((x, i) => {
const name = "emailAddress" + i;
Expand Down
2 changes: 1 addition & 1 deletion src/components/structures/ThreadPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const ThreadPanelHeader: React.FC<{
<>
<ContextMenuButton
className="mx_ThreadPanel_dropdown"
inputRef={button}
ref={button}
isExpanded={menuDisplayed}
onClick={(ev: ButtonEvent) => {
openMenu();
Expand Down
2 changes: 1 addition & 1 deletion src/components/structures/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
<ContextMenuButton
className="mx_UserMenu_contextMenuButton"
onClick={this.onOpenMenuClick}
inputRef={this.buttonRef}
ref={this.buttonRef}
label={_t("a11y|user_menu")}
isExpanded={!!this.state.contextMenuPosition}
onContextMenu={this.onContextMenu}
Expand Down
3 changes: 2 additions & 1 deletion src/components/views/audio_messages/PlayPauseButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import { _t } from "../../../languageHandler";
import { Playback, PlaybackState } from "../../../audio/Playback";

// omitted props are handled by render function
interface IProps extends Omit<React.ComponentProps<typeof AccessibleTooltipButton>, "title" | "onClick" | "disabled"> {
interface IProps
extends Omit<React.ComponentProps<typeof AccessibleTooltipButton>, "title" | "onClick" | "disabled" | "ref"> {
// Playback instance to manipulate. Cannot change during the component lifecycle.
playback: Playback;

Expand Down
10 changes: 5 additions & 5 deletions src/components/views/auth/InteractiveAuthEntryComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { _t } from "../../../languageHandler";
import SettingsStore from "../../../settings/SettingsStore";
import { LocalisedPolicy, Policies } from "../../../Terms";
import { AuthHeaderModifier } from "../../structures/auth/header/AuthHeaderModifier";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import AccessibleButton, { AccessibleButtonKind, ButtonEvent } from "../elements/AccessibleButton";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import Field from "../elements/Field";
import Spinner from "../elements/Spinner";
Expand Down Expand Up @@ -782,7 +782,7 @@ export class RegistrationTokenAuthEntry extends React.Component<IAuthEntryProps,

interface ISSOAuthEntryProps extends IAuthEntryProps {
continueText?: string;
continueKind?: string;
continueKind?: Extract<AccessibleButtonKind, "primary" | "danger">;
Comment thread
t3chguy marked this conversation as resolved.
Outdated
onCancel?: () => void;
}

Expand Down Expand Up @@ -866,7 +866,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
const cancelButton = (
<AccessibleButton
onClick={this.props.onCancel ?? null}
kind={this.props.continueKind ? this.props.continueKind + "_outline" : "primary_outline"}
kind={this.props.continueKind ? `${this.props.continueKind}_outline` : "primary_outline"}
>
{_t("action|cancel")}
</AccessibleButton>
Expand Down Expand Up @@ -971,7 +971,7 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
}
return (
<div>
<AccessibleButton kind="link" inputRef={this.fallbackButton} onClick={this.onShowFallbackClick}>
<AccessibleButton kind="link" ref={this.fallbackButton} onClick={this.onShowFallbackClick}>
{_t("auth|uia|fallback_button")}
</AccessibleButton>
{errorSection}
Expand All @@ -985,7 +985,7 @@ export interface IStageComponentProps extends IAuthEntryProps {
inputs?: IInputs;
stageState?: IStageStatus;
continueText?: string;
continueKind?: string;
continueKind?: Extract<AccessibleButtonKind, "primary" | "danger">;
setEmailSid?(sid: string): void;
onCancel?(): void;
requestEmailToken?(): Promise<void>;
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/context_menus/KebabContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const KebabContextMenu: React.FC<KebabContextMenuProps> = ({ options, tit

return (
<>
<ContextMenuButton {...props} onClick={openMenu} title={title} isExpanded={menuDisplayed} inputRef={button}>
<ContextMenuButton {...props} onClick={openMenu} title={title} isExpanded={menuDisplayed} ref={button}>
<ContextMenuIcon className="mx_KebabContextMenu_icon" />
</ContextMenuButton>
{menuDisplayed && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
onClick={openMenu}
title={_t("right_panel|thread_list|context_menu_label")}
isExpanded={menuDisplayed}
inputRef={button}
ref={button}
data-testid="threadlist-dropdown-button"
/>
{menuDisplayed && (
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/dialogs/AddExistingToSpaceDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { ReactElement, ReactNode, RefObject, useContext, useMemo, useRef, useState } from "react";
import React, { ReactElement, ReactNode, useContext, useMemo, useRef, useState } from "react";
import classNames from "classnames";
import { Room, EventType } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils";
Expand Down Expand Up @@ -144,7 +144,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
[cli, msc3946ProcessDynamicPredecessor],
);

const scrollRef = useRef() as RefObject<AutoHideScrollbar<"div">>;
const scrollRef = useRef<AutoHideScrollbar<"div">>(null);
const [scrollState, setScrollState] = useState<IScrollState>({
// these are estimates which update as soon as it mounts
scrollTop: 0,
Expand Down
7 changes: 4 additions & 3 deletions src/components/views/dialogs/DeactivateAccountDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ import StyledCheckbox from "../elements/StyledCheckbox";
import BaseDialog from "./BaseDialog";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
import { AccessibleButtonKind } from "../elements/AccessibleButton";

type DialogAesthetics = Partial<{
[x in AuthType]: {
[x: number]: {
body: string;
continueText?: string;
continueKind?: string;
continueKind?: Extract<AccessibleButtonKind, "primary" | "danger">;
};
};
}>;
Expand All @@ -53,7 +54,7 @@ interface IState {
// next to the InteractiveAuth component.
bodyText?: string;
continueText?: string;
continueKind?: string;
continueKind?: Extract<AccessibleButtonKind, "primary" | "danger">;
}

export default class DeactivateAccountDialog extends React.Component<IProps, IState> {
Expand Down Expand Up @@ -98,7 +99,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
const aesthetics = DEACTIVATE_AESTHETICS[stage];
let bodyText: string | undefined;
let continueText: string | undefined;
let continueKind: string | undefined;
let continueKind: Extract<AccessibleButtonKind, "primary" | "danger"> | undefined;
if (aesthetics) {
const phaseAesthetics = aesthetics[phase];
if (phaseAesthetics) {
Expand Down
6 changes: 3 additions & 3 deletions src/components/views/dialogs/InteractiveAuthDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { MatrixClient, UIAResponse } from "matrix-js-sdk/src/matrix";
import { AuthType } from "matrix-js-sdk/src/interactive-auth";

import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import AccessibleButton, { AccessibleButtonKind } from "../elements/AccessibleButton";
import InteractiveAuth, {
ERROR_USER_CANCELLED,
InteractiveAuthCallback,
Expand All @@ -36,7 +36,7 @@ type DialogAesthetics = Partial<{
title: string;
body: string;
continueText: string;
continueKind: string;
continueKind: Extract<AccessibleButtonKind, "primary" | "danger">;
};
};
}>;
Expand Down Expand Up @@ -146,7 +146,7 @@ export default class InteractiveAuthDialog<T> extends React.Component<Interactiv
let title = this.state.authError ? "Error" : this.props.title || _t("common|authentication");
let body = this.state.authError ? null : this.props.body;
let continueText: string | undefined;
let continueKind: string | undefined;
let continueKind: Extract<AccessibleButtonKind, "primary" | "danger"> | undefined;
const dialogAesthetics = this.props.aestheticsForStagePhases || this.getDefaultDialogAesthetics();
if (!this.state.authError && dialogAesthetics) {
if (
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/dialogs/ModalWidgetDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {

import BaseDialog from "./BaseDialog";
import { _t, getUserLanguage } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import AccessibleButton, { AccessibleButtonKind } from "../elements/AccessibleButton";
import { StopGapWidgetDriver } from "../../../stores/widgets/StopGapWidgetDriver";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { OwnProfileStore } from "../../../stores/OwnProfileStore";
Expand Down Expand Up @@ -158,7 +158,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
.slice(0, MAX_BUTTONS)
.reverse()
.map((def) => {
let kind = "secondary";
let kind: AccessibleButtonKind = "secondary";
switch (def.kind) {
case ModalButtonKind.Primary:
kind = "primary";
Expand Down
Loading