DEV: Convert admin user fields to FormKit (#29070)
This change replaces the admin form for adding and editing custom user fields with a new FormKit implementation.
This commit is contained in:
parent
ede06ffd43
commit
408de686bb
|
@ -1,134 +1,163 @@
|
|||
<div class="user-field">
|
||||
{{#if (or this.isEditing (not this.userField.id))}}
|
||||
<AdminFormRow @label="admin.user_fields.type">
|
||||
<ComboBox
|
||||
@content={{this.fieldTypes}}
|
||||
@value={{this.buffered.field_type}}
|
||||
@onChange={{fn (mut this.buffered.field_type)}}
|
||||
/>
|
||||
</AdminFormRow>
|
||||
{{#if (or this.isEditing (not @userField.id))}}
|
||||
<div class="admin-config-area user-field">
|
||||
<div class="admin-config-area__primary-content">
|
||||
<div class="admin-config-area-card">
|
||||
<Form
|
||||
@data={{this.formData}}
|
||||
@onSubmit={{this.save}}
|
||||
{{did-insert this._focusName}}
|
||||
as |form transientData|
|
||||
>
|
||||
<form.Field
|
||||
@name="field_type"
|
||||
@title={{i18n "admin.user_fields.type"}}
|
||||
@format="large"
|
||||
@validation="required"
|
||||
as |field|
|
||||
>
|
||||
<field.Select as |select|>
|
||||
{{#each @fieldTypes as |fieldType|}}
|
||||
<select.Option
|
||||
@value={{fieldType.id}}
|
||||
>{{fieldType.name}}</select.Option>
|
||||
{{/each}}
|
||||
</field.Select>
|
||||
</form.Field>
|
||||
|
||||
<AdminFormRow @label="admin.user_fields.name">
|
||||
<Input
|
||||
@value={{this.buffered.name}}
|
||||
class="user-field-name"
|
||||
maxlength="255"
|
||||
/>
|
||||
</AdminFormRow>
|
||||
<form.Field
|
||||
@name="name"
|
||||
@title={{i18n "admin.user_fields.name"}}
|
||||
@format="large"
|
||||
@validation="required"
|
||||
as |field|
|
||||
>
|
||||
<field.Input class="user-field-name" maxlength="255" />
|
||||
</form.Field>
|
||||
|
||||
<AdminFormRow @label="admin.user_fields.description">
|
||||
<Input
|
||||
@value={{this.buffered.description}}
|
||||
class="user-field-desc"
|
||||
maxlength="1000"
|
||||
/>
|
||||
</AdminFormRow>
|
||||
<form.Field
|
||||
@name="description"
|
||||
@title={{i18n "admin.user_fields.description"}}
|
||||
@format="large"
|
||||
@validation="required"
|
||||
as |field|
|
||||
>
|
||||
<field.Input class="user-field-desc" maxlength="1000" />
|
||||
</form.Field>
|
||||
|
||||
{{#if this.bufferedFieldType.hasOptions}}
|
||||
<AdminFormRow @label="admin.user_fields.options">
|
||||
<ValueList @values={{this.buffered.options}} @inputType="array" />
|
||||
</AdminFormRow>
|
||||
{{/if}}
|
||||
{{#if
|
||||
(or
|
||||
(eq transientData.field_type "dropdown")
|
||||
(eq transientData.field_type "multiselect")
|
||||
)
|
||||
}}
|
||||
<form.Field
|
||||
@name="options"
|
||||
@title={{i18n "admin.user_fields.options"}}
|
||||
@format="large"
|
||||
@validation="required"
|
||||
as |field|
|
||||
>
|
||||
<field.Custom>
|
||||
<ValueList
|
||||
@values={{transientData.options}}
|
||||
@inputType="array"
|
||||
@onChange={{field.set}}
|
||||
/>
|
||||
</field.Custom>
|
||||
</form.Field>
|
||||
{{/if}}
|
||||
|
||||
<AdminFormRow @label="admin.user_fields.requirement.title">
|
||||
<label class="optional">
|
||||
<RadioButton
|
||||
@value="optional"
|
||||
@name="requirement"
|
||||
@selection={{this.buffered.requirement}}
|
||||
@onChange={{action "changeRequirementType"}}
|
||||
/>
|
||||
<span>{{i18n "admin.user_fields.requirement.optional.title"}}</span>
|
||||
</label>
|
||||
<form.Field
|
||||
@name="requirement"
|
||||
@title={{i18n "admin.user_fields.requirement.title"}}
|
||||
@validation="required"
|
||||
@onSet={{this.setRequirement}}
|
||||
@format="full"
|
||||
as |field|
|
||||
>
|
||||
<field.RadioGroup as |radioGroup|>
|
||||
<radioGroup.Radio @value="optional">
|
||||
{{i18n "admin.user_fields.requirement.optional.title"}}
|
||||
</radioGroup.Radio>
|
||||
<radioGroup.Radio @value="for_all_users" as |radio|>
|
||||
{{i18n "admin.user_fields.requirement.for_all_users.title"}}
|
||||
<radio.Description>{{i18n
|
||||
"admin.user_fields.requirement.for_all_users.description"
|
||||
}}</radio.Description>
|
||||
</radioGroup.Radio>
|
||||
<radioGroup.Radio @value="on_signup" as |radio|>
|
||||
{{i18n "admin.user_fields.requirement.on_signup.title"}}
|
||||
<radio.Description>{{i18n
|
||||
"admin.user_fields.requirement.on_signup.description"
|
||||
}}</radio.Description>
|
||||
</radioGroup.Radio>
|
||||
</field.RadioGroup>
|
||||
</form.Field>
|
||||
|
||||
<label class="for_all_users">
|
||||
<RadioButton
|
||||
@value="for_all_users"
|
||||
@name="requirement"
|
||||
@selection={{this.buffered.requirement}}
|
||||
@onChange={{action "changeRequirementType"}}
|
||||
/>
|
||||
<div class="label-text">
|
||||
<span>{{i18n
|
||||
"admin.user_fields.requirement.for_all_users.title"
|
||||
}}</span>
|
||||
<div class="description">{{i18n
|
||||
"admin.user_fields.requirement.for_all_users.description"
|
||||
}}</div>
|
||||
</div>
|
||||
</label>
|
||||
<form.CheckboxGroup
|
||||
class="user-field-preferences"
|
||||
@title={{i18n "admin.user_fields.preferences"}}
|
||||
as |group|
|
||||
>
|
||||
<group.Field
|
||||
@name="editable"
|
||||
@showTitle={{false}}
|
||||
@title={{i18n "admin.user_fields.editable.title"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Checkbox disabled={{this.editableDisabled}} />
|
||||
</group.Field>
|
||||
<group.Field
|
||||
@name="show_on_profile"
|
||||
@showTitle={{false}}
|
||||
@title={{i18n "admin.user_fields.show_on_profile.title"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Checkbox />
|
||||
</group.Field>
|
||||
<group.Field
|
||||
@name="show_on_user_card"
|
||||
@showTitle={{false}}
|
||||
@title={{i18n "admin.user_fields.show_on_user_card.title"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Checkbox />
|
||||
</group.Field>
|
||||
<group.Field
|
||||
@name="searchable"
|
||||
@showTitle={{false}}
|
||||
@title={{i18n "admin.user_fields.searchable.title"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Checkbox />
|
||||
</group.Field>
|
||||
</form.CheckboxGroup>
|
||||
|
||||
<label class="on_signup">
|
||||
<RadioButton
|
||||
@value="on_signup"
|
||||
@name="requirement"
|
||||
@selection={{this.buffered.requirement}}
|
||||
@onChange={{action "changeRequirementType"}}
|
||||
/>
|
||||
<div class="label-text">
|
||||
<span>{{i18n "admin.user_fields.requirement.on_signup.title"}}</span>
|
||||
<div class="description">{{i18n
|
||||
"admin.user_fields.requirement.on_signup.description"
|
||||
}}</div>
|
||||
</div>
|
||||
</label>
|
||||
</AdminFormRow>
|
||||
|
||||
<AdminFormRow @label="admin.user_fields.preferences">
|
||||
<label>
|
||||
<Input
|
||||
@type="checkbox"
|
||||
@checked={{this.buffered.editable}}
|
||||
disabled={{this.editableDisabled}}
|
||||
/>
|
||||
<span>{{i18n "admin.user_fields.editable.title"}}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<Input @type="checkbox" @checked={{this.buffered.show_on_profile}} />
|
||||
<span>{{i18n "admin.user_fields.show_on_profile.title"}}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<Input @type="checkbox" @checked={{this.buffered.show_on_user_card}} />
|
||||
<span>{{i18n "admin.user_fields.show_on_user_card.title"}}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<Input @type="checkbox" @checked={{this.buffered.searchable}} />
|
||||
<span>{{i18n "admin.user_fields.searchable.title"}}</span>
|
||||
</label>
|
||||
</AdminFormRow>
|
||||
|
||||
<PluginOutlet
|
||||
@name="after-admin-user-fields"
|
||||
@outletArgs={{hash buffered=this.buffered}}
|
||||
/>
|
||||
|
||||
<AdminFormRow>
|
||||
<DButton
|
||||
@action={{this.save}}
|
||||
@icon="check"
|
||||
@label="admin.user_fields.save"
|
||||
class="btn-primary save"
|
||||
/>
|
||||
<DButton
|
||||
@action={{this.cancel}}
|
||||
@icon="xmark"
|
||||
@label="admin.user_fields.cancel"
|
||||
class="btn-danger cancel"
|
||||
/>
|
||||
</AdminFormRow>
|
||||
{{else}}
|
||||
<form.Actions>
|
||||
<form.Submit
|
||||
class="save"
|
||||
@icon="check"
|
||||
@label="admin.user_fields.save"
|
||||
/>
|
||||
<form.Button
|
||||
@action={{this.cancel}}
|
||||
@label="admin.user_fields.cancel"
|
||||
class="btn-default"
|
||||
/>
|
||||
</form.Actions>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="user-field">
|
||||
<div class="row">
|
||||
<div class="form-display">
|
||||
<b class="name">{{this.userField.name}}</b>
|
||||
<b class="name">{{@userField.name}}</b>
|
||||
<br />
|
||||
<span class="description">{{html-safe
|
||||
this.userField.description
|
||||
}}</span>
|
||||
<span class="description">{{html-safe @userField.description}}</span>
|
||||
</div>
|
||||
<div class="form-display field-type">{{this.fieldName}}</div>
|
||||
<div class="form-display field-type">{{@userField.fieldTypeName}}</div>
|
||||
<div class="form-element controls">
|
||||
<DButton
|
||||
@action={{this.edit}}
|
||||
|
@ -137,19 +166,19 @@
|
|||
class="btn-default edit"
|
||||
/>
|
||||
<DButton
|
||||
@action={{fn this.destroyAction this.userField}}
|
||||
@action={{fn @destroyAction @userField}}
|
||||
@icon="trash-can"
|
||||
@label="admin.user_fields.delete"
|
||||
class="btn-danger cancel"
|
||||
/>
|
||||
<DButton
|
||||
@action={{fn this.moveUpAction this.userField}}
|
||||
@action={{fn @moveUpAction @userField}}
|
||||
@icon="arrow-up"
|
||||
@disabled={{this.cantMoveUp}}
|
||||
class="btn-default"
|
||||
/>
|
||||
<DButton
|
||||
@action={{fn this.moveDownAction this.userField}}
|
||||
@action={{fn @moveDownAction @userField}}
|
||||
@icon="arrow-down"
|
||||
@disabled={{this.cantMoveDown}}
|
||||
class="btn-default"
|
||||
|
@ -157,5 +186,5 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="row user-field-flags">{{this.flags}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1,4 +1,5 @@
|
|||
import Component from "@ember/component";
|
||||
import Component from "@glimmer/component";
|
||||
import { cached, tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import { service } from "@ember/service";
|
||||
|
@ -6,78 +7,102 @@ import { isEmpty } from "@ember/utils";
|
|||
import { tagName } from "@ember-decorators/component";
|
||||
import { Promise } from "rsvp";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { i18n, propertyEqual } from "discourse/lib/computed";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
import UserField from "admin/models/user-field";
|
||||
|
||||
@tagName("")
|
||||
export default class AdminUserFieldItem extends Component.extend(
|
||||
bufferedProperty("userField")
|
||||
) {
|
||||
export default class AdminUserFieldItem extends Component {
|
||||
@service adminCustomUserFields;
|
||||
@service dialog;
|
||||
|
||||
isEditing = false;
|
||||
@tracked isEditing = false;
|
||||
@tracked
|
||||
editableDisabled = this.args.userField.requirement === "for_all_users";
|
||||
|
||||
@propertyEqual("userField", "firstField") cantMoveUp;
|
||||
@propertyEqual("userField", "lastField") cantMoveDown;
|
||||
|
||||
@i18n("admin.user_fields.description") userFieldsDescription;
|
||||
|
||||
@discourseComputed("buffered.field_type")
|
||||
bufferedFieldType(fieldType) {
|
||||
return UserField.fieldTypeById(fieldType);
|
||||
get fieldName() {
|
||||
return UserField.fieldTypeById(this.fieldType)?.name;
|
||||
}
|
||||
|
||||
didInsertElement() {
|
||||
super.didInsertElement(...arguments);
|
||||
|
||||
this._focusName();
|
||||
get cantMoveUp() {
|
||||
return this.args.userField.id === this.args.firstField?.id;
|
||||
}
|
||||
|
||||
_focusName() {
|
||||
schedule("afterRender", () => {
|
||||
document.querySelector(".user-field-name")?.focus();
|
||||
});
|
||||
get cantMoveDown() {
|
||||
return this.args.userField.id === this.args.lastField?.id;
|
||||
}
|
||||
|
||||
@discourseComputed("userField.field_type")
|
||||
fieldName(fieldType) {
|
||||
return UserField.fieldTypeById(fieldType)?.name;
|
||||
get isNewRecord() {
|
||||
return isEmpty(this.args.userField?.id);
|
||||
}
|
||||
|
||||
@discourseComputed(
|
||||
"userField.{editable,show_on_profile,show_on_user_card,searchable}"
|
||||
)
|
||||
flags(userField) {
|
||||
const ret = [];
|
||||
if (userField.editable) {
|
||||
ret.push(I18n.t("admin.user_fields.editable.enabled"));
|
||||
}
|
||||
if (userField.show_on_profile) {
|
||||
ret.push(I18n.t("admin.user_fields.show_on_profile.enabled"));
|
||||
}
|
||||
if (userField.show_on_user_card) {
|
||||
ret.push(I18n.t("admin.user_fields.show_on_user_card.enabled"));
|
||||
}
|
||||
if (userField.searchable) {
|
||||
ret.push(I18n.t("admin.user_fields.searchable.enabled"));
|
||||
}
|
||||
get flags() {
|
||||
const flags = [
|
||||
"editable",
|
||||
"show_on_profile",
|
||||
"show_on_user_card",
|
||||
"searchable",
|
||||
];
|
||||
|
||||
return ret.join(", ");
|
||||
return flags
|
||||
.map((flag) => {
|
||||
if (this.args.userField[flag]) {
|
||||
return I18n.t(`admin.user_fields.${flag}.enabled`);
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
@discourseComputed("buffered.requirement")
|
||||
editableDisabled(requirement) {
|
||||
return requirement === "for_all_users";
|
||||
@cached
|
||||
get formData() {
|
||||
return this.args.userField.getProperties(
|
||||
"field_type",
|
||||
"name",
|
||||
"description",
|
||||
"requirement",
|
||||
"editable",
|
||||
"show_on_profile",
|
||||
"show_on_user_card",
|
||||
"searchable",
|
||||
"options",
|
||||
...this.adminCustomUserFields.additionalProperties
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
changeRequirementType(requirement) {
|
||||
this.buffered.set("requirement", requirement);
|
||||
this.buffered.set("editable", requirement === "for_all_users");
|
||||
setRequirement(value, { set }) {
|
||||
set("requirement", value);
|
||||
|
||||
if (value === "for_all_users") {
|
||||
this.editableDisabled = true;
|
||||
set("editable", true);
|
||||
} else {
|
||||
this.editableDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async save(data) {
|
||||
let confirm = true;
|
||||
|
||||
if (data.requirement === "for_all_users") {
|
||||
confirm = await this._confirmChanges();
|
||||
}
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.args.userField
|
||||
.save(data)
|
||||
.then(() => {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isEditing = false;
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
|
||||
async _confirmChanges() {
|
||||
|
@ -90,57 +115,23 @@ export default class AdminUserFieldItem extends Component.extend(
|
|||
});
|
||||
}
|
||||
|
||||
@action
|
||||
async save() {
|
||||
const attrs = this.buffered.getProperties(
|
||||
"name",
|
||||
"description",
|
||||
"field_type",
|
||||
"editable",
|
||||
"requirement",
|
||||
"show_on_profile",
|
||||
"show_on_user_card",
|
||||
"searchable",
|
||||
"options",
|
||||
...this.adminCustomUserFields.additionalProperties
|
||||
);
|
||||
|
||||
let confirm = true;
|
||||
|
||||
if (attrs.requirement === "for_all_users") {
|
||||
confirm = await this._confirmChanges();
|
||||
}
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.userField
|
||||
.save(attrs)
|
||||
.then(() => {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("isEditing", false);
|
||||
this.commitBuffer();
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
|
||||
@action
|
||||
edit() {
|
||||
this.set("isEditing", true);
|
||||
this._focusName();
|
||||
this.isEditing = true;
|
||||
}
|
||||
|
||||
@action
|
||||
cancel() {
|
||||
if (isEmpty(this.userField?.id)) {
|
||||
this.destroyAction(this.userField);
|
||||
if (this.isNewRecord) {
|
||||
this.args.destroyAction(this.args.userField);
|
||||
} else {
|
||||
this.rollbackBuffer();
|
||||
this.set("isEditing", false);
|
||||
this.isEditing = false;
|
||||
}
|
||||
}
|
||||
|
||||
_focusName() {
|
||||
schedule("afterRender", () =>
|
||||
document.querySelector(".user-field-name")?.focus()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ export default class ValueList extends Component {
|
|||
newValue = "";
|
||||
collection = null;
|
||||
values = null;
|
||||
onChange = null;
|
||||
|
||||
@reads("addKey") noneKey;
|
||||
|
||||
|
@ -21,7 +22,7 @@ export default class ValueList extends Component {
|
|||
super.didReceiveAttrs(...arguments);
|
||||
|
||||
if (this.inputType === "array") {
|
||||
this.set("collection", this.values || []);
|
||||
this.set("collection", this.values ? [...this.values] : []);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -114,6 +115,11 @@ export default class ValueList extends Component {
|
|||
}
|
||||
|
||||
_saveValues() {
|
||||
if (this.onChange) {
|
||||
this.onChange([...this.collection]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.inputType === "array") {
|
||||
this.set("values", this.collection);
|
||||
return;
|
||||
|
|
|
@ -28,6 +28,7 @@ export default class AdminUserFieldsController extends Controller {
|
|||
createField() {
|
||||
const f = this.store.createRecord("user-field", {
|
||||
field_type: "text",
|
||||
requirement: "optional",
|
||||
position: MAX_FIELDS,
|
||||
});
|
||||
this.model.pushObject(f);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import EmberObject from "@ember/object";
|
||||
import { i18n } from "discourse/lib/computed";
|
||||
import RestModel from "discourse/models/rest";
|
||||
|
@ -19,6 +20,16 @@ export default class UserField extends RestModel {
|
|||
static fieldTypeById(id) {
|
||||
return this.fieldTypes().findBy("id", id);
|
||||
}
|
||||
|
||||
@tracked field_type;
|
||||
@tracked editable;
|
||||
@tracked show_on_profile;
|
||||
@tracked show_on_user_card;
|
||||
@tracked searchable;
|
||||
|
||||
get fieldTypeName() {
|
||||
return UserField.fieldTypes().find((ft) => ft.id === this.field_type).name;
|
||||
}
|
||||
}
|
||||
|
||||
class UserFieldType extends EmberObject {
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
<div class="user-fields">
|
||||
<h2>{{i18n "admin.user_fields.title"}}</h2>
|
||||
<div class="admin-config-page__main-area">
|
||||
<div class="user-fields">
|
||||
<h2>{{i18n "admin.user_fields.title"}}</h2>
|
||||
|
||||
<p class="desc">{{i18n "admin.user_fields.help"}}</p>
|
||||
<p class="desc">{{i18n "admin.user_fields.help"}}</p>
|
||||
|
||||
{{#if this.model}}
|
||||
{{#each this.sortedFields as |uf|}}
|
||||
<AdminUserFieldItem
|
||||
@userField={{uf}}
|
||||
@fieldTypes={{this.fieldTypes}}
|
||||
@firstField={{this.firstField}}
|
||||
@lastField={{this.lastField}}
|
||||
@destroyAction={{this.destroyField}}
|
||||
@moveUpAction={{this.moveUp}}
|
||||
@moveDownAction={{this.moveDown}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#if this.model}}
|
||||
{{#each this.sortedFields as |uf|}}
|
||||
<AdminUserFieldItem
|
||||
@userField={{uf}}
|
||||
@fieldTypes={{this.fieldTypes}}
|
||||
@firstField={{this.firstField}}
|
||||
@lastField={{this.lastField}}
|
||||
@destroyAction={{this.destroyField}}
|
||||
@moveUpAction={{this.moveUp}}
|
||||
@moveDownAction={{this.moveDown}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
<DButton
|
||||
@disabled={{this.createDisabled}}
|
||||
@action={{this.createField}}
|
||||
@label="admin.user_fields.create"
|
||||
@icon="plus"
|
||||
class="btn-primary"
|
||||
/>
|
||||
<DButton
|
||||
@disabled={{this.createDisabled}}
|
||||
@action={{this.createField}}
|
||||
@label="admin.user_fields.create"
|
||||
@icon="plus"
|
||||
class="btn-primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
|
@ -1,95 +0,0 @@
|
|||
import { click, render } from "@ember/test-helpers";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
module("Integration | Component | admin-user-field-item", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("user field without an id", async function (assert) {
|
||||
await render(hbs`<AdminUserFieldItem @userField={{this.userField}} />`);
|
||||
|
||||
assert.ok(exists(".save"), "displays editing mode");
|
||||
});
|
||||
|
||||
test("cancel action", async function (assert) {
|
||||
this.set("userField", { id: 1, field_type: "text" });
|
||||
this.set("isEditing", true);
|
||||
this.set("destroyAction", () => {});
|
||||
this.set("moveUpAction", () => {});
|
||||
this.set("moveDownAction", () => {});
|
||||
|
||||
await render(hbs`
|
||||
<AdminUserFieldItem
|
||||
@isEditing={{this.isEditing}}
|
||||
@destroyAction={{this.destroyAction}}
|
||||
@moveUpAction={{this.moveUpAction}}
|
||||
@moveDownAction={{this.moveDownAction}}
|
||||
@userField={{this.userField}}
|
||||
/>`);
|
||||
|
||||
await click(".cancel");
|
||||
assert.ok(exists(".edit"));
|
||||
});
|
||||
|
||||
test("edit action", async function (assert) {
|
||||
this.set("userField", { id: 1, field_type: "text" });
|
||||
this.set("destroyAction", () => {});
|
||||
this.set("moveUpAction", () => {});
|
||||
this.set("moveDownAction", () => {});
|
||||
|
||||
await render(hbs`
|
||||
<AdminUserFieldItem
|
||||
@destroyAction={{this.destroyAction}}
|
||||
@moveUpAction={{this.moveUpAction}}
|
||||
@moveDownAction={{this.moveDownAction}}
|
||||
@userField={{this.userField}}
|
||||
/>`);
|
||||
|
||||
await click(".edit");
|
||||
assert.ok(exists(".save"));
|
||||
});
|
||||
|
||||
test("field attributes are rendered correctly", async function (assert) {
|
||||
this.set("userField", {
|
||||
id: 1,
|
||||
field_type: "text",
|
||||
name: "foo",
|
||||
description: "what is foo",
|
||||
show_on_profile: true,
|
||||
show_on_user_card: true,
|
||||
searchable: true,
|
||||
});
|
||||
this.set("destroyAction", () => {});
|
||||
this.set("moveUpAction", () => {});
|
||||
this.set("moveDownAction", () => {});
|
||||
|
||||
await render(hbs`
|
||||
<AdminUserFieldItem
|
||||
@destroyAction={{this.destroyAction}}
|
||||
@moveUpAction={{this.moveUpAction}}
|
||||
@moveDownAction={{this.moveDownAction}}
|
||||
@userField={{this.userField}}
|
||||
/>`);
|
||||
|
||||
assert.strictEqual(query(".name").innerText, this.userField.name);
|
||||
assert.strictEqual(
|
||||
query(".description").innerText,
|
||||
this.userField.description
|
||||
);
|
||||
assert.strictEqual(
|
||||
query(".field-type").innerText,
|
||||
I18n.t("admin.user_fields.field_types.text")
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(".user-field-flags")
|
||||
.hasText(
|
||||
`${I18n.t("admin.user_fields.show_on_profile.enabled")}, ${I18n.t(
|
||||
"admin.user_fields.show_on_user_card.enabled"
|
||||
)}, ${I18n.t("admin.user_fields.searchable.enabled")}`
|
||||
);
|
||||
});
|
||||
});
|
|
@ -956,64 +956,16 @@ table.permalinks {
|
|||
|
||||
.user-fields {
|
||||
h2 {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.user-field {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-block: 0.5em;
|
||||
margin-bottom: 1em;
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
.form-display {
|
||||
width: 25%;
|
||||
float: left;
|
||||
}
|
||||
.form-element,
|
||||
.form-element-desc {
|
||||
float: left;
|
||||
min-height: 30px;
|
||||
padding: 0.25em 0;
|
||||
&.input-area {
|
||||
width: 75%;
|
||||
.value-list,
|
||||
.select-kit,
|
||||
input[type="text"] {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.value-list {
|
||||
.select-kit {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
label {
|
||||
font-weight: normal;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
.label-text {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 0.25em;
|
||||
color: var(--primary-medium);
|
||||
font-size: var(--font-down-1);
|
||||
line-height: var(--line-height-large);
|
||||
}
|
||||
}
|
||||
&.label-area {
|
||||
width: 25%;
|
||||
label {
|
||||
margin: 0.5em 1em 0 0;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
.controls {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
.clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
.form-kit__checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75em;
|
||||
gap: 0em;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.form-kit__control-custom {
|
||||
.value-list {
|
||||
.single-select.combobox {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
@import "_control-select";
|
||||
@import "_control-custom";
|
||||
@import "_control-textarea";
|
||||
@import "_control-custom-value-list";
|
||||
@import "_errors";
|
||||
@import "_errors-summary";
|
||||
@import "_field";
|
||||
|
|
|
@ -23,7 +23,7 @@ describe "Admin User Fields", type: :system, js: true do
|
|||
|
||||
user_fields_page.add_field(name: "Occupation", description: "")
|
||||
|
||||
expect(user_fields_page).to have_text(/Description can't be blank/)
|
||||
expect(user_fields_page.form.field(:description)).to have_errors("Required")
|
||||
end
|
||||
|
||||
it "makes sure new required fields are editable after signup" do
|
||||
|
@ -40,7 +40,7 @@ describe "Admin User Fields", type: :system, js: true do
|
|||
|
||||
user_fields_page.choose_requirement("optional")
|
||||
|
||||
expect(form).to have_field(editable_label, checked: false, disabled: false)
|
||||
expect(form).to have_field(editable_label, checked: true, disabled: false)
|
||||
end
|
||||
|
||||
it "requires confirmation when applying required fields retroactively" do
|
||||
|
|
|
@ -67,6 +67,16 @@ module PageObjects
|
|||
expect(self.value).to eq(expected_value)
|
||||
end
|
||||
|
||||
def has_errors?(*messages)
|
||||
within component do
|
||||
messages.all? { |m| find(".form-kit__errors", text: m) }
|
||||
end
|
||||
end
|
||||
|
||||
def has_no_errors?
|
||||
!has_css?(".form-kit__errors")
|
||||
end
|
||||
|
||||
def control_type
|
||||
component["data-control-type"]
|
||||
end
|
||||
|
|
|
@ -8,6 +8,10 @@ module PageObjects
|
|||
self
|
||||
end
|
||||
|
||||
def form
|
||||
PageObjects::Components::FormKit.new(".user-field .form-kit")
|
||||
end
|
||||
|
||||
def choose_requirement(requirement)
|
||||
form = page.find(".user-field")
|
||||
|
||||
|
|
Loading…
Reference in New Issue