diff --git a/core/core.libraries.yml b/core/core.libraries.yml index 0c212ce..3908c80 100644 --- a/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -102,7 +102,7 @@ drupal.autocomplete: - core/drupal - core/drupalSettings - core/drupal.ajax - - core/jquery.ui.autocomplete + - core/select2 drupal.batch: version: VERSION @@ -808,3 +808,19 @@ underscore: gpl-compatible: true js: assets/vendor/underscore/underscore.js: { weight: -20 } + +select2: + remote: https://github.com/ivaynberg/select2 + version: 3.5.2 + license: + name: GNU-GPL-2.0-or-later + url: https://github.com/ivaynberg/select2/blob/master/LICENSE + gpl-compatible: true + js: + assets/vendor/select2/select2.js: {} + misc/select2.js: {} + css: + theme: + assets/vendor/select2/select2.css: {} + dependencies: + - core/jquery diff --git a/core/lib/Drupal/Core/Installer/Form/SelectLanguageForm.php b/core/lib/Drupal/Core/Installer/Form/SelectLanguageForm.php index 79d9082..0e67b5e 100644 --- a/core/lib/Drupal/Core/Installer/Form/SelectLanguageForm.php +++ b/core/lib/Drupal/Core/Installer/Form/SelectLanguageForm.php @@ -67,6 +67,8 @@ public function buildForm(array $form, FormStateInterface $form_state, $install_ '#options' => $select_options, // Use the browser detected language as default or English if nothing found. '#default_value' => !empty($browser_langcode) ? $browser_langcode : 'en', + // Use the Select2 library. + '#select2' => TRUE, ); $form['help'] = array( '#type' => 'item', diff --git a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php index 750e967..146ee97 100644 --- a/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php +++ b/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php @@ -199,6 +199,8 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#description' => $this->t('By default, dates in this site will be displayed in the chosen time zone.'), '#weight' => 5, '#attributes' => array('class' => array('timezone-detect')), + // Use the Select2 library. + '#select2' => TRUE, ); $form['update_notifications'] = array( diff --git a/core/lib/Drupal/Core/Render/Element/FormElement.php b/core/lib/Drupal/Core/Render/Element/FormElement.php index 401c850..b6d1789 100644 --- a/core/lib/Drupal/Core/Render/Element/FormElement.php +++ b/core/lib/Drupal/Core/Render/Element/FormElement.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Render\Element; +use Drupal\Component\Serialization\Json; use Drupal\Core\Form\FormStateInterface; /** @@ -123,6 +124,49 @@ public static function processAutocomplete(&$element, FormStateInterface $form_s $element['#attached']['library'][] = 'core/drupal.autocomplete'; // Provide a data attribute for the JavaScript behavior to bind to. $element['#attributes']['data-autocomplete-path'] = $path; + + // Provide a data attribute which stores extra Select2 configuration. + $options = isset($element['#autocomplete_options']) ? $element['#autocomplete_options'] : array(); + $element['#attributes']['data-autocomplete-options'] = Json::encode($options); + } + + return $element; + } + + /** + * Integrates form elements with the Select2 JavaScript library. + * + * @param array $element + * The form element to process. Properties used: + * - #select2: Whether to use the Select2 library for this form element. If + * the value is TRUE, the Select2 library is initialized with its default + * options. If the value is an array, each key and value will map to the + * corresponding Select2 configuration. + * @see http://ivaynberg.github.io/select2/#documentation for the list of + * possible configuration options. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param array $complete_form + * The complete form structure. + * + * @return array + * The form element. + */ + public static function processSelect2(&$element, FormStateInterface $form_state, &$complete_form) { + if (!empty($element['#select2'])) { + // Provide some default options. + // @todo Figure out if we need more defaults here. + $configuration = array( + 'width' => 'resolve', + ); + if (is_array($element['#select2'])) { + $configuration = $element['#select2'] + $configuration; + } + + $element['#attributes']['class'][] = 'form-select2'; + $element['#attached']['library'][] = 'core/select2'; + // Provide a data attribute which stores the Select2 configuration array. + $element['#attributes']['data-drupal-select2'] = Json::encode($configuration); } return $element; diff --git a/core/lib/Drupal/Core/Render/Element/Select.php b/core/lib/Drupal/Core/Render/Element/Select.php index 70f14d6..4507fdb 100644 --- a/core/lib/Drupal/Core/Render/Element/Select.php +++ b/core/lib/Drupal/Core/Render/Element/Select.php @@ -31,6 +31,9 @@ public function getInfo() { '#process' => array( array($class, 'processSelect'), array($class, 'processAjaxForm'), + // Not to be confused with the processor above, this one is about the + // Select2 Javascript library. + array($class, 'processSelect2'), ), '#pre_render' => array( array($class, 'preRenderSelect'), diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js index af64521..35df8bd 100644 --- a/core/misc/autocomplete.js +++ b/core/misc/autocomplete.js @@ -2,16 +2,14 @@ "use strict"; - var autocomplete; - /** - * Helper splitting terms from the autocomplete value. + * Helper for splitting terms from the autocomplete value. * * @param {String} value * * @return {Array} */ - function autocompleteSplitValues(value) { + function splitValues(value) { // We will match the value against comma-separated terms. var result = []; var quote = false; @@ -41,132 +39,46 @@ } /** - * Returns the last value of an multi-value textfield. - * - * @param {String} terms - * - * @return {String} + * Sanitizes and splits the initial value of an autocomplete form element in + * the form required by Select2. */ - function extractLastTerm(terms) { - return autocomplete.splitValues(terms).pop(); + function prepareInitialValue(element, callback) { + var idInText = element.data('autocompleteOptions')['id_in_text'] || false; + + var data = splitValues(element.val()).map(function (value) { + // Strip the identifier from the default value of an autocomplete + // element. The value is expected to be in the form "text (id)". + var textValue = idInText ? value.match(/"?(.+) \(\d+|[\w.]+\)"?/)[1] : value; + return {id: value, text: window.Select2.util.escapeMarkup(textValue.replace('""', '"', 'g'))}; + }); + callback(data); } - /** - * The search handler is called before a search is performed. - * - * @param {Object} event - * - * @return {Boolean} - */ - function searchHandler(event) { - // Only search when the term is two characters or larger. - var term = autocomplete.extractLastTerm(event.target.value); - return term.length >= autocomplete.minLength; - } - - /** - * jQuery UI autocomplete source callback. - * - * @param {Object} request - * @param {Function} response - */ - function sourceData(request, response) { - /*jshint validthis:true */ - var elementId = this.element.attr('id'); - - if (!(elementId in autocomplete.cache)) { - autocomplete.cache[elementId] = {}; - } - - /** - * Filter through the suggestions removing all terms already tagged and - * display the available terms to the user. - * - * @param {Object} suggestions - */ - function showSuggestions(suggestions) { - var tagged = autocomplete.splitValues(request.term); - for (var i = 0, il = tagged.length; i < il; i++) { - var index = suggestions.indexOf(tagged[i]); - if (index >= 0) { - suggestions.splice(index, 1); - } + // Generic Select2 options that do not depend on any autocomplete element + // specific configuration. + var select2Options = { + tokenSeparators: [","], + tags: [], + minimumInputLength: 1, + cache: true, + width: 'resolve', + createSearchChoice: function (term) { + return {id: term, text: term}; + }, + initSelection: prepareInitialValue, + ajax: { + dataType: 'json', + quietMillis: 200, + data: function (term, page) { + return {q: term}; + }, + results: function (data, page) { + return {results: data}; } - response(suggestions); - } - - /** - * Transforms the data object into an array and update autocomplete results. - * - * @param {Object} data - */ - function sourceCallbackHandler(data) { - autocomplete.cache[elementId][term] = data; - - // Send the new string array of terms to the jQuery UI list. - showSuggestions(data); - } - - // Get the desired term and construct the autocomplete URL for it. - var term = autocomplete.extractLastTerm(request.term); - - // Check if the term is already cached. - if (autocomplete.cache[elementId].hasOwnProperty(term)) { - showSuggestions(autocomplete.cache[elementId][term]); - } - else { - var options = $.extend({ success: sourceCallbackHandler, data: { q: term } }, autocomplete.ajax); - /*jshint validthis:true */ - $.ajax(this.element.attr('data-autocomplete-path'), options); - } - } - - /** - * Handles an autocompletefocus event. - * - * @return {Boolean} - */ - function focusHandler() { - return false; - } - - /** - * Handles an autocompleteselect event. - * - * @param {Object} event - * @param {Object} ui - * - * @return {Boolean} - */ - function selectHandler(event, ui) { - var terms = autocomplete.splitValues(event.target.value); - // Remove the current input. - terms.pop(); - // Add the selected item. - if (ui.item.value.search(",") > 0) { - terms.push('"' + ui.item.value + '"'); - } - else { - terms.push(ui.item.value); - } - event.target.value = terms.join(', '); - // Return false to tell jQuery UI that we've filled in the value already. - return false; - } - - /** - * Override jQuery UI _renderItem function to output HTML by default. - * - * @param {Object} ul - * @param {Object} item - * - * @return {Object} - */ - function renderItem(ul, item) { - return $("
  • ") - .append($("").html(item.label)) - .appendTo(ul); - } + }, + // We do not want to escape markup since we are displaying html. + escapeMarkup: function (markup) { return markup; } + }; /** * Attaches the autocomplete behavior to all required fields. @@ -174,45 +86,17 @@ Drupal.behaviors.autocomplete = { attach: function (context) { // Act on textfields with the "form-autocomplete" class. - var $autocomplete = $(context).find('input.form-autocomplete').once('autocomplete'); - if ($autocomplete.length) { - // Use jQuery UI Autocomplete on the textfield. - $autocomplete.autocomplete(autocomplete.options) - .data("ui-autocomplete") - ._renderItem = autocomplete.options.renderItem; - } - }, - detach: function (context, settings, trigger) { - if (trigger === 'unload') { - $(context).find('input.form-autocomplete') - .removeOnce('autocomplete') - .autocomplete('destroy'); - } - } - }; + $(context).find('input.form-autocomplete').once('autocomplete') + .each(function () { + // Merge the default options with the user-defined ones. + var autocompleteOptions = $.extend({}, select2Options, JSON.parse(this.getAttribute('data-autocomplete-options'))); - /** - * Autocomplete object implementation. - */ - autocomplete = { - cache: {}, - // Exposes methods to allow overriding by contrib. - minLength: 1, - splitValues: autocompleteSplitValues, - extractLastTerm: extractLastTerm, - // jQuery UI autocomplete options. - options: { - source: sourceData, - focus: focusHandler, - search: searchHandler, - select: selectHandler, - renderItem: renderItem - }, - ajax: { - dataType: 'json' + // Set the element-specific AJAX url. + autocompleteOptions.ajax.url = this.getAttribute('data-autocomplete-path'); + + $(this).select2(autocompleteOptions); + }); } }; - Drupal.autocomplete = autocomplete; - })(jQuery, Drupal); diff --git a/core/misc/select2.js b/core/misc/select2.js new file mode 100644 index 0000000..db320ec --- /dev/null +++ b/core/misc/select2.js @@ -0,0 +1,17 @@ +(function ($, Drupal) { + + "use strict"; + + /** + * Attaches the Select2 behavior to all form elements that requested it. + */ + Drupal.behaviors.select2 = { + attach: function (context) { + $(context).find('[data-drupal-select2]').once('drupal-select2') + .each(function () { + $(this).select2(JSON.parse(this.getAttribute('data-drupal-select2'))); + }); + } + }; + +})(jQuery, Drupal); diff --git a/core/modules/entity_reference/src/EntityReferenceAutocomplete.php b/core/modules/entity_reference/src/EntityReferenceAutocomplete.php index ffc9995..f93e144 100644 --- a/core/modules/entity_reference/src/EntityReferenceAutocomplete.php +++ b/core/modules/entity_reference/src/EntityReferenceAutocomplete.php @@ -62,8 +62,6 @@ public function __construct(EntityManagerInterface $entity_manager, SelectionPlu * @param string $entity_id * (optional) The entity ID the entity reference field is attached to. * Defaults to ''. - * @param string $prefix - * (optional) A prefix for all the keys returned by this function. * @param string $string * (optional) The label of the entity to query by. * @@ -75,7 +73,7 @@ public function __construct(EntityManagerInterface $entity_manager, SelectionPlu * * @see \Drupal\entity_reference\EntityReferenceController */ - public function getMatches(FieldDefinitionInterface $field_definition, $entity_type, $bundle, $entity_id = '', $prefix = '', $string = '') { + public function getMatches(FieldDefinitionInterface $field_definition, $entity_type, $bundle, $entity_id = '', $string = '') { $matches = array(); $entity = NULL; @@ -102,7 +100,7 @@ public function getMatches(FieldDefinitionInterface $field_definition, $entity_t $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(String::decodeEntities(strip_tags($key))))); // Names containing commas or quotes must be wrapped in quotes. $key = Tags::encode($key); - $matches[] = array('value' => $prefix . $key, 'label' => $label); + $matches[] = array('id' => $key, 'text' => $label); } } } diff --git a/core/modules/entity_reference/src/EntityReferenceController.php b/core/modules/entity_reference/src/EntityReferenceController.php index afc4cc0..c83d74c 100644 --- a/core/modules/entity_reference/src/EntityReferenceController.php +++ b/core/modules/entity_reference/src/EntityReferenceController.php @@ -86,16 +86,9 @@ public function handleAutocomplete(Request $request, $type, $field_name, $entity // Get the typed string, if exists from the URL. $items_typed = $request->query->get('q'); $items_typed = Tags::explode($items_typed); - $last_item = Unicode::strtolower(array_pop($items_typed)); + $typed_string = Unicode::strtolower(array_pop($items_typed)); - $prefix = ''; - // The user entered a comma-separated list of entity labels, so we generate - // a prefix. - if ($type == 'tags' && !empty($last_item)) { - $prefix = count($items_typed) ? Tags::implode($items_typed) . ', ' : ''; - } - - $matches = $this->entityReferenceAutocomplete->getMatches($field_definition, $entity_type, $bundle_name, $entity_id, $prefix, $last_item); + $matches = $this->entityReferenceAutocomplete->getMatches($field_definition, $entity_type, $bundle_name, $entity_id, $typed_string); return new JsonResponse($matches); } diff --git a/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php b/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php index 40d0aaf..b392f45 100644 --- a/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php +++ b/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php @@ -93,6 +93,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen '#default_value' => implode(', ', $this->getLabels($items, $delta)), '#autocomplete_route_name' => 'entity_reference.autocomplete', '#autocomplete_route_parameters' => $autocomplete_route_parameters, + '#autocomplete_options' => array( + 'id_in_text' => TRUE, + ), '#size' => $this->getSetting('size'), '#placeholder' => $this->getSetting('placeholder'), '#element_validate' => array(array($this, 'elementValidate')), diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceAutocompleteTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceAutocompleteTest.php index ae9ac0c..11673ee 100644 --- a/core/modules/entity_reference/src/Tests/EntityReferenceAutocompleteTest.php +++ b/core/modules/entity_reference/src/Tests/EntityReferenceAutocompleteTest.php @@ -76,25 +76,19 @@ function testEntityReferenceAutocompletion() { // We should get both entities in a JSON encoded string. $input = '10/'; $data = $this->getAutocompleteResult('single', $input); - $this->assertIdentical($data[0]['label'], String::checkPlain($entity_1->name->value), 'Autocomplete returned the first matching entity'); - $this->assertIdentical($data[1]['label'], String::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity'); + $this->assertIdentical($data[0]['text'], String::checkPlain($entity_1->name->value), 'Autocomplete returned the first matching entity'); + $this->assertIdentical($data[1]['text'], String::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity'); // Try to autocomplete a entity label that matches the first entity. // We should only get the first entity in a JSON encoded string. $input = '10/16'; $data = $this->getAutocompleteResult('single', $input); $target = array( - 'value' => $entity_1->name->value . ' (1)', - 'label' => String::checkPlain($entity_1->name->value), + 'id' => $entity_1->name->value . ' (1)', + 'text' => String::checkPlain($entity_1->name->value), ); $this->assertIdentical(reset($data), $target, 'Autocomplete returns only the expected matching entity.'); - // Try to autocomplete a entity label that matches the second entity, and - // the first entity is already typed in the autocomplete (tags) widget. - $input = $entity_1->name->value . ' (1), 10/17'; - $data = $this->getAutocompleteResult('tags', $input); - $this->assertIdentical($data[0]['label'], String::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity'); - // Try to autocomplete a entity label with both a comma and a slash. $input = '"label with, and / t'; $data = $this->getAutocompleteResult('single', $input); @@ -102,8 +96,8 @@ function testEntityReferenceAutocompletion() { // Entity labels containing commas or quotes must be wrapped in quotes. $n = Tags::encode($n); $target = array( - 'value' => $n, - 'label' => String::checkPlain($entity_3->name->value), + 'id' => $n, + 'text' => String::checkPlain($entity_3->name->value), ); $this->assertIdentical(reset($data), $target, 'Autocomplete returns an entity label containing a comma and a slash.'); } @@ -146,8 +140,8 @@ public function testBaseField() { $result = $entity_reference_controller->handleAutocomplete($request, 'single', 'user_id', 'entity_test', 'entity_test', 'NULL')->getContent(); $data = Json::decode($result); - $this->assertIdentical($data[0]['label'], String::checkPlain($user_1->getUsername()), 'Autocomplete returned the first matching entity'); - $this->assertIdentical($data[1]['label'], String::checkPlain($user_2->getUsername()), 'Autocomplete returned the second matching entity'); + $this->assertIdentical($data[0]['text'], String::checkPlain($user_1->getUsername()), 'Autocomplete returned the first matching entity'); + $this->assertIdentical($data[1]['text'], String::checkPlain($user_2->getUsername()), 'Autocomplete returned the second matching entity'); // Checks that exception thrown for unknown field. try { diff --git a/core/modules/system/css/system.module.css b/core/modules/system/css/system.module.css index cea7a25..07f9cf3 100644 --- a/core/modules/system/css/system.module.css +++ b/core/modules/system/css/system.module.css @@ -4,29 +4,6 @@ */ /** - * Autocomplete. - * - * @see autocomplete.js - */ - -/* Animated throbber */ -.js input.form-autocomplete { - background-image: url(../../../misc/throbber-inactive.png); - background-position: 100% center; /* LTR */ - background-repeat: no-repeat; -} -.js[dir="rtl"] input.form-autocomplete { - background-position: 0% center; -} -.js input.form-autocomplete.ui-autocomplete-loading { - background-image: url(../../../misc/throbber-active.gif); - background-position: 100% center; /* LTR */ -} -.js[dir="rtl"] input.form-autocomplete.ui-autocomplete-loading { - background-position: 0% center; -} - -/** * Unboxed fieldsets for grouping form elements. */ .fieldgroup { diff --git a/core/modules/system/css/system.theme.css b/core/modules/system/css/system.theme.css index 48fc3dd..964f0c8 100644 --- a/core/modules/system/css/system.theme.css +++ b/core/modules/system/css/system.theme.css @@ -195,18 +195,6 @@ label button.link { font-weight: bold; } -/* - * Autocomplete. - * - * @see autocomplete.js - */ -/* Suggestion list */ -.ui-autocomplete li.ui-menu-item a.ui-state-focus, .autocomplete li.ui-menu-item a.ui-state-hover { - background: #0072b9; - color: #fff; - margin: 0; -} - /** * Collapsible details. * diff --git a/core/modules/system/src/Form/RegionalForm.php b/core/modules/system/src/Form/RegionalForm.php index 2d3934f..0550370 100644 --- a/core/modules/system/src/Form/RegionalForm.php +++ b/core/modules/system/src/Form/RegionalForm.php @@ -98,6 +98,8 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#title' => t('Default time zone'), '#default_value' => $system_date->get('timezone.default') ?: date_default_timezone_get(), '#options' => $zones, + // Use the Select2 library. + '#select2' => TRUE, ); $configurable_timezones = $system_date->get('timezone.user.configurable'); diff --git a/core/modules/taxonomy/src/Controller/TermAutocompleteController.php b/core/modules/taxonomy/src/Controller/TermAutocompleteController.php index d701c90..de5d207 100644 --- a/core/modules/taxonomy/src/Controller/TermAutocompleteController.php +++ b/core/modules/taxonomy/src/Controller/TermAutocompleteController.php @@ -182,13 +182,12 @@ protected function getMatchingTerms($tags_typed, array $vids, $tag_last) { ->range(0, 10) ->execute(); - $prefix = count($tags_typed) ? Tags::implode($tags_typed) . ', ' : ''; if (!empty($tids)) { $terms = $this->entityManager->getStorage('taxonomy_term')->loadMultiple(array_keys($tids)); foreach ($terms as $term) { // Term names containing commas or quotes must be wrapped in quotes. $name = Tags::encode($term->getName()); - $matches[] = array('value' => $prefix . $name, 'label' => String::checkPlain($term->getName())); + $matches[] = array('id' => $name, 'text' => String::checkPlain($term->getName())); } return $matches; } diff --git a/core/modules/taxonomy/src/Tests/TermTest.php b/core/modules/taxonomy/src/Tests/TermTest.php index 72694c0..7169d6b 100644 --- a/core/modules/taxonomy/src/Tests/TermTest.php +++ b/core/modules/taxonomy/src/Tests/TermTest.php @@ -272,13 +272,13 @@ function testNodeTermCreationAndDeletion() { // The term will be quoted, and the " will be encoded in unicode (\u0022). $input = substr($term_objects['term3']->getName(), 0, 3); $json = $this->drupalGet('taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input))); - $this->assertEqual($json, '[{"value":"\u0022' . $term_objects['term3']->getName() . '\u0022","label":"' . $term_objects['term3']->getName() . '"}]', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->getName()))); + $this->assertEqual($json, '[{"id":"\u0022' . $term_objects['term3']->getName() . '\u0022","text":"' . $term_objects['term3']->getName() . '"}]', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->getName()))); // Test autocomplete on term 4 - it is alphanumeric only, so no extra // quoting. $input = substr($term_objects['term4']->getName(), 0, 3); $this->drupalGet('taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input))); - $this->assertRaw('[{"value":"' . $term_objects['term4']->getName() . '","label":"' . $term_objects['term4']->getName() . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term4']->getName()))); + $this->assertRaw('[{"id":"' . $term_objects['term4']->getName() . '","text":"' . $term_objects['term4']->getName() . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term4']->getName()))); // Test taxonomy autocomplete with a nonexistent field. $field_name = $this->randomMachineName(); @@ -315,7 +315,7 @@ function testTermAutocompletion() { // Pull the label properties from the array of arrays. $data = Json::decode($result); $data = array_map(function ($item) { - return $item['label']; + return $item['text']; }, $data); $this->assertTrue(in_array(String::checkPlain($first_term->getName()), $data), 'Autocomplete returned the first matching term'); @@ -327,8 +327,8 @@ function testTermAutocompletion() { $path = 'taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(); $this->drupalGet($path, array('query' => array('q' => $input))); $target = array(array( - 'value' => String::checkPlain($first_term->getName()), - 'label' => $first_term->getName(), + 'id' => String::checkPlain($first_term->getName()), + 'text' => $first_term->getName(), )); $this->assertRaw(Json::encode($target), 'Autocomplete returns only the expected matching term.'); @@ -339,8 +339,8 @@ function testTermAutocompletion() { // Term names containing commas or quotes must be wrapped in quotes. $n = Tags::encode($third_term->getName()); $target = array(array( - 'value' => $n, - 'label' => String::checkPlain($third_term->getName()), + 'id' => $n, + 'text' => String::checkPlain($third_term->getName()), )); $this->assertRaw(Json::encode($target), 'Autocomplete returns a term containing a comma and a slash.'); } diff --git a/core/modules/user/src/Tests/UserAutocompleteTest.php b/core/modules/user/src/Tests/UserAutocompleteTest.php index a56c33c..dc97721 100644 --- a/core/modules/user/src/Tests/UserAutocompleteTest.php +++ b/core/modules/user/src/Tests/UserAutocompleteTest.php @@ -49,7 +49,7 @@ function testUserAutocomplete() { // Test that anonymous username is in the result when requested and escaped // with \Drupal\Component\Utility\String::checkPlain(). $users = $this->drupalGetJSON('user/autocomplete/anonymous', array('query' => array('q' => Unicode::substr($anonymous_name, 0, 4)))); - $this->assertEqual(String::checkPlain($anonymous_name), $users[0]['label'], 'The anonymous name found in autocompletion results.'); + $this->assertEqual(String::checkPlain($anonymous_name), $users[0]['text'], 'The anonymous name found in autocompletion results.'); $users = $this->drupalGetJSON('user/autocomplete', array('query' => array('q' => Unicode::substr($anonymous_name, 0, 4)))); $this->assertTrue(empty($users), 'The anonymous name not found in autocompletion results without enabling anonymous username.'); } diff --git a/core/modules/user/src/UserAutocomplete.php b/core/modules/user/src/UserAutocomplete.php index cb75d5b..9b28b42 100644 --- a/core/modules/user/src/UserAutocomplete.php +++ b/core/modules/user/src/UserAutocomplete.php @@ -74,7 +74,7 @@ public function getMatches($string, $include_anonymous = FALSE) { $anonymous_name = $this->configFactory->get('user.settings')->get('anonymous'); // Allow autocompletion for the anonymous user. if (stripos($anonymous_name, $string) !== FALSE) { - $matches[] = array('value' => $anonymous_name, 'label' => String::checkPlain($anonymous_name)); + $matches[] = array('id' => $anonymous_name, 'text' => String::checkPlain($anonymous_name)); } } $uids = $this->entityQuery->get('user') @@ -84,7 +84,7 @@ public function getMatches($string, $include_anonymous = FALSE) { $controller = $this->entityManager->getStorage('user'); foreach ($controller->loadMultiple($uids) as $account) { - $matches[] = array('value' => $account->getUsername(), 'label' => String::checkPlain($account->getUsername())); + $matches[] = array('id' => $account->getUsername(), 'text' => String::checkPlain($account->getUsername())); } } diff --git a/core/modules/views/src/Tests/ViewsTaxonomyAutocompleteTest.php b/core/modules/views/src/Tests/ViewsTaxonomyAutocompleteTest.php index 270da71..b8ac329 100644 --- a/core/modules/views/src/Tests/ViewsTaxonomyAutocompleteTest.php +++ b/core/modules/views/src/Tests/ViewsTaxonomyAutocompleteTest.php @@ -75,8 +75,8 @@ public function testTaxonomyAutocomplete() { // Test a with whole name term. $label = $this->term1->getName(); $expected = array(array( - 'value' => $label, - 'label' => String::checkPlain($label), + 'id' => $label, + 'text' => String::checkPlain($label), )); $this->assertIdentical($expected, $this->drupalGetJSON($base_autocomplete_path, array('query' => array('q' => $label)))); // Test a term by partial name. diff --git a/core/themes/seven/css/components/jquery.ui.theme.css b/core/themes/seven/css/components/jquery.ui.theme.css index b22ca5e..2d95975 100644 --- a/core/themes/seven/css/components/jquery.ui.theme.css +++ b/core/themes/seven/css/components/jquery.ui.theme.css @@ -381,10 +381,3 @@ border-color: #D2D2D2; color: #000; } - -/** - * Autocomplete - */ -.ui-autocomplete { - background: #fff; -}