mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 09:42:07 +08:00
UX: Add simple-list setting type (#9970)
This commit is contained in:
parent
76af25f753
commit
2d880b42a3
57
app/assets/javascripts/admin/components/simple-list.js
Normal file
57
app/assets/javascripts/admin/components/simple-list.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { empty } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { on } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [":simple-list", ":value-list"],
|
||||
inputEmpty: empty("newValue"),
|
||||
inputDelimiter: null,
|
||||
newValue: "",
|
||||
collection: null,
|
||||
values: null,
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setupCollection() {
|
||||
this.set("collection", this._splitValues(this.values, this.inputDelimiter));
|
||||
},
|
||||
|
||||
keyDown(event) {
|
||||
if (event.which === 13) {
|
||||
this.addValue(this.newValue);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
changeValue(index, newValue) {
|
||||
this.collection.replace(index, 1, [newValue]);
|
||||
this.collection.arrayContentDidChange(index);
|
||||
this._onChange();
|
||||
},
|
||||
|
||||
@action
|
||||
addValue(newValue) {
|
||||
if (this.inputEmpty) return;
|
||||
|
||||
this.set("newValue", null);
|
||||
this.collection.addObject(newValue);
|
||||
this._onChange();
|
||||
},
|
||||
|
||||
@action
|
||||
removeValue(value) {
|
||||
this.collection.removeObject(value);
|
||||
this._onChange();
|
||||
},
|
||||
|
||||
_onChange() {
|
||||
this.attrs.onChange && this.attrs.onChange(this.collection);
|
||||
},
|
||||
|
||||
_splitValues(values, delimiter) {
|
||||
return values && values.length
|
||||
? values.split(delimiter || "\n").filter(Boolean)
|
||||
: [];
|
||||
}
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default Component.extend({
|
||||
inputDelimiter: "|",
|
||||
|
||||
@action
|
||||
onChange(value) {
|
||||
this.set("value", value.join(this.inputDelimiter || "\n"));
|
||||
}
|
||||
});
|
|
@ -25,7 +25,8 @@ const CUSTOM_TYPES = [
|
|||
"upload",
|
||||
"group_list",
|
||||
"tag_list",
|
||||
"color"
|
||||
"color",
|
||||
"simple_list"
|
||||
];
|
||||
|
||||
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
{{#if collection}}
|
||||
<div class="values">
|
||||
{{#each collection as |value index|}}
|
||||
<div data-index={{index}} class="value">
|
||||
{{d-button
|
||||
action=(action "removeValue")
|
||||
actionParam=value
|
||||
icon="times"
|
||||
class="remove-value-btn btn-small"
|
||||
}}
|
||||
|
||||
{{input
|
||||
title=value
|
||||
value=value
|
||||
class="value-input"
|
||||
focus-out=(action "changeValue" index)
|
||||
}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="simple-list-input">
|
||||
{{input
|
||||
type="text"
|
||||
value=newValue
|
||||
placeholderKey="admin.site_settings.simple_list.add_item"
|
||||
class="add-value-input"
|
||||
autocomplete="discourse"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"}}
|
||||
|
||||
{{d-button
|
||||
action=(action "addValue")
|
||||
actionParam=newValue
|
||||
disabled=inputEmpty
|
||||
icon="plus"
|
||||
class="add-value-btn btn-small"
|
||||
}}
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
{{simple-list values=value inputDelimiter=inputDelimiter onChange=(action "onChange")}}
|
||||
{{setting-validation-message message=validationMessage}}
|
||||
<div class="desc">{{html-safe setting.description}}</div>
|
|
@ -931,6 +931,20 @@ table#user-badges {
|
|||
}
|
||||
}
|
||||
|
||||
.simple-list-input {
|
||||
display: flex;
|
||||
|
||||
.add-value-input {
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
flex: 1 0 0px;
|
||||
}
|
||||
|
||||
.add-value-btn {
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile view text-inputs need some padding
|
||||
.mobile-view .admin-contents {
|
||||
input[type="text"] {
|
||||
|
|
|
@ -4588,6 +4588,8 @@ en:
|
|||
modal_description: "Would you like to apply this change historically? This will change preferences for %{count} existing users."
|
||||
modal_yes: "Yes"
|
||||
modal_no: "No, only apply change going forward"
|
||||
simple_list:
|
||||
add_item: "Add item..."
|
||||
|
||||
badges:
|
||||
title: Badges
|
||||
|
|
|
@ -1445,7 +1445,7 @@ security:
|
|||
content_security_policy_collect_reports:
|
||||
default: false
|
||||
content_security_policy_script_src:
|
||||
type: list
|
||||
type: simple_list
|
||||
default: ""
|
||||
invalidate_inactive_admin_email_after_days:
|
||||
default: 365
|
||||
|
|
|
@ -34,7 +34,8 @@ class SiteSettings::TypeSupervisor
|
|||
group: 19,
|
||||
group_list: 20,
|
||||
tag_list: 21,
|
||||
color: 22
|
||||
color: 22,
|
||||
simple_list: 23
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -88,6 +88,9 @@ describe SiteSettings::TypeSupervisor do
|
|||
it "'color' should be at the right position" do
|
||||
expect(SiteSettings::TypeSupervisor.types[:color]).to eq(22)
|
||||
end
|
||||
it "'simple_list' should be at the right position" do
|
||||
expect(SiteSettings::TypeSupervisor.types[:simple_list]).to eq(23)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
84
test/javascripts/components/simple-list-test.js
Normal file
84
test/javascripts/components/simple-list-test.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
import componentTest from "helpers/component-test";
|
||||
moduleForComponent("simple-list", { integration: true });
|
||||
|
||||
componentTest("adding a value", {
|
||||
template: "{{simple-list values=values}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("values", "vinkas\nosama");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert.ok(
|
||||
find(".add-value-btn[disabled]").length,
|
||||
"while loading the + button is disabled"
|
||||
);
|
||||
|
||||
await fillIn(".add-value-input", "penar");
|
||||
await click(".add-value-btn");
|
||||
|
||||
assert.ok(
|
||||
find(".values .value").length === 3,
|
||||
"it adds the value to the list of values"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
find(".values .value[data-index='2'] .value-input")[0].value === "penar",
|
||||
"it sets the correct value for added item"
|
||||
);
|
||||
|
||||
await fillIn(".add-value-input", "eviltrout");
|
||||
await keyEvent(".add-value-input", "keydown", 13); // enter
|
||||
|
||||
assert.ok(
|
||||
find(".values .value").length === 4,
|
||||
"it adds the value when keying Enter"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("removing a value", {
|
||||
template: "{{simple-list values=values}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("values", "vinkas\nosama");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await click(".values .value[data-index='0'] .remove-value-btn");
|
||||
|
||||
assert.ok(
|
||||
find(".values .value").length === 1,
|
||||
"it removes the value from the list of values"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
find(".values .value[data-index='0'] .value-input")[0].value === "osama",
|
||||
"it removes the correct value"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
componentTest("delimiter support", {
|
||||
template: "{{simple-list values=values inputDelimiter='|'}}",
|
||||
|
||||
beforeEach() {
|
||||
this.set("values", "vinkas|osama");
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
await fillIn(".add-value-input", "eviltrout");
|
||||
await click(".add-value-btn");
|
||||
|
||||
assert.ok(
|
||||
find(".values .value").length === 3,
|
||||
"it adds the value to the list of values"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
find(".values .value[data-index='2'] .value-input")[0].value ===
|
||||
"eviltrout",
|
||||
"it adds the correct value"
|
||||
);
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue
Block a user