Improve AccessibleButton & related types (#12075)
* Fix wrong type enum usage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Use improved type definition for forwardRef which enables Generic props Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve AccessibleButton & related Props types Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove useless comment Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>pull/28788/head^2
parent
e26d3e9b68
commit
af31965866
|
@ -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;
|
||||
}
|
|
@ -16,25 +16,25 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { ComponentProps } from "react";
|
||||
|
||||
import AccessibleButton from "../../components/views/elements/AccessibleButton";
|
||||
|
||||
interface IProps extends React.ComponentProps<typeof AccessibleButton> {
|
||||
type Props<T extends keyof JSX.IntrinsicElements> = ComponentProps<typeof AccessibleButton<T>> & {
|
||||
label?: string;
|
||||
// whether or not the context menu is currently open
|
||||
// whether the context menu is currently open
|
||||
isExpanded: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
|
||||
export const ContextMenuButton: React.FC<IProps> = ({
|
||||
export const ContextMenuButton = <T extends keyof JSX.IntrinsicElements>({
|
||||
label,
|
||||
isExpanded,
|
||||
children,
|
||||
onClick,
|
||||
onContextMenu,
|
||||
...props
|
||||
}) => {
|
||||
}: Props<T>): JSX.Element => {
|
||||
return (
|
||||
<AccessibleButton
|
||||
{...props}
|
||||
|
|
|
@ -16,23 +16,23 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { ComponentProps } from "react";
|
||||
|
||||
import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
|
||||
|
||||
interface IProps extends React.ComponentProps<typeof AccessibleTooltipButton> {
|
||||
// whether or not the context menu is currently open
|
||||
type Props<T extends keyof JSX.IntrinsicElements> = ComponentProps<typeof AccessibleTooltipButton<T>> & {
|
||||
// whether the context menu is currently open
|
||||
isExpanded: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
|
||||
export const ContextMenuTooltipButton: React.FC<IProps> = ({
|
||||
export const ContextMenuTooltipButton = <T extends keyof JSX.IntrinsicElements>({
|
||||
isExpanded,
|
||||
children,
|
||||
onClick,
|
||||
onContextMenu,
|
||||
...props
|
||||
}) => {
|
||||
}: Props<T>): JSX.Element => {
|
||||
return (
|
||||
<AccessibleTooltipButton
|
||||
{...props}
|
||||
|
|
|
@ -14,25 +14,28 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { ComponentProps } from "react";
|
||||
|
||||
import AccessibleButton from "../../components/views/elements/AccessibleButton";
|
||||
import { useRovingTabIndex } from "../RovingTabIndex";
|
||||
import { Ref } from "./types";
|
||||
|
||||
interface IProps extends Omit<React.ComponentProps<typeof AccessibleButton>, "inputRef" | "tabIndex"> {
|
||||
type Props<T extends keyof JSX.IntrinsicElements> = Omit<
|
||||
ComponentProps<typeof AccessibleButton<T>>,
|
||||
"inputRef" | "tabIndex"
|
||||
> & {
|
||||
inputRef?: Ref;
|
||||
focusOnMouseOver?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components.
|
||||
export const RovingAccessibleButton: React.FC<IProps> = ({
|
||||
export const RovingAccessibleButton = <T extends keyof JSX.IntrinsicElements>({
|
||||
inputRef,
|
||||
onFocus,
|
||||
onMouseOver,
|
||||
focusOnMouseOver,
|
||||
...props
|
||||
}) => {
|
||||
}: Props<T>): JSX.Element => {
|
||||
const [onFocusInternal, isActive, ref] = useRovingTabIndex(inputRef);
|
||||
return (
|
||||
<AccessibleButton
|
||||
|
|
|
@ -14,19 +14,25 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { ComponentProps } from "react";
|
||||
|
||||
import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
|
||||
import { useRovingTabIndex } from "../RovingTabIndex";
|
||||
import { Ref } from "./types";
|
||||
|
||||
type ATBProps = React.ComponentProps<typeof AccessibleTooltipButton>;
|
||||
interface IProps extends Omit<ATBProps, "inputRef" | "tabIndex"> {
|
||||
type Props<T extends keyof JSX.IntrinsicElements> = Omit<
|
||||
ComponentProps<typeof AccessibleTooltipButton<T>>,
|
||||
"tabIndex"
|
||||
> & {
|
||||
inputRef?: Ref;
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper to allow use of useRovingTabIndex for simple AccessibleTooltipButtons outside of React Functional Components.
|
||||
export const RovingAccessibleTooltipButton: React.FC<IProps> = ({ inputRef, onFocus, ...props }) => {
|
||||
export const RovingAccessibleTooltipButton = <T extends keyof JSX.IntrinsicElements>({
|
||||
inputRef,
|
||||
onFocus,
|
||||
...props
|
||||
}: Props<T>): JSX.Element => {
|
||||
const [onFocusInternal, isActive, ref] = useRovingTabIndex(inputRef);
|
||||
return (
|
||||
<AccessibleTooltipButton
|
||||
|
|
|
@ -14,28 +14,27 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from "react";
|
||||
import React, { ComponentProps, ReactNode } from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
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"> {
|
||||
type Props = Omit<ComponentProps<typeof AccessibleTooltipButton>, "title" | "onClick" | "disabled" | "element"> & {
|
||||
// Playback instance to manipulate. Cannot change during the component lifecycle.
|
||||
playback: Playback;
|
||||
|
||||
// The playback phase to render. Able to change during the component lifecycle.
|
||||
playbackPhase: PlaybackState;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays a play/pause button (activating the play/pause function of the recorder)
|
||||
* to be displayed in reference to a recording.
|
||||
*/
|
||||
export default class PlayPauseButton extends React.PureComponent<IProps> {
|
||||
public constructor(props: IProps) {
|
||||
export default class PlayPauseButton extends React.PureComponent<Props> {
|
||||
public constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,10 @@ type Props<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> &
|
|||
onClick: ((e: ButtonEvent) => void | Promise<void>) | null;
|
||||
};
|
||||
|
||||
export interface IAccessibleButtonProps extends React.InputHTMLAttributes<Element> {
|
||||
/**
|
||||
* Type of the props passed to the element that is rendered by AccessibleButton.
|
||||
*/
|
||||
interface RenderedElementProps extends React.InputHTMLAttributes<Element> {
|
||||
ref?: React.Ref<Element>;
|
||||
}
|
||||
|
||||
|
@ -114,7 +117,7 @@ export default function AccessibleButton<T extends keyof JSX.IntrinsicElements>(
|
|||
triggerOnMouseDown,
|
||||
...restProps
|
||||
}: Props<T>): JSX.Element {
|
||||
const newProps: IAccessibleButtonProps = restProps;
|
||||
const newProps: RenderedElementProps = restProps;
|
||||
if (disabled) {
|
||||
newProps["aria-disabled"] = true;
|
||||
newProps["disabled"] = true;
|
||||
|
|
|
@ -25,7 +25,7 @@ import Tooltip, { Alignment } from "./Tooltip";
|
|||
*
|
||||
* Extends that of {@link AccessibleButton}.
|
||||
*/
|
||||
interface Props extends React.ComponentProps<typeof AccessibleButton> {
|
||||
type Props<T extends keyof JSX.IntrinsicElements> = React.ComponentProps<typeof AccessibleButton<T>> & {
|
||||
/**
|
||||
* Title to show in the tooltip and use as aria-label
|
||||
*/
|
||||
|
@ -58,9 +58,9 @@ interface Props extends React.ComponentProps<typeof AccessibleButton> {
|
|||
* Function to call when the tooltip goes from shown to hidden.
|
||||
*/
|
||||
onHideTooltip?(ev: SyntheticEvent): void;
|
||||
}
|
||||
};
|
||||
|
||||
function AccessibleTooltipButton({
|
||||
function AccessibleTooltipButton<T extends keyof JSX.IntrinsicElements>({
|
||||
title,
|
||||
tooltip,
|
||||
children,
|
||||
|
@ -69,7 +69,7 @@ function AccessibleTooltipButton({
|
|||
onHideTooltip,
|
||||
tooltipClassName,
|
||||
...props
|
||||
}: Props): JSX.Element {
|
||||
}: Props<T>): JSX.Element {
|
||||
const [hover, setHover] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { ComponentProps } from "react";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import Modal from "../../../Modal";
|
||||
import InfoDialog from "../dialogs/InfoDialog";
|
||||
import AccessibleButton, { IAccessibleButtonProps } from "./AccessibleButton";
|
||||
import AccessibleButton from "./AccessibleButton";
|
||||
|
||||
export interface LearnMoreProps extends IAccessibleButtonProps {
|
||||
type Props = Omit<ComponentProps<typeof AccessibleButton>, "kind" | "onClick" | "className"> & {
|
||||
title: string;
|
||||
description: string | React.ReactNode;
|
||||
}
|
||||
};
|
||||
|
||||
const LearnMore: React.FC<LearnMoreProps> = ({ title, description, ...rest }) => {
|
||||
const LearnMore: React.FC<Props> = ({ title, description, ...rest }) => {
|
||||
const onClick = (): void => {
|
||||
Modal.createDialog(InfoDialog, {
|
||||
title,
|
||||
|
|
|
@ -35,7 +35,6 @@ import FacePile from "../elements/FacePile";
|
|||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { CallDuration, SessionDuration } from "../voip/CallDuration";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { ContinueKind } from "../auth/InteractiveAuthEntryComponents";
|
||||
|
||||
const MAX_FACES = 8;
|
||||
|
||||
|
@ -127,7 +126,7 @@ const ActiveLoadedCallEvent = forwardRef<any, ActiveLoadedCallEventProps>(({ mxE
|
|||
);
|
||||
|
||||
const [buttonText, buttonKind, onButtonClick] = useMemo<
|
||||
[string, ContinueKind, null | ((ev: ButtonEvent) => void)]
|
||||
[string, AccessibleButtonKind, null | ((ev: ButtonEvent) => void)]
|
||||
>(() => {
|
||||
switch (connectionState) {
|
||||
case ConnectionState.Disconnected:
|
||||
|
|
|
@ -73,11 +73,7 @@ export default function ExtraTile({
|
|||
);
|
||||
if (isMinimized) nameContainer = null;
|
||||
|
||||
let Button = RovingAccessibleButton;
|
||||
if (isMinimized) {
|
||||
Button = RovingAccessibleTooltipButton;
|
||||
}
|
||||
|
||||
const Button = isMinimized ? RovingAccessibleTooltipButton : RovingAccessibleButton;
|
||||
return (
|
||||
<Button
|
||||
className={classes}
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ForwardedRef, forwardRef } from "react";
|
||||
import React, { ForwardedRef, forwardRef, FunctionComponent } from "react";
|
||||
import { FormattingFunctions, MappedSuggestion } from "@matrix-org/matrix-wysiwyg";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
|
@ -120,6 +120,6 @@ const WysiwygAutocomplete = forwardRef(
|
|||
},
|
||||
);
|
||||
|
||||
WysiwygAutocomplete.displayName = "WysiwygAutocomplete";
|
||||
(WysiwygAutocomplete as FunctionComponent).displayName = "WysiwygAutocomplete";
|
||||
|
||||
export { WysiwygAutocomplete };
|
||||
|
|
|
@ -15,18 +15,25 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
import React, { ComponentProps } from "react";
|
||||
|
||||
import { Icon as CaretIcon } from "../../../../../res/img/feather-customised/dropdown-arrow.svg";
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import AccessibleTooltipButton from "../../elements/AccessibleTooltipButton";
|
||||
|
||||
interface Props extends React.ComponentProps<typeof AccessibleTooltipButton> {
|
||||
type Props<T extends keyof JSX.IntrinsicElements> = Omit<
|
||||
ComponentProps<typeof AccessibleTooltipButton<T>>,
|
||||
"aria-label" | "title" | "kind" | "className" | "onClick"
|
||||
> & {
|
||||
isExpanded: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
};
|
||||
|
||||
export const DeviceExpandDetailsButton: React.FC<Props> = ({ isExpanded, onClick, ...rest }) => {
|
||||
export const DeviceExpandDetailsButton = <T extends keyof JSX.IntrinsicElements>({
|
||||
isExpanded,
|
||||
onClick,
|
||||
...rest
|
||||
}: Props<T>): JSX.Element => {
|
||||
const label = isExpanded ? _t("settings|sessions|hide_details") : _t("settings|sessions|show_details");
|
||||
return (
|
||||
<AccessibleTooltipButton
|
||||
|
|
|
@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { ComponentProps } from "react";
|
||||
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import LearnMore, { LearnMoreProps } from "../../elements/LearnMore";
|
||||
import LearnMore from "../../elements/LearnMore";
|
||||
import { DeviceSecurityVariation } from "./types";
|
||||
|
||||
interface Props extends Omit<LearnMoreProps, "title" | "description"> {
|
||||
type Props = Omit<ComponentProps<typeof LearnMore>, "title" | "description"> & {
|
||||
variation: DeviceSecurityVariation;
|
||||
}
|
||||
};
|
||||
|
||||
const securityCardContent: Record<
|
||||
DeviceSecurityVariation,
|
||||
|
|
|
@ -48,7 +48,10 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
|||
import { useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
|
||||
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
||||
|
||||
interface IButtonProps extends Omit<ComponentProps<typeof AccessibleTooltipButton>, "title" | "onClick" | "size"> {
|
||||
type ButtonProps<T extends keyof JSX.IntrinsicElements> = Omit<
|
||||
ComponentProps<typeof AccessibleTooltipButton<T>>,
|
||||
"title" | "onClick" | "size"
|
||||
> & {
|
||||
space?: Room;
|
||||
spaceKey?: SpaceKey;
|
||||
className?: string;
|
||||
|
@ -61,9 +64,9 @@ interface IButtonProps extends Omit<ComponentProps<typeof AccessibleTooltipButto
|
|||
innerRef?: RefObject<HTMLElement>;
|
||||
ContextMenuComponent?: ComponentType<ComponentProps<typeof SpaceContextMenu>>;
|
||||
onClick?(ev?: ButtonEvent): void;
|
||||
}
|
||||
};
|
||||
|
||||
export const SpaceButton: React.FC<IButtonProps> = ({
|
||||
export const SpaceButton = <T extends keyof JSX.IntrinsicElements>({
|
||||
space,
|
||||
spaceKey: _spaceKey,
|
||||
className,
|
||||
|
@ -77,7 +80,7 @@ export const SpaceButton: React.FC<IButtonProps> = ({
|
|||
innerRef,
|
||||
ContextMenuComponent,
|
||||
...props
|
||||
}) => {
|
||||
}: ButtonProps<T>): JSX.Element => {
|
||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLElement>(innerRef);
|
||||
const [onFocus, isActive] = useRovingTabIndex(handle);
|
||||
const tabIndex = isActive ? 0 : -1;
|
||||
|
|
|
@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createRef, useState } from "react";
|
||||
import React, { ComponentProps, createRef, useState } from "react";
|
||||
import classNames from "classnames";
|
||||
import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
|
||||
|
||||
|
@ -42,14 +42,13 @@ const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the butt
|
|||
|
||||
const CONTROLS_HIDE_DELAY = 2000;
|
||||
|
||||
interface IButtonProps extends Omit<React.ComponentProps<typeof AccessibleTooltipButton>, "title"> {
|
||||
type ButtonProps = Omit<ComponentProps<typeof AccessibleTooltipButton>, "title" | "element"> & {
|
||||
state: boolean;
|
||||
className: string;
|
||||
onLabel?: string;
|
||||
offLabel?: string;
|
||||
}
|
||||
};
|
||||
|
||||
const LegacyCallViewToggleButton: React.FC<IButtonProps> = ({
|
||||
const LegacyCallViewToggleButton: React.FC<ButtonProps> = ({
|
||||
children,
|
||||
state: isOn,
|
||||
className,
|
||||
|
@ -74,7 +73,7 @@ const LegacyCallViewToggleButton: React.FC<IButtonProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
interface IDropdownButtonProps extends IButtonProps {
|
||||
interface IDropdownButtonProps extends ButtonProps {
|
||||
deviceKinds: MediaDeviceKindEnum[];
|
||||
}
|
||||
|
||||
|
|
|
@ -14,15 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, {
|
||||
ComponentClass,
|
||||
createContext,
|
||||
forwardRef,
|
||||
PropsWithoutRef,
|
||||
ForwardRefExoticComponent,
|
||||
useContext,
|
||||
RefAttributes,
|
||||
} from "react";
|
||||
import React, { ComponentClass, createContext, forwardRef, useContext } from "react";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
// This context is available to components under LoggedInView,
|
||||
|
@ -42,10 +34,9 @@ export function useMatrixClientContext(): MatrixClient {
|
|||
|
||||
const matrixHOC = <ComposedComponentProps extends {}>(
|
||||
ComposedComponent: ComponentClass<ComposedComponentProps>,
|
||||
): ForwardRefExoticComponent<
|
||||
PropsWithoutRef<Omit<ComposedComponentProps, "mxClient">> &
|
||||
RefAttributes<InstanceType<ComponentClass<ComposedComponentProps>>>
|
||||
> => {
|
||||
): ((
|
||||
props: Omit<ComposedComponentProps, "mxClient"> & React.RefAttributes<InstanceType<typeof ComposedComponent>>,
|
||||
) => React.ReactElement | null) => {
|
||||
type ComposedComponentInstance = InstanceType<typeof ComposedComponent>;
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
|
|
Loading…
Reference in New Issue