mirror of
https://github.com/discourse/discourse.git
synced 2025-05-23 19:54:14 +08:00
FEATURE: introduces list/compact_list components
This commit is contained in:
@ -1,113 +1,101 @@
|
|||||||
|
import { on } from "ember-addons/ember-computed-decorators";
|
||||||
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNameBindings: [":value-list"],
|
classNameBindings: [":value-list"],
|
||||||
|
|
||||||
_enableSorting: function() {
|
inputInvalid: Ember.computed.empty("newValue"),
|
||||||
const self = this;
|
|
||||||
const placeholder = document.createElement("div");
|
|
||||||
placeholder.className = "placeholder";
|
|
||||||
|
|
||||||
let dragging = null;
|
inputDelimiter: null,
|
||||||
let over = null;
|
inputType: null,
|
||||||
let nodePlacement;
|
newValue: "",
|
||||||
|
collection: null,
|
||||||
|
values: null,
|
||||||
|
|
||||||
this.$().on("dragstart.discourse", ".values .value", function(e) {
|
@computed("addKey", "filteredChoices.length")
|
||||||
dragging = e.currentTarget;
|
noneKey(addKey, filteredChoicesLength) {
|
||||||
e.dataTransfer.effectAllowed = "move";
|
return addKey || filteredChoicesLength === 0
|
||||||
e.dataTransfer.setData("text/html", e.currentTarget);
|
? "admin.site_settings.value_list.no_choices_none"
|
||||||
});
|
: "admin.site_settings.value_list.default_none";
|
||||||
|
},
|
||||||
|
|
||||||
this.$().on("dragend.discourse", ".values .value", function() {
|
@on("didReceiveAttrs")
|
||||||
Ember.run(function() {
|
_setupCollection() {
|
||||||
dragging.parentNode.removeChild(placeholder);
|
|
||||||
dragging.style.display = "block";
|
|
||||||
|
|
||||||
// Update data
|
|
||||||
const from = Number(dragging.dataset.index);
|
|
||||||
let to = Number(over.dataset.index);
|
|
||||||
if (from < to) to--;
|
|
||||||
if (nodePlacement === "after") to++;
|
|
||||||
|
|
||||||
const collection = self.get("collection");
|
|
||||||
const fromObj = collection.objectAt(from);
|
|
||||||
collection.replace(from, 1);
|
|
||||||
collection.replace(to, 0, [fromObj]);
|
|
||||||
self._saveValues();
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$().on("dragover.discourse", ".values", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
dragging.style.display = "none";
|
|
||||||
if (e.target.className === "placeholder") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
over = e.target;
|
|
||||||
|
|
||||||
const relY = e.originalEvent.clientY - over.offsetTop;
|
|
||||||
const height = over.offsetHeight / 2;
|
|
||||||
const parent = e.target.parentNode;
|
|
||||||
|
|
||||||
if (relY > height) {
|
|
||||||
nodePlacement = "after";
|
|
||||||
parent.insertBefore(placeholder, e.target.nextElementSibling);
|
|
||||||
} else if (relY < height) {
|
|
||||||
nodePlacement = "before";
|
|
||||||
parent.insertBefore(placeholder, e.target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}.on("didInsertElement"),
|
|
||||||
|
|
||||||
_removeSorting: function() {
|
|
||||||
this.$()
|
|
||||||
.off("dragover.discourse")
|
|
||||||
.off("dragend.discourse")
|
|
||||||
.off("dragstart.discourse");
|
|
||||||
}.on("willDestroyElement"),
|
|
||||||
|
|
||||||
_setupCollection: function() {
|
|
||||||
const values = this.get("values");
|
const values = this.get("values");
|
||||||
if (this.get("inputType") === "array") {
|
if (this.get("inputType") === "array") {
|
||||||
this.set("collection", values || []);
|
this.set("collection", values || []);
|
||||||
} else {
|
|
||||||
this.set("collection", values && values.length ? values.split("\n") : []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.on("init")
|
|
||||||
.observes("values"),
|
|
||||||
|
|
||||||
_saveValues: function() {
|
|
||||||
if (this.get("inputType") === "array") {
|
|
||||||
this.set("values", this.get("collection"));
|
|
||||||
} else {
|
|
||||||
this.set("values", this.get("collection").join("\n"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
inputInvalid: Ember.computed.empty("newValue"),
|
|
||||||
|
|
||||||
keyDown(e) {
|
|
||||||
if (e.keyCode === 13) {
|
|
||||||
this.send("addValue");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
addValue() {
|
|
||||||
if (this.get("inputInvalid")) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get("collection").addObject(this.get("newValue"));
|
this.set(
|
||||||
this.set("newValue", "");
|
"collection",
|
||||||
|
this._splitValues(values, this.get("inputDelimiter") || "\n")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
this._saveValues();
|
@computed("choices.[]", "collection.[]")
|
||||||
|
filteredChoices(choices, collection) {
|
||||||
|
return Ember.makeArray(choices).filter(i => collection.indexOf(i) < 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
keyDown(event) {
|
||||||
|
if (event.keyCode === 13) this.send("addValue", this.get("newValue"));
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
changeValue(index, newValue) {
|
||||||
|
this._replaceValue(index, newValue);
|
||||||
|
},
|
||||||
|
|
||||||
|
addValue(newValue) {
|
||||||
|
if (this.get("inputInvalid")) return;
|
||||||
|
|
||||||
|
this.set("newValue", "");
|
||||||
|
this._addValue(newValue);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeValue(value) {
|
removeValue(value) {
|
||||||
|
this._removeValue(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
selectChoice(choice) {
|
||||||
|
this._addValue(choice);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_addValue(value) {
|
||||||
|
this.get("collection").addObject(value);
|
||||||
|
this._saveValues();
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeValue(value) {
|
||||||
const collection = this.get("collection");
|
const collection = this.get("collection");
|
||||||
collection.removeObject(value);
|
collection.removeObject(value);
|
||||||
this._saveValues();
|
this._saveValues();
|
||||||
|
},
|
||||||
|
|
||||||
|
_replaceValue(index, newValue) {
|
||||||
|
this.get("collection").replace(index, 1, [newValue]);
|
||||||
|
this._saveValues();
|
||||||
|
},
|
||||||
|
|
||||||
|
_saveValues() {
|
||||||
|
if (this.get("inputType") === "array") {
|
||||||
|
this.set("values", this.get("collection"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set(
|
||||||
|
"values",
|
||||||
|
this.get("collection").join(this.get("inputDelimiter") || "\n")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_splitValues(values, delimiter) {
|
||||||
|
if (values && values.length) {
|
||||||
|
return values.split(delimiter).filter(x => x);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,8 @@ const CUSTOM_TYPES = [
|
|||||||
"category_list",
|
"category_list",
|
||||||
"value_list",
|
"value_list",
|
||||||
"category",
|
"category",
|
||||||
"uploaded_image_list"
|
"uploaded_image_list",
|
||||||
|
"compact_list"
|
||||||
];
|
];
|
||||||
|
|
||||||
export default Ember.Mixin.create({
|
export default Ember.Mixin.create({
|
||||||
@ -59,11 +60,20 @@ export default Ember.Mixin.create({
|
|||||||
return setting.replace(/\_/g, " ");
|
return setting.replace(/\_/g, " ");
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("setting.type")
|
@computed("type")
|
||||||
componentType(type) {
|
componentType(type) {
|
||||||
return CUSTOM_TYPES.indexOf(type) !== -1 ? type : "string";
|
return CUSTOM_TYPES.indexOf(type) !== -1 ? type : "string";
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed("setting")
|
||||||
|
type(setting) {
|
||||||
|
if (setting.type === "list" && setting.list_type) {
|
||||||
|
return `${setting.list_type}_list`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting.type;
|
||||||
|
},
|
||||||
|
|
||||||
@computed("typeClass")
|
@computed("typeClass")
|
||||||
componentName(typeClass) {
|
componentName(typeClass) {
|
||||||
return "site-settings/" + typeClass;
|
return "site-settings/" + typeClass;
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{{list-setting settingValue=value choices=setting.choices settingName=setting.setting}}
|
||||||
|
{{setting-validation-message message=validationMessage}}
|
||||||
|
<div class='desc'>{{{unbound setting.description}}}</div>
|
@ -1,3 +1,3 @@
|
|||||||
{{list-setting settingValue=value choices=setting.choices settingName=setting.setting}}
|
{{value-list values=value inputDelimiter="|" choices=setting.choices}}
|
||||||
{{setting-validation-message message=validationMessage}}
|
{{setting-validation-message message=validationMessage}}
|
||||||
<div class='desc'>{{{unbound setting.description}}}</div>
|
<div class='desc'>{{{unbound setting.description}}}</div>
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
{{#if collection}}
|
{{#if collection}}
|
||||||
<div class='values'>
|
<div class='values'>
|
||||||
{{#each collection as |value index|}}
|
{{#each collection as |value index|}}
|
||||||
<div class='value' draggable='true' data-index={{index}}>
|
<div class='value' data-index={{index}}>
|
||||||
{{d-button action="removeValue"
|
{{d-button action="removeValue"
|
||||||
actionParam=value
|
actionParam=value
|
||||||
icon="times"
|
icon="times"
|
||||||
class="btn-small"}}
|
class="remove-value-btn btn-small"}}
|
||||||
{{value}}
|
|
||||||
|
{{input value=value class="value-input" focus-out=(action "changeValue" index)}}
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class='input'>
|
{{combo-box
|
||||||
{{text-field value=newValue placeholderKey=addKey}}
|
allowAny=true
|
||||||
{{d-button action="addValue" icon="plus" class="btn-primary btn-small" disabled=inputInvalid}}
|
allowContentReplacement=true
|
||||||
</div>
|
none=noneKey
|
||||||
|
content=filteredChoices
|
||||||
|
onSelect=(action "selectChoice")}}
|
||||||
|
@ -151,9 +151,14 @@ export default Ember.Component.extend(
|
|||||||
this
|
this
|
||||||
);
|
);
|
||||||
|
|
||||||
const existingCreatedComputedContent = this.get(
|
let existingCreatedComputedContent = [];
|
||||||
"computedContent"
|
if (!this.get("allowContentReplacement")) {
|
||||||
).filterBy("created", true);
|
existingCreatedComputedContent = this.get("computedContent").filterBy(
|
||||||
|
"created",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
computedContent: content
|
computedContent: content
|
||||||
.map(c => this.computeContentItem(c))
|
.map(c => this.computeContentItem(c))
|
||||||
|
@ -77,8 +77,8 @@ export default Ember.Mixin.create({
|
|||||||
|
|
||||||
// use to collapse and remove focus
|
// use to collapse and remove focus
|
||||||
close(event) {
|
close(event) {
|
||||||
this.collapse(event);
|
|
||||||
this.setProperties({ isFocused: false });
|
this.setProperties({ isFocused: false });
|
||||||
|
this.collapse(event);
|
||||||
},
|
},
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
@ -118,8 +118,11 @@ export default Ember.Mixin.create({
|
|||||||
|
|
||||||
collapse() {
|
collapse() {
|
||||||
this.set("isExpanded", false);
|
this.set("isExpanded", false);
|
||||||
|
|
||||||
|
Ember.run.next(() => {
|
||||||
Ember.run.schedule("afterRender", () => this._removeFixedPosition());
|
Ember.run.schedule("afterRender", () => this._removeFixedPosition());
|
||||||
this._boundaryActionHandler("onCollapse", this);
|
this._boundaryActionHandler("onCollapse", this);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// lose focus of the component in two steps
|
// lose focus of the component in two steps
|
||||||
|
@ -873,25 +873,43 @@ table#user-badges {
|
|||||||
|
|
||||||
.value-list {
|
.value-list {
|
||||||
.value {
|
.value {
|
||||||
border-bottom: 1px solid #ddd;
|
padding: 0.125em 0;
|
||||||
padding: 3px;
|
|
||||||
margin-right: 10px;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
cursor: move;
|
display: flex;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
border-color: $primary-low;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: $tertiary;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-value-btn {
|
||||||
|
margin-right: 0.25em;
|
||||||
|
width: 29px;
|
||||||
|
border: 1px solid $primary-low;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: $tertiary;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.values {
|
.values {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 0.5em;
|
||||||
}
|
|
||||||
.placeholder {
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
padding: 3px;
|
|
||||||
margin-right: 10px;
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
input[type="text"] {
|
|
||||||
width: 90%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,9 +444,17 @@
|
|||||||
padding: 0.25em 0;
|
padding: 0.25em 0;
|
||||||
&.input-area {
|
&.input-area {
|
||||||
width: 75%;
|
width: 75%;
|
||||||
|
.value-list,
|
||||||
|
.select-kit,
|
||||||
input[type="text"] {
|
input[type="text"] {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.value-list {
|
||||||
|
.select-kit {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.label-area {
|
&.label-area {
|
||||||
width: 25%;
|
width: 25%;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
class ThemeSettingsSerializer < ApplicationSerializer
|
class ThemeSettingsSerializer < ApplicationSerializer
|
||||||
attributes :setting, :type, :default, :value, :description, :valid_values
|
attributes :setting, :type, :default, :value, :description, :valid_values,
|
||||||
|
:list_type
|
||||||
|
|
||||||
def setting
|
def setting
|
||||||
object.name
|
object.name
|
||||||
@ -32,4 +33,12 @@ class ThemeSettingsSerializer < ApplicationSerializer
|
|||||||
def include_description?
|
def include_description?
|
||||||
object.description.present?
|
object.description.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def list_type
|
||||||
|
object.list_type
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_list_type?
|
||||||
|
object.type == ThemeSetting.types[:list]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -3828,6 +3828,9 @@ en:
|
|||||||
clear_filter: "Clear"
|
clear_filter: "Clear"
|
||||||
add_url: "add URL"
|
add_url: "add URL"
|
||||||
add_host: "add host"
|
add_host: "add host"
|
||||||
|
value_list:
|
||||||
|
default_none: "Type to filter or create..."
|
||||||
|
no_choices_none: "Type to create..."
|
||||||
uploaded_image_list:
|
uploaded_image_list:
|
||||||
label: "Edit list"
|
label: "Edit list"
|
||||||
empty: "There are no pictures yet. Please upload one."
|
empty: "There are no pictures yet. Please upload one."
|
||||||
|
@ -116,6 +116,7 @@ basic:
|
|||||||
client: true
|
client: true
|
||||||
refresh: true
|
refresh: true
|
||||||
type: list
|
type: list
|
||||||
|
list_type: compact
|
||||||
default: "latest|new|unread|top|categories"
|
default: "latest|new|unread|top|categories"
|
||||||
regex: "latest"
|
regex: "latest"
|
||||||
regex_error: "site_settings.errors.must_include_latest"
|
regex_error: "site_settings.errors.must_include_latest"
|
||||||
@ -176,6 +177,7 @@ basic:
|
|||||||
category_colors:
|
category_colors:
|
||||||
client: true
|
client: true
|
||||||
type: list
|
type: list
|
||||||
|
list_type: compact
|
||||||
default: 'BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890'
|
default: 'BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890'
|
||||||
category_style:
|
category_style:
|
||||||
client: true
|
client: true
|
||||||
|
@ -6,7 +6,7 @@ module SiteSettings; end
|
|||||||
class SiteSettings::TypeSupervisor
|
class SiteSettings::TypeSupervisor
|
||||||
include SiteSettings::Validations
|
include SiteSettings::Validations
|
||||||
|
|
||||||
CONSUMED_OPTS = %i[enum choices type validator min max regex hidden regex_error allow_any].freeze
|
CONSUMED_OPTS = %i[enum choices type validator min max regex hidden regex_error allow_any list_type].freeze
|
||||||
VALIDATOR_OPTS = %i[min max regex hidden regex_error].freeze
|
VALIDATOR_OPTS = %i[min max regex hidden regex_error].freeze
|
||||||
|
|
||||||
# For plugins, so they can tell if a feature is supported
|
# For plugins, so they can tell if a feature is supported
|
||||||
@ -63,6 +63,7 @@ class SiteSettings::TypeSupervisor
|
|||||||
@validators = {}
|
@validators = {}
|
||||||
@types = {}
|
@types = {}
|
||||||
@allow_any = {}
|
@allow_any = {}
|
||||||
|
@list_type = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_setting(name_arg, opts = {})
|
def load_setting(name_arg, opts = {})
|
||||||
@ -88,6 +89,7 @@ class SiteSettings::TypeSupervisor
|
|||||||
|
|
||||||
if type.to_sym == :list
|
if type.to_sym == :list
|
||||||
@allow_any[name] = opts[:allow_any] == false ? false : true
|
@allow_any[name] = opts[:allow_any] == false ? false : true
|
||||||
|
@list_type[name] = opts[:list_type] if opts[:list_type]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@types[name] = get_data_type(name, @defaults_provider[name])
|
@types[name] = get_data_type(name, @defaults_provider[name])
|
||||||
@ -144,6 +146,7 @@ class SiteSettings::TypeSupervisor
|
|||||||
end
|
end
|
||||||
|
|
||||||
result[:choices] = @choices[name] if @choices.has_key? name
|
result[:choices] = @choices[name] if @choices.has_key? name
|
||||||
|
result[:list_type] = @list_type[name] if @list_type.has_key? name
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -93,7 +93,12 @@ class ThemeSettingsManager
|
|||||||
(max.is_a?(::Integer) || max.is_a?(::Float)) && max != ::Float::INFINITY
|
(max.is_a?(::Integer) || max.is_a?(::Float)) && max != ::Float::INFINITY
|
||||||
end
|
end
|
||||||
|
|
||||||
class List < self; end
|
class List < self
|
||||||
|
def list_type
|
||||||
|
@opts[:list_type]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class String < self
|
class String < self
|
||||||
def is_valid_value?(new_value)
|
def is_valid_value?(new_value)
|
||||||
(@opts[:min]..@opts[:max]).include? new_value.to_s.length
|
(@opts[:min]..@opts[:max]).include? new_value.to_s.length
|
||||||
|
@ -33,6 +33,11 @@ class ThemeSettingsParser
|
|||||||
opts[:max] = raw_opts[:max].is_a?(Numeric) ? raw_opts[:max] : Float::INFINITY
|
opts[:max] = raw_opts[:max].is_a?(Numeric) ? raw_opts[:max] : Float::INFINITY
|
||||||
opts[:min] = raw_opts[:min].is_a?(Numeric) ? raw_opts[:min] : -Float::INFINITY
|
opts[:min] = raw_opts[:min].is_a?(Numeric) ? raw_opts[:min] : -Float::INFINITY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if raw_opts[:list_type]
|
||||||
|
opts[:list_type] = raw_opts[:list_type]
|
||||||
|
end
|
||||||
|
|
||||||
opts
|
opts
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ describe SiteSettings::TypeSupervisor do
|
|||||||
settings.setting(:type_url_list, 'string', type: 'url_list')
|
settings.setting(:type_url_list, 'string', type: 'url_list')
|
||||||
settings.setting(:type_enum_choices, '2', type: 'enum', choices: ['1', '2'])
|
settings.setting(:type_enum_choices, '2', type: 'enum', choices: ['1', '2'])
|
||||||
settings.setting(:type_enum_class, 'a', enum: 'TestEnumClass2')
|
settings.setting(:type_enum_class, 'a', enum: 'TestEnumClass2')
|
||||||
settings.setting(:type_list, 'a', type: 'list', choices: ['a', 'b'])
|
settings.setting(:type_list, 'a', type: 'list', choices: ['a', 'b'], list_type: 'compact')
|
||||||
settings.refresh!
|
settings.refresh!
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -336,6 +336,10 @@ describe SiteSettings::TypeSupervisor do
|
|||||||
expect(settings.type_supervisor.type_hash(:type_list)[:choices]).to eq ['a', 'b']
|
expect(settings.type_supervisor.type_hash(:type_list)[:choices]).to eq ['a', 'b']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'returns list list_type' do
|
||||||
|
expect(settings.type_supervisor.type_hash(:type_list)[:list_type]).to eq 'compact'
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns enum choices' do
|
it 'returns enum choices' do
|
||||||
hash = settings.type_supervisor.type_hash(:type_enum_choices)
|
hash = settings.type_supervisor.type_hash(:type_enum_choices)
|
||||||
expect(hash[:valid_values]).to eq [{ name: '1', value: '1' }, { name: '2', value: '2' }]
|
expect(hash[:valid_values]).to eq [{ name: '1', value: '1' }, { name: '2', value: '2' }]
|
||||||
|
@ -114,4 +114,11 @@ describe ThemeSettingsManager do
|
|||||||
expect { string_setting.value = ("a" * 21) }.to raise_error(Discourse::InvalidParameters)
|
expect { string_setting.value = ("a" * 21) }.to raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "List" do
|
||||||
|
it "can have a list type" do
|
||||||
|
list_setting = find_by_name(:compact_list_setting)
|
||||||
|
expect(list_setting.list_type).to eq("compact")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -82,4 +82,11 @@ describe ThemeSettingsParser do
|
|||||||
expect(choices.length).to eq(1)
|
expect(choices.length).to eq(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "list setting" do
|
||||||
|
it "supports list type" do
|
||||||
|
list_type = loader.find_by_name(:compact_list_setting)[:opts][:list_type]
|
||||||
|
expect(list_type).to eq("compact")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -37,6 +37,12 @@ list_setting:
|
|||||||
description: "help text"
|
description: "help text"
|
||||||
default: "name|age|last name"
|
default: "name|age|last name"
|
||||||
|
|
||||||
|
compact_list_setting:
|
||||||
|
type: list
|
||||||
|
list_type: compact
|
||||||
|
description: "help text"
|
||||||
|
default: "name|age|last name"
|
||||||
|
|
||||||
enum_setting:
|
enum_setting:
|
||||||
default: "trust level 4"
|
default: "trust level 4"
|
||||||
type: enum
|
type: enum
|
||||||
|
@ -1,63 +1,111 @@
|
|||||||
import componentTest from "helpers/component-test";
|
import componentTest from "helpers/component-test";
|
||||||
moduleForComponent("value-list", { integration: true });
|
moduleForComponent("value-list", { integration: true });
|
||||||
|
|
||||||
componentTest("functionality", {
|
componentTest("adding a value", {
|
||||||
template: '{{value-list values=values inputType="array"}}',
|
template: "{{value-list values=values}}",
|
||||||
|
|
||||||
async test(assert) {
|
async test(assert) {
|
||||||
assert.ok(this.$(".values .value").length === 0, "it has no values");
|
this.set("values", "vinkas\nosama");
|
||||||
assert.ok(this.$("input").length, "it renders the input");
|
|
||||||
|
await selectKit().expand();
|
||||||
|
await selectKit().fillInFilter("eviltrout");
|
||||||
|
await selectKit().keyboard("enter");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
this.$(".btn-primary[disabled]").length,
|
find(".values .value").length === 3,
|
||||||
"it is disabled with no value"
|
"it adds the value to the list of values"
|
||||||
);
|
);
|
||||||
|
|
||||||
await fillIn("input", "eviltrout");
|
assert.deepEqual(
|
||||||
|
this.get("values"),
|
||||||
|
"vinkas\nosama\neviltrout",
|
||||||
|
"it adds the value to the list of values"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest("removing a value", {
|
||||||
|
template: "{{value-list values=values}}",
|
||||||
|
|
||||||
|
async test(assert) {
|
||||||
|
this.set("values", "vinkas\nosama");
|
||||||
|
|
||||||
|
await click(".values .value[data-index='0'] .remove-value-btn");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
!this.$(".btn-primary[disabled]").length,
|
find(".values .value").length === 1,
|
||||||
"it isn't disabled anymore"
|
"it removes the value from the list of values"
|
||||||
);
|
);
|
||||||
|
|
||||||
await click(".btn-primary");
|
assert.equal(this.get("values"), "osama", "it removes the expected value");
|
||||||
assert.equal(this.$(".values .value").length, 1, "it adds the value");
|
|
||||||
assert.equal(this.$("input").val(), "", "it clears the input");
|
|
||||||
assert.ok(this.$(".btn-primary[disabled]").length, "it is disabled again");
|
|
||||||
assert.equal(this.get("values"), "eviltrout", "it appends the value");
|
|
||||||
|
|
||||||
await click(".value .btn-small");
|
|
||||||
assert.ok(this.$(".values .value").length === 0, "it removes the value");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
componentTest("with string delimited values", {
|
componentTest("selecting a value", {
|
||||||
template: "{{value-list values=valueString}}",
|
template: "{{value-list values=values choices=choices}}",
|
||||||
beforeEach() {
|
|
||||||
this.set("valueString", "hello\nworld");
|
|
||||||
},
|
|
||||||
|
|
||||||
async test(assert) {
|
async test(assert) {
|
||||||
assert.equal(this.$(".values .value").length, 2);
|
this.set("values", "vinkas\nosama");
|
||||||
|
this.set("choices", ["maja", "michael"]);
|
||||||
|
|
||||||
await fillIn("input", "eviltrout");
|
await selectKit().expand();
|
||||||
await click(".btn-primary");
|
await selectKit().selectRowByValue("maja");
|
||||||
|
|
||||||
assert.equal(this.$(".values .value").length, 3);
|
assert.ok(
|
||||||
assert.equal(this.get("valueString"), "hello\nworld\neviltrout");
|
find(".values .value").length === 3,
|
||||||
|
"it adds the value to the list of values"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
this.get("values"),
|
||||||
|
"vinkas\nosama\nmaja",
|
||||||
|
"it adds the value to the list of values"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
componentTest("with array values", {
|
componentTest("array support", {
|
||||||
template: '{{value-list values=valueArray inputType="array"}}',
|
template: "{{value-list values=values inputType='array'}}",
|
||||||
beforeEach() {
|
|
||||||
this.set("valueArray", ["abc", "def"]);
|
|
||||||
},
|
|
||||||
|
|
||||||
async test(assert) {
|
async test(assert) {
|
||||||
assert.equal(this.$(".values .value").length, 2);
|
this.set("values", ["vinkas", "osama"]);
|
||||||
|
|
||||||
await fillIn("input", "eviltrout");
|
await selectKit().expand();
|
||||||
await click(".btn-primary");
|
await selectKit().fillInFilter("eviltrout");
|
||||||
|
await selectKit().keyboard("enter");
|
||||||
|
|
||||||
assert.equal(this.$(".values .value").length, 3);
|
assert.ok(
|
||||||
assert.deepEqual(this.get("valueArray"), ["abc", "def", "eviltrout"]);
|
find(".values .value").length === 3,
|
||||||
|
"it adds the value to the list of values"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
this.get("values"),
|
||||||
|
["vinkas", "osama", "eviltrout"],
|
||||||
|
"it adds the value to the list of values"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest("delimiter support", {
|
||||||
|
template: "{{value-list values=values inputDelimiter='|'}}",
|
||||||
|
|
||||||
|
async test(assert) {
|
||||||
|
this.set("values", "vinkas|osama");
|
||||||
|
|
||||||
|
await selectKit().expand();
|
||||||
|
await selectKit().fillInFilter("eviltrout");
|
||||||
|
await selectKit().keyboard("enter");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
find(".values .value").length === 3,
|
||||||
|
"it adds the value to the list of values"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
this.get("values"),
|
||||||
|
"vinkas|osama|eviltrout",
|
||||||
|
"it adds the value to the list of values"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user