A11y - fix anchors-as-buttons (#7444)

* add link_inline accessiblebutton variant

* valid anchors in SecurityRoomSettingsTab

Signed-off-by: Kerry Archibald <kerrya@element.io>

* new room intro link button

Signed-off-by: Kerry Archibald <kerrya@element.io>

* replace anchor with button in rerequest encryption keys message

Signed-off-by: Kerry Archibald <kerrya@element.io>

* inline button in UrlPreviewSettings

Signed-off-by: Kerry Archibald <kerrya@element.io>

* ButtonResetDefault mixin

Signed-off-by: Kerry Archibald <kerrya@element.io>

* inline link buttons in TextForEvent

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix anchors in InviteDialog

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix anchors in DevToolsDialog

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix anchors in login/registration/reset pword flows

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix types after fixing anchors in devtools

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix anchors in MemberEventListSummary

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix anchors in ReactionsRow and RoomUpgrade

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix anchors in ReplyChain

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix more anchors

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix anchors in auth comps

* stylelint fixes

Signed-off-by: Kerry Archibald <kerrya@element.io>

* remove ignore of jsx-a11y rule that is not added yet

Signed-off-by: Kerry Archibald <kerrya@element.io>

* devtools style important explainer

Signed-off-by: Kerry Archibald <kerrya@element.io>

* translate button alt in devtools dialog

Signed-off-by: Kerry Archibald <kerrya@element.io>

* AccessibleButton is reactionsrow

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix viewsourcevent button placement, use AccessibleButton

Signed-off-by: Kerry Archibald <kerrya@element.io>

* use AccessibleButton in EventTile

Signed-off-by: Kerry Archibald <kerrya@element.io>

* unignore jsx-a11y/anchor-is-valid

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix lint issue in test jsx

Signed-off-by: Kerry Archibald <kerrya@element.io>

* update coment

Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
Kerry 2022-01-07 10:40:53 +01:00 committed by GitHub
parent 2b9eed5357
commit fed53a268b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 251 additions and 109 deletions

View file

@ -21,6 +21,19 @@ import { Key } from '../../../Keyboard';
export type ButtonEvent = React.MouseEvent<Element> | React.KeyboardEvent<Element> | React.FormEvent<Element>;
type AccessibleButtonKind = | 'primary'
| 'primary_outline'
| 'primary_sm'
| 'secondary'
| 'danger'
| 'danger_outline'
| 'danger_sm'
| 'link'
| 'link_inline'
| 'link_sm'
| 'confirm_sm'
| 'cancel_sm';
/**
* children: React's magic prop. Represents all children given to the element.
* element: (optional) The base element type. "div" by default.
@ -32,7 +45,7 @@ interface IProps extends React.InputHTMLAttributes<Element> {
element?: keyof ReactHTML;
// The kind of button, similar to how Bootstrap works.
// See available classes for AccessibleButton for options.
kind?: string;
kind?: AccessibleButtonKind | string;
// The ARIA role
role?: string;
// The tabIndex

View file

@ -23,6 +23,7 @@ import SdkConfig from "../../../SdkConfig";
import dis from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
import { UserTab } from "../dialogs/UserSettingsDialog";
import AccessibleButton from "./AccessibleButton";
export enum WarningKind {
Files,
@ -41,15 +42,19 @@ export default function DesktopBuildsNotice({ isRoomEncrypted, kind }: IProps) {
if (EventIndexPeg.error) {
return <>
{ _t("Message search initialisation failed, check <a>your settings</a> for more information", {}, {
a: sub => (<a onClick={(evt) => {
evt.preventDefault();
dis.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Security,
});
}}>
{ sub }
</a>),
a: sub => (
<AccessibleButton
kind="link_inline"
onClick={(evt) => {
evt.preventDefault();
dis.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Security,
});
}}
>
{ sub }
</AccessibleButton>),
}) }
</>;
}

View file

@ -30,6 +30,7 @@ import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePha
import { jsxJoin } from '../../../utils/ReactUtils';
import { Layout } from '../../../settings/enums/Layout';
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import AccessibleButton from './AccessibleButton';
const onPinnedMessagesClick = (): void => {
RightPanelStore.instance.setCard({ phase: RightPanelPhases.PinnedMessages }, false);
@ -322,10 +323,18 @@ export default class MemberEventListSummary extends React.Component<IProps> {
res = (userCount > 1)
? _t("%(severalUsers)schanged the <a>pinned messages</a> for the room %(count)s times.",
{ severalUsers: "", count: repeats },
{ "a": (sub) => <a onClick={onPinnedMessagesClick}> { sub } </a> })
{
"a": (sub) => <AccessibleButton kind='link_inline' onClick={onPinnedMessagesClick}>
{ sub }
</AccessibleButton>,
})
: _t("%(oneUser)schanged the <a>pinned messages</a> for the room %(count)s times.",
{ oneUser: "", count: repeats },
{ "a": (sub) => <a onClick={onPinnedMessagesClick}> { sub } </a> });
{
"a": (sub) => <AccessibleButton kind='link_inline' onClick={onPinnedMessagesClick}>
{ sub }
</AccessibleButton>,
});
break;
}

View file

@ -37,6 +37,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
import Spinner from './Spinner';
import ReplyTile from "../rooms/ReplyTile";
import Pill from './Pill';
import { ButtonEvent } from './AccessibleButton';
/**
* This number is based on the previous behavior - if we have message of height
@ -344,7 +345,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
this.initialize();
};
private onQuoteClick = async (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>): Promise<void> => {
private onQuoteClick = async (event: ButtonEvent): Promise<void> => {
const events = [this.state.loadedEv, ...this.state.events];
let loadedEv = null;
@ -380,7 +381,11 @@ export default class ReplyChain extends React.Component<IProps, IState> {
header = <blockquote className={`mx_ReplyChain ${this.getReplyChainColorClass(ev)}`}>
{
_t('<a>In reply to</a> <pill>', {}, {
'a': (sub) => <a onClick={this.onQuoteClick} className="mx_ReplyChain_show">{ sub }</a>,
'a': (sub) => (
<button onClick={this.onQuoteClick} className="mx_ReplyChain_show">
{ sub }
</button>
),
'pill': (
<Pill
type={Pill.TYPE_USER_MENTION}