mirror of https://github.com/vector-im/riot-web
Use semantic headings in user settings - account (#10972)
* account password section * account email and phone numbers * update cypress selectorspull/28788/head^2
parent
8d77d6e4cc
commit
d0d9a36d07
|
@ -83,10 +83,14 @@ describe("General user settings tab", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait until spinners disappear
|
// Wait until spinners disappear
|
||||||
cy.get(".mx_GeneralUserSettingsTab_section--account .mx_Spinner").should("not.exist");
|
cy.findByTestId("accountSection").within(() => {
|
||||||
cy.get(".mx_GeneralUserSettingsTab_section--discovery .mx_Spinner").should("not.exist");
|
cy.get(".mx_Spinner").should("not.exist");
|
||||||
|
});
|
||||||
|
cy.findByTestId("discoverySection").within(() => {
|
||||||
|
cy.get(".mx_Spinner").should("not.exist");
|
||||||
|
});
|
||||||
|
|
||||||
cy.get(".mx_GeneralUserSettingsTab_section--account").within(() => {
|
cy.findByTestId("accountSection").within(() => {
|
||||||
// Assert that input areas for changing a password exists
|
// Assert that input areas for changing a password exists
|
||||||
cy.get("form.mx_GeneralUserSettingsTab_section--account_changePassword")
|
cy.get("form.mx_GeneralUserSettingsTab_section--account_changePassword")
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
|
@ -95,29 +99,28 @@ describe("General user settings tab", () => {
|
||||||
cy.findByLabelText("New Password").should("be.visible");
|
cy.findByLabelText("New Password").should("be.visible");
|
||||||
cy.findByLabelText("Confirm password").should("be.visible");
|
cy.findByLabelText("Confirm password").should("be.visible");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check email addresses area
|
|
||||||
cy.get(".mx_EmailAddresses")
|
|
||||||
.scrollIntoView()
|
|
||||||
.within(() => {
|
|
||||||
// Assert that an input area for a new email address is rendered
|
|
||||||
cy.findByRole("textbox", { name: "Email Address" }).should("be.visible");
|
|
||||||
|
|
||||||
// Assert the add button is visible
|
|
||||||
cy.findByRole("button", { name: "Add" }).should("be.visible");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check phone numbers area
|
|
||||||
cy.get(".mx_PhoneNumbers")
|
|
||||||
.scrollIntoView()
|
|
||||||
.within(() => {
|
|
||||||
// Assert that an input area for a new phone number is rendered
|
|
||||||
cy.findByRole("textbox", { name: "Phone Number" }).should("be.visible");
|
|
||||||
|
|
||||||
// Assert that the add button is rendered
|
|
||||||
cy.findByRole("button", { name: "Add" }).should("be.visible");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
// Check email addresses area
|
||||||
|
cy.findByTestId("mx_AccountEmailAddresses")
|
||||||
|
.scrollIntoView()
|
||||||
|
.within(() => {
|
||||||
|
// Assert that an input area for a new email address is rendered
|
||||||
|
cy.findByRole("textbox", { name: "Email Address" }).should("be.visible");
|
||||||
|
|
||||||
|
// Assert the add button is visible
|
||||||
|
cy.findByRole("button", { name: "Add" }).should("be.visible");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check phone numbers area
|
||||||
|
cy.findByTestId("mx_AccountPhoneNumbers")
|
||||||
|
.scrollIntoView()
|
||||||
|
.within(() => {
|
||||||
|
// Assert that an input area for a new phone number is rendered
|
||||||
|
cy.findByRole("textbox", { name: "Phone Number" }).should("be.visible");
|
||||||
|
|
||||||
|
// Assert that the add button is rendered
|
||||||
|
cy.findByRole("button", { name: "Add" }).should("be.visible");
|
||||||
|
});
|
||||||
|
|
||||||
// Check language and region setting dropdown
|
// Check language and region setting dropdown
|
||||||
cy.get(".mx_GeneralUserSettingsTab_section_languageInput")
|
cy.get(".mx_GeneralUserSettingsTab_section_languageInput")
|
||||||
|
@ -188,7 +191,7 @@ describe("General user settings tab", () => {
|
||||||
|
|
||||||
it("should set a country calling code based on default_country_code", () => {
|
it("should set a country calling code based on default_country_code", () => {
|
||||||
// Check phone numbers area
|
// Check phone numbers area
|
||||||
cy.get(".mx_PhoneNumbers")
|
cy.findByTestId("mx_AccountPhoneNumbers")
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
.within(() => {
|
.within(() => {
|
||||||
// Assert that an input area for a new phone number is rendered
|
// Assert that an input area for a new phone number is rendered
|
||||||
|
|
|
@ -17,16 +17,12 @@ limitations under the License.
|
||||||
.mx_TagComposer {
|
.mx_TagComposer {
|
||||||
.mx_TagComposer_input {
|
.mx_TagComposer_input {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
.mx_Field {
|
|
||||||
flex: 1;
|
|
||||||
margin: 0; /* override from field styles */
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
min-width: 70px;
|
min-width: 70px;
|
||||||
padding: 0 8px; /* override from button styles */
|
padding: 0 8px; /* override from button styles */
|
||||||
margin-left: 16px; /* distance from <Field> */
|
align-self: stretch; /* override default settingstab style */
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Field,
|
.mx_Field,
|
||||||
|
|
|
@ -24,6 +24,24 @@ limitations under the License.
|
||||||
a {
|
a {
|
||||||
color: $links;
|
color: $links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $spacing-8;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
// never want full width buttons
|
||||||
|
// event when other content is 100% width
|
||||||
|
.mx_AccessibleButton {
|
||||||
|
align-self: flex-start;
|
||||||
|
justify-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Field {
|
||||||
|
margin: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SettingsTab_warningText {
|
.mx_SettingsTab_warningText {
|
||||||
|
|
|
@ -14,41 +14,9 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_section--account_changePassword {
|
|
||||||
.mx_Field {
|
|
||||||
margin-inline-end: var(--SettingsTab_fullWidthField-margin-inline-end);
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Make this selector less painful */
|
|
||||||
.mx_GeneralUserSettingsTab_section--account .mx_SettingsTab_subheading:nth-child(n + 1),
|
|
||||||
.mx_GeneralUserSettingsTab_section--discovery .mx_SettingsTab_subheading:nth-child(n + 2),
|
|
||||||
.mx_SetIdServer .mx_SettingsTab_subheading {
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_section--account,
|
|
||||||
.mx_GeneralUserSettingsTab_section--discovery {
|
|
||||||
.mx_Spinner {
|
|
||||||
/* Move the spinner to the left side of the container (default center) */
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_section--account .mx_EmailAddresses,
|
|
||||||
.mx_GeneralUserSettingsTab_section--account .mx_PhoneNumbers,
|
|
||||||
.mx_GeneralUserSettingsTab_section--discovery .mx_GeneralUserSettingsTab_section--discovery_existing {
|
|
||||||
margin-inline-end: var(--SettingsTab_fullWidthField-margin-inline-end);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_section--discovery_existing {
|
.mx_GeneralUserSettingsTab_section--discovery_existing {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_section--discovery_existing_address,
|
.mx_GeneralUserSettingsTab_section--discovery_existing_address,
|
||||||
|
|
|
@ -276,7 +276,7 @@ export default class EmailAddresses extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_EmailAddresses">
|
<>
|
||||||
{existingEmailElements}
|
{existingEmailElements}
|
||||||
<form onSubmit={this.onAddClick} autoComplete="off" noValidate={true}>
|
<form onSubmit={this.onAddClick} autoComplete="off" noValidate={true}>
|
||||||
<Field
|
<Field
|
||||||
|
@ -289,7 +289,7 @@ export default class EmailAddresses extends React.Component<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
{addButton}
|
{addButton}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -305,7 +305,7 @@ export default class PhoneNumbers extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_PhoneNumbers">
|
<>
|
||||||
{existingPhoneElements}
|
{existingPhoneElements}
|
||||||
<form onSubmit={this.onAddClick} autoComplete="off" noValidate={true} className="mx_PhoneNumbers_new">
|
<form onSubmit={this.onAddClick} autoComplete="off" noValidate={true} className="mx_PhoneNumbers_new">
|
||||||
<div className="mx_PhoneNumbers_input">
|
<div className="mx_PhoneNumbers_input">
|
||||||
|
@ -321,7 +321,7 @@ export default class PhoneNumbers extends React.Component<IProps, IState> {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{addVerifySection}
|
{addVerifySection}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,7 +294,7 @@ export default class PhoneNumbers extends React.Component<IProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsSubsection
|
<SettingsSubsection
|
||||||
data-testid="mx_PhoneNumbers"
|
data-testid="mx_DiscoveryPhoneNumbers"
|
||||||
heading={_t("Phone numbers")}
|
heading={_t("Phone numbers")}
|
||||||
description={description}
|
description={description}
|
||||||
stretchContent
|
stretchContent
|
||||||
|
|
|
@ -60,6 +60,7 @@ import { SettingsSection } from "../../shared/SettingsSection";
|
||||||
import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection";
|
import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection";
|
||||||
import { SettingsSubsectionHeading } from "../../shared/SettingsSubsectionHeading";
|
import { SettingsSubsectionHeading } from "../../shared/SettingsSubsectionHeading";
|
||||||
import Heading from "../../../typography/Heading";
|
import Heading from "../../../typography/Heading";
|
||||||
|
import InlineSpinner from "../../../elements/InlineSpinner";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeSettingsFn: () => void;
|
closeSettingsFn: () => void;
|
||||||
|
@ -196,7 +197,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
);
|
);
|
||||||
logger.warn(e);
|
logger.warn(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
emails: threepids.filter((a) => a.medium === ThreepidMedium.Email),
|
emails: threepids.filter((a) => a.medium === ThreepidMedium.Email),
|
||||||
msisdns: threepids.filter((a) => a.medium === ThreepidMedium.Phone),
|
msisdns: threepids.filter((a) => a.medium === ThreepidMedium.Phone),
|
||||||
|
@ -329,16 +329,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderAccountSection(): JSX.Element {
|
private renderAccountSection(): JSX.Element {
|
||||||
let passwordChangeForm: ReactNode = (
|
|
||||||
<ChangePassword
|
|
||||||
className="mx_GeneralUserSettingsTab_section--account_changePassword"
|
|
||||||
rowClassName=""
|
|
||||||
buttonKind="primary"
|
|
||||||
onError={this.onPasswordChangeError}
|
|
||||||
onFinished={this.onPasswordChanged}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
let threepidSection: ReactNode = null;
|
let threepidSection: ReactNode = null;
|
||||||
|
|
||||||
// For older homeservers without separate 3PID add and bind methods (MSC2290),
|
// For older homeservers without separate 3PID add and bind methods (MSC2290),
|
||||||
|
@ -351,33 +341,52 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
(this.state.haveIdServer || this.state.serverSupportsSeparateAddAndBind === true)
|
(this.state.haveIdServer || this.state.serverSupportsSeparateAddAndBind === true)
|
||||||
) {
|
) {
|
||||||
const emails = this.state.loading3pids ? (
|
const emails = this.state.loading3pids ? (
|
||||||
<Spinner />
|
<InlineSpinner />
|
||||||
) : (
|
) : (
|
||||||
<AccountEmailAddresses emails={this.state.emails} onEmailsChange={this.onEmailsChange} />
|
<AccountEmailAddresses emails={this.state.emails} onEmailsChange={this.onEmailsChange} />
|
||||||
);
|
);
|
||||||
const msisdns = this.state.loading3pids ? (
|
const msisdns = this.state.loading3pids ? (
|
||||||
<Spinner />
|
<InlineSpinner />
|
||||||
) : (
|
) : (
|
||||||
<AccountPhoneNumbers msisdns={this.state.msisdns} onMsisdnsChange={this.onMsisdnsChange} />
|
<AccountPhoneNumbers msisdns={this.state.msisdns} onMsisdnsChange={this.onMsisdnsChange} />
|
||||||
);
|
);
|
||||||
threepidSection = (
|
threepidSection = (
|
||||||
<div>
|
<>
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Email addresses")}</span>
|
<SettingsSubsection
|
||||||
{emails}
|
heading={_t("Email addresses")}
|
||||||
|
stretchContent
|
||||||
|
data-testid="mx_AccountEmailAddresses"
|
||||||
|
>
|
||||||
|
{emails}
|
||||||
|
</SettingsSubsection>
|
||||||
|
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Phone numbers")}</span>
|
<SettingsSubsection
|
||||||
{msisdns}
|
heading={_t("Phone numbers")}
|
||||||
</div>
|
stretchContent
|
||||||
|
data-testid="mx_AccountPhoneNumbers"
|
||||||
|
>
|
||||||
|
{msisdns}
|
||||||
|
</SettingsSubsection>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
} else if (this.state.serverSupportsSeparateAddAndBind === null) {
|
} else if (this.state.serverSupportsSeparateAddAndBind === null) {
|
||||||
threepidSection = <Spinner />;
|
threepidSection = <Spinner />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let passwordChangeText: ReactNode = _t("Set a new account password…");
|
let passwordChangeSection: ReactNode = null;
|
||||||
if (!this.state.canChangePassword) {
|
if (this.state.canChangePassword) {
|
||||||
// Just don't show anything if you can't do anything.
|
passwordChangeSection = (
|
||||||
passwordChangeText = null;
|
<>
|
||||||
passwordChangeForm = null;
|
<SettingsSubsectionText>{_t("Set a new account password…")}</SettingsSubsectionText>
|
||||||
|
<ChangePassword
|
||||||
|
className="mx_GeneralUserSettingsTab_section--account_changePassword"
|
||||||
|
rowClassName=""
|
||||||
|
buttonKind="primary"
|
||||||
|
onError={this.onPasswordChangeError}
|
||||||
|
onFinished={this.onPasswordChanged}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let externalAccountManagement: JSX.Element | undefined;
|
let externalAccountManagement: JSX.Element | undefined;
|
||||||
|
@ -386,13 +395,13 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
|
|
||||||
externalAccountManagement = (
|
externalAccountManagement = (
|
||||||
<>
|
<>
|
||||||
<p className="mx_SettingsTab_subsectionText" data-testid="external-account-management-outer">
|
<SettingsSubsectionText data-testid="external-account-management-outer">
|
||||||
{_t(
|
{_t(
|
||||||
"Your account details are managed separately at <code>%(hostname)s</code>.",
|
"Your account details are managed separately at <code>%(hostname)s</code>.",
|
||||||
{ hostname },
|
{ hostname },
|
||||||
{ code: (sub) => <code>{sub}</code> },
|
{ code: (sub) => <code>{sub}</code> },
|
||||||
)}
|
)}
|
||||||
</p>
|
</SettingsSubsectionText>
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
onClick={null}
|
onClick={null}
|
||||||
element="a"
|
element="a"
|
||||||
|
@ -408,13 +417,13 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="mx_SettingsTab_section mx_GeneralUserSettingsTab_section--account">
|
<>
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Account")}</span>
|
<SettingsSubsection heading={_t("Account")} stretchContent data-testid="accountSection">
|
||||||
{externalAccountManagement}
|
{externalAccountManagement}
|
||||||
<p className="mx_SettingsTab_subsectionText">{passwordChangeText}</p>
|
{passwordChangeSection}
|
||||||
{passwordChangeForm}
|
</SettingsSubsection>
|
||||||
{threepidSection}
|
{threepidSection}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,7 +549,11 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
{_t("Discovery")}
|
{_t("Discovery")}
|
||||||
</Heading>
|
</Heading>
|
||||||
);
|
);
|
||||||
discoverySection = <SettingsSection heading={heading}>{this.renderDiscoverySection()}</SettingsSection>;
|
discoverySection = (
|
||||||
|
<SettingsSection heading={heading} data-testid="discoverySection">
|
||||||
|
{this.renderDiscoverySection()}
|
||||||
|
</SettingsSection>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -4,7 +4,7 @@ exports[`<PhoneNumbers /> should handle no numbers 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection"
|
class="mx_SettingsSubsection"
|
||||||
data-testid="mx_PhoneNumbers"
|
data-testid="mx_DiscoveryPhoneNumbers"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
|
@ -32,7 +32,7 @@ exports[`<PhoneNumbers /> should render a loader while loading 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection"
|
class="mx_SettingsSubsection"
|
||||||
data-testid="mx_PhoneNumbers"
|
data-testid="mx_DiscoveryPhoneNumbers"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
|
@ -64,7 +64,7 @@ exports[`<PhoneNumbers /> should render phone numbers 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsection"
|
class="mx_SettingsSubsection"
|
||||||
data-testid="mx_PhoneNumbers"
|
data-testid="mx_DiscoveryPhoneNumbers"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsSubsectionHeading"
|
class="mx_SettingsSubsectionHeading"
|
||||||
|
|
Loading…
Reference in New Issue