FEATURE: Allow to customize the signup form descriptions (#29959)

This commit is contained in:
Jan Cernik 2024-11-27 14:23:14 -03:00 committed by GitHub
parent 43414abf83
commit b4ef3456d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 213 additions and 45 deletions

View File

@ -1,16 +1,26 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { on } from "@ember/modifier"; import { on } from "@ember/modifier";
import { service } from "@ember/service";
import InputTip from "discourse/components/input-tip"; import InputTip from "discourse/components/input-tip";
import TextField from "discourse/components/text-field"; import TextField from "discourse/components/text-field";
import valueEntered from "discourse/helpers/value-entered"; import valueEntered from "discourse/helpers/value-entered";
export default class SidebarEditNavigationMenuTagsModal extends Component { export default class SidebarEditNavigationMenuTagsModal extends Component {
@service siteSettings;
get showFullname() { get showFullname() {
return ( return (
this.siteSettings.full_name_required || this.siteSettings.enable_names this.siteSettings.full_name_required || this.siteSettings.enable_names
); );
} }
get showFullnameInstructions() {
return (
this.siteSettings.signup_form_full_name_instructions &&
!this.args.nameValidation.reason
);
}
<template> <template>
<div ...attributes> <div ...attributes>
<TextField <TextField
@ -27,7 +37,13 @@ export default class SidebarEditNavigationMenuTagsModal extends Component {
{{@nameTitle}} {{@nameTitle}}
</label> </label>
<InputTip @validation={{@nameValidation}} id="fullname-validation" /> {{#if this.showFullnameInstructions}}
<span class="more-info" id="fullname-validation-more-info">
{{this.siteSettings.signup_form_full_name_instructions}}
</span>
{{else}}
<InputTip @validation={{@nameValidation}} id="fullname-validation" />
{{/if}}
</div> </div>
</template> </template>
} }

View File

@ -55,19 +55,14 @@
<label class="alt-placeholder" for="new-account-email"> <label class="alt-placeholder" for="new-account-email">
{{i18n "user.email.title"}} {{i18n "user.email.title"}}
</label> </label>
{{#if {{#if this.showEmailValidation}}
(or
this.emailValidation.ok
(and this.emailValidationVisible this.emailValidation.reason)
)
}}
<InputTip <InputTip
@validation={{this.emailValidation}} @validation={{this.emailValidation}}
id="account-email-validation" id="account-email-validation"
/> />
{{else}} {{else}}
<span class="more-info" id="account-email-validation-more-info"> <span class="more-info" id="account-email-validation-more-info">
{{i18n "user.email.instructions"}} {{this.emailInstructions}}
</span> </span>
{{/if}} {{/if}}
</div> </div>
@ -89,10 +84,17 @@
{{i18n "user.username.title"}} {{i18n "user.username.title"}}
</label> </label>
<InputTip {{#if this.showUsernameInstructions}}
@validation={{this.usernameValidation}} <span class="more-info" id="username-validation-more-info">
id="username-validation" {{this.siteSettings.signup_form_username_instructions}}
/> </span>
{{else}}
<InputTip
@validation={{this.usernameValidation}}
id="username-validation"
/>
{{/if}}
</div> </div>
{{#if this.fullnameRequired}} {{#if this.fullnameRequired}}
@ -120,7 +122,7 @@
<div class="input-group create-account__password"> <div class="input-group create-account__password">
{{#if this.passwordRequired}} {{#if this.passwordRequired}}
<PasswordField <PasswordField
{{on "focusout" this.showPasswordValidation}} {{on "focusout" this.togglePasswordValidation}}
{{on "focusin" this.scrollInputIntoView}} {{on "focusin" this.scrollInputIntoView}}
@value={{this.accountPassword}} @value={{this.accountPassword}}
@capsLockOn={{this.capsLockOn}} @capsLockOn={{this.capsLockOn}}
@ -137,19 +139,15 @@
<div class="create-account__password-info"> <div class="create-account__password-info">
<div class="create-account__password-tip-validation"> <div class="create-account__password-tip-validation">
{{#if {{#if this.showPasswordValidation}}
(or
this.passwordValidation.ok
(and
this.passwordValidationVisible
this.passwordValidation.reason
)
)
}}
<InputTip <InputTip
@validation={{this.passwordValidation}} @validation={{this.passwordValidation}}
id="password-validation" id="password-validation"
/> />
{{else if this.passwordInstructions}}
<span class="more-info" id="password-validation-more-info">
{{this.passwordInstructions}}
</span>
{{/if}} {{/if}}
<div <div
class={{concat-class class={{concat-class

View File

@ -142,6 +142,58 @@ export default class CreateAccount extends Component.extend(
return this.siteSettings.full_name_required; return this.siteSettings.full_name_required;
} }
@discourseComputed(
"emailValidation.ok",
"emailValidation.reason",
"emailValidationVisible"
)
showEmailValidation(
emailValidationOk,
emailValidationReason,
emailValidationVisible
) {
return (
emailValidationOk || (emailValidationReason && emailValidationVisible)
);
}
@discourseComputed
emailInstructions() {
return (
this.siteSettings.signup_form_email_instructions ||
i18n("user.email.instructions")
);
}
@discourseComputed(
"passwordValidation.ok",
"passwordValidation.reason",
"passwordValidationVisible"
)
showPasswordValidation(
passwordValidationOk,
passwordValidationReason,
passwordValidationVisible
) {
return (
passwordValidationOk ||
(passwordValidationReason && passwordValidationVisible)
);
}
@discourseComputed
passwordInstructions() {
return this.siteSettings.signup_form_password_instructions;
}
@discourseComputed("usernameValidation.reason")
showUsernameInstructions(usernameValidationReason) {
return (
this.siteSettings.signup_form_username_instructions &&
!usernameValidationReason
);
}
@discourseComputed("model.authOptions.auth_provider") @discourseComputed("model.authOptions.auth_provider")
passwordRequired(authProvider) { passwordRequired(authProvider) {
return isEmpty(authProvider); return isEmpty(authProvider);
@ -221,7 +273,7 @@ export default class CreateAccount extends Component.extend(
} }
@action @action
showPasswordValidation() { togglePasswordValidation() {
this.set( this.set(
"passwordValidationVisible", "passwordValidationVisible",
Boolean(this.passwordValidation.reason) Boolean(this.passwordValidation.reason)

View File

@ -125,6 +125,58 @@ export default class SignupPageController extends Controller.extend(
return this.siteSettings.full_name_required; return this.siteSettings.full_name_required;
} }
@discourseComputed(
"emailValidation.ok",
"emailValidation.reason",
"emailValidationVisible"
)
showEmailValidation(
emailValidationOk,
emailValidationReason,
emailValidationVisible
) {
return (
emailValidationOk || (emailValidationReason && emailValidationVisible)
);
}
@discourseComputed
emailInstructions() {
return (
this.siteSettings.signup_form_email_instructions ||
i18n("user.email.instructions")
);
}
@discourseComputed(
"passwordValidation.ok",
"passwordValidation.reason",
"passwordValidationVisible"
)
showPasswordValidation(
passwordValidationOk,
passwordValidationReason,
passwordValidationVisible
) {
return (
passwordValidationOk ||
(passwordValidationReason && passwordValidationVisible)
);
}
@discourseComputed
passwordInstructions() {
return this.siteSettings.signup_form_password_instructions;
}
@discourseComputed("usernameValidation.reason")
showUsernameInstructions(usernameValidationReason) {
return (
this.siteSettings.signup_form_username_instructions &&
!usernameValidationReason
);
}
@discourseComputed("authOptions.auth_provider") @discourseComputed("authOptions.auth_provider")
passwordRequired(authProvider) { passwordRequired(authProvider) {
return isEmpty(authProvider); return isEmpty(authProvider);
@ -204,7 +256,7 @@ export default class SignupPageController extends Controller.extend(
} }
@action @action
showPasswordValidation() { togglePasswordValidation() {
if (this.passwordValidation.reason) { if (this.passwordValidation.reason) {
this.set("passwordValidationVisible", true); this.set("passwordValidationVisible", true);
} else { } else {

View File

@ -51,19 +51,14 @@
<label class="alt-placeholder" for="new-account-email"> <label class="alt-placeholder" for="new-account-email">
{{i18n "user.email.title"}} {{i18n "user.email.title"}}
</label> </label>
{{#if {{#if this.showEmailValidation}}
(or
this.emailValidation.ok
(and this.emailValidationVisible this.emailValidation.reason)
)
}}
<InputTip <InputTip
@validation={{this.emailValidation}} @validation={{this.emailValidation}}
id="account-email-validation" id="account-email-validation"
/> />
{{else}} {{else}}
<span class="more-info" id="account-email-validation-more-info"> <span class="more-info" id="account-email-validation-more-info">
{{i18n "user.email.instructions"}} {{this.emailInstructions}}
</span> </span>
{{/if}} {{/if}}
</div> </div>
@ -85,10 +80,17 @@
{{i18n "user.username.title"}} {{i18n "user.username.title"}}
</label> </label>
<InputTip {{#if this.showUsernameInstructions}}
@validation={{this.usernameValidation}} <span class="more-info" id="username-validation-more-info">
id="username-validation" {{this.siteSettings.signup_form_username_instructions}}
/> </span>
{{else}}
<InputTip
@validation={{this.usernameValidation}}
id="username-validation"
/>
{{/if}}
</div> </div>
{{#if this.fullnameRequired}} {{#if this.fullnameRequired}}
@ -116,7 +118,7 @@
<div class="input-group create-account__password"> <div class="input-group create-account__password">
{{#if this.passwordRequired}} {{#if this.passwordRequired}}
<PasswordField <PasswordField
{{on "focusout" this.showPasswordValidation}} {{on "focusout" this.togglePasswordValidation}}
{{on "focusin" this.scrollInputIntoView}} {{on "focusin" this.scrollInputIntoView}}
@value={{this.accountPassword}} @value={{this.accountPassword}}
@capsLockOn={{this.capsLockOn}} @capsLockOn={{this.capsLockOn}}
@ -133,19 +135,15 @@
<div class="create-account__password-info"> <div class="create-account__password-info">
<div class="create-account__password-tip-validation"> <div class="create-account__password-tip-validation">
{{#if {{#if this.showPasswordValidation}}
(or
this.passwordValidation.ok
(and
this.passwordValidationVisible
this.passwordValidation.reason
)
)
}}
<InputTip <InputTip
@validation={{this.passwordValidation}} @validation={{this.passwordValidation}}
id="password-validation" id="password-validation"
/> />
{{else if this.passwordInstructions}}
<span class="more-info" id="password-validation-more-info">
{{this.passwordInstructions}}
</span>
{{/if}} {{/if}}
<div <div
class={{concat-class class={{concat-class

View File

@ -66,6 +66,36 @@ acceptance("Create Account", function () {
.hasText(i18n("user.username.required"), "shows signup error"); .hasText(i18n("user.username.required"), "shows signup error");
}); });
test("custom instructions", async function (assert) {
this.siteSettings.signup_form_email_instructions = "email";
this.siteSettings.signup_form_username_instructions = "username";
this.siteSettings.signup_form_password_instructions = "password";
this.siteSettings.signup_form_full_name_instructions = "fullname";
await visit("/");
await click("header .sign-up-button");
assert.dom("#account-email-validation-more-info").hasText("email");
assert.dom("#username-validation-more-info").hasText("username");
assert.dom("#password-validation-more-info").hasText("password");
assert.dom("#fullname-validation-more-info").hasText("fullname");
await fillIn("#new-account-email", "z@z.co");
await fillIn("#new-account-password", "supersecurepassword");
await click(".d-modal__footer .btn-primary");
assert
.dom("#username-validation")
.hasText(i18n("user.username.required"), "shows signup error");
// only shows the instructions if the validation is not visible
assert.dom("#account-email-validation-more-info").doesNotExist();
assert.dom("#username-validation-more-info").doesNotExist();
assert.dom("#password-validation-more-info").doesNotExist();
assert.dom("#fullname-validation-more-info").exists();
});
test("can sign in using a third-party auth", async function (assert) { test("can sign in using a third-party auth", async function (assert) {
sinon.stub(LoginMethod, "buildPostForm").callsFake((url) => { sinon.stub(LoginMethod, "buildPostForm").callsFake((url) => {
assert.step("buildPostForm"); assert.step("buildPostForm");

View File

@ -248,6 +248,9 @@ body.signup-page {
.create-account__password-info { .create-account__password-info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.create-account__password-tip-validation {
display: flex;
}
} }
.inline-spinner { .inline-spinner {

View File

@ -242,6 +242,9 @@
.create-account__password-info { .create-account__password-info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.create-account__password-tip-validation {
display: flex;
}
} }
} }

View File

@ -1885,6 +1885,10 @@ en:
persistent_sessions: "Users will remain logged in when the web browser is closed" persistent_sessions: "Users will remain logged in when the web browser is closed"
maximum_session_age: "User will remain logged in for n hours since last visit" maximum_session_age: "User will remain logged in for n hours since last visit"
full_page_login: "Show the login and signup forms in a full page (when unchecked, users will see the forms in a modal). " full_page_login: "Show the login and signup forms in a full page (when unchecked, users will see the forms in a modal). "
signup_form_email_instructions: Email instructions to show on the signup form (overrides the default field description).
signup_form_username_instructions: Username instructions to show on the signup form.
signup_form_full_name_instructions: Full name instructions to show on the signup form.
signup_form_password_instructions: Password instructions to show on the signup form.
ga_version: "Version of Google Universal Analytics to use: v3 (analytics.js), v4 (gtag)" ga_version: "Version of Google Universal Analytics to use: v3 (analytics.js), v4 (gtag)"
ga_universal_tracking_code: "Google Universal Analytics tracking code ID, eg: UA-12345678-9; see <a href='https://google.com/analytics' target='_blank'>https://google.com/analytics</a>" ga_universal_tracking_code: "Google Universal Analytics tracking code ID, eg: UA-12345678-9; see <a href='https://google.com/analytics' target='_blank'>https://google.com/analytics</a>"
ga_universal_domain_name: "Google Universal Analytics domain name, eg: mysite.com; see <a href='https://google.com/analytics' target='_blank'>https://google.com/analytics</a>" ga_universal_domain_name: "Google Universal Analytics domain name, eg: mysite.com; see <a href='https://google.com/analytics' target='_blank'>https://google.com/analytics</a>"

View File

@ -631,6 +631,18 @@ login:
full_page_login: full_page_login:
default: false default: false
client: true client: true
signup_form_email_instructions:
client: true
default: ""
signup_form_username_instructions:
client: true
default: ""
signup_form_full_name_instructions:
client: true
default: ""
signup_form_password_instructions:
client: true
default: ""
users: users:
min_username_length: min_username_length: