MDL-69107 form_autocomplete: Rewrite item selection
The form_autocomplete is essentially a custom element. Unfortunately the `setValue()` function in Mink has undesired actions so it is necessary to write our own handling for it. The standard Mink `setValue()` function focuses the element, sets a value, and then blurs the element. In the case of the autocomplete this can cause the autocomplete suggestions list to be closed in some situations. Instead of using the setValue we click, and type the value, but do not immediately blur.
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1173,6 +1173,9 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
|
||||
originalSelect.hide();
|
||||
var container = originalSelect.parent();
|
||||
|
||||
// Ensure that the data-fieldtype is set for behat.
|
||||
$(input).find('input').attr('data-fieldtype', 'autocomplete');
|
||||
|
||||
container.append(layout);
|
||||
container.find('[data-region="form_autocomplete-input"]').replaceWith(input);
|
||||
container.find('[data-region="form_autocomplete-suggestions"]').replaceWith(suggestions);
|
||||
|
||||
@@ -101,7 +101,6 @@ class behat_field_manager {
|
||||
* @return behat_form_field
|
||||
*/
|
||||
public static function get_field_instance($type, NodeElement $fieldnode, Session $session) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
// If the field is not part of a moodleform, we should still try to find out
|
||||
@@ -152,6 +151,10 @@ class behat_field_manager {
|
||||
$type = $fieldnode->getAttribute('type');
|
||||
switch ($type) {
|
||||
case 'text':
|
||||
if ($fieldtype = $fieldnode->getAttribute('data-fieldtype')) {
|
||||
return self::normalise_fieldtype($fieldtype);
|
||||
}
|
||||
return 'text';
|
||||
case 'password':
|
||||
case 'email':
|
||||
case 'file':
|
||||
|
||||
@@ -48,43 +48,75 @@ class behat_form_autocomplete extends behat_form_text {
|
||||
throw new coding_exception('Setting the value of an autocomplete field requires javascript.');
|
||||
}
|
||||
|
||||
// Set the value of the autocomplete's input.
|
||||
// If this autocomplete offers suggestions then these should be fetched by setting the value and waiting for the
|
||||
// JS to finish fetching those suggestions.
|
||||
// Clear all current selections.
|
||||
$rootnode = $this->field->getParent()->getParent();
|
||||
$selections = $rootnode->findAll('css', '.form-autocomplete-selection [role=option]');
|
||||
foreach ($selections as $selection) {
|
||||
$selection->click();
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
$istagelement = $this->field->hasAttribute('data-tags') && $this->field->getAttribute('data-tags');
|
||||
$allowscreation = $this->field->hasAttribute('data-tags') && !empty($this->field->getAttribute('data-tags'));
|
||||
$hasmultiple = $this->field->hasAttribute('data-multiple') && !empty($this->field->getAttribute('data-multiple'));
|
||||
|
||||
if ($istagelement && false !== strpos($value, ',')) {
|
||||
// Commas have a special meaning as a value separator in 'tag' autocomplete elements.
|
||||
if ($hasmultiple && false !== strpos($value, ',')) {
|
||||
// Commas have a special meaning as a value separator in 'multiple' autocomplete elements.
|
||||
// To handle this we break the value up by comma, and enter it in chunks.
|
||||
$values = explode(',', $value);
|
||||
|
||||
while ($value = array_shift($values)) {
|
||||
$this->set_value($value);
|
||||
$this->add_value(trim($value), $allowscreation);
|
||||
}
|
||||
} else {
|
||||
$this->field->setValue($value);
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
// If the autocomplete found suggestions, then it will have:
|
||||
// 1) marked itself as expanded; and
|
||||
// 2) have an aria-selected suggestion in the list.
|
||||
$expanded = $this->field->getAttribute('aria-expanded');
|
||||
$suggestion = $this->field->getParent()->find('css', '.form-autocomplete-suggestions > [aria-selected="true"]');
|
||||
|
||||
if ($expanded && null !== $suggestion) {
|
||||
// A suggestion was found.
|
||||
// Click on the first item in the list.
|
||||
$suggestion->click();
|
||||
} else {
|
||||
// Press the return key to create a new tag.
|
||||
behat_base::type_keys($this->session, [behat_keys::ENTER]);
|
||||
}
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
// Press the escape to close the autocomplete suggestions list.
|
||||
behat_base::type_keys($this->session, [behat_keys::ESCAPE]);
|
||||
$this->wait_for_pending_js();
|
||||
$this->add_value(trim($value), $allowscreation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to the autocomplete.
|
||||
*
|
||||
* @param string $value
|
||||
* @param bool $allowscreation
|
||||
*/
|
||||
protected function add_value(string $value, bool $allowscreation): void {
|
||||
$value = trim($value);
|
||||
|
||||
// Click into the field.
|
||||
$this->field->click();
|
||||
|
||||
// Remove any existing text.
|
||||
do {
|
||||
behat_base::type_keys($this->session, [behat_keys::BACKSPACE, behat_keys::DELETE]);
|
||||
} while (strlen($this->field->getValue()) > 0);
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
// Type in the new value.
|
||||
behat_base::type_keys($this->session, str_split($value));
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
// If the autocomplete found suggestions, then it will have:
|
||||
// 1) marked itself as expanded; and
|
||||
// 2) have an aria-selected suggestion in the list.
|
||||
$expanded = $this->field->getAttribute('aria-expanded');
|
||||
$suggestion = $this->field->getParent()->getParent()->find('css', '.form-autocomplete-suggestions > [aria-selected="true"]');
|
||||
|
||||
if ($expanded && null !== $suggestion) {
|
||||
// A suggestion was found.
|
||||
// Click on the first item in the list.
|
||||
$suggestion->click();
|
||||
} else if ($allowscreation) {
|
||||
// Press the return key to create a new entry.
|
||||
behat_base::type_keys($this->session, [behat_keys::ENTER]);
|
||||
} else {
|
||||
throw new \InvalidArgumentException(
|
||||
"Unable to find '{$value}' in the list of options, and unable to create a new option"
|
||||
);
|
||||
}
|
||||
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
// Press the escape to close the autocomplete suggestions list.
|
||||
behat_base::type_keys($this->session, [behat_keys::ESCAPE]);
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,13 +37,35 @@
|
||||
}}
|
||||
{{#showSuggestions}}
|
||||
<div class="d-md-inline-block mr-md-2 position-relative">
|
||||
<input type="text" id="{{inputId}}" class="form-control" list="{{suggestionsId}}" placeholder="{{placeholder}}" role="combobox" aria-expanded="false" autocomplete="off" autocorrect="off" autocapitalize="off" aria-autocomplete="list" aria-owns="{{suggestionsId}} {{selectionId}}" {{#tags}}data-tags="1"{{/tags}}/>
|
||||
<input type="text"{{!
|
||||
}} id="{{inputId}}"{{!
|
||||
}} class="form-control"{{!
|
||||
}} list="{{suggestionsId}}"{{!
|
||||
}} placeholder="{{placeholder}}"{{!
|
||||
}} role="combobox"{{!
|
||||
}} aria-expanded="false"{{!
|
||||
}} autocomplete="off"{{!
|
||||
}} autocorrect="off"{{!
|
||||
}} autocapitalize="off"{{!
|
||||
}} aria-autocomplete="list"{{!
|
||||
}} aria-owns="{{suggestionsId}} {{selectionId}}"{{!
|
||||
}} {{#tags}}data-tags="1"{{/tags}}{{!
|
||||
}} {{#multiple}}data-multiple="multiple"{{/multiple}}{{!
|
||||
}}>
|
||||
<span class="form-autocomplete-downarrow position-absolute p-1" id="{{downArrowId}}">▼</span>
|
||||
</div>
|
||||
{{/showSuggestions}}
|
||||
{{^showSuggestions}}
|
||||
<div class="d-md-inline-block mr-md-2">
|
||||
<input type="text" id="{{inputId}}" class="form-control" placeholder="{{placeholder}}" role="textbox" aria-owns="{{selectionId}}" {{#tags}}data-tags="1"{{/tags}}/>
|
||||
<input type="text"{{!
|
||||
}} id="{{inputId}}"{{!
|
||||
}} class="form-control"{{!
|
||||
}} placeholder="{{placeholder}}"{{!
|
||||
}} role="textbox"{{!
|
||||
}} aria-owns="{{selectionId}}"{{!
|
||||
}} {{#tags}}data-tags="1"{{/tags}}{{!
|
||||
}} {{#multiple}}data-multiple="multiple"{{/multiple}}{{!
|
||||
}}>
|
||||
</div>
|
||||
{{/showSuggestions}}
|
||||
|
||||
|
||||
@@ -693,8 +693,6 @@ class behat_forms extends behat_base {
|
||||
$xpathtarget = "//ul[@class='form-autocomplete-suggestions']//*[contains(concat('|', string(.), '|'),'|" . $item . "|')]";
|
||||
|
||||
$this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']);
|
||||
|
||||
$this->execute('behat_general::i_press_key_in_element', ['13', 'body', 'xpath_element']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user