Problem/Motivation

Currently core dialog links can set the attribute data-dialog-type to specify where the dialog will be either a generic dialog or a modal dialog.

The experimental Settings Module provides a new Off-canvas dialog that is not modal but is vastly different from the regular "dialog" that is provided by core(but not used anywhere in core).

In core there is currently not a way to specify which renderer class a dialog link should use so it can be handled by 1 of 2 classes \Drupal\Core\Render\MainContent\DialogRenderer or \Drupal\Core\Render\MainContent\ModalRenderer

The Setting Tray module currently get around this by looping through Drupal.ajax.instances and looking for the attribute data-dialog-renderer with the value 'off_canvas' and then manually updating the wrapper string sent in the query string of the instance's url.

Proposed resolution

'
Provide an new attribute for dialog links to use data-dialog-renderer.
Update ajax.js to handle this new attribute and concatenate it if provided to the _wrapper_format provided in the query string.
This would mean the new off-canvas tray provided in Settings Tray would use "drupal_dialog.offcanvas". The current links would continue to use "drupal_dialog" and "drupal_modal".

The current Drupal\outside_in\Render\MainContent\OffCanvasRender class uses this service by simply being tagged with the "format: drupal_dialog.offcanvas"
Note: this slight changed for the current implementation "format: drupal_dialog_offcanvas" to have "." show a clear split from is coming from the data-dialog-renderer attribute.

Remaining tasks

Review patch

User interface changes

None

API changes

New option to links attribute data-dialog-renderer
Used like:

$mylink['attributes'] = [
'class' => ['use-ajax'],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off-canvas',
];

For new services that are tagged with render.main_content_renderer that wanted to be used with the dialog library they would need to use the "format" tag, "drupal_dialog.SOME_RENDER" or "drupal_modal.SOME_RENDER"

Data model changes

None

CommentFileSizeAuthor
#45 2844261-45.patch17.26 KBtedbow
#45 interdiff-38-45.txt2.19 KBtedbow
#38 2844261-38.patch17.3 KBtedbow
#38 interdiff-32-38.txt520 bytestedbow
#32 2844261-32.patch17.57 KBtedbow
#32 interdiff-29-32.txt2.95 KBtedbow
#29 2844261-29.patch16.91 KBtedbow
#29 interdiff-27-29.txt2.33 KBtedbow
#27 2844261-27.patch15.86 KBGrandmaGlassesRopeMan
#27 interdiff-2844261-25-27.txt5.51 KBGrandmaGlassesRopeMan
#25 2844261-25.patch15.76 KBtedbow
#23 2844261-23.patch89.87 KBtedbow
#23 interdiff-21-23.txt12.04 KBtedbow
#21 interdiff-2844261-19-21.txt774 bytestedbow
#21 2844261-21.patch101.9 KBtedbow
#19 2844261-19.patch16.04 KBtedbow
#19 interdiff-2844261-16-19.txt1.07 KBtedbow
#16 2844261-16.patch15.94 KBtedbow
#16 interdiff-15-16.txt2.6 KBtedbow
#15 2844261-14.patch15.68 KBtedbow
#15 interdiff-2844261-11-14.txt1.61 KBtedbow
#11 2844261-11.patch15.93 KBtedbow
#9 interdiff-4-9.txt888 bytestedbow
#9 2844261-9.patch11.68 KBtedbow
#4 interdiff-2-4.txt7.13 KBtedbow
#4 2844261-4.patch10.81 KBtedbow
#2 2844261-2.patch3.68 KBtedbow
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tedbow created an issue. See original summary.

tedbow’s picture

Here is the first patch with needed changes to:

  1. ajax.js
  2. outside_in.js: to remove the logic that was handling data-dialog-renderer
  3. outside_in.services.yml: for the slight different format tagging of the service
tedbow’s picture

Status: Active » Needs review
tedbow’s picture

Added Javascript test to prove that dialog links with use a different renderer based on the 'data-dialog-renderer' attribute.

tedbow’s picture

Issue tags: +JavaScript
tedbow’s picture

Here are couple points from #2786459: "Offcanvas" tray should be using the existing dialog system
because I thought they might be brought up here

  1. Why not use data-dialog-type="wide_modal"? @nod_ explained that we want to keep data-dialog-type compatible with html5 dialog element.
  2. Why not add a new tag. "renderer" for services that are tagged with "render.main_content_renderer"?

    Because the $main_content_renderers are keyed by format in \Drupal\Core\Render\MainContent\MainContentRenderersPass::process then we can only have 1 main rendered for a "format". I tested changing outside_in.services.yml to use format: drupal_dialog. This does fix that Outside in module but would change the current behaviour of any request that was expecting DialogRenderer to be used, though I am not sure there are any in core(contrib could be using it).

tedbow’s picture

Status: Needs work » Needs review
FileSize
11.68 KB
888 bytes

OK forgot that OffCanvasDialogTest uses the format of the renderer service. Updated

tedbow’s picture

tedbow’s picture

Status: Closed (duplicate) » Needs review
FileSize
15.93 KB

Ok I am reopening this issue because #2784443: Move off-canvas functionality from Settings tray module into drupal.dialog.ajax library so that other modules can use it is blocked on other issues.

If this gets in there will be no there will no other changes needed to ajax.js in #2784443 and hopefully it will be easier to review

Rerolled the patch from #9 now that we are using es6.js files.

I also improve the tests a little bit. Now the test modal provides to 2 different render services and the test make sure the correct 1 is used for each test dialog link.

tedbow’s picture

Version: 8.3.x-dev » 8.4.x-dev
Status: Needs work » Needs review

Updating version

GrandmaGlassesRopeMan’s picture

Status: Needs review » Needs work
  1. +++ b/core/misc/ajax.es6.js
    @@ -526,7 +527,11 @@
    +    let wrapper = `drupal_${(element_settings.dialogType || 'ajax')}`;
    

    Can we put this conditional value where the property is declared?

  2. +++ b/core/modules/outside_in/js/outside_in.es6.js
    @@ -211,28 +211,18 @@
    +          return hasElement && rendererOffCanvas;
    

    This is slightly confusing syntax.

@tedbow

Some minor nits, but it looks like the tests don't pass.

tedbow’s picture

Status: Needs work » Needs review
FileSize
1.61 KB
15.68 KB

Ok the test failed because the dialog was still on the page after clicking close. It is probably a timing issue where locally it passes because things are running faster and there is no need to wait.

I am changing the test to make a drupalGet call to reload the page before clicking the different modal link.

There should probably be a waitForElementRemoved() like there is \Drupal\FunctionalJavascriptTests\JSWebAssert::waitForElement()

I will make the issue.

tedbow’s picture

@drpal thanks for the review. I didn't see when I posted #15

#14.1
No we shouldn't assign 'ajax' to element_settings.dialogType because the only values are 'modal' or 'dialog'.
Update the comment to reflect what is happening here
// If this element has a dialog type use if for the wrapper if not use 'ajax'

#14.2
I reworked that block and added commented. It was more complex because it was also doing the logic that has now move to ajax.es6.js. The block simpler now and hopefully less confusing.

tedbow’s picture

Issue summary: View changes
GrandmaGlassesRopeMan’s picture

Status: Needs review » Needs work
+++ b/core/modules/outside_in/js/outside_in.es6.js
@@ -211,28 +211,17 @@
+      // Loop through all Ajax links to set active editable ID.

Should probably be documentation about the filter expression

tedbow’s picture

OK updated the comments there to reflect that we are first finding the instances and then looping through them.

tim.plunkett’s picture

Only the smallest of nitpicks:

+++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
@@ -0,0 +1,77 @@
+   * WideModalRenderer constructor.
+   * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver

"Constructs a new WideModalRenderer." and needs a blank line.

The rest looks great.

tedbow’s picture

GrandmaGlassesRopeMan’s picture

Status: Needs review » Needs work
  1. +++ b/core/misc/ajax.es6.js
    @@ -60,6 +60,7 @@
    diff --git a/core/misc/ajax.es6.js.orig b/core/misc/ajax.es6.js.orig
    

    I don't think we need this file.

  2. +++ b/core/misc/ajax.es6.js.orig
    @@ -0,0 +1,1334 @@
    diff --git a/core/misc/ajax.es6.js.rej b/core/misc/ajax.es6.js.rej
    

    Or this file.

tedbow’s picture

Status: Needs work » Needs review
FileSize
12.04 KB
89.87 KB

@drpal thanks. yes let over from the patch command.

tedbow’s picture

Status: Needs work » Needs review
FileSize
15.76 KB

I messed up reroll in #23. Needed manual reroll because of #2880007: Auto-fix ESLint errors and warnings

Wim Leers’s picture

Status: Needs review » Needs work
  1. +++ b/core/misc/ajax.es6.js
    @@ -517,7 +518,13 @@
    +    // If this element has a dialog type use if for the wrapper if not use 'ajax'
    

    Nit: Missing trailing period.

  2. +++ b/core/modules/outside_in/js/outside_in.es6.js
    @@ -210,21 +210,17 @@
    +            // If there is an element and the renderer is 'off_canvas' then we want to add our changes.
    ...
    +        // Loop through all Ajax instances that use the 'off_canvas' renderer to set active editable ID.
    

    Nit: 80 cols.

  3. +++ b/core/modules/outside_in/outside_in.services.yml
    @@ -3,7 +3,7 @@ services:
         class: Drupal\outside_in\Render\MainContent\OffCanvasRender
    

    This must be named OffCanvasRenderer.

  4. +++ b/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.services.yml
    @@ -0,0 +1,13 @@
    +# Provide 2 render services that use the same class but behave differently
    

    s/render/main content renderer/

  5. +++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Controller/TestController.php
    @@ -0,0 +1,80 @@
    +          'data-dialog-type' => 'modal',
    ...
    +          'data-dialog-type' => 'modal',
    +          'data-dialog-renderer' => 'wide',
    ...
    +          'data-dialog-type' => 'modal',
    +          'data-dialog-renderer' => 'extra_wide',
    

    Looks good.

  6. +++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
    @@ -0,0 +1,77 @@
    + * Wide main content renderer for modal dialog requests.
    

    For consistency with the existing ones:

    Default main content renderer for wide modal dialog requests.
    
  7. +++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
    @@ -0,0 +1,77 @@
    + * This test class is copied from \Drupal\Core\Render\MainContent\ModalRenderer
    ...
    +class WideModalRenderer extends DialogRenderer {
    

    Why is this not extending ModalRenderer?

  8. +++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
    @@ -0,0 +1,77 @@
    +   * WideModalRenderer constructor.
    +   * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
    

    Nit: Needs blank line in between.

  9. +++ b/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
    @@ -0,0 +1,38 @@
    +    $style = $session_assert->waitForElementVisible('css', '.ui-dialog')->getAttribute('style');
    +    $this->assertNotContains('700px', $style);
    +    $this->assertNotContains('1000px', $style);
    +    $this->drupalGet('/dialog_renderer-test-links');
    +    $this->clickLink('Wide Modal!');
    +    $this->assertNotEmpty($session_assert->waitForElementVisible('css', '.ui-dialog[style*="width: 700px;"]'));
    +    $this->drupalGet('/dialog_renderer-test-links');
    +    $this->clickLink('Extra Wide Modal!');
    +    $this->assertNotEmpty($session_assert->waitForElementVisible('css', '.ui-dialog[style*="width: 1000px;"]'));
    

    Interesting way of testing this, seems sound.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
5.51 KB
15.86 KB

- #26.1,2,3,4,5,6,8,9 👍
- #26.7 🤷‍♀️
- Fixed an issue with two missing variables that were accidently removed in #21/#23, search & replace
- Simplified Drupal.ajax.instances filter.

tim.plunkett’s picture

+++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
@@ -0,0 +1,78 @@
+   * WideModalRenderer constructor.
+   * ¶

This is still wrong, needs to be something like "Constructs a new WideModalRenderer." and also there is now a trailing whitespace on the blank line

tedbow’s picture

#26.3 The class file still needed to be renamed.
#26.7 fixed
#26.8 and #28 fixed.

Status: Needs review » Needs work

The last submitted patch, 29: 2844261-29.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tedbow’s picture

Status: Needs work » Needs review
FileSize
2.95 KB
17.57 KB

Fixed failure.

Fixed an issue with two missing variables that were accidently removed in #21/#23, search & replace

Actually the variables were removed on purpose but the line referencing them should have also been removed.

Wim Leers’s picture

One nit, one question.

  1. +++ b/core/modules/outside_in/js/outside_in.es6.js
    @@ -199,7 +199,8 @@
    -   * Toggle the js-outside-edit-mode class on items that we want to disable while in edit mode.
    +   * Toggle the js-outside-edit-mode class on items that we want to disable
    +   * while in edit mode.
    

    Nit: Must fit on single line.

  2. +++ b/core/modules/outside_in/outside_in.services.yml
    @@ -1,9 +1,9 @@
       main_content_renderer.off_canvas:
    
    +++ b/core/modules/outside_in/src/Render/MainContent/OffCanvasRenderer.php
    @@ -13,7 +13,7 @@
     /**
      * Default main content renderer for off-canvas dialog requests.
      */
    

    Why is this still called "off canvas renderer"? Isn't that a remnant from when this module was called off_canvas.module?

    Is the plan to move this into core itself, outside this module, once this module becomes stable?

GrandmaGlassesRopeMan’s picture

@Wim Leers

#33.1 - Really?

Wim Leers’s picture

"Nit".

It's a coding standard rule we have. Maybe that changed with the shift to ES6? Not clear to me. Anyway, just a nit — should not hold up commit.

tedbow’s picture

#33.2
This will be moved into core dialog library in #2784443: Move off-canvas functionality from Settings tray module into drupal.dialog.ajax library so that other modules can use it

So it could be changed to OffCanvasDialogRenderer but I think that this is out of scope for this issue.
Also it follows the pattern of ModalRenderer though technically this is could also be called ModalDialogRenderer.

Modal and OffCanvas are both special kinds of dialogs.

droplet’s picture

It's a coding standard rule we have. Maybe that changed with the shift to ES6? Not clear to me. Anyway, just a nit — should not hold up commit.

No changes to ES6 as I known. It's same as PHP I think..

/** this style allow multiple line **/
// this one should be a single line no matter how loooooooooooooooooooooooooooooooooooog it is.
tedbow’s picture

I just noticed that #33.1 referenced a comment that was actually an out of scope change introduced in #27

Reverting that change. I think shortening that comment could be complicated because I don't think it is actually a correct description and should be separate issue.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

#36 Ok.
#38 Yay, that ends that yak shaving exercise immediately!

effulgentsia’s picture

Status: Reviewed & tested by the community » Needs review

This patch looks great. My only question is:

+++ b/core/misc/ajax.es6.js
@@ -60,6 +60,7 @@
+        element_settings.drupalDialogRenderer = $(this).data('dialog-renderer');

Why are we prefixing this with "drupal"? Why not just element_settings.dialogRenderer?

effulgentsia’s picture

Also, I'm not entirely thrilled with the word "renderer" in the attribute name, because when I think of "what's rendering the dialog?", I think of Drupal.dialog() or jQuery UI's dialog() function, and the value of 'data-dialog-renderer' isn't changing that (it could in theory, depending on the implementation of the PHP service that binds to this, but the off_canvas "renderer" is using the same jQuery UI dialog() function for client-side rendering that the base dialog type does). It's a "renderer" on the PHP side, but HTML attribute names and JS code are in the client-side world, and in that world, I don't see "off_canvas" as a separate renderer. Looks to me like it's more like a dialog "style", but we shouldn't use that word either, since that would lead people to think of CSS, rather than "style" in the more abstract sense.

I can't think of a better word, so won't hold this up on that.

effulgentsia’s picture

Actually, for #41, what do you all think of "preset"? I.e., "data-dialog-preset" => "off_canvas"? I don't know if that helps to clarify or not, so feel free to shoot it down.

tim.plunkett’s picture

That machine name corresponds to a class that implements \Drupal\Core\Render\MainContent\MainContentRendererInterface, which says

* The interface for "main content" renderers.

Example classes, and their corresponding machine name
drupal_ajax => \Drupal\Core\Render\MainContent\AjaxRenderer
drupal_modal => \Drupal\Core\Render\MainContent\ModalRenderer
drupal_dialog => \Drupal\Core\Render\MainContent\DialogRenderer
drupal_dialog_off_canvas => \Drupal\outside_in\Render\MainContent\OffCanvasRender

effulgentsia’s picture

That machine name corresponds to a class that implements \Drupal\Core\Render\MainContent\MainContentRendererInterface

Right, per #41, that's PHP-centric. But we're talking about a name for an HTML attribute and JS variable. How that's connected back to PHP is an implementation detail.

tedbow’s picture

Fixes #40

I think we I added this was thinking of things like `dialogOptions.drupalAutoButtons` but dialogOptions is different because it is options passed to Jquery Ui dialog and so prefix drupal specific things.

That is not need here.

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community

Thanks @tedbow!

tedbow’s picture

Re #41 while I do think preset might be better word for what this being used for in the off-canvas example. This feature will potentially allow a module provide dialog system alongside core's that uses a totally different JS library so renderer I think portrays more the potential of what it could be used for.

effulgentsia’s picture

Thanks for #45, and I see your point in #47, so I'm about to commit this. Ticking credit boxes for reviewers.

  • effulgentsia committed 76767ed on 8.4.x
    Issue #2844261 by tedbow, drpal, tim.plunkett, Wim Leers, droplet: Allow...
effulgentsia’s picture

Status: Reviewed & tested by the community » Fixed

Pushed to 8.4.x.

tedbow’s picture

@effulgentsia thanks for review and committing!

@tim.plunkett, @drpal, @Wim Leers @droplet thanks for reviews!

effulgentsia’s picture

+++ b/core/misc/ajax.es6.js
@@ -517,7 +518,13 @@
-    ajax.options.url += `${Drupal.ajax.WRAPPER_FORMAT}=drupal_${element_settings.dialogType || 'ajax'}`;
+    // If this element has a dialog type use if for the wrapper if not use 'ajax'.
+    let wrapper = `drupal_${(element_settings.dialogType || 'ajax')}`;
+    if (element_settings.dialogRenderer) {
+      wrapper += `.${element_settings.dialogRenderer}`;
+    }
+    ajax.options.url += `${Drupal.ajax.WRAPPER_FORMAT}=${wrapper}`;

There might be a side effect to having committed this. ExceptionJsonSubscriber::getHandledFormats() has a hard-coded list of formats. drupal_dialog.off_canvas isn't one of them, so errors within such a request might not get handled/formatted correctly. This could use a follow-up issue for solving.

Wim Leers’s picture

#52 Wow, great catch!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.