Use EditInPlace control for Identity Server picker to improve a11y (#29280)
* Use EditInPlace for identity server picker. * Exclude picker from default dialog button styles. * Remove unused import. * Update test * Remove unused css * Update test * drop only * Add a test for setting an ID server. * Add a unit test for SetIdServer * fix tests * Reformat mx_Dialog button :not list to use a more readable selector. * Reformat other :not sections * forgot a comma * We're in 2025 now. * Update copyright + use class methods.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024-2025 New Vector Ltd.
|
||||
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React, { type ReactNode } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type IThreepid } from "matrix-js-sdk/src/matrix";
|
||||
import { EditInPlace, ErrorMessage } from "@vector-im/compound-web";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
@@ -22,7 +23,6 @@ import { timeout } from "../../../utils/promise";
|
||||
import { type ActionPayload } from "../../../dispatcher/payloads";
|
||||
import InlineSpinner from "../elements/InlineSpinner";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import Field from "../elements/Field";
|
||||
import QuestionDialog from "../dialogs/QuestionDialog";
|
||||
import SettingsFieldset from "./SettingsFieldset";
|
||||
import { SettingsSubsectionText } from "./shared/SettingsSubsection";
|
||||
@@ -86,10 +86,12 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
defaultIdServer = abbreviateUrl(getDefaultIdentityServerUrl());
|
||||
}
|
||||
|
||||
const currentClientIdServer = MatrixClientPeg.safeGet().getIdentityServerUrl();
|
||||
|
||||
this.state = {
|
||||
defaultIdServer,
|
||||
currentClientIdServer: MatrixClientPeg.safeGet().getIdentityServerUrl(),
|
||||
idServer: "",
|
||||
currentClientIdServer,
|
||||
idServer: currentClientIdServer ?? "",
|
||||
busy: false,
|
||||
disconnectBusy: false,
|
||||
checking: false,
|
||||
@@ -117,26 +119,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
private onIdentityServerChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const u = ev.target.value;
|
||||
|
||||
this.setState({ idServer: u });
|
||||
};
|
||||
|
||||
private getTooltip = (): JSX.Element | undefined => {
|
||||
if (this.state.checking) {
|
||||
return (
|
||||
<div>
|
||||
<InlineSpinner />
|
||||
{_t("identity_server|checking")}
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.error) {
|
||||
return <strong className="warning">{this.state.error}</strong>;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
private idServerChangeEnabled = (): boolean => {
|
||||
return !!this.state.idServer && !this.state.busy;
|
||||
this.setState({ idServer: u, error: undefined });
|
||||
};
|
||||
|
||||
private saveIdServer = (fullUrl: string): void => {
|
||||
@@ -148,7 +131,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
busy: false,
|
||||
error: undefined,
|
||||
currentClientIdServer: fullUrl,
|
||||
idServer: "",
|
||||
idServer: fullUrl,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -175,7 +158,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
// Double check that the identity server even has terms of service.
|
||||
const hasTerms = await doesIdentityServerHaveTerms(MatrixClientPeg.safeGet(), fullUrl);
|
||||
if (!hasTerms) {
|
||||
const [confirmed] = await this.showNoTermsWarning(fullUrl);
|
||||
const [confirmed] = await this.showNoTermsWarning();
|
||||
save = !!confirmed;
|
||||
}
|
||||
|
||||
@@ -213,7 +196,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
});
|
||||
};
|
||||
|
||||
private showNoTermsWarning(fullUrl: string): Promise<[ok?: boolean]> {
|
||||
private showNoTermsWarning(): Promise<[ok?: boolean]> {
|
||||
const { finished } = Modal.createDialog(QuestionDialog, {
|
||||
title: _t("terms|identity_server_no_terms_title"),
|
||||
description: (
|
||||
@@ -347,6 +330,9 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
});
|
||||
};
|
||||
|
||||
private onInputCancel = (): void => this.setState((s) => ({ idServer: s.currentClientIdServer ?? "" }));
|
||||
private onClearServerErrors = (): void => this.setState({ error: undefined });
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const idServerUrl = this.state.currentClientIdServer;
|
||||
let sectionTitle;
|
||||
@@ -356,13 +342,13 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
bodyText = _t(
|
||||
"identity_server|description_connected",
|
||||
{},
|
||||
{ server: (sub) => <strong>{abbreviateUrl(idServerUrl)}</strong> },
|
||||
{ server: () => <strong>{abbreviateUrl(idServerUrl)}</strong> },
|
||||
);
|
||||
if (this.props.missingTerms) {
|
||||
bodyText = _t(
|
||||
"identity_server|change_server_prompt",
|
||||
{},
|
||||
{ server: (sub) => <strong>{abbreviateUrl(idServerUrl)}</strong> },
|
||||
{ server: () => <strong>{abbreviateUrl(idServerUrl)}</strong> },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -393,28 +379,25 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
|
||||
return (
|
||||
<SettingsFieldset legend={sectionTitle} description={bodyText}>
|
||||
<form className="mx_SetIdServer" onSubmit={this.checkIdServer}>
|
||||
<Field
|
||||
label={_t("identity_server|url_field_label")}
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
placeholder={this.state.defaultIdServer}
|
||||
value={this.state.idServer}
|
||||
onChange={this.onIdentityServerChanged}
|
||||
tooltipContent={this.getTooltip()}
|
||||
tooltipClassName="mx_SetIdServer_tooltip"
|
||||
disabled={this.state.busy}
|
||||
forceValidity={this.state.error ? false : undefined}
|
||||
/>
|
||||
<AccessibleButton
|
||||
kind="primary_sm"
|
||||
onClick={this.checkIdServer}
|
||||
disabled={!this.idServerChangeEnabled()}
|
||||
>
|
||||
{_t("action|change")}
|
||||
</AccessibleButton>
|
||||
{discoSection}
|
||||
</form>
|
||||
<EditInPlace
|
||||
className="mx_IdentityServerPicker"
|
||||
cancelButtonLabel={_t("action|reset")}
|
||||
disabled={!!this.state.busy}
|
||||
label={_t("identity_server|url_field_label")}
|
||||
onCancel={this.onInputCancel}
|
||||
onChange={this.onIdentityServerChanged}
|
||||
onClearServerErrors={this.onClearServerErrors}
|
||||
onSave={this.checkIdServer}
|
||||
placeholder={this.state.defaultIdServer}
|
||||
saveButtonLabel={_t("action|change")}
|
||||
savedLabel={this.state.error ? undefined : _t("identity_server|changed")}
|
||||
savingLabel={_t("identity_server|checking")}
|
||||
serverInvalid={!!this.state.error}
|
||||
value={this.state.idServer}
|
||||
>
|
||||
{this.state.error && <ErrorMessage>{this.state.error}</ErrorMessage>}
|
||||
</EditInPlace>
|
||||
{discoSection}
|
||||
</SettingsFieldset>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1245,6 +1245,7 @@
|
||||
"change": "Change identity server",
|
||||
"change_prompt": "Disconnect from the identity server <current /> and connect to <new /> instead?",
|
||||
"change_server_prompt": "If you don't want to use <server /> to discover and be discoverable by existing contacts you know, enter another identity server below.",
|
||||
"changed": "Your identity server has been changed",
|
||||
"checking": "Checking server",
|
||||
"description_connected": "You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.",
|
||||
"description_disconnected": "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.",
|
||||
|
||||
Reference in New Issue
Block a user