Merge pull request #4976 from matrix-org/t3chguy/a11y-toolbar-breadcrumbs

Wire up new room list breadcrumbs as an ARIA Toolbar
pull/21833/head
Michael Telatynski 2020-07-16 12:52:59 +01:00 committed by GitHub
commit f4da1b6f6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 135 additions and 43 deletions

View File

@ -23,12 +23,11 @@ import React, {
useRef,
useReducer,
Reducer,
RefObject,
Dispatch,
} from "react";
import {Key} from "../Keyboard";
import AccessibleButton from "../components/views/elements/AccessibleButton";
import {FocusHandler, Ref} from "./roving/types";
/**
* Module to simplify implementing the Roving TabIndex accessibility technique
@ -45,8 +44,6 @@ import AccessibleButton from "../components/views/elements/AccessibleButton";
const DOCUMENT_POSITION_PRECEDING = 2;
type Ref = RefObject<HTMLElement>;
export interface IState {
activeRef: Ref;
refs: Ref[];
@ -202,8 +199,6 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEn
</RovingTabIndexContext.Provider>;
};
type FocusHandler = () => void;
// Hook to register a roving tab index
// inputRef parameter specifies the ref to use
// onFocus should be called when the index gained focus in any manner
@ -244,28 +239,7 @@ export const useRovingTabIndex = (inputRef: Ref): [FocusHandler, boolean, Ref] =
return [onFocus, isActive, ref];
};
interface IRovingTabIndexWrapperProps {
inputRef?: Ref;
children(renderProps: {
onFocus: FocusHandler;
isActive: boolean;
ref: Ref;
});
}
// Wrapper to allow use of useRovingTabIndex outside of React Functional Components.
export const RovingTabIndexWrapper: React.FC<IRovingTabIndexWrapperProps> = ({children, inputRef}) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
return children({onFocus, isActive, ref});
};
interface IRovingAccessibleButtonProps extends Omit<React.ComponentProps<typeof AccessibleButton>, "onFocus" | "inputRef" | "tabIndex"> {
inputRef?: Ref;
}
// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components.
export const RovingAccessibleButton: React.FC<IRovingAccessibleButtonProps> = ({inputRef, ...props}) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
return <AccessibleButton {...props} onFocus={onFocus} inputRef={ref} tabIndex={isActive ? 0 : -1} />;
};
// re-export the semantic helper components for simplicity
export {RovingTabIndexWrapper} from "./roving/RovingTabIndexWrapper";
export {RovingAccessibleButton} from "./roving/RovingAccessibleButton";
export {RovingAccessibleTooltipButton} from "./roving/RovingAccessibleTooltipButton";

View File

@ -0,0 +1,32 @@
/*
Copyright 2020 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 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>, "onFocus" | "inputRef" | "tabIndex"> {
inputRef?: Ref;
}
// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components.
export const RovingAccessibleButton: React.FC<IProps> = ({inputRef, ...props}) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
return <AccessibleButton {...props} onFocus={onFocus} inputRef={ref} tabIndex={isActive ? 0 : -1} />;
};

View File

@ -0,0 +1,32 @@
/*
Copyright 2020 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 from "react";
import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
import {useRovingTabIndex} from "../RovingTabIndex";
import {Ref} from "./types";
interface IProps extends Omit<React.ComponentProps<typeof AccessibleTooltipButton>, "onFocus" | "inputRef" | "tabIndex"> {
inputRef?: Ref;
}
// Wrapper to allow use of useRovingTabIndex for simple AccessibleTooltipButtons outside of React Functional Components.
export const RovingAccessibleTooltipButton: React.FC<IProps> = ({inputRef, ...props}) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
return <AccessibleTooltipButton {...props} onFocus={onFocus} inputRef={ref} tabIndex={isActive ? 0 : -1} />;
};

View File

@ -0,0 +1,36 @@
/*
Copyright 2020 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 from "react";
import AccessibleButton from "../../components/views/elements/AccessibleButton";
import {useRovingTabIndex} from "../RovingTabIndex";
import {FocusHandler, Ref} from "./types";
interface IProps {
inputRef?: Ref;
children(renderProps: {
onFocus: FocusHandler;
isActive: boolean;
ref: Ref;
});
}
// Wrapper to allow use of useRovingTabIndex outside of React Functional Components.
export const RovingTabIndexWrapper: React.FC<IProps> = ({children, inputRef}) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
return children({onFocus, isActive, ref});
};

View File

@ -0,0 +1,21 @@
/*
Copyright 2020 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 {RefObject} from "react";
export type Ref = RefObject<HTMLElement>;
export type FocusHandler = () => void;

View File

@ -16,7 +16,7 @@ limitations under the License.
*/
import React from 'react';
import classnames from 'classnames';
import classNames from 'classnames';
import AccessibleButton from "./AccessibleButton";
import {IProps} from "./AccessibleButton";
@ -52,15 +52,11 @@ export default class AccessibleTooltipButton extends React.PureComponent<IToolti
};
render() {
const {title, children, ...props} = this.props;
const tooltipClassName = classnames(
"mx_AccessibleTooltipButton_tooltip",
this.props.tooltipClassName,
);
const {title, children, tooltipClassName, ...props} = this.props;
const tip = this.state.hover ? <Tooltip
className="mx_AccessibleTooltipButton_container"
tooltipClassName={tooltipClassName}
tooltipClassName={classNames("mx_AccessibleTooltipButton_tooltip", tooltipClassName)}
label={title}
/> : <div />;
return (

View File

@ -25,7 +25,8 @@ import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import { CSSTransition } from "react-transition-group";
import RoomListStore from "../../../stores/room-list/RoomListStore2";
import { DefaultTagID } from "../../../stores/room-list/models";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { RovingAccessibleTooltipButton } from "../../../accessibility/RovingTabIndex";
import Toolbar from "../../../accessibility/Toolbar";
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367
@ -86,7 +87,7 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
const roomTags = RoomListStore.instance.getTagsForRoom(r);
const roomTag = roomTags.includes(DefaultTagID.DM) ? DefaultTagID.DM : roomTags[0];
return (
<AccessibleTooltipButton
<RovingAccessibleTooltipButton
className="mx_RoomBreadcrumbs2_crumb"
key={r.roomId}
onClick={() => this.viewRoom(r, i)}
@ -101,7 +102,7 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
displayBadge={true}
forceCount={true}
/>
</AccessibleTooltipButton>
</RovingAccessibleTooltipButton>
);
});
@ -112,9 +113,9 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
appear={true} in={this.state.doAnimation} timeout={640}
classNames='mx_RoomBreadcrumbs2'
>
<div className='mx_RoomBreadcrumbs2'>
<Toolbar className='mx_RoomBreadcrumbs2'>
{tiles.slice(this.state.skipFirst ? 1 : 0)}
</div>
</Toolbar>
</CSSTransition>
);
} else {