richnologies / ngx-stripe

Angular 6+ wrapper for StripeJS
MIT License
219 stars 77 forks source link

[BUG] The 'containerClass' attribute doesn't work for component level css files due to missing '_ngcontent' tokenizations #180

Closed KyleLehtinenDev closed 1 year ago

KyleLehtinenDev commented 2 years ago

Describe the bug When using the 'containerClass' attribute on ngx-stripe elements, the specified class does get added to the wrapping div of the Stripe element as expected, however if the styles for the target element are defined in an individual component's css file, the css will be transformed to include Angular's '_ngcontent' tokenizations, and because these tokenizations aren't making it to the wrapping div element the style fail to apply to the container element.

Note, this occurs regardless of component encapsulation settings.

To Reproduce Steps to reproduce the behavior:

  1. Create a component with a ngx-stripe-card-number which includes the 'containerClass' attribute with a class that defines style in the component's CSS file:

    
    // component.html
    <label class="subtitle1" for="card-number-element">
    Credit card information
    <div style="margin-top: 14px"></div>
    <ngx-stripe-card-number
      #cardNumber
      containerClass="stripe-element-input"
      disabled="isProcessingPayment"
      [options]="cardNumberOptions"
      (change)="handleStripeChange($event)"
    ></ngx-stripe-card-number>
    </label>
    
    // component.scss
    .stripe-element-input {
    border: 1px solid rgba(73, 80, 87, 0.23);
    border-radius: 4px;
    padding: 0 0.857em;
    }

.stripe-element-input:hover { outline: 2px solid #163f6c; }


2. Build and run the component, observe in developer tools that the specified container class is applied to the wrapping div, but the styles defined for that class are not shown or found:
![image](https://user-images.githubusercontent.com/22458332/192605015-46adb060-9723-4b5e-b387-95e8d9f7a033.png)
![image](https://user-images.githubusercontent.com/22458332/192605912-2c346b3a-3a1c-4705-8fb1-f0610a8f7025.png)

Note also the lack of `_ngcontent` tokenization on the wrapping element. If we look at the css definitions in the webpack chunks we can see the defined css classes gain a `[_ngcontent]` token:
<img width="693" alt="image" src="https://user-images.githubusercontent.com/22458332/192605568-a833f5a8-0efb-4c73-8d57-755d921d6be1.png">

3. Move the container class style definition to the root `/styles.scss` file in your angular project, which should bypass tokenization as it's not part of a component's style, and rebuild/run.

4. The styles are now applied to the wrapping element.
![Screen Shot 2022-09-27 at 2 23 06 PM](https://user-images.githubusercontent.com/22458332/192606469-5299ce27-12c6-4756-826a-5d5a5f8a7d3e.png)
![image](https://user-images.githubusercontent.com/22458332/192606184-1fee8fee-9fe4-497f-8776-b5f17a939b2a.png)

**Expected behavior**
The container class should account for angular build time tokenizations so defined classes at the component level can be applied.

**Screenshots**
See above.

**Desktop (please complete the following information):**
 - OS: macOS 12.5.1
 - Browser chrome
 - Version 105.0.5195.125
richnologies commented 2 years ago

Hi @KyleLehtinenDev, thanks for the very well document issue.

Can you please share with me the versions you're using of @angular/core, @stripe/stripe-js and ngx-stripe, in case there is a problem with some specific version.

Also, can you share with me what's the value of cardNumberOptions so I can see what options are you passing in case the conflict in an unexpected way?

Thanks in advance

R

KyleLehtinenDev commented 2 years ago

@richnologies you're welcome, thanks for your work on this module 😄

Your requested details: "@angular/core": "^14.0.6", "@stripe/stripe-js": "^1.32.0", "ngx-stripe": "^14.1.0",

baseStyle: StripeElementStyleVariant = {
    fontFamily: 'Lato, sans-serif',
    fontSize: '16px',
    fontWeight: '400',
    padding: '50px',
    lineHeight: '3',
    color: '#242F3E',
    letterSpacing: '0.15px',
  };

  cardNumberOptions: StripeCardNumberElementOptions = {
    style: {
      base: this.baseStyle,
      invalid: {
        color: 'red',
      },
    },
    showIcon: true,
    placeholder: 'Credit card number',
  };
richnologies commented 2 years ago

Hello again @KyleLehtinenDev, thanks again for your very detailed answer.

I've been checking your problem I was able to replicate it. What I realice a bit too late is that this is the Angular expected behaviour. By default Angular components styles are private and they're not suppose to apply to child elements.

What you call _ngcontent tokenization is actually the implementation of the Angular view encapsulation and is a private API, not meant to be use or modified by library authors.

https://angular.io/guide/component-styles#component-styling-best-practices

This leaves us with three alternatives:

  1. Using the styles.scss to set global styles as you suggested in point 3
  2. You can also change the encapsulation of your component to None
  3. You can use the ::ng-deep pseudoselector that helps you do exactly what you want. Of the three options this is by far the more "private" as this styles will only apply to the component and it's childs. It won't apply to siblings or ancestors.

You can change this snippet you've shared:

.stripe-element-input {
  border: 1px solid rgba(73, 80, 87, 0.23);
  border-radius: 4px;
  padding: 0 0.857em;
}

.stripe-element-input:hover {
  outline: 2px solid #163f6c;
}

to this:

::ng-deep .stripe-element-input {
  border: 1px solid rgba(73, 80, 87, 0.23);
  border-radius: 4px;
  padding: 0 0.857em;
}

::ng-deep .stripe-element-input:hover {
  outline: 2px solid #163f6c;
}

and it should work fine.

Here is a trivial example, but check how without the ::ng-deep pseudoselector the h1 element is not red

https://stackblitz.com/edit/angular-ivy-s2hqmd?file=src%2Fapp%2Fapp.component.ts&file=src%2Fapp%2Fhello.component.ts

Hope this helps you

I leave the ticket open in case you have more questions

Kind regards

R

richnologies commented 1 year ago

I'm closing this due to inactivity. If you have any other question feel free to re-open it or create a new issue