diff --git a/.eslintrc.js b/.eslintrc.js index 2090ef2220..37379f1304 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -43,7 +43,6 @@ module.exports = { // There are too many a11y violations to fix at once // Turn violated rules off until they are fixed "jsx-a11y/alt-text": "off", - "jsx-a11y/anchor-is-valid": "off", "jsx-a11y/aria-activedescendant-has-tabindex": "off", "jsx-a11y/click-events-have-key-events": "off", "jsx-a11y/iframe-has-title": "off", diff --git a/res/css/_common.scss b/res/css/_common.scss index c72e4362ce..a5f6c65b15 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -423,9 +423,9 @@ legend { * We should go through and have one consistent set of styles for buttons throughout the app. * For now, I am duplicating the selectors here for mx_Dialog and mx_DialogButtons. */ -.mx_Dialog button:not(.mx_Dialog_nonDialogButton), +.mx_Dialog button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton), .mx_Dialog input[type="submit"], -.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton), +.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton), .mx_Dialog_buttons input[type="submit"] { @mixin mx_DialogButton; margin-left: 0px; @@ -440,20 +440,20 @@ legend { font-family: inherit; } -.mx_Dialog button:not(.mx_Dialog_nonDialogButton):last-child { +.mx_Dialog button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):last-child { margin-right: 0px; } -.mx_Dialog button:not(.mx_Dialog_nonDialogButton):hover, +.mx_Dialog button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):hover, .mx_Dialog input[type="submit"]:hover, -.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):hover, +.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):hover, .mx_Dialog_buttons input[type="submit"]:hover { @mixin mx_DialogButton_hover; } -.mx_Dialog button:not(.mx_Dialog_nonDialogButton):focus, +.mx_Dialog button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):focus, .mx_Dialog input[type="submit"]:focus, -.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):focus, +.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):focus, .mx_Dialog_buttons input[type="submit"]:focus { filter: brightness($focus-brightness); } @@ -482,9 +482,9 @@ legend { color: $alert; } -.mx_Dialog button:not(.mx_Dialog_nonDialogButton):disabled, +.mx_Dialog button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):disabled, .mx_Dialog input[type="submit"]:disabled, -.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):disabled, +.mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):disabled, .mx_Dialog_buttons input[type="submit"]:disabled { background-color: $light-fg-color; border: solid 1px $light-fg-color; @@ -655,3 +655,15 @@ legend { outline-style: auto; } } + +@define-mixin ButtonResetDefault { + appearance: none; + background: none; + border: none; + padding: 0; + margin: 0; + font-size: inherit; + font-family: inherit; + line-height: inherit; + cursor: pointer; +} diff --git a/res/css/structures/auth/_SetupEncryptionBody.scss b/res/css/structures/auth/_SetupEncryptionBody.scss index f40e31280b..1999fb581d 100644 --- a/res/css/structures/auth/_SetupEncryptionBody.scss +++ b/res/css/structures/auth/_SetupEncryptionBody.scss @@ -17,8 +17,9 @@ limitations under the License. .mx_SetupEncryptionBody_reset { color: $light-fg-color; margin-top: $font-14px; - - a.mx_SetupEncryptionBody_reset_link:is(:link, :hover, :visited) { - color: $alert; - } +} + +.mx_SetupEncryptionBody_reset_link { + @mixin ButtonResetDefault; + color: $alert; } diff --git a/res/css/views/dialogs/_DevtoolsDialog.scss b/res/css/views/dialogs/_DevtoolsDialog.scss index 738cc9ea22..d92cc7858b 100644 --- a/res/css/views/dialogs/_DevtoolsDialog.scss +++ b/res/css/views/dialogs/_DevtoolsDialog.scss @@ -257,3 +257,10 @@ limitations under the License. margin-bottom: 8px; } } + +.mx_DevTools_SettingsExplorer_setting { + // override default link button color + // as it is the same as the background highlight + // used on focus + color: $links !important; +} diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index 8559c89e41..bf5440d682 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -107,6 +107,16 @@ limitations under the License. opacity: 0.4; } +.mx_AccessibleButton_kind_link_inline { + color: $accent; + font-size: inherit; + padding: 0 2px; +} + +.mx_AccessibleButton_kind_link_inline.mx_AccessibleButton_disabled { + opacity: 0.4; +} + .mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_link_sm { padding: 5px 12px; color: $accent; diff --git a/res/css/views/elements/_ReplyChain.scss b/res/css/views/elements/_ReplyChain.scss index b43ff084df..cb3c078771 100644 --- a/res/css/views/elements/_ReplyChain.scss +++ b/res/css/views/elements/_ReplyChain.scss @@ -24,7 +24,12 @@ limitations under the License. border-radius: 2px; .mx_ReplyChain_show { - cursor: pointer; + @mixin ButtonResetDefault; + color: inherit; + + &:hover { + color: $links; + } } &.mx_ReplyChain_color1 { diff --git a/res/css/views/messages/_ReactionsRow.scss b/res/css/views/messages/_ReactionsRow.scss index 1b0b847932..d4695888df 100644 --- a/res/css/views/messages/_ReactionsRow.scss +++ b/res/css/views/messages/_ReactionsRow.scss @@ -57,15 +57,13 @@ limitations under the License. } .mx_ReactionsRow_showAll { + @mixin ButtonResetDefault; text-decoration: none; font-size: $font-12px; line-height: $font-20px; margin-left: 4px; vertical-align: middle; - - &:link, &:visited { - color: $tertiary-content; - } + color: $tertiary-content; &:hover { color: $primary-content; diff --git a/res/css/views/messages/_ViewSourceEvent.scss b/res/css/views/messages/_ViewSourceEvent.scss index f076c77473..5e288eb19a 100644 --- a/res/css/views/messages/_ViewSourceEvent.scss +++ b/res/css/views/messages/_ViewSourceEvent.scss @@ -18,6 +18,7 @@ limitations under the License. display: flex; opacity: 0.6; font-size: $font-12px; + width: 100%; pre, code { flex: 1; @@ -29,11 +30,15 @@ limitations under the License. } .mx_ViewSourceEvent_toggle { + visibility: hidden; + // override styles from AccessibleButton + border-radius: 0; + padding: 0; + // icon mask-repeat: no-repeat; mask-position: 0 center; mask-size: auto 12px; width: 12px; - visibility: hidden; background-color: $accent; mask-image: url("$(res)/img/element-icons/maximise-expand.svg"); } diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index a1ad892bee..95ad106684 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -607,7 +607,8 @@ $left-gutter: 64px; opacity: 0.5; } -.mx_EventTile_keyRequestInfo_text a { +.mx_EventTile_keyRequestInfo_text .mx_AccessibleButton { + @mixin ButtonResetDefault; color: $primary-content; text-decoration: underline; cursor: pointer; diff --git a/src/TextForEvent.tsx b/src/TextForEvent.tsx index 14d4768e83..53fa8e3e56 100644 --- a/src/TextForEvent.tsx +++ b/src/TextForEvent.tsx @@ -32,6 +32,7 @@ import { Action } from './dispatcher/actions'; import defaultDispatcher from './dispatcher/dispatcher'; import { MatrixClientPeg } from "./MatrixClientPeg"; import { ROOM_SECURITY_TAB } from "./components/views/dialogs/RoomSettingsDialog"; +import AccessibleButton from './components/views/elements/AccessibleButton'; import RightPanelStore from './stores/right-panel/RightPanelStore'; // These functions are frequently used just to check whether an event has @@ -229,9 +230,9 @@ function textForJoinRulesEvent(ev: MatrixEvent, allowJSX: boolean): () => string { _t('%(senderDisplayName)s changed who can join this room. View settings.', { senderDisplayName, }, { - "a": (sub) => + "a": (sub) => { sub } - , + , }) } ; } @@ -528,13 +529,13 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string { senderName }, { "a": (sub) => - onPinnedOrUnpinnedMessageClick(messageId, roomId)}> + onPinnedOrUnpinnedMessageClick(messageId, roomId)}> { sub } - , + , "b": (sub) => - + { sub } - , + , }, ) } @@ -556,13 +557,13 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string { senderName }, { "a": (sub) => - onPinnedOrUnpinnedMessageClick(messageId, roomId)}> + onPinnedOrUnpinnedMessageClick(messageId, roomId)}> { sub } - , + , "b": (sub) => - + { sub } - , + , }, ) } @@ -578,7 +579,12 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string { _t( "%(senderName)s changed the pinned messages for the room.", { senderName }, - { "a": (sub) => { sub } }, + { + "a": (sub) => + + { sub } + , + }, ) } ); diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f37fbdc443..0f20e940bd 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -2162,9 +2162,9 @@ export default class MatrixChat extends React.PureComponent {
{ errorBox } - + { _t('Logout') } - +
); } diff --git a/src/components/structures/auth/ForgotPassword.tsx b/src/components/structures/auth/ForgotPassword.tsx index dee6f2dec6..79ae5e201f 100644 --- a/src/components/structures/auth/ForgotPassword.tsx +++ b/src/components/structures/auth/ForgotPassword.tsx @@ -38,6 +38,7 @@ import ErrorDialog from "../../views/dialogs/ErrorDialog"; import AuthHeader from "../../views/auth/AuthHeader"; import AuthBody from "../../views/auth/AuthBody"; import PassphraseConfirmField from "../../views/auth/PassphraseConfirmField"; +import AccessibleButton from '../../views/elements/AccessibleButton'; enum Phase { // Show the forgot password inputs @@ -338,9 +339,9 @@ export default class ForgotPassword extends React.Component { value={_t('Send Reset Email')} /> - + { _t('Sign in instead') } - + ; } diff --git a/src/components/structures/auth/Login.tsx b/src/components/structures/auth/Login.tsx index 1d963a2229..3fc47d6693 100644 --- a/src/components/structures/auth/Login.tsx +++ b/src/components/structures/auth/Login.tsx @@ -38,6 +38,7 @@ import ServerPicker from "../../views/elements/ServerPicker"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import AuthBody from "../../views/auth/AuthBody"; import AuthHeader from "../../views/auth/AuthHeader"; +import AccessibleButton from '../../views/elements/AccessibleButton'; // These are used in several places, and come from the js-sdk's autodiscovery // stuff. We define them here so that they'll be picked up by i18n. @@ -588,7 +589,10 @@ export default class LoginComponent extends React.PureComponent footer = ( { _t("New? Create account", {}, { - a: sub => { sub }, + a: sub => + + { sub } + , }) } ); diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index 44f40a37d9..298df8b13b 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -538,16 +538,20 @@ export default class Registration extends React.Component { const signIn = { _t("Already have an account? Sign in here", {}, { - a: sub => { sub }, + a: sub => { sub }, }) } ; // Only show the 'go back' button if you're not looking at the form let goBack; if (this.state.doingUIAuth) { - goBack = + goBack = { _t('Go back') } - ; + ; } let body; diff --git a/src/components/structures/auth/SetupEncryptionBody.tsx b/src/components/structures/auth/SetupEncryptionBody.tsx index 6492220680..b18c56768c 100644 --- a/src/components/structures/auth/SetupEncryptionBody.tsx +++ b/src/components/structures/auth/SetupEncryptionBody.tsx @@ -121,7 +121,7 @@ export default class SetupEncryptionBody extends React.Component store.returnAfterSkip(); }; - private onResetClick = (ev: React.MouseEvent) => { + private onResetClick = (ev: React.MouseEvent) => { ev.preventDefault(); const store = SetupEncryptionStore.sharedInstance(); store.reset(); @@ -214,10 +214,9 @@ export default class SetupEncryptionBody extends React.Component
{ _t("Forgotten or lost all recovery methods? Reset all", null, { - a: (sub) => , }) }
diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.tsx b/src/components/views/auth/InteractiveAuthEntryComponents.tsx index c0396ff186..63845f0e97 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.tsx +++ b/src/components/views/auth/InteractiveAuthEntryComponents.tsx @@ -755,7 +755,7 @@ export class SSOAuthEntry extends React.Component { private popupWindow: Window; - private fallbackButton = createRef(); + private fallbackButton = createRef(); constructor(props) { super(props); @@ -814,9 +814,9 @@ export class FallbackAuthEntry extends React.Component { } return (
- { + { _t("Start authentication") - } + } { errorSection }
); diff --git a/src/components/views/dialogs/AddressPickerDialog.tsx b/src/components/views/dialogs/AddressPickerDialog.tsx index e5bb579d65..52db395a00 100644 --- a/src/components/views/dialogs/AddressPickerDialog.tsx +++ b/src/components/views/dialogs/AddressPickerDialog.tsx @@ -37,6 +37,7 @@ import AddressSelector from '../elements/AddressSelector'; import AddressTile from '../elements/AddressTile'; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; +import AccessibleButton from '../elements/AccessibleButton'; const TRUNCATE_QUERY_LIST = 40; const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200; @@ -712,8 +713,14 @@ export default class AddressPickerDialog extends React.Component defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl), }, { - default: sub => { sub }, - settings: sub => { sub }, + default: sub => ( + + { sub } + + ), + settings: sub => + { sub } + , }, ) }; } else { @@ -721,7 +728,9 @@ export default class AddressPickerDialog extends React.Component "Use an identity server to invite by email. " + "Manage in Settings.", {}, { - settings: sub => { sub }, + settings: sub => + { sub } + , }, ) }; } diff --git a/src/components/views/dialogs/DevtoolsDialog.tsx b/src/components/views/dialogs/DevtoolsDialog.tsx index 120b9c7ea5..8b698acd7c 100644 --- a/src/components/views/dialogs/DevtoolsDialog.tsx +++ b/src/components/views/dialogs/DevtoolsDialog.tsx @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useState, useEffect, ChangeEvent, MouseEvent } from 'react'; +import React, { useState, useEffect, ChangeEvent } from 'react'; import { PHASE_UNSENT, PHASE_REQUESTED, @@ -44,6 +44,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent"; import { SettingLevel } from '../../../settings/SettingLevel'; import BaseDialog from "./BaseDialog"; import TruncatedList from "../elements/TruncatedList"; +import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton'; interface IGenericEditorProps { onBack: () => void; @@ -965,12 +966,12 @@ class SettingsExplorer extends React.PureComponent { + private onViewClick = (ev: ButtonEvent, settingId: string) => { ev.preventDefault(); this.setState({ viewSetting: settingId }); }; - private onEditClick = (ev: MouseEvent, settingId: string) => { + private onEditClick = (ev: ButtonEvent, settingId: string) => { ev.preventDefault(); this.setState({ editSetting: settingId, @@ -1078,16 +1079,16 @@ class SettingsExplorer extends React.PureComponent ( - this.onViewClick(e, i)}> + this.onViewClick(e, i)}> { i } - - + this.onEditClick(e, i)} className='mx_DevTools_SettingsExplorer_edit' > ✏ - + { this.renderSettingValue(SettingsStore.getValue(i)) } diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 13e48db702..f352393c5b 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1255,8 +1255,14 @@ export default class InviteDialog extends React.PureComponent { sub }, - settings: sub => { sub }, + default: sub => + + { sub } + , + settings: sub => + + { sub } + , }, ) } ); @@ -1266,7 +1272,10 @@ export default class InviteDialog extends React.PureComponentSettings.", {}, { - settings: sub => { sub }, + settings: sub => + + { sub } + , }, ) } ); diff --git a/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx b/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx index 35d27afe25..0da8fb97d3 100644 --- a/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx +++ b/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx @@ -29,6 +29,7 @@ import BugReportDialog from './BugReportDialog'; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; import ProgressBar from "../elements/ProgressBar"; +import AccessibleButton from '../elements/AccessibleButton'; export interface IFinishedOpts { continue: boolean; @@ -135,7 +136,9 @@ export default class RoomUpgradeWarningDialog extends React.Component { - return { sub }; + return + { sub } + ; }, }, ) } diff --git a/src/components/views/dialogs/StorageEvictedDialog.tsx b/src/components/views/dialogs/StorageEvictedDialog.tsx index 8a41224d57..acbb62b43d 100644 --- a/src/components/views/dialogs/StorageEvictedDialog.tsx +++ b/src/components/views/dialogs/StorageEvictedDialog.tsx @@ -24,6 +24,7 @@ import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; import BugReportDialog from "./BugReportDialog"; import { IDialogProps } from "./IDialogProps"; +import AccessibleButton from '../elements/AccessibleButton'; interface IProps extends IDialogProps { } @@ -45,7 +46,9 @@ export default class StorageEvictedDialog extends React.Component { "To help us prevent this in future, please send us logs.", {}, { - a: text => { text }, + a: text => + { text } + , }, ); } diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx index a9fea93fd0..cecd53ac0c 100644 --- a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx +++ b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx @@ -287,10 +287,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent { _t("Forgotten or lost all recovery methods? Reset all", null, { - a: (sub) => { sub }, + className="mx_AccessSecretStorageDialog_reset_link">{ sub }, }) } ); diff --git a/src/components/views/elements/AccessibleButton.tsx b/src/components/views/elements/AccessibleButton.tsx index ae11098e73..a93bb5ab83 100644 --- a/src/components/views/elements/AccessibleButton.tsx +++ b/src/components/views/elements/AccessibleButton.tsx @@ -21,6 +21,19 @@ import { Key } from '../../../Keyboard'; export type ButtonEvent = React.MouseEvent | React.KeyboardEvent | React.FormEvent; +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?: 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 diff --git a/src/components/views/elements/DesktopBuildsNotice.tsx b/src/components/views/elements/DesktopBuildsNotice.tsx index e06db603ec..8c600a152f 100644 --- a/src/components/views/elements/DesktopBuildsNotice.tsx +++ b/src/components/views/elements/DesktopBuildsNotice.tsx @@ -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 your settings for more information", {}, { - a: sub => ( { - evt.preventDefault(); - dis.dispatch({ - action: Action.ViewUserSettings, - initialTabId: UserTab.Security, - }); - }}> - { sub } - ), + a: sub => ( + { + evt.preventDefault(); + dis.dispatch({ + action: Action.ViewUserSettings, + initialTabId: UserTab.Security, + }); + }} + > + { sub } + ), }) } ; } diff --git a/src/components/views/elements/MemberEventListSummary.tsx b/src/components/views/elements/MemberEventListSummary.tsx index 53a604fe9d..9b364856c1 100644 --- a/src/components/views/elements/MemberEventListSummary.tsx +++ b/src/components/views/elements/MemberEventListSummary.tsx @@ -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 { res = (userCount > 1) ? _t("%(severalUsers)schanged the pinned messages for the room %(count)s times.", { severalUsers: "", count: repeats }, - { "a": (sub) => { sub } }) + { + "a": (sub) => + { sub } + , + }) : _t("%(oneUser)schanged the pinned messages for the room %(count)s times.", { oneUser: "", count: repeats }, - { "a": (sub) => { sub } }); + { + "a": (sub) => + { sub } + , + }); break; } diff --git a/src/components/views/elements/ReplyChain.tsx b/src/components/views/elements/ReplyChain.tsx index 657fc189c1..00221f4585 100644 --- a/src/components/views/elements/ReplyChain.tsx +++ b/src/components/views/elements/ReplyChain.tsx @@ -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 { this.initialize(); }; - private onQuoteClick = async (event: React.MouseEvent): Promise => { + private onQuoteClick = async (event: ButtonEvent): Promise => { const events = [this.state.loadedEv, ...this.state.events]; let loadedEv = null; @@ -380,7 +381,11 @@ export default class ReplyChain extends React.Component { header =
{ _t('In reply to ', {}, { - 'a': (sub) => { sub }, + 'a': (sub) => ( + + ), 'pill': ( { * We'll use it to learn how the download link * would have been styled if it was rendered inline. */ } - { /* eslint-disable-next-line jsx-a11y/anchor-has-content */ } + { /* this violates multiple eslint rules + so ignore it completely */ } + { /* eslint-disable-next-line */ } { /* diff --git a/src/components/views/messages/MjolnirBody.tsx b/src/components/views/messages/MjolnirBody.tsx index 00cbcf461f..14956554f1 100644 --- a/src/components/views/messages/MjolnirBody.tsx +++ b/src/components/views/messages/MjolnirBody.tsx @@ -19,6 +19,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { _t } from '../../../languageHandler'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import AccessibleButton from '../elements/AccessibleButton'; interface IProps { mxEvent: MatrixEvent; @@ -40,7 +41,11 @@ export default class MjolnirBody extends React.Component { return (
{ _t( "You have ignored this user, so their message is hidden. Show anyways.", - {}, { a: (sub) => { sub } }, + {}, { + a: (sub) => + { sub } + , + }, ) }
); } diff --git a/src/components/views/messages/ReactionsRow.tsx b/src/components/views/messages/ReactionsRow.tsx index d2718c20b1..3058770c47 100644 --- a/src/components/views/messages/ReactionsRow.tsx +++ b/src/components/views/messages/ReactionsRow.tsx @@ -27,6 +27,7 @@ import ContextMenu, { aboveLeftOf, useContextMenu } from "../../structures/Conte import ReactionPicker from "../emojipicker/ReactionPicker"; import ReactionsRowButton from "./ReactionsRowButton"; import RoomContext from "../../../contexts/RoomContext"; +import AccessibleButton from "../elements/AccessibleButton"; // The maximum number of reactions to initially show on a message. const MAX_ITEMS_WHEN_LIMITED = 8; @@ -201,13 +202,13 @@ export default class ReactionsRow extends React.PureComponent { let showAllButton; if ((items.length > MAX_ITEMS_WHEN_LIMITED + 1) && !showAll) { items = items.slice(0, MAX_ITEMS_WHEN_LIMITED); - showAllButton = { _t("Show all") } - ; + ; } let addReactionButton; diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index 66a5cc8b54..30b7edf7cf 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -45,6 +45,7 @@ import EditMessageComposer from '../rooms/EditMessageComposer'; import LinkPreviewGroup from '../rooms/LinkPreviewGroup'; import { IBodyProps } from "./IBodyProps"; import RoomContext from "../../../contexts/RoomContext"; +import AccessibleButton from '../elements/AccessibleButton'; const MAX_HIGHLIGHT_LENGTH = 4096; @@ -529,9 +530,9 @@ export default class TextualBody extends React.Component { if (this.props.highlightLink) { body = { body }; } else if (content.data && typeof content.data["org.matrix.neb.starter_link"] === "string") { - body = { body }; + >{ body }; } let widgets; diff --git a/src/components/views/messages/TileErrorBoundary.tsx b/src/components/views/messages/TileErrorBoundary.tsx index a15806ae0c..ac84562cf9 100644 --- a/src/components/views/messages/TileErrorBoundary.tsx +++ b/src/components/views/messages/TileErrorBoundary.tsx @@ -23,6 +23,7 @@ import Modal from '../../../Modal'; import SdkConfig from "../../../SdkConfig"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import BugReportDialog from '../dialogs/BugReportDialog'; +import AccessibleButton from '../elements/AccessibleButton'; interface IProps { mxEvent: MatrixEvent; @@ -67,9 +68,9 @@ export default class TileErrorBoundary extends React.Component { let submitLogsButton; if (SdkConfig.get().bug_report_endpoint_url) { - submitLogsButton = + submitLogsButton = { _t("Submit logs") } - ; + ; } return (
diff --git a/src/components/views/messages/ViewSourceEvent.tsx b/src/components/views/messages/ViewSourceEvent.tsx index 3e6d47dc13..6385f05e51 100644 --- a/src/components/views/messages/ViewSourceEvent.tsx +++ b/src/components/views/messages/ViewSourceEvent.tsx @@ -21,6 +21,7 @@ import classNames from 'classnames'; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { _t } from '../../../languageHandler'; +import AccessibleButton from '../elements/AccessibleButton'; interface IProps { mxEvent: MatrixEvent; @@ -76,7 +77,8 @@ export default class ViewSourceEvent extends React.PureComponent return { content } -
, @@ -163,11 +165,9 @@ export default class SecurityRoomSettingsTab extends React.ComponentLearn more about encryption.", {}, { - a: sub => { sub }, + >{ sub }, }, ), onFinished: (confirm) => { @@ -306,12 +306,12 @@ export default class SecurityRoomSettingsTab extends React.Component { dialog.close(); this.createNewRoom(true, false); - }}> { sub } , + }}> { sub } , }, ) }

, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 69051ff7c7..cb63540a4b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2476,6 +2476,7 @@ "Setting ID": "Setting ID", "Value": "Value", "Value in this room": "Value in this room", + "Edit setting": "Edit setting", "Setting:": "Setting:", "Caution:": "Caution:", "This UI does NOT check the types of the values. Use at your own risk.": "This UI does NOT check the types of the values. Use at your own risk.", diff --git a/test/components/views/settings/SettingsFieldset-test.tsx b/test/components/views/settings/SettingsFieldset-test.tsx index f056c02681..28a784f25b 100644 --- a/test/components/views/settings/SettingsFieldset-test.tsx +++ b/test/components/views/settings/SettingsFieldset-test.tsx @@ -40,7 +40,7 @@ describe('', () => { }); it('renders fieldset with react description', () => { - const description = <>

Test

a link; + const description = <>

Test

a link; expect(getComponent({ description })).toMatchSnapshot(); }); }); diff --git a/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap b/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap index d5924073e3..44cf7af89e 100644 --- a/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap +++ b/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap @@ -38,7 +38,7 @@ exports[` renders fieldset with react description 1`] = ` Test

a link