Skip to content

Commit

Permalink
Highlight tags first in results even if other selections exist (#5812)
Browse files Browse the repository at this point in the history
* 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 <kevin@kevin-brown.com>
  • Loading branch information
Martin Carlin and kevin-brown committed Jan 22, 2021
1 parent 5e8699d commit af79c80
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/js/select2/data/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand Down
10 changes: 9 additions & 1 deletion src/js/select2/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ define([
'./dropdown/selectOnClose',
'./dropdown/closeOnSelect',
'./dropdown/dropdownCss',
'./dropdown/tagsSearchHighlight',

'./i18n/en'
], function ($,
Expand All @@ -49,7 +50,7 @@ define([

Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
DropdownCSS,
DropdownCSS, TagsSearchHighlight,

EnglishTranslation) {
function Defaults () {
Expand Down Expand Up @@ -124,6 +125,13 @@ define([
SelectOnClose
);
}

if (options.tags) {
options.resultsAdapter = Utils.Decorate(
options.resultsAdapter,
TagsSearchHighlight
);
}
}

if (options.dropdownAdapter == null) {
Expand Down
31 changes: 31 additions & 0 deletions src/js/select2/dropdown/tagsSearchHighlight.js
Original file line number Diff line number Diff line change
@@ -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 changes: 110 additions & 0 deletions tests/results/focusing-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
]
}
});
});

0 comments on commit af79c80

Please sign in to comment.