FEATURE: initial implementation of an ember native select
This commit is contained in:
parent
7213e02dee
commit
482924b161
|
@ -36,11 +36,10 @@
|
||||||
|
|
||||||
<h3>{{i18n "admin.customize.theme.color_scheme"}}</h3>
|
<h3>{{i18n "admin.customize.theme.color_scheme"}}</h3>
|
||||||
<p>{{i18n "admin.customize.theme.color_scheme_select"}}</p>
|
<p>{{i18n "admin.customize.theme.color_scheme_select"}}</p>
|
||||||
<p>{{combo-box content=colorSchemes
|
<p>{{d-select-box data=colorSchemes
|
||||||
nameProperty="name"
|
textKey="name"
|
||||||
value=colorSchemeId
|
value=colorSchemeId
|
||||||
selectionIcon="paint-brush"
|
icon="paint-brush"}}
|
||||||
valueAttribute="id"}}
|
|
||||||
{{#if colorSchemeChanged}}
|
{{#if colorSchemeChanged}}
|
||||||
{{d-button action="changeScheme" class="btn-primary btn-small submit-edit" icon="check"}}
|
{{d-button action="changeScheme" class="btn-primary btn-small submit-edit" icon="check"}}
|
||||||
{{d-button action="cancelChangeScheme" class="btn-small cancel-edit" icon="times"}}
|
{{d-button action="cancelChangeScheme" class="btn-small cancel-edit" icon="times"}}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import SelectBoxComponent from "discourse/components/select-box";
|
||||||
|
|
||||||
|
export default SelectBoxComponent.extend({
|
||||||
|
layoutName: "components/select-box",
|
||||||
|
|
||||||
|
classNames: "discourse"
|
||||||
|
});
|
|
@ -0,0 +1,211 @@
|
||||||
|
import { observes } from "ember-addons/ember-computed-decorators";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: "select-box",
|
||||||
|
|
||||||
|
classNameBindings: ["expanded:is-expanded"],
|
||||||
|
|
||||||
|
attributeBindings: ['componentStyle:style'],
|
||||||
|
componentStyle: function() {
|
||||||
|
return Ember.String.htmlSafe(`width: ${this.get("maxWidth")}px`);
|
||||||
|
}.property("maxWidth"),
|
||||||
|
|
||||||
|
expanded: false,
|
||||||
|
focused: false,
|
||||||
|
|
||||||
|
caretUpIcon: "caret-up",
|
||||||
|
caretDownIcon: "caret-down",
|
||||||
|
icon: null,
|
||||||
|
|
||||||
|
value: null,
|
||||||
|
noDataText: I18n.t("select_box.no_data"),
|
||||||
|
lastHoveredId: null,
|
||||||
|
|
||||||
|
idKey: "id",
|
||||||
|
textKey: "text",
|
||||||
|
iconKey: "icon",
|
||||||
|
|
||||||
|
filterable: false,
|
||||||
|
filter: "",
|
||||||
|
filterPlaceholder: I18n.t("select_box.filter_placeholder"),
|
||||||
|
filterIcon: "search",
|
||||||
|
|
||||||
|
selectBoxRowComponent: "select-box/select-box-row",
|
||||||
|
selectBoxFilterComponent: "select-box/select-box-filter",
|
||||||
|
selectBoxHeaderComponent: "select-box/select-box-header",
|
||||||
|
selectBoxCollectionComponent: "select-box/select-box-collection",
|
||||||
|
|
||||||
|
maxCollectionHeight: 200,
|
||||||
|
maxWidth: 200,
|
||||||
|
verticalOffset: 0,
|
||||||
|
horizontalOffset: 0,
|
||||||
|
|
||||||
|
_renderBody: false,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
if(!this.get("data")) {
|
||||||
|
this.set("data", []);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setProperties({
|
||||||
|
componentId: this.elementId,
|
||||||
|
filteredData: [],
|
||||||
|
selectedData: {}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
@observes("filter")
|
||||||
|
_filter: function() {
|
||||||
|
if(_.isEmpty(this.get("filter"))) {
|
||||||
|
this.set("filteredData", this._remapData(this.get("data")));
|
||||||
|
} else {
|
||||||
|
const filtered = _.filter(this.get("data"), (data)=> {
|
||||||
|
return data[this.get("textKey")].toLowerCase().indexOf(this.get("filter")) > -1;
|
||||||
|
});
|
||||||
|
this.set("filteredData", this._remapData(filtered));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@observes("expanded", "filteredData")
|
||||||
|
_expand: function() {
|
||||||
|
if(this.get("expanded")) {
|
||||||
|
this.setProperties({focused: false, _renderBody: true});
|
||||||
|
|
||||||
|
Ember.$(document).on("keydown.select-box", (event) => {
|
||||||
|
const keyCode = event.keyCode || event.which;
|
||||||
|
if (keyCode === 9) {
|
||||||
|
this.set("expanded", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(_.isUndefined(this.get("lastHoveredId"))) {
|
||||||
|
this.set("lastHoveredId", this.get("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ember.run.scheduleOnce("afterRender", this, () => {
|
||||||
|
this.$(".select-box-filter .filter-query").focus();
|
||||||
|
this.$(".select-box-collection").css("max-height", this.get("maxCollectionHeight"));
|
||||||
|
this.$().removeClass("is-reversed");
|
||||||
|
|
||||||
|
const offsetTop = this.$()[0].getBoundingClientRect().top;
|
||||||
|
const windowHeight = Ember.$(window).height();
|
||||||
|
const headerHeight = this.$(".select-box-header").outerHeight();
|
||||||
|
const filterHeight = this.$(".select-box-filter").outerHeight();
|
||||||
|
|
||||||
|
if(windowHeight - (offsetTop + this.get("maxCollectionHeight") + filterHeight + headerHeight) < 0) {
|
||||||
|
this.$().addClass("is-reversed");
|
||||||
|
this.$(".select-box-body").css({
|
||||||
|
left: this.get("horizontalOffset"),
|
||||||
|
top: "",
|
||||||
|
bottom: headerHeight + this.get("verticalOffset")
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$(".select-box-body").css({
|
||||||
|
left: this.get("horizontalOffset"),
|
||||||
|
top: headerHeight + this.get("verticalOffset"),
|
||||||
|
bottom: ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$(".select-box-wrapper").css({
|
||||||
|
width: this.get("maxWidth"),
|
||||||
|
display: "block",
|
||||||
|
height: headerHeight + this.$(".select-box-body").outerHeight()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Ember.$(document).off("keydown.select-box");
|
||||||
|
this.$(".select-box-wrapper").hide();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
Ember.$(document).off("click.select-box");
|
||||||
|
Ember.$(document).off("keydown.select-box");
|
||||||
|
this.$(".select-box-offscreen").off("focusin.select-box");
|
||||||
|
this.$(".select-box-offscreen").off("focusout.select-box");
|
||||||
|
},
|
||||||
|
|
||||||
|
didReceiveAttrs() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
this.set("lastHoveredId", this.get("data")[this.get("idKey")]);
|
||||||
|
this.set("filteredData", this._remapData(this.get("data")));
|
||||||
|
this._setSelectedData(this.get("data"));
|
||||||
|
},
|
||||||
|
|
||||||
|
didRender() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
this.$(".select-box-body").css('width', this.get("maxWidth"));
|
||||||
|
this._expand();
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
Ember.$(document).on("click.select-box", (event) => {
|
||||||
|
if(this.get("expanded") && $(event.target).parents(".select-box").attr("id") !== this.$().attr("id")) {
|
||||||
|
this.setProperties({
|
||||||
|
expanded: false,
|
||||||
|
focused: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$(".select-box-offscreen").on("focusin.select-box", () => {
|
||||||
|
this.set("focused", true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$(".select-box-offscreen").on("focusout.select-box", () => {
|
||||||
|
this.set("focused", false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
onToggle() {
|
||||||
|
this.toggleProperty("expanded");
|
||||||
|
},
|
||||||
|
|
||||||
|
onFilterChange(filter) {
|
||||||
|
this.set("filter", filter);
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelectRow(id) {
|
||||||
|
this.setProperties({
|
||||||
|
value: id,
|
||||||
|
expanded: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onHoverRow(id) {
|
||||||
|
this.set("lastHoveredId", id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_setSelectedData(data) {
|
||||||
|
const selectedData = _.find(data, (d)=> {
|
||||||
|
return d[this.get("idKey")] === this.get("value");
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!_.isUndefined(selectedData)) {
|
||||||
|
this.set("selectedData", this._normalizeData(selectedData));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_remapData(data) {
|
||||||
|
return data.map(d => this._normalizeData(d));
|
||||||
|
},
|
||||||
|
|
||||||
|
_normalizeData(data) {
|
||||||
|
return {
|
||||||
|
id: data[this.get("idKey")],
|
||||||
|
text: data[this.get("textKey")],
|
||||||
|
icon: data[this.get("iconKey")]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: "select-box-collection"
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: "select-box-filter"
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: "select-box-header",
|
||||||
|
|
||||||
|
classNameBindings: ["focused:is-focused"],
|
||||||
|
|
||||||
|
didReceiveAttrs() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
this._setCaretIcon();
|
||||||
|
},
|
||||||
|
|
||||||
|
click() {
|
||||||
|
this.sendAction("onToggle");
|
||||||
|
},
|
||||||
|
|
||||||
|
_setCaretIcon() {
|
||||||
|
if(this.get("expanded")) {
|
||||||
|
this.set("caretIcon", this.get("caretUpIcon"));
|
||||||
|
} else {
|
||||||
|
this.set("caretIcon", this.get("caretDownIcon"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: "select-box-row",
|
||||||
|
|
||||||
|
tagName: "li",
|
||||||
|
|
||||||
|
classNameBindings: ["isHighlighted"],
|
||||||
|
|
||||||
|
attributeBindings: ["text:title"],
|
||||||
|
|
||||||
|
lastHoveredId: null,
|
||||||
|
|
||||||
|
mouseEnter() {
|
||||||
|
this.sendAction("onHover", this.get("data.id"));
|
||||||
|
},
|
||||||
|
|
||||||
|
click() {
|
||||||
|
this.sendAction("onSelect", this.get("data.id"));
|
||||||
|
},
|
||||||
|
|
||||||
|
didReceiveAttrs() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
this.set("isHighlighted", this._isHighlighted());
|
||||||
|
this.set("text", this.get("data.text"));
|
||||||
|
},
|
||||||
|
|
||||||
|
_isHighlighted() {
|
||||||
|
if(_.isUndefined(this.get("lastHoveredId"))) {
|
||||||
|
return this.get("data.id") === this.get("selectedId");
|
||||||
|
} else {
|
||||||
|
return this.get("data.id") === this.get("lastHoveredId");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,41 @@
|
||||||
|
<input
|
||||||
|
class="select-box-offscreen"
|
||||||
|
type="text"
|
||||||
|
aria-haspopup="true"
|
||||||
|
role="button"
|
||||||
|
aria-labelledby="select-box-input-{{componentId}}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{component selectBoxHeaderComponent
|
||||||
|
data=selectedData
|
||||||
|
focused=focused
|
||||||
|
caretUpIcon=caretUpIcon
|
||||||
|
caretDownIcon=caretDownIcon
|
||||||
|
onToggle=(action "onToggle")
|
||||||
|
icon=icon
|
||||||
|
expanded=expanded
|
||||||
|
}}
|
||||||
|
|
||||||
|
<div class="select-box-body">
|
||||||
|
{{#if _renderBody}}
|
||||||
|
{{#if filterable}}
|
||||||
|
{{component selectBoxFilterComponent
|
||||||
|
onFilterChange=(action "onFilterChange")
|
||||||
|
filterIcon=filterIcon
|
||||||
|
filterPlaceholder=filterPlaceholder
|
||||||
|
}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{component selectBoxCollectionComponent
|
||||||
|
filteredData=filteredData
|
||||||
|
selectBoxRowComponent=selectBoxRowComponent
|
||||||
|
lastHoveredId=lastHoveredId
|
||||||
|
onSelectRow=(action "onSelectRow")
|
||||||
|
onHoverRow=(action "onHoverRow")
|
||||||
|
noDataText=noDataText
|
||||||
|
selectedId=value
|
||||||
|
}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="select-box-wrapper"></div>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<ul class="collection">
|
||||||
|
{{#each filteredData as |data|}}
|
||||||
|
{{component selectBoxRowComponent
|
||||||
|
data=data
|
||||||
|
lastHoveredId=lastHoveredId
|
||||||
|
onSelect=onSelectRow
|
||||||
|
onHover=onHoverRow
|
||||||
|
selectedId=selectedId
|
||||||
|
}}
|
||||||
|
{{else}}
|
||||||
|
{{#if noDataText}}
|
||||||
|
<li class="select-box-row no-data">
|
||||||
|
{{noDataText}}
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<div class="wrapper">
|
||||||
|
{{input
|
||||||
|
tabindex="-1"
|
||||||
|
class="filter-query"
|
||||||
|
placeholder=filterPlaceholder
|
||||||
|
key-up=onFilterChange
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{#if filterIcon}}
|
||||||
|
<div class="filter-icon">
|
||||||
|
{{d-icon filterIcon}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<div class="wrapper">
|
||||||
|
{{#if icon}}
|
||||||
|
<div class="icon">
|
||||||
|
{{d-icon icon}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<span class="current-selection">
|
||||||
|
{{data.text}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="caret-icon">
|
||||||
|
{{d-icon caretIcon}}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,7 @@
|
||||||
|
{{#if data.icon}}
|
||||||
|
{{d-icon data.icon}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<p class="text">
|
||||||
|
{{data.text}}
|
||||||
|
</p>
|
|
@ -0,0 +1,246 @@
|
||||||
|
.select-box {
|
||||||
|
border-radius: 3px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
z-index: 998;
|
||||||
|
|
||||||
|
&.is-expanded {
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
.select-box-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-reversed {
|
||||||
|
.select-box-body {
|
||||||
|
bottom: 0;
|
||||||
|
top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-wrapper {
|
||||||
|
bottom: 0;
|
||||||
|
top: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-icon {
|
||||||
|
color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-header {
|
||||||
|
background: $secondary;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 32px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-body {
|
||||||
|
display: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-row {
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
padding: 5px;
|
||||||
|
height: 28px;
|
||||||
|
min-height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-collection {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
background: $secondary;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
border-radius: 0 0 3px 3px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-filter {
|
||||||
|
border-bottom: 1px solid $primary-low;
|
||||||
|
background: $secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background: none;
|
||||||
|
display: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box .select-box-header {
|
||||||
|
.wrapper {
|
||||||
|
height: inherit;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-selection {
|
||||||
|
text-align: left;
|
||||||
|
flex: 1;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caret-icon {
|
||||||
|
margin-left: 5px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box .select-box-row {
|
||||||
|
&.is-highlighted {
|
||||||
|
background: $highlight-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box .select-box-collection {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
|
||||||
|
.collection {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||||
|
-webkit-transition: color .2s ease;
|
||||||
|
transition: color .2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box .select-box-row {
|
||||||
|
.d-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-selected {
|
||||||
|
a {
|
||||||
|
background: $highlight-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box .select-box-filter {
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, input:focus {
|
||||||
|
margin: 0;
|
||||||
|
flex: 1;
|
||||||
|
outline: none;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box .select-box-offscreen, .select-box .select-box-offscreen:focus {
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
outline: 0;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box.discourse {
|
||||||
|
.select-box-header {
|
||||||
|
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&.is-focused {
|
||||||
|
border: 1px solid $tertiary;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: $tertiary 0px 0px 6px 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-expanded {
|
||||||
|
.select-box-header {
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-body, .collection, {
|
||||||
|
border-radius: 0 0 3px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-wrapper {
|
||||||
|
border: 1px solid $tertiary;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: $tertiary 0px 0px 6px 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-box-row {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-highlighted .select-box-header {
|
||||||
|
border: 1px solid $tertiary;
|
||||||
|
box-shadow: $tertiary 0px 0px 6px 0px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1143,6 +1143,10 @@ en:
|
||||||
ctrl: 'Ctrl'
|
ctrl: 'Ctrl'
|
||||||
alt: 'Alt'
|
alt: 'Alt'
|
||||||
|
|
||||||
|
select_box:
|
||||||
|
no_data: No data
|
||||||
|
filter_placeholder: Search...
|
||||||
|
|
||||||
emoji_picker:
|
emoji_picker:
|
||||||
filter_placeholder: Search for emoji
|
filter_placeholder: Search for emoji
|
||||||
people: People
|
people: People
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
import componentTest from 'helpers/component-test';
|
||||||
|
|
||||||
|
moduleForComponent('select-box', {integration: true});
|
||||||
|
|
||||||
|
componentTest('updating the data refreshes the list', {
|
||||||
|
template: '{{select-box value=1 data=data}}',
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("data", [{id:1, text:"robin"}]);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-row .text").html().trim(), "robin");
|
||||||
|
|
||||||
|
andThen(() => this.set("data", [{id:1, text:"regis"}]));
|
||||||
|
andThen(() => assert.equal(this.$(".select-box-row .text").html().trim(), "regis"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('accepts a value by reference', {
|
||||||
|
template: '{{select-box value=value data=data}}',
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("value", 1);
|
||||||
|
this.set("data", [{id:1, text:"robin"}, {id: 2, text:"regis"}]);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-row.is-highlighted .text").html().trim(), "robin", "it highlights the row corresponding to the value");
|
||||||
|
|
||||||
|
click(this.$(".select-box-row[title='robin']"));
|
||||||
|
andThen(() => assert.equal(this.get("value"), 1, "it mutates the value"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('select-box can be filtered', {
|
||||||
|
template: '{{select-box filterable=true value=1 data=data}}',
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("data", [{id:1, text:"robin"}, {id: 2, text:"regis"}]);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
andThen(() => assert.equal(this.$(".filter-query").length, 1, "it has a search input"));
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
this.$(".filter-query").val("regis");
|
||||||
|
this.$(".filter-query").trigger("keyup");
|
||||||
|
});
|
||||||
|
andThen(() => assert.equal(this.$(".select-box-row").length, 1, "it filters results"));
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
this.$(".filter-query").val("");
|
||||||
|
this.$(".filter-query").trigger("keyup");
|
||||||
|
});
|
||||||
|
andThen(() => assert.equal(this.$(".select-box-row").length, 2, "it returns to original data when filter is empty"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('no default icon', {
|
||||||
|
template: '{{select-box}}',
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.equal(this.$(".select-box-header .icon").length, 0, "it doesn’t have an icon if not specified");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('customisable icon', {
|
||||||
|
template: '{{select-box icon="shower"}}',
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.equal(this.$(".select-box-header .icon").html().trim(), "<i class=\"fa fa-shower d-icon d-icon-shower\"></i>", "it has a the correct icon");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('default search icon', {
|
||||||
|
template: '{{select-box filterable=true}}',
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-filter .filter-icon").html().trim(), "<i class=\"fa fa-search d-icon d-icon-search\"></i>", "it has a the correct icon");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('with no search icon', {
|
||||||
|
template: '{{select-box filterable=true searchIcon=null}}',
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".search-icon").length, 0, "it has no icon");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('custom search icon', {
|
||||||
|
template: '{{select-box filterable=true filterIcon="shower"}}',
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-filter .filter-icon").html().trim(), "<i class=\"fa fa-shower d-icon d-icon-shower\"></i>", "it has a the correct icon");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('not filterable by default', {
|
||||||
|
template: '{{select-box}}',
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-filter").length, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
componentTest('select-box is expandable', {
|
||||||
|
template: '{{select-box}}',
|
||||||
|
test(assert) {
|
||||||
|
|
||||||
|
click(".select-box-header");
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box").hasClass("is-expanded"), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
click(".select-box-header");
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box").hasClass("is-expanded"), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('accepts custom id/text keys', {
|
||||||
|
template: '{{select-box value=value data=data idKey="identifier" textKey="name"}}',
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("value", 1);
|
||||||
|
this.set("data", [{identifier:1, name:"robin"}]);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-row.is-highlighted .text").html().trim(), "robin");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('doesn’t render collection content before first expand', {
|
||||||
|
template: '{{select-box value=1 data=data idKey="identifier" textKey="name"}}',
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("data", [{identifier:1, name:"robin"}]);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.equal(this.$(".select-box-body .collection").length, 0);
|
||||||
|
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-body .collection").length, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest('persists filter state when expandind/collapsing', {
|
||||||
|
template: '{{select-box value=1 data=data filterable=true}}',
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("data", [{id:1, text:"robin"}, {id:2, text:"régis"}]);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
this.$(".filter-query").val("rob");
|
||||||
|
this.$(".filter-query").trigger("keyup");
|
||||||
|
});
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-row").length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$().hasClass("is-expanded"), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-row").length, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
componentTest('supports options to limit size', {
|
||||||
|
template: '{{select-box maxWidth=100 maxCollectionHeight=20 data=data}}',
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("data", [{id:1, text:"robin"}]);
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.equal(this.$(".select-box-header").outerWidth(), 100, "it limits the width");
|
||||||
|
|
||||||
|
click(this.$(".select-box-header"));
|
||||||
|
andThen(() => {
|
||||||
|
assert.equal(this.$(".select-box-body").height(), 20, "it limits the height");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in New Issue