Problem/Motivation
#2853208: [META] Determine best method ensure consistent theming of Off Canvas Tray explains the need to have the Off Canvas dialog look consistent when used in "admin" mode
It is very difficult to do this with a CSS reset but this is being explored in #2826722: Add a 'fence' around settings tray with aggressive CSS reset.
Another solution would be to use an iFrame. An iFrame could totally avoid CSS bleeding in from the sites default theme.
But it could also introduce other problems:
- Accessibility: what is the state of iFrame and Accessibility?
- Will not be able to use the regular method for dialog links. It doesn't make sense to load the iFrame element via AJAX because we just need the href for the link src attribute of the iframe element. Also even though there is dialog.js no where else in core uses the dialog system expect with AJAX dialogs(is this true??)
- Responsiveness
Proposed resolution
Use iFrame for dialog contents
Remaining tasks
Explore how an iFrame could be used.
Figure out if it is good idea.
User interface changes
API changes
Dialog link would not use class 'use-ajax'
No need for \Drupal\outside_in\Render\MainContent\OffCanvasRender
\Drupal\outside_in\Ajax\OpenOffCanvasDialogCommand
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#3 | 2853222-3-do-not-test.patch | 18.2 KB | tedbow |
#3 | interdiff-2-3.txt | 9.49 KB | tedbow |
#2 | 2853222-2-do-not-test.patch.patch | 12.59 KB | tedbow |
Comments
Comment #2
tedbowOK here is the first try at it.
I marked as do not test because I haven't updated the tests. But it does work, roughly
Comment #3
tedbowSome more progress finding some tricking things. Will self review when uploaded.
Comment #4
tedbowAny links that aren't explicitly targeting the _parent frame need to be appended to make sure they use the correct format.
This uses OffCanvasRender. Basically this we only want to show the main content here. Also we need to remove the toolbar when we are inside the iframe.
Also we don't want any links to tray to use the iframe nested inside the original iframe.
If we are already in the iframe don't attach event to handle opening links in the iframe. Don't want nested iframes.
This will make sure that the title of the dialog matches the title of the document inside the iframe.
There is a little delay in the title showing up which is not ideal.
@todo Need to figure out on server-side how to get rid of "* | SITE NAME" pattern.
In the BlockEntityOffCanvasForm.php we have explicitly designate that the form should submit to the _parent frame. This is the default behaviour if we don't use the iframe.
Should each form that will appear in the iframe have to set this? Or
Can we handle this in \Drupal\outside_in\Render\MainContent\OffCanvasRender::renderResponse and somehow search through the whole content array and do this when #type = form?
Are there any case where we would want a form to submit to the iframe?
Adds library that keeps adds correct format.
Adds a class to designate we are in the iframe. Used for JS now but could be used for styling.
Should this be an attribute?
Just updating offcanvas_test module to use iframe. Haven't actually fixed tests yet :(
So some new complications to think about!
Comment #5
nod_I haven't looked to closely at the patch but from the start I'm not for an iframe solution for a couple of reasons:
Purely on the performance aspect that's a no-go for me.
The overlay was one thing, but at least it was a whole page where no direct interactions with the opened page was supposed to happen, this is not the case here. If you have an ajax call on the main page, attachBehaviors will not be called inside the iframe. So you need to initialize things twice or introduce something new somewhere. If a contrib module makes content appear in the off canvas thing, and expect the content to be quick-editable, what happens? I don't want to have attachbehavior look for iframes or be monkey-patched by the outside-in module to do it.
Comment #6
tedbow@nod_ thanks for the feedback from your experience with Overlay. I am inclined to agree with you. I just through Overlay's JS, WOW
I think in general if we stick with the regular dialog system we are in for less surprises when we try to use this with other parts of core and contrib.
I think if there anyway we could another way to work we should do. Probably something along the lines of #2826722: Add a 'fence' around settings tray with aggressive CSS reset.
Comment #7
tkoleary CreditAttribution: tkoleary at Acquia commented@nod
Yes, but that is simply a description of the nature of this challenge which is 'how do we achieve administrative interactions that directly manipulate rendered content on the page in a fluid and intuitive way'.
Overlay never did anything like that. It seems obvious to me that a solution that does would be more complex and would require more code. Remember though that this is a performance hit that is *only* incurred by an administrator and not by a site visitor.
The trade off of having a much more direct path to editing almost everything in a site with immediate preview is well worth that cost IMO.
Having said all that, if you have up your sleeve a clever way to achieve the same goal without iFrame I would be very eager to hear it. :)
Comment #8
nod_we do have control over the css we could prefix all css rules from a specific frontend-admin library with some id at least that'll boost the rules specificity. Or add !important to everything during preprocess
Comment #9
Wim LeersThat's exactly the CSS nightmare that the iframe-based solution would avoid :)
#5:
window.postMessage()
IIRC.To be clear: I'd rather not use iframes either. But given that we can't use Shadow DOM, I don't see how else we can arrive at a sane, workable solution, that doesn't require every single bit of CSS to be duplicated, hence making Off-Canvas Tray-specific CSS an absolute nightmare. That's the only reason I even proposed this.
Comment #10
tkoleary CreditAttribution: tkoleary at Acquia commented@Nod
There is another benefit of iFrame that has not been mentioned, Without iFrame, special CSS needs to be written for content in the tray to make it behave like it's in a narrow viewport. With iFrame the narrow viewport media queries for mobile also apply to the tray (when it's used at it's default narrow width). It essentially gives us the equivalent of an element query.
Comment #11
tedbowre #5.3 and @Wim Leers' response
I think eventually though we will want JS in the tray to altering DOM elements in the main page. The original Outside In designs I think provide optimistic feedback editing items in the tray. For example editing the Branding block and updating the site name. Ultimately it would be nice if the title on the page updated as you typed. We aren't there yet but probably we will want be able to do things like that.
To me it seems like it will be harder to do such things if the elements aren't in the same frame in the same DOM.
Comment #12
tkoleary CreditAttribution: tkoleary at Acquia commentedOne other option we have not suggested yet AFAICR is putting the tray styling in the javascript and injecting it inline, which will always win out in specificity.
Comment #13
Wim Leers#11:
But you can still have that. Ensure you have different JS in the main frame and in the Settings Tray iframe, and have them communicate via
window.postMessage()
. That probably even results in a cleaner separation of concerns.EDIT: or I guess https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API.
#12: How are you going to gather the appropriate CSS to inject? You'd need to parse CSS, and run every single CSS selector through JS, scoped to the Settings Tray root element, and then generate
style
attributes for each selector's style rules.Comment #14
tkoleary CreditAttribution: tkoleary at Acquia commentedYeah, I was thinking of some kind of pre-render but @mattgrill explained the huge pitfalls of that.
Comment #15
GrandmaGlassesRopeManSome thoughts,
#5
Looking at an obviously contrived example, loading the 'Main Navigation' edit form in the tray executes another 276 requests with aggregation turned off. I don't think we are going to want to re-render an entire form and all the weight that comes with it.
U+1F44D
#5, #9, #11
I think @tedbow is correct. At some point in the future we'd like to have optimistic feedback that can escape the confines of the iframe. Even if we used
window.postMessage()
, we would still want to provide a consistent interface that other developers could take advantage of. Additionally,window.postMessage()
is only supported starting with IE8 which may or may not be of concern.However could we do something like the following.
I think this allows us to keep the form encapsulated in a frame, but we can also just load (via ajax) the form html, preventing the duplicate loading of scripts.
Comment #16
Wim LeersReproduced. But you forgot to mention they're all served from the browser's cache!
How would an iframe prevent that?
What would that consistent interface look like? I think it's fair to guess that it'd be an event. And for that even to be consistent, it'd have to tell the Settings Tray/Outside In to trigger an event, so that all Outside In events are handled the same. At which point… Outside In might as well be using
window.postMessage()
inside the iframe, that message would be received inside the main frame and would trigger an event there.So, I really don't see what the problem is with that.
That's not a concern.
So: I'm not an expert in this, nor am I fan of iframes. But I've only seen pretty thin arguments against iframes so far.
P.S.: in testing that, I noticed that
\Drupal\outside_in\Block\BlockEntityOffCanvasForm::form()
is called three times when opening a menu block in the Settings Tray! That's a major bug right there, I'm afraid…Comment #17
tkoleary CreditAttribution: tkoleary at Acquia commented@cosmicdreams suggested the alternative of using webcomponents.js to polyfill shadow dom—apart from drawbacks of yet another javascript library others already voiced—In looking through the documentation for webcomponent.js I discovered that it cannot effectively polyfill all of the CSS properties of web components without resorting to, you guessed it, an iFrame!
See https://github.com/webcomponents/webcomponentsjs/blob/master/src/ShadowC...
Comment #18
cosmicdreams CreditAttribution: cosmicdreams commentedHi gang: thanks for bringing this issue to my attention here @tkleary.
I just wanted to update everyone on the progress that has gone into web components in case you were not aware. About a year or two ago, the major browser vendors starting meeting regular to discuss how to properly implement web components in a way that all browsers can be comfortable with. What was produced from those discussions is V1 of the four main web standards that comprise this Web Components concept (Templates, Custom Elements, Shadow Dom, and HTML Imports).
Natively, most browsers support templates (I'm looking at you IE 11). Support for Custom Elements and Shadow Dom is either underway for most browsers to on the list of things to do / considering (yes even for IE Edge). I think it's a good bet to expect that native support will come eventually. (I'm being optimistic).
Until then a polyfill does exist. You may have seen it before : https://github.com/WebComponents/webcomponentsjs/tree/v1
The link above points to the matured version of this set of polyfills. Whearas before, the polyfill was a pretty large file that was loaded for all the browsers, they've now created a tiny js file to detect what level of support for Web Components your browser has and then load the appropriate polyfill. As a result: Complete support of the standards can be enabled through the download of a payload varying between < 7 kb -> ~ 30kb (referencing minified / compressed file sizes). That get the library size into the ballpark of jQuery. For a library, that may not be needed for anonymous users.
I'll report back soon. But I'm also working with the folks who make the ui_patterns module to introduce automatic discovery of these 3rd party components so that your Drupal site can have smart ways to populating them with data.
Fundamentally though, I really think the Shadom Dom support that Web Components provide is specifically designed to solve the problem of allowing these Admin elements to stand alone without having outside influence bleed in.
Comment #19
tkoleary CreditAttribution: tkoleary at Acquia commented@cosmicdreams
I personally am very excited about these advances, but as others have said better than me, it seems like core experimental is not the place for this just yet.
I am going to really dig in on this and start testing it out in other more bleeding edge experiments I'm working on though, and I'd really like to talk to you in more depth about it if you're going to be in Baltimore.
Comment #20
tedbowMarking this as fixed because we explored it.
CSS reset seems to be the way to go. #2826722: Add a 'fence' around settings tray with aggressive CSS reset.
See discussion
here, #2826722, and #2853208: [META] Determine best method ensure consistent theming of Off Canvas Tray
Comment #22
tedbowChanging to new settings_tray.module component. @drpal thanks for script help! :)