FIX: select-box improvments
- more tests for category-select-box - allows to clear selection - fix positioning on safari - focus on click - do not display uncategorized if not allowed in settings - fix component state impacting other specs - better texts - higher category-select-box on mobile
This commit is contained in:
parent
be972912cb
commit
b83c0747d9
|
@ -15,6 +15,8 @@ export default SelectBoxComponent.extend({
|
|||
|
||||
width: "100%",
|
||||
|
||||
clearable: true,
|
||||
|
||||
filterFunction: function() {
|
||||
const _matchFunction = (filter, text) => {
|
||||
return text.toLowerCase().indexOf(filter) > -1;
|
||||
|
@ -75,6 +77,9 @@ export default SelectBoxComponent.extend({
|
|||
const categoryId = c.get("id");
|
||||
if (scopedCategoryId && categoryId !== scopedCategoryId && c.get("parent_category_id") !== scopedCategoryId) { return false; }
|
||||
if (excludeCategoryId === categoryId) { return false; }
|
||||
if (!this.siteSettings.allow_uncategorized_topics && c.get("isUncategorizedCategory")) {
|
||||
return false;
|
||||
}
|
||||
return c.get("permission") === PermissionType.FULL;
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { on, observes } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
|
@ -16,9 +17,10 @@ export default Ember.Component.extend({
|
|||
|
||||
caretUpIcon: "caret-up",
|
||||
caretDownIcon: "caret-down",
|
||||
headerText: null,
|
||||
headerText: I18n.t("select_box.default_header_text"),
|
||||
dynamicHeaderText: true,
|
||||
icon: null,
|
||||
clearable: false,
|
||||
|
||||
value: null,
|
||||
selectedContent: null,
|
||||
|
@ -103,13 +105,8 @@ export default Ember.Component.extend({
|
|||
this.set("filterable", false);
|
||||
}
|
||||
|
||||
if (this.get("castInteger")) {
|
||||
this.set("value", parseInt(this.get("value"), 10));
|
||||
}
|
||||
|
||||
this.set("headerText", Handlebars.escapeExpression(this.get("headerText")));
|
||||
|
||||
this.setProperties({
|
||||
value: this._castInteger(this.get("value")),
|
||||
componentId: this.elementId,
|
||||
filteredContent: []
|
||||
});
|
||||
|
@ -117,8 +114,7 @@ export default Ember.Component.extend({
|
|||
|
||||
@on("willDestroyElement")
|
||||
_removeDocumentListeners: function() {
|
||||
$(document).off("click.select-box");
|
||||
$(document).off("keydown.select-box");
|
||||
$(document).off("click.select-box", "keydown.select-box");
|
||||
$(window).off("resize.select-box");
|
||||
},
|
||||
|
||||
|
@ -160,6 +156,8 @@ export default Ember.Component.extend({
|
|||
_setupDocumentListeners: function() {
|
||||
$(document)
|
||||
.on("click.select-box", (event) => {
|
||||
if (this.isDestroying || this.isDestroyed) { return; }
|
||||
|
||||
const $element = this.$();
|
||||
const $target = $(event.target);
|
||||
|
||||
|
@ -180,19 +178,13 @@ export default Ember.Component.extend({
|
|||
|
||||
@on("didInsertElement")
|
||||
_bindEvents: function() {
|
||||
this.$(".select-box-offscreen").on("focusin.select-box", () => {
|
||||
this.set("focused", true);
|
||||
});
|
||||
this.$(".select-box-offscreen").on("focusout.select-box", () => {
|
||||
this.set("focused", false);
|
||||
});
|
||||
this.$(".select-box-offscreen")
|
||||
.on("focusin.select-box", () => this.set("focused", true) )
|
||||
.on("focusout.select-box", () => this.set("focused", false) );
|
||||
|
||||
this.$(".filter-query").on("focusin.select-box", () => {
|
||||
this.set("filterFocused", true);
|
||||
});
|
||||
this.$(".filter-query").on("focusout.select-box", () => {
|
||||
this.set("filterFocused", false);
|
||||
});
|
||||
this.$(".filter-query")
|
||||
.on("focusin.select-box", () => this.set("filterFocused", true) )
|
||||
.on("focusout.select-box", () => this.set("filterFocused", false) );
|
||||
|
||||
this.$(".select-box-offscreen").on("keydown.select-box", (event) => {
|
||||
const keyCode = event.keyCode || event.which;
|
||||
|
@ -221,18 +213,6 @@ export default Ember.Component.extend({
|
|||
if (Ember.isNone(this.get("value"))) {
|
||||
this.set("lastHoveredId", null);
|
||||
}
|
||||
|
||||
this.set("filteredContent", this._remapContent(this.get("content")));
|
||||
},
|
||||
|
||||
@observes("filter")
|
||||
_filterChanged: function() {
|
||||
if (Ember.isEmpty(this.get("filter"))) {
|
||||
this.set("filteredContent", this._remapContent(this.get("content")));
|
||||
} else {
|
||||
const filtered = this.filterFunction()(this);
|
||||
this.set("filteredContent", this._remapContent(filtered));
|
||||
}
|
||||
},
|
||||
|
||||
@observes("expanded")
|
||||
|
@ -250,6 +230,42 @@ export default Ember.Component.extend({
|
|||
};
|
||||
},
|
||||
|
||||
@computed("value", "content")
|
||||
selectedContent(value, content) {
|
||||
if (Ember.isNone(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const selectedContent = content.find((c) => {
|
||||
return c[this.get("idKey")] === value;
|
||||
});
|
||||
|
||||
if (!Ember.isNone(selectedContent)) {
|
||||
return this._normalizeContent(selectedContent);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
@computed("headerText", "dynamicHeaderText", "selectedContent.text")
|
||||
generatedHeadertext(headerText, dynamic, text) {
|
||||
if (dynamic && !Ember.isNone(text)) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return headerText;
|
||||
},
|
||||
|
||||
@observes("content.[]", "filter")
|
||||
_filterChanged: function() {
|
||||
if (Ember.isEmpty(this.get("filter"))) {
|
||||
this.set("filteredContent", this._remapContent(this.get("content")));
|
||||
} else {
|
||||
const filtered = this.filterFunction()(this);
|
||||
this.set("filteredContent", this._remapContent(filtered));
|
||||
}
|
||||
},
|
||||
|
||||
@observes("content.[]", "value")
|
||||
@on("didReceiveAttrs")
|
||||
_contentChanged: function() {
|
||||
|
@ -260,13 +276,6 @@ export default Ember.Component.extend({
|
|||
}
|
||||
|
||||
this.set("filteredContent", this._remapContent(this.get("content")));
|
||||
this._setSelectedContent(this.get("content"));
|
||||
|
||||
if (this.get("dynamicHeaderText")) {
|
||||
if (!Ember.isNone(this.get("selectedContent.text"))) {
|
||||
this.set("headerText", this.get("selectedContent.text"));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
@ -274,17 +283,17 @@ export default Ember.Component.extend({
|
|||
this.toggleProperty("expanded");
|
||||
},
|
||||
|
||||
onClearSelection() {
|
||||
this.set("value", null);
|
||||
},
|
||||
|
||||
onFilterChange(filter) {
|
||||
this.set("filter", filter);
|
||||
},
|
||||
|
||||
onSelectRow(id) {
|
||||
if (this.get("castInteger")) {
|
||||
id = parseInt(id, 10);
|
||||
}
|
||||
|
||||
this.setProperties({
|
||||
value: id,
|
||||
value: this._castInteger(id),
|
||||
expanded: false
|
||||
});
|
||||
},
|
||||
|
@ -294,28 +303,13 @@ export default Ember.Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
_setSelectedContent(content) {
|
||||
const selectedContent = content.find((c) => {
|
||||
return c[this.get("idKey")] === this.get("value");
|
||||
});
|
||||
|
||||
if (!Ember.isNone(selectedContent)) {
|
||||
this.set("selectedContent", this._normalizeContent(selectedContent));
|
||||
}
|
||||
},
|
||||
|
||||
_remapContent(content) {
|
||||
return content.map(c => this._normalizeContent(c));
|
||||
},
|
||||
|
||||
_normalizeContent(content) {
|
||||
let id = content[this.get("idKey")];
|
||||
if (this.get("castInteger")) {
|
||||
id = parseInt(id, 10);
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
id: this._castInteger(content[this.get("idKey")]),
|
||||
text: content[this.get("textKey")],
|
||||
icon: content[this.get("iconKey")]
|
||||
};
|
||||
|
@ -330,4 +324,12 @@ export default Ember.Component.extend({
|
|||
height: headerHeight + this.$(".select-box-body").outerHeight()
|
||||
});
|
||||
},
|
||||
|
||||
_castInteger(value) {
|
||||
if (this.get("castInteger") && Ember.isPresent(value)) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,18 +1,33 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: "select-box-header",
|
||||
|
||||
classNameBindings: ["focused:is-focused"],
|
||||
|
||||
showClearButton: false,
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super();
|
||||
|
||||
this._setCaretIcon();
|
||||
},
|
||||
|
||||
@computed("clearable", "selectedId")
|
||||
showClearButton(clearable, selectedId) {
|
||||
return clearable === true && !Ember.isNone(selectedId);
|
||||
},
|
||||
|
||||
click() {
|
||||
this.sendAction("onToggle");
|
||||
},
|
||||
|
||||
actions: {
|
||||
clearSelection() {
|
||||
this.sendAction("onClearSelection");
|
||||
}
|
||||
},
|
||||
|
||||
_setCaretIcon() {
|
||||
if(this.get("expanded")) {
|
||||
this.set("caretIcon", this.get("caretUpIcon"));
|
||||
|
|
|
@ -8,13 +8,16 @@
|
|||
/>
|
||||
|
||||
{{component selectBoxHeaderComponent
|
||||
text=headerText
|
||||
text=generatedHeadertext
|
||||
focused=focused
|
||||
caretUpIcon=caretUpIcon
|
||||
caretDownIcon=caretDownIcon
|
||||
onToggle=(action "onToggle")
|
||||
onClearSelection=(action "onClearSelection")
|
||||
icon=icon
|
||||
clearable=clearable
|
||||
expanded=expanded
|
||||
selectedId=value
|
||||
}}
|
||||
|
||||
<div class="select-box-body">
|
||||
|
|
|
@ -6,4 +6,10 @@
|
|||
{{text}}
|
||||
</span>
|
||||
|
||||
{{#if showClearButton}}
|
||||
<button class="btn btn-primary clear-selection" {{action "clearSelection" bubbles=false}}>
|
||||
{{d-icon "times"}}
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
{{d-icon caretIcon class="caret-icon"}}
|
||||
|
|
|
@ -91,6 +91,18 @@
|
|||
-webkit-box-shadow: $tertiary 0px 0px 6px 0px;
|
||||
box-shadow: $tertiary 0px 0px 6px 0px;
|
||||
}
|
||||
|
||||
.clear-selection {
|
||||
margin: 0 5px;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
|
||||
.d-icon {
|
||||
margin: 0;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-box-body {
|
||||
|
@ -190,12 +202,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.select-box .select-box-row {
|
||||
&.is-highlighted {
|
||||
background: $highlight-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.select-box .select-box-collection {
|
||||
-webkit-box-flex: 0;
|
||||
-ms-flex: 0 1 auto;
|
||||
|
@ -231,6 +237,14 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&.is-highlighted {
|
||||
background: $highlight-medium;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
a {
|
||||
background: $highlight-medium;
|
||||
|
|
|
@ -237,8 +237,7 @@
|
|||
min-width: 1280px;
|
||||
.form-element {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
display: inline-block;
|
||||
|
||||
.category-input {
|
||||
width: 430px;
|
||||
|
@ -313,6 +312,7 @@
|
|||
}
|
||||
.title-input, .category-input, .show-admin-options {
|
||||
display: inline;
|
||||
float: left;
|
||||
}
|
||||
.show-admin-options {
|
||||
vertical-align: top;
|
||||
|
|
|
@ -128,10 +128,6 @@ input[type=radio], input[type=checkbox] {
|
|||
}
|
||||
.category-input {
|
||||
margin-top: 3px;
|
||||
|
||||
.category-select-box {
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
.wmd-controls {
|
||||
transition: top 0.3s ease;
|
||||
|
@ -206,7 +202,7 @@ input[type=radio], input[type=checkbox] {
|
|||
margin: 7px 0 0 5px;
|
||||
position: absolute;
|
||||
}
|
||||
.btn-primary {
|
||||
.create.btn-primary {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1145,7 +1145,8 @@ en:
|
|||
alt: 'Alt'
|
||||
|
||||
select_box:
|
||||
no_content: No content
|
||||
default_header_text: Select...
|
||||
no_content: No matches found
|
||||
filter_placeholder: Search...
|
||||
|
||||
emoji_picker:
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { acceptance } from "helpers/qunit-helpers";
|
||||
|
||||
acceptance("CategorySelectBox", {
|
||||
loggedIn: true,
|
||||
settings: {
|
||||
allow_uncategorized_topics: false
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test("does not display uncategorized if not allowed", assert => {
|
||||
visit("/");
|
||||
click('#create-topic');
|
||||
|
||||
click(".category-select-box .select-box-header");
|
||||
andThen(() => {
|
||||
assert.ok(!exists('.category-select-box .select-box-row[title="uncategorized"]'));
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("prefill category when category_id is set", assert => {
|
||||
visit("/new-topic?category_id=1");
|
||||
|
||||
andThen(() => {
|
||||
assert.equal(find('.category-select-box .current-selection').html().trim(), "bug");
|
||||
});
|
||||
});
|
|
@ -313,3 +313,25 @@ componentTest('static headerText', {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
componentTest('clearable selection', {
|
||||
template: '{{select-box value=1 content=content clearable=true}}',
|
||||
|
||||
beforeEach() {
|
||||
this.set("content", [{ id: 1, text: "robin" }, { id: 2, text: "regis" }]);
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
click(".select-box-header");
|
||||
andThen(() => {
|
||||
assert.ok(exists(".select-box-row.is-highlighted"));
|
||||
assert.equal(find(".select-box-header .current-selection").html().trim(), "robin");
|
||||
});
|
||||
|
||||
click(".select-box-header .clear-selection");
|
||||
andThen(() => {
|
||||
assert.notOk(exists(".select-box-row.is-highlighted"));
|
||||
assert.equal(find(".select-box-header .current-selection").html().trim(), "Select...");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue