From 482924b161e821aa7a7b93268e6473ff8503dca9 Mon Sep 17 00:00:00 2001 From: Joffrey JAFFEUX Date: Sun, 13 Aug 2017 14:34:50 +0200 Subject: [PATCH] FEATURE: initial implementation of an ember native select --- .../admin/templates/customize-themes-show.hbs | 9 +- .../discourse/components/d-select-box.js.es6 | 7 + .../discourse/components/select-box.js.es6 | 211 +++++++++++++++ .../select-box/select-box-collection.js.es6 | 3 + .../select-box/select-box-filter.js.es6 | 3 + .../select-box/select-box-header.js.es6 | 23 ++ .../select-box/select-box-row.js.es6 | 34 +++ .../templates/components/select-box.hbs | 41 +++ .../select-box/select-box-collection.hbs | 17 ++ .../select-box/select-box-filter.hbs | 14 + .../select-box/select-box-header.hbs | 15 ++ .../components/select-box/select-box-row.hbs | 7 + .../common/components/select-box.scss | 246 ++++++++++++++++++ config/locales/client.en.yml | 4 + .../components/select-box-test.js.es6 | 224 ++++++++++++++++ 15 files changed, 853 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/d-select-box.js.es6 create mode 100644 app/assets/javascripts/discourse/components/select-box.js.es6 create mode 100644 app/assets/javascripts/discourse/components/select-box/select-box-collection.js.es6 create mode 100644 app/assets/javascripts/discourse/components/select-box/select-box-filter.js.es6 create mode 100644 app/assets/javascripts/discourse/components/select-box/select-box-header.js.es6 create mode 100644 app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/select-box.hbs create mode 100644 app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs create mode 100644 app/assets/javascripts/discourse/templates/components/select-box/select-box-filter.hbs create mode 100644 app/assets/javascripts/discourse/templates/components/select-box/select-box-header.hbs create mode 100644 app/assets/javascripts/discourse/templates/components/select-box/select-box-row.hbs create mode 100644 app/assets/stylesheets/common/components/select-box.scss create mode 100644 test/javascripts/components/select-box-test.js.es6 diff --git a/app/assets/javascripts/admin/templates/customize-themes-show.hbs b/app/assets/javascripts/admin/templates/customize-themes-show.hbs index 638d7f24d42..c9ff316d032 100644 --- a/app/assets/javascripts/admin/templates/customize-themes-show.hbs +++ b/app/assets/javascripts/admin/templates/customize-themes-show.hbs @@ -36,11 +36,10 @@

{{i18n "admin.customize.theme.color_scheme"}}

{{i18n "admin.customize.theme.color_scheme_select"}}

-

{{combo-box content=colorSchemes - nameProperty="name" - value=colorSchemeId - selectionIcon="paint-brush" - valueAttribute="id"}} +

{{d-select-box data=colorSchemes + textKey="name" + value=colorSchemeId + icon="paint-brush"}} {{#if colorSchemeChanged}} {{d-button action="changeScheme" class="btn-primary btn-small submit-edit" icon="check"}} {{d-button action="cancelChangeScheme" class="btn-small cancel-edit" icon="times"}} diff --git a/app/assets/javascripts/discourse/components/d-select-box.js.es6 b/app/assets/javascripts/discourse/components/d-select-box.js.es6 new file mode 100644 index 00000000000..6f222715e6b --- /dev/null +++ b/app/assets/javascripts/discourse/components/d-select-box.js.es6 @@ -0,0 +1,7 @@ +import SelectBoxComponent from "discourse/components/select-box"; + +export default SelectBoxComponent.extend({ + layoutName: "components/select-box", + + classNames: "discourse" +}); diff --git a/app/assets/javascripts/discourse/components/select-box.js.es6 b/app/assets/javascripts/discourse/components/select-box.js.es6 new file mode 100644 index 00000000000..80469785d69 --- /dev/null +++ b/app/assets/javascripts/discourse/components/select-box.js.es6 @@ -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")] + }; + }, +}); diff --git a/app/assets/javascripts/discourse/components/select-box/select-box-collection.js.es6 b/app/assets/javascripts/discourse/components/select-box/select-box-collection.js.es6 new file mode 100644 index 00000000000..922121c7088 --- /dev/null +++ b/app/assets/javascripts/discourse/components/select-box/select-box-collection.js.es6 @@ -0,0 +1,3 @@ +export default Ember.Component.extend({ + classNames: "select-box-collection" +}); diff --git a/app/assets/javascripts/discourse/components/select-box/select-box-filter.js.es6 b/app/assets/javascripts/discourse/components/select-box/select-box-filter.js.es6 new file mode 100644 index 00000000000..8c87886c961 --- /dev/null +++ b/app/assets/javascripts/discourse/components/select-box/select-box-filter.js.es6 @@ -0,0 +1,3 @@ +export default Ember.Component.extend({ + classNames: "select-box-filter" +}); diff --git a/app/assets/javascripts/discourse/components/select-box/select-box-header.js.es6 b/app/assets/javascripts/discourse/components/select-box/select-box-header.js.es6 new file mode 100644 index 00000000000..57d5f066ab1 --- /dev/null +++ b/app/assets/javascripts/discourse/components/select-box/select-box-header.js.es6 @@ -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")); + } + } +}); diff --git a/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 b/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 new file mode 100644 index 00000000000..2b56d94f2e8 --- /dev/null +++ b/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 @@ -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"); + } + }, +}); diff --git a/app/assets/javascripts/discourse/templates/components/select-box.hbs b/app/assets/javascripts/discourse/templates/components/select-box.hbs new file mode 100644 index 00000000000..c15fdd873a9 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/select-box.hbs @@ -0,0 +1,41 @@ + + +{{component selectBoxHeaderComponent + data=selectedData + focused=focused + caretUpIcon=caretUpIcon + caretDownIcon=caretDownIcon + onToggle=(action "onToggle") + icon=icon + expanded=expanded +}} + +

+ {{#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}} +
+ +
diff --git a/app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs b/app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs new file mode 100644 index 00000000000..268db6a407f --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs @@ -0,0 +1,17 @@ + diff --git a/app/assets/javascripts/discourse/templates/components/select-box/select-box-filter.hbs b/app/assets/javascripts/discourse/templates/components/select-box/select-box-filter.hbs new file mode 100644 index 00000000000..23984daac8b --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/select-box/select-box-filter.hbs @@ -0,0 +1,14 @@ +
+ {{input + tabindex="-1" + class="filter-query" + placeholder=filterPlaceholder + key-up=onFilterChange + }} + + {{#if filterIcon}} +
+ {{d-icon filterIcon}} +
+ {{/if}} +
diff --git a/app/assets/javascripts/discourse/templates/components/select-box/select-box-header.hbs b/app/assets/javascripts/discourse/templates/components/select-box/select-box-header.hbs new file mode 100644 index 00000000000..562e290fe20 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/select-box/select-box-header.hbs @@ -0,0 +1,15 @@ +
+ {{#if icon}} +
+ {{d-icon icon}} +
+ {{/if}} + + + {{data.text}} + + +
+ {{d-icon caretIcon}} +
+
diff --git a/app/assets/javascripts/discourse/templates/components/select-box/select-box-row.hbs b/app/assets/javascripts/discourse/templates/components/select-box/select-box-row.hbs new file mode 100644 index 00000000000..c7a99d1bf17 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/select-box/select-box-row.hbs @@ -0,0 +1,7 @@ +{{#if data.icon}} + {{d-icon data.icon}} +{{/if}} + +

+ {{data.text}} +

diff --git a/app/assets/stylesheets/common/components/select-box.scss b/app/assets/stylesheets/common/components/select-box.scss new file mode 100644 index 00000000000..8e298408d8d --- /dev/null +++ b/app/assets/stylesheets/common/components/select-box.scss @@ -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; + } +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 5d722ec5e42..14830dcea30 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1143,6 +1143,10 @@ en: ctrl: 'Ctrl' alt: 'Alt' + select_box: + no_data: No data + filter_placeholder: Search... + emoji_picker: filter_placeholder: Search for emoji people: People diff --git a/test/javascripts/components/select-box-test.js.es6 b/test/javascripts/components/select-box-test.js.es6 new file mode 100644 index 00000000000..ac0dcec359d --- /dev/null +++ b/test/javascripts/components/select-box-test.js.es6 @@ -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(), "", "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(), "", "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(), "", "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"); + }); + } +});