mirror of https://github.com/vector-im/riot-web
Improve ThreadPanel ctx menu accessibility (#7217)
parent
9727a82a12
commit
fe24c8ad2a
|
@ -27,7 +27,6 @@ limitations under the License.
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 1.0;
|
||||
z-index: 5000;
|
||||
}
|
||||
|
||||
.mx_ContextualMenu {
|
||||
|
|
|
@ -120,7 +120,7 @@ limitations under the License.
|
|||
&:hover {
|
||||
background-color: $event-selected-color;
|
||||
}
|
||||
&[aria-selected="true"] {
|
||||
&[aria-checked="true"] {
|
||||
:first-child {
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ interface IState {
|
|||
// all options inside the menu should be of role=menuitem/menuitemcheckbox/menuitemradiobutton and have tabIndex={-1}
|
||||
// this will allow the ContextMenu to manage its own focus using arrow keys as per the ARIA guidelines.
|
||||
@replaceableComponent("structures.ContextMenu")
|
||||
export class ContextMenu extends React.PureComponent<IProps, IState> {
|
||||
export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
||||
private readonly initialFocus: HTMLElement;
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -411,6 +411,7 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
onClick={this.onClick}
|
||||
onContextMenu={this.onContextMenuPreventBubbling}
|
||||
>
|
||||
{ background }
|
||||
<div
|
||||
className={menuClasses}
|
||||
style={menuStyle}
|
||||
|
@ -419,7 +420,6 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
>
|
||||
{ body }
|
||||
</div>
|
||||
{ background }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -530,30 +530,22 @@ export const useContextMenu = <T extends any = HTMLElement>(): ContextMenuTuple<
|
|||
return [isOpen, button, open, close, setIsOpen];
|
||||
};
|
||||
|
||||
@replaceableComponent("structures.LegacyContextMenu")
|
||||
export default class LegacyContextMenu extends ContextMenu {
|
||||
render() {
|
||||
return this.renderMenu(false);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Deprecated, used only for dynamic Tooltips. Avoid using at all costs.
|
||||
export function createMenu(ElementClass, props) {
|
||||
const onFinished = function(...args) {
|
||||
ReactDOM.unmountComponentAtNode(getOrCreateContainer());
|
||||
|
||||
if (props && props.onFinished) {
|
||||
props.onFinished.apply(null, args);
|
||||
}
|
||||
props?.onFinished?.apply(null, args);
|
||||
};
|
||||
|
||||
const menu = <LegacyContextMenu
|
||||
const menu = <ContextMenu
|
||||
{...props}
|
||||
mountAsChild={true}
|
||||
hasBackground={false}
|
||||
onFinished={onFinished} // eslint-disable-line react/jsx-no-bind
|
||||
windowResize={onFinished} // eslint-disable-line react/jsx-no-bind
|
||||
>
|
||||
<ElementClass {...props} onFinished={onFinished} />
|
||||
</LegacyContextMenu>;
|
||||
</ContextMenu>;
|
||||
|
||||
ReactDOM.render(menu, getOrCreateContainer());
|
||||
|
||||
|
|
|
@ -24,12 +24,11 @@ import ResizeNotifier from '../../utils/ResizeNotifier';
|
|||
import MatrixClientContext from '../../contexts/MatrixClientContext';
|
||||
import { _t } from '../../languageHandler';
|
||||
import { ContextMenuButton } from '../../accessibility/context_menu/ContextMenuButton';
|
||||
import ContextMenu, { ChevronFace, useContextMenu } from './ContextMenu';
|
||||
import ContextMenu, { ChevronFace, MenuItemRadio, useContextMenu } from './ContextMenu';
|
||||
import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext';
|
||||
import TimelinePanel from './TimelinePanel';
|
||||
import { Layout } from '../../settings/enums/Layout';
|
||||
import { useEventEmitter } from '../../hooks/useEventEmitter';
|
||||
import AccessibleButton from '../views/elements/AccessibleButton';
|
||||
import { TileShape } from '../views/rooms/EventTile';
|
||||
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
|
||||
|
||||
|
@ -98,14 +97,14 @@ export const ThreadPanelHeaderFilterOptionItem = ({
|
|||
onClick: () => void;
|
||||
isSelected: boolean;
|
||||
}) => {
|
||||
return <AccessibleButton
|
||||
aria-selected={isSelected}
|
||||
return <MenuItemRadio
|
||||
active={isSelected}
|
||||
className="mx_ThreadPanel_Header_FilterOptionItem"
|
||||
onClick={onClick}
|
||||
>
|
||||
<span>{ label }</span>
|
||||
<span>{ description }</span>
|
||||
</AccessibleButton>;
|
||||
</MenuItemRadio>;
|
||||
};
|
||||
|
||||
export const ThreadPanelHeader = ({ filterOption, setFilterOption }: {
|
||||
|
@ -141,8 +140,8 @@ export const ThreadPanelHeader = ({ filterOption, setFilterOption }: {
|
|||
top={0}
|
||||
right={25}
|
||||
onFinished={closeMenu}
|
||||
managed={false}
|
||||
chevronFace={ChevronFace.Top}
|
||||
mountAsChild={true}
|
||||
>
|
||||
{ contextMenuOptions }
|
||||
</ContextMenu> : null;
|
||||
|
|
|
@ -21,7 +21,7 @@ import MemberAvatar from '../avatars/MemberAvatar';
|
|||
import classNames from 'classnames';
|
||||
import StatusMessageContextMenu from "../context_menus/StatusMessageContextMenu";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { ChevronFace, ContextMenu, ContextMenuButton } from "../../structures/ContextMenu";
|
||||
import ContextMenu, { ChevronFace, ContextMenuButton } from "../../structures/ContextMenu";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { ResizeMethod } from "matrix-js-sdk/src/@types/partials";
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { ContextMenu, IProps as IContextMenuProps, MenuItem } from '../../structures/ContextMenu';
|
||||
import ContextMenu, { IProps as IContextMenuProps, MenuItem } from '../../structures/ContextMenu';
|
||||
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||
import CallHandler from '../../../CallHandler';
|
||||
import InviteDialog, { KIND_CALL_TRANSFER } from '../dialogs/InviteDialog';
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
import * as React from "react";
|
||||
import { createRef } from "react";
|
||||
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import { ContextMenu, IProps as IContextMenuProps } from '../../structures/ContextMenu';
|
||||
import ContextMenu, { IProps as IContextMenuProps } from '../../structures/ContextMenu';
|
||||
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||
import Field from "../elements/Field";
|
||||
import DialPad from '../voip/DialPad';
|
||||
|
|
|
@ -17,9 +17,8 @@ limitations under the License.
|
|||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import {
|
||||
import ContextMenu, {
|
||||
ChevronFace,
|
||||
ContextMenu,
|
||||
IProps as IContextMenuProps,
|
||||
MenuItem,
|
||||
MenuItemCheckbox, MenuItemRadio,
|
||||
|
|
|
@ -21,9 +21,8 @@ import { IProtocol } from "matrix-js-sdk/src/client";
|
|||
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import { instanceForInstanceId } from '../../../utils/DirectoryUtils';
|
||||
import {
|
||||
import ContextMenu, {
|
||||
ChevronFace,
|
||||
ContextMenu,
|
||||
ContextMenuButton,
|
||||
MenuGroup,
|
||||
MenuItem,
|
||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
|||
import TagTile from './TagTile';
|
||||
|
||||
import React from 'react';
|
||||
import { ContextMenu, toRightOf, useContextMenu } from "../../structures/ContextMenu";
|
||||
import ContextMenu, { toRightOf, useContextMenu } from "../../structures/ContextMenu";
|
||||
import * as sdk from '../../../index';
|
||||
|
||||
export default function DNDTagTile(props) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import dis from '../../../dispatcher/dispatcher';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import classNames from 'classnames';
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { ContextMenu, ContextMenuButton, toRightOf } from "../../structures/ContextMenu";
|
||||
import ContextMenu, { ContextMenuButton, toRightOf } from "../../structures/ContextMenu";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
|
|
@ -23,7 +23,7 @@ import type { Relations } from 'matrix-js-sdk/src/models/relations';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { Action } from '../../../dispatcher/actions';
|
||||
import { aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu } from '../../structures/ContextMenu';
|
||||
import ContextMenu, { aboveLeftOf, ContextMenuTooltipButton, useContextMenu } from '../../structures/ContextMenu';
|
||||
import { isContentActionable, canEditContent } from '../../../utils/EventUtils';
|
||||
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||
import Toolbar from "../../../accessibility/Toolbar";
|
||||
|
|
|
@ -24,7 +24,7 @@ import { _t } from '../../../languageHandler';
|
|||
import { isContentActionable } from '../../../utils/EventUtils';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton";
|
||||
import { aboveLeftOf, ContextMenu, useContextMenu } from "../../structures/ContextMenu";
|
||||
import ContextMenu, { aboveLeftOf, useContextMenu } from "../../structures/ContextMenu";
|
||||
import ReactionPicker from "../emojipicker/ReactionPicker";
|
||||
import ReactionsRowButton from "./ReactionsRowButton";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
|
|
|
@ -27,9 +27,8 @@ import { makeRoomPermalink, RoomPermalinkCreator } from '../../../utils/permalin
|
|||
import ContentMessages from '../../../ContentMessages';
|
||||
import E2EIcon from './E2EIcon';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {
|
||||
import ContextMenu, {
|
||||
aboveLeftOf,
|
||||
ContextMenu,
|
||||
useContextMenu,
|
||||
MenuItem,
|
||||
AboveLeftOf,
|
||||
|
|
|
@ -26,9 +26,8 @@ import { _t } from "../../../languageHandler";
|
|||
import AccessibleButton from "../../views/elements/AccessibleButton";
|
||||
import RoomTile from "./RoomTile";
|
||||
import { ListLayout } from "../../../stores/room-list/ListLayout";
|
||||
import {
|
||||
import ContextMenu, {
|
||||
ChevronFace,
|
||||
ContextMenu,
|
||||
ContextMenuTooltipButton,
|
||||
StyledMenuItemCheckbox,
|
||||
StyledMenuItemRadio,
|
||||
|
|
|
@ -13,6 +13,7 @@ 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 from 'react';
|
||||
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
|
@ -24,7 +25,7 @@ import WidgetUtils, { IWidgetEvent } from '../../../utils/WidgetUtils';
|
|||
import PersistedElement from "../elements/PersistedElement";
|
||||
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { ChevronFace, ContextMenu } from "../../structures/ContextMenu";
|
||||
import ContextMenu, { ChevronFace } from "../../structures/ContextMenu";
|
||||
import { WidgetType } from "../../../widgets/WidgetType";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore";
|
||||
|
|
|
@ -18,7 +18,7 @@ import React, { useMemo } from "react";
|
|||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { alwaysAboveRightOf, ChevronFace, ContextMenu, useContextMenu } from "../../structures/ContextMenu";
|
||||
import ContextMenu, { alwaysAboveRightOf, ChevronFace, useContextMenu } from "../../structures/ContextMenu";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||
import { MetaSpace } from "../../../stores/spaces";
|
||||
|
|
|
@ -22,7 +22,7 @@ import { HistoryVisibility, Preset } from "matrix-js-sdk/src/@types/partials";
|
|||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { ChevronFace, ContextMenu } from "../../structures/ContextMenu";
|
||||
import ContextMenu, { ChevronFace } from "../../structures/ContextMenu";
|
||||
import createRoom, { IOpts as ICreateOpts } from "../../../createRoom";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import SpaceBasicSettings, { SpaceAvatar } from "./SpaceBasicSettings";
|
||||
|
|
|
@ -70,7 +70,7 @@ describe('ThreadPanel', () => {
|
|||
wrapper.find(ContextMenuButton).simulate('click');
|
||||
const found = wrapper.find(ThreadPanelHeaderFilterOptionItem);
|
||||
expect(found.length).toEqual(2);
|
||||
const foundButton = found.find('[aria-selected=true]').first();
|
||||
const foundButton = found.find('[aria-checked=true]').first();
|
||||
expect(foundButton.text()).toEqual(`${_t("All threads")}${_t('Shows all threads from current room')}`);
|
||||
expect(foundButton).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -46,21 +46,21 @@ exports[`ThreadPanel Header expect that My filter for ThreadPanelHeader properly
|
|||
|
||||
exports[`ThreadPanel Header expect that ThreadPanelHeader has the correct option selected in the context menu 1`] = `
|
||||
<AccessibleButton
|
||||
aria-selected={true}
|
||||
aria-checked={true}
|
||||
className="mx_ThreadPanel_Header_FilterOptionItem"
|
||||
element="div"
|
||||
onClick={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
role="menuitemradio"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div
|
||||
aria-selected={true}
|
||||
aria-checked={true}
|
||||
className="mx_AccessibleButton mx_ThreadPanel_Header_FilterOptionItem"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
role="menuitemradio"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<span>
|
||||
All threads
|
||||
|
|
Loading…
Reference in New Issue