Bläddra i källkod

Highlight tags first in results even if other selections exist (#5812)

* refactor: check for unselected options when using tags and highlight that option first

* Switch to using a decorator for highlighting tags

This switches the implementation to use a decorator which is only
added when the `tags` option is set during initialization.
Functionally it has no change on the effect but it allows for the
logic to be more isolated. In the end it just keeps it more organized
and works towards cutting back on what is included in the core results.

This also changes the call for setting the `select2-data-tag` attribute
to properly set the attribute as a string instead of relying on the
boolean to string conversion.

* Fix tabbing in newly added decorator

* Add tests for tag focusing in results

Co-authored-by: Kevin Brown <[email protected]>
Martin Carlin 4 år sedan
förälder
incheckning
af79c80b22

+ 1 - 1
src/js/select2/data/tags.js

@@ -78,7 +78,7 @@ define([
 
       if (tag != null) {
         var $option = self.option(tag);
-        $option.attr('data-select2-tag', true);
+        $option.attr('data-select2-tag', 'true');
 
         self.addOptions([$option]);
 

+ 9 - 1
src/js/select2/defaults.js

@@ -33,6 +33,7 @@ define([
   './dropdown/selectOnClose',
   './dropdown/closeOnSelect',
   './dropdown/dropdownCss',
+  './dropdown/tagsSearchHighlight',
 
   './i18n/en'
 ], function ($,
@@ -49,7 +50,7 @@ define([
 
              Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
-             DropdownCSS,
+             DropdownCSS, TagsSearchHighlight,
 
              EnglishTranslation) {
   function Defaults () {
@@ -124,6 +125,13 @@ define([
           SelectOnClose
         );
       }
+
+      if (options.tags) {
+        options.resultsAdapter = Utils.Decorate(
+          options.resultsAdapter,
+          TagsSearchHighlight
+        );
+      }
     }
 
     if (options.dropdownAdapter == null) {

+ 31 - 0
src/js/select2/dropdown/tagsSearchHighlight.js

@@ -0,0 +1,31 @@
+define([
+  '../utils'
+], function (Utils) {
+  function TagsSearchHighlight () { }
+
+  TagsSearchHighlight.prototype.highlightFirstItem = function (decorated) {
+    var $options = this.$results
+    .find(
+      '.select2-results__option--selectable' +
+      ':not(.select2-results__option--selected)'
+    );
+
+    if ($options.length > 0) {
+      var $firstOption = $options.first();
+      var data = Utils.GetData($firstOption[0], 'data');
+      var firstElement = data.element;
+
+      if (firstElement && firstElement.getAttribute) {
+        if (firstElement.getAttribute('data-select2-tag') === 'true') {
+          $firstOption.trigger('mouseenter');
+
+          return;
+        }
+      }
+    }
+
+    decorated.call(this);
+  };
+
+  return TagsSearchHighlight;
+});

+ 110 - 0
tests/results/focusing-tests.js

@@ -239,3 +239,113 @@ test('!scrollAfterSelect does not trigger results:focus', function (assert) {
 
   container.trigger('select', {});
 });
+
+test('tag result is highlighted with no other selections', function (assert) {
+  assert.expect(2);
+
+  var $ = require('jquery');
+
+  var $select = $('<select></select>');
+  var $parent = $('<div></div>');
+
+  var $container = $('<span></span>');
+  var container = new MockContainer();
+
+  $parent.appendTo($('#qunit-fixture'));
+  $select.appendTo($parent);
+
+  var Utils = require('select2/utils');
+  var Options = require('select2/options');
+
+  var Results = require('select2/results');
+  var Tags = require('select2/dropdown/tagsSearchHighlight');
+  var TagResults = Utils.Decorate(Results, Tags);
+
+  var results = new TagResults($select, new Options({}));
+
+  // Fake the data adapter for the `setClasses` method
+  results.data = {};
+  results.data.current = function (callback) {
+    callback([]);
+  };
+
+  results.render();
+
+  results.bind(container, $container);
+
+  results.on('results:focus', function (params) {
+    assert.equal(params.data.id, 'tag');
+    assert.equal(params.data.text, 'Tag');
+  });
+
+  var tagElement = $('<option data-select2-tag="true"></option>')[0];
+
+  container.trigger('results:all', {
+    data: {
+      results: [
+        {
+          id: 'tag',
+          text: 'Tag',
+          element: tagElement
+        }
+      ]
+    }
+  });
+});
+
+test('tag result is highlighted with other selections', function (assert) {
+  assert.expect(2);
+
+  var $ = require('jquery');
+
+  var $select = $('<select></select>');
+  var $parent = $('<div></div>');
+
+  var $container = $('<span></span>');
+  var container = new MockContainer();
+
+  $parent.appendTo($('#qunit-fixture'));
+  $select.appendTo($parent);
+
+  var Utils = require('select2/utils');
+  var Options = require('select2/options');
+
+  var Results = require('select2/results');
+  var Tags = require('select2/dropdown/tagsSearchHighlight');
+  var TagResults = Utils.Decorate(Results, Tags);
+
+  var results = new TagResults($select, new Options({}));
+
+  // Fake the data adapter for the `setClasses` method
+  results.data = {};
+  results.data.current = function (callback) {
+    callback([{ id: 'test' }]);
+  };
+
+  results.render();
+
+  results.bind(container, $container);
+
+  results.on('results:focus', function (params) {
+    assert.equal(params.data.id, 'tag');
+    assert.equal(params.data.text, 'Tag');
+  });
+
+  var tagElement = $('<option data-select2-tag="true"></option>')[0];
+
+  container.trigger('results:all', {
+    data: {
+      results: [
+        {
+          id: 'tag',
+          text: 'Tag',
+          element: tagElement
+        },
+        {
+          id: 'test',
+          text: 'Test'
+        }
+      ]
+    }
+  });
+});