PatternFly 5 Modal - #33
Conversation
|
wow! this is a great first start @dabeng. Can you do me a favor and go ahead and expand the iframe size in This is a really promising start. We will definitely need some tests (please see my last merged PR for notes on how to debug these locally with Karma/Phantom). Also, maybe a modal with a form like the one here would be nice? You may want to ask @dgutride and @dlabrecq for any advice here too. |
| * An instance of the element is created or upgraded | ||
| * We build up the complete DOM tree of web components | ||
| */ | ||
| constructor () { |
There was a problem hiding this comment.
We aren't quite here yet with v1 constructors. Can you use the v0 attachedCallback for now? Would like to change all these new components in a new PR for #27
|
Hi @priley86 , I have added unit testing and replaced custom elements v1 with v0. Please review agagin 😊 |
|
wow! this is awesome @dabeng !!! 😈 Thanks for making the v0 change. Looks good! I have one more additional request after reviewing a bit more. Can we pretty please with sugar on top change the I think it's fine to keep the Bootstrap template separate like you have done, we would just need to query this new I would suggest the same patterns for I know this is a little bit of refactor, but take your time. You're doing an awesome job with this already!! 👍 |
|
also - you may want to check your tests in build w/ Phantom too? Travis seems to be upset again. |
| */ | ||
| _afterShowModal () { | ||
| this._element.focus(); | ||
| this._element.dispatchEvent(new CustomEvent('modalShown', { 'detail': { 'relatedTarget': this._target }})); |
There was a problem hiding this comment.
can we change the event name to shown.bs.modal to match the Bootstrap API?
| } | ||
| } | ||
|
|
||
| this._element.dispatchEvent(new CustomEvent('modalHide', {})); |
There was a problem hiding this comment.
same thing here.. can we go with hidden.bs.modal instead?
| * @public | ||
| */ | ||
| show () { | ||
| this._element.dispatchEvent(new CustomEvent('modalShow', { 'detail': { 'relatedTarget': this._target }})); |
There was a problem hiding this comment.
Can we go with show.bs.modal?
There was a problem hiding this comment.
What about "show.pf.modal"?
There was a problem hiding this comment.
After all, bs is bootstrap keyword.
There was a problem hiding this comment.
You raise a great point. We have been using the same events as Bootstrap before (so we'd have to adopt this convention globally), but I'm not opposed to this.
I will raise a question on this to the mailing list this week. Feel free to pledge support for this...
There was a problem hiding this comment.
Hi @priley86 , "show.bs.modal" is the special event namespace of jquery style. Are you sure to introduce it into patternfly web components?
| * @private | ||
| */ | ||
| _hideModal () { | ||
| pfUtil.removeClass(document.body, 'modal-open'); |
|
After running "gulp test", all unit tests succeed. I don't know why travis failed. |
|
@dabeng, Travis will fail if any of these tasks fails: |
| 'src/**/*.spec.js' | ||
| 'src/**/*.spec.js', | ||
| 'dist/css/patternfly.css', | ||
| 'dist/css/patternfly-webcomponents.css' |
There was a problem hiding this comment.
Please do not include CSS files here. PhantomJS goes out of memory when you try to load CSS and WebFonts in Travis.
There was a problem hiding this comment.
Where should I place these two css file references?
There was a problem hiding this comment.
Do not include them at all. You probably need CSS for transitions, instead of depending upon the events, manually call the handlers
|
Hi @priley86 , I thought about custom element pf-modal-header, but I found that it violated the aims of Custom Element -- reusability. Except for being included in pf-modal, are there other scenarios where pf-modal-header can apply in? In fact, what we need is just a container which contains custom content for header(body or footer). We don't even care what container element. |
| pfUtil.addClass(this._mask, 'in'); | ||
| pfUtil.reflow(this._element); | ||
| pfUtil.addClass(this._element, 'in'); | ||
| pfUtil.once(this._dialog, 'transitionend', this._afterShowModal, this); |
There was a problem hiding this comment.
You will need to manually call afterShowModal in your tests.
There was a problem hiding this comment.
Then removing your CSS files will probably make the travis errors go away.
To exactly simulate Travis, make an ubuntu 14.04 lxc container with 1.5gb memory allocated to it and run the tests inside it 😈
|
@dabeng I think the main idea with For too long we have encouraged "div" soup and this project aims to avoid that. Ideally, we can still support the "div" scenarios by using common attributes or properties as selectors, but what I am suggesting is that Does this sort of make more sense why I am suggesting this now? I see your point about reusability of child CEs, however I much prefer the "sugar" of having a more expressive syntax (and typically these children CEs are pretty simple to implement). Any more thoughts here? |
| * @private | ||
| */ | ||
| _keydownHandler (e) { | ||
| let isEscape = (e.key && e.key === 'Escape') || (e.keyIdentifier && e.keyIdentifier === 'U+001B') || (e.keyCode && e.keyCode === 27); |
There was a problem hiding this comment.
Need to support IE and Edge. We will need to test if keyCode exists, for example:
let keycode = event.keyCode ? event.keyCode : event.which;
There was a problem hiding this comment.
I have tested this PR on IE10 or Edge. It worked well.
Besides, there are too many code snippets of patternfly-webcomponents waiting for cross-browser checkout. keydown event of "Escape" key is just the tip of the iceberg. That's why I wanna introduce jquery as a common module at the beginning.
There was a problem hiding this comment.
jQuery is just a Module providing us with the syntactic sugar of basic DOM operation. It's essentially different with angular, react, vuejs. It's not essentially different with vanilla javascript if we just make use of its core DOM functionalities of jquery.
There was a problem hiding this comment.
There is a reason that Bootstrap 4 doesn't give up jQuery
There was a problem hiding this comment.
There are cases where event.keyCode does not exist.
There was a problem hiding this comment.
And many Angular developers won't use the components because of jQuery -- it's a non-starter for some people.
There was a problem hiding this comment.
Hi @dabeng, i think it is important to keep in mind that our core two target frameworks initially will be the Angular JS and React JS frameworks. These are still the most common frameworks in use at Red Hat today. What @dlabrecq is saying is certainly true about many of these communities avoiding the use of Jquery. It adds additional API overhead (where frameworks often come in solving the same problems) and typically is not as clean as fit as framework native components or now, web components. You might have seen this link on our PF Showcase page about how to avoid jquery:
http://codeblog.cz/vanilla/overview.html#prerequisities
With the amount of existing components implementing complex behaviors with Jquery (such as our treeview), we obviously have a long ways to go to remove it and reimplement these components. But we've learned a lot from some of the communites below on how to do this...
http://www.vanillalist.com/
http://thednp.github.io/bootstrap.native/
There was a problem hiding this comment.
Hi @dlabrecq , I have added event.which testing.
| <hr> | ||
|
|
||
| <button class="btn btn-default" id="btn-toggle-modal">Launch demo modal</button> | ||
| <pf-modal targetSelector="#btn-toggle-modal" backdrop keyboard> |
There was a problem hiding this comment.
I'm concerned that developers must provide all this raw HTML themselves. This particular HTML is basically a cut and paste from the PF core test page. It appears that the component is used mainly to open/close the modal?
I believe the component should provide the header, close button, etc. As an example, consider the modal component from Angular Patternfly. All this HTML is output by the component -- more complicated layouts are defined using transclusion. Thus, the APF modal example is much more simplified and looks more like this:
<div pf-about-modal is-open="isOpen" on-close="onClose()"
additional-info="additionalInfo" product-info="productInfo" title="title"
copyright="copyright" img-alt="imgAlt" img-src="proxy.php?url=https%3A%2F%2Fgithub.com%2Fpatternfly-webcomponents%2Fpatternfly-webcomponents%2Fpull%2FimgSrc"></div>
There was a problem hiding this comment.
I think "provide all this raw HTML themselves" is just what users want most from pf-modal because children custom elements(pf-modal-header, pf-modal-body, pf-modal-footer) provide users with the maximal free space to construct their own internal structure of pf-modal 😊
There was a problem hiding this comment.
I disagree. The Angular Patternfly modal was created because developers don't want to have to find an example and provide the raw HTML themselves. Otherwise, there is little value in creating a component.
There was a problem hiding this comment.
i too was thinking about this with something such as a "USA State dropdown" or a "USA Zip code select" component? These types of common UI components may become a possibility with web components...
There was a problem hiding this comment.
I think the value of web components should not include completing stuff outside its duty. e.g. custom content or layout.
There was a problem hiding this comment.
<pf-modal>
<pf-modal-header>custom content</pf-modal-header>
<pf-modal-body>custom content</pf-modal-body>
<pf-modal-footer>custom content</pf-modal-footer>
<pf-modal>There was a problem hiding this comment.
let modal = new PfModal({
headerInfo: {…},
bodyInfo: {…},
footerInfo: {…},
otherOptions: {…}
});
document.body.appendChild(modal);
There was a problem hiding this comment.
There gotta be a better one.
| this._element.setAttribute('tabindex', -1); | ||
| let headerPart = this._element.querySelector('[modalPart="header"]').innerHTML; | ||
| let bodyPart = this._element.querySelector('[modalPart="body"]').innerHTML; | ||
| let footerPart = this._element.querySelector('[modalPart="footer"]').innerHTML; |
There was a problem hiding this comment.
Just a nit, but vars should appear top of the function.
| * @public | ||
| */ | ||
| show () { | ||
| this._element.dispatchEvent(new CustomEvent('show.bs.modal', { 'detail': { 'relatedTarget': this._target }})); |
There was a problem hiding this comment.
We should discuss, but I don't necessarily agree we should use Bootstrap event names? For example, we generate a tooltip event, but we don't use shown.bs.tooltip as the event name.
We have many other events that don't necessarily match up with Bootstrap. I'm thinking we need to decide on common naming convention for the sake of consistency -- it's getting messy.
Here a just a few of our existing event names:
show.bs.dropdown/hide.bs.dropdown
initialized
itemClicked
tooltipOpened/tooltipClosed
thresholdSet
tabChanged
There was a problem hiding this comment.
+1 for standardizing these. Thus the question 😉
| */ | ||
| _afterShowModal () { | ||
| this._element.focus(); | ||
| this._element.dispatchEvent(new CustomEvent('shown.bs.modal', { 'detail': { 'relatedTarget': this._target }})); |
There was a problem hiding this comment.
We should document exactly what events, names, and properties are generated. If not for this private function, at least for the public ones. And perhaps also at the top of the component's JsDoc to make it clear what public events are available?
|
Hi @priley86 , I have added children CEs(pf-modal-header, pf-modal-body and pf-modal-footer) |
|
thanks @dabeng , i think this looks good, but will wait for others to weigh in on this. Have we decided on an event naming convention @bleathem ? do we prefer created #35 for this... |
|
@priley86 @dlabrecq @bleathem , there is one other point I would like to discuss with you. Some web components are suitable for declaration with html, like pf-tabs. Some are suitable for instantiation with javascript. And others can be created by any of two above ways. Here, I think it's a better way to create pf-modal with declaration pattern because the way can facilitate users to construct the internal content of pf-modal's every sections. |
|
There was a discussion during the last UXD/UI Developer meeting and believe the consensus is that we want to provide a pattern's default behavior first, but allow developers to override if necessary. For example, the pf-modal-header component can provide the close button and title layout by default. If a developer wishes to override that, they can replace it using transclusion via the body of pf-modal-header. Likewise, the pf-modal-footer component should provide the save and cancel buttons by default and then allow developers to override if necessary. The pf-modal-body component is fine as is. |
|
Cool. This consensus is a great improvement. New commit is coming 😊 |
| <html> | ||
| <head> | ||
| <title>pf-tooltip example</title> | ||
| <link rel="stylesheet" href="/dist/css/patternfly.css"> |
There was a problem hiding this comment.
you'll want to make all of these dist/css/ paths relative after the recent update. i.e. ../../dist/css/patternfly.css. Check the other components for an example on this...
| <!doctype html> | ||
| <html> | ||
| <head> | ||
| <title>pf-tooltip example</title> |
There was a problem hiding this comment.
should be: pf-modal example
There was a problem hiding this comment.
Sorry for my carelessness
There was a problem hiding this comment.
Test page of pf-modal -- https://rawgit.com/dabeng/patternfly-webcomponents/modal-dist/src/pf-modal/index.html
| * @shown.bs.modal It's fired when the modal has been made visible to the user (will wait for CSS transitions to complete). If caused by a click, | ||
| * the clicked element is available as the relatedTarget property of the event.detail | ||
| * @hide.bs.modal It's fired immediately when the hide instance method has been called | ||
| * @hidden.bs.modal It's fired when the modal has finished being hidden from the user (will wait for CSS transitions to complete) |
There was a problem hiding this comment.
We need to change these events from Bootstrap specific to the new convention the community agreed upon.
| <body> | ||
| <div class="container"> | ||
| <div class="page-header"> | ||
| <h1>Modals</h1> |
There was a problem hiding this comment.
Sorry @dlabrecq , I can't understand this requirement. Could you explain it in more details?
There was a problem hiding this comment.
Please disregard that comment. I thought I was looking at the pf-modal-header. It's difficult to read when GitHub injects all these comments between lines of code.
| </div> | ||
| </form> | ||
| </pf-modal-body> | ||
| <pf-modal-footer></pf-modal-footer> |
There was a problem hiding this comment.
Are there events generated for the close (in header), save, and cancel buttons? It would be good if this example showed how to capture a save event.
If you look at our Angular Patternfly examples for reference, that uses a textarea to output/confirm which event/button was just clicked.
There was a problem hiding this comment.
@dlabrecq , I can't find your mentioned textarea on http://www.patternfly.org/angular-patternfly/#/api/patternfly.modals.directive:pfAboutModal. Could you give me the right link?
There was a problem hiding this comment.
I wasn't specifically speaking about the modal example, but something more like listView, toolbar, etc. Click on the buttons in the example below and you'll see the interaction.
http://www.patternfly.org/angular-patternfly/#/api/patternfly.views.directive:pfListView
| expect(callback).toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Now that we're providing default cancel, save, and close buttons, we should test the events here. For example, we can test if clicking the close button hides the modal. Users should be able to receive an event for save, cancel, etc.
|
@priley86 , Test page of pf-modal -- https://rawgit.com/dabeng/patternfly-webcomponents/modal-dist/src/pf-modal/index.html |
|
Thanks @dabeng. The modal looks to work, but it seems that your branch needs rebasing? If I go here for instance: It does not load the same files as it does in |
|
I'll check this issue ASAP after I append enough unit testing. |
|
@priley86 , Is pf.modal.show final custom event namespace convention? |
|
@dabeng wow! great job...the tests are very helpful and it looks like you've captured all of the events. |
|
@dlabrecq any more thoughts on these updated unit tests? |
|
One other thing here... after looking at I'm not sure if it matters or not with your CE though...just want to be sure we can support the case of something like this (rendered in a framework): Just want to be sure that when the pf-modal-body content changes via the framework binding (which occurs outside the CE's lifecycle) the CE content updates. If it's not updating the content, I'd suggest allowing the outside framework shim to fire Can we just verify this? You could also just write some html/js to test it like: js: Does the content of the |
|
@priley86 , the following code snippets can change the content of pf-modal-body. No problem. var pfModal = document.getElementById('pf-modal');
document. getElementById('#salutation').textContent = 'search of web components';Additionally, according to @dlabrecq's recommendation:
In pf-modal, we just allow uses to fire handleContentChanged event by calling setInnerHtml() method. |
|
@dabeng do you mind creating a plunker demonstrating this (like I have done with This will also help us out write any shims that might be necessary here. |
|
@priley86 |
|
thanks very much @dabeng for providing this... these templates are great and we can certainly use them in the future! For React, I am really hoping to support something like this: This gives an example of how content can change via props/bindings. This will definitely happen in real app scenarios where you are passing dynamic content via props/state into your modal (and it will have to update). I guess my hope is that we support this kind of behavior via Mutation Observer or providing some event to tell the WC to reload itself...we can mask this in the React shim though... Thanks again for researching, this is great!!! |
|
in looking at this again, @dabeng, another option may be to "shim" the modal-header, modal-body, and modal-footer and just write the content as |
|
Sorry @priley86 , I couldn't get your points, can you explain in more detail?
|
|
As for pf-modal demo under anguarl 2 context. I think it's a good timing when we replace custom elements v0 with v1. |
|
Sorry @dabeng, it is hard to give a full explanation without a complete example... I think the simple use case is still valid (and a good first step to test). Can you make this WC support the codepen I forked? The issue is we need to make the WC respond to changes in state/props being passed down into the template of the modal. When I click "Change field one", I'm updating state (and props coming down), but the content label does not change. The content in the modal body will be assumed dynamic by the consumer (angular or react). The other idea I'm having is to create React shims (which internally render Long story short, if you can fix the use case for the codepen I've added (and reload the content either via Mutation Observer or an event), then that use case should work as well...(we'd just replicate the solution)... Hope this helps... |
|
Hi @priley86 , I have to use dangerousSetInnerHTML() method in order to render HTML string as real HTML in JSX. Maybe there are other better solutions. Please review again Demo As for "create React shims", @dlabrecq has denied the use of mutation
So I moved to use setInnerHTML() method and dispatch pf-modal.changed event. |
|
@dabeng have you seen @Agathver's latest plnkr? My vote is to do something similar here. This is part of the issue with setting nested content as a string. That is not the typical scenario by any React / Angular consumer I don't think. I.e. most React components I work with wrap nested components as Can you take a look at this one and see what you think? We are still having an open conversation on this, so appreciate any feedback... |
|
Hi @priley86 , you mean I should replace setInnerHTML() with mutation observer? |
|
@priley86 For the last few days, I tried introduced MutationObserver object into the pf-modal. However, I found that it's not a good idea to build up web components based on mutation observer mechanism because it will result in a huge of redundant and low-performance code snippets as well as poor readability and maintainability. Specially, it's a nightmare for complicated and nested web component to work with mutations. |
|
@dabeng The entire Polymer project is heavily based on MutationObservers, and they have been stabilized for quite a time, so it can be safe to assume the performance issues of MutationObservers have been alleviated by now. Sure there is some performance hits (~3%), but the convenience it gives, is worth it, and in many cases it won't be the performance bottleneck either. |
|
@dabeng @Agathver my vote is to try to abstract the Observers away as best we can for now (can we possibly write a helper to contain that logic?). Either way, same performance issue is there for Angular...and I'm not sure of another way to fire the also, kudos to @cdcabrera for the abstraction idea...could this go in Long term, I think we look to |
|
@priley86 I have made the pf-modal respond to changes in state/props being passed down into the template of the modal. Could you the demos on the top of this page again? |
|
@dabeng great job... Really like the examples across Angular/React you did! These are really useful for future components and show how flexible web components can be... PF-modal is definitely a more complex component which we can all learn from. I think the examples adequately show conditional rendering and you did a great job w/ this. The API is a bit confusing to me from the examples, so I definitely think writing a really doc page for this will be useful (clearly denoting different WC attributes/intended usages). My previous plnkr appears to be updating correctly now, but styling is off and looks like there is some event handler error in console. Any idea? It doesn't appear we have a MutationObserver now, but the @cdcabrera @dlabrecq @bleathem any more thoughts before we move forward here? I think next step is to clearly document the API...would <3 a PR here documenting the PF Modal next. Let's try to keep the examples clear and concise, and use case focused now that we know it supports the complex patterns... |
|
@priley86 I added assistant elements(pf-modal-dialog and pf-modal-content), please see this demo |
|
awesome @dabeng ! the new codepen looks great. thanks so much... Let me know once you have some API docs ready for showcase site or if you want any help ;) I will definitely review those w/ you. |
|
@priley86 ,I have upgraded pf-modal to v1, please review Demos again. |
|
looks good to me @dabeng ... the v1 examples appear to be working correctly in everything other than Firefox for me (as you noted). These also appear to be different issues than we ever had w/ v0. Is there an upstream webcomponent polyfill issue we can track (thanks for finding the Firefox bug)? You're definitely the farthest along w/ v1 research so thanks a lot for pushing this forward... Any short term workarounds for this? The only other thing I noted was a minor difference in the Cancel/Save buttons and our PF Core test page. The button heights are different for some reason. Can you double check that we don't need to extend this modal with any new styles? https://rawgit.com/patternfly/patternfly/master-dist/dist/tests/modals.html |
|
@dabeng I guess |

@bleathem @priley86 @dlabrecq Would you like to review this PR again?
Demo on dist branch
Using pf-modal in ReactJS
Using pf-modal in Angular 1.6
Using pf-modal in Angular 2
FYI, currently Firefox doesn't support attributeChange callback of Custom Elements v1. Please refer to https://bugzilla.mozilla.org/show_bug.cgi?id=1334051 for more details.