Blocked by: #2853509: Don't render status messages if there are no messages but also include their assets if there might be

Problem/Motivation

Provide a consistent method for displaying messages in JavaScript.

Currently the Settings tray module needs to provide various message via JavaScript in:
#2773601: Display "You are now in edit mode" prompt when user enters edit mode
#2785047: In Outside In mode, form validation messages should appear in the off-canvas tray, not the main page

And also Inline Form Errors needs to be able to update messages through JavaScript:
#2876321: Update inline form errors summary after form validation with AJAX

Proposed resolution

Change div for {{ messages }} to always be rendered and add a data-drupal-messages attribute for JavaScript to select.

Add new methods for:

  • Adding a message
  • Removing one or multiple messages
  • Clearing all messages of a specific type
  • Clearing all messages

How to test manually

  1. Apply the latest patch
  2. Install standard
  3. Install the js_message_test module. You need to ensure $settings['extension_discovery_scan_tests'] = TRUE; is in your settings.php in order to enable it.
  4. Visit the url /js_message_test_link on your site.

Remaining tasks

Review patch
Write change record

API changes

Persistent div for {{ messages }} would be required in themes.

API Additions

const messageInterface = new Drupal.Message();

const messageId = messageInterface.add(message, options);
const messageList = messageInterface.select(type);

messageInterface.remove(messageId);

messageInterface.clear();

Original report by kkaefer

While developing some JavaScript enhancements for Drupal, I am facing the problem of how to indicate the status of an action properly. For example, when editing a label, the user needs to know if the action has been successful (saving the new title) or if it failed. Of course, some widgets can use their own status indicating system (like the autocomplete textfield), but others have to display text.

But – we already have status messages in Drupal! Take a look at theme_status_message(). We just need to make sure that the status message div is always there so that we can inject new items via JavaScript... How about that? Post your opinion or your own suggestion for this issue.

Release notes

In order to provide the ability to indicate the status of an action from JavaScript, a new new API has been added. This interface provides the ability to add and remove single or multiple messages, and clear all messages to a global or unique location on the page.

CommentFileSizeAuthor
#419 interdiff.txt1.46 KBlauriii
#419 77245-419.patch45.37 KBlauriii
#415 interdiff-77245-414.txt821 bytesGrandmaGlassesRopeMan
#411 interdiff-77245-406-411.txt1.87 KBphenaproxima
#411 77245-411.patch45.31 KBphenaproxima
#406 interdiff-77245-404-407.txt1.01 KBphenaproxima
#406 77245-406.patch45.4 KBphenaproxima
#404 interdiff-77245-394-404.txt12.81 KBGrandmaGlassesRopeMan
#404 77245-404.patch45.42 KBGrandmaGlassesRopeMan
#394 interdiff-77245-388-394.txt5.49 KBphenaproxima
#394 77245-394.patch45.16 KBphenaproxima
#391 77245-messages-391.patch59.81 KBtim.plunkett
#391 77245-messages-391-interdiff.txt6.34 KBtim.plunkett
#390 77245-messages-369.patch54.82 KBtim.plunkett
#390 77245-messages-369-interdiff.txt11.18 KBtim.plunkett
#388 interdiff-77245-383-388.txt4.56 KBphenaproxima
#388 77245-388.patch44.71 KBphenaproxima
#383 interdiff-77245-378-383.txt3.63 KBphenaproxima
#383 77245-383.patch44.17 KBphenaproxima
#378 77245-378.patch41.75 KBphenaproxima
#362 77245-362.patch41.79 KBGrandmaGlassesRopeMan
#362 interdiff-77245-357-362.txt5.64 KBGrandmaGlassesRopeMan
#360 77245-360.patch41.82 KBGrandmaGlassesRopeMan
#357 interdiff-77245-355-357.txt22.83 KBGrandmaGlassesRopeMan
#357 77245-357.patch103.57 KBGrandmaGlassesRopeMan
#355 77245-355.patch39.32 KBtedbow
#355 interdiff-352-355.txt3.31 KBtedbow
#352 77245-351-duplicate-headings.PNG13.92 KBandrewmacpherson
#351 interdiff-77245-341-351.txt1.19 KBGrandmaGlassesRopeMan
#351 77245-351.patch39.39 KBGrandmaGlassesRopeMan
#341 77245-341.patch39.59 KBtedbow
#341 interdiff-338-341.txt2.99 KBtedbow
#338 77245-338.patch39.62 KBtedbow
#338 interdiff-336-338.txt6.68 KBtedbow
#336 77245-336.patch38.8 KBtedbow
#336 interdiff-332-336.txt1.35 KBtedbow
#332 interdiff-318-332.txt584 bytestedbow
#332 77245-332.patch38.14 KBtedbow
#327 77245-327.patch39.75 KBtedbow
#327 interdiff-318-327.txt5.19 KBtedbow
#318 interdiff-77245-311-318.txt4.31 KBGrandmaGlassesRopeMan
#318 77245-318.patch38.52 KBGrandmaGlassesRopeMan
#311 interdiff-77245-296-311.txt2.55 KBGrandmaGlassesRopeMan
#311 77245-311.patch38.57 KBGrandmaGlassesRopeMan
#296 interdiff-77245-293-296.txt3.11 KBGrandmaGlassesRopeMan
#296 77245-296.patch38.17 KBGrandmaGlassesRopeMan
#293 interdiff-77245-292-293.txt2.12 KBGrandmaGlassesRopeMan
#293 77245-293.patch38.09 KBGrandmaGlassesRopeMan
#292 interdiff-77245-288-292.txt1.94 KBGrandmaGlassesRopeMan
#292 77245-292.patch38.12 KBGrandmaGlassesRopeMan
#288 77245-288.patch38.33 KBchr.fritsch
#287 77245-287.patch39.18 KBWim Leers
#282 77245-282.patch38.08 KBGrandmaGlassesRopeMan
#282 interdiff-77245-280-282.txt810 bytesGrandmaGlassesRopeMan
#280 77245-280.patch38.07 KBGrandmaGlassesRopeMan
#280 interdiff-77245-279-280.txt4.21 KBGrandmaGlassesRopeMan
#279 77245-279.patch37.79 KBtedbow
#279 interdiff-277-279.txt4.7 KBtedbow
#279 not_in_inner_div.png19.86 KBtedbow
#278 js_msg.png33.66 KBtedbow
#278 server_msg.png27.64 KBtedbow
#277 77245-277.patch36.59 KBtedbow
#277 interdiff-275-277.txt2.04 KBtedbow
#275 77245-275.patch36.67 KBtedbow
#275 interdiff-272-275.txt3.19 KBtedbow
#272 interdiff-77245-270-272.txt484 bytesGrandmaGlassesRopeMan
#272 77245-272.patch36.32 KBGrandmaGlassesRopeMan
#270 77245-270.patch36.32 KBGrandmaGlassesRopeMan
#269 77245-269.patch51.2 KBGrandmaGlassesRopeMan
#269 interdiff-77245-266-269.txt16.84 KBGrandmaGlassesRopeMan
#266 77245-266.patch36.64 KBGrandmaGlassesRopeMan
#266 interdiff-77245-263-266.txt1.12 KBGrandmaGlassesRopeMan
#263 77245-263.patch36.75 KBGrandmaGlassesRopeMan
#263 interdiff-77245-261-263.txt3.21 KBGrandmaGlassesRopeMan
#261 77245-261.patch36.69 KBGrandmaGlassesRopeMan
#261 interdiff-77245-256-261.txt1.76 KBGrandmaGlassesRopeMan
#256 77245-256.patch36.71 KBtedbow
#256 interdiff-249-256.txt8.55 KBtedbow
#250 interdiff-77245-247-249.txt11.17 KBGrandmaGlassesRopeMan
#250 77245-249.patch35.33 KBGrandmaGlassesRopeMan
#247 interdiff-77245-245-247.txt15.1 KBGrandmaGlassesRopeMan
#247 77245-247.patch35.15 KBGrandmaGlassesRopeMan
#245 77245-245.patch50.05 KBGrandmaGlassesRopeMan
#245 77245-237-245.txt21.91 KBGrandmaGlassesRopeMan
#242 77245-242.patch4.79 KBpk188
#240 77245-240.patch4.84 KBpk188
#237 interdiff-77245-235-237.txt1.81 KBGrandmaGlassesRopeMan
#237 77245-237.patch29.53 KBGrandmaGlassesRopeMan
#235 core-js-status-messages-77245-234.patch29.29 KBnod_
#231 core-js-status-messages-77245-231.patch29.29 KBnod_
#231 interdiff-228-231.txt9.92 KBnod_
#228 interdiff-77245-221-228.txt1.85 KBGrandmaGlassesRopeMan
#228 77245-228.patch27.12 KBGrandmaGlassesRopeMan
#221 interdiff-77245-218-221.txt827 bytesGrandmaGlassesRopeMan
#221 77245-221.patch28.49 KBGrandmaGlassesRopeMan
#219 77245-218.patch28.5 KBGrandmaGlassesRopeMan
#218 interdiff-217-218.txt432 bytesGrandmaGlassesRopeMan
#217 interdiff-208-217.txt6.79 KBGrandmaGlassesRopeMan
#217 77245-217.patch28.48 KBGrandmaGlassesRopeMan
#208 core-js-status-messages-77245-208.patch31.58 KBnod_
#208 interdiff-203-208.txt5.11 KBnod_
#203 interdiff.txt16.02 KBnod_
#203 core-js-status-messages-77245-203.patch31.53 KBnod_
#199 77245-199.patch27.92 KBtedbow
#199 interdiff-77245-196-199.txt3.68 KBtedbow
#196 77245-196.patch27.78 KBtedbow
#196 interdiff-77245-194-196.txt283 bytestedbow
#194 interdiff-190-194.txt1.76 KBGrandmaGlassesRopeMan
#194 77245-194.patch27.75 KBGrandmaGlassesRopeMan
#190 interdiff.txt1.09 KBGrandmaGlassesRopeMan
#190 77245-190.patch27.52 KBGrandmaGlassesRopeMan
#187 interdiff.txt13.23 KBGrandmaGlassesRopeMan
#187 77245-187.patch27.77 KBGrandmaGlassesRopeMan
#184 interdiff.txt20.39 KBGrandmaGlassesRopeMan
#184 77245-184.patch35.2 KBGrandmaGlassesRopeMan
#180 core-js-status-messages-77245-180.patch29.63 KBnod_
#180 interdiff.txt10.66 KBnod_
#178 interdiff-173-178.txt3.53 KBGrandmaGlassesRopeMan
#178 77245-178.patch27.14 KBGrandmaGlassesRopeMan
#175 77245-175.patch27.11 KBtedbow
#175 interdiff-173-175.txt727 bytestedbow
#173 77245-173.patch26.97 KBtedbow
#173 interdiff-171-173.txt17.06 KBtedbow
#171 77245-171.patch22.52 KBtedbow
#171 interdiff-165-171.txt1.39 KBtedbow
#169 interdiff.txt12.26 KBPavan B S
#169 77245-167.patch35.69 KBPavan B S
#166 77245-166.patch23.49 KBWim Leers
#166 interdiff.txt1.73 KBWim Leers
#165 77245-165.patch22.54 KBWim Leers
#165 interdiff.txt2.33 KBWim Leers
#163 77245-163.patch20.33 KBtedbow
#163 interdiff-161-163.txt1.58 KBtedbow
#161 77245-161.patch20.3 KBtedbow
#161 interdiff-157-161.txt7.71 KBtedbow
#157 77245-157.patch18.65 KBtedbow
#157 interdiff-154-157.txt9.26 KBtedbow
#154 77245-154.patch16.07 KBtedbow
#154 interdiff-148-154.txt9.45 KBtedbow
#148 77245-148.patch8.82 KBjoelpittet
#145 77245-145.patch5.62 KBjoelpittet
#145 interdiff.txt2.61 KBjoelpittet
#144 a_place_for_javascript-77245-144.patch6.38 KBhitesh-jain
#140 interdiff-129-140.txt1.26 KBBarisW
#140 a_place_for_javascript-77245-140.patch10.71 KBBarisW
#132 javascript_status_msg-77245-132.patch7.98 KBjoginderpc
#129 77245-129-interdiff.txt503 byteslokapujya
#129 77245-129.patch11.13 KBlokapujya
#123 a_place_for_javascript-77245-123.patch10.61 KBkostyashupenko
#119 a_place_for_javascript-77245-119.patch7.04 KBmgifford
#112 js-1.png18.24 KBdroplet
#111 interdiff.txt2.53 KBgoogletorp
#111 a_place_for_javascript-77245-111.patch10.55 KBgoogletorp
#108 interdiff.txt1.71 KBgoogletorp
#108 a_place_for_javascript-77245-108.patch9.83 KBgoogletorp
#105 a_place_for_javascript-77245-105.patch10.44 KBgoogletorp
#94 interdiff.txt2.12 KBnod_
#93 core-js-status-messages-77245-93.patch9.74 KBnod_
#89 core-js-status-messages-77245-89.patch8.74 KBnod_
#86 core-js-status-messages-77245-86.patch8.87 KBidflood
#86 interdiff.txt1.45 KBidflood
#80 core-js-status-messages-77245-80.patch8.81 KBidflood
#80 interdiff.txt1.75 KBidflood
#75 interdiff.txt415 bytesjavisr
#75 core-js-status-messages-77245-75.patch8.21 KBjavisr
#73 core-js-status-messages-77245-73.patch8.22 KBrteijeiro
#67 core-js-status-messages-77245-67.patch6.53 KBnod_
#62 core-js-status-messages-77245-62.patch8.27 KBnod_
#55 core-js-status-messages-77245-55.patch7.74 KBrteijeiro
#48 core-js-status-messages-77245-48.patch7.79 KBrteijeiro
#43 core-js-status-messages-77245-43.patch7.9 KBnod_
#38 core-js-status-messages-77245-38.patch7.91 KBsarav.din33
#33 core-js-status-messages-77245-33.patch8.03 KBnod_
#31 core-js-status-messages-77245-31.patch6.21 KBsocketwench
#25 core-js-status-messages-77245-25.patch8.15 KBnod_
#24 core-js-status-messages-77245-24.patch8.3 KBnod_
#15 core-js-status-messages-77245-15.patch7.15 KBnod_
#12 core-js-status-messages-77245-12.patch3.43 KBnod_
#10 edit_js-77245-9.patch1.65 KBvineet.osscube
#9 edit_js-77245-9.patch1.65 KBvineet.osscube
#7 content_edit_ajax_message-7342336-7.patch2.34 KBvineet.osscube
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Anonymous’s picture

PLEASE! This was something I wanted a while ago.

Every theme would need to remove the if statement if $message != ''

would be great to have this.

Steven’s picture

There would need to be a style to make the div invisible if empty though, so that JS can reveal it. Otherwise you might get an empty container, depending on the theme.

nod_’s picture

Issue tags: +js-novice

I'd like that, it should be fairly simple.

vineet.osscube’s picture

So initial patch for review :)-

shivenduray’s picture

Patch is not working due to white space problem in patch "content_edit_ajax_message-7342336-7.patch" !!!

vineet.osscube’s picture

FileSize
1.65 KB

Hi Team,

I've updated the toolbar-view.js file to show status message under div as a solution for displaying status message on field edit. Attaching patch for review.

vineet.osscube’s picture

Status: Active » Needs review
FileSize
1.65 KB

I've updated the toolbar-view.js file to show status message under div as a solution for displaying status message on field edit. Attaching patch for review :)-

nod_’s picture

Status: Needs review » Needs work

The current patch won't work for core. We need something more generic.

If we're going down this road we need to add a JS API to handle messages. Like it was said in #1 we'd need all theme to remove the if ($messages) {} so that the DOM element is always available. We could be using that for the tabledrag and views warning about unsaved changes too.

Ideally I wouldn't mind to have Drupal.announce use that message area to put all it's stuff for screen readers too. Would keep everything at the same place.

nod_’s picture

Status: Needs work » Needs review
FileSize
3.43 KB

Totally not ready but putting NR for attention.

Patch contains what I mean. Needs to be polished and all, probably merged with drupal.announce too actually.

adds 2 methods:

// Add a message to the message area. Used the same as drupal_set_message().
Drupal.message(message, type);

// Remove everything in the message area.
Drupal.message.clear();

The message area is designated with a data-drupal-messages attribute because id of the zone is different in default template and the seven theme. Also bartik adds a wrapper we don't want to have to deal with.

I don't think we want a fancy "dismiss message" functionality. If you want to get rid of the messages, just reload the page like it's done currently. simple > fancy.

jessebeach’s picture

Needs to be polished and all, probably merged with drupal.announce too actually.

(bolding added).

Yes, indeed. We've already solved an issue with aria-live and near-simultaneous messages: #1959306: Drupal.announce does not handle multiple messages at the same time; Improve the utility so that simultaneously calls are read..

I'd be fine with renaming announce to message or with merging a visual output component to announce in addition to the audio output it currently supports.

I see mention of aria-live in comments in the patch.

+ * Builds a div element with the aria-live attribute and attaches it
+ * to the DOM.

but I don't see the attribute in the code yet.

+Drupal.theme.message = function (message, type) {
+  return '<div class="messages ' + type +'">' + message + '</div>';
+};

vineet.osscube, do you feel like you can take a shot at merging the code here in this issue into the existing announce method?

nod_’s picture

yeah I copied the annouce file and removed all the comments, not all of them it seems. It'll need commenting.

nod_’s picture

merged in announce.js, not too bad. At least I don't have the whole library mess like before.

ericduran’s picture

+++ b/core/misc/message.jsundefined
@@ -0,0 +1,38 @@
+  messagesElement.style.display = 'block';

Hmm, Do we really need to do this?

Wouldn't it be better to do a simple class toggling.

nod_’s picture

yeah that was a very rough patch. new one is better.

ericduran’s picture

Also a lot of people are going to dis-like the forcing of the message block to be printed at all times.

oresh’s picture

print only when it fails :)

Wim Leers’s picture

Status: Needs review » Needs work

Great to see this nicely integrated with Drupal.announce :)

+++ b/core/misc/announce.jsundefined
@@ -25,6 +28,9 @@
+      if (!messagesElement) {
+        messagesElement = document.querySelector('[data-drupal-messages]');
+      }
       // Create only one aria-live element.
       if (!liveElement) {
         liveElement = document.createElement('div');
@@ -32,7 +38,7 @@

@@ -32,7 +38,7 @@
         liveElement.className = 'element-invisible';
         liveElement.setAttribute('aria-live', 'polite');
         liveElement.setAttribute('aria-busy', 'false');
-        document.body.appendChild(liveElement);
+        messagesElement.parentNode.insertBefore(liveElement, messagesElement);

- What if a theme specifically doesn't want a <div data-drupal-messages />? Shouldn't that still be allowed?
- When a theme specifically doesn't have such an element, this will break Drupal.announce() too, because it now effectively depends on such an element.

+++ b/core/misc/announce.jsundefined
@@ -41,34 +47,47 @@
+    if (messages.length) {
+      text = [];
+      for (var n = 0, nl = messages.length; n < nl; n++) {
+        message = messages.pop();

I don't like that Drupal.announce() is now also handling the rendering for Drupal.message().

AFAICT there are valid use cases to use Drupal.announce() *without* showing a visible message, so I don't think we should let this internal overlap occur.

seutje’s picture

It also seems to imply a single instance, is that something we want to force people into, or would supporting multiple messages areas be a bit over the top?

nod_’s picture

about [drupal-data-messages] I think it should be required for theme to have an element with the attribute all the time.

Technically it's not Drupal.announce() that takes care of displaying, it's announce(). If I rename it processQueues() would that help? it's what it's doing after all. If you want announce and not a message then don't fill both queues at the same time :p

I'd say we keep it in only one place. If contrib want to use it somewhere else they'll probably want to be able to "stick" one message over pages, close a specific message, make it show up fancy and all. All sort of stuff we're not supporting with the current patch. I'm pretty sure someone will throw together a module with some jQuery plugin to handle that (and they'll just need to override Drupal.message()).

nod_’s picture

Status: Needs work » Needs review
FileSize
8.3 KB

Updated patch, there is a problem in how the debounce is actually used.

I separated the messages and announce processing so that should make you happy Wim :) They're even in two files just in case contrib wants to mess with it.

nod_’s picture

ok little small patch creation issue :p

nod_’s picture

BTW to see what's happening try that in the JS console:

Drupal.message('good message');
Drupal.message('Bad message', 'error');
Drupal.message('Careful message', 'warning');
Drupal.message('Whatever', 'whatever');
designfitsu’s picture

Patch doesn't apply anymore since removal of tpl.php files for TWIG. Tagging for reroll.

socketwench’s picture

Assigned: socketwench » Unassigned
FileSize
6.21 KB

Re-rolled.

jibran’s picture

Status: Needs review » Needs work

I think some page.html.twig changes are missing. Thanks for the re-roll.

nod_’s picture

Status: Needs work » Needs review
FileSize
8.03 KB

reroll with template changes.

mr_tuff’s picture

Status: Needs review » Needs work

At DrupalCon Portland 2013 I was assigned to manually test this patch. Patch 25 worked for me but changes happened rapidly and I was unsure how to reset my git branch to the latest.

Patch 25 tests I completed:

System:
OS X 10.8.3 (12D78)

Browsers tested:
Safari
6.0.4 (8536.29.13) - PASSED

Firefox
13.0.1 and 21.0 - PASSED

Opera
12.15 build 1748 - PASSED

UPDATE: I did some reading and figured out how to update my branch and apply the patch but now I get this:

MBP:drupal user$ patch -p1 < core-js-status-messages-77245-33.patch
patching file core/misc/announce.js
The next patch would create the file core/misc/message.js,
which already exists! Assume -R? [n] yes
patching file core/misc/message.js
patching file core/modules/system/system.module
Hunk #1 succeeded at 1212 (offset -13 lines).
patching file core/modules/system/templates/page.html.twig
patching file core/themes/bartik/templates/page.html.twig
patching file core/themes/seven/templates/page.html.twig
MBP:drupal user$ git status
# On branch issue_77245
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
# modified: core/misc/announce.js
# modified: core/modules/system/system.module
# modified: core/modules/system/templates/page.html.twig
# modified: core/themes/bartik/templates/page.html.twig
# modified: core/themes/seven/templates/page.html.twig
#
# Untracked files:
# (use "git add ..." to include in what will be committed)
#
# core-js-status-messages-77245-33.patch
# core/modules/system/system.module.orig
# sites/default/files/
# sites/default/settings.php
no changes added to commit (use "git add" and/or "git commit -a")

I've exhausted my current level of skills. Hope this helps.

nod_’s picture

Status: Needs work » Needs review

The important part is the testing. For git as long as the test bot doesn't complain it's all good.

sarav.din33’s picture

Once i apply #33 using following command,

In Terminal:
git apply core-js-status-messages-77245-33.patch

My patch file is in my root folder. Its named as "core-js-status-messages-77245-33.patch"

I got the following error:
error: patch failed: core/misc/announce.js:16
error: core/misc/announce.js: patch does not apply

Is there any mistake in unix command?

Wim Leers’s picture

Status: Needs review » Needs work

#36: No, Drupal 8 has just changed so much already that the patch no longer is applicable. It needs to be rerolled. Hence setting to "needs work".

sarav.din33’s picture

#33 Patch rerolled. Refer (https://drupal.org/patch/reroll)

nod_’s picture

nod_’s picture

Status: Needs work » Needs review
FileSize
7.9 KB

reroll

rteijeiro’s picture

Re-rolled.

dealancer’s picture

Messaging functionality works fine for me, but I have not tested screen reading yet.

nod_’s picture

Issue tags: +markup

Patch is good to go, pinged morten about adding empty div everywhere.

mortendk’s picture

Issue tags: -markup

Can we please not use ID's if they are not absolutely extremely nessesary - they make the css work further down the road harder.

  1. +++ b/core/modules/system/templates/page.html.twig
    @@ -104,7 +104,7 @@
    -  {{ messages }}
    +  <div id="messages" data-drupal-messages>{{ messages }}</div>
    

    please dont use id's unless its absolutely nessesary, id to make the life of the css harder.
    maybe a class="drupalsystem--messages" instead

    Im generally not happy for having empty divs floating around unless theres a value inside of em. if we can test on {{ messages }} and test if its empty its fine with me even that its a bit cluttered

  2. +++ b/core/themes/bartik/templates/page.html.twig
    @@ -128,11 +128,9 @@
    +  <div id="messages"><div class="section clearfix" data-drupal-messages>
    +    {{ messages }}
    +  </div></div> <!-- /.section, /#messages -->
    

    bartik is its own monster (that the frontend team will take on later based on the principles of d8 theming) anyways please dont add more id's in use a class like drupalsystem--messages instead

jessebeach’s picture

This should be the only drupal system messages div on the page, so for the purposes of JavaScript, having an ID makes finding it easier. The document keeps a list of ID'ed elements on the page and looking them up is very fast.

For the purposes of styling, I agree, we shouldn't be using the ID. I don't see any CSS in this patch, so it's not introducing any ID-keyed styling.

jessebeach’s picture

Status: Needs review » Needs work
--- /dev/null
+++ b/core/misc/message.jsundefined
@@ -0,0 +1,58 @@
+/**
+ *
+ */

The messages.js file is missing the file doc block.

+++ b/core/misc/message.jsundefined
@@ -0,0 +1,58 @@
+    // Save the text and priority into a closure variable. Multiple simultaneous
+    // announcements will be concatenated and read in sequence.

This is a copy-paste block of comment from announce.js. It should be updated.

rteijeiro’s picture

Assigned: Unassigned » rteijeiro

I will fix it ;)

rteijeiro’s picture

Status: Needs work » Needs review
FileSize
7.74 KB

I guess this patch contains all @mortendk and @jessebeach suggestions. Tried to comment the function but I guess it's too verbose. I accept more suggestions regarding that :)

Re-rolled with the latest changes and updated page.html.twig for Seven too.

Also I have a question about the return of Drupal.theme.message function:

return '<div class="messages messages--' + type + '">' + message + '</div>';

It includes a new div so I'm not sure if we should update it according to @mortendk suggestions.

nod_’s picture

You can't put the if messages. The element has to be on the page all the time, othewise JS can't find it and the whole thing can't work.

jessebeach’s picture

Also I have a question about the return of Drupal.theme.message function:

return '<div class="messages messages--' + type + '">' + message + '</div>';

It includes a new div so I'm not sure if we should update it according to @mortendk suggestions.

rteijeiro, I'm not sure how you're suggesting to update this div. Can you elaborate?

jessebeach’s picture

Also I have a question about the return of Drupal.theme.message function:

return '<div class="messages messages--' + type + '">' + message + '</div>';

It includes a new div so I'm not sure if we should update it according to @mortendk suggestions.

rteijeiro, I'm not sure how you're suggesting to update this div. Can you elaborate?

rteijeiro’s picture

Status: Needs review » Needs work

@nod_: Ok, I will fix it :'(

@jessebeach: I mean include the "drupalsystem--messages" class suggested by @mortendk instead the "messages" class.

mortendk’s picture

urgh just rmemebered the updated name conversions for css classes

the classname should be prefixed with a js- (js-drupalmessages) that will tell the themers to stay away from messing iwth that class & follow our standards (https://drupal.org/node/1887918#separate-concerns)

rteijeiro’s picture

@mortendk: The js-drupalmessages class should be in the tpl div or in the div returned by Drupal.theme.message function in the message.js file?

I will try to ping you in IRC in order to get this issue fixed ;)

nod_’s picture

Category: feature » task
Status: Needs work » Needs review
Issue tags: -Needs issue summary update +JavaScript
FileSize
8.27 KB

Talked some more with mortendk, here is the reroll.

This patch adds a (usually empty) <div data-drupal-messages> on all pages for all themes to be able to print errors and warnings from the JS (to finally fix #1232416: Drupal alerts "An AJAX HTTP request terminated abnormally" during normal site operation, confusing site visitors/editors).

I did not touch the classes/ids or anything because it's purely a JS patch. To mess with the markup a follow-up is needed, as long as the data- attribute is still around, the JS will work regardless of what happens to the markup/classes/ids.

The patch also fixes a bad use of debounce in announce.js.

rteijeiro’s picture

Status: Needs review » Reviewed & tested by the community

It looks good. Patch applies well and it creates the div placeholder for messages.

Maybe RTBC now?

seutje’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/misc/announce.js
@@ -42,38 +42,43 @@
-    var priority = 'polite';
-    var announcement;
+    var priority, announcement;

Looks like this breaks the default priority, making it default to "undefined".

Also, the initial implementation didn't seem to follow the spec, as that says that "off" is the default, not "polite".

ref: http://www.w3.org/TR/wai-aria/states_and_properties#aria-live

Also, I was under the impression the idea was to have the messages trigger the announce, otherwise it would be impossible for a11y users to know there was an error, wouldn't it?

nod_’s picture

Making .message use .annonce is interesting. for now I just want whatever to close down the HTTP:0 issue

rteijeiro’s picture

Issue summary: View changes

update for #62 patch

Wim Leers’s picture

The patch in #67 simply does not include message.js, hence the test failures.

rteijeiro’s picture

Status: Needs work » Needs review
FileSize
8.22 KB

Thanks @Win Leers for the help :)

Added message.js file to #67 patch

jibran’s picture

+++ b/core/modules/system/system.module
@@ -983,6 +984,19 @@ function system_library_info() {
+  ¶

Extra white space.

javisr’s picture

remove whitespace commented in #74

idflood’s picture

Tested the patch in #75 by calling the following javascript from the chrome dev tool:

Drupal.message('Testing javascript message error', 'error');
Drupal.message('Testing javascript message status', 'status');
Drupal.message('Testing javascript message warning', 'warning');

This worked beautifully and added the message to the page (tested with bartik, stark and seven themes). The patch also applies cleanly so maybe this can be marked RTBC.

bannockree’s picture

Issue tags: -Needs manual testing

I read through all the comments, and while I didn't see any specifics on what needed manual testing, it appears that adequate manual testing has been done, so I'm removing the "needs manual testing" tag. Please put it back if I'm wrong!

nod_’s picture

Status: Needs review » Reviewed & tested by the community

Ah yes manual testing was in #76. Because no code is using it yet, needs to be triggered manually :)

RTBC per #76.

Wim Leers’s picture

Status: Reviewed & tested by the community » Needs work

I'm sorry, there are a bunch of nitpicks left:

  1. +++ b/core/misc/message.js
    @@ -0,0 +1,58 @@
    + *
    

    Missing file-level docs.

  2. +++ b/core/misc/message.js
    @@ -0,0 +1,58 @@
    +
    +
    

    2 newlines, should probably be 1.

  3. +++ b/core/misc/message.js
    @@ -0,0 +1,58 @@
    +   * to the DOM.
    ...
    +   * Builds a div element with the aria-live attribute and attaches it
    

    Incorrect wrapping: more of the text fits on the first line.

  4. +++ b/core/misc/message.js
    @@ -0,0 +1,58 @@
    +  Drupal.theme.message = function (message) {
    

    Docs missing.

  5. +++ b/core/misc/message.js
    @@ -0,0 +1,58 @@
    +  Drupal.message = function (message, type) {
    

    Docs missing.

  6. +++ b/core/misc/message.js
    @@ -0,0 +1,58 @@
    +  function processMessages () {
    

    I *think* that we also want docs for this.

idflood’s picture

Status: Needs work » Needs review
FileSize
1.75 KB
8.81 KB

This patch should fix the nitpicks described in #79. However there are certainly better comments to add than what I have written.

Wim Leers’s picture

  1. +++ b/core/misc/message.js
    @@ -41,6 +45,15 @@
    +   * Display a message on the page.
    
    @@ -23,7 +24,10 @@
    +   * Display all queued messages in one pass instead of one after the other.
    

    s/Display/Displays/

  2. +++ b/core/misc/message.js
    @@ -41,6 +45,15 @@
    +   * @param message
    ...
    +   * @param type
    
    @@ -51,6 +64,16 @@
    +   * @param message
    ...
    +   * @return
    

    Parameter types missing

  3. +++ b/core/misc/message.js
    @@ -51,6 +64,16 @@
    +   *   This function return a string.
    

    Inconsistent with other JS theme functions in core.

  4. +++ b/core/misc/message.js
    @@ -1,5 +1,7 @@
    + * @file
      *
    + * Message API.
    

    Extraneous empty line in the middle.

idflood’s picture

Status: Needs work » Needs review
FileSize
1.45 KB
8.87 KB

Thanks @Wim Leers for the quick review.

I'm not sure about the new @return but I found this in toolbar.js and dropbuttons.js.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

Thanks, looks good now! :)

nod_’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
8.74 KB

This is a reroll because the indentation standard changed for JavaScript files. No need for commit credit because of this reroll.

jibran’s picture

Status: Needs review » Reviewed & tested by the community

Reroll seems same as last patch so back to RTBC then.

droplet’s picture

few questions:

- Why no functions to remove messages posted by Drupal.message?
- Why not filter the undefined messages.
eg:

Drupal.message();
//return: <div class="messages messages--status">undefined</div>

- What if there's a lightbox windows. How to display messages there?
- Should it "throw new Error" that block whole scripts from being executed?
- It will be more useful if debouncedProcessMessages() can be triggered via events.

nod_’s picture

FileSize
9.74 KB

Nothing to remove messages because I don't want this script to be complicated. The current errors we get from PHP can't be removed, you have to reload to page to get them to disapear. This is intented to display ajax errors (as you can see by the dependency), nothing else in core. If we give the ability to remove messages it's a lot more complex to handle, there are UX concerns and that always takes time to solve.

The lightbox question is a good point. Didn't think of that.

The throw won't stop anything else from executing. We catch attach behaviors is wrapped in a try/catch, that won't prevent anything else from running.

About events not sure it's very useful. Instead of Drupal.message() we'd call $().trigger('message', 'mytext'); ? Events are not the fastest thing ever, we add a dep on jQuery and I don't see how it'd be better. Do you have something in mind?

Here is a reroll to prevent undefined messages to show up.

nod_’s picture

FileSize
2.12 KB
droplet’s picture

  • This is intented to display ajax errors (as you can see by the dependency),

    It would be more important to have a function to remove the message then. Even an UI changes with "Close" button.

    Imagine there's a uploader that only allow to upload JPG and 100kb of the max size. And then you uploaded a PNG.

  • First error: wrong image type

    And then you upload a JPG and files size larger than 100

    Second error: max file size problem

    Now, you have 2 error messages. Do you still upload a wrong type ??

  • About events not sure it's very useful. Instead of Drupal.message() we'd call $().trigger('message', 'mytext'); ? Events are not the fastest thing ever, we add a dep on jQuery and I don't see how it'd be better. Do you have something in mind?

    Eg. we could register a function to remove the message after 10s...

  • Also,
    if it accepts selectors, it could reuse on other modules, eg. Using on tabledrag to display message (tableDragChangedWarning)
  • +++ b/core/misc/message.js
    @@ -0,0 +1,82 @@
    +  Drupal.behaviors.drupalMessage = {
    

    And I think we can remove the behaviors. and setup it on first message call.

lanchez’s picture

Looked a bit into this and it needs to be at least converted to use libraries.yml. I can look more into it tomorrow.

rteijeiro’s picture

Issue tags: +FUDK
lanchez’s picture

The message adding is going to break after console div is removed in this issue https://www.drupal.org/node/2213583 . The messages could be probably added before region-content or after main-content link?

Also now with core.libraries.yml the js needs to be added as dependency to other script to be included everywhere. Which one should it be? I didn't make a patch yet, but the content would be pretty much this in core.libraries.yml:

drupal.message:
version: VERSION
js:
misc/message.js: {}
dependencies:
- core/drupal
- core/drupal.debounce

LewisNyman’s picture

heddn’s picture

Is this still being worked on for 8.0 or is it bumped to 8.1 now?

Wim Leers’s picture

heddn’s picture

Issue tags: +Needs beta evaluation
nod_’s picture

Issue tags: +Needs reroll

I would like to have it in 8.0.x but it needs someone to work on it.

googletorp’s picture

Status: Needs work » Needs review
FileSize
10.44 KB

This is basically a reroll of #94.

The patch from is so old, that I won't try to actually make an interdiff.txt. The JS from the patch is untouched, the changes is the "new" way of defining a library, attaching it to the system_messages and changes to new templates, since system message has changed a lot since the patch from a year ago.

googletorp’s picture

Issue tags: -Needs reroll
googletorp’s picture

Status: Needs work » Needs review
FileSize
9.83 KB
1.71 KB

We shouldn't attached the drupal.message library by default, also fixes some JSLint issues.

droplet’s picture

Status: Needs review » Needs work

It's more than reroll. @see #95.

googletorp’s picture

Regarding #95

I don't believe that having the messages disappear after a certain amount of time, is a good idea, since we don't know how complex and how many messages will appear, and thus how long they should be on the page.

I don't see that message.js will have any sensible way of determine which messages should be removed, when. We could add a function to remove all messages generated by message.js or provide a context or something similar to that you could remove all validation errors for a specific form upon resubmitting an ajax form. Then it would be up to the scripts invoking the messages, to handle the removal when it makes sense.

Behaviors provides a nice error handling, if we don't have use then, we would need to code our own handling of a case where we don't have a place to put messages.

googletorp’s picture

Status: Needs work » Needs review
FileSize
10.55 KB
2.53 KB

I've added the concept of context, so it's possible to specify where messages are coming from. I've added a method to remove a messages of a certain type/context, though this could be done manually as well.

This should handle the concerns from #95.

It still uses behaviors as I believe this is the best option we have atm.

droplet’s picture

Issue summary: View changes
FileSize
18.24 KB

- On maintenance mode, upload a file
(same problem on unpatch)

googletorp’s picture

@droplet what we are making is a framework, the patch itself only makes the framework available - it's not actually used anywhere (yet).

googletorp’s picture

To test this

  1. You need to include the library on the page, basically add $build['#attached'] = ['library' => ['core/drupal.message']]; somewhere, see patch #105 for more info (this was removed, since we don't want to load a lot of javascript by default on pages)
  2. The javascript code to add a message would look like this: Drupal.message.add('the message', 'the type status/warning/error, 'optional context')
mgifford’s picture

Issue tags: +Drupal.announce
lokapujya’s picture

Do these messages go into the block mentioned in #102?

mgifford’s picture

Status: Needs work » Needs review
FileSize
7.04 KB

Just a re-roll.

star-szr’s picture

Version: 8.0.x-dev » 8.1.x-dev
Issue tags: -Needs beta evaluation
tic2000’s picture

Status: Needs review » Needs work

The last patch misses message.js

andypost’s picture

Issue tags: +Needs reroll
kostyashupenko’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
10.61 KB
andypost’s picture

You need to include the library on the page

#type=messages element should add this library

Wim Leers’s picture

Status: Needs review » Needs work

+1 for #124.

  1. +++ b/core/core.libraries.yml
    @@ -225,6 +225,15 @@ drupal.machine-name:
    +drupal.message:
    +  version: VERSION
    +  js:
    +    misc/message.js: {}
    +  dependencies:
    +    - core/jquery
    +    - core/drupal
    +    - core/drupal.debounce
    

    Why no dependency on core/drupal.announce?

  2. +++ b/core/themes/classy/templates/misc/status-messages.html.twig
    @@ -21,7 +21,7 @@
    -{{ attach_library('classy/messages') }}
    

    This would be a regression today. So, if you do this, then we should add this to classy.info.yml:

    libraries-extend:
      core/drupal.message:
        - classy/messages
    
tic2000’s picture

Is this really addressing the issue? The announce part is good, because it reads the text, so it doesn't matter where on the page the action was initiated from.

The text is added to the top of the page and the top of the page may not be in view. Imagine this comment form using it. I submit through AJAX and the confirmation is all at the top. Sure in the case of comments you can get feedback by prepending the message to the form or adding the comment in it's place. But you have situations where this is not possible (the action is initiated from a table cell, that cell may be to narrow to display a message; or a subscribe form in the footer, you don't want a message to brake the footer layout, you just want a small message appear informing the user the subscription was successful). Something like https://material.angularjs.org/latest/demo/toast

mgifford’s picture

lokapujya’s picture

Status: Needs work » Needs review
FileSize
11.13 KB
503 bytes

Rerolled.

#126: Yeah, but isn't that for contrib to do?

jibran’s picture

  1. +++ b/core/misc/message.js
    @@ -0,0 +1,104 @@
    +        messagesElement = document.querySelector('[data-drupal-messages]');
    

    We can use jQuery selector here.

  2. +++ b/core/misc/message.js
    @@ -0,0 +1,104 @@
    +        message, n, nl;
    

    Let's rename n,nl to better helpful name.

joginderpc’s picture

Status: Needs work » Needs review
FileSize
7.98 KB

Updates following things as given in previous comments #131.

+++ b/core/misc/message.js
@@ -0,0 +1,104 @@
+ messagesElement = document.querySelector('[data-drupal-messages]');
We can use jQuery selector here.
+++ b/core/misc/message.js
@@ -0,0 +1,104 @@
+ message, n, nl;

Please review.

jibran’s picture

message.js is missing from the patch.

mgifford’s picture

Issue tags: +Accessibility

I'm tagging this for accessibility as it's required to get inline form errors as a fully fledged part of Core.

andrewmacpherson’s picture

Issue tags: +Needs reroll

The patch from #132 needs a re-roll, today's test says it fails to apply.

BarisW’s picture

Assigned: Unassigned » BarisW

On it

BarisW’s picture

Assigned: BarisW » Unassigned
Status: Needs work » Needs review
FileSize
10.71 KB
1.26 KB

Here is a re-roll. I've also re-added the messages.js file that was missing in patch #132.

hitesh-jain’s picture

Assigned: Unassigned » hitesh-jain
Manuel Garcia’s picture

Issue tags: -Needs reroll
hitesh-jain’s picture

Status: Needs work » Needs review
FileSize
6.38 KB

Patched against 8.3.x branch.

joelpittet’s picture

Trying to fix the twig template, the indentation went a bit wonky and div conditional went missing for the closing div.
Also undid the context removal but I don't think that's needed for this patch.

Bigpipe test should fail because the removal of the library, what's the reason behind that in the classy template?

droplet’s picture

Is that intended to remove the JS in #140 and above patches..

the blocker is #91

I agreed with #126 also. We could jump out of the box. Here's an JS lib we can use:
http://codeseven.github.io/toastr/demo.html

joelpittet’s picture

Assigned: hitesh-jain » Unassigned
FileSize
8.82 KB

@droplet, good catch, it likely was a mistake because it wasn't mentioned. I've added it back into the latest patch.

Also probably blocked a bit on changing Classy markup which is API.

mgifford’s picture

Issue tags: +Needs tests

Just wanted to encourage someone to write tests for this patch so we can hopefully meet this deadline:
https://www.drupal.org/node/2504847#comment-11750733

dmsmidt’s picture

Adding my thoughts about message visibility and toast in #126 and #147.

I agree that when, for example, you are scrolled down and a message appears at the top of the page they are not really accessible.
This can however be addressed when we get Inline Form Errors in core (#2504847: [meta] Roadmap for stabilizing Inline Form Errors module (IFE)).
What we are addressing here is that we have a central place to put the messages with JS, and keep them there.
That is a small enough change, and can later be extended with things like toasts.
Let's do things in small enough steps so that we can at least go on.
The above also covers @droplet's concern in #91.

tedbow’s picture

Status: Needs work » Needs review
FileSize
9.45 KB
16.07 KB

Ok trying to get this going again.

I was working on #2773601: Display "You are now in edit mode" prompt when user enters edit mode but hope we can get this done first so that issue can just use the core messages library.

I correct some eslint errors in messages.js. Also a couple logic errors.

Also had to update \Drupal\Core\Render\Element\StatusMessages::renderMessages because it was not showing the place holder if there were no messages.

I also add a test. But it is failing for me now. Not sure why.
If you enable the test module js_message_test you can see the show and remove message links. The module works for me but test doesn't. Not sure why

GrandmaGlassesRopeMan’s picture

  1. +++ b/core/misc/announce.js
    @@ -50,21 +50,23 @@
    +      for (var i = 0; i < il; i++) {
    

    announcements.reverse().forEach()

  2. +++ b/core/misc/announce.js
    @@ -50,21 +50,23 @@
           }
    

    Set announcements back to an empty array at the end.

  3. +++ b/core/misc/announce.js
    @@ -94,27 +101,23 @@
    +   * @param {String} text
    

    string

  4. +++ b/core/misc/announce.js
    @@ -94,27 +101,23 @@
    +   * @param {String} priority
    

    string

  5. +++ b/core/misc/message.js
    @@ -0,0 +1,106 @@
    +        if (!$('[data-drupal-messages]').length) {
    

    Capture this selector and store outside the behavior.

  6. +++ b/core/misc/message.js
    @@ -0,0 +1,106 @@
    +      for (messagesIndex = 0, messagesLength = messages.length; messagesIndex < messagesLength; messagesIndex++) {
    

    array.forEach()

  7. +++ b/core/misc/message.js
    @@ -0,0 +1,106 @@
    +   * @param {String} message
    

    string

  8. +++ b/core/misc/message.js
    @@ -0,0 +1,106 @@
    +   * @param {String} type
    

    string

  9. +++ b/core/misc/message.js
    @@ -0,0 +1,106 @@
    +   * @param {String} context
    

    string

  10. +++ b/core/misc/message.js
    @@ -0,0 +1,106 @@
    +   * @param {Object} message
    

    Update JSDoc format for object/key descriptions.

  11. +++ b/core/misc/message.js
    @@ -0,0 +1,106 @@
    +   * @return {String}
    

    string

tedbow’s picture

Status: Needs work » Needs review
FileSize
9.26 KB
18.65 KB

@drpal thanks for the review!

1,2 I think are out of scope :(

6. had to use messages.reverse().forEach() to keep message order

fixed all the rest.

Also in Drupal.theme.message there is no need for the context parameter.

Got JsMessageTest passing it was missing a library dependency in the test module.

The other tests that are failing I think are because of the changes to \Drupal\Core\Render\Element\StatusMessages::renderMessages but I haven't had time to look at that yet.

GrandmaGlassesRopeMan’s picture

  1. +++ b/core/misc/announce.js
    @@ -94,27 +101,23 @@
    +   * @param {String} text
    

    string

  2. +++ b/core/misc/announce.js
    @@ -94,27 +101,23 @@
    +   * @param {String} priority
    

    string

  3. +++ b/core/misc/announce.js
    @@ -94,27 +101,23 @@
    +    if (typeof text === 'string') {
    

    In addition to checking that text is a string, should we throw an error if text is not passed it, or is not actually a string?

  4. +++ b/core/misc/message.js
    @@ -0,0 +1,109 @@
    +      debouncedProcessMessages(messages);
    

    No need to pass messages.

  5. +++ b/core/misc/message.js
    @@ -0,0 +1,109 @@
    +    $('.messages--' + type + '.js-messages-context--' + context).remove();
    

    It might be nice to have the ability to remove all messages of a given context regardless of type.

    Drupal.message.remove = function (context, type) {
      context = context || 'default';
      $((type ? '.messages--' + type : '') + '.js-messages-context--' + context).remove();
    };
    
tedbow’s picture

Do want give the option to specify the element where the messages should go?

In #2785047: In Outside In mode, form validation messages should appear in the off-canvas tray, not the main page I would like to just the new messages library but it will not work because the messages div will not be on the tray.

  1. +++ b/core/misc/message.js
    @@ -0,0 +1,109 @@
    +  Drupal.behaviors.drupalMessage = {
    +    attach: function () {
    +      if (!messagesElement) {
    +        var $messagesWrapper = $(messageWrapperSelector);
    +        if (!$messagesWrapper.length) {
    +          throw new Error(Drupal.t('There is no element with a [data-drupal-messages] attribute'));
    +        }
    +        messagesElement = $messagesWrapper[0];
    +      }
    +    }
    +  };
    

    Then we wouldn't want to do this on attach but when I message is added to check if container is available.

  2. +++ b/core/misc/message.js
    @@ -0,0 +1,109 @@
    +  Drupal.message.add = function (message, type, context) {
    

    Here we could add containerSelector which would default to '[data-drupal-messages]'.

I think beyond the Settings Tray module any interaction in a dialog is going to run into this same problem. I think upcoming Media work will need this ability because the browser will be in a dialog.

tedbow’s picture

Status: Needs work » Needs review
FileSize
7.71 KB
20.3 KB

@drupal thanks for review in #159

1-2 fixed
3. Won't if (typeof text === 'string') { not be true if it wasn't passed. I just added an else here and threw an error.
4. fixed
5. Yes I think this could be good. If a module wants to remove all messages it added but not other modules they could use context.
I added this and added test coverage.

tedbow’s picture

Status: Needs work » Needs review
FileSize
1.58 KB
20.33 KB

Just test fixes.

BigPipe test will still fail. Looking into that.

Wim Leers’s picture

+++ b/core/themes/classy/templates/misc/status-messages.html.twig
@@ -21,7 +21,7 @@
-{{ attach_library('classy/messages') }}

Why would classy/messages no longer be necessary?

Wim Leers’s picture

+++ b/core/themes/classy/templates/misc/status-messages.html.twig
@@ -21,7 +21,7 @@
+<div data-drupal-messages>
 {% block messages %}
 {% for type, messages in message_list %}
   {%
@@ -54,3 +54,4 @@

@@ -54,3 +54,4 @@
   {% set attributes = attributes.removeClass(classes) %}
 {% endfor %}
 {% endblock messages %}
+</div>
 

This changed the markup for status messages. BigPipe's expectations therefore need to be updated as well.

This fixes the "no-js" BigPipe fail, and part of the other BigPipe fail.

Wim Leers’s picture

+++ b/core/themes/classy/templates/misc/status-messages.html.twig
@@ -21,7 +21,7 @@
-{{ attach_library('classy/messages') }}

Why would classy/messages no longer be necessary?

Because this asset library is no longer being attached in the template, that's another BigPipe expectation we need to update: when BigPipe is using JS, it no longer needs to update the AJAX page state nor load the CSS file in this asset library.

I am posting this fix separately (from #165) because I'm not at all convinced that removing that asset library is correct. So then it's easy to revert this change.

Finally, if this change is correct, then you will need to expand BigPipe's test coverage with a case where it is explicitly loading some CSS or JS. Because this is effectively reducing BigPipe's test coverage.

Pavan B S’s picture

  1. +++ b/core/misc/message.js
    @@ -0,0 +1,110 @@
    +   * Builds a div element with the aria-live attribute and attaches it to the DOM.
    

    LIne exceeding 80 characters

  2. +++ b/core/misc/message.js
    @@ -0,0 +1,110 @@
    +      // Save the text and priority into a closure variable. Multiple simultaneous
    

    Line exceeding 80 characters

Fixed coding standard error in the patch, please give me suggestion if i am wrong

tedbow’s picture

Status: Needs work » Needs review
FileSize
1.39 KB
22.52 KB

@Pavan B S thanks for the patch but you have a bunch of unrelated files in there.

Re #164 I think this is mistake. I can confirm with the library the regular messages are not theme correctly.

Ok added back "classy/messages" library.
Update BigPipeTest

tedbow’s picture

Status: Needs work » Needs review
FileSize
17.06 KB
26.97 KB

Ok so this implements the ability to send messages to different divs. See my idea in #160

This will be needed for the Settings Tray module.

tedbow’s picture

Title: A place for JavaScript status messages » Provide a common API for displaying JavaScript messages
Issue summary: View changes

Update summary.

Right now Drupal.message.add() does not use Drupal.announce. I think it should right?

tedbow’s picture

Ok. so this calls Drupal.annouce() inside of Drupal.message.add().

It seems like every on screen message should be "announced" for screen readers but maybe I don't know the idea behind it.

@todo add test coverage is this is the correct behaviour.

nod_’s picture

Been a while since I checked this issue :p

I'm not happy about adding a jquery dependency for this script. There are no events involved, I don't see the need for it. When we first wrote announce.js es6 wasn't nowhere near a possibility. We could make those array processing more modern. Patch incoming.

GrandmaGlassesRopeMan’s picture

Here is a slight refactor. I talked with @tedbow and this skips the work in #175.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
nod_’s picture

All right, simplified the JS.

  • Got debounce out, when the method to add a message is called, it's added right away to the page. If someone uses it in a loop they'll have a bad time since a lot of messages will trash the page. That can always be added back if it's a real issue later.
  • Remove the context thing. Unless there is a good use-case I don't think we should handle it in core, if people want to group their message they can handle it in their module/code.
  • Made the API more generic, in case a module wants to override it (and I'm sure they'll want to).
  • Drupal.announce is called when a message is displayed. It's possible to add a separate string for the screenreader version of the message.

Made a few changes in the test js file to show how it can be used but no tests have been updated to pass.

There is an issue with the HTML, we should have a template for status wrapper and status message. To let themes add the data-drupal-messages attribute where they want. It shouldn't be in classy like that. Anyway if the js looks good we can look at the tests.

nod_’s picture

Issue summary: View changes

Oh and Drupal.message() expects an HTMLElement, no selector.

tedbow’s picture

diff --git a/core/misc/announce.js b/core/misc/announce.js

diff --git a/core/misc/announce.js b/core/misc/announce.js
index acf850a..cd1ee5b 100644

@nod_ do you know why this patch contains changes to this file? It seems unrelated to the messages changes.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
35.2 KB
20.39 KB
  • Fixes coding standards error from #180
  • Refactors remove functionality. Remove messages by id, nth-clild, arrays of both or just a single type
  • Tests. Thanks @tedbow
nod_’s picture

Looking at the interdiff. Could you keep the queryselectorall and instead of select+remove in the foreach build an array of selectors (map would work better than foreach). Would help readability.

The separation of select and remove in separate steps was on purpose.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
27.77 KB
13.23 KB

Interdiff is between the patch in #180.

GrandmaGlassesRopeMan’s picture

Interdiff is between the patch in #180. Poor internet at DDD.

tedbow’s picture

  1. +++ b/core/misc/message.js
    @@ -70,27 +70,30 @@
    +      var removeSelector = (messages instanceof Array ? messages : [messages])
    

    Since this will be array of selectors at this point for each "message" should we change the name to removeSelectors?

  2. +++ b/core/misc/message.js
    @@ -70,27 +70,30 @@
    +      var remove = this.element.querySelectorAll(removeSelector.join(', '));
    

    Agree with @nod_ that it is more readable seperatable

  3. +++ b/core/misc/message.js
    @@ -70,27 +70,30 @@
    +            // If the index is numeric remove the element based on the DOM index.
    +            '[data-drupal-message]:nth-child(' + messageIndex + ')';
    

    I really don't see a use case for specify the nth child message.

    Presumably there would be JS from multiple modules calling messageAdd(). How would any 1 module know which index their messages were. They could easily know the order of their messages but they would not know if another module set a message in between theirs.

    It seems like they would have actually inspect the DOM to know the order of the messages. If they are inspecting the DOM anyways why would they not just read the data-drupal-message attribute to get the string index?

GrandmaGlassesRopeMan’s picture

Addressed the feedback in #189.

tedbow’s picture

@drpal thanks

That is much more readable to me.

Just want clarify for anyone readying through that in #190 @drpal removed the ability to remove the nth child message as was asking for in #189.3

tedbow’s picture

Status: Needs review » Reviewed & tested by the community

I think this is re my comment in #191

nod_’s picture

Status: Reviewed & tested by the community » Needs work

Thanks for all the tests :)

Few things before commit: In message.js

We could do better for docs of the main entry point of this new API.

  /**
   * woot

s/%/@/

        throw new Error(Drupal.t('There is no @element on the page.', {'%element': defaultMessageWrapperSelector}));

Here I'd prefer Array.isArray().

messages instanceof Array

I'm wondering if we could simplify the api and not expose messageWrapper at all. Why would we have that in the api, It's passed to Drupal.message to begin with? I know it might be a bit different from our older apis but I'd like to start making conscious choices.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
27.75 KB
1.76 KB

Some minor fixes from #193.

I removed messageWrapper from the exposed response from the constructor which should simplify everything.

tedbow’s picture

Status: Needs work » Needs review
FileSize
283 bytes
27.78 KB
+++ b/core/misc/message.js
@@ -99,7 +103,6 @@
-      element: messageWrapper,

This patch just reverts this change. The add and remove methods will not work if this is removed.

  1. +++ b/core/modules/system/tests/modules/js_message_test/js/js_message_test.js
    @@ -0,0 +1,64 @@
    +          messageObjects[divSelector] = Drupal.message(document.querySelector(divSelector));
    

    Since how this API is used is you call Drupal.message() and receive by an object with the methods on it.

  2. +++ b/core/misc/message.js
    @@ -0,0 +1,160 @@
    +      this.element.innerHTML += Drupal.theme('message', {text: message, type: type}, options);
    ...
    +      var remove = this.element.querySelectorAll(removeSelectors.join(', '));
    

    Then the add and remove methods both need this.element so they can manipulate it.

+++ b/core/misc/message.js
@@ -16,10 +16,14 @@
-   * woot
+   * Constructs a new instance of the Drupal.message object.

I think we can all agree this is better ;)

I found another problem though.

I was testing manually in Bartik.
I forgot you need the test theme to make Javascript on the test module js_message_test.js to work.

So I was clicking the links that should make the messages appear in the div with data-drupal-messages-other
The message where appearing in the default message div because data-drupal-messages-other didn't exist.

Took me a second to figure out what was going on.

+++ b/core/modules/system/tests/modules/js_message_test/js/js_message_test.js
@@ -0,0 +1,64 @@
+          messageObjects[divSelector] = Drupal.message(document.querySelector(divSelector));

document.querySelector(divSelector) was returning null because the element wasn't on that page.

  1. +++ b/core/misc/message.js
    @@ -0,0 +1,160 @@
    +    if (typeof messageWrapper === 'string') {
    +      throw new Error(Drupal.t('Drupal.message() expect an HTMLElement as parameter.'));
    +    }
    +    if (!messageWrapper) {
    +      messageWrapper = document.querySelector(defaultMessageWrapperSelector);
    +      if (!messageWrapper) {
    +        throw new Error(Drupal.t('There is no @element on the page.', {'@element': defaultMessageWrapperSelector}));
    +      }
    +    }
    

    But here when messageWrapper is null it will call document.querySelector(defaultMessageWrapperSelector);.

    The message will then appear in the default messages div.

    I think it would be better to throw an error in this case.

Should we be worrying about that?

nod_’s picture

Status: Needs review » Needs work

I wanted to get rid of the use of this too, replace this.element with messageWrapper and it'll work we shouldn't add stuff just because.

If it fails and the error is not helpful, we should fix it. Let's check that if messageWrapper is not undefined messageWrapper.nodeType exists and equals to 1, that way we know we have a DOM Node of some kind. Might need to add this check all over the place too, to avoid surprises. But that's for another issue.

tedbow’s picture

Wrote this before I saw @nod_'s comment in #197

So I think the problem I mentioned in #196 I think is something that we should check for. Because Javascript code easily be written wrong to get the call to Drupal.message() but messages still would be output to the screen just in the default messages div rather than what is expected.

For example in both:
Drupal.message(document.querySelector('#one-character-off'));
and
Drupal.message($('#one-character-off')[0]);

No error will be thrown and the messages will be display in the default messages div which is not the intention.

This could fix both cases:

if ((messageWrapper === null || typeof messageWrapper === 'string')
    || (arguments.length === 1 && typeof messageWrapper === 'undefined')) {
      throw new Error(Drupal.t('Drupal.message() expect an HTMLElement as parameter.'));
}

You have to check for null because document.querySelector('#one-character-off') will send null

$('#one-character-off')[0] will send undefined but you can't simply check for undefined because sending no argument will also make typeof messageWrapper === 'undefined' so you have to check for arguments.length === 1

I think the point of adding these extra checks is not to babysit bad calls but not write messages to the wrong div.

thoughts? is this overkill?

tedbow’s picture

Status: Needs work » Needs review
FileSize
3.68 KB
27.92 KB
  1. re #197

    I wanted to get rid of the use of this too, replace this.element with messageWrapper and it'll work we shouldn't add stuff just because.

    Yeah whoops fixed this. Also removed doc comment that was referring to it that was never removed.

  2. Also added the extra check I was referring to in #198. As soon as I add this it actually required an update to the test because the "Show Multiple" and "Remove Multiple" in \Drupal\js_message_test\Controller\JSMessageTestController::messageLinks() didn't have a data-selector attribute so
    var divSelector = e.currentTarget.getAttribute('data-selector');
    if (!messageObjects.hasOwnProperty(divSelector)) {
      messageObjects[divSelector] = Drupal.message(document.querySelector(divSelector));
    }
    return messageObjects[divSelector];
    

    Was actually sendingnull as messageWrapper unintentionally.

    I think this makes the point that extra check is actually necessary.

  3. Also fixed a couple spelling errors. "screenreader" is actually two words. "Drupal.annonce" spelled wrong in comment.
andypost’s picture

nod_’s picture

Assigned: Unassigned » nod_
Status: Needs review » Needs work

Js supporting the tests has some issues.

I'm also adding a clear method to the message object.

dmsmidt’s picture

Was about to review, but I'll wait for nod_'s changes.

nod_’s picture

Status: Needs work » Needs review
FileSize
31.53 KB
16.02 KB

Let's see how it goes.

In the test JS messages indexes were stepping over each other and made it easy to have unremovable messages on the page.

nod_’s picture

Assigned: nod_ » Unassigned
dmsmidt’s picture

+++ b/core/misc/message.js
@@ -26,11 +26,13 @@
+      if (!messageWrapper || (messageWrapper && messageWrapper.nodeType !== 1)) {
+++ b/core/misc/message.js
@@ -68,22 +70,23 @@
+      if (!messages || (messages && Array.isArray(messages) && messages.length === 0)) {

Please educate me on why messageWrapper and messages need to be checked again? Can't we do:

 if (!messages || Array.isArray(messages) && messages.length === 0) {
nod_’s picture

lol yeah you're right, my mistake.

nod_’s picture

Status: Needs work » Needs review
FileSize
5.11 KB
31.58 KB

Turns out, Array.from isn't supported by IE 11, back to for loop.
Fixed tests and comments from #205

nod_’s picture

Issue summary: View changes

Touched the IS a bit. It'd be quite something to fix an 11 years old issue…

nod_’s picture

Issue summary: View changes
mgifford’s picture

@nod_ I like your comment in #5, 5 years ago :)

dmsmidt’s picture

Issue summary: View changes

11 years, wow, let's do this!

Added another issue from Inline Form Errors to the IS.

dmsmidt’s picture

Assigned: Unassigned » dmsmidt

I'm currently reviewing code and testing with a screen reader.

dmsmidt’s picture

Assigned: dmsmidt » Unassigned
Status: Needs review » Needs work

First of all what a great progress! I'm so happy to see this and immediately want to start experimenting with toast messages for example (in contrib) :-p.

Some remarks:

  1. +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
    @@ -75,19 +75,17 @@ public static function generatePlaceholder(array $element) {
         $render = [];
    

    This can now be removed.

  2. +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
    @@ -75,19 +75,17 @@ public static function generatePlaceholder(array $element) {
    +    // Render the messages.
    

    Needs to say what happens now. Like: "Render the messages or an empty messages container div which can be used by the JavaScript messages API."

    And it would be great to improve the docblock as well so that @return and the description show there will always be a container.

  3. +++ b/core/misc/announce.js
    @@ -81,8 +83,13 @@
    +
    

    Nit: remove empty line

  4. +++ b/core/misc/announce.js
    @@ -96,25 +103,24 @@
    +    }
    

    Nit: Add empty line

  5. +++ b/core/modules/system/tests/modules/js_message_test/src/Controller/JSMessageTestController.php
    @@ -0,0 +1,128 @@
    +        ]
    

    Nit: missing comma

  6. +++ b/core/modules/system/tests/modules/js_message_test/src/Controller/JSMessageTestController.php
    @@ -0,0 +1,128 @@
    +      ]
    

    Nit: missing comma

  7. +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
    index acf850a641..cd1ee5bb1a 100644
    --- a/core/misc/announce.js
    
    --- a/core/misc/announce.js
    +++ b/core/misc/announce.js
    

    All changes to announce.js seem out of scope. We can keep this patch much simpler that way. Although the changes look logical, we should do that in a separate issue. My tests without these changes work just as well!

  8. My tests with ChromeVox are succesful, the messages are read out aloud, jay!
    We should however improve it a bit. Currenlty it is unclear what type of message was added. I propose we by default first tell the type of message (error, status, warning) and then the actual message.
  9. +++ b/core/misc/message.js
    @@ -0,0 +1,188 @@
    +    // Check this. Might be too much.
    +    if (!options.priority && type !== 'status') {
    +      options.priority = 'assertive';
    +    }
    

    Checked here:
    https://www.w3.org/TR/wai-aria/states_and_properties#aria-live

    Let's keep polite as a default (see announce.js).
    And only set the priority to assertive on 'warnings' and 'errors'. This way, if devs introduce their own status (like a notice) it won't be so aggressive by default.

  10. +++ b/core/misc/message.js
    @@ -0,0 +1,188 @@
    +    function messageRemove(messages) {
    

    Can we rename 'messages' to 'index', this is much more understandable for me.

  11. --- a/core/themes/bartik/css/components/messages.css
    +++ b/core/themes/bartik/css/components/messages.css
    

    We need some before/after images for this.

  12. +++ b/core/misc/message.js
    @@ -0,0 +1,188 @@
    +      options.index = Math.random().toFixed(15).replace('0.', '');
    

    This makes it impossible to use a custom generated index. Allowing custom generated indexes opens up a lot of possibilities in contrib. And may be needed for Inline Form Errors.

  13. It is currently impossible to delete specific server side generated messages with the proposed API. (This is needed for Inline Form Errors)
    I think we should add the possibility to add a (custom or generated) index (data-drupal-message) via drupal_set_message().
dmsmidt’s picture

Issue summary: View changes

Update IS: relationship with drupal.announce() is clear now.

nod_’s picture

missing the dependency on the message css library too

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
28.48 KB
6.79 KB
  • 1,2 - fixed
  • 3,4 - I removed the changes to annouce.js for this patch, which could be posted in a new issue.
  • 5,6,7 - fixed
  • 8,9 - fixed
  • 10 - fixed
  • 12 - fixed
  • 13 - I feel like this should be a new issue.
GrandmaGlassesRopeMan’s picture

FileSize
432 bytes

I'm sorry for breaking announce(). Everything else should still apply.

GrandmaGlassesRopeMan’s picture

dmsmidt’s picture

Status: Needs review » Needs work

That is looking much better, thanks @drpal.

#13 in a separate issue seems legit.

However from checking the interdiff in #217 I think you misunderstood my accesibility goal.

+++ b/core/misc/message.js
@@ -154,12 +155,15 @@
   function announce(message, type, options) {
     // Check this. Might be too much.
     if (!options.priority && type !== 'status') {
-      options.priority = 'assertive';
+      options.priority = 'polite';
     }
+    // Always announce the priority of the upcomming message.
+    Drupal.announce(options.priority);

The "// Check this" line should be removed, and we can keep the options.priority = 'assertive part, but change type !== 'status' into (type === 'warning' || type === 'error'). This way all other types will be polite.

Also Drupal.announce(options.priority); doesn't sound to friendly or helpful and doesn't get announced with the same priority as the actual message. Can't we just use type as a starter?

GrandmaGlassesRopeMan’s picture

@dmsmidt

Yep, totally my fault here. We should now be announcing the type of the upcoming message at the same priority as the message itself.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
tedbow’s picture

Pretty sure this will need to be re-rolled because this was reverted #2853509-16: Don't render status messages if there are no messages but also include their assets if there might be

Retesting to last patch to confirm it no longer applies.

dmsmidt’s picture

@drpal, sorry for all the iterations, a11y is better now, but we can still improve.


+++ b/core/misc/message.js
@@ -0,0 +1,191 @@
+    // Always announce the type of the upcomming message.
+    Drupal.announce(type, options.priority);
+
+    // If screen reader message is not disabled announce screen reader specific
+    // text or fallback to the displayed message.
+    if (options.announce !== '') {
+      Drupal.announce(options.announce || message, options.priority);
+    }

Custom announce messages are currently never read out loud due to if (options.announce !== '') {.
Also let's not separately announce the type, it sounds funny/weird (yes I tried), I would concat it to the message like so: type + ': '+ message. But.. we should also make it possible to translate it, and then it becomes much more work, we would need a mapping of type => "read out loud version of type" and then we would like that on the server side generated messages as well (reminder: we could use #status_headings for that).
So let's remove announcing type from this patch and make a follow up (just like #217-13).

Furthermore #214-2 is not solved, comments/dockblock needs update.

@_nod, not sure how we should fix the messages.css dependency in Classy and Bartik.
We add css/components/messages.css to the Classy and Bartik libraries base library and remove all {{ attach_library('classy/messages') }}. Or we have to overwrite Drupal.theme.message in the themes and add it there?

We are getting somewhere, woot!

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
27.12 KB
1.85 KB

@dmsmidt

Haha, don't worry about it.

I think the changes in #214-2 are out of scope right now since we don't actually need to make any changes to this file. I also removed the announcement of the message type.

dmsmidt’s picture

Issue summary: View changes
Status: Needs review » Postponed

I did some more checks and saw that I missed another thing in previous reviews:

+++ b/core/misc/message.js
@@ -0,0 +1,190 @@
+    return '<div class="messages messages--' + message.type + '" ' +
+      'data-drupal-message="' + options.index + '"> ' + message.text + '</div>';

The aria-label is missing. It is present on server-side generated messages. The #status_headings in StatusMessages::renderMessages() should somehow be usable by the Message API.

But before we start refactoring this we should wait on #2853509: Don't render status messages if there are no messages but also include their assets if there might be, it's critical and changes the way StatusMessages::renderMessages() works again. But it'll also make sure we always have message styling.

nod_’s picture

Status: Postponed » Needs work

There is more work needed on the js side no need to hold things off

nod_’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
9.92 KB
29.29 KB

Added a message.select method to the api, put back the announce method like it was before, possibility to disable announce was a feature. Added all the attributes and structure of PHP's messages, that should help a11y.

Potential issues with error messages, since both the announce and the message itself could trigger, hence reading twice the error.

GrandmaGlassesRopeMan’s picture

  1. +++ b/core/misc/message.js
    @@ -0,0 +1,253 @@
    +      if (!index || (Array.isArray(index) && index.length === 0)) {
    

    We can just check that the length exists, not that it equals 0.

  2. +++ b/core/misc/message.js
    @@ -0,0 +1,253 @@
    +        return [];
    

    Maybe we can return a boolean or throw an error in this case.

  3. +++ b/core/misc/message.js
    @@ -0,0 +1,253 @@
    +      Drupal.announce(options.announce || message, options.priority);
    

    Do we want to make sure that options.annouce is a string?

  4. +++ b/core/misc/message.js
    --- a/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php
    +++ b/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php
    

    I'm not sure these are needed anymore?

dmsmidt’s picture

@nod_ #231, ah my bad about the announce message. Now I understand, if you explicitly send an empty string the announcement will be skipped. Drpal's third point makes sense: check if it is a string as well. If option.announce is FALSE it would read the default message right? That feels strange.

nod_’s picture

Status: Needs work » Needs review
FileSize
29.29 KB

Reroll.

  1. Makes sense to me like that, we're checking it's an array before. To me NO || (YES && YES) is clearer than NO || (YES && NO).
  2. It's a selection method, if you send it trash it should return an empty array, like querySelectorAll or jQuery.
  3. Meh, don't care. most probably won't see this in the API anyway. If someone should check it's data it's Drupal.announce.
  4. I think we should keep that, the message template changed.

( edit ) I guess false should be taken care of too. No strong feelings either way.

GrandmaGlassesRopeMan’s picture

@nod_

Good points on the JS. I fixed the out of date tests.

alexpott’s picture

  1. +++ b/core/themes/bartik/templates/status-messages.html.twig
    @@ -23,7 +23,7 @@
       {% if message_list is not empty %}
         {{ attach_library('bartik/messages') }}
    -    <div class="messages__wrapper layout-container">
    +    <div class="messages__wrapper layout-container" data-drupal-messages>
    

    This isn't going to work if there are no messages on the page.

  2. +++ b/core/themes/classy/templates/misc/status-messages.html.twig
    @@ -22,6 +22,7 @@
     {{ attach_library('classy/messages') }}
    +<div data-drupal-messages>
    

    I think rather than adding this is .html.twig files we should consider adding it in \Drupal\Core\Render\Element\StatusMessages::renderMessages() that way we can ensure that there is something for javascript to find to add messages regardless of what someone does in the .html.twig files for custom themes etc.

    Doing this will also mean that this change is compatible with #2853509: Don't render status messages if there are no messages but also include their assets if there might be

nod_’s picture

Status: Needs review » Needs work

doesn't apply and #238 needs addressing

pk188’s picture

Status: Needs work » Needs review
FileSize
4.84 KB
pk188’s picture

Status: Needs work » Needs review
FileSize
4.79 KB
droplet’s picture

should rewrite into ES6 Class.

some basic things:

  1. +++ b/core/misc/message.js
    @@ -0,0 +1,253 @@
    +  Drupal.message = function (messageWrapper) {
    +    // If an argument is passed, verify that it's a DOM Node of some kind.
    +    if (arguments.length === 1) {
    +      if (!messageWrapper || messageWrapper.nodeType !== 1) {
    +        throw new Error(Drupal.t('Drupal.message() expect an HTMLElement as parameter.'));
    +      }
    +    }
    

    do not pass in selectors?

    It doesn't work and do not take the options if I read the source code correctly.
    methods: add / remove / select / clear should apply to same messageWrapper
    To imagine one page has 2 messages area

  2. +++ b/core/misc/message.js
    @@ -0,0 +1,253 @@
    +      options.index = type + ' ' + (options.index || Math.random().toFixed(15).replace('0.', ''));
    ...
    +      return options.index;
    

    - a bit odd to assign index back to option;
    - and index should be ID I think;
    - Imagine you adding 10 messages and remove 2 randomly. which one is index 2?
    - To consider using [data-drupal-message-id="adopt-backend-pre-set-message"]

GrandmaGlassesRopeMan’s picture

A bit of a refactor from #244. Still needs to address #238.2

- Drupal.message is now an ES6 class.
- Drupal.message constructor expects just a HTML element, not a selector.
- options.index is now options.id to better reflect what it actually is.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
GrandmaGlassesRopeMan’s picture

Accidently included the sourcemap. When #2880004: Improve (again) ES6 helper scripts lands we shouldn't have this problem.

droplet’s picture

- Drupal.message constructor expects just a HTML element, not a selector.

OK. Any reason?

Just few thoughts:

add(message, type = 'status', options = {}) {

Put `type` into `options`

// NOW:
message.add(`string`, 'status', { id: 'myID' })

// THEN:
message.add(`string`, { 'type': 'status', id: 'myID' })

// THEN, more handy:
message.add(`string`, { id: 'myID' })

-----------------

options.id = `${type} ${options.id || Math.random().toFixed(15).replace('0.', '')}`;

- If we passing in `options.id`, we should not add prefix to it.
- Should we checking the `ID === String`?
- I think we can add [drupal-message-type]
- Math.random isn't safe enough

------------------

.map(i => `[data-drupal-message~="${i}"]`)
we can't use `~=`. For example: "App" matches "App" and "Apple"

------------------

remove(options) {
// TO
remove(ids) {

------------------

Should we add a handy replace mode? Most of the time, we only display a single message.

------------------

In Drupal, we usually display the Message from backend. If a module does this:

new Drupal.message(); // or inside a clousre, JS module

How can I get the instance?

THANKS!

GrandmaGlassesRopeMan’s picture

Status: Needs review » Needs work
GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
35.33 KB
11.17 KB

- function signature for message.add() now accepts just two arguments, message, and options which contains a status key.
- adjustments to id creation logic. math.random isn't great, but I'm not sure we're going to hit a duplicate key within reason.
- message theme functions now has two data attributes, data-drupal-message-id and data-drupal-message-type
- documentation updates to better clarify what's actually being passed to a function.

@droplet

I wanted to simplify the constructor, handling the ability to pass a selector string and an HTML element felt a bit unnecessary. About, new Drupal.message();. Unless a module specifically exposes it's instance of Drupal.message(), it will only be available to itself.

nod_’s picture

Status: Needs review » Needs work

Wait a second, why do we have a class all of a sudden? I really do no want classes anywhere in our code. And for the moment it'll introduce a concistency problem. Most if not all our Drupal API don't need to use new. If we start going for classes I really want more discussion that sneaking that in there.

  1. not too happy about adding 2 types of data attributes. We can take care of everything with only one. think of what's in the data attribute as tags.
  2. @droplet:

    - Drupal.message constructor expects just a HTML element, not a selector.

    Because if you allow a selector, you'll need to allow a context to select from because not everyone might want to select from document.querySelector(). It makes our code more consistent, less magic.

  3. about the type in options, I was going for consistency with the drupal_set_message API. But I don't care that much it really depends on how people use it.
  4. about ~= that works for me, When I try, "App" doesn't select "Apple", it does an exact match, not a substring match (like ^= does)
  5. Replace, why not. Later though? trying to keep things simple here.
  6. How can I get the instance?

    The module has to deal with it. Same as Drupal.dialog(), Drupal.ajax() (notice how there are no new * to use those API too).

So I'd expect: no classes (because that need at least some discussion+change notice), one data attribute, using ~=.

droplet’s picture

Wait a second, why do we have a class all of a sudden? I really do no want classes anywhere in our code. And for the moment it'll introduce a concistency problem. Most if not all our Drupal API don't need to use new. If we start going for classes I really want more discussion that sneaking that in there.

Not really. We have Class everywhere already. Just usually we don't call it directly but in *.attach() and from PHP.
Drupal.tableDrag is one of the examples.

not too happy about adding 2 types of data attributes. We can take care of everything with only one. think of what's in the data attribute as tags.

I think 2 types is more clear and better for themers. Or we just drop `type` and use the `class` instead. If there's no BC problem, I'd recommend move style to [data-attr] also.

Because if you allow a selector, you'll need to allow a context to select from because not everyone might want to select from document.querySelector(). It makes our code more consistent, less magic.

I see your point but it's not handy.

about ~= that works for me, When I try, "App" doesn't select "Apple", it does an exact match, not a substring match (like ^= does)

Ahh Alright. But
App matches "App Apple"
Apple matches "App Apple"

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review

Phew, this was a can of worms I didn't want to open.

- Since classes are basically syntactical sugar over JavaScript's existing prototype, I don't see this as being a bad idea. If this issue is with new, we can abstract that away, but I wouldn't recommend it.
- Having two distinct data attributes makes sense to me. There's two distinct bits of information, let's make those accessible.
- I thought ^= was a starts-with match?
- I agree with keeping a replace method to another issue. This is already 10 years old.

droplet’s picture

OK. The main structure is fine.

- Since classes are basically syntactical sugar over JavaScript's existing prototype, I don't see this as being a bad idea. If this issue is with new, we can abstract that away, but I wouldn't recommend it.

One bad drawback is we can't make methods to Private scope in an elegant way. We may have to add @internal or prefix with _ temporarily.

It will land in Babel soon I bet:
http://thejameskyle.com/javascripts-new-private-class-fields.html

          .map(i => `[data-drupal-message-id^="${i}"]`)

I don't like to mix ids with error type and filter from it but well... not a very bad idea. So now, we make a implicit code standard on message ids:

errorType-customMessage

(and [data-drupal-message-type] is useless then)

Do we support custom error types anyway?

(it won't allow using `status|warning|error` as prefix on custom type)

* @param {string} [options.id]
* The message ID, it can be a simple value: `'filevalidationerror'`
* or several values separated by a space: `'mymodule formvalidation'`

some comments needs work.

the remaining thing is: "A way to get the message status/instances" (we can do it in a follow-up I think)

dmsmidt’s picture

Issue tags: +sprint
tedbow’s picture

Ok this patch does a few things

1. Adds a $messagesSelector parameter \Drupal\FunctionalJavascriptTests\Core\JsMessageTest::assertCurrentMessages. This function was not actually check to see if the message were in the right container. Though /js_message_test.js we were assigning to different containers the test would have passed either way. because assertCurrentMessages() we found the messages by
$this->getSession()->getPage()->findAll('css', '.messages')
This didn't take into account the container.

2. Fixes a problem where if you added a message like
defaultMessageArea.add('Msg-id-no-status', {id: 'my-special-id'});
The message would not have a status.

This was because the add() signature was
add(message, options = { type: 'status' })
So if options was provided but didn't have a status then the default status would not be assign.

At @drpal suggestion I first tried:
options = Object.assign({ type: 'status' }, options);
While this work when tested the tst page manually and no errors shown in the console at when clicking all buttons. This did throw a JS error when running the tests.

setting js_errors: false in your Poltergeist configuration (see documentation for details).
TypeError: undefined is not a function (evaluating 'Object.assign({ type: 'status' }, options)')
TypeError: undefined is not a function (evaluating 'Object.assign({ type: 'status' }, options)')
at http://d8_2_ux.octo2.dev/core/misc/message.js?v=8.4.0-dev:30 in add
at http://d8_2_ux.octo2.dev/core/modules/system/tests/modules/js_message_te...
at http://d8_2_ux.octo2.dev/core/assets/vendor/jquery/jquery.min.js?v=2.2.4:3 in dispatch
at http://d8_2_ux.octo2.dev/core/assets/vendor/jquery/jquery.min.js?v=2.2.4:3 in handle
at :0 in sendEvent
at phantomjs://code/web_page.js:71
at phantomjs://code/web_page.js:685 in mouseEvent
at phantomjs://code/node.js:89 in mouseEvent
at phantomjs://code/browser.js:721 in mouse_event
at phantomjs://code/browser.js:746 in click
at phantomjs://code/browser.js:1272 in serverRunCommand
at phantomjs://code/poltergeist.js:38 in serverRunCommand
at phantomjs://code/server.js:76 in handleRequest
at phantomjs://code/server.js:22

/var/www/d8_2_ux/vendor/jcalderonzumba/gastonjs/src/Browser/BrowserBase.php:119
/var/www/d8_2_ux/vendor/jcalderonzumba/gastonjs/src/Browser/BrowserBase.php:99
/var/www/d8_2_ux/vendor/jcalderonzumba/gastonjs/src/Browser/BrowserMouseEventTrait.php:17
/var/www/d8_2_ux/vendor/jcalderonzumba/mink-phantomjs-driver/src/MouseTrait.php:31
/var/www/d8_2_ux/core/tests/Drupal/Tests/BrowserTestBase.php:1137
/var/www/d8_2_ux/core/tests/Drupal/FunctionalJavascriptTests/Core/JsMessageTest.php:44

I am not sure why but I did restart phantom to make sure it wasn't some cache issue.

The patch uses hasOwnProperty() check and works both manually and in the test.

3. changes Message.announce to a static function.

droplet’s picture

add(message, options = { type: 'status' })

Possible in ES6:
add(message, { type = 'status' } = {})

@see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operat...

GrandmaGlassesRopeMan’s picture

@droplet

Since we're not passing status separately anymore (part of the options object), I don't think that makes sense.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
    @@ -73,21 +73,18 @@ public static function generatePlaceholder(array $element) {
       public static function renderMessages($type) {
    -    $render = [];
         $messages = drupal_get_messages($type);
    -    if ($messages) {
    -      // Render the messages.
    -      $render = [
    -        '#theme' => 'status_messages',
    -        // @todo Improve when https://www.drupal.org/node/2278383 lands.
    -        '#message_list' => $messages,
    -        '#status_headings' => [
    -          'status' => t('Status message'),
    -          'error' => t('Error message'),
    -          'warning' => t('Warning message'),
    -        ],
    -      ];
    -    }
    +    // Render the messages.
    +    $render = [
    +      '#theme' => 'status_messages',
    +      // @todo Improve when https://www.drupal.org/node/2278383 lands.
    +      '#message_list' => $messages,
    +      '#status_headings' => [
    

    Should we document here that the template should check whether messages exists or not?

  2. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,238 @@
    +  Drupal.message = class Message {
    

    This is a a weird class name. "Message", I would have expect it to be a single Message, but in fact it holds multiple messages. What about using "Messages" instead?

  3. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,238 @@
    +    add(message, options = {}) {
    

    Are we okay of not supporting the $repeat parameter of drupal_set_message? I guess the fact that JS depends on time, two equal messages aren't no longer the same, but I wanted to mention that here.

  4. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,238 @@
    +    /**
    +     * Select a set of messages based on type or index.
    +     *
    ...
    +     * @param {string|Array.<string>} index
    +     *   The message index or type to delete from the area.
    +     *
    ...
    +    select(index) {
    

    I have a hard time seeing where type is used. Maybe you want to have a selectByType method

  5. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,238 @@
    +          .join(''),
    

    This , here is quite weird. Is this really part of your CS?

  6. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,238 @@
    +      messageText = `<h2 class="visually-hidden">${messagesTypes[options.type]}</h2>\n${message.text}`;
    ...
    +    if (options.type === 'error') {
    +      const ariaAlert = document.createElement('div');
    

    Reading up about alert on https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Tec... the role is on the h2 instead of a wrapper around it. Does that matter?

GrandmaGlassesRopeMan’s picture

@dawehner Thanks!

#260.2 - Lets just make this a class expression, done.
#260.3 - I think so for now. It's a new api.
#260.4 - I think it was a documentation leftover, I cleaned it up.
#260.5 - Yes, trailing comma.
#260.6 - Maybe @dmsmidt can answer this.

dmsmidt’s picture

Status: Needs review » Needs work

I'll try to test later, but a quick glance shows me that in case of an error message we read the message twice. Once through Drupal.announce() and once due to the addition of an element with role=alert in Drupal.theme.message . In the announce version we have a higher priority for both warnings and errors. In the theme we only read out messages of type error. Also, in the announce version we don't announce the error type, which we agreed we should.
Anyhow, seems to need work.

+++ b/core/misc/message.es6.js
@@ -0,0 +1,238 @@
+    // Alerts have a different HTML structure

Nit: add period to end of sentence.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
3.21 KB
36.75 KB

- An attempt at refactoring Drupal.theme.message.

dawehner’s picture

One thing, and this might be a minor detail, which confused me a bit is that this is basically a dom only api. There is no way to get an array of messages somehow, so you could do something about it. Maybe the usecases are limited, but I would have expected to have that capability in a new API we design from scratch. Any thoughts about that?

GrandmaGlassesRopeMan’s picture

- Fixes a mistake with inserting HTML elements.
- #264 - You should be able to construct an array of messages by fetching them from the DOM yourself. I'm not sure if there's value in storing message data on the instance of Drupal.message, but maybe a followup?

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
dawehner’s picture

I don't feel really confident to approve this patch.

- Fixes a mistake with inserting HTML elements.

Damnit, test coverage actually helps.

- #264 - You should be able to construct an array of messages by fetching them from the DOM yourself. I'm not sure if there's value in storing message data on the instance of Drupal.message, but maybe a followup?

Agreed

+++ b/core/misc/message.es6.js
@@ -0,0 +1,237 @@
+      // If the index has spaces, add several data matches, the same way
...
+        // class names work.
+        currentIndex.trim()
+          .split(whitespace)
+          .map(i => `[data-drupal-message-id^="${i}"]`)

🔧 This line jump of the comment makes it really hard to read :(

❓ On top of that I'm a bit confused why we have to support multiple indexes both using an array and using a string with spaces, but maybe I'm missing something obvious.

Note: I'm following emoji code ... https://gist.github.com/pfleidi/4422a5cac5b04550f714f1f886d2feea

GrandmaGlassesRopeMan’s picture

- Cleans up sketchy comment placement mentioned in #268. 👍
- Removes string manipulation methods on indexes within select method. 👍

GrandmaGlassesRopeMan’s picture

- no sourcemap 🤦‍♂️

dawehner’s picture

  1. +++ b/core/misc/message.es6.js
    @@ -99,16 +99,9 @@
    +      // Constuct an array of selectors based on the available message index(s).
    

    🙃 Typo in Constuct

  2. +++ b/core/misc/message.js
    @@ -49,13 +49,8 @@
    @@ -139 +134,2 @@
    
    @@ -139 +134,2 @@
    -})(Drupal);
    \ No newline at end of file
    +})(Drupal);
    +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJ...
    \ No newline at end of file
    

    ❌ Wait, do we include source mappings by default?

GrandmaGlassesRopeMan’s picture

FileSize
36.32 KB
484 bytes

@dawehner Yep, no source maps are included by default. You'll need to run yarn run watch:js-dev if you want source maps. I had accidentally included it in #269 but quickly realized that for #270.

- Fixed spelling mistake in #271.

dawehner’s picture

Thank you @drpal. It would be great if one of the two other JS maintainers @droplet or @nod_ could sign this patch off.

star-szr’s picture

If the <div data-drupal-messages> element is required for this to work, then we should be updating Stable too (we're already updating Classy). This markup change may be slightly disruptive but probably acceptable.

Also, I'm not seeing much discussion about performance since this rolls back the big change in #2853509: Don't render status messages if there are no messages but also include their assets if there might be.

+++ b/core/themes/classy/templates/misc/status-messages.html.twig
@@ -21,6 +21,8 @@
+{{ attach_library('classy/messages') }}

Didn't #2853509: Don't render status messages if there are no messages but also include their assets if there might be remove the need for this line since it's now always included via classy.info.yml?

tedbow’s picture

Status: Needs work » Needs review
FileSize
3.19 KB
36.67 KB

Added stable/templates/misc/status-messages.html.twig to stable/templates/misc/status-messages.html.twig

Re performance using @alexpott's idea here #2853509-22: Don't render status messages if there are no messages but also include their assets if there might be about not including the actual template if there are no messages from the server just a div place holder. This works but there must be some CSS difference because there is not padding at the top of the message in Bartik. That will need to be fixed.

Removed {{ attach_library('classy/messages') }}, yes not needed anymore.

tedbow’s picture

Status: Needs work » Needs review
FileSize
2.04 KB
36.59 KB

Some test fixes

  1. +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
    @@ -74,17 +74,26 @@ public static function generatePlaceholder(array $element) {
    +        '#markup' => '<div data-drupal-messages><div class="messages__wrapper layout-container" data-drupal-messages=""></div></div>',
    

    I didn't mean to leave in the inner div here. This cause the ErrorHandler test.

  2. +++ b/core/modules/system/tests/themes/test_messages/templates/status-messages.html.twig
    @@ -4,7 +4,6 @@
    -{{ attach_library('classy/messages') }}
    

    I think removing these lines remove a return character in the response which broke the BigPipeTest. Remove the return character from the test expectation.

tedbow’s picture

Status: Needs review » Needs work
Issue tags: +CSS
FileSize
27.64 KB
33.66 KB

The only thing I see remaining is the difference in padding(or margin) to message when there is no message from the server
JS Message

Server message

adding CSS tag.

tedbow’s picture

Status: Needs work » Needs review
FileSize
19.86 KB
4.7 KB
37.79 KB

Ok I figured out what the difference in #278

Server site messages at least in Bartik are not rendered directly under <div data-drupal-messages> but within an inner div that has the class messages__wrapper.

So the current patch actually doesn't target that inner div that server-side messages are rendered into which causes the margin problem.

We could just update our placeholder StatusMessages::renderMessages():

else {
      // Provide empty div for Javascript to add messages to.
      $render = [
        '#markup' => '<div data-drupal-messages></div>',
      ];
    }

But then we would take way the advantage of having a template in the first place because whatever we put in this placeholder would have to match what is in the theme's status-messages.html.twig file.

So working with @drpal we figured out that we need to actually create this inner div on the client side but in a way that can be way that can be overwritten by a theme.

Furthermore if no selector is passed to the Drupal.message we need the default target to be the inner div if it already exists(there was message from the server) and to create it if it doesn't exist.

The other thing that this means if you want to target the default messages container you can't just pass inner div element because it might not exist. So for target the default contain you should not pass an argument to the Drupal.message constructor. I updated the test js to reflect this.

GrandmaGlassesRopeMan’s picture

- Fixes comments and adds some more documentation.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
810 bytes
38.08 KB

- null is still a value. need to pass undefined for the default param assignment to trigger.

droplet’s picture

** I haven't done a full review.

#282,

document.querySelector returns null if no found.

GrandmaGlassesRopeMan’s picture

@dawehner,

+++ b/core/misc/message.es6.js
@@ -0,0 +1,268 @@
+      const wrapper = document.querySelector('[data-drupal-messages]');

This shouldn't ever return null, unless you've adjusted the template, then you should override this method.

+++ b/core/modules/system/tests/modules/js_message_test/js/js_message_test.js
@@ -0,0 +1,86 @@
+    messageObjects[selector] = new Drupal.message(selector === '' ? undefined : document.querySelector(selector));

It only mattered here, because we wanted to trigger the default param behavior.

Wim Leers’s picture

Assigned: Unassigned » Wim Leers

@drpal asked me to look at the assertions in the BigPipe test coverage that assert whitespace.

Wim Leers’s picture

Did some git+issue queue archeology. The test coverage with whitespace assertions was added in #2469431-210: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts. That just imported the contrib BigPipe module though. The actual issue where this test coverage was added is therefore a contrib issue: #2675670: BigPipe response delivery test coverage with JS enabled & disabled (BigPipe + HtmlResponseBigPipeSubscriber).

#2675670 does not state explicit reasons for asserting the whitespace. But the reasons are clear now:

  1. assertions of expected raw HTML ($this->assertRaw()), in the case of testBigPipeNoJS() ($status_messages->embeddedHtmlResponse)
  2. assertions of expected JSON for AJAX responses, in the case of testBigPipe() ($status_messages->embeddedAjaxResponseCommands)

I don't see a good alternative for them. For the first, one could argue it should just compare it as HTML (easier said than done, but possible). For the second… there is no good way to compare the expected JSON with the received JSON.

I think the answer is actually pretty simple: just update the test cases' expectations. It's extremely rare for us to change the rendered HTML for something like status messages. So needing to update these assertion expectations this one time is okay, and an acceptable cost.

chr.fritsch’s picture

Just a reroll

webchick’s picture

Assigned: Unassigned » nod_
Priority: Normal » Major

@drpal gave me an overview of this patch today. This looks like a great addition to help with the kinds of responsive JavaScript UIs we want to do, both now [Settings Tray] and in the future. As a result, escalating this to "major" because without a central API for this I can quickly see Media Library + Settings Tray + Layout Builder + whatever doing their own thing here.

As part of the walkthrough, he showcased this UI, which is part of the test:

Only local images are allowed.

This demonstrates that adding/removing various types of status messages functions, both page-level and local-level scope, which is awesome. I had asked about accessibility, and he pointed me to the place in the code where Drupal.announce() is called on these, so we seem to be covered on that basis as well.

I notice up in #251 that @nod_ was pushing back on making this a class with all the ES6 fanciness. With the ENORMOUS caveat that my JavaScript knowledge could fit inside here: || this does seem consistent with the way that we've gone about adding new APIs to the PHP part of Drupal; for example, the Migrate system doesn't use hooks, it uses Symfony Events because that's the New World Order™. While it introduces some inconsistency, it also means we don't add more legacy code to clean up later.

However, it seems like it'd be good to get @nod_'s sign-off on this patch's approach, if possible. So assigning to him. It looks like this patch has been pretty stalled for a couple of months, and it'd be a nice addition to 8.5 if we could swing it.

nod_’s picture

Assigned: nod_ » Unassigned
Status: Needs review » Needs work

I'm fine with ES6 class now. One thing I'd like to point out is that I tried to keep functions in Drupal.theme to return strings only. Theme functions shouldn't be dependent on the DOM (i know some old ones are but can't get rid of them easily). That way if someone wants to swap Drupal.theme with some other templating language it won't be an issue. And a theme function named "createMessageInternalWrapper" just doesn't sound like a theme function :p

Patch doesn't work in firefox: TypeError: wrapper is null at message.js:88:9.

GrandmaGlassesRopeMan’s picture

Assigned: Unassigned » GrandmaGlassesRopeMan
GrandmaGlassesRopeMan’s picture

Assigned: GrandmaGlassesRopeMan » Unassigned
Status: Needs work » Needs review
FileSize
38.12 KB
1.94 KB

@nod_ - An interesting point. I agree that Drupal.theme.createMessageInternalWrapper is not the best name. Both of the theme functions do try and make some assumptions based on the state of the DOM, and compute some output. This is especially difficult in this scenario due to the fact that messages could be rendered on the server, and we want to add messages to the default location.

Additionally, while I couldn't replicate any issues in Firefox directly, it does point out a case where we did not proactively error early enough.

GrandmaGlassesRopeMan’s picture

- A slight revision to theme function names. From createMessageInternalWrapper to messageInternalWrapper

GrandmaGlassesRopeMan’s picture

Issue summary: View changes
tedbow’s picture

+++ b/core/misc/message.es6.js
@@ -0,0 +1,267 @@
+  Drupal.theme.messageInternalWrapper = (messageWrapper) => {

Regarding @nod_ comment in # I think this would better if this were not a theme funciton. It seems different from what `Drupal.theme` functions are currently used for.

Also it would still be overridable if it was `Drupal.message.messageInternalWrapper`

Other than that it looks good to RTBC!

GrandmaGlassesRopeMan’s picture

tedbow’s picture

Status: Needs review » Reviewed & tested by the community

@drpal looks good

RTBC 🎉

tedbow’s picture

Status: Needs work » Needs review

#296 is getting unrelated DrupalCI errors like

1) Drupal\Tests\system\Kernel\Action\ActionTest::testOperations
Drupal\Core\Database\DatabaseNotFoundException: SQLSTATE[HY000] [1049] Unknown database 'jenkins_drupal_patches_40429'

Retesting

tedbow’s picture

Status: Needs review » Reviewed & tested by the community

tests passed

nod_’s picture

Thanks for moving some things out of Drupal.theme. It's overdue and we're on page #2 so that's a RTBC from me too :) thanks a lot for pushing this over the finish line.

If future me complains again about Drupal.theme not returning a string here is some working code:

      this.messageWrapper.innerHTML += Drupal.theme('message', { text: message }, options);
Drupal.theme.message = ({ text }, options) => {
    const messagesTypes = Drupal.message.getMessageTypes();
    const typeLabel = messagesTypes[options.type];

    return `<div 
      class="messages messages--${options.type}"
      data-drupal-message-id="${options.id}" 
      data-drupal-message-type="${options.type}" 
      role="contentinfo"
      ${typeLabel ? ` aria-label="${typeLabel}"` : ''}>
        ${typeLabel ? `<h2 class="visually-hidden"${options.type === 'error' ? ' role="alert"' : ''}>${typeLabel}</h2>` : ''}
        ${text}
    </div>`;
  };
GrandmaGlassesRopeMan’s picture

Status: Needs work » Reviewed & tested by the community

Looks like a random failure from ci.

tedbow’s picture

Status: Needs work » Reviewed & tested by the community

Build time issue with DrupalCI

jibran’s picture

Status: Needs work » Reviewed & tested by the community
lauriii’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,267 @@
    +    if (options.type in messagesTypes) {
    

    Should we throw an exception if an invalid message type is provided?

    Also, it would be better to do the assertion before this function, to ensure that any overrides of this function wouldn't have to be able to handle invalid message types.

  2. +++ b/core/themes/classy/templates/misc/status-messages.html.twig
    @@ -21,6 +21,7 @@
    +<div data-drupal-messages>
    

    We are not allowed to make changes to markup in Classy and Stable themes. We can make exceptions to this case by case basis but it seems like the current approach is too risky.

    Instead of wrapping the old message, could we add a new div as a sibling for the messages?

    Maybe something like this would work:

    <div data-drupal-messages></div>
    <div backend-messages></div>
    
GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
38.57 KB
2.55 KB

- #310.1 - This makes sense. In message.add() we can check that message type is valid and throw an error if an invalid type is used.
- #310.2 - I don't really think that this is a particularly risky decision. Only in the case of a message provided to browser from the backend are we wrapping it, and only the actual message section.

dmsmidt’s picture

May I kindly note that this still is a blocker for Inline Form Errors #2876321: Update inline form errors summary after form validation with AJAX, however the current version of this API won't be able to manage a mix of server side and client side generated messages. By moving forward with #310.2 I fear this becomes even more difficult in the future.

lauriii’s picture

Thanks @drpal, the changes look great!

I thought more about this after #311 and #312 and I think we should proceed with the current solution given that there's downsides on the solution I proposed on #310. There's more risk involved with wrapping existing divs compared to adding new siblings but compared to many other kinds of changes, it is still one of the changes with least risk involved. This is also supported by the fact that @Cottser didn't hold particular concerns about this approach on his review #274.

Another concern that I have now, is that there are many overrides of this template that won't have this new wrapper. I suggest that we add the wrapper forcefully outside the template like we've done in the settings_tray module.

GrandmaGlassesRopeMan’s picture

@lauriii - Great! One thing to take into consideration is that unless you include this library, and create a new instance of the message class, nothing happens. Additionally whenever you're creating that new instance, you can specify a dom node as a destination for those messages. If you've overridden the template, and another module expects something (the default destination), then we throw an error, which I think is pretty appropriate behavior.

lauriii’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record

@drpal sorry for a late reply. This makes sense. The error indeed should be enough to guide users how to get the JS messages working.

It would be great to create another, more specific change record for this template change to suggest themers adding this change before they even see the error.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record

Here is the additional change record to help themers with this, https://www.drupal.org/node/2935209

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
    @@ -88,6 +87,13 @@ public static function renderMessages($type) {
    +        '#markup' => '<div data-drupal-messages></div>',
    
    +++ b/core/themes/bartik/templates/status-messages.html.twig
    @@ -23,7 +23,7 @@
    -    <div class="messages__wrapper layout-container">
    +    <div class="messages__wrapper layout-container" data-drupal-messages>
    

    Could we hide this empty DIV by default using js-hide or so?

  2. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,272 @@
    +        throw new Error(Drupal.t('There is no @element on the page.', { '@element': '[data-drupal-messages]' }));
    

    This is an error. I don't think we need to make this translatable.

  3. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,272 @@
    +    /**
    +     * Provide an object containing the available message types.
    +     *
    +     * @return {Object}
    +     *   An object containing message type strings.
    +     */
    +    static getMessageTypes() {
    

    What about using getMessageTypeLabels?

  4. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,272 @@
    +      if (!(Drupal.message.getMessageTypes().hasOwnProperty(options.type))) {
    +        throw new Error(Drupal.t(`The message type, ${options.type}, is not present in Drupal.message.getMessageTypes().`));
    +      }
    

    Again, I don't see why this needs to be translated

  5. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,272 @@
    +     * @return {number}
    +     *  Number of removed messages.
    ...
    +     * @return {number}
    +     *  Number of removed messages.
    

    NITPICK: There is one space missing here :P

GrandmaGlassesRopeMan’s picture

@dawehner Thanks!

- #317.1 - I think this div may not always be empty if the server has rendered some messages.
- #317.2 - This makes sense to me. I checked and we are only translating one other JavaScript error.
- #317.3 - 👍
- #317.4 - See #2.
- #317.5 - Fixed. I wonder if this is something we can enforce with eslint?

dawehner’s picture

- #317.5 - Fixed. I wonder if this is something we can enforce with eslint?

Good question. I had a look at https://eslint.org/docs/rules/valid-jsdoc and there was nothing obvious there.

- #317.1 - I think this div may not always be empty if the server has rendered some messages.

Could we somehow hide it at least for the empty case? Maybe I'm talking nonsense here.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

@drpal and myself talked about it quickly in slack and agreed for #317.1 to postpone to a followup.

GrandmaGlassesRopeMan’s picture

🎉™️

alexpott’s picture

  1. +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
    @@ -88,6 +87,13 @@ public static function renderMessages($type) {
    +    else {
    +      // Provide empty div for Javascript to add messages to.
    +      $render = [
    +        '#markup' => '<div data-drupal-messages></div>',
    +      ];
    +    }
    

    Nice - still not loading up the entire rendering system with twig for no messages. +1

  2. +++ b/core/themes/bartik/templates/status-messages.html.twig
    @@ -23,7 +23,7 @@
     {% block messages %}
       {% if message_list is not empty %}
         {{ attach_library('bartik/messages') }}
    -    <div class="messages__wrapper layout-container">
    +    <div class="messages__wrapper layout-container" data-drupal-messages>
           {{ parent() }}
         </div>
       {% endif %}
    
    +++ b/core/themes/classy/templates/misc/status-messages.html.twig
    @@ -21,6 +21,7 @@
      *   - class: HTML classes.
      */
     #}
    +<div data-drupal-messages>
     {% block messages %}
     {% for type, messages in message_list %}
       {%
    @@ -53,3 +54,4 @@
    
    @@ -53,3 +54,4 @@
       {% set attributes = attributes.removeClass(classes) %}
     {% endfor %}
     {% endblock messages %}
    +</div>
    

    I was wondering if we hadn't considered putting <div data-drupal-messages> inside the block in core/themes/classy/templates/misc/status-messages.html.twig

    That would mean core/themes/bartik/templates/status-messages.html.twig wouldn't have to change and neither would any other theme that had extended classy in the same way.

    It would mean an extra div in places though.

tedbow’s picture

@alexpott I think you form forgot to use <code> around <div data-drupal-messages> so it didn't show up in the comment

By looking at the html source of the comment I think what @alexpott meant to put was:

I was wondering if we hadn't considered putting
<div data-drupal-messages>

inside the block in core/themes/classy/templates/misc/status-messages.html.twig
That would mean core/themes/bartik/templates/status-messages.html.twig wouldn't have to change and neither would any other theme that had extended classy in the same way.

It would mean an extra div in places though.

alexpott’s picture

@tedbow yep you're right - sorry - I've fixed the comment.

dawehner’s picture

Re #322
There are certainly advantages of your proposal, but I'm wondering whether having a div hanging around and not being changable by themes, would be a bad thing.

alexpott’s picture

Isn't it changeable themers? - just don't use the (twig)block. I think we should err on the side of everyone getting the messages and if themers want to customise this then they need to do it in full knowledge that drupal needs a <div data-drupal-messages> in order to work as expected.

tedbow’s picture

I agree with #326 in that which try to make sure as many themes automatically get the JS messages as possible without any changes.

If we keep this change does there need to be updates to this change record? https://www.drupal.org/node/2935209 ?

lauriii’s picture

Status: Reviewed & tested by the community » Needs work

I'm not sure if I understand the reason behind moving the wrapper inside the block? It seems like this actually has reverse effect compared to what #326 states. Now if developers want to extend this template and override the block, they will have to remember to include the wrapper inside the block unless they use {{ parent() }}.

The change in #318 for Bartik is redundant and there's duplicate Drupal messages wrapper.

alexpott’s picture

@lauriii if we put the wrapper in classy's status-messages.html.twig then anything that extends classy gets js message support with no changes if they have copied Bartik. If we don't put it there then every theme that uses classy and extends status-messages.html.twig will need to be changed. I think adding the div to classy is part of the promise to classy - ie. to continue working with minimal changes to your theme.

If people override status-messages.html.twig and don't use {{ parent() }} they are going to include the <div data-drupal-messages> no matter what we do.

alexpott’s picture

Ignore me in #330. @lauriii is correct. We should go back to #318 but without the changes to Bartik's status-messages.html.twig - Bartik is only replacing the inner block - that's how extends works. I should have read the manual. https://twig.symfony.com/doc/2.x/tags/extends.html

tedbow’s picture

Status: Needs work » Needs review
FileSize
38.14 KB
584 bytes

Here is patch that does

We should go back to #318 but without the changes to Bartik's status-messages.html.twig

alexpott’s picture

So all I think we need to do is update the change record with something about how extends works and when you are going to need to do something.

GrandmaGlassesRopeMan’s picture

Status: Needs review » Reviewed & tested by the community

- tests pass
- change record updated, https://www.drupal.org/node/2935209
- 👍

lauriii’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/themes/classy/templates/misc/status-messages.html.twig
    --- a/core/themes/stable/templates/misc/status-messages.html.twig
    +++ b/core/themes/stable/templates/misc/status-messages.html.twig
    
    +++ b/core/themes/stable/templates/misc/status-messages.html.twig
    @@ -21,6 +21,7 @@
    +<div data-drupal-messages>
    

    We have to do this change also for Umami theme now that it has landed.

  2. +++ b/core/themes/bartik/css/components/messages.css
    @@ -4,10 +4,15 @@
    +.messages__wrapper .messages:first-child {
    +  margin-top: 28px;
    +}
    +.messages__wrapper .messages:last-child {
    +  margin-bottom: 13px;
    

    We should move these selectors to the end of the file after the RTL rule, so that the LTR and RTL rules are grouped together.

    We can also remove the .messages__wrapper from the selectors.

tedbow’s picture

Status: Needs work » Needs review
FileSize
1.35 KB
38.8 KB

#335 fixed both

samuel.mortenson’s picture

+++ b/core/modules/system/src/Tests/JsMessageTestCases.php
--- a/core/modules/system/templates/status-messages.html.twig
+++ b/core/modules/system/templates/status-messages.html.twig

+++ b/core/modules/system/templates/status-messages.html.twig
@@ -23,25 +23,27 @@
+<div data-drupal-messages>

I have concerns about relying on this markup in a JS API. Many themes (ex: Bootstrap, Drupal8 Zymphonies Theme, and AdaptiveTheme) have overridden status-messages.html.twig and would all need updated if this change got in.

I can think of two ways to address this:

1) Since we're already outputting a fallback in \Drupal\Core\Render\Element\StatusMessages::renderMessages - why not remove all the template changes and always output the empty div after the normal status messages?

2) We could have a fallback in JS, so that instead of an Error being thrown when [data-drupal-messages] is not present, we append that div somewhere on the page. Not sure how we would determine where that should go.

tedbow’s picture

@samuel.mortenson I see your concerns about themes having to be updated. Also if a site used a custom theme and they overrode this template they would need to update there theme. That would be very hard to tell how common that is.

So I chatted with @drpal about this and think we have solution that works kind of like your suggestion in #337.1

So there are benefits to have the messages in the same div has your server-side messages if any that is why we were adding the changes to the templates status-messages.html.twig. But also this template will not be used if there are no server-side messages.

The current approach in #336 was to output <div data-drupal-messages></div> if there were no server-side messages.

But of course that does lead to the problem that if

  1. a theme has overriden status-messages.html.twig and doesn't update this template after this API is added
  2. and there is page that has no server-side messages but there is client-side message (hopefully common after this API is used)
  3. then we would throw a JS error so the user a) would not see the message b) any further js on the page would not execute.

So this patch instead of adding code>

if there were no server-side messages it always adds <div data-drupal-messages-fallback class="visually-hidden"></div> whether there are server-side messages or not.

Then in message.es6.js default message container is being used and it doesn't exist then it changes the data-drupal-messages-fallback into the data-drupal-messages div by replacing the attributes and remove the visually-hidden class.

This way:

  1. the same behavior would be used if the theme didn't update template or if they there are no server-side messages
  2. Themes that didn't override status-messages.html.twig or want to update it can have there server-side and client-side messages in the same wrapper
  3. The will never be case where we need to throw a JS error because the div is not on the page.
  4. If a theme really wanted to put data-drupal-messages div somewhere else they can. As long it is on the page somewhere then data-drupal-messages-fallback div will not be used.
GrandmaGlassesRopeMan’s picture

Another change, which is not clearly highlighted in @tedbow notes is the change for when we figure out messageWrapper. Previously this happened when a new instance of Drupal.message was created, however due to template possibly not being rendered (bigpipe?) this caused some issues. Now, the messageWrapper location is figured out right before the first message is added to the page.

alexpott’s picture

+++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
@@ -87,12 +89,9 @@ public static function renderMessages($type) {
+    $render[] = [
+      '#markup' => '<div data-drupal-messages-fallback class="visually-hidden"></div>',
+    ];

This seems like a really good idea. A very nice BC layer. It'd be good to have a test for it.

One thought is that visually-hidden is probably the wrong class. I think hidden is the correct class. Since this is meant to be hidden from all users not just those that can see.

tedbow’s picture

@alexpott good catch! changing to use hidden class

droplet’s picture

I'd remove the data-drupal-messages-fallback and introduce an option for target wrapper selector: "[data-drupal-messages]".

If it's really needed, another option for autoCreateWrapper: bool. But I perferred:

Allow to change the wrapper HTML:

wrapperHTML: "HTML"
if (wrapperHTML != EMPTY)
  Create Fallback Wrapper
else
 "Developers hate Messages on current page"

It sorted one of the blockers in my early comments. It may has [data-drupal-messages] in the Modal & background but we only send message to Modal..etc. And our code using document.querySelector that only catch first element. First In, First Win.

tedbow’s picture

@droplet

I'd remove the data-drupal-messages-fallback and introduce an option for target wrapper selector: "[data-drupal-messages]".

There is already option to pick a target per instance of Drupal.message. We only use only use [data-drupal-messages] if no selector is specified. @see core/modules/system/tests/modules/js_message_test/js/js_message_test.js for an example of using different target.

"Developers hate Messages on current page"

Unless there is very very good reason not to I think we should add the BC layer that data-drupal-messages-fallback gives us

So if a theme like Bootstrap, Drupal8 Zymphonies Theme, or AdaptiveTheme as @samuel.mortenson mentioned in #337 did not update there templates they would always see the messages not a warning that the messages could not be displayed. Core goes out of its way to make sure that server-side messages via \Drupal\Core\Render\Element\StatusMessages::renderMessages are always placed on the page even if the "status message" block is intentionally deleted from the page. I think we should follow this same pattern here no matter if the theme was update or not the messages should show up on the page.

This especially important because we would introducing this in minor version of Drupal(8.6 hopefully 🙏). This is of course if we think messages that are created with this JS API our as important as server-side message which I think they are.

droplet’s picture

+++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
@@ -88,6 +89,10 @@ public static function renderMessages($type) {
+    $render[] = [
+      '#markup' => '<div data-drupal-messages-fallback class="hidden"></div>',
+    ];

What's the main reason that must do in server-side but not client-side? I believe the client-side script will benefit for React & JS 3rd parties API binding for a long-term.

tedbow’s picture

What's the main reason that must do in server-side but not client-side?

The server response has to provide some indication of where the messages should appear in the markup of the page. That is all this doing. Also nothing is stopping the theme from putting the messages in another location. All the theme would have to do is put the data-drupal-messages attribute somewhere on the page via JS or PHP.

I believe the client-side script will benefit for React & JS 3rd parties API binding for a long-term.

Yes that is fine long term. But I don't think we should wait on that to happen before we create simple API for Javascript message. Again this is API. we can expand it or change how the internals work later.

GrandmaGlassesRopeMan’s picture

Status: Needs review » Reviewed & tested by the community

🚀

andrewmacpherson’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs accessibility review

How much testing has been done with actual assistive technology? I've seen screen readers have been mentioned a few times, but some of the comments sound like assumptions which would need manual testing to confirm.

I think the way Drupal.announce() is being used here is a bit ambitious. For example it looks like we could be are adding a new role=alert region AND calling Drupal.announce() - I expect that will lead to messages being announced twice.

I'm not sure how to carry out manual testing. Would anyone mind walking me though the code, say in a Google Hangout or similar?

@dmsmidt - I see you did some screen reader testing in #214. Can you recall if you tested a situation where several messages were displayed? The static messages we have at the moment group all errors inside one role=alert wrapper, for example. I'm curious as to what happens when an extra error message is added when there is already an existing error message. Browsers seem to differ in the way they handle aria-atomic and aria-relevant - I think they provide different defaults.

Update:
role=alert has an implicit aria-atomic=true. That means when a live region changes, it's supposed to read the entire live region. If multiple error messages are being added one at a time to a single role=alert wrapper things code get verbose quickly.

I see the patch has some test modules, and the tests have a mention of multiple-error, so that's encouraging. I'll try using the test modules for manual testing with some screen readers.

andrewmacpherson’s picture

Status: Needs review » Needs work

role="contentinfo" is definitely wrong. There's an issue to fix this for static messages too (where?). contentinfo is supposed to be for information about the document as a whole, not status updates. It's a landmark role, and there should only be one such region per page. It's intended to serve the same purpose as a top-level HTML footer element.

role=status is the appropriate role. Like role=alert it is an ARIA live region. It's possible that we might not need Drupal.announce() at all if we employ role=status correctly instead of contentinfo.

Update: I couldn't find the issue about static messages, so I filed #2942404: Messages should have role=status instead of role=contentinfo.

GrandmaGlassesRopeMan’s picture

@andrewmacpherson

I made a minor change here, github.com/mattgrill/drupal, if you want to test this.

alexpott’s picture

Issue summary: View changes

@andrewmacpherson I've added a how to test section to the issue summary.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
39.39 KB
1.19 KB

This is the changes that I posted in #349, but as a patch!

I'm not sure how to carry out manual testing. Would anyone mind walking me though the code, say in a Google Hangout or similar?

@andrewmacpherson - If you want to review this in realtime, let me know. I can be available whenever.

andrewmacpherson’s picture

Status: Needs review » Needs work
FileSize
13.92 KB

I did some manual testing of patch #351 with a couple of screen readers and various browsers.

  1. Nothing to do with logic, but the messages at /js_message_test_link are very confusing to follow in a screen reader. As in, the specimen message content. We have buttons, invisible headings, roles, and message content which all contain various combinations of "error", "status", "message", "warning" and numbers. Manual testing would be much easier if the messages had some content apart from these. Maybe if the messages were all about different farmyard animals, or something, they'd be easier to follow in manual testing.
  2. The accessible role should be "alert" or "status" but not both. The JS makes a <div role="status"> for every message, and then it nests an additional role="alert"inside this for error messages. This might confuse assistive technology, but I'm not certain of that. I am certain that this isn't the right way to use these roles though. An element with role="status" has an implicit aria-live="polite", while an element with role="alert" gets an implicit aria-live="assertive". Nesting assertive inside of polite doesn't make sense. Instead, the message wrapper DIV should get a role of "status" or "alert".
  3. Don't stick a role on the nested heading element, because then it's no longer a heading (the role attribute semantics override the HTML element name).
  4. I don't understand why there's a H2 for every message. There are a few problems with this....
  5. It's using a H2 regardless of where the message appears. If this feature is intended for inline messages, then using all these H2's are likely to disrupt the document outline in strange ways....
  6. Once a few messages have appeared on screen, there may be several headings with the same text. An attached screenshot shows what the NVDA screen reader thinks the heading structure looks like after pressing "Add multiple". It has 2 headings called "warning message" and 2 headings called "status message". Note there aren't any headings called "error message" because these have had their semantics overridden by the role attribute.
  7. Bear in mind that screen reader users often rely on headings to navigate, and build up a mental model of the page as they go along. After hearing a message, they might want go looking around to find it, or correct a problem. Confusion could result from introducing several new headings, duplicate headings, or headings with inappropriate levels. It's like shifting the street signs.
  8. Screen reader announcements of messages does seem to take place, but it's inconsistent. I'm not sure whether that's to do with they way drupal.message.announce() is used, or the fact that different numbers of aria-live regions are being created in short succession, or browser support for aria-live regions, or whatever. In any case it needs more testing. I tried ChromeVox, WIndow Eyes, and NVDA, in combination with IE11, Firefox 52-esr, and Chrome 65. Haven't tried JAWS, iOS VoiceOver, or MacOS VoiceOver yet.
dmsmidt’s picture

@andrew, I must say, fabulous testing and explanations.

#1 got me hooked, more animals in core thus seem to be legit!
#4-7: What do you propose? For sure the heading elements should be limited.
#8: Would be great if you could find a pattern, or at least provide us with the way we could most likely reproduce that issue.

andrewmacpherson’s picture

I'm still thinking about this.

One idea is to stop adding the headings in the JS behaviour. Instead, put a visually-hidden heading "status messages" before the {{ messages }} in the template.

Pros:
- no risk of introducing a heading with the wrong level
- doesn't proliferate headings with duplicate text
- avoids changing document outline after the page loaded.

Cons:
- if there are no messages, we have a heading with no relevant content after it. That might not be too bad though. If I found a heading saying messages, but there were none, would I be confused, or is that clear enough to understand? We could mitigate it by managing a fallback "no messages", so a user can find "status messages, heading 2, no messages".

tedbow’s picture

Status: Needs work » Needs review
FileSize
3.31 KB
39.32 KB

#352
1. Made the message texts somewhat more whimsical and unique. No farm animals, I am not that clever.
2. fixed now only the wrapper as a role attribute.
3. done in 2)
4. H2s We were trying to make the client side messages have similar markup structure to messages that are render on the server. Bartik's message structure is going to come from core/themes/classy/templates/misc/status-messages.html.twig. This will true for any theme that extends classy and doesn't override the template. core/themes/stable/templates/misc/status-messages.html.twig also uses h2s. themes/bootstrap/templates/system/status-messages.html.twig for some reason uses h4s.

So in most sites the server side messages will have these H2 headings.

Should we worry about attempting to match the server-side message structure? We can't change stable theme template. I assume if the current markup is bad for accessibility we could change the markup classy.

Maybe the nature of client side messages in that they will appear after the page is loaded and that they may appear in various locations on the page means that they are different enough and should not have to match the server-side messages markup.

If we want the server-side and client side message markup structure should be the same, as much as possible, then I think we should keep the current structure with the H2s. If the H2 should not be there then they probably shouldn't be there regardless of whether it is server-side or client-side message and we should remove them both after this issue in a follow-up.

If there is no reason for server-side and client side message markup structure to be the same then we can remove the H2's here. but I would like feedback on this first.

6.

Note there aren't any headings called "error message" because these have had their semantics overridden by the role attribute.

This patch removes the role attribute on H2 so that should not be an issue.
For other points see 4) here.
7. So maybe this means we shouldn't have H2's at all the client-side.
8. I think it may be problem with Drupal.announce. I made a test module https://github.com/tedbow/annouce_tester
Just enable and goto /annouce_tester/announce

It will call Drupal.announce 10x very quickly.

If this is problem with Drupal.announce we can file a follow up.

Status: Needs review » Needs work

The last submitted patch, 355: 77245-355.patch, failed testing. View results

GrandmaGlassesRopeMan’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
103.57 KB
22.83 KB

- correct constructor case, Drupal.message() -> Drupal.Message()
- cleanup test code
- fix tests from #355
- removeAll method is removed. select and remove are singular

tedbow’s picture

Status: Needs review » Reviewed & tested by the community
  1. +++ b/core/misc/message.es6.js
    @@ -115,79 +115,45 @@
    -     * Select a set of messages based on index.
    +     * Select a message based on id.
    

    I think changing select/rmoeve to only use 1 message id is good change makes the code much simpler.

    I played around the test code it will still be super simple for custom to Javascript to select/remove multiple messages if they have an array of message ids.

    ids.forEach(id => messageObjects.default.zone.remove(id));
    
  2. +++ b/core/misc/message.es6.js
    @@ -115,79 +115,45 @@
    -    removeElements(elements) {
    -      if (!elements || !elements.length) {
    -        return 0;
    -      }
    -
    -      const length = elements.length;
    -      for (let i = 0; i < length; i++) {
    -        this.messageWrapper.removeChild(elements[i]);
    -      }
    -      return length;
    

    This is also is good to remove super simple just call multiple times.

  3. +++ b/core/misc/message.js
    @@ -133,7 +114,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
    diff --git a/core/modules/system/tests/modules/js_message_test/js/js_message_test.es6.js b/core/modules/system/tests/modules/js_message_test/js/js_message_test.es6.js
    
    diff --git a/core/modules/system/tests/modules/js_message_test/js/js_message_test.es6.js b/core/modules/system/tests/modules/js_message_test/js/js_message_test.es6.js
    new file mode 100644
    

    Thanks for adding the es6 version of the test js code!

  4. +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/JsMessageTest.php
    @@ -45,8 +45,8 @@ public function testAddRemoveMessages() {
    -        $web_assert->elementContains('css', $selector, "Msg-$type");
    -        $current_messages[$selector] = ucfirst($type) . " message Msg-$type";
    +        $web_assert->elementContains('css', $selector, "This is a message of the type, $type. You be the the judge of its importance.");
    +        $current_messages[$selector] = ucfirst($type) . " message This is a message of the type, $type. You be the the judge of its importance.";
    
    @@ -64,7 +64,7 @@ public function testAddRemoveMessages() {
    -      $current_messages[] = ucfirst($types[$i % count($types)]) . " message Msg-$i";
    +      $current_messages[] = ucfirst($types[$i % count($types)]) . " message This is message number $i of the type, {$types[$i % count($types)]}. You be the the judge of its importance.";
    

    These changes look good they are only update the text in tests to match the change in the test JS code I added in #355

I would say this is RTBC.

I think we could have 2 follow ups
#355.4 should we remove h2 for messages for both server-side and client-side messages.
#355.6 does Drupal.announce work when calling multiple times quickly.

lauriii’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/comment/src/Entity/Comment.php
index 7d75b6e6dd..0000000000
--- a/core/modules/comment/tests/src/Functional/Update/CommentHostnameUpdateTest.php

+++ /dev/null
index dc4f37b63f..0000000000
--- a/core/modules/comment/tests/src/Kernel/CommentHostnameTest.php

+++ /dev/null
index 7cd2e0926a..0000000000
--- a/core/modules/config/tests/config_test_id_mismatch/config_test_id_mismatch.info.yml

+++ /dev/null
--- a/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceItemTest.php
+++ b/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceItemTest.php

The latest patch has lots of unrelated changes

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
41.82 KB

- matt learns how to copy/paste.

lauriii’s picture

Status: Needs review » Needs work

This certainly starts to look better. Here's few more comments after doing an extensive review on the patch.

  1. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,240 @@
    +    static getMessageTypeLabels() {
    ...
    +      if (!options.priority && (options.type === 'warning' || options.type === 'error')) {
    +        options.priority = 'assertive';
    +      }
    

    I'm wondering if the ::getMessageTypeLabels should be extended to support this use case.

    Also, is there use case for setting the priority outside of the message type?

  2. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,240 @@
    +     *   The context of the message, used for removing messages again.
    

    I guess this comment is outdated since we are not using the options for removing messages again (only the options.id).

  3. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,240 @@
    +     *   which can be used as a sort of tag for message deletion.
    

    This comment could be more explicit than saying that it works as "sort of tag for message deletion".

  4. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,240 @@
    +     *   being sent to Drupal.announce() this should be `''`.
    

    Should we just say empty string?

  5. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,240 @@
    +       * Use the provided index for the message or generate a unique key to
    +       * allow message deletion.
    

    We should at least remove the word unique from the documentation since Math.random() isn't unique.

  6. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,240 @@
    +  Drupal.theme.message = ({ text }, options) => {
    

    Nit pick: what do you think, would this function be easier to read if we destructured the option properties as well?

  7. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,240 @@
    +    const messageWraper = document.createElement('div');
    

    s/messageWraper/messageWrapper

  8. +++ b/core/misc/message.es6.js
    @@ -0,0 +1,240 @@
    +    messageWraper.setAttribute('class', `messages messages--${options.type}`);
    

    This could be also customized in the template. We should add mention in the change record that if they've customized this part in their template, they might want to override the theme function as well.

  9. +++ b/core/modules/system/templates/status-messages.html.twig
    @@ -21,25 +21,27 @@
    -      {% if status_headings[type] %}
    ...
    +    {% if status_headings[type] %}
    

    Just leaving a note here: I assume the reason for this change is that some text editors automatically fix indentation when you add the wrapping div. This has been indented like this to have correct indentations in the outputted markup. IMHO we should prioritize readability of template files over markup output so I'm fine with this change.

  10. +++ b/core/modules/system/tests/modules/js_message_test/js/js_message_test.es6.js
    @@ -0,0 +1,92 @@
    +  // Message types
    

    Nitpick: missing a period.

Looks like all of the accessibility feedback has been addressed, but would be great to get a sign off from the topic maintainers. Also, we should open a follow-up for the h2 issue.

GrandmaGlassesRopeMan’s picture

Alright,

#361.1 - I'm not sure.
#361.2 - Fixed. This was confusing anyways.
#361.3 - Fixed.
#361.4 - Fixed.
#361.5 - Fixed. pseudo-random
#361.6 - Fixed. Yes!
#361.7 - Fixed
#361.8 - We can add a note about this in the change record.
#361.9 - 👍
#361.10 - Fixed.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 362: 77245-362.patch, failed testing. View results

tacituseu’s picture

Status: Needs work » Needs review

Unrelated failure.

andrewmacpherson’s picture

Issue tags: +Needs manual testing

Looks like all of the accessibility feedback has been addressed, but would be great to get a sign off from the topic maintainers.

I need to take this patch for another round of manual testing. Last time I tried it wasn't very good. There were confusing headings, and parts of the message weren't announced, and it wasn't always clear that there were multiple messages being announced, and some announcements differed from the content of the visible message. The message status level wasn't conveyed. I thought it was a messy mix of all sorts of roles, headings, implicit live regions AND Drupal.announce().

Also, we should open a follow-up for the h2 issue.

The headings are a VERY big concern - let's address them here please.

lauriii’s picture

Status: Needs review » Needs work

Discussion from Slack:

lauriii [Yesterday at 22:21] in #accessibility
> The headings are a VERY big concern - let’s address them here please.
does the headings cause problems specifically on the JS implementation?

andrewmacpherson:
I think so.  Put it another way - there's nothing wrong with the headings when messages appear in a full-page from the server. Assuming the messages block is in the appropriate theme region, of course. (edited)

andrewmacpherson:
The ARIA roles in the messages block from the server are wrong, but there's already an issue for that.

andrewmacpherson:
but the beadings in the JS messages are duplicated, and don't show consideration for where they appear in the document.

So I think based on that we cannot move the heading issue to a follow-up.

chr.fritsch’s picture

Not sure if this is an issue that should be addressed here, but I noticed it when I played around with the buttons on /js_message_test_link.

So clicking on "Add multiple 'error' and one 'status'" and then on "Remove multiple" doesn't remove all the messages.

GrandmaGlassesRopeMan’s picture

@chr.fritsch - Add multiple 'error' and one 'status' is used with the Remove 'error' type button. Additionally all of this is test code.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
mgifford’s picture

I'm trying to figure out how to test this for accessibility. If it's an API, I don't know how it affects what is presented to the DOM.

If I can get some guidance here it would be useful. I'd like to see this in Core, but am not a JS person.

lauriii’s picture

@mgifford you are right that we are only adding an API in this patch. You can test this by following the instructions in the issue summary 🙂

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

DuaelFr’s picture

Hi there,
Congratulations for the great job done here and all the dedication it needed to get to this point!

I made an "a11y-minded" code review. I really like the way you used Drupal.announce() to manage the addition of a new message. I appreciate the fact we can set a different message for screen reader users. Good job here!

I don't know why the patch does not already provide a CommandInterface implementation to use this new API but I trust you people (and I don't really have the time to read all the previous comments).

I can't make a real life test as I still don't have a proper screen reader installed but I can't see why this would not work as expected. I hope someone with a screen reader can jump in to finish this review and push it to RTBC soon :)

Kudos for everyone!

GrandmaGlassesRopeMan’s picture

Status: Needs review » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 362: 77245-362.patch, failed testing. View results

andrewmacpherson’s picture

The H2 headings still need to be removed.

phenaproxima’s picture

Status: Needs work » Needs review
FileSize
41.75 KB

Reroll against 8.7.x.

Status: Needs review » Needs work

The last submitted patch, 378: 77245-378.patch, failed testing. View results

phenaproxima’s picture

Discussed with @drpal today, and we figured out why AjaxTest fails (although TBH I'm not sure how it has ever worked), although JsMessageTest is still broken (for reasons that are not related, although I'm not sure if the test is wrong, or the code). Patch forthcoming.

What we discovered in looking at this patch is that adding the fallback message area to the StatusMessages render element will not work. That element is automatically prepended to most AJAX responses delivered by the form system, which means every form AJAX response will return a fallback messages area! That's no good.

So I suggested an alternate approach -- let's revert the changes to the StatusMessages element, and add the fallback message area to the SystemMessageBlock plugin's output. That way, it will be rendered at least once in most themes (I can't imagine too many instances where you'd never want to show the messages block), but not more than once under most circumstances.

DuaelFr’s picture

I've seen projects where the SystemMessageBlock were disabled. If not already anticipated, please consider testing the existence of the area before trying to write in :)

lauriii’s picture

I discussed with @phenaproxima about this issue and we agreed that adding the fallback in Drupal\Core\Render\Element\StatusMessages doesn't lead to the right behavior since it could be used for rendering messages out of the context of Drupal\system\Plugin\Block\SystemMessagesBlock. Since the wanted behavior is that the JS messages are attached to the Drupal\system\Plugin\Block\SystemMessagesBlock, we could just render the fallback in that block directly. In that case we have to explicitly add a fallback for the element in Drupal\block\Plugin\DisplayVariant\BlockPageVariant, which is fine.

phenaproxima’s picture

Status: Needs work » Needs review
FileSize
44.17 KB
3.63 KB

This oughta do the trick. A quick summary of the changes:

  • StatusMessages now has a new public static method, fallback(), which generates the renderable array for the fallback area. renderMessages() no longer renders the fallback area.
  • BlockPageVariant and SimplePageVariant explicitly call StatusMessages::fallback() in order to make sure the the fallback area is always present, even if SystemMessagesBlock is disabled.
  • SystemMessagesBlock always renders the fallback area.

Status: Needs review » Needs work

The last submitted patch, 383: 77245-383.patch, failed testing. View results

lauriii’s picture

Thank you @phenaproxima 🤩

  1. +++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
    @@ -97,4 +97,16 @@ public static function renderMessages($type = NULL) {
    +  public static function fallback() {
    

    I think this should be somewhere else than the render element since there's no connection between these two.

  2. +++ b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
    @@ -178,6 +179,7 @@ public function build() {
    +      $build['content']['messages_fallback'] = StatusMessages::fallback();
    

    Should we set weight for this element as well so that it would be rendered at the same location as the other status messages?

Wim Leers’s picture

#383 changed where the fallback is added. Now the updated BigPipe test expectations are wrong. That’s also why BlockPageVariantTest fails. Looks pretty simple to fix! :)

tim.plunkett’s picture

+++ b/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php
@@ -55,6 +56,7 @@ public function build() {
           '#type' => 'status_messages',
           '#weight' => -1000,
...
+        'messages_fallback' => StatusMessages::fallback(),

I would rather see this as a flag set on the render array, like '#include_fallback' => TRUE,
And then use a #pre_render to actually include it.

This would also address the #weight issue @lauriii mentions.

phenaproxima’s picture

Status: Needs work » Needs review
FileSize
44.71 KB
4.56 KB

Okay; I removed the fallback() method and implemented an #include_fallback flag on the render element (defaults to FALSE to maintain backwards compatibility). The variants, and SystemMessagesBlock, set #include_fallback to TRUE.

Let's see if this passes the tests.

Status: Needs review » Needs work

The last submitted patch, 388: 77245-388.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
11.18 KB
54.82 KB

The token changed from _HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA to EBaaW0Ackt8KfI-t1xwsNY4hd3CsCxNyTZLB3t0f71k, that's not a problem.

The problem is that there is a second instance of StatusMessages::renderMessages added to the page, and the count of placeholders is therefore wrong.
I think this a real bug, not just a wonkiness of BigPipeTest. And I think it's unrelated to the difference between the flag or the dedicated call.

Note that the token change may just be necessitated by this additional instance, and might need to be reverted if the bug is fixed.

tim.plunkett’s picture

@phenaproxima thinks there should be a second placeholder. Not so sure myself, but if that's okay then this should pass.

The last submitted patch, 390: 77245-messages-369.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 391: 77245-messages-391.patch, failed testing. View results

phenaproxima’s picture

Status: Needs work » Needs review
FileSize
45.16 KB
5.49 KB

Trying a slightly different approach, starting from #388.

The thing is -- there is no good reason for the fallback area to ever be rendered with a placeholder, BigPipe or no BigPipe. It's completely static markup; all the dynamic stuff takes place on the client side, by design. So I changed generatePlaceholder() such that, if #include_fallback is truthy, the fallback area will be rendered immediately, alongside the placeholder, rather than as part of the placeholdered content.

In the interest of full disclosure: this does have the side effect of changing the output of generatePlaceholder(). However, I don't know if we consider render arrays built by internal pre-render callbacks to be part of Drupal's API, so I don't know if this has BC implications (I doubt it, but I thought I'd mention it).

I ran all the tests locally which broke in #388, and they all passed. Fingers crossed, then.

phenaproxima’s picture

Added a change record describing the new #include_fallback property: https://www.drupal.org/node/3002643

NickDickinsonWilde’s picture

One thing that I'm unclear if it is the desired behaviour:
If there is a *non* JS rendered status message, it isn't removable - to easily see, just run a cache clear while on /js_message_test_link.
One thing that concerns me as a sometime themer - anyone using a Bartik based child theme, if they've customized messaged div padding/margin will likely have to adjust it; should this at least have a CR? (Due to the changes in /core/themes/bartik/css/components/messages.css.
Other than those two concerns RTBC

phenaproxima’s picture

If there is a *non* JS rendered status message, it isn't removable - to easily see, just run a cache clear while on /js_message_test_link.

Yeah...that's unfortunate, but so far there's no way in core to dismiss a message. But there should be, and if I'm not imagining things, I think there's already an issue open for that. Implementing it here would be out of scope, though.

anyone using a Bartik based child theme, if they've customized messaged div padding/margin will likely have to adjust it

@lauriii is the authoritative word on this, but if my memory is correct, I believe that Bartik is considered "internal" and not meant to be extended by themers. Meaning that we can safely change its CSS in this patch. Classy and Stable, on the other hand, are base themes and they generally cannot be changed until Drupal 9.

NickDickinsonWilde’s picture

Status: Needs review » Reviewed & tested by the community

Then unless @lauriii sees a problem there +1 RTBC

adriancid’s picture

@phenaproxima I see this on the patch.

  1. +++ b/core/misc/message.js
    @@ -0,0 +1,137 @@
    +  };
    +})(Drupal);
    \ No newline at end of file
    

    Add a newline at the end of the file

  2. +++ b/core/modules/system/tests/modules/js_message_test/js/js_message_test.js
    @@ -0,0 +1,79 @@
    +  };
    +})(jQuery, Drupal, drupalSettings);
    \ No newline at end of file
    

    The new line is missing here too.

tim.plunkett’s picture

Those are generated files. Not fixable here.


This approach is much better and I'm glad to see it fixed the tests

andrewmacpherson’s picture

Status: Reviewed & tested by the community » Needs work

Accessibility concerns from earlier comments haven't been addressed, and I've found a few new ones. I think I have a better understanding of this now.

  1. The headings are still a problem. These MUST NOT be injected per-message, they are extremely disruptive to the document outline, repetitive, and confusing for navigation. Remember, a heading is not scoped to the message DIV we are injecting, rather it acts as a parent for all content thsat follows it, until the next H1 or H2. If a page/form is making good use of H3 and H4 levels sensibly, then injecting a H2 will break all of that. Let's remove the from the message template in this issue, please.
  2. The aria-live announcements are flaky. Remember, role="status" and role="alert" are implicit live regions per the ARIA spec, but we're using them in conjunction with the live region from drupal.announce(), and that's where things are getting messy.
    • Live regions work by reporting changes in their content, but simply injecting a role="alert" won't cause them to be announced. There are known quirks in browser support, and the ARIA spec itself isn't clear on how they should behave. For some breowsers you need explicit aria-live properties. I've seen some encouraging approaches where an empty <div role="alert"></div> is injected first, and then populated with the message text as a separate step, so the browser effectively sees a change in the live region content.
    • I don't we should be using Drupal.announce() for the messages. It's good for conveying stuff that is otherwise only apparent visually (e.g. the module page - "3 results in the modified list"), but using it to echo text which is visible on screen isn't really what we intended it for.
    • Visually, the messages type is conveyed, but this isn't conveyed aurally. We're passing the message text only to Drupal.announce(), then indicating the message type in the theme function.

    I think we can leave the aria-live stuff to follow-up issues. It's going to be tricky to get it good across a range of browser/OS/AT combinations. Would it be a good idea to mark the API as internal initially? So far nothing is actually using it, this patch just adds the API.

andrewmacpherson’s picture

The aria-live mappings are off, this is something new I found. We have 3 message types (status, warning, error) and we map them to polite/assertive in aria-live. But we're doing it inconsistently.

In Drupal.Message.announce()...

  • error => assertive
  • warning => assertive
  • status => polite

But this doesn't match the roles used in Drupal.theme.message()...

  • error => role="alert", implicitly aria-live="assertive"
  • warning => role="status", implicitly aria-live="polite"
  • status => role="status", implicitly aria-live="polite"

In Drupal.theme.tabbleDragChangedWarning(), we already have a warning (amber) message marked up with the alert role (assertive)...

      tableDragChangedWarning() {
        return `<div class="tabledrag-changed-warning messages messages--warning" role="alert">${Drupal.theme(
          'tableDragChangedMarker',
        )} ${Drupal.t('You have unsaved changes.')}</div>`;
      },

TODO: I suggest we treat "error" and "warning" as assertive/alert. Should be an easy patch update in this issue, to make these consistent.

andrewmacpherson’s picture

Question: does this API allow messages to be injected literally anywhere, or does it require a pre-arranged wrapper region? I can see we have such regions in the test module, but I'm not clear if the API requires it.

It relates to the headings problem. I think it will be better to recommend a heading for the region where messages are added, which is much less disruptive than one heading per message.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
45.42 KB
12.81 KB

#401.1 - fixed
#401.2 - sure, if you create the followup we can fix it after.

#402.1 - fixed

#403.1 - yes, you can create your own destination for messages and fill it with as many different message types that you want.

Status: Needs review » Needs work

The last submitted patch, 404: 77245-404.patch, failed testing. View results

phenaproxima’s picture

Status: Needs work » Needs review
FileSize
45.4 KB
1.01 KB

Let's see if this fixes it.

Status: Needs review » Needs work

The last submitted patch, 406: 77245-406.patch, failed testing. View results

dmsmidt’s picture

After reading the change record in https://www.drupal.org/node/3002643, I'm worried it will be a lot more difficult to fix #2876321: Update inline form errors summary after form validation with AJAX.
In that case there will be a server side rendered message showing the summary of all inline form errors. After fixing one of the errors, and if that form element is resubmitted via AJAX, the summary will be wrong (for example a required file). With the current approach it will be much more difficult to make it possible in a follow up to update server side rendered messages with this API without breaking BC. I know it is out of scope of this issue to mix SSR messages and CSR messages, but we should at least not make it too difficult.

phenaproxima’s picture

@dmsmidt, I don't understand why this would cause a server-side problem. Under normal circumstances, the fallback area is not rendered in a status_messages element unless the #include_fallback switch is specifically set to true (it's false by default). In other words, 99% of the time, the status_messages element's behavior is unchanged by this patch. Can you provide an example or code path demonstrating how this would break inline form errors?

dmsmidt’s picture

@phenaproxima, I hope I'm mistaken, but let me elaborate a bit.
I'm not saying this patch wil break any current behavior and certainly not interfere with server side rendering.
Initially my thought was that this client side API would be able to interact/modify the already (server side) rendered messages shown on a page. Because that is needed to change the inline form error summary. That summary is on the top of the page and is generated on the server side. Once a user fixes a problem in the form, and the error is resolved, we cannot use this current API approach to modify the summary via the client side. I was oké with this first to just get a basic API in and then in a follow up work on the interaction between SSR and CSR messages. I just read up on recent changes and see the JS API will use a completely separate fallback area. My fear is that this separation will make it more difficult to work towards a situation that the JS message API and the current system messages API will work together without BC breaks or lots of workarounds. Is this more clear?

phenaproxima’s picture

Status: Needs work » Needs review
FileSize
45.31 KB
1.87 KB

This should fix the tests.

phenaproxima’s picture

Issue tags: -Needs manual testing

@dmsmidt, I discussed your concerns with @alexpott. We agreed that this can still be committed as-is, because intermixing server-rendered messages and client-rendered messages is out of scope for this patch. True, BC makes this trickier, but we need to comply with the policy because Drupal 8. :) We can and probably should open a follow-up issue to investigate what it would take to "expose" server-rendered messages to the client-side API. But it's not really part of the work being done here, and doesn't need to block commit.

So, if someone will re-RTBC, I think this could potentially land soon.

Removing the "Needs manual testing" tag, because we have automated tests.

starshaped’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me! Setting to RTBC.

GrandmaGlassesRopeMan’s picture

This looks really good, 👍 from me.

I attached a small interdiff with some minor documentation changes, object to class. This probably shouldn't block this from getting in, but for correctness sake I wanted to add it.

GrandmaGlassesRopeMan’s picture

FileSize
821 bytes

So... yeah, here is the interdiff.

andrewmacpherson’s picture

Status: Reviewed & tested by the community » Needs work

The H2 headings have not been removed - I've asked for this several times already in previous comments, and it looks like I'm being ignored.

They are highly disruptive to the page structure, and inserting them dynamically seriously undermines any other efforts which authors have made to achieve a sensible heading structure on their page. The H2 injected here doesn't just apply to the error message - When a H2 heading is introduced, it acts as a heading for ALL content following it, until the next H1 or H2. So if a page makes sensible use of H3 to H6 (e.g. "billing details" and "shipping details" as H3's), those are effectively re-parented by an error message which doesn't apply to them.

This issue is NOT getting past the accessibility gate while those headings are still there.

Update (5 mins later): very sorry, I made a mistake, looking at the wrong diffs, and this comment was written in haste. The changes in #404 DO remove the injected H2-per-message. The remaining H2's in the patch are part of the pre-arranged message region.

The thing I blocked it on HAS been addressed. Other a11y concerns noted in earlier comments (a) can be left to some easy follow-ups, or (b) are harder problems relating to inconsistent support for aria-live regions in browsers, OS accessibility APIs, and AT.

andrewmacpherson’s picture

Status: Needs work » Reviewed & tested by the community

back to rtbc

GrandmaGlassesRopeMan’s picture

Issue summary: View changes
lauriii’s picture

Found unnecessary line change and extra whitespace which I have removed in this patch. I also applied the interdiff from #415. Just running the tests to make sure the tests are still passing since this change contains a minor change in the rendered output.

Also removing the needs accessibility review based on #416.

lauriii’s picture

Updating commit credit

  • lauriii committed 280995f on 8.7.x
    Issue #77245 by drpal, tedbow, nod_, phenaproxima, Wim Leers, googletorp...
lauriii’s picture

Status: Reviewed & tested by the community » Fixed

Thank you everyone who has worked on this issue during the last 12 years. 🙏 This issue was opened over 4 years before I created my Drupal.org account. 😳 Committed 280995f and pushed to 8.7.x. 🥳 Thanks! ✨

nod_’s picture

wow, so happy to see this land. Thanks!

dmsmidt’s picture

@phenaproxima thanks for taking my comment seriously and making a deliberative descision to move forward.

Great job everybody! I'm very interested in how this will be used in practice.

Status: Fixed » Closed (fixed)

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

xjm’s picture

Issue tags: +8.7.0 release notes

Since this fix required a change to our stable base themes, we'll list it in the release notes since it is a small BC break for themes.

joegl’s picture

https://www.drupal.org/project/drupal/issues/3053838

What's the proper thing to do now when there is an empty messages block in its own region? Why does it make sense to render an empty block in the first place? On upgrade a lot of our sites look screwy because the region is being rendered when there's nothing in it. From an HTML/CSS perspective this feels wrong.

Apologies if this is not the correct place to post. I came here from https://www.drupal.org/node/3002643

joegl’s picture

Trying to follow-up on this:

Change div for {{ messages }} to always be rendered and add a data-drupal-messages attribute for JavaScript to select.

Isn't this kind of anti-drupal? I don't understand why always rendering an empty div is seen as desirable, as we are currently having problems with entire regions being rendered with no content in them, because of the empty messages div (see linked issue). It seems inappropriate, but it could also be due to a bug I'm not aware of it (I plead ignorance here).

TerranRich’s picture

How do we utilize this functionality? Whenever I try to call new Drupal.Message() on an existing Drupal 8 site, it just throws an error about Drupal.Message not being a valid constructor. Is there something that needs to be enabled/configured?

Taiger’s picture

Issue tags: -JavaScript +JavaScript

@TerranRich you need to add the drupal.message library because it is not included by default.

For example:

// Existing page or form
$form['#attached']['library'][] = 'core/drupal.message';
// New
$build['#attached'] = ['library' => ['core/drupal.message']];

There many ways to add a library: https://www.drupal.org/docs/8/modules/libraries-api-8x/using-libraries-a...