Tweak rendering of icons for accessibility (#31346)

* Tweak rendering of icons in dropdowns

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in composer format bar

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in jump to bottom button

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in quick settings button

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in left panel search

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Delint

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix margin

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in security user settings tab

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in space hierarchy

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update screenshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Delint

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Simplify

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tidy

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2025-11-27 16:03:19 +00:00
committed by GitHub
parent 771696e0f0
commit 5869c519ed
44 changed files with 1104 additions and 325 deletions

View File

@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import classNames from "classnames";
import React, { type JSX, type FunctionComponent, type Key, type PropsWithChildren, type ReactNode } from "react";
import { ChevronDownIcon, CheckIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { MenuItemRadio } from "../../accessibility/context_menu/MenuItemRadio";
import { type ButtonEvent } from "../views/elements/AccessibleButton";
@@ -42,9 +43,10 @@ export function GenericDropdownMenuOption<T extends Key>({
className="mx_GenericDropdownMenu_Option mx_GenericDropdownMenu_Option--item"
onClick={onClick}
>
{isSelected && <CheckIcon className="mx_GenericDropdownMenu_Option--checkIcon" />}
<div className="mx_GenericDropdownMenu_Option--label">
<span>{label}</span>
<span>{description}</span>
{description && <span>{description}</span>}
</div>
{adornment}
</MenuItemRadio>
@@ -202,6 +204,7 @@ export function GenericDropdownMenu<T>({
}}
>
{selectedLabel(selected)}
<ChevronDownIcon />
</ContextMenuButton>
{contextMenu}
</>

View File

@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import classNames from "classnames";
import React from "react";
import { SearchIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { ALTERNATE_KEY_NAME } from "../../accessibility/KeyboardShortcuts";
import defaultDispatcher from "../../dispatcher/dispatcher";
@@ -34,8 +35,6 @@ export default class RoomSearch extends React.PureComponent<IProps> {
"mx_RoomSearch_spotlightTrigger",
);
const icon = <div className="mx_RoomSearch_icon" />;
const shortcutPrompt = (
<kbd className="mx_RoomSearch_shortcutPrompt">
{IS_MAC ? "⌘ K" : _t(ALTERNATE_KEY_NAME[Key.CONTROL]) + " K"}
@@ -44,7 +43,7 @@ export default class RoomSearch extends React.PureComponent<IProps> {
return (
<AccessibleButton onClick={this.openSpotlight} className={classes} aria-label={_t("action|search")}>
{icon}
<SearchIcon className="mx_RoomSearch_icon" />
{!this.props.isMinimized && (
<div className="mx_RoomSearch_spotlightTriggerText">{_t("action|search")}</div>
)}

View File

@@ -40,6 +40,7 @@ import classNames from "classnames";
import { sortBy, uniqBy } from "lodash";
import { logger } from "matrix-js-sdk/src/logger";
import { KnownMembership, type SpaceChildEventContent } from "matrix-js-sdk/src/types";
import { ChevronDownIcon, CheckIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import defaultDispatcher from "../../dispatcher/dispatcher";
import { _t } from "../../languageHandler";
@@ -251,7 +252,12 @@ const Tile: React.FC<ITileProps> = ({
let joinedSection: ReactElement | undefined;
if (joinedRoom) {
joinedSection = <div className="mx_SpaceHierarchy_roomTile_joined">{_t("common|joined")}</div>;
joinedSection = (
<div className="mx_SpaceHierarchy_roomTile_joined">
<CheckIcon />
{_t("common|joined")}
</div>
);
}
let suggestedSection: ReactElement | undefined;
@@ -294,7 +300,9 @@ const Tile: React.FC<ITileProps> = ({
ev.stopPropagation();
toggleShowChildren();
}}
/>
>
<ChevronDownIcon />
</div>
);
if (showChildren) {

View File

@@ -10,7 +10,6 @@ import { without } from "lodash";
import React, { useCallback, useEffect, useState } from "react";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { MenuItemRadio } from "../../../accessibility/context_menu/MenuItemRadio";
import { _t } from "../../../languageHandler";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import Modal from "../../../Modal";
@@ -22,6 +21,7 @@ import {
type AdditionalOptionsProps,
GenericDropdownMenu,
type GenericDropdownMenuItem,
GenericDropdownMenuOption,
} from "../../structures/GenericDropdownMenu";
import TextInputDialog from "../dialogs/TextInputDialog";
import AccessibleButton from "../elements/AccessibleButton";
@@ -200,9 +200,8 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
({ closeMenu }: AdditionalOptionsProps) => (
<>
<span className="mx_GenericDropdownMenu_divider" />
<MenuItemRadio
active={false}
className="mx_GenericDropdownMenu_Option mx_GenericDropdownMenu_Option--item"
<GenericDropdownMenuOption
key="add-server"
onClick={async (): Promise<void> => {
closeMenu();
const { finished } = Modal.createDialog(
@@ -229,13 +228,9 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
});
}
}}
>
<div className="mx_GenericDropdownMenu_Option--label">
<span className="mx_NetworkDropdown_addServer">
{_t("spotlight|public_rooms|network_dropdown_add_server_option")}
</span>
</div>
</MenuItemRadio>
isSelected={false}
label={_t("spotlight|public_rooms|network_dropdown_add_server_option")}
/>
</>
),
[allServers, setConfig, setUserDefinedServers, userDefinedServers],

View File

@@ -17,6 +17,7 @@ import React, {
type Ref,
} from "react";
import classnames from "classnames";
import { ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import AccessibleButton, { type ButtonEvent } from "./AccessibleButton";
import { _t } from "../../../languageHandler";
@@ -411,7 +412,7 @@ export default class Dropdown extends React.Component<DropdownProps, IState> {
onKeyDown={this.onKeyDown}
>
{currentValue}
<span className="mx_Dropdown_arrow" />
<ChevronDownIcon className="mx_Dropdown_arrow" />
{menu}
</AccessibleButton>
</div>

View File

@@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import classNames from "classnames";
import { ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler";
import AccessibleButton, { type ButtonEvent } from "../elements/AccessibleButton";
@@ -32,7 +33,9 @@ const JumpToBottomButton: React.FC<IProps> = (props) => {
className="mx_JumpToBottomButton_scrollDown"
title={_t("room|jump_to_bottom_button")}
onClick={props.onScrollToBottomClick}
/>
>
<ChevronDownIcon />
</AccessibleButton>
{badge}
</div>
);

View File

@@ -6,8 +6,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React, { createRef } from "react";
import React, { createRef, type JSX } from "react";
import classNames from "classnames";
import {
BoldIcon,
ItalicIcon,
StrikethroughIcon,
InlineCodeIcon,
QuoteIcon,
LinkIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler";
import { RovingAccessibleButton } from "../../../accessibility/RovingTabIndex";
@@ -54,41 +62,41 @@ export default class MessageComposerFormatBar extends React.PureComponent<IProps
<FormatButton
label={_t("composer|format_bold")}
onClick={() => this.props.onAction(Formatting.Bold)}
icon="Bold"
icon={<BoldIcon />}
shortcut={this.props.shortcuts.bold}
visible={this.state.visible}
/>
<FormatButton
label={_t("composer|format_italics")}
onClick={() => this.props.onAction(Formatting.Italics)}
icon="Italic"
icon={<ItalicIcon />}
shortcut={this.props.shortcuts.italics}
visible={this.state.visible}
/>
<FormatButton
label={_t("composer|format_strikethrough")}
onClick={() => this.props.onAction(Formatting.Strikethrough)}
icon="Strikethrough"
icon={<StrikethroughIcon />}
visible={this.state.visible}
/>
<FormatButton
label={_t("composer|format_code_block")}
onClick={() => this.props.onAction(Formatting.Code)}
icon="Code"
icon={<InlineCodeIcon />}
shortcut={this.props.shortcuts.code}
visible={this.state.visible}
/>
<FormatButton
label={_t("action|quote")}
onClick={() => this.props.onAction(Formatting.Quote)}
icon="Quote"
icon={<QuoteIcon />}
shortcut={this.props.shortcuts.quote}
visible={this.state.visible}
/>
<FormatButton
label={_t("composer|format_insert_link")}
onClick={() => this.props.onAction(Formatting.InsertLink)}
icon="InsertLink"
icon={<LinkIcon />}
shortcut={this.props.shortcuts.insert_link}
visible={this.state.visible}
/>
@@ -116,7 +124,7 @@ export default class MessageComposerFormatBar extends React.PureComponent<IProps
interface IFormatButtonProps {
label: string;
icon: string;
icon: JSX.Element;
shortcut?: string;
visible?: boolean;
onClick(): void;
@@ -124,8 +132,6 @@ interface IFormatButtonProps {
class FormatButton extends React.PureComponent<IFormatButtonProps> {
public render(): React.ReactNode {
const className = `mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIcon${this.props.icon}`;
// element="button" and type="button" are necessary for the buttons to work on WebKit,
// otherwise the text is deselected before onClick can ever be called
return (
@@ -136,8 +142,10 @@ class FormatButton extends React.PureComponent<IFormatButtonProps> {
aria-label={this.props.label}
title={this.props.label}
caption={this.props.shortcut}
className={className}
/>
className="mx_MessageComposerFormatBar_button"
>
{this.props.icon}
</RovingAccessibleButton>
);
}
}

View File

@@ -11,6 +11,7 @@ import { sleep } from "matrix-js-sdk/src/utils";
import { type Room, RoomEvent, type IServerVersions } from "matrix-js-sdk/src/matrix";
import { KnownMembership, type Membership } from "matrix-js-sdk/src/types";
import { logger } from "matrix-js-sdk/src/logger";
import { WarningIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../../../languageHandler";
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
@@ -305,6 +306,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
if (!privateShouldBeEncrypted(MatrixClientPeg.safeGet())) {
warning = (
<div className="mx_SecurityUserSettingsTab_warning">
<WarningIcon />
{_t("settings|security|e2ee_default_disabled_warning")}
</div>
);

View File

@@ -13,7 +13,9 @@ import {
UserProfileSolidIcon,
FavouriteSolidIcon,
PinSolidIcon,
SettingsSolidIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { IconButton, Text, Tooltip } from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import ContextMenu, { alwaysAboveRightOf, ChevronFace, useContextMenu } from "../../structures/ContextMenu";
@@ -34,7 +36,7 @@ import { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement";
const QuickSettingsButton: React.FC<{
isPanelCollapsed: boolean;
}> = ({ isPanelCollapsed = false }) => {
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLButtonElement>();
const { [MetaSpace.Favourites]: favouritesEnabled, [MetaSpace.People]: peopleEnabled } =
useSettingValue("Spaces.enabledMetaSpaces");
@@ -136,6 +138,35 @@ const QuickSettingsButton: React.FC<{
);
}
let button = (
<IconButton
aria-label={_t("quick_settings|title")}
className={classNames("mx_QuickSettingsButton", { expanded: !isPanelCollapsed })}
onClick={openMenu}
title={isPanelCollapsed ? _t("quick_settings|title") : undefined}
ref={handle}
aria-expanded={!isPanelCollapsed}
>
<>
<SettingsSolidIcon />
{/* This is dirty, but we need to add the label to the indicator icon */}
{!isPanelCollapsed && (
<Text className="mx_QuickSettingsButton_label" as="span" size="md" title={_t("common|settings")}>
{_t("common|settings")}
</Text>
)}
</>
</IconButton>
);
if (isPanelCollapsed) {
button = (
<Tooltip label={_t("quick_settings|title")} placement="right">
{button}
</Tooltip>
);
}
return (
<>
<ReleaseAnnouncement
@@ -145,16 +176,7 @@ const QuickSettingsButton: React.FC<{
closeLabel={_t("room_list|release_announcement|done")}
placement="right"
>
<AccessibleButton
className={classNames("mx_QuickSettingsButton", { expanded: !isPanelCollapsed })}
onClick={openMenu}
aria-label={_t("quick_settings|title")}
title={isPanelCollapsed ? _t("quick_settings|title") : undefined}
ref={handle}
aria-expanded={!isPanelCollapsed}
>
{!isPanelCollapsed ? _t("common|settings") : null}
</AccessibleButton>
{button}
</ReleaseAnnouncement>
{contextMenu}