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:
parent
2b9eed5357
commit
fed53a268b
39 changed files with 251 additions and 109 deletions
|
@ -43,7 +43,6 @@ module.exports = {
|
||||||
// There are too many a11y violations to fix at once
|
// There are too many a11y violations to fix at once
|
||||||
// Turn violated rules off until they are fixed
|
// Turn violated rules off until they are fixed
|
||||||
"jsx-a11y/alt-text": "off",
|
"jsx-a11y/alt-text": "off",
|
||||||
"jsx-a11y/anchor-is-valid": "off",
|
|
||||||
"jsx-a11y/aria-activedescendant-has-tabindex": "off",
|
"jsx-a11y/aria-activedescendant-has-tabindex": "off",
|
||||||
"jsx-a11y/click-events-have-key-events": "off",
|
"jsx-a11y/click-events-have-key-events": "off",
|
||||||
"jsx-a11y/iframe-has-title": "off",
|
"jsx-a11y/iframe-has-title": "off",
|
||||||
|
|
|
@ -423,9 +423,9 @@ legend {
|
||||||
* We should go through and have one consistent set of styles for buttons throughout the app.
|
* 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.
|
* 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 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"] {
|
.mx_Dialog_buttons input[type="submit"] {
|
||||||
@mixin mx_DialogButton;
|
@mixin mx_DialogButton;
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
|
@ -440,20 +440,20 @@ legend {
|
||||||
font-family: inherit;
|
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;
|
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 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 {
|
.mx_Dialog_buttons input[type="submit"]:hover {
|
||||||
@mixin mx_DialogButton_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 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 {
|
.mx_Dialog_buttons input[type="submit"]:focus {
|
||||||
filter: brightness($focus-brightness);
|
filter: brightness($focus-brightness);
|
||||||
}
|
}
|
||||||
|
@ -482,9 +482,9 @@ legend {
|
||||||
color: $alert;
|
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 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 {
|
.mx_Dialog_buttons input[type="submit"]:disabled {
|
||||||
background-color: $light-fg-color;
|
background-color: $light-fg-color;
|
||||||
border: solid 1px $light-fg-color;
|
border: solid 1px $light-fg-color;
|
||||||
|
@ -655,3 +655,15 @@ legend {
|
||||||
outline-style: auto;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,9 @@ limitations under the License.
|
||||||
.mx_SetupEncryptionBody_reset {
|
.mx_SetupEncryptionBody_reset {
|
||||||
color: $light-fg-color;
|
color: $light-fg-color;
|
||||||
margin-top: $font-14px;
|
margin-top: $font-14px;
|
||||||
|
}
|
||||||
a.mx_SetupEncryptionBody_reset_link:is(:link, :hover, :visited) {
|
|
||||||
color: $alert;
|
.mx_SetupEncryptionBody_reset_link {
|
||||||
}
|
@mixin ButtonResetDefault;
|
||||||
|
color: $alert;
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,3 +257,10 @@ limitations under the License.
|
||||||
margin-bottom: 8px;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -107,6 +107,16 @@ limitations under the License.
|
||||||
opacity: 0.4;
|
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 {
|
.mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_link_sm {
|
||||||
padding: 5px 12px;
|
padding: 5px 12px;
|
||||||
color: $accent;
|
color: $accent;
|
||||||
|
|
|
@ -24,7 +24,12 @@ limitations under the License.
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
||||||
.mx_ReplyChain_show {
|
.mx_ReplyChain_show {
|
||||||
cursor: pointer;
|
@mixin ButtonResetDefault;
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $links;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_ReplyChain_color1 {
|
&.mx_ReplyChain_color1 {
|
||||||
|
|
|
@ -57,15 +57,13 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ReactionsRow_showAll {
|
.mx_ReactionsRow_showAll {
|
||||||
|
@mixin ButtonResetDefault;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
line-height: $font-20px;
|
line-height: $font-20px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
color: $tertiary-content;
|
||||||
&:link, &:visited {
|
|
||||||
color: $tertiary-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $primary-content;
|
color: $primary-content;
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
display: flex;
|
display: flex;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
pre, code {
|
pre, code {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -29,11 +30,15 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ViewSourceEvent_toggle {
|
.mx_ViewSourceEvent_toggle {
|
||||||
|
visibility: hidden;
|
||||||
|
// override styles from AccessibleButton
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
// icon
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-position: 0 center;
|
mask-position: 0 center;
|
||||||
mask-size: auto 12px;
|
mask-size: auto 12px;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
visibility: hidden;
|
|
||||||
background-color: $accent;
|
background-color: $accent;
|
||||||
mask-image: url("$(res)/img/element-icons/maximise-expand.svg");
|
mask-image: url("$(res)/img/element-icons/maximise-expand.svg");
|
||||||
}
|
}
|
||||||
|
|
|
@ -607,7 +607,8 @@ $left-gutter: 64px;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_keyRequestInfo_text a {
|
.mx_EventTile_keyRequestInfo_text .mx_AccessibleButton {
|
||||||
|
@mixin ButtonResetDefault;
|
||||||
color: $primary-content;
|
color: $primary-content;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { Action } from './dispatcher/actions';
|
||||||
import defaultDispatcher from './dispatcher/dispatcher';
|
import defaultDispatcher from './dispatcher/dispatcher';
|
||||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||||
import { ROOM_SECURITY_TAB } from "./components/views/dialogs/RoomSettingsDialog";
|
import { ROOM_SECURITY_TAB } from "./components/views/dialogs/RoomSettingsDialog";
|
||||||
|
import AccessibleButton from './components/views/elements/AccessibleButton';
|
||||||
import RightPanelStore from './stores/right-panel/RightPanelStore';
|
import RightPanelStore from './stores/right-panel/RightPanelStore';
|
||||||
|
|
||||||
// These functions are frequently used just to check whether an event has
|
// 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. <a>View settings</a>.', {
|
{ _t('%(senderDisplayName)s changed who can join this room. <a>View settings</a>.', {
|
||||||
senderDisplayName,
|
senderDisplayName,
|
||||||
}, {
|
}, {
|
||||||
"a": (sub) => <a onClick={onViewJoinRuleSettingsClick}>
|
"a": (sub) => <AccessibleButton kind='link_inline' onClick={onViewJoinRuleSettingsClick}>
|
||||||
{ sub }
|
{ sub }
|
||||||
</a>,
|
</AccessibleButton>,
|
||||||
}) }
|
}) }
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
@ -528,13 +529,13 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string
|
||||||
{ senderName },
|
{ senderName },
|
||||||
{
|
{
|
||||||
"a": (sub) =>
|
"a": (sub) =>
|
||||||
<a onClick={(e) => onPinnedOrUnpinnedMessageClick(messageId, roomId)}>
|
<AccessibleButton kind='link_inline' onClick={(e) => onPinnedOrUnpinnedMessageClick(messageId, roomId)}>
|
||||||
{ sub }
|
{ sub }
|
||||||
</a>,
|
</AccessibleButton>,
|
||||||
"b": (sub) =>
|
"b": (sub) =>
|
||||||
<a onClick={onPinnedMessagesClick}>
|
<AccessibleButton kind='link_inline' onClick={onPinnedMessagesClick}>
|
||||||
{ sub }
|
{ sub }
|
||||||
</a>,
|
</AccessibleButton>,
|
||||||
},
|
},
|
||||||
) }
|
) }
|
||||||
</span>
|
</span>
|
||||||
|
@ -556,13 +557,13 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string
|
||||||
{ senderName },
|
{ senderName },
|
||||||
{
|
{
|
||||||
"a": (sub) =>
|
"a": (sub) =>
|
||||||
<a onClick={(e) => onPinnedOrUnpinnedMessageClick(messageId, roomId)}>
|
<AccessibleButton kind='link_inline' onClick={(e) => onPinnedOrUnpinnedMessageClick(messageId, roomId)}>
|
||||||
{ sub }
|
{ sub }
|
||||||
</a>,
|
</AccessibleButton>,
|
||||||
"b": (sub) =>
|
"b": (sub) =>
|
||||||
<a onClick={onPinnedMessagesClick}>
|
<AccessibleButton kind='link_inline' onClick={onPinnedMessagesClick}>
|
||||||
{ sub }
|
{ sub }
|
||||||
</a>,
|
</AccessibleButton>,
|
||||||
},
|
},
|
||||||
) }
|
) }
|
||||||
</span>
|
</span>
|
||||||
|
@ -578,7 +579,12 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string
|
||||||
{ _t(
|
{ _t(
|
||||||
"%(senderName)s changed the <a>pinned messages</a> for the room.",
|
"%(senderName)s changed the <a>pinned messages</a> for the room.",
|
||||||
{ senderName },
|
{ senderName },
|
||||||
{ "a": (sub) => <a onClick={onPinnedMessagesClick}> { sub } </a> },
|
{
|
||||||
|
"a": (sub) =>
|
||||||
|
<AccessibleButton kind='link_inline' onClick={onPinnedMessagesClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
|
},
|
||||||
) }
|
) }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2162,9 +2162,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
<div className="mx_MatrixChat_splash">
|
<div className="mx_MatrixChat_splash">
|
||||||
{ errorBox }
|
{ errorBox }
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<a href="#" className="mx_MatrixChat_splashButtons" onClick={this.onLogoutClick}>
|
<AccessibleButton kind='link_inline' className="mx_MatrixChat_splashButtons" onClick={this.onLogoutClick}>
|
||||||
{ _t('Logout') }
|
{ _t('Logout') }
|
||||||
</a>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import ErrorDialog from "../../views/dialogs/ErrorDialog";
|
||||||
import AuthHeader from "../../views/auth/AuthHeader";
|
import AuthHeader from "../../views/auth/AuthHeader";
|
||||||
import AuthBody from "../../views/auth/AuthBody";
|
import AuthBody from "../../views/auth/AuthBody";
|
||||||
import PassphraseConfirmField from "../../views/auth/PassphraseConfirmField";
|
import PassphraseConfirmField from "../../views/auth/PassphraseConfirmField";
|
||||||
|
import AccessibleButton from '../../views/elements/AccessibleButton';
|
||||||
|
|
||||||
enum Phase {
|
enum Phase {
|
||||||
// Show the forgot password inputs
|
// Show the forgot password inputs
|
||||||
|
@ -338,9 +339,9 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
|
||||||
value={_t('Send Reset Email')}
|
value={_t('Send Reset Email')}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
<a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
|
<AccessibleButton kind='link_inline' className="mx_AuthBody_changeFlow" onClick={this.onLoginClick}>
|
||||||
{ _t('Sign in instead') }
|
{ _t('Sign in instead') }
|
||||||
</a>
|
</AccessibleButton>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import ServerPicker from "../../views/elements/ServerPicker";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import AuthBody from "../../views/auth/AuthBody";
|
import AuthBody from "../../views/auth/AuthBody";
|
||||||
import AuthHeader from "../../views/auth/AuthHeader";
|
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
|
// 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.
|
// 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<IProps, IState>
|
||||||
footer = (
|
footer = (
|
||||||
<span className="mx_AuthBody_changeFlow">
|
<span className="mx_AuthBody_changeFlow">
|
||||||
{ _t("New? <a>Create account</a>", {}, {
|
{ _t("New? <a>Create account</a>", {}, {
|
||||||
a: sub => <a onClick={this.onTryRegisterClick} href="#">{ sub }</a>,
|
a: sub =>
|
||||||
|
<AccessibleButton kind='link_inline' onClick={this.onTryRegisterClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
}) }
|
}) }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -538,16 +538,20 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
const signIn = <span className="mx_AuthBody_changeFlow">
|
const signIn = <span className="mx_AuthBody_changeFlow">
|
||||||
{ _t("Already have an account? <a>Sign in here</a>", {}, {
|
{ _t("Already have an account? <a>Sign in here</a>", {}, {
|
||||||
a: sub => <a onClick={this.onLoginClick} href="#">{ sub }</a>,
|
a: sub => <AccessibleButton kind='link_inline' onClick={this.onLoginClick}>{ sub }</AccessibleButton>,
|
||||||
}) }
|
}) }
|
||||||
</span>;
|
</span>;
|
||||||
|
|
||||||
// Only show the 'go back' button if you're not looking at the form
|
// Only show the 'go back' button if you're not looking at the form
|
||||||
let goBack;
|
let goBack;
|
||||||
if (this.state.doingUIAuth) {
|
if (this.state.doingUIAuth) {
|
||||||
goBack = <a className="mx_AuthBody_changeFlow" onClick={this.onGoToFormClicked} href="#">
|
goBack = <AccessibleButton
|
||||||
|
kind='link_inline'
|
||||||
|
className="mx_AuthBody_changeFlow"
|
||||||
|
onClick={this.onGoToFormClicked}
|
||||||
|
>
|
||||||
{ _t('Go back') }
|
{ _t('Go back') }
|
||||||
</a>;
|
</AccessibleButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let body;
|
let body;
|
||||||
|
|
|
@ -121,7 +121,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
|
||||||
store.returnAfterSkip();
|
store.returnAfterSkip();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onResetClick = (ev: React.MouseEvent<HTMLAnchorElement>) => {
|
private onResetClick = (ev: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
store.reset();
|
store.reset();
|
||||||
|
@ -214,10 +214,9 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_SetupEncryptionBody_reset">
|
<div className="mx_SetupEncryptionBody_reset">
|
||||||
{ _t("Forgotten or lost all recovery methods? <a>Reset all</a>", null, {
|
{ _t("Forgotten or lost all recovery methods? <a>Reset all</a>", null, {
|
||||||
a: (sub) => <a
|
a: (sub) => <button
|
||||||
href=""
|
|
||||||
onClick={this.onResetClick}
|
onClick={this.onResetClick}
|
||||||
className="mx_SetupEncryptionBody_reset_link">{ sub }</a>,
|
className="mx_SetupEncryptionBody_reset_link">{ sub }</button>,
|
||||||
}) }
|
}) }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -755,7 +755,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
|
||||||
@replaceableComponent("views.auth.FallbackAuthEntry")
|
@replaceableComponent("views.auth.FallbackAuthEntry")
|
||||||
export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
|
export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
|
||||||
private popupWindow: Window;
|
private popupWindow: Window;
|
||||||
private fallbackButton = createRef<HTMLAnchorElement>();
|
private fallbackButton = createRef<HTMLButtonElement>();
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -814,9 +814,9 @@ export class FallbackAuthEntry extends React.Component<IAuthEntryProps> {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<a href="" ref={this.fallbackButton} onClick={this.onShowFallbackClick}>{
|
<AccessibleButton kind='link_inline' inputRef={this.fallbackButton} onClick={this.onShowFallbackClick}>{
|
||||||
_t("Start authentication")
|
_t("Start authentication")
|
||||||
}</a>
|
}</AccessibleButton>
|
||||||
{ errorSection }
|
{ errorSection }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -37,6 +37,7 @@ import AddressSelector from '../elements/AddressSelector';
|
||||||
import AddressTile from '../elements/AddressTile';
|
import AddressTile from '../elements/AddressTile';
|
||||||
import BaseDialog from "./BaseDialog";
|
import BaseDialog from "./BaseDialog";
|
||||||
import DialogButtons from "../elements/DialogButtons";
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
const TRUNCATE_QUERY_LIST = 40;
|
const TRUNCATE_QUERY_LIST = 40;
|
||||||
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
|
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
|
||||||
|
@ -712,8 +713,14 @@ export default class AddressPickerDialog extends React.Component<IProps, IState>
|
||||||
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
|
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: sub => <a href="#" onClick={this.onUseDefaultIdentityServerClick}>{ sub }</a>,
|
default: sub => (
|
||||||
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{ sub }</a>,
|
<AccessibleButton kind="link_inline" onClick={this.onUseDefaultIdentityServerClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>
|
||||||
|
),
|
||||||
|
settings: sub => <AccessibleButton kind="link_inline" onClick={this.onManageSettingsClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
},
|
},
|
||||||
) }</div>;
|
) }</div>;
|
||||||
} else {
|
} else {
|
||||||
|
@ -721,7 +728,9 @@ export default class AddressPickerDialog extends React.Component<IProps, IState>
|
||||||
"Use an identity server to invite by email. " +
|
"Use an identity server to invite by email. " +
|
||||||
"Manage in <settings>Settings</settings>.",
|
"Manage in <settings>Settings</settings>.",
|
||||||
{}, {
|
{}, {
|
||||||
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{ sub }</a>,
|
settings: sub => <AccessibleButton kind="link_inline" onClick={this.onManageSettingsClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
},
|
},
|
||||||
) }</div>;
|
) }</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect, ChangeEvent, MouseEvent } from 'react';
|
import React, { useState, useEffect, ChangeEvent } from 'react';
|
||||||
import {
|
import {
|
||||||
PHASE_UNSENT,
|
PHASE_UNSENT,
|
||||||
PHASE_REQUESTED,
|
PHASE_REQUESTED,
|
||||||
|
@ -44,6 +44,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { SettingLevel } from '../../../settings/SettingLevel';
|
import { SettingLevel } from '../../../settings/SettingLevel';
|
||||||
import BaseDialog from "./BaseDialog";
|
import BaseDialog from "./BaseDialog";
|
||||||
import TruncatedList from "../elements/TruncatedList";
|
import TruncatedList from "../elements/TruncatedList";
|
||||||
|
import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
|
||||||
|
|
||||||
interface IGenericEditorProps {
|
interface IGenericEditorProps {
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
|
@ -965,12 +966,12 @@ class SettingsExplorer extends React.PureComponent<IExplorerProps, ISettingsExpl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onViewClick = (ev: MouseEvent, settingId: string) => {
|
private onViewClick = (ev: ButtonEvent, settingId: string) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.setState({ viewSetting: settingId });
|
this.setState({ viewSetting: settingId });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onEditClick = (ev: MouseEvent, settingId: string) => {
|
private onEditClick = (ev: ButtonEvent, settingId: string) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.setState({
|
this.setState({
|
||||||
editSetting: settingId,
|
editSetting: settingId,
|
||||||
|
@ -1078,16 +1079,16 @@ class SettingsExplorer extends React.PureComponent<IExplorerProps, ISettingsExpl
|
||||||
{ allSettings.map(i => (
|
{ allSettings.map(i => (
|
||||||
<tr key={i}>
|
<tr key={i}>
|
||||||
<td>
|
<td>
|
||||||
<a href="" onClick={(e) => this.onViewClick(e, i)}>
|
<AccessibleButton kind='link_inline' className='mx_DevTools_SettingsExplorer_setting' onClick={(e) => this.onViewClick(e, i)}>
|
||||||
<code>{ i }</code>
|
<code>{ i }</code>
|
||||||
</a>
|
</AccessibleButton>
|
||||||
<a
|
<AccessibleButton
|
||||||
href=""
|
alt={_t('Edit setting')}
|
||||||
onClick={(e) => this.onEditClick(e, i)}
|
onClick={(e) => this.onEditClick(e, i)}
|
||||||
className='mx_DevTools_SettingsExplorer_edit'
|
className='mx_DevTools_SettingsExplorer_edit'
|
||||||
>
|
>
|
||||||
✏
|
✏
|
||||||
</a>
|
</AccessibleButton>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code>{ this.renderSettingValue(SettingsStore.getValue(i)) }</code>
|
<code>{ this.renderSettingValue(SettingsStore.getValue(i)) }</code>
|
||||||
|
|
|
@ -1255,8 +1255,14 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
|
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: sub => <a href="#" onClick={this.onUseDefaultIdentityServerClick}>{ sub }</a>,
|
default: sub =>
|
||||||
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{ sub }</a>,
|
<AccessibleButton kind='link_inline' onClick={this.onUseDefaultIdentityServerClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
|
settings: sub =>
|
||||||
|
<AccessibleButton kind='link_inline' onClick={this.onManageSettingsClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
},
|
},
|
||||||
) }</div>
|
) }</div>
|
||||||
);
|
);
|
||||||
|
@ -1266,7 +1272,10 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
"Use an identity server to invite by email. " +
|
"Use an identity server to invite by email. " +
|
||||||
"Manage in <settings>Settings</settings>.",
|
"Manage in <settings>Settings</settings>.",
|
||||||
{}, {
|
{}, {
|
||||||
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{ sub }</a>,
|
settings: sub =>
|
||||||
|
<AccessibleButton kind='link_inline' onClick={this.onManageSettingsClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
},
|
},
|
||||||
) }</div>
|
) }</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -29,6 +29,7 @@ import BugReportDialog from './BugReportDialog';
|
||||||
import BaseDialog from "./BaseDialog";
|
import BaseDialog from "./BaseDialog";
|
||||||
import DialogButtons from "../elements/DialogButtons";
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
import ProgressBar from "../elements/ProgressBar";
|
import ProgressBar from "../elements/ProgressBar";
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
export interface IFinishedOpts {
|
export interface IFinishedOpts {
|
||||||
continue: boolean;
|
continue: boolean;
|
||||||
|
@ -135,7 +136,9 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"a": (sub) => {
|
"a": (sub) => {
|
||||||
return <a href='#' onClick={this.openBugReportDialog}>{ sub }</a>;
|
return <AccessibleButton kind='link_inline' onClick={this.openBugReportDialog}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
) }
|
) }
|
||||||
|
|
|
@ -24,6 +24,7 @@ import BaseDialog from "./BaseDialog";
|
||||||
import DialogButtons from "../elements/DialogButtons";
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
import BugReportDialog from "./BugReportDialog";
|
import BugReportDialog from "./BugReportDialog";
|
||||||
import { IDialogProps } from "./IDialogProps";
|
import { IDialogProps } from "./IDialogProps";
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
interface IProps extends IDialogProps { }
|
interface IProps extends IDialogProps { }
|
||||||
|
|
||||||
|
@ -45,7 +46,9 @@ export default class StorageEvictedDialog extends React.Component<IProps> {
|
||||||
"To help us prevent this in future, please <a>send us logs</a>.",
|
"To help us prevent this in future, please <a>send us logs</a>.",
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
a: text => <a href="#" onClick={this.sendBugReport}>{ text }</a>,
|
a: text => <AccessibleButton kind='link_inline' onClick={this.sendBugReport}>
|
||||||
|
{ text }
|
||||||
|
</AccessibleButton>,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,10 +287,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
|
||||||
const resetButton = (
|
const resetButton = (
|
||||||
<div className="mx_AccessSecretStorageDialog_reset">
|
<div className="mx_AccessSecretStorageDialog_reset">
|
||||||
{ _t("Forgotten or lost all recovery methods? <a>Reset all</a>", null, {
|
{ _t("Forgotten or lost all recovery methods? <a>Reset all</a>", null, {
|
||||||
a: (sub) => <a
|
a: (sub) => <AccessibleButton
|
||||||
href=""
|
kind="link_inline"
|
||||||
onClick={this.onResetAllClick}
|
onClick={this.onResetAllClick}
|
||||||
className="mx_AccessSecretStorageDialog_reset_link">{ sub }</a>,
|
className="mx_AccessSecretStorageDialog_reset_link">{ sub }</AccessibleButton>,
|
||||||
}) }
|
}) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,6 +21,19 @@ import { Key } from '../../../Keyboard';
|
||||||
|
|
||||||
export type ButtonEvent = React.MouseEvent<Element> | React.KeyboardEvent<Element> | React.FormEvent<Element>;
|
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.
|
* children: React's magic prop. Represents all children given to the element.
|
||||||
* element: (optional) The base element type. "div" by default.
|
* element: (optional) The base element type. "div" by default.
|
||||||
|
@ -32,7 +45,7 @@ interface IProps extends React.InputHTMLAttributes<Element> {
|
||||||
element?: keyof ReactHTML;
|
element?: keyof ReactHTML;
|
||||||
// The kind of button, similar to how Bootstrap works.
|
// The kind of button, similar to how Bootstrap works.
|
||||||
// See available classes for AccessibleButton for options.
|
// See available classes for AccessibleButton for options.
|
||||||
kind?: string;
|
kind?: AccessibleButtonKind | string;
|
||||||
// The ARIA role
|
// The ARIA role
|
||||||
role?: string;
|
role?: string;
|
||||||
// The tabIndex
|
// The tabIndex
|
||||||
|
|
|
@ -23,6 +23,7 @@ import SdkConfig from "../../../SdkConfig";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
import { UserTab } from "../dialogs/UserSettingsDialog";
|
import { UserTab } from "../dialogs/UserSettingsDialog";
|
||||||
|
import AccessibleButton from "./AccessibleButton";
|
||||||
|
|
||||||
export enum WarningKind {
|
export enum WarningKind {
|
||||||
Files,
|
Files,
|
||||||
|
@ -41,15 +42,19 @@ export default function DesktopBuildsNotice({ isRoomEncrypted, kind }: IProps) {
|
||||||
if (EventIndexPeg.error) {
|
if (EventIndexPeg.error) {
|
||||||
return <>
|
return <>
|
||||||
{ _t("Message search initialisation failed, check <a>your settings</a> for more information", {}, {
|
{ _t("Message search initialisation failed, check <a>your settings</a> for more information", {}, {
|
||||||
a: sub => (<a onClick={(evt) => {
|
a: sub => (
|
||||||
evt.preventDefault();
|
<AccessibleButton
|
||||||
dis.dispatch({
|
kind="link_inline"
|
||||||
action: Action.ViewUserSettings,
|
onClick={(evt) => {
|
||||||
initialTabId: UserTab.Security,
|
evt.preventDefault();
|
||||||
});
|
dis.dispatch({
|
||||||
}}>
|
action: Action.ViewUserSettings,
|
||||||
{ sub }
|
initialTabId: UserTab.Security,
|
||||||
</a>),
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>),
|
||||||
}) }
|
}) }
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePha
|
||||||
import { jsxJoin } from '../../../utils/ReactUtils';
|
import { jsxJoin } from '../../../utils/ReactUtils';
|
||||||
import { Layout } from '../../../settings/enums/Layout';
|
import { Layout } from '../../../settings/enums/Layout';
|
||||||
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
|
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
|
||||||
|
import AccessibleButton from './AccessibleButton';
|
||||||
|
|
||||||
const onPinnedMessagesClick = (): void => {
|
const onPinnedMessagesClick = (): void => {
|
||||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.PinnedMessages }, false);
|
RightPanelStore.instance.setCard({ phase: RightPanelPhases.PinnedMessages }, false);
|
||||||
|
@ -322,10 +323,18 @@ export default class MemberEventListSummary extends React.Component<IProps> {
|
||||||
res = (userCount > 1)
|
res = (userCount > 1)
|
||||||
? _t("%(severalUsers)schanged the <a>pinned messages</a> for the room %(count)s times.",
|
? _t("%(severalUsers)schanged the <a>pinned messages</a> for the room %(count)s times.",
|
||||||
{ severalUsers: "", count: repeats },
|
{ 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.",
|
: _t("%(oneUser)schanged the <a>pinned messages</a> for the room %(count)s times.",
|
||||||
{ oneUser: "", count: repeats },
|
{ oneUser: "", count: repeats },
|
||||||
{ "a": (sub) => <a onClick={onPinnedMessagesClick}> { sub } </a> });
|
{
|
||||||
|
"a": (sub) => <AccessibleButton kind='link_inline' onClick={onPinnedMessagesClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import Spinner from './Spinner';
|
import Spinner from './Spinner';
|
||||||
import ReplyTile from "../rooms/ReplyTile";
|
import ReplyTile from "../rooms/ReplyTile";
|
||||||
import Pill from './Pill';
|
import Pill from './Pill';
|
||||||
|
import { ButtonEvent } from './AccessibleButton';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This number is based on the previous behavior - if we have message of height
|
* 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();
|
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];
|
const events = [this.state.loadedEv, ...this.state.events];
|
||||||
|
|
||||||
let loadedEv = null;
|
let loadedEv = null;
|
||||||
|
@ -380,7 +381,11 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
||||||
header = <blockquote className={`mx_ReplyChain ${this.getReplyChainColorClass(ev)}`}>
|
header = <blockquote className={`mx_ReplyChain ${this.getReplyChainColorClass(ev)}`}>
|
||||||
{
|
{
|
||||||
_t('<a>In reply to</a> <pill>', {}, {
|
_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': (
|
||||||
<Pill
|
<Pill
|
||||||
type={Pill.TYPE_USER_MENTION}
|
type={Pill.TYPE_USER_MENTION}
|
||||||
|
|
|
@ -258,7 +258,9 @@ export default class MFileBody extends React.Component<IProps, IState> {
|
||||||
* We'll use it to learn how the download link
|
* We'll use it to learn how the download link
|
||||||
* would have been styled if it was rendered inline.
|
* 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 */ }
|
||||||
<a ref={this.dummyLink} />
|
<a ref={this.dummyLink} />
|
||||||
</div>
|
</div>
|
||||||
{ /*
|
{ /*
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -40,7 +41,11 @@ export default class MjolnirBody extends React.Component<IProps> {
|
||||||
return (
|
return (
|
||||||
<div className='mx_MjolnirBody'><i>{ _t(
|
<div className='mx_MjolnirBody'><i>{ _t(
|
||||||
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>",
|
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>",
|
||||||
{}, { a: (sub) => <a href="#" onClick={this.onAllowClick}>{ sub }</a> },
|
{}, {
|
||||||
|
a: (sub) => <AccessibleButton kind="link_inline" onClick={this.onAllowClick}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
|
},
|
||||||
) }</i></div>
|
) }</i></div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import ContextMenu, { aboveLeftOf, useContextMenu } from "../../structures/Conte
|
||||||
import ReactionPicker from "../emojipicker/ReactionPicker";
|
import ReactionPicker from "../emojipicker/ReactionPicker";
|
||||||
import ReactionsRowButton from "./ReactionsRowButton";
|
import ReactionsRowButton from "./ReactionsRowButton";
|
||||||
import RoomContext from "../../../contexts/RoomContext";
|
import RoomContext from "../../../contexts/RoomContext";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
|
||||||
// The maximum number of reactions to initially show on a message.
|
// The maximum number of reactions to initially show on a message.
|
||||||
const MAX_ITEMS_WHEN_LIMITED = 8;
|
const MAX_ITEMS_WHEN_LIMITED = 8;
|
||||||
|
@ -201,13 +202,13 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
|
||||||
let showAllButton;
|
let showAllButton;
|
||||||
if ((items.length > MAX_ITEMS_WHEN_LIMITED + 1) && !showAll) {
|
if ((items.length > MAX_ITEMS_WHEN_LIMITED + 1) && !showAll) {
|
||||||
items = items.slice(0, MAX_ITEMS_WHEN_LIMITED);
|
items = items.slice(0, MAX_ITEMS_WHEN_LIMITED);
|
||||||
showAllButton = <a
|
showAllButton = <AccessibleButton
|
||||||
|
kind="link_inline"
|
||||||
className="mx_ReactionsRow_showAll"
|
className="mx_ReactionsRow_showAll"
|
||||||
href="#"
|
|
||||||
onClick={this.onShowAllClick}
|
onClick={this.onShowAllClick}
|
||||||
>
|
>
|
||||||
{ _t("Show all") }
|
{ _t("Show all") }
|
||||||
</a>;
|
</AccessibleButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let addReactionButton;
|
let addReactionButton;
|
||||||
|
|
|
@ -45,6 +45,7 @@ import EditMessageComposer from '../rooms/EditMessageComposer';
|
||||||
import LinkPreviewGroup from '../rooms/LinkPreviewGroup';
|
import LinkPreviewGroup from '../rooms/LinkPreviewGroup';
|
||||||
import { IBodyProps } from "./IBodyProps";
|
import { IBodyProps } from "./IBodyProps";
|
||||||
import RoomContext from "../../../contexts/RoomContext";
|
import RoomContext from "../../../contexts/RoomContext";
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
const MAX_HIGHLIGHT_LENGTH = 4096;
|
const MAX_HIGHLIGHT_LENGTH = 4096;
|
||||||
|
|
||||||
|
@ -529,9 +530,9 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||||
if (this.props.highlightLink) {
|
if (this.props.highlightLink) {
|
||||||
body = <a href={this.props.highlightLink}>{ body }</a>;
|
body = <a href={this.props.highlightLink}>{ body }</a>;
|
||||||
} else if (content.data && typeof content.data["org.matrix.neb.starter_link"] === "string") {
|
} else if (content.data && typeof content.data["org.matrix.neb.starter_link"] === "string") {
|
||||||
body = <a href="#"
|
body = <AccessibleButton kind="link_inline"
|
||||||
onClick={this.onStarterLinkClick.bind(this, content.data["org.matrix.neb.starter_link"])}
|
onClick={this.onStarterLinkClick.bind(this, content.data["org.matrix.neb.starter_link"])}
|
||||||
>{ body }</a>;
|
>{ body }</AccessibleButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let widgets;
|
let widgets;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import Modal from '../../../Modal';
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import BugReportDialog from '../dialogs/BugReportDialog';
|
import BugReportDialog from '../dialogs/BugReportDialog';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -67,9 +68,9 @@ export default class TileErrorBoundary extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
let submitLogsButton;
|
let submitLogsButton;
|
||||||
if (SdkConfig.get().bug_report_endpoint_url) {
|
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||||
submitLogsButton = <a onClick={this.onBugReport} href="#">
|
submitLogsButton = <AccessibleButton kind="link_inline" onClick={this.onBugReport}>
|
||||||
{ _t("Submit logs") }
|
{ _t("Submit logs") }
|
||||||
</a>;
|
</AccessibleButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<div className={classNames(classes)}>
|
return (<div className={classNames(classes)}>
|
||||||
|
|
|
@ -21,6 +21,7 @@ import classNames from 'classnames';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -76,7 +77,8 @@ export default class ViewSourceEvent extends React.PureComponent<IProps, IState>
|
||||||
|
|
||||||
return <span className={classes}>
|
return <span className={classes}>
|
||||||
{ content }
|
{ content }
|
||||||
<button
|
<AccessibleButton
|
||||||
|
kind='link_inline'
|
||||||
title={_t('toggle event')}
|
title={_t('toggle event')}
|
||||||
className="mx_ViewSourceEvent_toggle"
|
className="mx_ViewSourceEvent_toggle"
|
||||||
onClick={this.onToggle}
|
onClick={this.onToggle}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { SettingLevel } from "../../../settings/SettingLevel";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import SettingsFlag from "../elements/SettingsFlag";
|
import SettingsFlag from "../elements/SettingsFlag";
|
||||||
import SettingsFieldset from '../settings/SettingsFieldset';
|
import SettingsFieldset from '../settings/SettingsFieldset';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -55,13 +56,21 @@ export default class UrlPreviewSettings extends React.Component<IProps> {
|
||||||
if (accountEnabled) {
|
if (accountEnabled) {
|
||||||
previewsForAccount = (
|
previewsForAccount = (
|
||||||
_t("You have <a>enabled</a> URL previews by default.", {}, {
|
_t("You have <a>enabled</a> URL previews by default.", {}, {
|
||||||
'a': (sub)=><a onClick={this.onClickUserSettings} href=''>{ sub }</a>,
|
'a': (sub) => <AccessibleButton
|
||||||
|
kind='link_inline'
|
||||||
|
onClick={this.onClickUserSettings}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
previewsForAccount = (
|
previewsForAccount = (
|
||||||
_t("You have <a>disabled</a> URL previews by default.", {}, {
|
_t("You have <a>disabled</a> URL previews by default.", {}, {
|
||||||
'a': (sub)=><a onClick={this.onClickUserSettings} href=''>{ sub }</a>,
|
'a': (sub) => <AccessibleButton
|
||||||
|
kind='link_inline'
|
||||||
|
onClick={this.onClickUserSettings}>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ import { ThreadNotificationState } from '../../../stores/notifications/ThreadNot
|
||||||
import { RoomNotificationStateStore } from '../../../stores/notifications/RoomNotificationStateStore';
|
import { RoomNotificationStateStore } from '../../../stores/notifications/RoomNotificationStateStore';
|
||||||
import { NotificationStateEvents } from '../../../stores/notifications/NotificationState';
|
import { NotificationStateEvents } from '../../../stores/notifications/NotificationState';
|
||||||
import { NotificationColor } from '../../../stores/notifications/NotificationColor';
|
import { NotificationColor } from '../../../stores/notifications/NotificationColor';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import { CardContext } from '../right_panel/BaseCard';
|
import { CardContext } from '../right_panel/BaseCard';
|
||||||
|
|
||||||
const eventTileTypes = {
|
const eventTileTypes = {
|
||||||
|
@ -1262,7 +1263,17 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
_t(
|
_t(
|
||||||
'<requestLink>Re-request encryption keys</requestLink> from your other sessions.',
|
'<requestLink>Re-request encryption keys</requestLink> from your other sessions.',
|
||||||
{},
|
{},
|
||||||
{ 'requestLink': (sub) => <a tabIndex={0} onClick={this.onRequestKeysClick}>{ sub }</a> },
|
{
|
||||||
|
'requestLink': (sub) =>
|
||||||
|
<AccessibleButton
|
||||||
|
className="mx_EventTile_rerequestKeysCta"
|
||||||
|
kind='link_inline'
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={this.onRequestKeysClick}
|
||||||
|
>
|
||||||
|
{ sub }
|
||||||
|
</AccessibleButton>,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const keyRequestInfo = isEncryptionFailure && !isRedacted ?
|
const keyRequestInfo = isEncryptionFailure && !isRedacted ?
|
||||||
|
|
|
@ -207,7 +207,7 @@ const NewRoomIntro = () => {
|
||||||
let subButton;
|
let subButton;
|
||||||
if (room.currentState.mayClientSendStateEvent(EventType.RoomEncryption, MatrixClientPeg.get())) {
|
if (room.currentState.mayClientSendStateEvent(EventType.RoomEncryption, MatrixClientPeg.get())) {
|
||||||
subButton = (
|
subButton = (
|
||||||
<a onClick={openRoomSettings} href="#"> { _t("Enable encryption in settings.") }</a>
|
<AccessibleButton kind='link_inline' onClick={openRoomSettings}>{ _t("Enable encryption in settings.") }</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import CreateRoomDialog from '../../../dialogs/CreateRoomDialog';
|
||||||
import JoinRuleSettings from "../../JoinRuleSettings";
|
import JoinRuleSettings from "../../JoinRuleSettings";
|
||||||
import ErrorDialog from "../../../dialogs/ErrorDialog";
|
import ErrorDialog from "../../../dialogs/ErrorDialog";
|
||||||
import SettingsFieldset from '../../SettingsFieldset';
|
import SettingsFieldset from '../../SettingsFieldset';
|
||||||
|
import ExternalLink from '../../../elements/ExternalLink';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
@ -139,12 +140,13 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
"To avoid these issues, create a <a>new encrypted room</a> for " +
|
"To avoid these issues, create a <a>new encrypted room</a> for " +
|
||||||
"the conversation you plan to have.",
|
"the conversation you plan to have.",
|
||||||
null,
|
null,
|
||||||
{ "a": (sub) => <a
|
{
|
||||||
className="mx_linkButton"
|
"a": (sub) => <AccessibleButton kind='link_inline'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dialog.close();
|
dialog.close();
|
||||||
this.createNewRoom(false, true);
|
this.createNewRoom(false, true);
|
||||||
}}> { sub } </a> },
|
}}> { sub } </AccessibleButton>,
|
||||||
|
},
|
||||||
) } </p>
|
) } </p>
|
||||||
</div>,
|
</div>,
|
||||||
|
|
||||||
|
@ -163,11 +165,9 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
"may prevent many bots and bridges from working correctly. <a>Learn more about encryption.</a>",
|
"may prevent many bots and bridges from working correctly. <a>Learn more about encryption.</a>",
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
a: sub => <a
|
a: sub => <ExternalLink
|
||||||
href="https://element.io/help#encryption"
|
href="https://element.io/help#encryption"
|
||||||
rel="noreferrer noopener"
|
>{ sub }</ExternalLink>,
|
||||||
target="_blank"
|
|
||||||
>{ sub }</a>,
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
onFinished: (confirm) => {
|
onFinished: (confirm) => {
|
||||||
|
@ -306,12 +306,12 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
||||||
"you plan to have.",
|
"you plan to have.",
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
"a": (sub) => <a
|
"a": (sub) => <AccessibleButton
|
||||||
className="mx_linkButton"
|
kind='link_inline'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dialog.close();
|
dialog.close();
|
||||||
this.createNewRoom(true, false);
|
this.createNewRoom(true, false);
|
||||||
}}> { sub } </a>,
|
}}> { sub } </AccessibleButton>,
|
||||||
},
|
},
|
||||||
) } </p>
|
) } </p>
|
||||||
</div>,
|
</div>,
|
||||||
|
|
|
@ -2476,6 +2476,7 @@
|
||||||
"Setting ID": "Setting ID",
|
"Setting ID": "Setting ID",
|
||||||
"Value": "Value",
|
"Value": "Value",
|
||||||
"Value in this room": "Value in this room",
|
"Value in this room": "Value in this room",
|
||||||
|
"Edit setting": "Edit setting",
|
||||||
"Setting:": "Setting:",
|
"Setting:": "Setting:",
|
||||||
"Caution:": "Caution:",
|
"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.",
|
"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.",
|
||||||
|
|
|
@ -40,7 +40,7 @@ describe('<SettingsFieldset />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders fieldset with react description', () => {
|
it('renders fieldset with react description', () => {
|
||||||
const description = <><p>Test</p><a href='#'>a link</a></>;
|
const description = <><p>Test</p><a href='#test'>a link</a></>;
|
||||||
expect(getComponent({ description })).toMatchSnapshot();
|
expect(getComponent({ description })).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,7 +38,7 @@ exports[`<SettingsFieldset /> renders fieldset with react description 1`] = `
|
||||||
Test
|
Test
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#test"
|
||||||
>
|
>
|
||||||
a link
|
a link
|
||||||
</a>
|
</a>
|
||||||
|
|
Loading…
Reference in a new issue