@getspot/spot-widget-react 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +247 -5
- package/dist/ReactSpotWidget.d.ts +83 -0
- package/dist/ReactSpotWidget.d.ts.map +1 -0
- package/dist/__tests__/ReactSpotWidget.test.d.ts +2 -0
- package/dist/__tests__/ReactSpotWidget.test.d.ts.map +1 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.es.js +38 -36
- package/dist/index.umd.js +1 -1
- package/dist/setupTests.d.ts +2 -0
- package/dist/setupTests.d.ts.map +1 -0
- package/package.json +38 -9
- package/.turbo/turbo-build.log +0 -14
- package/src/ReactSpotWidget.jsx +0 -93
- package/vite.config.js +0 -23
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,12 +1,254 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @getspot/spot-widget-react
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
React component wrapper for the Spot refund guarantee widget.
|
|
4
|
+
|
|
5
|
+
> **Note:** This React wrapper uses types from [@getspot/spot-widget](https://www.npmjs.com/package/@getspot/spot-widget).
|
|
6
|
+
|
|
7
|
+
### Key Types
|
|
8
|
+
- `ApiConfig` - API configuration (environment, partnerId, customEndpoint)
|
|
9
|
+
- `QuoteRequestData` - Quote request data for single items or batch requests
|
|
10
|
+
- `SelectionData` - User selection data returned in callbacks
|
|
11
|
+
- `Quote` - Quote response data with pricing and terms
|
|
12
|
+
- `Theme` - Styling customization options
|
|
13
|
+
|
|
14
|
+
For complete type definitions, see the [@getspot/spot-widget documentation](https://www.npmjs.com/package/@getspot/spot-widget).
|
|
5
15
|
|
|
6
16
|
## Installation
|
|
7
17
|
|
|
8
18
|
```bash
|
|
9
|
-
npm install
|
|
19
|
+
npm install @getspot/spot-widget-react
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import React from 'react';
|
|
26
|
+
import ReactSpotWidget from '@getspot/spot-widget-react';
|
|
27
|
+
|
|
28
|
+
function App() {
|
|
29
|
+
return (
|
|
30
|
+
<ReactSpotWidget
|
|
31
|
+
apiConfig={{
|
|
32
|
+
environment: 'production', // or 'sandbox' for testing
|
|
33
|
+
partnerId: 'your-partner-id'
|
|
34
|
+
}}
|
|
35
|
+
quoteRequestData={{
|
|
36
|
+
startDate: '2024-01-01T00:00:00Z',
|
|
37
|
+
endDate: '2024-01-07T23:59:59Z',
|
|
38
|
+
currencyCode: 'USD',
|
|
39
|
+
eventType: 'Ski Trip',
|
|
40
|
+
productType: 'Trip',
|
|
41
|
+
productDuration: 'Trip',
|
|
42
|
+
productPrice: 500,
|
|
43
|
+
productId: 'ski-trip-2024',
|
|
44
|
+
cartId: 'cart-123',
|
|
45
|
+
productName: 'Aspen Ski Trip 2024'
|
|
46
|
+
}}
|
|
47
|
+
onOptIn={(data) => {
|
|
48
|
+
console.log('User opted in:', data);
|
|
49
|
+
// Handle opt-in (e.g., add to cart, track analytics)
|
|
50
|
+
}}
|
|
51
|
+
onOptOut={(data) => {
|
|
52
|
+
console.log('User opted out:', data);
|
|
53
|
+
// Handle opt-out (e.g., track analytics)
|
|
54
|
+
}}
|
|
55
|
+
onError={(error) => {
|
|
56
|
+
console.error('Widget error:', error);
|
|
57
|
+
// Handle errors (e.g., show fallback UI)
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
10
62
|
```
|
|
11
63
|
|
|
12
|
-
##
|
|
64
|
+
## TypeScript Support
|
|
65
|
+
|
|
66
|
+
This package includes full TypeScript definitions. All props are typed for better development experience.
|
|
67
|
+
|
|
68
|
+
## Props Reference
|
|
69
|
+
|
|
70
|
+
### Required Props
|
|
71
|
+
|
|
72
|
+
| Prop | Type | Description |
|
|
73
|
+
|------|------|-------------|
|
|
74
|
+
| `apiConfig` | `ApiConfig` | Configuration for the Spot API including environment and partner ID |
|
|
75
|
+
| `quoteRequestData` | `QuoteRequestData` | Quote request data containing product and cart information |
|
|
76
|
+
|
|
77
|
+
### Optional Props
|
|
78
|
+
|
|
79
|
+
| Prop | Type | Default | Description |
|
|
80
|
+
|------|------|---------|-------------|
|
|
81
|
+
| `showTable` | `boolean` | `true` | Whether to show the payout table |
|
|
82
|
+
| `optInSelected` | `boolean` | `false` | Whether the widget should be pre-selected for opt-in |
|
|
83
|
+
| `theme` | `Theme` | `undefined` | Theme customization options for styling the widget |
|
|
84
|
+
|
|
85
|
+
### Callback Props
|
|
86
|
+
|
|
87
|
+
| Prop | Type | Description |
|
|
88
|
+
|------|------|-------------|
|
|
89
|
+
| `onQuoteRetrieved` | `(quote: Quote) => void` | Callback fired when a quote is successfully retrieved |
|
|
90
|
+
| `onOptIn` | `(data: SelectionData) => void` | Callback fired when user opts in to the refund guarantee |
|
|
91
|
+
| `onOptOut` | `(data: SelectionData) => void` | Callback fired when user opts out of the refund guarantee |
|
|
92
|
+
| `onError` | `(error: ErrorData) => void` | Callback fired when an error occurs during quote retrieval |
|
|
93
|
+
| `onNoMatchingQuote` | `(data: NoQuoteData) => void` | Callback fired when no matching quote is found |
|
|
94
|
+
| `onSelectionChange` | `(data: SelectionData) => void` | Callback fired when user changes their selection (opt-in or opt-out) |
|
|
95
|
+
|
|
96
|
+
## Using Refs
|
|
97
|
+
|
|
98
|
+
You can access widget methods using React refs:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import React, { useRef } from 'react';
|
|
102
|
+
import ReactSpotWidget, { ReactSpotWidgetRef } from '@getspot/spot-widget-react';
|
|
103
|
+
|
|
104
|
+
function App() {
|
|
105
|
+
const widgetRef = useRef<ReactSpotWidgetRef>(null);
|
|
106
|
+
|
|
107
|
+
const handleUpdateQuote = async () => {
|
|
108
|
+
const success = await widgetRef.current?.updateQuote({
|
|
109
|
+
// new quote data
|
|
110
|
+
});
|
|
111
|
+
console.log('Quote updated:', success);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleGetSelection = () => {
|
|
115
|
+
const selection = widgetRef.current?.getSelection();
|
|
116
|
+
console.log('Current selection:', selection);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleValidateSelection = () => {
|
|
120
|
+
const isValid = widgetRef.current?.validateSelection();
|
|
121
|
+
console.log('Selection is valid:', isValid);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div>
|
|
126
|
+
<ReactSpotWidget
|
|
127
|
+
ref={widgetRef}
|
|
128
|
+
// ... props
|
|
129
|
+
/>
|
|
130
|
+
<button onClick={handleUpdateQuote}>Update Quote</button>
|
|
131
|
+
<button onClick={handleGetSelection}>Get Selection</button>
|
|
132
|
+
<button onClick={handleValidateSelection}>Validate Selection</button>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Ref Methods
|
|
139
|
+
|
|
140
|
+
| Method | Return Type | Description |
|
|
141
|
+
|--------|-------------|-------------|
|
|
142
|
+
| `updateQuote(data)` | `Promise<boolean>` | Update the quote with new request data |
|
|
143
|
+
| `getSelection()` | `SelectionData \| null` | Get the current user selection |
|
|
144
|
+
| `validateSelection()` | `boolean` | Validate that the user has made a selection |
|
|
145
|
+
| `destroy()` | `void` | Destroy the widget instance and clean up resources |
|
|
146
|
+
|
|
147
|
+
## API Configuration
|
|
148
|
+
|
|
149
|
+
The `apiConfig` prop accepts the following options:
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
{
|
|
153
|
+
environment: 'production' | 'sandbox' | 'local',
|
|
154
|
+
partnerId: string,
|
|
155
|
+
customEndpoint?: string // Optional custom API endpoint
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Quote Request Data
|
|
160
|
+
|
|
161
|
+
The `quoteRequestData` prop requires the following fields:
|
|
162
|
+
|
|
163
|
+
### Single Quote Format
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
{
|
|
167
|
+
startDate: string, // ISO 8601 date string
|
|
168
|
+
endDate: string, // ISO 8601 date string
|
|
169
|
+
currencyCode: 'USD' | 'CAD' | 'AUD',
|
|
170
|
+
eventType: string, // e.g., "Ski Trip", "Concert"
|
|
171
|
+
productType: 'Pass' | 'Trip' | 'Registration',
|
|
172
|
+
productDuration: 'Daily' | 'Seasonal' | 'Trip' | 'Event',
|
|
173
|
+
productPrice: number, // Price in specified currency
|
|
174
|
+
productId: string, // Unique product identifier
|
|
175
|
+
cartId: string, // Cart identifier
|
|
176
|
+
productName: string, // Human-readable product name
|
|
177
|
+
participantDescription?: string // Optional participant details
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Batch Quote Format
|
|
182
|
+
|
|
183
|
+
For multiple items in a cart:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
{
|
|
187
|
+
cartInfo: {
|
|
188
|
+
cartId: string,
|
|
189
|
+
cartName: string,
|
|
190
|
+
currencyCode: 'USD' | 'CAD' | 'AUD'
|
|
191
|
+
},
|
|
192
|
+
items: Array<{
|
|
193
|
+
// Same fields as single quote, minus cartId and currencyCode
|
|
194
|
+
cartItemId?: string // Optional unique identifier for cart item
|
|
195
|
+
}>
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Styling
|
|
200
|
+
|
|
201
|
+
Customize the widget appearance using the `theme` prop:
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
<ReactSpotWidget
|
|
205
|
+
theme={{
|
|
206
|
+
primaryColor: '#007bff',
|
|
207
|
+
borderRadius: '8px',
|
|
208
|
+
fontFamily: 'Arial, sans-serif'
|
|
209
|
+
// Add any CSS custom properties (without -- prefix)
|
|
210
|
+
}}
|
|
211
|
+
// ... other props
|
|
212
|
+
/>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Error Handling
|
|
216
|
+
|
|
217
|
+
Always implement error handling for production use:
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
<ReactSpotWidget
|
|
221
|
+
onError={(error) => {
|
|
222
|
+
// Log error for debugging
|
|
223
|
+
console.error('Spot Widget Error:', error);
|
|
224
|
+
|
|
225
|
+
// Show user-friendly message
|
|
226
|
+
toast.error('Unable to load refund options. Please try again.');
|
|
227
|
+
|
|
228
|
+
// Track error in analytics
|
|
229
|
+
analytics.track('spot_widget_error', {
|
|
230
|
+
message: error.message,
|
|
231
|
+
status: error.status
|
|
232
|
+
});
|
|
233
|
+
}}
|
|
234
|
+
onNoMatchingQuote={() => {
|
|
235
|
+
// Handle case where no quote is available
|
|
236
|
+
console.log('No refund guarantee available for this product');
|
|
237
|
+
}}
|
|
238
|
+
// ... other props
|
|
239
|
+
/>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Compatibility
|
|
243
|
+
|
|
244
|
+
- **React**: 18.x
|
|
245
|
+
- **TypeScript**: 5.x
|
|
246
|
+
- **Node.js**: 16+
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
See the main package for license information.
|
|
251
|
+
|
|
252
|
+
## Support
|
|
253
|
+
|
|
254
|
+
For support, please contact [support@getspot.com](mailto:support@getspot.com).
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type SpotWidgetOptions, type SelectionData, type Quote } from "@getspot/spot-widget";
|
|
3
|
+
/**
|
|
4
|
+
* Props for the ReactSpotWidget component
|
|
5
|
+
*/
|
|
6
|
+
interface ReactSpotWidgetProps extends Omit<SpotWidgetOptions, 'location' | 'callbacks'> {
|
|
7
|
+
/** Configuration for the Spot API including environment and partner ID */
|
|
8
|
+
apiConfig: SpotWidgetOptions['apiConfig'];
|
|
9
|
+
/** Quote request data containing product and cart information */
|
|
10
|
+
quoteRequestData: SpotWidgetOptions['quoteRequestData'];
|
|
11
|
+
/** Whether to show the payout table. Defaults to true */
|
|
12
|
+
showTable?: boolean;
|
|
13
|
+
/** Whether the widget should be pre-selected for opt-in. Defaults to false */
|
|
14
|
+
optInSelected?: boolean;
|
|
15
|
+
/** Theme customization options for styling the widget */
|
|
16
|
+
theme?: SpotWidgetOptions['theme'];
|
|
17
|
+
/** Callback fired when a quote is successfully retrieved */
|
|
18
|
+
onQuoteRetrieved?: (quote: Quote) => void;
|
|
19
|
+
/** Callback fired when user opts in to the refund guarantee */
|
|
20
|
+
onOptIn?: (data: SelectionData) => void;
|
|
21
|
+
/** Callback fired when user opts out of the refund guarantee */
|
|
22
|
+
onOptOut?: (data: SelectionData) => void;
|
|
23
|
+
/** Callback fired when an error occurs during quote retrieval */
|
|
24
|
+
onError?: (error: {
|
|
25
|
+
message: string;
|
|
26
|
+
status?: number;
|
|
27
|
+
responseBody?: any;
|
|
28
|
+
}) => void;
|
|
29
|
+
/** Callback fired when no matching quote is found */
|
|
30
|
+
onNoMatchingQuote?: (data: {
|
|
31
|
+
status: string;
|
|
32
|
+
data: any;
|
|
33
|
+
}) => void;
|
|
34
|
+
/** Callback fired when user changes their selection (opt-in or opt-out) */
|
|
35
|
+
onSelectionChange?: (data: SelectionData) => void;
|
|
36
|
+
/** Additional callback configuration options */
|
|
37
|
+
callbacks?: SpotWidgetOptions['callbacks'];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Methods available on the ReactSpotWidget component ref
|
|
41
|
+
*/
|
|
42
|
+
export interface ReactSpotWidgetRef {
|
|
43
|
+
/** Update the quote with new request data */
|
|
44
|
+
updateQuote: (newQuoteRequestData: SpotWidgetOptions['quoteRequestData']) => Promise<boolean>;
|
|
45
|
+
/** Get the current user selection */
|
|
46
|
+
getSelection: () => SelectionData | null;
|
|
47
|
+
/** Validate that the user has made a selection */
|
|
48
|
+
validateSelection: () => boolean;
|
|
49
|
+
/** Destroy the widget instance and clean up resources */
|
|
50
|
+
destroy: () => void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* React component wrapper for the Spot refund guarantee widget.
|
|
54
|
+
*
|
|
55
|
+
* This component provides a React-friendly interface to the Spot Widget,
|
|
56
|
+
* offering refund guarantee options for e-commerce purchases.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* import ReactSpotWidget from '@getspot/spot-widget-react';
|
|
61
|
+
*
|
|
62
|
+
* function MyComponent() {
|
|
63
|
+
* return (
|
|
64
|
+
* <ReactSpotWidget
|
|
65
|
+
* apiConfig={{
|
|
66
|
+
* environment: 'production',
|
|
67
|
+
* partnerId: 'your-partner-id'
|
|
68
|
+
* }}
|
|
69
|
+
* quoteRequestData={{
|
|
70
|
+
* productPrice: 100,
|
|
71
|
+
* productType: 'Trip',
|
|
72
|
+
* // ... other required fields
|
|
73
|
+
* }}
|
|
74
|
+
* onOptIn={(data) => console.log('User opted in:', data)}
|
|
75
|
+
* onOptOut={(data) => console.log('User opted out:', data)}
|
|
76
|
+
* />
|
|
77
|
+
* );
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare const ReactSpotWidget: React.ForwardRefExoticComponent<ReactSpotWidgetProps & React.RefAttributes<ReactSpotWidgetRef>>;
|
|
82
|
+
export default ReactSpotWidget;
|
|
83
|
+
//# sourceMappingURL=ReactSpotWidget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactSpotWidget.d.ts","sourceRoot":"","sources":["../src/ReactSpotWidget.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6D,MAAM,OAAO,CAAC;AAClF,OAAmB,EAAE,KAAK,iBAAiB,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE1G;;GAEG;AACH,UAAU,oBAAqB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,WAAW,CAAC;IACtF,0EAA0E;IAC1E,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC1C,iEAAiE;IACjE,gBAAgB,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;IACxD,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yDAAyD;IACzD,KAAK,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACnC,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,iEAAiE;IACjE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,GAAG,CAAA;KAAE,KAAK,IAAI,CAAC;IACpF,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAClD,gDAAgD;IAChD,SAAS,CAAC,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,6CAA6C;IAC7C,WAAW,EAAE,CAAC,mBAAmB,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9F,qCAAqC;IACrC,YAAY,EAAE,MAAM,aAAa,GAAG,IAAI,CAAC;IACzC,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,OAAO,CAAC;IACjC,yDAAyD;IACzD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,QAAA,MAAM,eAAe,iGAuFnB,CAAC;AAIH,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactSpotWidget.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ReactSpotWidget.test.tsx"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type SpotWidgetOptions, type SelectionData, type Quote } from "@getspot/spot-widget";
|
|
3
|
+
/**
|
|
4
|
+
* Props for the ReactSpotWidget component
|
|
5
|
+
*/
|
|
6
|
+
interface ReactSpotWidgetProps extends Omit<SpotWidgetOptions, 'location' | 'callbacks'> {
|
|
7
|
+
/** Configuration for the Spot API including environment and partner ID */
|
|
8
|
+
apiConfig: SpotWidgetOptions['apiConfig'];
|
|
9
|
+
/** Quote request data containing product and cart information */
|
|
10
|
+
quoteRequestData: SpotWidgetOptions['quoteRequestData'];
|
|
11
|
+
/** Whether to show the payout table. Defaults to true */
|
|
12
|
+
showTable?: boolean;
|
|
13
|
+
/** Whether the widget should be pre-selected for opt-in. Defaults to false */
|
|
14
|
+
optInSelected?: boolean;
|
|
15
|
+
/** Theme customization options for styling the widget */
|
|
16
|
+
theme?: SpotWidgetOptions['theme'];
|
|
17
|
+
/** Callback fired when a quote is successfully retrieved */
|
|
18
|
+
onQuoteRetrieved?: (quote: Quote) => void;
|
|
19
|
+
/** Callback fired when user opts in to the refund guarantee */
|
|
20
|
+
onOptIn?: (data: SelectionData) => void;
|
|
21
|
+
/** Callback fired when user opts out of the refund guarantee */
|
|
22
|
+
onOptOut?: (data: SelectionData) => void;
|
|
23
|
+
/** Callback fired when an error occurs during quote retrieval */
|
|
24
|
+
onError?: (error: {
|
|
25
|
+
message: string;
|
|
26
|
+
status?: number;
|
|
27
|
+
responseBody?: any;
|
|
28
|
+
}) => void;
|
|
29
|
+
/** Callback fired when no matching quote is found */
|
|
30
|
+
onNoMatchingQuote?: (data: {
|
|
31
|
+
status: string;
|
|
32
|
+
data: any;
|
|
33
|
+
}) => void;
|
|
34
|
+
/** Callback fired when user changes their selection (opt-in or opt-out) */
|
|
35
|
+
onSelectionChange?: (data: SelectionData) => void;
|
|
36
|
+
/** Additional callback configuration options */
|
|
37
|
+
callbacks?: SpotWidgetOptions['callbacks'];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Methods available on the ReactSpotWidget component ref
|
|
41
|
+
*/
|
|
42
|
+
export interface ReactSpotWidgetRef {
|
|
43
|
+
/** Update the quote with new request data */
|
|
44
|
+
updateQuote: (newQuoteRequestData: SpotWidgetOptions['quoteRequestData']) => Promise<boolean>;
|
|
45
|
+
/** Get the current user selection */
|
|
46
|
+
getSelection: () => SelectionData | null;
|
|
47
|
+
/** Validate that the user has made a selection */
|
|
48
|
+
validateSelection: () => boolean;
|
|
49
|
+
/** Destroy the widget instance and clean up resources */
|
|
50
|
+
destroy: () => void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* React component wrapper for the Spot refund guarantee widget.
|
|
54
|
+
*
|
|
55
|
+
* This component provides a React-friendly interface to the Spot Widget,
|
|
56
|
+
* offering refund guarantee options for e-commerce purchases.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* import ReactSpotWidget from '@getspot/spot-widget-react';
|
|
61
|
+
*
|
|
62
|
+
* function MyComponent() {
|
|
63
|
+
* return (
|
|
64
|
+
* <ReactSpotWidget
|
|
65
|
+
* apiConfig={{
|
|
66
|
+
* environment: 'production',
|
|
67
|
+
* partnerId: 'your-partner-id'
|
|
68
|
+
* }}
|
|
69
|
+
* quoteRequestData={{
|
|
70
|
+
* productPrice: 100,
|
|
71
|
+
* productType: 'Trip',
|
|
72
|
+
* // ... other required fields
|
|
73
|
+
* }}
|
|
74
|
+
* onOptIn={(data) => console.log('User opted in:', data)}
|
|
75
|
+
* onOptOut={(data) => console.log('User opted out:', data)}
|
|
76
|
+
* />
|
|
77
|
+
* );
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare const ReactSpotWidget: React.ForwardRefExoticComponent<ReactSpotWidgetProps & React.RefAttributes<ReactSpotWidgetRef>>;
|
|
82
|
+
export default ReactSpotWidget;
|
|
83
|
+
//# sourceMappingURL=ReactSpotWidget.d.ts.map
|
package/dist/index.es.js
CHANGED
|
@@ -1,70 +1,72 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
import { jsx as P } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as b, useRef as R, useImperativeHandle as p, useEffect as z } from "react";
|
|
3
|
+
import A from "@getspot/spot-widget";
|
|
4
|
+
const B = b(({
|
|
4
5
|
apiConfig: m,
|
|
5
|
-
quoteRequestData:
|
|
6
|
-
showTable:
|
|
6
|
+
quoteRequestData: i,
|
|
7
|
+
showTable: y = !0,
|
|
7
8
|
optInSelected: W = !1,
|
|
8
|
-
theme:
|
|
9
|
-
callbacks:
|
|
10
|
-
onQuoteRetrieved:
|
|
11
|
-
onOptIn:
|
|
12
|
-
onOptOut:
|
|
9
|
+
theme: a,
|
|
10
|
+
callbacks: v = {},
|
|
11
|
+
onQuoteRetrieved: c,
|
|
12
|
+
onOptIn: s,
|
|
13
|
+
onOptOut: t,
|
|
13
14
|
onError: f,
|
|
14
15
|
onNoMatchingQuote: d,
|
|
15
16
|
onSelectionChange: e,
|
|
16
|
-
...
|
|
17
|
-
},
|
|
18
|
-
const l =
|
|
19
|
-
...
|
|
20
|
-
...
|
|
21
|
-
...
|
|
17
|
+
...w
|
|
18
|
+
}, j) => {
|
|
19
|
+
const l = R(null), u = R(null), k = {
|
|
20
|
+
...v,
|
|
21
|
+
...c && { onQuoteRetrieved: c },
|
|
22
|
+
...s && {
|
|
22
23
|
onOptIn: (r) => {
|
|
23
|
-
|
|
24
|
+
s == null || s(r), e == null || e(r);
|
|
24
25
|
}
|
|
25
26
|
},
|
|
26
|
-
...
|
|
27
|
+
...t && {
|
|
27
28
|
onOptOut: (r) => {
|
|
28
|
-
|
|
29
|
+
t == null || t(r), e == null || e(r);
|
|
29
30
|
}
|
|
30
31
|
},
|
|
31
32
|
...f && { onError: f },
|
|
32
|
-
...d && {
|
|
33
|
-
},
|
|
33
|
+
...d && { noMatchingQuote: d }
|
|
34
|
+
}, H = {
|
|
34
35
|
apiConfig: m,
|
|
35
|
-
quoteRequestData:
|
|
36
|
-
showTable:
|
|
36
|
+
quoteRequestData: i,
|
|
37
|
+
showTable: y,
|
|
37
38
|
optInSelected: W,
|
|
38
|
-
theme:
|
|
39
|
-
callbacks:
|
|
40
|
-
...
|
|
39
|
+
theme: a,
|
|
40
|
+
callbacks: k,
|
|
41
|
+
...w
|
|
41
42
|
};
|
|
42
|
-
return
|
|
43
|
+
return p(j, () => ({
|
|
43
44
|
updateQuote: (r) => {
|
|
44
|
-
var
|
|
45
|
-
return (
|
|
45
|
+
var x;
|
|
46
|
+
return ((x = u.current) == null ? void 0 : x.updateQuote(r)) ?? Promise.resolve(!1);
|
|
46
47
|
},
|
|
47
48
|
getSelection: () => {
|
|
48
49
|
var r;
|
|
49
|
-
return (r = u.current) == null ? void 0 : r.getSelection();
|
|
50
|
+
return ((r = u.current) == null ? void 0 : r.getSelection()) ?? null;
|
|
50
51
|
},
|
|
51
52
|
validateSelection: () => {
|
|
52
53
|
var r;
|
|
53
|
-
return (r = u.current) == null ? void 0 : r.validateSelection();
|
|
54
|
+
return ((r = u.current) == null ? void 0 : r.validateSelection()) ?? !1;
|
|
54
55
|
},
|
|
55
56
|
destroy: () => {
|
|
56
57
|
u.current && (u.current.destroy(), u.current = null);
|
|
57
58
|
}
|
|
58
|
-
})),
|
|
59
|
+
})), z(() => {
|
|
59
60
|
if (l.current)
|
|
60
|
-
return u.current && u.current.destroy(), u.current = new
|
|
61
|
+
return u.current && u.current.destroy(), u.current = new A({
|
|
61
62
|
location: l.current,
|
|
62
|
-
...
|
|
63
|
+
...H
|
|
63
64
|
}), () => {
|
|
64
65
|
u.current && (u.current.destroy(), u.current = null);
|
|
65
66
|
};
|
|
66
|
-
}, [m,
|
|
67
|
+
}, [m, i, y, W, a, v, c, s, t, f, d, e, w]), /* @__PURE__ */ P("div", { ref: l });
|
|
67
68
|
});
|
|
69
|
+
B.displayName = "ReactSpotWidget";
|
|
68
70
|
export {
|
|
69
|
-
|
|
71
|
+
B as default
|
|
70
72
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(u,t){typeof exports=="object"&&typeof module<"u"?module.exports=t(require("react/jsx-runtime"),require("react"),require("@getspot/spot-widget")):typeof define=="function"&&define.amd?define(["react/jsx-runtime","react","@getspot/spot-widget"],t):(u=typeof globalThis<"u"?globalThis:u||self,u.ReactSpotWidget=t(u.React,u.React,u.SpotWidget))})(this,function(u,t,n){"use strict";const p=t.forwardRef(({apiConfig:y,quoteRequestData:R,showTable:x=!0,optInSelected:W=!1,theme:j,callbacks:w={},onQuoteRetrieved:i,onOptIn:d,onOptOut:f,onError:c,onNoMatchingQuote:l,onSelectionChange:s,...v},q)=>{const m=t.useRef(null),r=t.useRef(null),H={...w,...i&&{onQuoteRetrieved:i},...d&&{onOptIn:e=>{d==null||d(e),s==null||s(e)}},...f&&{onOptOut:e=>{f==null||f(e),s==null||s(e)}},...c&&{onError:c},...l&&{noMatchingQuote:l}},P={apiConfig:y,quoteRequestData:R,showTable:x,optInSelected:W,theme:j,callbacks:H,...v};return t.useImperativeHandle(q,()=>({updateQuote:e=>{var k;return((k=r.current)==null?void 0:k.updateQuote(e))??Promise.resolve(!1)},getSelection:()=>{var e;return((e=r.current)==null?void 0:e.getSelection())??null},validateSelection:()=>{var e;return((e=r.current)==null?void 0:e.validateSelection())??!1},destroy:()=>{r.current&&(r.current.destroy(),r.current=null)}})),t.useEffect(()=>{if(m.current)return r.current&&r.current.destroy(),r.current=new n({location:m.current,...P}),()=>{r.current&&(r.current.destroy(),r.current=null)}},[y,R,x,W,j,w,i,d,f,c,l,s,v]),u.jsx("div",{ref:m})});return p.displayName="ReactSpotWidget",p});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setupTests.d.ts","sourceRoot":"","sources":["../src/setupTests.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,23 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getspot/spot-widget-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"main": "dist/index.umd.js",
|
|
8
8
|
"module": "dist/index.es.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"CHANGELOG.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "vite build && npx -p typescript tsc --emitDeclarationOnly && cp dist/ReactSpotWidget.d.ts dist/index.d.ts",
|
|
17
|
+
"docs": "npx -p typedoc -p typedoc-plugin-markdown typedoc --plugin typedoc-plugin-markdown --out docs src/ReactSpotWidget.tsx",
|
|
18
|
+
"docs:readme": "npm run docs && node scripts/generate-readme.js",
|
|
19
|
+
"test": "jest",
|
|
20
|
+
"test:watch": "jest --watch",
|
|
21
|
+
"test:coverage": "jest --coverage",
|
|
22
|
+
"test:ci": "jest --ci --coverage --watchAll=false",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
9
25
|
"dependencies": {
|
|
10
|
-
"@getspot/spot-widget": "
|
|
26
|
+
"@getspot/spot-widget": "workspace:*"
|
|
11
27
|
},
|
|
12
28
|
"peerDependencies": {
|
|
13
|
-
"react": "
|
|
14
|
-
"react-dom": "
|
|
29
|
+
"react": ">=18.0.0",
|
|
30
|
+
"react-dom": ">=18.0.0",
|
|
31
|
+
"@types/react": ">=18.0.0",
|
|
32
|
+
"@types/react-dom": ">=18.0.0"
|
|
15
33
|
},
|
|
16
34
|
"devDependencies": {
|
|
17
35
|
"vite": "^5.0.0",
|
|
18
|
-
"@vitejs/plugin-react": "^4.0.0"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
36
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
37
|
+
"typescript": "~5.4.0",
|
|
38
|
+
"@types/react": "^18.0.0",
|
|
39
|
+
"@types/react-dom": "^18.0.0",
|
|
40
|
+
"typedoc": "^0.28.0",
|
|
41
|
+
"typedoc-plugin-markdown": "^4.0.0",
|
|
42
|
+
"jest": "^29.0.0",
|
|
43
|
+
"@testing-library/react": "^14.0.0",
|
|
44
|
+
"@testing-library/jest-dom": "^6.0.0",
|
|
45
|
+
"@testing-library/user-event": "^14.0.0",
|
|
46
|
+
"@types/jest": "^29.0.0",
|
|
47
|
+
"jest-environment-jsdom": "^29.0.0",
|
|
48
|
+
"react": "^18.0.0",
|
|
49
|
+
"react-dom": "^18.0.0",
|
|
50
|
+
"ts-jest": "^29.0.0"
|
|
22
51
|
}
|
|
23
|
-
}
|
|
52
|
+
}
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @getspot/spot-widget-react@1.4.0 build /builds/getspot/spot-widget/packages/react
|
|
3
|
-
> vite build
|
|
4
|
-
|
|
5
|
-
[33mThe CJS build of Vite's Node API is deprecated. See https://vite.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.[39m
|
|
6
|
-
[36mvite v5.4.18 [32mbuilding for production...[36m[39m
|
|
7
|
-
transforming...
|
|
8
|
-
[32m✓[39m 1 modules transformed.
|
|
9
|
-
No name was provided for external module "@getspot/spot-widget" in "output.globals" – guessing "SpotWidget".
|
|
10
|
-
rendering chunks...
|
|
11
|
-
computing gzip size...
|
|
12
|
-
[2mdist/[22m[36mindex.umd.js [39m[1m[2m1.41 kB[22m[1m[22m[2m │ gzip: 0.65 kB[22m
|
|
13
|
-
[2mdist/[22m[36mindex.es.js [39m[1m[2m1.71 kB[22m[1m[22m[2m │ gzip: 0.68 kB[22m
|
|
14
|
-
[32m✓ built in 140ms[39m
|
package/src/ReactSpotWidget.jsx
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useImperativeHandle, forwardRef } from "react";
|
|
2
|
-
import SpotWidget from "@getspot/spot-widget";
|
|
3
|
-
|
|
4
|
-
const ReactSpotWidget = forwardRef(({
|
|
5
|
-
apiConfig,
|
|
6
|
-
quoteRequestData,
|
|
7
|
-
showTable = true,
|
|
8
|
-
optInSelected = false,
|
|
9
|
-
theme,
|
|
10
|
-
callbacks = {},
|
|
11
|
-
onQuoteRetrieved,
|
|
12
|
-
onOptIn,
|
|
13
|
-
onOptOut,
|
|
14
|
-
onError,
|
|
15
|
-
onNoMatchingQuote,
|
|
16
|
-
onSelectionChange,
|
|
17
|
-
...otherOptions
|
|
18
|
-
}, ref) => {
|
|
19
|
-
const widgetRef = useRef(null);
|
|
20
|
-
const spotWidgetInstance = useRef(null);
|
|
21
|
-
|
|
22
|
-
// Merge callbacks from props with callbacks object
|
|
23
|
-
const mergedCallbacks = {
|
|
24
|
-
...callbacks,
|
|
25
|
-
...(onQuoteRetrieved && { onQuoteRetrieved }),
|
|
26
|
-
...(onOptIn && {
|
|
27
|
-
onOptIn: (data) => {
|
|
28
|
-
onOptIn?.(data);
|
|
29
|
-
onSelectionChange?.(data);
|
|
30
|
-
}
|
|
31
|
-
}),
|
|
32
|
-
...(onOptOut && {
|
|
33
|
-
onOptOut: (data) => {
|
|
34
|
-
onOptOut?.(data);
|
|
35
|
-
onSelectionChange?.(data);
|
|
36
|
-
}
|
|
37
|
-
}),
|
|
38
|
-
...(onError && { onError }),
|
|
39
|
-
...(onNoMatchingQuote && { onNoMatchingQuote }),
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const options = {
|
|
43
|
-
apiConfig,
|
|
44
|
-
quoteRequestData,
|
|
45
|
-
showTable,
|
|
46
|
-
optInSelected,
|
|
47
|
-
theme,
|
|
48
|
-
callbacks: mergedCallbacks,
|
|
49
|
-
...otherOptions,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
useImperativeHandle(ref, () => ({
|
|
53
|
-
updateQuote: (newQuoteRequestData) => {
|
|
54
|
-
return spotWidgetInstance.current?.updateQuote(newQuoteRequestData);
|
|
55
|
-
},
|
|
56
|
-
getSelection: () => {
|
|
57
|
-
return spotWidgetInstance.current?.getSelection();
|
|
58
|
-
},
|
|
59
|
-
validateSelection: () => {
|
|
60
|
-
return spotWidgetInstance.current?.validateSelection();
|
|
61
|
-
},
|
|
62
|
-
destroy: () => {
|
|
63
|
-
if (spotWidgetInstance.current) {
|
|
64
|
-
spotWidgetInstance.current.destroy();
|
|
65
|
-
spotWidgetInstance.current = null;
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
}));
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
if (widgetRef.current) {
|
|
72
|
-
if (spotWidgetInstance.current) {
|
|
73
|
-
spotWidgetInstance.current.destroy();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
spotWidgetInstance.current = new SpotWidget({
|
|
77
|
-
location: widgetRef.current,
|
|
78
|
-
...options,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return () => {
|
|
82
|
-
if (spotWidgetInstance.current) {
|
|
83
|
-
spotWidgetInstance.current.destroy();
|
|
84
|
-
spotWidgetInstance.current = null;
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}, [apiConfig, quoteRequestData, showTable, optInSelected, theme, callbacks, onQuoteRetrieved, onOptIn, onOptOut, onError, onNoMatchingQuote, onSelectionChange, otherOptions]);
|
|
89
|
-
|
|
90
|
-
return <div ref={widgetRef}></div>;
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
export default ReactSpotWidget;
|
package/vite.config.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "vite";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
build: {
|
|
6
|
-
outDir: "dist",
|
|
7
|
-
lib: {
|
|
8
|
-
entry: path.resolve(__dirname, "src/ReactSpotWidget.jsx"),
|
|
9
|
-
name: "ReactSpotWidget",
|
|
10
|
-
fileName: (format) => `index.${format}.js`,
|
|
11
|
-
formats: ["umd", "es"],
|
|
12
|
-
},
|
|
13
|
-
rollupOptions: {
|
|
14
|
-
external: ["react", "react-dom", "@getspot/spot-widget"],
|
|
15
|
-
output: {
|
|
16
|
-
globals: {
|
|
17
|
-
react: "React",
|
|
18
|
-
"react-dom": "ReactDOM",
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
});
|