braintree / braintree-web-drop-in

Braintree Drop-in for the web
MIT License
200 stars 126 forks source link

Support web components (shadow DOM encapsulation) #296

Closed ernsheong closed 1 year ago

ernsheong commented 7 years ago

General information

Issue description

https://github.com/braintree/braintree-web-drop-in/blob/master/src/dropin.js#L219 assumes that the container can be found in the global document scope.

For web components with shadow DOM encapsulation (https://developers.google.com/web/fundamentals/web-components/shadowdom), this does not work.

Some quick top of the head possible solutions:

  1. From the top, search shadowRoot of elements recursively (naive)
  2. Allow devs to pass target HTMLElement (shadowRoot) to the Braintree create function, replacing document
  3. ...

Possible workarounds:

  1. Apps can use an iframe.
  2. Devs can use Custom Elements without shadow DOM (but it has to be without shadow DOM all the way to the top in order to use document.querySelector, which is unlikely for Polymer framework)
  3. Use hosted fields integration (Update: looks like braintree-web also uses document.querySelector, no go)

There would also be styling issues because global CSS styling will not work. Everything needs to reside within the payment component shadow DOM. Once passed the shadowRoot element the dropin should be able to inject CSS, in theory.

In fact, it would make much more sense for the dropin itself to be a web-component, in the future. This provides lighter encapsulation vs. iframe.

lilaconlee commented 7 years ago

Awesome suggestion! This isn't currently on our roadmap, but we're definitely interested and have some ideas floating around about how to make this work. If we make any progress, we'll let you know here.

If other folks are interested in web component support, let us know by commenting or +1-ing and we can see about prioritizing the issue.

crookedneighbor commented 6 years ago

I did some exploration of this. The querySelector issues you brought up are one element of this, but not the main blocker for making this work in the shadow dom.

Hosted Fields (what Drop-in uses internally to make the card form) works by grabbing the sibling iframes off of the parent and then initializing them with a shared card form object. This allows the iframes to maintain a model for the validity of the card form.

When enclosed inside the shadow dom, Hosted Fields cannot access the sibling iframes, so it is unable to initialize them with a shared card form.

To support web components and the shadow dom without any weird hacks, we'd have to fundamentally change how Hosted Fields is implemented. Perhaps with a separate iframe that acts as the orchestrator that maintains the model of the card form and receives post messages from the individual fields to maintain the state of the model.

This is something we're interested in doing, but it will probably need to wait until the next major version update of braintree-web.

The PayPal portion of Drop-in uses checkout.js. Polymer support is being tracked here: https://github.com/paypal/paypal-checkout/issues/353

Using a bit of a hack, it is possible to use Drop-in with the shadow dom. Here's a simple example. The trick is to set it up outside of the shadow dom and then insert it into it: https://codepen.io/braintree/pen/VrYXYW

This user posted a code snippet for how to use Hosted Fields in a Polymer app using the slot element: https://github.com/braintree/braintree-web/issues/226#issuecomment-298020600 A similar method should be possible with Drop-in.

ernsheong commented 6 years ago

Thanks for the leads. I guess the non-hacky option that could work is to put the drop-in in a global top-level (non-shadow DOM) modal. This forces the user to enter credit card in a modal, which isn't ideal but it should work.

ernsheong commented 6 years ago

Happy to report that slotting from the top as demonstrated in https://github.com/braintree/braintree-web/issues/226#issuecomment-298020600 works very well for me. It's an acceptable workaround. To note that the syntax for Polymer 2 is:

<my-app>
  <div slot="braintree"></div>
</my-app>

Within <my-app>:

<my-container>
  <my-payment>
    <slot name="braintree"></slot>
  </my-payment>
</my-container>

Within <my-payment>:

<div>Checkout</div>
<slot></slot> <-- drop in UI will appear here!
crookedneighbor commented 6 years ago

Great, I'm going to close out this issue since there's nothing else actionable for us at the moment. Glad it's working for you.

bzitzow commented 5 years ago

Would love it if this could be re-opened as this is not a solution but a workaround. Happy to help if you have any questions about Web Components.

crookedneighbor commented 5 years ago

@bzitzow If you can provide a solution for the problem noted here: https://github.com/braintree/braintree-web-drop-in/issues/296#issuecomment-340479173, I'd be happy to look into this again

phips28 commented 4 years ago

any update on this issue? still no shadow dom support?

crookedneighbor commented 4 years ago

This is something we're actively looking into again now that the PayPal SDK we use to offer PayPal has been updated to support it.

kostasp commented 3 years ago

Happy to report that slotting from the top as demonstrated in braintree/braintree-web#226 (comment) works very well for me. It's an acceptable workaround. To note that the syntax for Polymer 2 is:

<my-app>
  <div slot="braintree"></div>
</my-app>

Within <my-app>:

<my-container>
  <my-payment>
    <slot name="braintree"></slot>
  </my-payment>
</my-container>

Within <my-payment>:

<div>Checkout</div>
<slot></slot> <-- drop in UI will appear here!

Hi There.

Additionally, it might help someone to mention that the usages of nested slots are the only way to fix this when the Element that needs to render the DropIn is also nested in other shadowo doms.

**Index.html**
**1st layer**

<app>
    <div id="loader" class="pageloader is-active"><span class="title">Loading.</span></div>
        <div slot="braintree-fields">
<!--            include nothing inside the drop-in div. do otherwise and payment braintree dropin fails.-->
            <div id="drop-in"></div>
        </div>
</ps-app>

**2nd layer** 

<secodlayer>
<slot name="braintree-fields" slot="braintree-fields"></slot>
</secodlayer>

**3rd layer** 

<thirdlayer>
<slot name="braintree-fields" slot="braintree-fields"></slot>
</thirdlayer>

etc. I lost about 6 hours messing around in order to get this right using nested slots.

letrthang commented 3 years ago

I have issue here, so I vote for Support web components (lit-element) .

issue: https://stackoverflow.com/questions/68374506/polymer-braintree-gateway-integration-javascript-reference-style-problem

armandodlvr commented 1 year ago

closing for inactivity. If you continue to encounter errors, please contact Support