summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGábor Hojtsy2018-05-29 16:11:54 (GMT)
committerGábor Hojtsy2018-05-29 16:12:49 (GMT)
commitf16ee05a6e975e7db74797772f3b50468e57799e (patch)
tree8c7db2a40b34533f131fed5f1ba65ae37359b9ea
parenta8f46205a0cae7508967ec600b489d93844270d8 (diff)
Issue #2916781 by timmillwood, tedbow, drpal, lauriii, Adita, andrewmacpherson, yoroy, amateescu, borisson_, ckrina: Allow off-canvas dialog to be rendered at the top of the page
-rw-r--r--core/core.services.yml5
-rw-r--r--core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php7
-rw-r--r--core/lib/Drupal/Core/Render/MainContent/OffCanvasRenderer.php14
-rw-r--r--core/misc/dialog/off-canvas.es6.js71
-rw-r--r--core/misc/dialog/off-canvas.js52
-rw-r--r--core/misc/dialog/off-canvas.motion.css2
-rw-r--r--core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php46
-rw-r--r--core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php25
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php160
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php23
-rw-r--r--core/modules/toolbar/js/toolbar.es6.js21
-rw-r--r--core/modules/toolbar/js/toolbar.js20
-rw-r--r--core/tests/Drupal/Tests/Core/Ajax/OpenOffCanvasDialogCommandTest.php21
-rw-r--r--core/themes/stable/css/core/dialog/off-canvas.motion.css2
14 files changed, 387 insertions, 82 deletions
diff --git a/core/core.services.yml b/core/core.services.yml
index d938836..cbcccfe 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1096,6 +1096,11 @@ services:
arguments: ['@title_resolver', '@renderer']
tags:
- { name: render.main_content_renderer, format: drupal_dialog.off_canvas }
+ main_content_renderer.off_canvas_top:
+ class: Drupal\Core\Render\MainContent\OffCanvasRenderer
+ arguments: ['@title_resolver', '@renderer', 'top']
+ tags:
+ - { name: render.main_content_renderer, format: drupal_dialog.off_canvas_top }
main_content_renderer.modal:
class: Drupal\Core\Render\MainContent\ModalRenderer
arguments: ['@title_resolver']
diff --git a/core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php b/core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php
index da6a26e..78c406b 100644
--- a/core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php
+++ b/core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php
@@ -34,19 +34,22 @@ class OpenOffCanvasDialogCommand extends OpenDialogCommand {
* (optional) Custom settings that will be passed to the Drupal behaviors
* on the content of the dialog. If left empty, the settings will be
* populated automatically from the current request.
+ * @param string $position
+ * (optional) The position to render the off-canvas dialog.
*/
- public function __construct($title, $content, array $dialog_options = [], $settings = NULL) {
+ public function __construct($title, $content, array $dialog_options = [], $settings = NULL, $position = 'side') {
parent::__construct('#drupal-off-canvas', $title, $content, $dialog_options, $settings);
$this->dialogOptions['modal'] = FALSE;
$this->dialogOptions['autoResize'] = FALSE;
$this->dialogOptions['resizable'] = 'w';
$this->dialogOptions['draggable'] = FALSE;
$this->dialogOptions['drupalAutoButtons'] = FALSE;
+ $this->dialogOptions['drupalOffCanvasPosition'] = $position;
// @todo drupal.ajax.js does not respect drupalAutoButtons properly, pass an
// empty set of buttons until https://www.drupal.org/node/2793343 is in.
$this->dialogOptions['buttons'] = [];
if (empty($dialog_options['dialogClass'])) {
- $this->dialogOptions['dialogClass'] = 'ui-dialog-off-canvas';
+ $this->dialogOptions['dialogClass'] = "ui-dialog-off-canvas ui-dialog-position-$position";
}
// If no width option is provided then use the default width to avoid the
// dialog staying at the width of the previous instance when opened
diff --git a/core/lib/Drupal/Core/Render/MainContent/OffCanvasRenderer.php b/core/lib/Drupal/Core/Render/MainContent/OffCanvasRenderer.php
index 55bf8eb..b8f0e73 100644
--- a/core/lib/Drupal/Core/Render/MainContent/OffCanvasRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/OffCanvasRenderer.php
@@ -24,16 +24,26 @@ class OffCanvasRenderer extends DialogRenderer {
protected $renderer;
/**
+ * The position to render the off-canvas dialog.
+ *
+ * @var string
+ */
+ protected $position;
+
+ /**
* Constructs a new OffCanvasRenderer.
*
* @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
* The title resolver.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
+ * @param string $position
+ * (optional) The position to render the off-canvas dialog.
*/
- public function __construct(TitleResolverInterface $title_resolver, RendererInterface $renderer) {
+ public function __construct(TitleResolverInterface $title_resolver, RendererInterface $renderer, $position = 'side') {
parent::__construct($title_resolver);
$this->renderer = $renderer;
+ $this->position = $position;
}
/**
@@ -55,7 +65,7 @@ class OffCanvasRenderer extends DialogRenderer {
// Determine the title: use the title provided by the main content if any,
// otherwise get it from the routing information.
$options = $request->request->get('dialogOptions', []);
- $response->addCommand(new OpenOffCanvasDialogCommand($title, $content, $options));
+ $response->addCommand(new OpenOffCanvasDialogCommand($title, $content, $options, NULL, $this->position));
return $response;
}
diff --git a/core/misc/dialog/off-canvas.es6.js b/core/misc/dialog/off-canvas.es6.js
index 0068e44..a4de606 100644
--- a/core/misc/dialog/off-canvas.es6.js
+++ b/core/misc/dialog/off-canvas.es6.js
@@ -13,6 +13,19 @@
* @namespace
*/
Drupal.offCanvas = {
+ /**
+ * Storage for position information about the tray.
+ *
+ * @type {?String}
+ */
+ position: null,
+
+ /**
+ * The minimum height of the tray when opened at the top of the page.
+ *
+ * @type {Number}
+ */
+ minimumHeight: 30,
/**
* The minimum width to use body displace needs to match the width at which
@@ -75,10 +88,14 @@
};
/**
- * Applies initial height to dialog based on window height.
+ * Applies initial height and with to dialog based depending on position.
* @see http://api.jqueryui.com/dialog for all dialog options.
*/
- settings.height = $(window).height();
+ const position = settings.drupalOffCanvasPosition;
+ const height = position === 'side' ? $(window).height() : settings.height;
+ const width = position === 'side' ? settings.width : '100%';
+ settings.height = height;
+ settings.width = width;
},
/**
@@ -90,8 +107,7 @@
$('body').removeClass('js-off-canvas-dialog-open');
// Remove all *.off-canvas events
Drupal.offCanvas.removeOffCanvasEvents($element);
-
- Drupal.offCanvas.$mainCanvasWrapper.css(`padding-${Drupal.offCanvas.getEdge()}`, 0);
+ Drupal.offCanvas.resetPadding();
},
/**
@@ -168,11 +184,27 @@
* Data attached to the event.
*/
resetSize(event) {
- const offsets = displace.offsets;
const $element = event.data.$element;
const container = Drupal.offCanvas.getContainer($element);
+ const position = event.data.settings.drupalOffCanvasPosition;
+
+ // Only remove the `data-offset-*` attribute if the value previously
+ // exists and the orientation is changing.
+ if (
+ Drupal.offCanvas.position &&
+ Drupal.offCanvas.position !== position) {
+ container.removeAttr(`data-offset-${Drupal.offCanvas.position}`);
+ }
+ // Set a minimum height on $element
+ if (position === 'top') {
+ $element.css('min-height', `${Drupal.offCanvas.minimumHeight}px`);
+ }
+
+ displace();
+
+ const offsets = displace.offsets;
- const topPosition = (offsets.top !== 0 ? `+${offsets.top}` : '');
+ const topPosition = position === 'side' && offsets.top !== 0 ? `+${offsets.top}` : '';
const adjustedOptions = {
// @see http://api.jqueryui.com/position/
position: {
@@ -182,14 +214,17 @@
},
};
+ const height = position === 'side' ? `${$(window).height() - (offsets.top + offsets.bottom)}px` : event.data.settings.height;
container.css({
position: 'fixed',
- height: `${$(window).height() - (offsets.top + offsets.bottom)}px`,
+ height,
});
$element
.dialog('option', adjustedOptions)
.trigger('dialogContentResize.off-canvas');
+
+ Drupal.offCanvas.position = position;
},
/**
@@ -201,20 +236,29 @@
* Data attached to the event.
*/
bodyPadding(event) {
- if ($('body').outerWidth() < Drupal.offCanvas.minDisplaceWidth) {
+ const position = event.data.settings.drupalOffCanvasPosition;
+ if (position === 'side' && $('body').outerWidth() < Drupal.offCanvas.minDisplaceWidth) {
return;
}
+ Drupal.offCanvas.resetPadding();
const $element = event.data.$element;
const $container = Drupal.offCanvas.getContainer($element);
const $mainCanvasWrapper = Drupal.offCanvas.$mainCanvasWrapper;
const width = $container.outerWidth();
const mainCanvasPadding = $mainCanvasWrapper.css(`padding-${Drupal.offCanvas.getEdge()}`);
- if (width !== mainCanvasPadding) {
+ if (position === 'side' && width !== mainCanvasPadding) {
$mainCanvasWrapper.css(`padding-${Drupal.offCanvas.getEdge()}`, `${width}px`);
$container.attr(`data-offset-${Drupal.offCanvas.getEdge()}`, width);
displace();
}
+
+ const height = $container.outerHeight();
+ if (position === 'top') {
+ $mainCanvasWrapper.css('padding-top', `${height}px`);
+ $container.attr('data-offset-top', height);
+ displace();
+ }
},
/**
@@ -238,6 +282,15 @@
getEdge() {
return document.documentElement.dir === 'rtl' ? 'left' : 'right';
},
+
+ /**
+ * Resets main canvas wrapper and toolbar padding / margin.
+ */
+ resetPadding() {
+ Drupal.offCanvas.$mainCanvasWrapper.css(`padding-${Drupal.offCanvas.getEdge()}`, 0);
+ Drupal.offCanvas.$mainCanvasWrapper.css('padding-top', 0);
+ displace();
+ },
};
/**
diff --git a/core/misc/dialog/off-canvas.js b/core/misc/dialog/off-canvas.js
index 1de5f67..85498b7 100644
--- a/core/misc/dialog/off-canvas.js
+++ b/core/misc/dialog/off-canvas.js
@@ -7,6 +7,10 @@
(function ($, Drupal, debounce, displace) {
Drupal.offCanvas = {
+ position: null,
+
+ minimumHeight: 30,
+
minDisplaceWidth: 768,
$mainCanvasWrapper: $('[data-off-canvas-main-canvas]'),
@@ -33,7 +37,11 @@
of: window
};
- settings.height = $(window).height();
+ var position = settings.drupalOffCanvasPosition;
+ var height = position === 'side' ? $(window).height() : settings.height;
+ var width = position === 'side' ? settings.width : '100%';
+ settings.height = height;
+ settings.width = width;
},
beforeClose: function beforeClose(_ref2) {
var $element = _ref2.$element;
@@ -41,8 +49,7 @@
$('body').removeClass('js-off-canvas-dialog-open');
Drupal.offCanvas.removeOffCanvasEvents($element);
-
- Drupal.offCanvas.$mainCanvasWrapper.css('padding-' + Drupal.offCanvas.getEdge(), 0);
+ Drupal.offCanvas.resetPadding();
},
afterCreate: function afterCreate(_ref3) {
var $element = _ref3.$element,
@@ -79,11 +86,23 @@
$element.height(modalHeight - offset - scrollOffset);
},
resetSize: function resetSize(event) {
- var offsets = displace.offsets;
var $element = event.data.$element;
var container = Drupal.offCanvas.getContainer($element);
+ var position = event.data.settings.drupalOffCanvasPosition;
+
+ if (Drupal.offCanvas.position && Drupal.offCanvas.position !== position) {
+ container.removeAttr('data-offset-' + Drupal.offCanvas.position);
+ }
+
+ if (position === 'top') {
+ $element.css('min-height', Drupal.offCanvas.minimumHeight + 'px');
+ }
- var topPosition = offsets.top !== 0 ? '+' + offsets.top : '';
+ displace();
+
+ var offsets = displace.offsets;
+
+ var topPosition = position === 'side' && offsets.top !== 0 ? '+' + offsets.top : '';
var adjustedOptions = {
position: {
my: Drupal.offCanvas.getEdge() + ' top',
@@ -92,34 +111,51 @@
}
};
+ var height = position === 'side' ? $(window).height() - (offsets.top + offsets.bottom) + 'px' : event.data.settings.height;
container.css({
position: 'fixed',
- height: $(window).height() - (offsets.top + offsets.bottom) + 'px'
+ height: height
});
$element.dialog('option', adjustedOptions).trigger('dialogContentResize.off-canvas');
+
+ Drupal.offCanvas.position = position;
},
bodyPadding: function bodyPadding(event) {
- if ($('body').outerWidth() < Drupal.offCanvas.minDisplaceWidth) {
+ var position = event.data.settings.drupalOffCanvasPosition;
+ if (position === 'side' && $('body').outerWidth() < Drupal.offCanvas.minDisplaceWidth) {
return;
}
+ Drupal.offCanvas.resetPadding();
var $element = event.data.$element;
var $container = Drupal.offCanvas.getContainer($element);
var $mainCanvasWrapper = Drupal.offCanvas.$mainCanvasWrapper;
var width = $container.outerWidth();
var mainCanvasPadding = $mainCanvasWrapper.css('padding-' + Drupal.offCanvas.getEdge());
- if (width !== mainCanvasPadding) {
+ if (position === 'side' && width !== mainCanvasPadding) {
$mainCanvasWrapper.css('padding-' + Drupal.offCanvas.getEdge(), width + 'px');
$container.attr('data-offset-' + Drupal.offCanvas.getEdge(), width);
displace();
}
+
+ var height = $container.outerHeight();
+ if (position === 'top') {
+ $mainCanvasWrapper.css('padding-top', height + 'px');
+ $container.attr('data-offset-top', height);
+ displace();
+ }
},
getContainer: function getContainer($element) {
return $element.dialog('widget');
},
getEdge: function getEdge() {
return document.documentElement.dir === 'rtl' ? 'left' : 'right';
+ },
+ resetPadding: function resetPadding() {
+ Drupal.offCanvas.$mainCanvasWrapper.css('padding-' + Drupal.offCanvas.getEdge(), 0);
+ Drupal.offCanvas.$mainCanvasWrapper.css('padding-top', 0);
+ displace();
}
};
diff --git a/core/misc/dialog/off-canvas.motion.css b/core/misc/dialog/off-canvas.motion.css
index b3158e9..60d8d6a 100644
--- a/core/misc/dialog/off-canvas.motion.css
+++ b/core/misc/dialog/off-canvas.motion.css
@@ -7,5 +7,5 @@
*/
.dialog-off-canvas-main-canvas {
- transition: all 0.7s ease;
+ transition: padding-right 0.7s ease, padding-left 0.7s ease, padding-top 0.3s ease;
}
diff --git a/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php b/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
index ea310fa..fbfa5a7 100644
--- a/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
+++ b/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
@@ -45,17 +45,22 @@ class TestController {
public function linksDisplay() {
return [
'off_canvas_link_1' => [
- '#title' => 'Click Me 1!',
+ '#title' => 'Open side panel 1',
'#type' => 'link',
'#url' => Url::fromRoute('off_canvas_test.thing1'),
'#attributes' => [
'class' => ['use-ajax'],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
+ 'data-dialog-options' => Json::encode([
+ 'classes' => [
+ "ui-dialog" => "ui-corner-all side-1",
+ ],
+ ]),
],
],
'off_canvas_link_2' => [
- '#title' => 'Click Me 2!',
+ '#title' => 'Open side panel 2',
'#type' => 'link',
'#url' => Url::fromRoute('off_canvas_test.thing2'),
'#attributes' => [
@@ -64,6 +69,43 @@ class TestController {
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => Json::encode([
'width' => 555,
+ 'classes' => [
+ "ui-dialog" => "ui-corner-all side-2",
+ ],
+ ]),
+ ],
+ ],
+ 'off_canvas_top_link_1' => [
+ '#title' => 'Open top panel 1',
+ '#type' => 'link',
+ '#url' => Url::fromRoute('off_canvas_test.thing1'),
+ '#attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'dialog',
+ 'data-dialog-renderer' => 'off_canvas_top',
+ 'data-dialog-options' => Json::encode([
+ 'width' => 555,
+ 'classes' => [
+ "ui-dialog" => "ui-corner-all top-1",
+ ],
+ ]),
+ ],
+
+ ],
+ 'off_canvas_top_link_2' => [
+ '#title' => 'Open top panel 2',
+ '#type' => 'link',
+ '#url' => Url::fromRoute('off_canvas_test.thing2'),
+ '#attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'dialog',
+ 'data-dialog-renderer' => 'off_canvas_top',
+ 'data-dialog-options' => Json::encode([
+ 'height' => 421,
+ 'classes' => [
+ "ui-dialog" => "ui-corner-all top-2",
+ ],
+
]),
],
],
diff --git a/core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php b/core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php
index 222ebc4..c786722 100644
--- a/core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php
+++ b/core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php
@@ -23,8 +23,10 @@ class OffCanvasDialogTest extends BrowserTestBase {
/**
* Test sending AJAX requests to open and manipulate off-canvas dialog.
+ *
+ * @dataProvider dialogPosition
*/
- public function testDialog() {
+ public function testDialog($position) {
// Ensure the elements render without notices or exceptions.
$this->drupalGet('ajax-test/dialog');
@@ -45,8 +47,9 @@ class OffCanvasDialogTest extends BrowserTestBase {
'resizable' => 'w',
'draggable' => FALSE,
'drupalAutoButtons' => FALSE,
+ 'drupalOffCanvasPosition' => $position ?: 'side',
'buttons' => [],
- 'dialogClass' => 'ui-dialog-off-canvas',
+ 'dialogClass' => 'ui-dialog-off-canvas ui-dialog-position-' . ($position ?: 'side'),
'width' => 300,
],
'effect' => 'fade',
@@ -54,9 +57,23 @@ class OffCanvasDialogTest extends BrowserTestBase {
];
// Emulate going to the JS version of the page and check the JSON response.
- $ajax_result = $this->drupalGet('ajax-test/dialog-contents', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_dialog.off_canvas']]);
+ $wrapper_format = $position && ($position !== 'side') ? 'drupal_dialog.off_canvas_' . $position : 'drupal_dialog.off_canvas';
+ $ajax_result = $this->drupalGet('ajax-test/dialog-contents', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => $wrapper_format]]);
$ajax_result = Json::decode($ajax_result);
- $this->assertEqual($off_canvas_expected_response, $ajax_result[3], 'off-canvas dialog JSON response matches.');
+ $this->assertEquals($off_canvas_expected_response, $ajax_result[3], 'off-canvas dialog JSON response matches.');
+ }
+
+ /**
+ * The data provider for potential dialog positions.
+ *
+ * @return array
+ */
+ public static function dialogPosition() {
+ return [
+ [NULL],
+ ['side'],
+ ['top'],
+ ];
}
}
diff --git a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
index 0cadece..7719634 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
@@ -10,6 +10,15 @@ namespace Drupal\Tests\system\FunctionalJavascript;
class OffCanvasTest extends OffCanvasTestBase {
/**
+ * Stores to the class that should be on the last dialog.
+ *
+ * @var string
+ *
+ * @see \Drupal\off_canvas_test\Controller\TestController::linksDisplay.
+ */
+ protected $lastDialogClass;
+
+ /**
* {@inheritdoc}
*/
public static $modules = [
@@ -18,60 +27,83 @@ class OffCanvasTest extends OffCanvasTestBase {
/**
* Tests that non-contextual links will work with the off-canvas dialog.
+ *
+ * @dataProvider themeDataProvider
*/
- public function testOffCanvasLinks() {
- // Test the same functionality on multiple themes.
- foreach ($this->getTestThemes() as $theme) {
- $this->enableTheme($theme);
- $this->drupalGet('/off-canvas-test-links');
+ public function testOffCanvasLinks($theme) {
+ $this->enableTheme($theme);
+ $this->drupalGet('/off-canvas-test-links');
- $page = $this->getSession()->getPage();
- $web_assert = $this->assertSession();
-
- // Make sure off-canvas dialog is on page when first loaded.
- $web_assert->elementNotExists('css', '#drupal-off-canvas');
+ $page = $this->getSession()->getPage();
+ $web_assert = $this->assertSession();
- // Check opening and closing with two separate links.
- // Make sure tray updates to new content.
- // Check the first link again to make sure the empty title class is
- // removed.
- foreach (['1', '2', '1'] as $link_index) {
- // Click the first test like that should open the page.
- $page->clickLink("Click Me $link_index!");
+ // Make sure off-canvas dialog is on page when first loaded.
+ $web_assert->elementNotExists('css', '#drupal-off-canvas');
+
+ // Check opening and closing with two separate links.
+ // Make sure tray updates to new content.
+ // Check the first link again to make sure the empty title class is
+ // removed.
+ foreach (['1', '2', '1'] as $link_index) {
+ $this->assertOffCanvasDialog($link_index, 'side');
+ $header_text = $this->getOffCanvasDialog()->find('css', '.ui-dialog-title')->getText();
+ if ($link_index == '2') {
+ // Check no title behavior.
+ $web_assert->elementExists('css', '.ui-dialog-empty-title');
+ $this->assertEquals("\xc2\xa0", $header_text);
+
+ $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
+ $this->assertTrue(strstr($style, 'width: 555px;') !== FALSE, 'Dialog width respected.');
+ $page->clickLink("Open side panel 1");
$this->waitForOffCanvasToOpen();
+ $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
+ $this->assertTrue(strstr($style, 'width: 555px;') === FALSE, 'Dialog width reset to default.');
+ }
+ else {
+ // Check that header is correct.
+ $this->assertEquals("Thing $link_index", $header_text);
+ $web_assert->elementNotExists('css', '.ui-dialog-empty-title');
+ }
+ }
+
+ // Test the off_canvas_top tray.
+ foreach ([1, 2] as $link_index) {
+ $this->assertOffCanvasDialog($link_index, 'top');
- // Check that the canvas is not on the page.
- $web_assert->elementExists('css', '#drupal-off-canvas');
- // Check that response text is on page.
- $web_assert->pageTextContains("Thing $link_index says hello");
- $off_canvas_tray = $this->getOffCanvasDialog();
-
- // Check that tray is visible.
- $this->assertEquals(TRUE, $off_canvas_tray->isVisible());
- $header_text = $off_canvas_tray->find('css', '.ui-dialog-title')->getText();
-
- $tray_text = $off_canvas_tray->findById('drupal-off-canvas')->getText();
- $this->assertEquals("Thing $link_index says hello", $tray_text);
-
- if ($link_index == '2') {
- // Check no title behavior.
- $web_assert->elementExists('css', '.ui-dialog-empty-title');
- $this->assertEquals("\xc2\xa0", $header_text);
-
- $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
- $this->assertTrue(strstr($style, 'width: 555px;') !== FALSE, 'Dialog width respected.');
- $page->clickLink("Click Me 1!");
- $this->waitForOffCanvasToOpen();
- $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
- $this->assertTrue(strstr($style, 'width: 555px;') === FALSE, 'Dialog width reset to default.');
- }
- else {
- // Check that header is correct.
- $this->assertEquals("Thing $link_index", $header_text);
- $web_assert->elementNotExists('css', '.ui-dialog-empty-title');
- }
+ $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
+ if ($link_index === 1) {
+ $this->assertTrue((bool) strstr($style, 'height: auto;'));
+ }
+ else {
+ $this->assertTrue((bool) strstr($style, 'height: 421px;'));
}
}
+
+ // Ensure an off-canvas link opened from inside the off-canvas dialog will
+ // work.
+ $this->drupalGet('/off-canvas-test-links');
+ $page->clickLink('Display more links!');
+ $this->waitForOffCanvasToOpen();
+ $web_assert->linkExists('Off_canvas link!');
+ // Click off-canvas link inside off-canvas dialog
+ $page->clickLink('Off_canvas link!');
+ /* @var \Behat\Mink\Element\NodeElement $dialog */
+ $this->waitForOffCanvasToOpen();
+ $web_assert->elementTextContains('css', '.ui-dialog[aria-describedby="drupal-off-canvas"]', 'Thing 2 says hello');
+
+ // Ensure an off-canvas link opened from inside the off-canvas dialog will
+ // work after another dialog has been opened.
+ $this->drupalGet('/off-canvas-test-links');
+ $page->clickLink("Open side panel 1");
+ $this->waitForOffCanvasToOpen();
+ $page->clickLink('Display more links!');
+ $this->waitForOffCanvasToOpen();
+ $web_assert->linkExists('Off_canvas link!');
+ // Click off-canvas link inside off-canvas dialog
+ $page->clickLink('Off_canvas link!');
+ /* @var \Behat\Mink\Element\NodeElement $dialog */
+ $this->waitForOffCanvasToOpen();
+ $web_assert->elementTextContains('css', '.ui-dialog[aria-describedby="drupal-off-canvas"]', 'Thing 2 says hello');
}
/**
@@ -91,7 +123,7 @@ class OffCanvasTest extends OffCanvasTestBase {
$this->getSession()->resizeWindow($narrow_width_breakpoint + $offset, $height);
$this->drupalGet('/off-canvas-test-links');
$this->assertFalse($page->find('css', '.dialog-off-canvas-main-canvas')->hasAttribute('style'), 'Body not padded on wide page load.');
- $page->clickLink("Click Me 1!");
+ $page->clickLink("Open side panel 1");
$this->waitForOffCanvasToOpen();
// Check that the main canvas is padded when page is not narrow width and
// tray is open.
@@ -101,10 +133,40 @@ class OffCanvasTest extends OffCanvasTestBase {
$this->getSession()->resizeWindow($narrow_width_breakpoint - $offset, $height);
$this->drupalGet('/off-canvas-test-links');
$this->assertFalse($page->find('css', '.dialog-off-canvas-main-canvas')->hasAttribute('style'), 'Body not padded on narrow page load.');
- $page->clickLink("Click Me 1!");
+ $page->clickLink("Open side panel 1");
$this->waitForOffCanvasToOpen();
$this->assertFalse($page->find('css', '.dialog-off-canvas-main-canvas')->hasAttribute('style'), 'Body not padded on narrow page with tray open.');
}
}
+ /**
+ * @param int $link_index
+ * The index of the link to test.
+ * @param string $position
+ * The position of the dialog to test.
+ */
+ protected function assertOffCanvasDialog($link_index, $position) {
+ $page = $this->getSession()->getPage();
+ $web_assert = $this->assertSession();
+ $link_text = "Open $position panel $link_index";
+
+ // Click the first test like that should open the page.
+ $page->clickLink($link_text);
+ if ($this->lastDialogClass) {
+ $this->waitForNoElement('.' . $this->lastDialogClass);
+ }
+ $this->waitForOffCanvasToOpen($position);
+ $this->lastDialogClass = "$position-$link_index";
+
+ // Check that response text is on page.
+ $web_assert->pageTextContains("Thing $link_index says hello");
+ $off_canvas_tray = $this->getOffCanvasDialog();
+
+ // Check that tray is visible.
+ $this->assertEquals(TRUE, $off_canvas_tray->isVisible());
+
+ $tray_text = $off_canvas_tray->findById('drupal-off-canvas')->getText();
+ $this->assertEquals("Thing $link_index says hello", $tray_text);
+ }
+
}
diff --git a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php
index d3f446c..293d9f7 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php
@@ -60,14 +60,21 @@ abstract class OffCanvasTestBase extends JavascriptTestBase {
/**
* Waits for off-canvas dialog to open.
+ *
+ * @param string $position
+ * The position of the dialog.
+ *
+ * @throws \Behat\Mink\Exception\ElementNotFoundException
*/
- protected function waitForOffCanvasToOpen() {
+ protected function waitForOffCanvasToOpen($position = 'side') {
$web_assert = $this->assertSession();
// Wait just slightly longer than the off-canvas dialog CSS animation.
// @see core/misc/dialog/off-canvas.motion.css
$this->getSession()->wait(800);
$web_assert->assertWaitOnAjaxRequest();
$this->assertElementVisibleAfterWait('css', '#drupal-off-canvas');
+ // Check that the canvas is positioned on the side.
+ $web_assert->elementExists('css', '.ui-dialog-position-' . $position);
}
/**
@@ -128,4 +135,18 @@ abstract class OffCanvasTestBase extends JavascriptTestBase {
$this->assertNotEmpty($this->assertSession()->waitForElementVisible($selector, $locator, $timeout));
}
+ /**
+ * Dataprovider that returns theme name as the sole argument.
+ */
+ public function themeDataProvider() {
+ $themes = $this->getTestThemes();
+ $data = [];
+ foreach ($themes as $theme) {
+ $data[$theme] = [
+ $theme,
+ ];
+ }
+ return $data;
+ }
+
}
diff --git a/core/modules/toolbar/js/toolbar.es6.js b/core/modules/toolbar/js/toolbar.es6.js
index 4149d27..3113776 100644
--- a/core/modules/toolbar/js/toolbar.es6.js
+++ b/core/modules/toolbar/js/toolbar.es6.js
@@ -143,6 +143,27 @@
activeTab: $('.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a').get(0),
});
}
+
+ $(window).on({
+ 'dialog:aftercreate': (event, dialog, $element, settings) => {
+ const $toolbar = $('#toolbar-bar');
+ $toolbar.css('margin-top', '0');
+
+ // When off-canvas is positioned in top, toolbar has to be moved down.
+ if (settings.drupalOffCanvasPosition === 'top') {
+ const height = Drupal.offCanvas.getContainer($element).outerHeight();
+ $toolbar.css('margin-top', `${height}px`);
+
+ $element.on('dialogContentResize.off-canvas', () => {
+ const newHeight = Drupal.offCanvas.getContainer($element).outerHeight();
+ $toolbar.css('margin-top', `${newHeight}px`);
+ });
+ }
+ },
+ 'dialog:beforeclose': () => {
+ $('#toolbar-bar').css('margin-top', '0');
+ },
+ });
});
},
};
diff --git a/core/modules/toolbar/js/toolbar.js b/core/modules/toolbar/js/toolbar.js
index 7246420..f705a9b 100644
--- a/core/modules/toolbar/js/toolbar.js
+++ b/core/modules/toolbar/js/toolbar.js
@@ -97,6 +97,26 @@
activeTab: $('.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a').get(0)
});
}
+
+ $(window).on({
+ 'dialog:aftercreate': function dialogAftercreate(event, dialog, $element, settings) {
+ var $toolbar = $('#toolbar-bar');
+ $toolbar.css('margin-top', '0');
+
+ if (settings.drupalOffCanvasPosition === 'top') {
+ var height = Drupal.offCanvas.getContainer($element).outerHeight();
+ $toolbar.css('margin-top', height + 'px');
+
+ $element.on('dialogContentResize.off-canvas', function () {
+ var newHeight = Drupal.offCanvas.getContainer($element).outerHeight();
+ $toolbar.css('margin-top', newHeight + 'px');
+ });
+ }
+ },
+ 'dialog:beforeclose': function dialogBeforeclose() {
+ $('#toolbar-bar').css('margin-top', '0');
+ }
+ });
});
}
};
diff --git a/core/tests/Drupal/Tests/Core/Ajax/OpenOffCanvasDialogCommandTest.php b/core/tests/Drupal/Tests/Core/Ajax/OpenOffCanvasDialogCommandTest.php
index e2d933a..eca0c58 100644
--- a/core/tests/Drupal/Tests/Core/Ajax/OpenOffCanvasDialogCommandTest.php
+++ b/core/tests/Drupal/Tests/Core/Ajax/OpenOffCanvasDialogCommandTest.php
@@ -13,9 +13,11 @@ class OpenOffCanvasDialogCommandTest extends UnitTestCase {
/**
* @covers ::render
+ *
+ * @dataProvider dialogPosition
*/
- public function testRender() {
- $command = new OpenOffCanvasDialogCommand('Title', '<p>Text!</p>', ['url' => 'example']);
+ public function testRender($position) {
+ $command = new OpenOffCanvasDialogCommand('Title', '<p>Text!</p>', ['url' => 'example'], NULL, $position);
$expected = [
'command' => 'openDialog',
@@ -31,8 +33,9 @@ class OpenOffCanvasDialogCommandTest extends UnitTestCase {
'draggable' => FALSE,
'drupalAutoButtons' => FALSE,
'buttons' => [],
- 'dialogClass' => 'ui-dialog-off-canvas',
+ 'dialogClass' => 'ui-dialog-off-canvas ui-dialog-position-' . $position,
'width' => 300,
+ 'drupalOffCanvasPosition' => $position,
],
'effect' => 'fade',
'speed' => 1000,
@@ -40,4 +43,16 @@ class OpenOffCanvasDialogCommandTest extends UnitTestCase {
$this->assertEquals($expected, $command->render());
}
+ /**
+ * The data provider for potential dialog positions.
+ *
+ * @return array
+ */
+ public static function dialogPosition() {
+ return [
+ ['side'],
+ ['top'],
+ ];
+ }
+
}
diff --git a/core/themes/stable/css/core/dialog/off-canvas.motion.css b/core/themes/stable/css/core/dialog/off-canvas.motion.css
index b3158e9..60d8d6a 100644
--- a/core/themes/stable/css/core/dialog/off-canvas.motion.css
+++ b/core/themes/stable/css/core/dialog/off-canvas.motion.css
@@ -7,5 +7,5 @@
*/
.dialog-off-canvas-main-canvas {
- transition: all 0.7s ease;
+ transition: padding-right 0.7s ease, padding-left 0.7s ease, padding-top 0.3s ease;
}