Skip to content

Commit f0bad98

Browse files
committed
fix: typescript issues, forward element events to component
1 parent 0d7b181 commit f0bad98

File tree

2 files changed

+58
-47
lines changed

2 files changed

+58
-47
lines changed

src/components/StripeElement.vue

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,70 +4,70 @@
44

55
<script setup lang="ts">
66
import type { StripeElementType, StripeElements } from "@stripe/stripe-js"
7+
import type { Ref } from "vue"
78
import {
9+
computed,
810
defineEmits,
911
inject,
1012
onBeforeUnmount,
1113
onMounted,
1214
ref,
1315
toRefs,
1416
watch,
15-
withDefaults,
1617
} from "vue"
18+
import type { StripeElementOptionsMap } from "../stripe-elements"
1719
import { createElement } from "../stripe-elements"
18-
import type { StripeElementOptions } from "../stripe-elements"
1920
2021
interface Props {
2122
type?: StripeElementType
2223
elements?: StripeElements
23-
options?: StripeElementOptions
24+
options?: StripeElementOptionsMap[StripeElementType]
2425
}
2526
26-
const props = withDefaults(defineProps<Props>(), {
27-
type: () =>
28-
(inject("providedElements") as StripeElementType) ? "payment" : "card",
29-
elements: () => inject("providedElements") as StripeElements,
30-
})
27+
const props = defineProps<Props>()
28+
29+
const emit = defineEmits(eventTypes)
3130
const { type, elements, options } = toRefs(props)
3231
33-
// TODO: verify it works
34-
const emit = defineEmits()
32+
// Backward-compatibility: used when no type is provided. Having elements at this stage means it comes from slot props.
33+
const fallbackType = computed(() => (elements.value ? "card" : "payment"))
34+
35+
// This dependency is provided by StripeElements.vue
36+
const providedElements = inject("providedElements") as Ref<StripeElements>
3537
3638
const domElement = ref(document.createElement("div"))
37-
const stripeElement = ref()
38-
const mountPoint = ref()
39+
const stripeElement = ref<ReturnType<typeof createElement>>()
40+
const mountPoint = ref<HTMLDivElement>()
3941
4042
onMounted(() => {
4143
const mountElement = () => {
4244
stripeElement.value = createElement(
43-
elements.value,
44-
type.value,
45+
elements.value || providedElements.value,
46+
type.value || fallbackType.value,
4547
options.value,
4648
)
47-
mountPoint.value.appendChild(domElement.value)
49+
mountPoint.value?.appendChild(domElement.value)
4850
stripeElement.value?.mount(domElement.value)
4951
}
5052
51-
// Handle event listeners
52-
const wrapperFn = (t: string, e: Event) => {
53-
emit(t, e)
54-
}
55-
5653
const handleEvents = () => {
57-
// See stripe element events: https://stripe.com/docs/js/element/events
58-
const eventTypes = [
59-
"change",
60-
"ready",
61-
"focus",
62-
"blur",
63-
"click",
64-
"escape",
65-
"loaderror",
66-
"loaderstart",
67-
]
68-
69-
for (const eventType of eventTypes) {
70-
stripeElement.value?.on(eventType, wrapperFn.bind(null, eventType))
54+
// Define the interface for the type with the "on" method
55+
interface StripeElementWithOn {
56+
on(eventType: string, handler: (e: Event) => void): void
57+
}
58+
59+
// Type guard function to check if an object has the "on" method
60+
function hasOnMethod(obj: unknown): obj is StripeElementWithOn {
61+
return !!obj && typeof (obj as StripeElementWithOn).on === "function"
62+
}
63+
64+
if (stripeElement.value && hasOnMethod(stripeElement.value)) {
65+
// See stripe element events: https://stripe.com/docs/js/element/events
66+
for (const eventType of eventTypes) {
67+
stripeElement.value.on(eventType, (e: Event) => {
68+
emit(eventType, e)
69+
})
70+
}
7171
}
7272
}
7373
@@ -85,7 +85,9 @@ onBeforeUnmount(() => {
8585
})
8686
8787
watch(options, () => {
88-
stripeElement.value?.update(props.options)
88+
if (options.value && stripeElement.value && "update" in stripeElement.value) {
89+
stripeElement.value.update(options.value)
90+
}
8991
})
9092
9193
defineExpose({
@@ -94,3 +96,16 @@ defineExpose({
9496
mountPoint,
9597
})
9698
</script>
99+
100+
<script lang="ts">
101+
export const eventTypes = [
102+
"change",
103+
"ready",
104+
"focus",
105+
"blur",
106+
"click",
107+
"escape",
108+
"loaderror",
109+
"loaderstart",
110+
]
111+
</script>

src/stripe-elements.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import type {
99
StripeCardExpiryElementOptions,
1010
StripeCardNumberElementOptions,
1111
StripeConstructorOptions,
12-
StripeCurrencySelectorElement,
13-
StripeElement,
1412
StripeElementType,
1513
StripeElements,
1614
StripeElementsOptions,
@@ -26,7 +24,6 @@ import type {
2624
StripeIssuingCardExpiryDisplayElementOptions,
2725
StripeIssuingCardNumberDisplayElementOptions,
2826
StripeIssuingCardPinDisplayElementOptions,
29-
StripeLinkAuthenticationElement,
3027
StripeP24BankElementOptions,
3128
StripePaymentElementOptions,
3229
StripePaymentMethodMessagingElementOptions,
@@ -43,7 +40,6 @@ export type StripeElementOptions =
4340
| StripeCardElementOptions
4441
| StripeCardExpiryElementOptions
4542
| StripeCardNumberElementOptions
46-
| StripeCurrencySelectorElement
4743
| StripeEpsBankElementOptions
4844
| StripeExpressCheckoutElementOptions
4945
| StripeFpxBankElementOptions
@@ -54,14 +50,13 @@ export type StripeElementOptions =
5450
| StripeIssuingCardExpiryDisplayElementOptions
5551
| StripeIssuingCardNumberDisplayElementOptions
5652
| StripeIssuingCardPinDisplayElementOptions
57-
| StripeLinkAuthenticationElement
5853
| StripeP24BankElementOptions
5954
| StripePaymentElementOptions
6055
| StripePaymentMethodMessagingElementOptions
6156
| StripePaymentRequestButtonElementOptions
6257
| StripeShippingAddressElementOptions
6358

64-
/* type StripeElementOptionsMap = {
59+
export type StripeElementOptionsMap = {
6560
address: StripeAddressElementOptions
6661
affirmMessage: StripeAffirmMessageElementOptions
6762
afterpayClearpayMessage: StripeAfterpayClearpayMessageElementOptions
@@ -87,12 +82,13 @@ export type StripeElementOptions =
8782
paymentMethodMessaging: StripePaymentMethodMessagingElementOptions
8883
paymentRequestButton: StripePaymentRequestButtonElementOptions
8984
shippingAddress: StripeShippingAddressElementOptions
90-
} */
85+
}
9186

9287
export const ERRORS = {
93-
STRIPE_NOT_LOADED: "Stripe script is not loaded",
88+
STRIPE_NOT_LOADED:
89+
"Stripe is not loaded. Include it as script or load using loadStripe method of @stripe/stripe-js",
9490
INSTANCE_NOT_DEFINED:
95-
"Instance object is not defined. Initialize Stripe before creating elements",
91+
"Stripe instance is not defined. Initialize Stripe before creating elements",
9692
ELEMENTS_NOT_DEFINED:
9793
"Elements object is not defined. You can't create stripe element without it",
9894
ELEMENT_TYPE_NOT_DEFINED:
@@ -134,16 +130,16 @@ export const createElements = (
134130
export const createElement = (
135131
elements: StripeElements,
136132
elementType: StripeElementType,
137-
options?: StripeElementOptions,
138-
): StripeElement | undefined => {
133+
options?: StripeElementOptionsMap[StripeElementType],
134+
) => {
139135
try {
140136
if (!elements) {
141137
throw new Error(ERRORS.ELEMENTS_NOT_DEFINED)
142138
}
143139
if (!elementType) {
144140
throw new Error(ERRORS.ELEMENT_TYPE_NOT_DEFINED)
145141
}
146-
return elements.create(elementType as any, options as any)
142+
return elements.create(elementType, options)
147143
} catch (error) {
148144
console.error(error)
149145
}

0 commit comments

Comments
 (0)