Commit 8e0be71c authored by Nate Andersen's avatar Nate Andersen Committed by Nate Andersen
Browse files

Issue #3046416 by oknate, kellyimagined: Remove button conflict wrong triggering element

parent ebe48117
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -534,7 +534,8 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor
      // Find and remove correct entity.
      $values = explode(' ', $form_state->getValue(array_merge($parents, ['target_id'])));
      foreach ($values as $index => $item) {
        if ($item == $id && $index == $row_id) {
        // @todo add weight field.
        if ($item == $id) {
          array_splice($values, $index, 1);

          break;
@@ -767,7 +768,8 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor
    $is_relevant_submit = FALSE;
    if (($trigger = $form_state->getTriggeringElement())) {
      // Can be triggered by hidden target_id element or "Remove" button.
      if (end($trigger['#parents']) === 'target_id' || (end($trigger['#parents']) === 'remove_button')) {
      $last_parent = end($trigger['#parents']);
      if (in_array($last_parent, ['target_id', 'remove_button', 'replace_button'])) {
        $is_relevant_submit = TRUE;

        // In case there are more instances of this widget on the same page we
@@ -867,7 +869,11 @@ class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactor
      [$this->fieldDefinition->getName(), 'target_id']
    );

    if (!NestedArray::keyExists($form_state->getUserInput(), $target_id_element_path)) {
    $user_input = $form_state->getUserInput();

    $ief_submit = (!empty($user_input['_triggering_element_name']) && strpos($user_input['_triggering_element_name'], 'ief-edit-submit') === 0);

    if (!$ief_submit || !NestedArray::keyExists($form_state->getUserInput(), $target_id_element_path)) {
      return FALSE;
    }

+42 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ use Drupal\file\Entity\File;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Behat\Mink\Element\NodeElement;

/**
 * Base class for Entity browser Javascript functional tests.
@@ -181,4 +182,45 @@ abstract class EntityBrowserWebDriverTestBase extends WebDriverTestBase {
    $this->assertSession()->assertWaitOnAjaxRequest();
  }

  /**
   * Drag element in document with defined offset position.
   *
   * @param \Behat\Mink\Element\NodeElement $element
   *   Element that will be dragged.
   * @param int $offsetX
   *   Vertical offset for element drag in pixels.
   * @param int $offsetY
   *   Horizontal offset for element drag in pixels.
   */
  protected function dragDropElement(NodeElement $element, $offsetX, $offsetY) {

    $elemXpath = $element->getXpath();

    $jsCode = "var fireMouseEvent = function (type, element, x, y) {
      var event = document.createEvent('MouseEvents');
      event.initMouseEvent(type, true, (type !== 'mousemove'), window, 0, 0, 0, x, y, false, false, false, false, 0, element);
      element.dispatchEvent(event); };";

    // XPath provided by getXpath uses single quote (') to encapsulate strings,
    // that's why xpath has to be quited with double quites in javascript code.
    $jsCode .= "(function() {
      var dragElement = document.evaluate(\"{$elemXpath}\", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
      var pos = dragElement.getBoundingClientRect();
      var centerX = Math.floor((pos.left + pos.right) / 2);
      var centerY = Math.floor((pos.top + pos.bottom) / 2);
      fireMouseEvent('mousedown', dragElement, centerX, centerY);
      var xOffset = {$offsetX};
      var yOffset = {$offsetY};
      var moves = 3;
	    for (i = 0 ; i < moves ; i++ ) {
			  centerX += xOffset / moves;
			  centerY += yOffset / moves;
		    fireMouseEvent('mousemove', dragElement, Math.round(centerX), Math.round(centerY));
		  }
      fireMouseEvent('mouseup', dragElement, centerX, centerY);
    })();";

    $this->getSession()->executeScript($jsCode);
  }

}
+216 −0
Original line number Diff line number Diff line
@@ -319,4 +319,220 @@ class EntityReferenceWidgetTest extends EntityBrowserWebDriverTestBase {
    $assert_session->buttonNotExists('edit-field-entity-reference1-current-items-0-edit-button');
  }

  /**
   * Tests that drag and drop functions properly.
   */
  public function testDragAndDrop() {

    $gatsby = $this->createNode(['type' => 'shark', 'title' => 'Gatsby']);
    $daisy = $this->createNode(['type' => 'jet', 'title' => 'Daisy']);
    $nick = $this->createNode(['type' => 'article', 'title' => 'Nick']);

    $santa = $this->createNode(['type' => 'shark', 'title' => 'Santa Claus']);
    $easter_bunny = $this->createNode(['type' => 'jet', 'title' => 'Easter Bunny']);
    $pumpkin_king = $this->createNode(['type' => 'article', 'title' => 'Pumpkin King']);

    $field1_storage_config = [
      'field_name' => 'field_east_egg',
      'type' => 'entity_reference',
      'entity_type' => 'node',
      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
      'settings' => [
        'target_type' => 'node',
      ],
    ];

    $field2_storage_config = [
      'field_name' => 'field_east_egg2',
    ] + $field1_storage_config;

    $field_storage = FieldStorageConfig::create($field1_storage_config);
    $field_storage->save();

    $field_storage2 = FieldStorageConfig::create($field2_storage_config);
    $field_storage2->save();

    $field1_config = [
      'field_name' => 'field_east_egg',
      'entity_type' => 'node',
      'bundle' => 'article',
      'label' => 'East Eggers',
      'settings' => [
        'handler_settings' => [
          'target_bundles' => [
            'shark' => 'shark',
            'jet' => 'jet',
            'article' => 'article',
          ],
        ],
      ],
    ];

    $field2_config = [
      'field_name' => 'field_east_egg2',
      'label' => 'Easter Eggs',
    ] + $field1_config;

    $field = FieldConfig::create($field1_config);
    $field->save();

    $field2 = FieldConfig::create($field2_config);
    $field2->save();

    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
    $form_display = $this->container->get('entity_type.manager')
      ->getStorage('entity_form_display')
      ->load('node.article.default');

    $form_display->removeComponent('field_reference');

    $field_widget_config = [
      'type' => 'entity_browser_entity_reference',
      'settings' => [
        'entity_browser' => 'widget_context_default_value',
        'table_settings' => [
          'status_column' => TRUE,
          'bundle_column' => TRUE,
          'label_column' => FALSE,
        ],
        'open' => FALSE,
        'field_widget_edit' => TRUE,
        'field_widget_remove' => TRUE,
        'field_widget_replace' => FALSE,
        'selection_mode' => EntityBrowserElement::SELECTION_MODE_APPEND,
        'field_widget_display' => 'label',
        'field_widget_display_settings' => [],
      ],
    ];

    $form_display->setComponent('field_east_egg', $field_widget_config)->save();
    $form_display->setComponent('field_east_egg2', $field_widget_config)->save();

    // Set auto open to false on the entity browser.
    $entity_browser = $this->container->get('entity_type.manager')
      ->getStorage('entity_browser')
      ->load('widget_context_default_value');

    $display_configuration = $entity_browser->get('display_configuration');
    $display_configuration['auto_open'] = FALSE;
    $entity_browser->set('display_configuration', $display_configuration);
    $entity_browser->save();

    $account = $this->drupalCreateUser([
      'access widget_context_default_value entity browser pages',
      'create article content',
      'access content',
    ]);
    $this->drupalLogin($account);

    $this->drupalGet('node/add/article');

    $this->assertSession()->elementExists('xpath', '(//summary)[1]')->click();

    // Open the entity browser widget form.
    $this->getSession()->getPage()->clickLink('Select entities');
    $this->getSession()->switchToIFrame('entity_browser_iframe_widget_context_default_value');

    $page = $this->getSession()->getPage();

    $page->checkField('edit-entity-browser-select-node' . $gatsby->id());
    $page->checkField('edit-entity-browser-select-node' . $daisy->id());
    $page->checkField('edit-entity-browser-select-node' . $nick->id());
    $page->pressButton('Select entities');
    $this->waitForAjaxToFinish();
    $page->pressButton('Use selected');
    $this->waitForAjaxToFinish();
    $this->getSession()->switchToIFrame();
    $this->waitForAjaxToFinish();

    $correct_order = [
      1 => 'Gatsby',
      2 => 'Daisy',
      3 => 'Nick',
    ];
    foreach ($correct_order as $key => $value) {
      $this->assertSession()
        ->elementContains('xpath', "(//div[contains(@class, 'item-container')])[" . $key . "]", $value);
    }

    // Close details 1.
    $this->assertSession()->elementExists('xpath', '(//summary)[1]')->click();
    // Open details 2.
    $this->assertSession()->elementExists('xpath', '(//summary)[2]')->click();

    // Open the entity browser widget form.
    $this->assertSession()->elementExists('xpath', "(//a[contains(text(), 'Select entities')])[2]")->click();
    $this->getSession()->switchToIFrame('entity_browser_iframe_widget_context_default_value');

    $page = $this->getSession()->getPage();

    $page->checkField('edit-entity-browser-select-node' . $santa->id());
    $page->checkField('edit-entity-browser-select-node' . $easter_bunny->id());
    $page->checkField('edit-entity-browser-select-node' . $pumpkin_king->id());
    $page->pressButton('Select entities');
    $this->waitForAjaxToFinish();
    $page->pressButton('Use selected');
    $this->waitForAjaxToFinish();
    $this->getSession()->switchToIFrame();
    $this->waitForAjaxToFinish();

    // Close details 2.
    $this->assertSession()->elementExists('xpath', '(//summary)[2]')->click();
    // Open details 1.
    $this->assertSession()->elementExists('xpath', '(//summary)[1]')->click();

    $first_item = $this->assertSession()->elementExists('xpath', "(//div[contains(@class, 'item-container')])[1]");
    $this->dragDropElement($first_item, 160, 0);
    $this->waitForAjaxToFinish();

    $this->assertSession()->fieldExists('title[0][value]')->setValue('Hello World');

    $this->assertSession()->buttonExists('Save')->press();

    $this->drupalGet('node/7/edit');

    $correct_order = [
      1 => 'Daisy',
      2 => 'Gatsby',
      3 => 'Nick',
      4 => 'Santa Claus',
      5 => 'Easter Bunny',
      6 => 'Pumpkin King',
    ];
    foreach ($correct_order as $key => $value) {
      $this->assertSession()
        ->elementContains('xpath', "(//div[contains(@class, 'item-container')])[" . $key . "]", $value);
    }

    $fourth = $this->assertSession()->elementExists('xpath', "(//div[contains(@class, 'item-container')])[4]");
    $this->dragDropElement($fourth, 160, 0);

    $correct_order = [
      4 => 'Easter Bunny',
      5 => 'Santa Claus',
      6 => 'Pumpkin King',
    ];
    foreach ($correct_order as $key => $value) {
      $this->assertSession()
        ->elementContains('xpath', "(//div[contains(@class, 'item-container')])[" . $key . "]", $value);
    }

    // Test that order is preserved after removing item.
    $this->assertSession()
      ->elementExists('xpath', '(//input[contains(@class, "remove-button")])[5]')
      ->press();

    $this->waitForAjaxToFinish();

    $correct_order = [
      4 => 'Easter Bunny',
      5 => 'Pumpkin King',
    ];

    foreach ($correct_order as $key => $value) {
      $this->assertSession()
        ->elementContains('xpath', "(//div[contains(@class, 'item-container')])[" . $key . "]", $value);
    }
  }

}
+0 −35
Original line number Diff line number Diff line
@@ -2,8 +2,6 @@

namespace Drupal\Tests\entity_browser\FunctionalJavascript;

use Behat\Mink\Element\NodeElement;

/**
 * Test for integration of entity browser and inline entity form.
 *
@@ -48,39 +46,6 @@ class InlineEntityFormTest extends EntityBrowserWebDriverTestBase {
    'edit any ief_content content',
  ];

  /**
   * Drag element in document with defined offset position.
   *
   * @param \Behat\Mink\Element\NodeElement $element
   *   Element that will be dragged.
   * @param int $offsetX
   *   Vertical offset for element drag in pixels.
   * @param int $offsetY
   *   Horizontal offset for element drag in pixels.
   */
  protected function dragDropElement(NodeElement $element, $offsetX, $offsetY) {
    $elemXpath = $element->getXpath();

    $jsCode = "var fireMouseEvent = function (type, element, x, y) {"
      . "  var event = document.createEvent('MouseEvents');"
      . "  event.initMouseEvent(type, true, (type !== 'mousemove'), window, 0, 0, 0, x, y, false, false, false, false, 0, element);"
      . "  element.dispatchEvent(event); };";

    // XPath provided by getXpath uses single quote (') to encapsulate strings,
    // that's why xpath has to be quited with double quites in javascript code.
    $jsCode .= "(function() {" .
      "  var dragElement = document.evaluate(\"{$elemXpath}\", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" .
      "  var pos = dragElement.getBoundingClientRect();" .
      "  var centerX = Math.floor((pos.left + pos.right) / 2);" .
      "  var centerY = Math.floor((pos.top + pos.bottom) / 2);" .
      "  fireMouseEvent('mousedown', dragElement, centerX, centerY);" .
      "  fireMouseEvent('mousemove', document, centerX + {$offsetX}, centerY + {$offsetY});" .
      "  fireMouseEvent('mouseup', dragElement, centerX + {$offsetX}, centerY + {$offsetY});" .
      "})();";

    $this->getSession()->executeScript($jsCode);
  }

  /**
   * Check that selection state in entity browser Inline Entity Form.
   */