mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-11 10:10:25 +01:00
[PM-11657] Stripe + Browser Refresh Styling (#10978)
* add check for `ExtensionRefresh` in StripeService - Stripe components need new styles to match the new CL components * add global styles for Stripe components - Matches closer to the browser refresh components * add browser refresh component details to Stripe JS initialization * add component to match the display of the new component library that shows only when the `ExtensionRefresh` flag is enabled * update both payment components to use payment label component - This styling of the label is separate from the `AC2476_DeprecateStripeSourcesAPI` flag * update security code copy * change layout of the trial component to account for new CL components * absolutely position label to remove extra spacing around the label * remove unneeded logic
This commit is contained in:
parent
b6cde7e3ef
commit
70fbcf2a10
@ -1,6 +1,8 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { BankAccount } from "@bitwarden/common/billing/models/domain";
|
import { BankAccount } from "@bitwarden/common/billing/models/domain";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
|
||||||
import { BillingServicesModule } from "./billing-services.module";
|
import { BillingServicesModule } from "./billing-services.module";
|
||||||
@ -15,7 +17,10 @@ export class StripeService {
|
|||||||
cardCvc: string;
|
cardCvc: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(private logService: LogService) {}
|
constructor(
|
||||||
|
private logService: LogService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads [Stripe JS]{@link https://docs.stripe.com/js} in the <head> element of the current page and mounts
|
* Loads [Stripe JS]{@link https://docs.stripe.com/js} in the <head> element of the current page and mounts
|
||||||
@ -32,15 +37,23 @@ export class StripeService {
|
|||||||
const script = window.document.createElement("script");
|
const script = window.document.createElement("script");
|
||||||
script.id = "stripe-script";
|
script.id = "stripe-script";
|
||||||
script.src = "https://js.stripe.com/v3?advancedFraudSignals=false";
|
script.src = "https://js.stripe.com/v3?advancedFraudSignals=false";
|
||||||
script.onload = () => {
|
script.onload = async () => {
|
||||||
const window$ = window as any;
|
const window$ = window as any;
|
||||||
this.stripe = window$.Stripe(process.env.STRIPE_KEY);
|
this.stripe = window$.Stripe(process.env.STRIPE_KEY);
|
||||||
this.elements = this.stripe.elements();
|
this.elements = this.stripe.elements();
|
||||||
const options = this.getElementOptions();
|
const isExtensionRefresh = await this.configService.getFeatureFlag(
|
||||||
|
FeatureFlag.ExtensionRefresh,
|
||||||
|
);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.elements.create("cardNumber", options);
|
this.elements.create(
|
||||||
this.elements.create("cardExpiry", options);
|
"cardNumber",
|
||||||
this.elements.create("cardCvc", options);
|
this.getElementOptions("cardNumber", isExtensionRefresh),
|
||||||
|
);
|
||||||
|
this.elements.create(
|
||||||
|
"cardExpiry",
|
||||||
|
this.getElementOptions("cardExpiry", isExtensionRefresh),
|
||||||
|
);
|
||||||
|
this.elements.create("cardCvc", this.getElementOptions("cardCvc", isExtensionRefresh));
|
||||||
if (autoMount) {
|
if (autoMount) {
|
||||||
this.mountElements();
|
this.mountElements();
|
||||||
}
|
}
|
||||||
@ -135,7 +148,10 @@ export class StripeService {
|
|||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getElementOptions(): any {
|
private getElementOptions(
|
||||||
|
element: "cardNumber" | "cardExpiry" | "cardCvc",
|
||||||
|
isExtensionRefresh: boolean,
|
||||||
|
): any {
|
||||||
const options: any = {
|
const options: any = {
|
||||||
style: {
|
style: {
|
||||||
base: {
|
base: {
|
||||||
@ -160,6 +176,17 @@ export class StripeService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Unique settings that should only be applied when the extension refresh flag is active
|
||||||
|
if (isExtensionRefresh) {
|
||||||
|
options.style.base.fontWeight = "500";
|
||||||
|
options.classes.base = "v2";
|
||||||
|
|
||||||
|
// Remove the placeholder for number and CVC fields
|
||||||
|
if (["cardNumber", "cardCvc"].includes(element)) {
|
||||||
|
options.placeholder = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const style = getComputedStyle(document.documentElement);
|
const style = getComputedStyle(document.documentElement);
|
||||||
options.style.base.color = `rgb(${style.getPropertyValue("--color-text-main")})`;
|
options.style.base.color = `rgb(${style.getPropertyValue("--color-text-main")})`;
|
||||||
options.style.base["::placeholder"].color = `rgb(${style.getPropertyValue(
|
options.style.base["::placeholder"].color = `rgb(${style.getPropertyValue(
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
<ng-template #defaultContent>
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-container *ngIf="extensionRefreshFlag; else defaultLabel">
|
||||||
|
<div class="tw-relative tw-mt-2">
|
||||||
|
<bit-label
|
||||||
|
[attr.for]="for"
|
||||||
|
class="tw-absolute tw-bg-background tw-px-1 tw-text-sm tw-text-muted -tw-top-2.5 tw-left-3 tw-mb-0 tw-max-w-full tw-pointer-events-auto"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="defaultContent"></ng-container>
|
||||||
|
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
|
||||||
|
</bit-label>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-template #defaultLabel>
|
||||||
|
<label [attr.for]="for">
|
||||||
|
<ng-container *ngTemplateOutlet="defaultContent"></ng-container>
|
||||||
|
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
|
||||||
|
</label>
|
||||||
|
</ng-template>
|
@ -0,0 +1,36 @@
|
|||||||
|
import { booleanAttribute, Component, Input, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
|
import { FormFieldModule } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label that should be used for elements loaded via Stripe API.
|
||||||
|
*
|
||||||
|
* Applies the same label styles from CL form-field component when
|
||||||
|
* the `ExtensionRefresh` flag is set.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: "app-payment-label-v2",
|
||||||
|
templateUrl: "./payment-label-v2.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [FormFieldModule, SharedModule],
|
||||||
|
})
|
||||||
|
export class PaymentLabelV2 implements OnInit {
|
||||||
|
/** `id` of the associated input */
|
||||||
|
@Input({ required: true }) for: string;
|
||||||
|
/** Displays required text on the label */
|
||||||
|
@Input({ transform: booleanAttribute }) required = false;
|
||||||
|
|
||||||
|
protected extensionRefreshFlag = false;
|
||||||
|
|
||||||
|
constructor(private configService: ConfigService) {}
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
this.extensionRefreshFlag = await this.configService.getFeatureFlag(
|
||||||
|
FeatureFlag.ExtensionRefresh,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -43,10 +43,9 @@
|
|||||||
<ng-container *ngIf="usingCard">
|
<ng-container *ngIf="usingCard">
|
||||||
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4">
|
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4">
|
||||||
<div class="tw-col-span-1">
|
<div class="tw-col-span-1">
|
||||||
<label for="stripe-card-number">
|
<app-payment-label-v2 for="stripe-card-number" required>
|
||||||
{{ "number" | i18n }}
|
{{ "number" | i18n }}
|
||||||
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
|
</app-payment-label-v2>
|
||||||
</label>
|
|
||||||
<div id="stripe-card-number" class="form-control stripe-form-control"></div>
|
<div id="stripe-card-number" class="form-control stripe-form-control"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-col-span-1 tw-flex tw-items-end">
|
<div class="tw-col-span-1 tw-flex tw-items-end">
|
||||||
@ -57,29 +56,24 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-col-span-1">
|
<div class="tw-col-span-1">
|
||||||
<label for="stripe-card-expiry">
|
<app-payment-label-v2 for="stripe-card-expiry" required>
|
||||||
{{ "expiration" | i18n }}
|
{{ "expiration" | i18n }}
|
||||||
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
|
</app-payment-label-v2>
|
||||||
</label>
|
|
||||||
<div id="stripe-card-expiry" class="form-control stripe-form-control"></div>
|
<div id="stripe-card-expiry" class="form-control stripe-form-control"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-col-span-1">
|
<div class="tw-col-span-1">
|
||||||
<div class="tw-flex">
|
<app-payment-label-v2 for="stripe-card-cvc" required>
|
||||||
<label for="stripe-card-cvc">
|
{{ "securityCodeSlashCVV" | i18n }}
|
||||||
{{ "securityCode" | i18n }}
|
|
||||||
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
|
|
||||||
</label>
|
|
||||||
<a
|
<a
|
||||||
href="https://www.cvvnumber.com/cvv.html"
|
href="https://www.cvvnumber.com/cvv.html"
|
||||||
tabindex="-1"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
class="ml-auto"
|
|
||||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
|
class="hover:tw-no-underline"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</app-payment-label-v2>
|
||||||
<div id="stripe-card-cvc" class="form-control stripe-form-control"></div>
|
<div id="stripe-card-cvc" class="form-control stripe-form-control"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,6 +10,8 @@ import { TokenizedPaymentSourceRequest } from "@bitwarden/common/billing/models/
|
|||||||
import { SharedModule } from "../../../shared";
|
import { SharedModule } from "../../../shared";
|
||||||
import { BillingServicesModule, BraintreeService, StripeService } from "../../services";
|
import { BillingServicesModule, BraintreeService, StripeService } from "../../services";
|
||||||
|
|
||||||
|
import { PaymentLabelV2 } from "./payment-label-v2.component";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a form that allows the user to enter their payment method, tokenize it against one of our payment providers and,
|
* Render a form that allows the user to enter their payment method, tokenize it against one of our payment providers and,
|
||||||
* optionally, submit it using the {@link onSubmit} function if it is provided.
|
* optionally, submit it using the {@link onSubmit} function if it is provided.
|
||||||
@ -20,7 +22,7 @@ import { BillingServicesModule, BraintreeService, StripeService } from "../../se
|
|||||||
selector: "app-payment-v2",
|
selector: "app-payment-v2",
|
||||||
templateUrl: "./payment-v2.component.html",
|
templateUrl: "./payment-v2.component.html",
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [BillingServicesModule, SharedModule],
|
imports: [BillingServicesModule, SharedModule, PaymentLabelV2],
|
||||||
})
|
})
|
||||||
export class PaymentV2Component implements OnInit, OnDestroy {
|
export class PaymentV2Component implements OnInit, OnDestroy {
|
||||||
/** Show account credit as a payment option. */
|
/** Show account credit as a payment option. */
|
||||||
|
@ -26,8 +26,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="showMethods && method === paymentMethodType.Card">
|
<ng-container *ngIf="showMethods && method === paymentMethodType.Card">
|
||||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4 tw-mb-4">
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4 tw-mb-4">
|
||||||
<div [ngClass]="trialFlow ? 'tw-col-span-5' : 'tw-col-span-4'">
|
<div [ngClass]="trialFlow ? 'tw-col-span-12' : 'tw-col-span-4'">
|
||||||
<label for="stripe-card-number-element">{{ "number" | i18n }}</label>
|
<app-payment-label-v2 for="stripe-card-number-element">{{
|
||||||
|
"number" | i18n
|
||||||
|
}}</app-payment-label-v2>
|
||||||
<div id="stripe-card-number-element" class="form-control stripe-form-control"></div>
|
<div id="stripe-card-number-element" class="form-control stripe-form-control"></div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!trialFlow" class="tw-col-span-8 tw-flex tw-items-end">
|
<div *ngIf="!trialFlow" class="tw-col-span-8 tw-flex tw-items-end">
|
||||||
@ -38,26 +40,26 @@
|
|||||||
height="32"
|
height="32"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div [ngClass]="trialFlow ? 'tw-col-span-3' : 'tw-col-span-4'">
|
<div [ngClass]="trialFlow ? 'tw-col-span-6' : 'tw-col-span-4'">
|
||||||
<label for="stripe-card-expiry-element">{{ "expiration" | i18n }}</label>
|
<app-payment-label-v2 for="stripe-card-expiry-element">{{
|
||||||
|
"expiration" | i18n
|
||||||
|
}}</app-payment-label-v2>
|
||||||
<div id="stripe-card-expiry-element" class="form-control stripe-form-control"></div>
|
<div id="stripe-card-expiry-element" class="form-control stripe-form-control"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-col-span-4">
|
<div [ngClass]="trialFlow ? 'tw-col-span-6' : 'tw-col-span-4'">
|
||||||
<div class="tw-flex">
|
<app-payment-label-v2 for="stripe-card-cvc-element">
|
||||||
<label for="stripe-card-cvc-element">
|
{{ "securityCodeSlashCVV" | i18n }}
|
||||||
{{ "securityCode" | i18n }}
|
|
||||||
</label>
|
|
||||||
<a
|
<a
|
||||||
href="https://www.cvvnumber.com/cvv.html"
|
href="https://www.cvvnumber.com/cvv.html"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
class="ml-auto"
|
class="hover:tw-no-underline"
|
||||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</app-payment-label-v2>
|
||||||
<div id="stripe-card-cvc-element" class="form-control stripe-form-control"></div>
|
<div id="stripe-card-cvc-element" class="form-control stripe-form-control"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,15 +5,19 @@ import { Subject, takeUntil } from "rxjs";
|
|||||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
|
||||||
import { SharedModule } from "../../../shared";
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
|
import { PaymentLabelV2 } from "./payment-label-v2.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-payment",
|
selector: "app-payment",
|
||||||
templateUrl: "payment.component.html",
|
templateUrl: "payment.component.html",
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule],
|
imports: [SharedModule, PaymentLabelV2],
|
||||||
})
|
})
|
||||||
export class PaymentComponent implements OnInit, OnDestroy {
|
export class PaymentComponent implements OnInit, OnDestroy {
|
||||||
@Input() showMethods = true;
|
@Input() showMethods = true;
|
||||||
@ -63,14 +67,15 @@ export class PaymentComponent implements OnInit, OnDestroy {
|
|||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private themingService: AbstractThemingService,
|
private themingService: AbstractThemingService,
|
||||||
|
private configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
this.stripeScript = window.document.createElement("script");
|
this.stripeScript = window.document.createElement("script");
|
||||||
this.stripeScript.src = "https://js.stripe.com/v3/?advancedFraudSignals=false";
|
this.stripeScript.src = "https://js.stripe.com/v3/?advancedFraudSignals=false";
|
||||||
this.stripeScript.async = true;
|
this.stripeScript.async = true;
|
||||||
this.stripeScript.onload = () => {
|
this.stripeScript.onload = async () => {
|
||||||
this.stripe = (window as any).Stripe(process.env.STRIPE_KEY);
|
this.stripe = (window as any).Stripe(process.env.STRIPE_KEY);
|
||||||
this.stripeElements = this.stripe.elements();
|
this.stripeElements = this.stripe.elements();
|
||||||
this.setStripeElement();
|
await this.setStripeElement();
|
||||||
};
|
};
|
||||||
this.btScript = window.document.createElement("script");
|
this.btScript = window.document.createElement("script");
|
||||||
this.btScript.src = `scripts/dropin.js?cache=${process.env.CACHE_TAG}`;
|
this.btScript.src = `scripts/dropin.js?cache=${process.env.CACHE_TAG}`;
|
||||||
@ -187,7 +192,7 @@ export class PaymentComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}, 250);
|
}, 250);
|
||||||
} else {
|
} else {
|
||||||
this.setStripeElement();
|
void this.setStripeElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +272,17 @@ export class PaymentComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setStripeElement() {
|
private async setStripeElement() {
|
||||||
|
const extensionRefreshFlag = await this.configService.getFeatureFlag(
|
||||||
|
FeatureFlag.ExtensionRefresh,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Apply unique styles for extension refresh
|
||||||
|
if (extensionRefreshFlag) {
|
||||||
|
this.StripeElementStyle.base.fontWeight = "500";
|
||||||
|
this.StripeElementClasses.base = "v2";
|
||||||
|
}
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
if (this.showMethods && this.method === PaymentMethodType.Card) {
|
if (this.showMethods && this.method === PaymentMethodType.Card) {
|
||||||
if (this.stripeCardNumberElement == null) {
|
if (this.stripeCardNumberElement == null) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<form [formGroup]="taxFormGroup">
|
<form [formGroup]="taxFormGroup">
|
||||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||||
<div [ngClass]="trialFlow ? 'tw-col-span-7' : 'tw-col-span-6'">
|
<div class="tw-col-span-6">
|
||||||
<bit-form-field>
|
<bit-form-field>
|
||||||
<bit-label>{{ "country" | i18n }}</bit-label>
|
<bit-label>{{ "country" | i18n }}</bit-label>
|
||||||
<bit-select formControlName="country" autocomplete="country">
|
<bit-select formControlName="country" autocomplete="country">
|
||||||
@ -13,7 +13,7 @@
|
|||||||
</bit-select>
|
</bit-select>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div [ngClass]="trialFlow ? 'tw-col-span-5' : 'tw-col-span-4'">
|
<div [ngClass]="trialFlow ? 'tw-col-span-6' : 'tw-col-span-4'">
|
||||||
<bit-form-field>
|
<bit-form-field>
|
||||||
<bit-label>{{ "zipPostalCode" | i18n }}</bit-label>
|
<bit-label>{{ "zipPostalCode" | i18n }}</bit-label>
|
||||||
<input bitInput type="text" formControlName="postalCode" autocomplete="postal-code" />
|
<input bitInput type="text" formControlName="postalCode" autocomplete="postal-code" />
|
||||||
|
@ -143,6 +143,9 @@
|
|||||||
"securityCode": {
|
"securityCode": {
|
||||||
"message": "Security code (CVV)"
|
"message": "Security code (CVV)"
|
||||||
},
|
},
|
||||||
|
"securityCodeSlashCVV": {
|
||||||
|
"message": "Security code / CVV"
|
||||||
|
},
|
||||||
"identityName": {
|
"identityName": {
|
||||||
"message": "Identity name"
|
"message": "Identity name"
|
||||||
},
|
},
|
||||||
|
@ -98,7 +98,7 @@ input[type="checkbox"] {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control.stripe-form-control {
|
.form-control.stripe-form-control:not(.v2) {
|
||||||
padding-top: 0.55rem;
|
padding-top: 0.55rem;
|
||||||
|
|
||||||
&.is-focused {
|
&.is-focused {
|
||||||
@ -126,6 +126,30 @@ input[type="checkbox"] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-control.stripe-form-control.v2 {
|
||||||
|
padding: 0.6875rem 0.875rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border-color: rgb(var(--color-text-muted));
|
||||||
|
height: unset;
|
||||||
|
font-weight: 500;
|
||||||
|
color: rgb(var(--color-text-main));
|
||||||
|
background-color: rgb(var(--color-background));
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgb(var(--color-primary-500));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-focused {
|
||||||
|
outline: 0;
|
||||||
|
border-color: rgb(var(--color-primary-500));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-invalid {
|
||||||
|
color: rgb(var(--color-text-main));
|
||||||
|
border-color: rgb(var(--color-danger-600));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-menu,
|
.dropdown-menu,
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
@include themify($themes) {
|
@include themify($themes) {
|
||||||
|
Loading…
Reference in New Issue
Block a user