Merge pull request #4976 from matrix-org/t3chguy/a11y-toolbar-breadcrumbs
Wire up new room list breadcrumbs as an ARIA Toolbar
This commit is contained in:
commit
f4da1b6f6b
7 changed files with 135 additions and 43 deletions
|
@ -23,12 +23,11 @@ import React, {
|
||||||
useRef,
|
useRef,
|
||||||
useReducer,
|
useReducer,
|
||||||
Reducer,
|
Reducer,
|
||||||
RefObject,
|
|
||||||
Dispatch,
|
Dispatch,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
import {Key} from "../Keyboard";
|
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
|
* 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;
|
const DOCUMENT_POSITION_PRECEDING = 2;
|
||||||
|
|
||||||
type Ref = RefObject<HTMLElement>;
|
|
||||||
|
|
||||||
export interface IState {
|
export interface IState {
|
||||||
activeRef: Ref;
|
activeRef: Ref;
|
||||||
refs: Ref[];
|
refs: Ref[];
|
||||||
|
@ -202,8 +199,6 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEn
|
||||||
</RovingTabIndexContext.Provider>;
|
</RovingTabIndexContext.Provider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type FocusHandler = () => void;
|
|
||||||
|
|
||||||
// Hook to register a roving tab index
|
// Hook to register a roving tab index
|
||||||
// inputRef parameter specifies the ref to use
|
// inputRef parameter specifies the ref to use
|
||||||
// onFocus should be called when the index gained focus in any manner
|
// 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];
|
return [onFocus, isActive, ref];
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IRovingTabIndexWrapperProps {
|
// re-export the semantic helper components for simplicity
|
||||||
inputRef?: Ref;
|
export {RovingTabIndexWrapper} from "./roving/RovingTabIndexWrapper";
|
||||||
children(renderProps: {
|
export {RovingAccessibleButton} from "./roving/RovingAccessibleButton";
|
||||||
onFocus: FocusHandler;
|
export {RovingAccessibleTooltipButton} from "./roving/RovingAccessibleTooltipButton";
|
||||||
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} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
32
src/accessibility/roving/RovingAccessibleButton.tsx
Normal file
32
src/accessibility/roving/RovingAccessibleButton.tsx
Normal 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} />;
|
||||||
|
};
|
||||||
|
|
32
src/accessibility/roving/RovingAccessibleTooltipButton.tsx
Normal file
32
src/accessibility/roving/RovingAccessibleTooltipButton.tsx
Normal 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} />;
|
||||||
|
};
|
||||||
|
|
36
src/accessibility/roving/RovingTabIndexWrapper.tsx
Normal file
36
src/accessibility/roving/RovingTabIndexWrapper.tsx
Normal 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});
|
||||||
|
};
|
21
src/accessibility/roving/types.ts
Normal file
21
src/accessibility/roving/types.ts
Normal 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;
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import AccessibleButton from "./AccessibleButton";
|
import AccessibleButton from "./AccessibleButton";
|
||||||
import {IProps} from "./AccessibleButton";
|
import {IProps} from "./AccessibleButton";
|
||||||
|
@ -52,15 +52,11 @@ export default class AccessibleTooltipButton extends React.PureComponent<IToolti
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {title, children, ...props} = this.props;
|
const {title, children, tooltipClassName, ...props} = this.props;
|
||||||
const tooltipClassName = classnames(
|
|
||||||
"mx_AccessibleTooltipButton_tooltip",
|
|
||||||
this.props.tooltipClassName,
|
|
||||||
);
|
|
||||||
|
|
||||||
const tip = this.state.hover ? <Tooltip
|
const tip = this.state.hover ? <Tooltip
|
||||||
className="mx_AccessibleTooltipButton_container"
|
className="mx_AccessibleTooltipButton_container"
|
||||||
tooltipClassName={tooltipClassName}
|
tooltipClassName={classNames("mx_AccessibleTooltipButton_tooltip", tooltipClassName)}
|
||||||
label={title}
|
label={title}
|
||||||
/> : <div />;
|
/> : <div />;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -25,7 +25,8 @@ import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||||
import { CSSTransition } from "react-transition-group";
|
import { CSSTransition } from "react-transition-group";
|
||||||
import RoomListStore from "../../../stores/room-list/RoomListStore2";
|
import RoomListStore from "../../../stores/room-list/RoomListStore2";
|
||||||
import { DefaultTagID } from "../../../stores/room-list/models";
|
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
|
// 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 roomTags = RoomListStore.instance.getTagsForRoom(r);
|
||||||
const roomTag = roomTags.includes(DefaultTagID.DM) ? DefaultTagID.DM : roomTags[0];
|
const roomTag = roomTags.includes(DefaultTagID.DM) ? DefaultTagID.DM : roomTags[0];
|
||||||
return (
|
return (
|
||||||
<AccessibleTooltipButton
|
<RovingAccessibleTooltipButton
|
||||||
className="mx_RoomBreadcrumbs2_crumb"
|
className="mx_RoomBreadcrumbs2_crumb"
|
||||||
key={r.roomId}
|
key={r.roomId}
|
||||||
onClick={() => this.viewRoom(r, i)}
|
onClick={() => this.viewRoom(r, i)}
|
||||||
|
@ -101,7 +102,7 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
|
||||||
displayBadge={true}
|
displayBadge={true}
|
||||||
forceCount={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}
|
appear={true} in={this.state.doAnimation} timeout={640}
|
||||||
classNames='mx_RoomBreadcrumbs2'
|
classNames='mx_RoomBreadcrumbs2'
|
||||||
>
|
>
|
||||||
<div className='mx_RoomBreadcrumbs2'>
|
<Toolbar className='mx_RoomBreadcrumbs2'>
|
||||||
{tiles.slice(this.state.skipFirst ? 1 : 0)}
|
{tiles.slice(this.state.skipFirst ? 1 : 0)}
|
||||||
</div>
|
</Toolbar>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue