Tooltip: Improve the accessibility of the composer and the rich text editor (#12459)
* Use `AccessibleButton` in `RovingAccessibleTooltipButton` * Update snapshots * Update @vector-im/compound-web * Update composer * Update formating buttons * Update snapshots * Remove placement * Update snapshots * Use kbd * Update ``@vector-im/compound-web`
This commit is contained in:
parent
6e31f69118
commit
77a724526e
9 changed files with 36 additions and 63 deletions
|
@ -76,7 +76,7 @@
|
|||
"@sentry/browser": "^7.0.0",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@vector-im/compound-design-tokens": "^1.2.0",
|
||||
"@vector-im/compound-web": "^4.1.2",
|
||||
"@vector-im/compound-web": "^4.2.0",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||
"@zxcvbn-ts/language-en": "^3.0.2",
|
||||
|
|
|
@ -102,9 +102,4 @@ limitations under the License.
|
|||
font-weight: var(--cpd-font-weight-semibold);
|
||||
min-width: 54px;
|
||||
text-align: center;
|
||||
|
||||
.mx_MessageComposerFormatBar_tooltipShortcut {
|
||||
font-size: $font-9px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,19 +64,11 @@ limitations under the License.
|
|||
}
|
||||
}
|
||||
|
||||
.mx_FormattingButtons_Tooltip {
|
||||
padding: 0 2px 0 2px;
|
||||
|
||||
.mx_FormattingButtons_Tooltip_KeyboardShortcut {
|
||||
color: $tertiary-content;
|
||||
|
||||
kbd {
|
||||
margin-top: 2px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
text-transform: capitalize;
|
||||
font-size: 12px;
|
||||
font-family: Inter, sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,12 +92,12 @@ type Props<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> &
|
|||
/**
|
||||
* The tooltip to show on hover or focus.
|
||||
*/
|
||||
title?: string;
|
||||
title?: TooltipProps["label"];
|
||||
/**
|
||||
* The caption is a secondary text displayed under the `title` of the tooltip.
|
||||
* Only valid when used in conjunction with `title`.
|
||||
*/
|
||||
caption?: string;
|
||||
caption?: TooltipProps["caption"];
|
||||
/**
|
||||
* The placement of the tooltip.
|
||||
*/
|
||||
|
|
|
@ -35,7 +35,6 @@ import { makeRoomPermalink, RoomPermalinkCreator } from "../../../utils/permalin
|
|||
import E2EIcon from "./E2EIcon";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { aboveLeftOf, MenuProps } from "../../structures/ContextMenu";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import ReplyPreview from "./ReplyPreview";
|
||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||
import VoiceRecordComposerTile from "./VoiceRecordComposerTile";
|
||||
|
@ -52,7 +51,7 @@ import UIStore, { UI_EVENTS } from "../../../stores/UIStore";
|
|||
import RoomContext from "../../../contexts/RoomContext";
|
||||
import { SettingUpdatedPayload } from "../../../dispatcher/payloads/SettingUpdatedPayload";
|
||||
import MessageComposerButtons from "./MessageComposerButtons";
|
||||
import { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||
import { isLocalRoom } from "../../../utils/localRoom/isLocalRoom";
|
||||
import { Features } from "../../../settings/Settings";
|
||||
|
@ -75,7 +74,7 @@ interface ISendButtonProps {
|
|||
|
||||
function SendButton(props: ISendButtonProps): JSX.Element {
|
||||
return (
|
||||
<AccessibleTooltipButton
|
||||
<AccessibleButton
|
||||
className="mx_MessageComposer_sendMessage"
|
||||
onClick={props.onClick}
|
||||
title={props.title ?? _t("composer|send_button_title")}
|
||||
|
|
|
@ -19,7 +19,6 @@ import { IEventRelation, Room, MatrixClient, THREAD_RELATION_TYPE, M_POLL_START
|
|||
import React, { createContext, ReactElement, ReactNode, useContext, useRef } from "react";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { CollapsibleButton } from "./CollapsibleButton";
|
||||
import { MenuProps } from "../../structures/ContextMenu";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
|
@ -37,7 +36,7 @@ import IconizedContextMenu, { IconizedContextMenuOptionList } from "../context_m
|
|||
import { EmojiButton } from "./EmojiButton";
|
||||
import { filterBoolean } from "../../../utils/arrays";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
import { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
||||
|
||||
interface IProps {
|
||||
addEmoji: (emoji: string) => boolean;
|
||||
|
@ -128,7 +127,7 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => {
|
|||
<UploadButtonContextProvider roomId={room.roomId} relation={props.relation}>
|
||||
{mainButtons}
|
||||
{moreButtons.length > 0 && (
|
||||
<AccessibleTooltipButton
|
||||
<AccessibleButton
|
||||
className={moreOptionsClasses}
|
||||
onClick={props.toggleButtonMenu}
|
||||
title={_t("quick_settings|sidebar_settings")}
|
||||
|
|
|
@ -30,54 +30,42 @@ import { Icon as NumberedListIcon } from "../../../../../../res/img/element-icon
|
|||
import { Icon as CodeBlockIcon } from "../../../../../../res/img/element-icons/room/composer/code_block.svg";
|
||||
import { Icon as IndentIcon } from "../../../../../../res/img/element-icons/room/composer/indent_increase.svg";
|
||||
import { Icon as UnIndentIcon } from "../../../../../../res/img/element-icons/room/composer/indent_decrease.svg";
|
||||
import AccessibleTooltipButton from "../../../elements/AccessibleTooltipButton";
|
||||
import { Alignment } from "../../../elements/Tooltip";
|
||||
import { KeyboardShortcut } from "../../../settings/KeyboardShortcut";
|
||||
import { KeyCombo } from "../../../../../KeyBindingsManager";
|
||||
import { _t } from "../../../../../languageHandler";
|
||||
import { ButtonEvent } from "../../../elements/AccessibleButton";
|
||||
import AccessibleButton, { ButtonEvent } from "../../../elements/AccessibleButton";
|
||||
import { openLinkModal } from "./LinkModal";
|
||||
import { useComposerContext } from "../ComposerContext";
|
||||
import { KeyboardShortcut } from "../../../settings/KeyboardShortcut";
|
||||
import { KeyCombo } from "../../../../../KeyBindingsManager";
|
||||
|
||||
interface TooltipProps {
|
||||
interface ButtonProps {
|
||||
icon: ReactNode;
|
||||
actionState: ActionState;
|
||||
onClick: MouseEventHandler<HTMLButtonElement>;
|
||||
label: string;
|
||||
keyCombo?: KeyCombo;
|
||||
}
|
||||
|
||||
function Tooltip({ label, keyCombo }: TooltipProps): JSX.Element {
|
||||
return (
|
||||
<div className="mx_FormattingButtons_Tooltip">
|
||||
{label}
|
||||
{keyCombo && (
|
||||
<KeyboardShortcut value={keyCombo} className="mx_FormattingButtons_Tooltip_KeyboardShortcut" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ButtonProps extends TooltipProps {
|
||||
icon: ReactNode;
|
||||
actionState: ActionState;
|
||||
onClick: MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
function Button({ label, keyCombo, onClick, actionState, icon }: ButtonProps): JSX.Element {
|
||||
return (
|
||||
<AccessibleTooltipButton
|
||||
<AccessibleButton
|
||||
element="button"
|
||||
onClick={onClick as (e: ButtonEvent) => void}
|
||||
title={label}
|
||||
aria-label={label}
|
||||
className={classNames("mx_FormattingButtons_Button", {
|
||||
mx_FormattingButtons_active: actionState === "reversed",
|
||||
mx_FormattingButtons_Button_hover: actionState === "enabled",
|
||||
mx_FormattingButtons_disabled: actionState === "disabled",
|
||||
})}
|
||||
tooltip={keyCombo && <Tooltip label={label} keyCombo={keyCombo} />}
|
||||
forceHide={actionState === "disabled"}
|
||||
alignment={Alignment.Top}
|
||||
title={actionState === "disabled" ? undefined : label}
|
||||
caption={
|
||||
keyCombo && (
|
||||
<KeyboardShortcut value={keyCombo} className="mx_FormattingButtons_Tooltip_KeyboardShortcut" />
|
||||
)
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
{icon}
|
||||
</AccessibleTooltipButton>
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { ActionState, ActionTypes, AllActionStates, FormattingFunctions } from "@matrix-org/matrix-wysiwyg";
|
||||
|
||||
|
@ -135,7 +135,7 @@ describe("FormattingButtons", () => {
|
|||
const { label } = testCase;
|
||||
|
||||
await userEvent.hover(screen.getByLabelText(label));
|
||||
expect(screen.getByText(label)).toBeInTheDocument();
|
||||
await waitFor(() => expect(screen.getByText(label)).toBeInTheDocument());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -3097,10 +3097,10 @@
|
|||
dependencies:
|
||||
svg2vectordrawable "^2.9.1"
|
||||
|
||||
"@vector-im/compound-web@^4.1.2":
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-4.1.2.tgz#d8f9ba523700660942722a800c64406216bbbfea"
|
||||
integrity sha512-u/jj8HF8qpX1NU+sh6f/S1B7HUMGcoAGYLH0wc5lVbf6x6elBsYKD0LSa+/8NDPuQqVWMztu76chUsM5slC49w==
|
||||
"@vector-im/compound-web@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-4.2.0.tgz#16915a5e64c405360fc049ddfa39b5185725f950"
|
||||
integrity sha512-VSZxIFToDesjiiCGLOj+DrrKv1I0rtpzJbdylarJXY7REnHzVdgaBBtGm403iJ8KkZ2Rn16Mxe+P1/+VS4yiAA==
|
||||
dependencies:
|
||||
"@floating-ui/react" "^0.26.9"
|
||||
"@floating-ui/react-dom" "^2.0.8"
|
||||
|
|
Loading…
Reference in a new issue