diff --git a/client/galaxy/scripts/components/DataDialog.vue b/client/galaxy/scripts/components/DataDialog.vue deleted file mode 100644 index 6e30d3a5c870..000000000000 --- a/client/galaxy/scripts/components/DataDialog.vue +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - Clear - - - - {{ errorMessage }} - - - - - - {{ data.item.hid }}: {{ data.value }} - - - {{ data.value ? data.value : "-" }} - - - {{ data.value ? data.value.substring(0, 16).replace("T", " ") : "-" }} - - - - View - - - - - - No search results found for: {{ this.filter }}. - - No entries. - - - Please wait... - - - - - Back - - - Ok - - Cancel - - - - - - diff --git a/client/galaxy/scripts/components/DataDialog/DataDialog.test.js b/client/galaxy/scripts/components/DataDialog/DataDialog.test.js new file mode 100644 index 000000000000..682ff54a4a46 --- /dev/null +++ b/client/galaxy/scripts/components/DataDialog/DataDialog.test.js @@ -0,0 +1,180 @@ +import sinon from "sinon"; +import { mount } from "@vue/test-utils"; +import DataDialog from "./DataDialog.vue"; +import { __RewireAPI__ as rewire } from "./DataDialog"; +import { Model } from "./model.js"; +import { UrlTracker } from "./utilities.js"; +import { Services } from "./services"; +import Vue from "vue"; + +const mockOptions = { + callback: () => {}, + host: "host", + root: "root", + history: "history" +}; + +describe("model.js", () => { + let result = null; + it("Model operations for single, no format", () => { + let model = new Model(); + try { + model.add({ idx: 1 }); + throw "Accepted invalid record."; + } catch (error) { + expect(error).to.equals("Invalid record with no ."); + } + model.add({ id: 1 }); + expect(model.count()).to.equals(1); + expect(model.exists(1)).to.equals(true); + model.add({ id: 2, tag: "tag" }); + expect(model.count()).to.equals(1); + expect(model.exists(1)).to.equals(false); + expect(model.exists(2)).to.equals(true); + result = model.finalize(); + expect(result.id).to.equals(2); + expect(result.tag).to.equals("tag"); + }); + it("Model operations for multiple, with format", () => { + let model = new Model({ multiple: true, format: "tag" }); + model.add({ id: 1, tag: "tag_1" }); + expect(model.count()).to.equals(1); + model.add({ id: 2, tag: "tag_2" }); + expect(model.count()).to.equals(2); + result = model.finalize(); + expect(result.length).to.equals(2); + expect(result[0]).to.equals("tag_1"); + expect(result[1]).to.equals("tag_2"); + model.add({ id: 1 }); + expect(model.count()).to.equals(1); + result = model.finalize(); + expect(result[0]).to.equals("tag_2"); + }); +}); + +describe("utilities.js/UrlTracker", () => { + it("Test url tracker", () => { + let urlTracker = new UrlTracker("url_initial"); + let url = urlTracker.getUrl(); + expect(url).to.equals("url_initial"); + expect(urlTracker.atRoot()).to.equals(true); + url = urlTracker.getUrl("url_1"); + expect(url).to.equals("url_1"); + expect(urlTracker.atRoot()).to.equals(false); + url = urlTracker.getUrl("url_2"); + expect(url).to.equals("url_2"); + expect(urlTracker.atRoot()).to.equals(false); + url = urlTracker.getUrl(); + expect(url).to.equals("url_1"); + expect(urlTracker.atRoot()).to.equals(false); + url = urlTracker.getUrl(); + expect(url).to.equals("url_initial"); + expect(urlTracker.atRoot()).to.equals(true); + }); +}); + +describe("services/Services:isDataset", () => { + it("Test dataset identifier", () => { + let services = new Services(mockOptions); + expect(services.isDataset({})).to.equals(false); + expect(services.isDataset({ history_content_type: "dataset" })).to.equals(true); + expect(services.isDataset({ history_content_type: "xyz" })).to.equals(false); + expect(services.isDataset({ type: "file" })).to.equals(true); + expect(services.getRecord({ hid: 1, history_content_type: "dataset" }).isDataset).to.equals(true); + expect(services.getRecord({ hid: 2, history_content_type: "xyz" }).isDataset).to.equals(false); + expect(services.getRecord({ type: "file" }).isDataset).to.equals(true); + }); +}); + +describe("services.js/Services", () => { + it("Test data population from raw data", () => { + let rawData = { + hid: 1, + id: 1, + history_id: 0, + name: "name_1" + }; + let services = new Services(mockOptions); + let items = services.getItems(rawData); + expect(items.length).to.equals(1); + let first = items[0]; + expect(first.name).to.equals("1: name_1"); + expect(first.download).to.equals("host/api/histories/0/contents/1/display"); + }); +}); + +describe("DataDialog.vue", () => { + let stub; + let wrapper; + let emitted; + + let rawData = [ + { + id: 1, + hid: 1, + name: "dataset_1", + history_content_type: "dataset" + }, + { + id: 2, + name: "dataset_2", + type: "file" + }, + { + id: 3, + hid: 3, + name: "collection_1", + history_content_type: "dataset_collection" + } + ]; + + let mockServices = class { + get(url) { + let services = new Services(mockOptions); + let items = services.getItems(rawData); + return new Promise((resolve, reject) => { + resolve(items); + }); + } + }; + + beforeEach(() => { + rewire.__Rewire__("Services", mockServices); + }); + + afterEach(() => { + if (stub) stub.restore(); + }); + + it("loads correctly, shows alert", () => { + wrapper = mount(DataDialog, { + propsData: mockOptions + }); + emitted = wrapper.emitted(); + expect(wrapper.classes()).contain("data-dialog-modal"); + expect(wrapper.find(".fa-spinner").text()).to.equals(""); + expect(wrapper.find(".btn-secondary").text()).to.equals("Clear"); + expect(wrapper.find(".btn-primary").text()).to.equals("Ok"); + expect(wrapper.contains(".fa-spinner")).to.equals(true); + return Vue.nextTick().then(() => { + expect(wrapper.findAll(".fa-copy").length).to.equals(2); + expect(wrapper.findAll(".fa-file-o").length).to.equals(2); + }); + }); + + it("loads correctly, shows datasets and folders", () => { + wrapper = mount(DataDialog, { + propsData: mockOptions + }); + emitted = wrapper.emitted(); + expect(wrapper.classes()).contain("data-dialog-modal"); + expect(wrapper.find(".fa-spinner").text()).to.equals(""); + expect(wrapper.find(".btn-secondary").text()).to.equals("Clear"); + expect(wrapper.find(".btn-primary").text()).to.equals("Ok"); + expect(wrapper.contains(".fa-spinner")).to.equals(true); + return Vue.nextTick().then(() => { + expect(wrapper.findAll(".fa-copy").length).to.equals(2); + expect(wrapper.findAll(".fa-file-o").length).to.equals(2); + }); + }); +}); diff --git a/client/galaxy/scripts/components/DataDialog/DataDialog.vue b/client/galaxy/scripts/components/DataDialog/DataDialog.vue new file mode 100644 index 000000000000..04abd4d86a26 --- /dev/null +++ b/client/galaxy/scripts/components/DataDialog/DataDialog.vue @@ -0,0 +1,160 @@ + + + + + + + + + Please wait... + + + + + Back + + + Ok + + Cancel + + + + + + diff --git a/client/galaxy/scripts/components/DataDialog/DataDialogSearch.vue b/client/galaxy/scripts/components/DataDialog/DataDialogSearch.vue new file mode 100644 index 000000000000..fc6a6f4ec07a --- /dev/null +++ b/client/galaxy/scripts/components/DataDialog/DataDialogSearch.vue @@ -0,0 +1,29 @@ + + + + + Clear + + + + + diff --git a/client/galaxy/scripts/components/DataDialog/DataDialogTable.vue b/client/galaxy/scripts/components/DataDialog/DataDialogTable.vue new file mode 100644 index 000000000000..c3ad745bdab9 --- /dev/null +++ b/client/galaxy/scripts/components/DataDialog/DataDialogTable.vue @@ -0,0 +1,115 @@ + + + + + + {{ data.value ? data.value : "-" }} + + + {{ data.value ? data.value : "-" }} + + + {{ data.value ? data.value : "-" }} + + + + View + + + + + + No search results found for: {{ this.filter }}. + + No entries. + + + + + + diff --git a/client/galaxy/scripts/components/DataDialog/model.js b/client/galaxy/scripts/components/DataDialog/model.js new file mode 100644 index 000000000000..072445a0f5fa --- /dev/null +++ b/client/galaxy/scripts/components/DataDialog/model.js @@ -0,0 +1,52 @@ +export class Model { + constructor(options = {}) { + this.values = {}; + this.multiple = options.multiple || false; + this.format = options.format || null; + } + + /** Adds a new record to the value stack **/ + add(record) { + if (!this.multiple) { + this.values = {}; + } + let key = record && record.id; + if (key) { + if (!this.values[key]) { + this.values[key] = record; + } else { + delete this.values[key]; + } + } else { + throw "Invalid record with no ."; + } + } + + /** Returns the number of added records **/ + count() { + return Object.keys(this.values).length; + } + + /** Returns true if a record is available for a given key **/ + exists(key) { + return !!this.values[key]; + } + + /** Finalizes the results from added records **/ + finalize() { + let results = []; + Object.values(this.values).forEach(v => { + let value = null; + if (this.format) { + value = v[this.format]; + } else { + value = v; + } + results.push(value); + }); + if (results.length > 0 && !this.multiple) { + results = results[0]; + } + return results; + } +} diff --git a/client/galaxy/scripts/components/DataDialog/services.js b/client/galaxy/scripts/components/DataDialog/services.js new file mode 100644 index 000000000000..debf80724c7a --- /dev/null +++ b/client/galaxy/scripts/components/DataDialog/services.js @@ -0,0 +1,82 @@ +import axios from "axios"; + +/** Data populator traverses raw server responses **/ +export class Services { + constructor(options = {}) { + this.root = options.root; + this.host = options.host; + } + + get(url) { + return new Promise((resolve, reject) => { + axios + .get(url) + .then(response => { + let items = this.getItems(response.data); + resolve(items); + }) + .catch(e => { + let errorMessage = "Request failed."; + if (e.response) { + errorMessage = e.response.data.err_msg || `${e.response.statusText} (${e.response.status})`; + } + reject(errorMessage); + }); + }); + } + + /** Returns the formatted results **/ + getItems(data) { + let items = []; + let stack = [data]; + while (stack.length > 0) { + let root = stack.pop(); + if (Array.isArray(root)) { + root.forEach(element => { + stack.push(element); + }); + } else if (root.elements) { + stack.push(root.elements); + } else if (root.object) { + stack.push(root.object); + } else { + let record = this.getRecord(root); + if (record) { + items.push(record); + } + } + } + return items; + } + + /** Populate record data from raw record source **/ + getRecord(record) { + record.details = record.extension || record.description; + record.time = record.update_time || record.create_time; + record.isDataset = this.isDataset(record); + if (record.time) { + record.time = record.time.substring(0, 16).replace("T", " "); + } + if (record.model_class == "Library") { + record.url = `${this.root}api/libraries/${record.id}/contents`; + return record; + } else if (record.hid) { + record.name = `${record.hid}: ${record.name}`; + record.download = `${this.host}/api/histories/${record.history_id}/contents/${record.id}/display`; + return record; + } else if (record.type == "file") { + if (record.name && record.name[0] === "/") { + record.name = record.name.substring(1); + } + record.download = `${this.host}${this.root}api/libraries/datasets/download/uncompressed?ld_ids=${ + record.id + }`; + return record; + } + } + + /** Checks if record is a dataset or drillable **/ + isDataset(record) { + return record.history_content_type == "dataset" || record.type == "file"; + } +} diff --git a/client/galaxy/scripts/components/DataDialog/utilities.js b/client/galaxy/scripts/components/DataDialog/utilities.js new file mode 100644 index 000000000000..8ebcd1c3132c --- /dev/null +++ b/client/galaxy/scripts/components/DataDialog/utilities.js @@ -0,0 +1,28 @@ +/** This helps track urls for data drilling **/ +export class UrlTracker { + constructor(root) { + this.root = root; + this.navigation = []; + } + + /** Returns urls for data drilling **/ + getUrl(url) { + if (url) { + this.navigation.push(url); + } else { + this.navigation.pop(); + let navigationLength = this.navigation.length; + if (navigationLength > 0) { + url = this.navigation[navigationLength - 1]; + } else { + url = this.root; + } + } + return url; + } + + /** Returns true if the last data is at navigation root **/ + atRoot() { + return this.navigation.length == 0; + } +} diff --git a/client/galaxy/scripts/layout/data.js b/client/galaxy/scripts/layout/data.js index e62980954907..44f4cad5b6eb 100644 --- a/client/galaxy/scripts/layout/data.js +++ b/client/galaxy/scripts/layout/data.js @@ -1,5 +1,5 @@ import $ from "jquery"; -import DataDialog from "components/DataDialog.vue"; +import DataDialog from "components/DataDialog/DataDialog.vue"; import Vue from "vue"; import { getGalaxyInstance } from "app"; import { getAppRoot } from "onload/loadConfig"; @@ -10,7 +10,14 @@ export default class Data { * @param {function} callback - Result function called with selection */ dialog(callback, options = {}) { - options.callback = callback; + let galaxy = getGalaxyInstance(); + let host = `${window.location.protocol}//${window.location.hostname}:${window.location.port}`; + Object.assign(options, { + callback: callback, + history: galaxy.currHistoryPanel && galaxy.currHistoryPanel.model.id, + root: galaxy.root, + host: host + }); var instance = Vue.extend(DataDialog); var vm = document.createElement("div"); $("body").append(vm); diff --git a/client/galaxy/scripts/mvc/ui/ui-options.js b/client/galaxy/scripts/mvc/ui/ui-options.js index 7b7ab975cba6..03fe661287a1 100644 --- a/client/galaxy/scripts/mvc/ui/ui-options.js +++ b/client/galaxy/scripts/mvc/ui/ui-options.js @@ -12,6 +12,7 @@ var Base = Backbone.View.extend({ (options && options.model) || new Backbone.Model({ visible: true, + cls: null, data: [], id: Utils.uid(), error_text: "No options available.", @@ -36,6 +37,7 @@ var Base = Backbone.View.extend({ .empty() .removeClass() .addClass("ui-options") + .addClass(this.model.get("cls")) .append((this.$message = $("").addClass("mt-2"))) .append((this.$menu = $("").addClass("ui-options-menu"))) .append((this.$options = $(this._template()))); @@ -154,6 +156,16 @@ var Base = Backbone.View.extend({ return this.$(".ui-option").length; }, + /** Shows the options */ + show: function() { + this.model.set("visible", true); + }, + + /** Hides the options */ + hide: function() { + this.model.set("visible", false); + }, + /** Set value to dom */ _setValue: function(new_value) { var self = this; @@ -260,7 +272,7 @@ RadioButton.View = Base.extend({ /** Template for a single option */ _templateOption: function(pair) { - var $el = $("").addClass("btn btn-secondary"); + var $el = $("").addClass("btn btn-secondary m-0"); if (pair.icon) { $el.append( $("") @@ -286,7 +298,7 @@ RadioButton.View = Base.extend({ /** Main template function */ _template: function() { - return $("").addClass("btn-group ui-radiobutton"); + return $("").addClass("btn-group ui-radiobutton d-flex"); } }); diff --git a/client/galaxy/scripts/mvc/ui/ui-select-content.js b/client/galaxy/scripts/mvc/ui/ui-select-content.js index b70c3eb6a8cb..3f3aae33364e 100644 --- a/client/galaxy/scripts/mvc/ui/ui-select-content.js +++ b/client/galaxy/scripts/mvc/ui/ui-select-content.js @@ -149,28 +149,11 @@ var View = Backbone.View.extend({ } ] }); - var $batch_div = $("") - .addClass("form-text text-muted") - .append($("").addClass("fa fa-sitemap")) - .append( - $("").html( - "This is a batch mode input field. Separate jobs will be triggered for each dataset selection." - ) - ); this.$batch = { - linked: $batch_div.clone(), - enabled: $batch_div + linked: $(this._templateBatch()).clone(), + enabled: $(this._templateBatch()) .clone() - .append( - $("") - .append( - $("") - .addClass("ui-form-title") - .html("Batch options:") - ) - .append(this.button_product.$el) - ) - .append($("").css("clear", "both")) + .append(this.button_product.$el) }; // add drag-drop event handlers @@ -186,7 +169,13 @@ var View = Backbone.View.extend({ this.lastenter === e.target && self.$el.removeClass("ui-dragover"); }); element.addEventListener("drop", e => { - self._handleDrop(e); + e.preventDefault(); + try { + let drop_data = JSON.parse(e.dataTransfer.getData("text"))[0]; + this._handleDropValues(drop_data); + } catch (e) { + this._handleDropStatus("danger"); + } }); // track current history elements @@ -234,7 +223,7 @@ var View = Backbone.View.extend({ /** Return the currently selected dataset values */ value: function(new_value) { - let Galaxy = getGalaxyInstance(); + let galaxy = getGalaxyInstance(); new_value !== undefined && this.model.set("value", new_value); var current = this.model.get("current"); if (this.config[current]) { @@ -248,7 +237,7 @@ var View = Backbone.View.extend({ if (details) { result.values.push(details); } else { - Galaxy.emit.debug( + galaxy.emit.debug( "ui-select-content::value()", `Requested details not found for '${id_list[i]}'.` ); @@ -260,7 +249,7 @@ var View = Backbone.View.extend({ } } } else { - Galaxy.emit.debug("ui-select-content::value()", `Invalid value/source '${new_value}'.`); + galaxy.emit.debug("ui-select-content::value()", `Invalid value/source '${new_value}'.`); } return null; }, @@ -269,22 +258,37 @@ var View = Backbone.View.extend({ _changeCurrent: function() { var self = this; _.each(this.fields, (field, i) => { + let cnf = self.config[i]; if (self.model.get("current") == i) { field.$el.show(); _.each(self.$batch, ($batchfield, batchmode) => { - $batchfield[self.config[i].batch == batchmode ? "show" : "hide"](); + if (cnf.batch == batchmode) { + $batchfield.show(); + } else { + $batchfield.hide(); + } }); + if (cnf.showdialog) { + self.button_dialog.show(); + } else { + self.button_dialog.hide(); + } self.button_type.value(i); } else { field.$el.hide(); } }); + if (this.fields.length > 1) { + this.button_type.show(); + } else { + this.button_type.hide(); + } }, /** Change of type */ _changeType: function() { - let Galaxy = getGalaxyInstance(); - var self = this; + let self = this; + let galaxy = getGalaxyInstance(); // identify selector type identifier i.e. [ flavor ]_[ type ]_[ multiple ] var config_id = @@ -295,7 +299,7 @@ var View = Backbone.View.extend({ this.config = Configurations[config_id]; } else { this.config = Configurations["data"]; - Galaxy.emit.debug("ui-select-content::_changeType()", `Invalid configuration/type id '${config_id}'.`); + galaxy.emit.debug("ui-select-content::_changeType()", `Invalid configuration/type id '${config_id}'.`); } // prepare extension component of error message @@ -303,7 +307,7 @@ var View = Backbone.View.extend({ var extensions = Utils.textify(this.model.get("extensions")); var src_labels = this.model.get("src_labels"); - // build views + // build radio button for data selectors this.fields = []; this.button_data = []; _.each(this.config, (c, i) => { @@ -329,24 +333,47 @@ var View = Backbone.View.extend({ this.button_type = new Ui.RadioButton.View({ value: this.model.get("current"), data: this.button_data, + cls: "mr-2", onchange: function(value) { self.model.set("current", value); self.trigger("change"); } }); + // build data dialog button + this.button_dialog = new Ui.Button({ + icon: "fa-folder-open-o", + tooltip: "Browse Datasets", + cls: "ml-2", + onclick: () => { + let current = this.model.get("current"); + let cnf = this.config[current]; + galaxy.data.dialog( + response => { + this._handleDropValues(response, false); + }, + { + multiple: cnf.multiple, + format: null, + library: false + } + ); + } + }); + // append views - this.$el.empty(); - var button_width = 0; - if (this.fields.length > 1) { - this.$el.append(this.button_type.$el); - button_width = `${Math.max(0, this.fields.length * 40)}px`; - } + let $fields = $("").addClass("w-100"); + this.$el + .empty() + .addClass("d-flex flex-row") + .append($("").append(this.button_type.$el)) + .append($fields) + .append($("").append(this.button_dialog.$el)); _.each(this.fields, field => { - self.$el.append(field.$el.css({ "margin-left": button_width })); + $fields.append(field.$el); }); _.each(this.$batch, ($batchfield, batchmode) => { - self.$el.append($batchfield.css({ "margin-left": button_width })); + $fields.append($batchfield); }); this.model.set("current", 0); this._changeCurrent(); @@ -412,52 +439,62 @@ var View = Backbone.View.extend({ } }, - /** Handles drop events e.g. from history panel */ - _handleDrop: function(ev) { - try { - var data = this.model.get("data"); - var current = this.model.get("current"); - var config = this.config[current]; - var field = this.fields[current]; - var drop_data = JSON.parse(ev.dataTransfer.getData("text"))[0]; - var new_id = drop_data.id; - var new_src = drop_data.history_content_type == "dataset_collection" ? "hdca" : "hda"; - var new_value = { id: new_id, src: new_src }; - if (data && drop_data.history_id) { - if (!_.findWhere(data[new_src], new_value)) { - data[new_src].push({ - id: new_id, - src: new_src, - hid: drop_data.hid || "Dropped", - name: drop_data.hid ? drop_data.name : new_id, - keep: true, - tags: [] - }); + /** Source helper matches history_content_types to source types */ + _getSource: function(v) { + return v.history_content_type == "dataset_collection" ? "hdca" : "hda"; + }, + + /** Add values from drag/drop */ + _handleDropValues: function(drop_data, drop_partial = true) { + let data = this.model.get("data"); + let current = this.model.get("current"); + let config = this.config[current]; + let field = this.fields[current]; + if (data) { + let values = $.isArray(drop_data) ? drop_data : [drop_data]; + if (values.length > 0) { + let data_changed = false; + _.each(values, v => { + let new_id = v.id; + let new_src = (v.src = this._getSource(v)); + let new_value = { id: new_id, src: new_src }; + if (!_.findWhere(data[new_src], new_value)) { + data_changed = true; + data[new_src].push({ + id: new_id, + src: new_src, + hid: v.hid || "Selected", + name: v.hid ? v.name : new_id, + keep: true, + tags: [] + }); + } + }); + if (data_changed) { this._changeData(); } - if (config.src == new_src) { + let first_id = values[0].id; + let first_src = values[0].src; + if (config.src == first_src && drop_partial) { var current_value = field.value(); if (current_value && config.multiple) { - if (current_value.indexOf(new_id) == -1) { - current_value.push(new_id); - } + _.each(values, v => { + if (current_value.indexOf(v.id) == -1) { + current_value.push(v.id); + } + }); } else { - current_value = new_id; + current_value = first_id; } field.value(current_value); } else { - this.model.set("value", { values: [new_value] }); + this.model.set("value", { values: values }); this.model.trigger("change:value"); } this.trigger("change"); - this._handleDropStatus("success"); - } else { - this._handleDropStatus("danger"); } - } catch (e) { - this._handleDropStatus("danger"); } - ev.preventDefault(); + this._handleDropStatus("success"); }, /** Highlight drag result */ @@ -487,6 +524,16 @@ var View = Backbone.View.extend({ } } return result; + }, + + /** Template for batch mode execution options */ + _templateBatch: function() { + return ` + + + This is a batch mode input field. Separate jobs will be triggered for each dataset selection. + + `; } }); diff --git a/client/galaxy/scripts/qunit/tests/ui_tests.js b/client/galaxy/scripts/qunit/tests/ui_tests.js index 35c7d8618dbb..e60164f79862 100644 --- a/client/galaxy/scripts/qunit/tests/ui_tests.js +++ b/client/galaxy/scripts/qunit/tests/ui_tests.js @@ -726,15 +726,18 @@ QUnit.test("select-content", function(assert) { "Contains " + options.totalmultiple + " multiselect fields" ); assert.ok( - select.$el.children(".ui-options").find(".ui-option").length === - (options.selectfields > 1 ? options.selectfields : 0), - "Radio button count" + select.$el.find(".ui-options:first .ui-option").length === options.selectfields, + "Radio button count, expected " + options.selectfields ); assert.ok(select.$(".ui-select:first").css("display") == "block", "Check select visibility"); assert.ok( select.$(".ui-select:last").css("display") == (options.selectfields == 1 ? "block" : "none"), "Last select visibility" ); + /*assert.ok( + (select.button_dialog.$el.css("display") != "none") === options.showdialog, + "Data dialog button visible" + );*/ _testSelect("first", options); _testSelect("last", options); }; @@ -759,7 +762,8 @@ QUnit.test("select-content", function(assert) { lastvalue: "id2", lastlabel: "hid2: name2", lastlength: 3, - lastmultiple: false + lastmultiple: false, + showdialog: true }; _test(initial); @@ -775,7 +779,8 @@ QUnit.test("select-content", function(assert) { lastvalue: "id2", lastlabel: "hid2: name2", lastlength: 3, - lastmultiple: true + lastmultiple: true, + showdialog: true }); select.model.set("multiple", false); @@ -790,7 +795,8 @@ QUnit.test("select-content", function(assert) { lastvalue: "id2", lastlabel: "hid2: name2", lastlength: 3, - lastmultiple: false + lastmultiple: false, + showdialog: false }); select.model.set("type", "module_data_collection"); @@ -804,7 +810,8 @@ QUnit.test("select-content", function(assert) { lastvalue: "id2", lastlabel: "hid2: name2", lastlength: 3, - lastmultiple: true + lastmultiple: true, + showdialog: false }); select.model.set("type", "module_data"); @@ -818,7 +825,8 @@ QUnit.test("select-content", function(assert) { lastvalue: "id0", lastlabel: "hid0: name0", lastlength: 2, - lastmultiple: true + lastmultiple: true, + showdialog: true }); select.model.set("type", "data"); diff --git a/client/galaxy/style/scss/ui.scss b/client/galaxy/style/scss/ui.scss index 1e2847c840f6..5d19dd672796 100644 --- a/client/galaxy/style/scss/ui.scss +++ b/client/galaxy/style/scss/ui.scss @@ -421,6 +421,9 @@ $ui-margin-horizontal-large: $margin-v * 2; -webkit-appearance: none; -moz-border-radius: $border-radius-base; line-height: 1.5rem; + .select2-chosen { + white-space: normal; + } .select2-arrow { display: none; } @@ -449,12 +452,6 @@ $ui-margin-horizontal-large: $margin-v * 2; } } -.ui-select-content { - .ui-options { - @extend .float-left; - } -} - .ui-dragover { border-radius: 3px; border: 2px solid $table-border-color;