diff --git a/focal_point.module b/focal_point.module index b10bf78..a9388e2 100644 --- a/focal_point.module +++ b/focal_point.module @@ -149,6 +149,13 @@ function _focal_point_replace_media_library_preview(array $element, FormStateInt // widget, so we want to be sure that's visible. $preview_access = $element['preview']['#access']; $element['preview']['#access'] = TRUE; + + // Since core does not support nested modal dialogs, we need to ensure that + // the preview page opens in a new tab, rather than a modal dialog via AJAX. + $preview_link_attributes = &$element['preview']['preview_link']['#attributes']; + unset($preview_link_attributes['data-dialog-type']); + $preview_link_attributes['class'] = array_diff($preview_link_attributes['class'], ['use-ajax']); + // We expect the array parents to be something like // ['media', $delta, 'fields', $source_field, 'widget', 0]. Here, we // transform that to target the static preview thumbnail, which we expect to diff --git a/js/focal_point.js b/js/focal_point.js index 3ca457d..0ff12af 100644 --- a/js/focal_point.js +++ b/js/focal_point.js @@ -201,10 +201,22 @@ $previewLink.attr('href', href.join('/')); } + // Generates a new AJAX URL for the preview link, taking existing wrapper + // formats and dialog rendering options into account. + function rebuildAjaxUrl(href, settings) { + var wrapper_format = 'drupal_' + (settings.dialogType || 'ajax'); + if (settings.dialogRenderer) { + wrapper_format += '.' + settings.dialogRenderer; + } + href += href.indexOf('?') === -1 ? '?' : '&'; + href += Drupal.ajax.WRAPPER_FORMAT + '=' + wrapper_format; + return href; + } + // Update the ajax binding to reflect the new preview link href value. Drupal.ajax.instances.forEach(function (instance, index) { if (instance && $(instance.element).data('selector') === dataSelector) { - var href = $(instance.selector).attr('href'); + const href = rebuildAjaxUrl($(instance.selector).attr('href'), instance.elementSettings); Drupal.ajax.instances[index].url = href; Drupal.ajax.instances[index].options.url = href; } diff --git a/src/Controller/FocalPointPreviewController.php b/src/Controller/FocalPointPreviewController.php index a7ef76f..e8d1f57 100644 --- a/src/Controller/FocalPointPreviewController.php +++ b/src/Controller/FocalPointPreviewController.php @@ -2,6 +2,7 @@ namespace Drupal\focal_point\Controller; +use Drupal\Core\Ajax\AjaxHelperTrait; use Drupal\Core\Controller\ControllerBase; use Drupal\focal_point\Plugin\Field\FieldWidget\FocalPointImageWidget; use Drupal\file\Entity\File; @@ -24,6 +25,8 @@ use Symfony\Component\HttpFoundation\RequestStack; */ class FocalPointPreviewController extends ControllerBase { + use AjaxHelperTrait; + /** * The image factory service. * @@ -141,16 +144,21 @@ class FocalPointPreviewController extends ControllerBase { '#derivative_image_note' => $derivative_image_note, ]; - $options = [ - 'dialogClass' => 'popup-dialog-class', - 'width' => '80%', - ]; - $response = new AjaxResponse(); - $response->addCommand( - new OpenModalDialogCommand($this->t('Images preview'), $output, $options) - ); + if ($this->isAjax()) { + $options = [ + 'dialogClass' => 'popup-dialog-class', + 'width' => '80%', + ]; + $response = new AjaxResponse(); + $response->addCommand( + new OpenModalDialogCommand($this->t('Images preview'), $output, $options) + ); - return $response; + return $response; + } + else { + return $output; + } } /** diff --git a/tests/src/FunctionalJavascript/FocalPointWidgetTest.php b/tests/src/FunctionalJavascript/FocalPointWidgetTest.php new file mode 100644 index 0000000..01a6900 --- /dev/null +++ b/tests/src/FunctionalJavascript/FocalPointWidgetTest.php @@ -0,0 +1,115 @@ +drupalCreateContentType(['type' => 'article']); + $this->createImageField('field_image', 'article'); + + $this->container->get('entity_display.repository') + ->getFormDisplay('node', 'article') + ->setComponent('field_image', [ + 'type' => 'image_focal_point', + ]) + ->save(); + } + + /** + * Tests that preview works after changing the default focal point. + */ + public function testChangeFocalPointAndPreview() { + $assert_session = $this->assertSession(); + + $account = $this->drupalCreateUser(['create article content']); + $this->drupalLogin($account); + $this->drupalGet('/node/add/article'); + + $uri = uniqid('public://') . '.png'; + $uri = $this->getRandomGenerator()->image($uri, '100x100', '100x100'); + $path = $this->container->get('file_system')->realpath($uri); + $this->assertNotEmpty($path); + $this->assertFileExists($path); + + $this->getSession()->getPage()->attachFileToField('field_image', $path); + $preview_image = $assert_session->waitForElementVisible('css', '.focal-point-indicator'); + $this->assertNotEmpty($preview_image); + + $get_preview_url = function () use ($assert_session) { + return $assert_session->elementExists('css', '.focal-point-preview-link') + ->getAttribute('href'); + }; + + // Change the default focal point and ensure that we can open a preview + // which reflects that. + $old_preview_url = $get_preview_url(); + $this->setFocalPoint($preview_image, 10, 10); + $new_preview_url = $get_preview_url(); + $this->assertNotSame($old_preview_url, $new_preview_url); + + $assert_session->elementExists('css', '.focal-point-preview-link')->click(); + } + + /** + * Sets the focal point. + * + * This method directly calls methods of the underlying WebDriver session, so + * it will fail if this test is not using the Selenium2 Mink driver. + * + * @param \Behat\Mink\Element\ElementInterface $preview_image + * The preview image element. + * @param int $x_offset + * The X offset of the focal point, relative to the preview image. + * @param int $y_offset + * The Y offset of the focal point, relative to the preview image. + */ + private function setFocalPoint(ElementInterface $preview_image, $x_offset, $y_offset) { + /** @var \Behat\Mink\Driver\Selenium2Driver $driver */ + $driver = $this->getSession()->getDriver(); + $this->assertInstanceOf('\Behat\Mink\Driver\Selenium2Driver', $driver); + + $wd = $driver->getWebDriverSession(); + + // We need to get the preview image's element ID (a concept specific to + // WebDriver) in order to click the mouse in a position relative to it. + /** @var \WebDriver\Element $element */ + $element = $wd->element('xpath', $preview_image->getXpath()); + $this->assertInstanceOf('\WebDriver\Element', $element); + + $wd->moveto([ + 'element' => $element->getID(), + 'xoffset' => $x_offset, + 'yoffset' => $y_offset, + ]); + // 0 is the left mouse button, according to the WebDriver specification. + $wd->click(['button' => 0]); + } + +} diff --git a/tests/src/FunctionalJavascript/MediaLibraryIntegrationTest.php b/tests/src/FunctionalJavascript/MediaLibraryIntegrationTest.php index 9ba048e..8444077 100644 --- a/tests/src/FunctionalJavascript/MediaLibraryIntegrationTest.php +++ b/tests/src/FunctionalJavascript/MediaLibraryIntegrationTest.php @@ -122,6 +122,10 @@ class MediaLibraryIntegrationTest extends WebDriverTestBase { return count($elements) === 1; }); $this->assertTrue($widget_exists); + + // The preview link should be present, but it should not be using a modal + // dialog via AJAX. + $assert_session->elementExists('css', '.focal-point-preview-link[target="_blank"]:not(.use-ajax[data-dialog-type])'); } }