@buddy-technology/offer-component 0.2.5

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/API.md ADDED
@@ -0,0 +1,132 @@
1
+ ## Functions
2
+
3
+ <dl>
4
+ <dt><a href="#BuddyOfferElement">BuddyOfferElement(options)</a> ⇒ <code><a href="#BuddyOfferElementProps">FunctionComponent.&lt;BuddyOfferElementProps&gt;</a></code></dt>
5
+ <dd></dd>
6
+ </dl>
7
+
8
+ ## Typedefs
9
+
10
+ <dl>
11
+ <dt><a href="#DataObject">DataObject</a> : <code>Object</code></dt>
12
+ <dd></dd>
13
+ <dt><a href="#AddToCartFunction">AddToCartFunction</a> ⇒ <code><a href="#DataObject">DataObject</a></code></dt>
14
+ <dd><p>A callback to be called with a payload object when users opt into the offer.</p>
15
+ </dd>
16
+ <dt><a href="#RemoveFromCartFunction">RemoveFromCartFunction</a> ⇒ <code><a href="#DataObject">DataObject</a></code></dt>
17
+ <dd><p>A callback to be called with a payload object when users opt out of the offer.</p>
18
+ </dd>
19
+ <dt><a href="#OverridesObject">OverridesObject</a> : <code>Object</code></dt>
20
+ <dd></dd>
21
+ <dt><a href="#ThemeObject">ThemeObject</a> : <code>Object</code></dt>
22
+ <dd></dd>
23
+ <dt><a href="#BuddyOfferElementProps">BuddyOfferElementProps</a> : <code>Object</code></dt>
24
+ <dd></dd>
25
+ </dl>
26
+
27
+ <a name="BuddyOfferElement"></a>
28
+
29
+ ## BuddyOfferElement(options) ⇒ [<code>FunctionComponent.&lt;BuddyOfferElementProps&gt;</code>](#BuddyOfferElementProps)
30
+ **Kind**: global function
31
+
32
+ | Param | Type |
33
+ | --- | --- |
34
+ | options | [<code>BuddyOfferElementProps</code>](#BuddyOfferElementProps) |
35
+
36
+ <a name="DataObject"></a>
37
+
38
+ ## DataObject : <code>Object</code>
39
+ **Kind**: global typedef
40
+ **Properties**
41
+
42
+ | Name | Type | Description |
43
+ | --- | --- | --- |
44
+ | [customer] | <code>Object</code> | customer data |
45
+ | [policy] | <code>Object</code> | policy data |
46
+
47
+ <a name="AddToCartFunction"></a>
48
+
49
+ ## AddToCartFunction ⇒ [<code>DataObject</code>](#DataObject)
50
+ A callback to be called with a payload object when users opt into the offer.
51
+
52
+ **Kind**: global typedef
53
+ **Returns**: [<code>DataObject</code>](#DataObject) - the policy payload
54
+ <a name="RemoveFromCartFunction"></a>
55
+
56
+ ## RemoveFromCartFunction ⇒ [<code>DataObject</code>](#DataObject)
57
+ A callback to be called with a payload object when users opt out of the offer.
58
+
59
+ **Kind**: global typedef
60
+ **Returns**: [<code>DataObject</code>](#DataObject) - the policy payload
61
+ <a name="OverridesObject"></a>
62
+
63
+ ## OverridesObject : <code>Object</code>
64
+ **Kind**: global typedef
65
+ **Properties**
66
+
67
+ | Name | Type | Description |
68
+ | --- | --- | --- |
69
+ | [webFonts] | <code>Array.&lt;String&gt;</code> | array of url strings linking to web fonts. |
70
+ | [styles] | <code>Object</code> | Object Styles object for overriding any css. |
71
+ | [colors] | <code>Object</code> | Object for overriding individual color options. |
72
+
73
+ <a name="ThemeObject"></a>
74
+
75
+ ## ThemeObject : <code>Object</code>
76
+ **Kind**: global typedef
77
+ **Properties**
78
+
79
+ | Name | Type | Default | Description |
80
+ | --- | --- | --- | --- |
81
+ | [baseTheme] | <code>String</code> | <code>&#x27;base&#x27;</code> | The base theme to style with. Can be used simply as a started and overwritten. |
82
+ | [palette] | <code>String</code> | <code>&#x27;base&#x27;</code> | The base color scheme to style with. Can be used simply as a started and overwritten. |
83
+ | [overrides] | [<code>OverridesObject</code>](#OverridesObject) | | |
84
+
85
+ **Example**
86
+ ```js
87
+ const theme = {
88
+ baseTheme: 'base',
89
+ palette: 'buddy',
90
+ overrides: {
91
+ webFonts: [
92
+ 'https://fonts.googleapis.com/css2?family=Rubik:wght@700&display=swap',
93
+ ],
94
+ styles: {
95
+ h1: {
96
+ color: 'var(--color-text-primary)',
97
+ fontFamily: 'Rubik, sans-serif',
98
+ fontSize: '2rem',
99
+ '@media (min-width: 992px)': {
100
+ fontSize: '3rem',
101
+ },
102
+ '&:hover': {
103
+ boxShadow: 'none',
104
+ backgroundColor: '#FBF9EF',
105
+ },
106
+ },
107
+ body: { color: '#0A242D' },
108
+ '.input-text': {
109
+ border: 'none',
110
+ color: '#333333',
111
+ },
112
+ },
113
+ colors: { textPrimary: '#0A242D' },
114
+ },
115
+ };
116
+ ```
117
+ <a name="BuddyOfferElementProps"></a>
118
+
119
+ ## BuddyOfferElementProps : <code>Object</code>
120
+ **Kind**: global typedef
121
+ **Properties**
122
+
123
+ | Name | Type | Default | Description |
124
+ | --- | --- | --- | --- |
125
+ | ion | <code>String</code> | | The ion id for the offering. |
126
+ | partnerID | <code>String</code> | | The partner ID required for instantiating the Offer |
127
+ | [viewType] | <code>String</code> | <code>&quot;paginated&quot;</code> | establishes how the offer should display to the user. One of: 'paginated', 'single-form' or 'offer-only.' |
128
+ | [data] | [<code>DataObject</code>](#DataObject) | | Any customer or policy data to pre-fill the offer with. Refer to your individual ION for data structure. |
129
+ | [onAddToCart] | [<code>AddToCartFunction</code>](#AddToCartFunction) | | - |
130
+ | [onRemoveFromCart] | [<code>RemoveFromCartFunction</code>](#RemoveFromCartFunction) | | |
131
+ | [theme] | [<code>ThemeObject</code>](#ThemeObject) | | |
132
+
package/CHANGELOG.md ADDED
@@ -0,0 +1,78 @@
1
+ # CHANGELOG
2
+
3
+ ### v0.2.5
4
+ 2022-08-09
5
+
6
+ - remove pre-release tags
7
+ ---
8
+
9
+
10
+ ### v0.2.4
11
+ 2022-08-09
12
+
13
+ - making public
14
+ ---
15
+
16
+
17
+ ### v0.2.2
18
+ 2022-07-28
19
+
20
+ - added error catch for stage enums
21
+ - few more console warnings for users
22
+ - test-app to use DEV stage and buddytest partner
23
+ ---
24
+
25
+
26
+ ### v0.2.1
27
+ 2022-07-27
28
+
29
+ - moved unneeded deps into devDeps and peerDeps to not cause react conflicts
30
+ - added a catch for a non-enum stage being passed
31
+ ---
32
+
33
+
34
+ ### v0.1.12
35
+ 2022-05-13
36
+
37
+ - Removed crossOrigin
38
+ ---
39
+
40
+
41
+ ### v0.1.11
42
+ 2022-05-13
43
+
44
+ - The crossOrigin thing
45
+ ---
46
+
47
+
48
+ ### v0.1.10
49
+ 2022-05-13
50
+
51
+ - Version number change
52
+ ---
53
+
54
+
55
+ ### v0.1.9
56
+ 2022-05-13
57
+
58
+ - Added crossDomain to script tag
59
+ ---
60
+
61
+
62
+ ### v0.1.8
63
+ 2022-05-11
64
+
65
+ - docs
66
+ ---
67
+
68
+
69
+ ### v0.1.7
70
+ 2022-05-11
71
+
72
+ - fix build script
73
+ - fix gh workflow
74
+ - add changelog
75
+ - add env urls
76
+ - check for required props in loadScript
77
+ ---
78
+
package/README.md ADDED
@@ -0,0 +1,295 @@
1
+ # Buddy Offer Component
2
+ ---
3
+ React component for adding [Buddy's](https://buddy.insure) Insurance Gateway
4
+
5
+ ## Requirements
6
+ Contact [Buddy](mailto:partners@buddy.insure) to obtain a `partnerID` and compatible `ions` for your specific use case.
7
+
8
+ ---
9
+ ## Installation
10
+ Install it from npm and include it in your React build process
11
+
12
+ `npm install @buddy-technology/offer-component`
13
+ or
14
+ `yarn add @buddy-technology/offer-component`
15
+
16
+ ---
17
+ ## Basic Usage
18
+ ```javascript
19
+ import React from 'react';
20
+ import BuddyOfferElement from '@buddy-technology/offer-component';
21
+
22
+ function App() {
23
+ return (
24
+ <div id="app">
25
+ <h1>My App</h1>
26
+ <BuddyOfferElement ion="MY_ION" partnerID="my-partner-id" stage="staging" />
27
+ </div>
28
+ );
29
+ }
30
+ export default App;
31
+ ```
32
+ ---
33
+ ## Props
34
+
35
+ | Option Name | Type | Default | Description
36
+ | --- | --- | --- | --- |
37
+ | *partnerID | string | | The partnerID assigned to you from Buddy |
38
+ | *ion | string | | The ion ID for the product to be presented |
39
+ | stage | enum | "STAGING" | One of: `"STAGING"`, `"PRODUCTION"`. Set to `"STAGING"` (default) for testing and development. |
40
+ | viewType | enum | `"paginated"` | Sets how the offer element is displayed. One of: `"paginated"`, `"single-form"`, `"offer-only"`. _See [View types](#view-types) for details._|
41
+ | includeCheckout | boolean | `true` | Toggles whether to render the secure checkout. |
42
+ | onAddToCart | function | | The callback fired when a user opts into an offer. See Buddy's Partner API docs for details on how to complete the transaction.|
43
+ | onRemoveCart | function | | The callback fired when a user opts out of the offer. |
44
+ | data | object | | Customer or policy data to pre-load the offer with. Refer to specific ION documentation for the data structure. |
45
+ | theme | object | |The [theme](#styling) object for passing in styles and color palettes |
46
+ _*required_
47
+
48
+ ---
49
+ ## View Types
50
+
51
+ Buddy's Offer Element comes in three different view types:
52
+
53
+ - __paginated__ (default) - A paginated form where users click through to the next section.
54
+ - __single form__ - A single form where all fields are displayed in the same, scrollable view.
55
+ - __offer only__ - Hide all the form fields and only display the insurance offer with the quote. Using this mode requires collecting all necessary data and a payment integration (See Buddy's [Partner API docs](https://buddyinsurance.stoplight.io/docs/partner-api-documentation/ZG9jOjE1NDc1MzQx)).
56
+
57
+ ---
58
+ ## Checkout
59
+ ### For client-side only apps
60
+
61
+ The embed widget's default mode includes a full checkout. Customers can go through the entire flow through to a secure purchase and policies will be delivered directly to their inbox.
62
+
63
+ To explicitly set the widget to include checkout, set the `includeCheckout` property to `true`.
64
+ ### For full stack apps
65
+
66
+ In server side and full-stack applications where customers are already purchasing other products, it's best to exclude the secure checkout built into the widget so customers can purchase their insurance alongside any other products as part of a single transaction. A backend integration (See Buddy's [Partner API docs](https://buddyinsurance.stoplight.io/docs/partner-api-documentation/ZG9jOjE1NDc1MzQx)) is required in order to complete the transaction.
67
+
68
+ To use this mode, set `includeCheckout` to `false`, or set viewType to `offer-only`.
69
+
70
+ When users opt in, the `onAddToCart` callback is fired with the completed application object. If a user opts out, `onRemoveFromCart` will be called.
71
+
72
+ ```javascript
73
+ import React from 'react';
74
+ import BuddyOfferElement from '@buddy-technology/offer-component';
75
+
76
+ function App() {
77
+ const handleAddToCart = (payload) => {
78
+ console.log(payload)
79
+ };
80
+
81
+ const handleRemoveFromCart = (payload) => {
82
+ console.log(payload)
83
+ };
84
+
85
+ return (
86
+ <div id="app">
87
+ <h1>My App</h1>
88
+ <BuddyOfferElement
89
+ ion="MY_ION"
90
+ partnerID="my-partner-id"
91
+ viewType="offer-only"
92
+ onAddToCart={handleAddToCart}
93
+ onRemoveFromCart={handleRemoveFromCart}
94
+ stage="PRODUCTION"
95
+ />
96
+ </div>
97
+ );
98
+ }
99
+ export default App;
100
+ ```
101
+ ---
102
+ ## Pre-filling Data
103
+
104
+
105
+ It's a best practice to pass in any customer and policy data that has already been collected to optimize user experience. Unless viewType is `offer-only`, users will be able to see and edit any data that is passed.
106
+
107
+ Data follows the application object structure. Refer to your specific ION (or offering) documentation.
108
+
109
+ ```javascript
110
+ import React from 'react';
111
+ import BuddyOfferElement from '@buddy-technology/offer-component';
112
+
113
+ function App() {
114
+ const data = {
115
+ customer: {
116
+ firstName: 'customer first name',
117
+ lastName: 'customer last name',
118
+ address: {
119
+ line1: 'street address',
120
+ city: 'city',
121
+ state: 'state',
122
+ zip: 'zip',
123
+ }
124
+ },
125
+ policy: {
126
+ startDate: '12/31/2022',
127
+ }
128
+ };
129
+
130
+ return (
131
+ <div id="app">
132
+ <h1>My App</h1>
133
+ <BuddyOfferElement
134
+ ion="MY_ION"
135
+ partnerID="my-partner-id"
136
+ data={data}
137
+ stage="PRODUCTION"
138
+ />
139
+ </div>
140
+ );
141
+ }
142
+ export default App;
143
+ ```
144
+ ---
145
+ ## Styling
146
+
147
+ Buddy's offer element supports extensive visual customization, allowing you to seamlessly match the design of your site with the theme prop.
148
+
149
+ 1. Pick a base theme and color palette:
150
+ Quickly get running by picking the prebuilt theme and palette that most closely resembles your website.
151
+
152
+ 2. Customize even further by adding [overrides](#overrides):
153
+ Virtually any property can be further customized using [CSS object styling](https://emotion.sh/docs/object-styles).
154
+
155
+ ```javascript
156
+ import React from 'react';
157
+ import BuddyOfferElement from '@buddy-technology/offer-component';
158
+
159
+ function App() {
160
+ const customTheme = {
161
+ baseTheme: "base",
162
+ palette: "buddy",
163
+ overrides: {
164
+ webFonts: [
165
+ 'https://fonts.googleapis.com/css2?family=Rubik:wght@700&display=swap',
166
+ ],
167
+ styles: {
168
+ h1: {
169
+ color: 'var(--color-text-primary)',
170
+ fontFamily: 'Rubik, sans-serif',
171
+ fontSize: '2rem',
172
+ '@media (min-width: 992px)': {
173
+ fontSize: '3rem',
174
+ },
175
+ '&:hover': {
176
+ boxShadow: 'none',
177
+ backgroundColor: '#FBF9EF',
178
+ },
179
+ },
180
+ body: { color: '#0A242D' },
181
+ '.input-text': {
182
+ border: 'none',
183
+ color: '#333333',
184
+ }
185
+ },
186
+ colors: { textPrimary: '#0A242D' },
187
+ },
188
+ };
189
+
190
+ return (
191
+ <div id="app">
192
+ <h1>My App</h1>
193
+ <BuddyOfferElement
194
+ ion="MY_ION"
195
+ partnerID="my-partner-id"
196
+ theme={customTheme}
197
+ stage="PRODUCTION"
198
+ />
199
+ </div>
200
+ );
201
+ }
202
+ export default App;
203
+ ```
204
+ ### Base Themes
205
+
206
+ We currently offer two baseThemes to start with (with more coming soon!) If you plan on doing very extensive overrides, set the baseTheme to `'none'`.
207
+
208
+ #### Available themes:
209
+
210
+ `'base`':
211
+ ![base_theme.png](https://stoplight.io/api/v1/projects/cHJqOjEwMDA3NA/images/P1s10VpVT64)
212
+
213
+ `'buddy'`:
214
+ ![buddy_theme.png](https://stoplight.io/api/v1/projects/cHJqOjEwMDA3NA/images/ker3veyTXrs)
215
+
216
+ ### Color Palettes
217
+ We currently offer two color palettes to start with (with more coming soon!).
218
+
219
+ __Base__:
220
+
221
+ primary: '#000000',
222
+ secondary: '#254E70',
223
+ tertiary: '#848484',
224
+ negative: '#e45b78',
225
+ positive: '#A3D9B1',
226
+ textPrimary: '#000',
227
+ textSecondary: '848484',
228
+ backgroundPrimary: '#fff',
229
+ backgroundSecondary: '#7d8387',
230
+
231
+ __Buddy__:
232
+
233
+ primary: '#DC5648',
234
+ secondary: '#0A242D99',
235
+ tertiary: '#46460E33',
236
+ negative: '#D62719',
237
+ positive: '#A3D9B1',
238
+ textPrimary: '#0A242D',
239
+ textSecondary: '#0A242D99',
240
+ backgroundPrimary: 'linear-gradient(#D6F1F2, #F8F6D2)',
241
+ backgroundSecondary: '#FBF9EF',
242
+
243
+ ### Overrides
244
+
245
+ The overrides object consists of three major properties:
246
+
247
+ `webFonts`: An array of string urls for web fonts. _Note: if using web fonts, it is still necessary to set the `fontFamily` property on any elements using that particular webfont._
248
+
249
+ `styles`: A CSS styles object with any CSS overrides on a given theme. Styles will overwrite existing theme styles. Media queries and child selectors can be accessed as a string key name, and nested within a given selector (see the example above). [Component classes](#classes-and-components) can also be manipulated within the styles object.
250
+
251
+ `colors`: A colors object will override any of the color properties in the table below:
252
+
253
+ | Key | Where its used |
254
+ | --- | --- |
255
+ | primary | graphics, call-outs, primary buttons |
256
+ | secondary | focused borders, accents |
257
+ | tertiary | inactive accents |
258
+ | negative | error messages (note: this is currently set to "accessible red." be mindful of accessibility when overriding this property) |
259
+ | positive | success messages |
260
+ | textPrimary | primary body copy |
261
+ | textSecondary | secondary copy, accents |
262
+ | backgroundPrimary | main background (containers) |
263
+ | backgroundSecondary | secondary backgrounds |
264
+
265
+ ### Classes and Components
266
+
267
+ Some components can be directly modified by accessing their class names in the theme object. See the full list of component class names below.
268
+
269
+ ```javascript
270
+ var customTheme = {
271
+ style: {
272
+ h1: { fontSize: 36 },
273
+ '.button-primary': {
274
+ // custom styles
275
+ },
276
+ },
277
+ };
278
+
279
+ ```
280
+
281
+ **Available Classes**
282
+
283
+ - card
284
+ - button-primary
285
+ - button-secondary
286
+ - input-text
287
+ - input-invalid
288
+ - input-select
289
+ - input-label
290
+ - input-label-invalid
291
+ - offer-container
292
+ - field-container
293
+ - view-container
294
+
295
+ ---
package/dist/index.js ADDED
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+
3
+ require("core-js/modules/web.dom-collections.iterator.js");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.default = void 0;
9
+
10
+ require("core-js/modules/es.promise.js");
11
+
12
+ var _react = _interopRequireWildcard(require("react"));
13
+
14
+ var _util = require("./util");
15
+
16
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
17
+
18
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
19
+
20
+ // eslint-disable-next-line no-unused-vars
21
+
22
+ /**
23
+ * @typedef {Object} DataObject
24
+ * @property {Object} [customer] - customer data
25
+ * @property {Object} [policy] - policy data
26
+ */
27
+
28
+ /**
29
+ * @typedef {Function} AddToCartFunction
30
+ * @description A callback to be called with a payload object when users opt into the offer.
31
+ * @returns {DataObject} the policy payload
32
+ */
33
+
34
+ /**
35
+ * @typedef {Function} RemoveFromCartFunction
36
+ * @description A callback to be called with a payload object when users opt out of the offer.
37
+ * @returns {DataObject} the policy payload
38
+ */
39
+
40
+ /**
41
+ * @typedef {Object} OverridesObject
42
+ * @property {Array<String>} [webFonts] - array of url strings linking to web fonts.
43
+ * @property {Object} [styles] - Object Styles object for overriding any css.
44
+ * @property {Object} [colors] - Object for overriding individual color options.
45
+ */
46
+
47
+ /**
48
+ * @typedef {Object} ThemeObject
49
+ * @property {String} [baseTheme='base'] - The base theme to style with. Can be used simply as a started and overwritten.
50
+ * @property {String} [palette='base'] - The base color scheme to style with. Can be used simply as a started and overwritten.
51
+ * @property {OverridesObject} [overrides]
52
+ * @example
53
+ * const theme = {
54
+ * baseTheme: 'base',
55
+ * palette: 'buddy',
56
+ * overrides: {
57
+ * webFonts: [
58
+ * 'https://fonts.googleapis.com/css2?family=Rubik:wght@700&display=swap',
59
+ * ],
60
+ * styles: {
61
+ * h1: {
62
+ * color: 'var(--color-text-primary)',
63
+ * fontFamily: 'Rubik, sans-serif',
64
+ * fontSize: '2rem',
65
+ * '@media (min-width: 992px)': {
66
+ * fontSize: '3rem',
67
+ * },
68
+ * '&:hover': {
69
+ * boxShadow: 'none',
70
+ * backgroundColor: '#FBF9EF',
71
+ * },
72
+ * },
73
+ * body: { color: '#0A242D' },
74
+ * '.input-text': {
75
+ * border: 'none',
76
+ * color: '#333333',
77
+ * },
78
+ * },
79
+ * colors: { textPrimary: '#0A242D' },
80
+ * },
81
+ * };
82
+ */
83
+
84
+ /**
85
+ * @typedef {Object} BuddyOfferElementProps
86
+ * @property {String} ion - The ion id for the offering.
87
+ * @property {String} partnerID - The partner ID required for instantiating the Offer
88
+ * @property {String} [viewType="paginated"] - establishes how the offer should display to the user. One of: 'paginated', 'single-form' or 'offer-only.'
89
+ * @property {DataObject} [data] - Any customer or policy data to pre-fill the offer with. Refer to your individual ION for data structure.
90
+ * @property {AddToCartFunction} [onAddToCart] -
91
+ * @property {RemoveFromCartFunction} [onRemoveFromCart]
92
+ * @property {ThemeObject} [theme]
93
+ */
94
+
95
+ /**
96
+ * @function BuddyOfferElement
97
+ * @param {BuddyOfferElementProps} options
98
+ * @returns {FunctionComponent<BuddyOfferElementProps>}
99
+ */
100
+ function BuddyOfferElement(options) {
101
+ const getScript = async () => {
102
+ try {
103
+ await (0, _util.loadScript)(options);
104
+ } catch (error) {
105
+ // eslint-disable-next-line no-console
106
+ console.error(error);
107
+ }
108
+ };
109
+
110
+ getScript();
111
+ (0, _react.useEffect)(() => {
112
+ (0, _util.updateOffer)(options);
113
+ }, [options]);
114
+ return /*#__PURE__*/_react.default.createElement("div", {
115
+ id: "buddy_offer"
116
+ });
117
+ }
118
+
119
+ var _default = BuddyOfferElement;
120
+ exports.default = _default;
package/dist/util.js ADDED
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.updateOffer = exports.loadScript = exports.findScript = void 0;
7
+
8
+ require("core-js/modules/es.array.includes.js");
9
+
10
+ require("core-js/modules/es.string.trim.js");
11
+
12
+ require("core-js/modules/es.array.reduce.js");
13
+
14
+ require("core-js/modules/es.regexp.exec.js");
15
+
16
+ require("core-js/modules/es.regexp.test.js");
17
+
18
+ require("core-js/modules/es.promise.js");
19
+
20
+ require("core-js/modules/es.string.includes.js");
21
+
22
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
23
+
24
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
25
+
26
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
27
+
28
+ const EXISTING_SCRIPT_MESSAGE = 'Buddy Offer Script is already loaded.';
29
+ const SCRIPTS = {
30
+ LOCAL: {
31
+ URL: 'http://localhost:8008/index.js',
32
+ REGEX: /^http:\/\/localhost:8008\/index\.js\/?(\?.*)?$/
33
+ },
34
+ DEVELOPMENT: {
35
+ URL: 'https://js.buddy.insure/v2/dev/index.js',
36
+ REGEX: /^https:\/\/js\.buddy\.insure\/v2\/staging\/index\.js\/?(\?.*)?$/
37
+ },
38
+ STAGING: {
39
+ URL: 'https://js.buddy.insure/v2/staging/index.js',
40
+ REGEX: /^https:\/\/js\.buddy\.insure\/v2\/staging\/index\.js\/?(\?.*)?$/
41
+ },
42
+ PRODUCTION: {
43
+ URL: 'https://js.buddy.insure/v2/index.js',
44
+ REGEX: /^https:\/\/js\.buddy\.insure\/v2\/index\.js\/?(\?.*)?$/
45
+ }
46
+ };
47
+ const STAGES = Object.keys(SCRIPTS);
48
+ /**
49
+ * @type {Object}
50
+ * @property {string} stage
51
+ */
52
+
53
+ const defaultOptions = {
54
+ stage: 'STAGING'
55
+ };
56
+ /**
57
+ * @function isEmptyOrNil
58
+ * @param {*} value
59
+ * @returns {Boolean}
60
+ */
61
+
62
+ const isEmptyOrNil = value => {
63
+ const isNil = val => [undefined, null].includes(val);
64
+
65
+ const isEmptyStr = str => typeof str === 'string' && str.trim().length === 0;
66
+
67
+ const isEmptyObjOrArr = obj => typeof obj === 'object' && obj.length === undefined && Object.keys(obj).length === 0 || Array.isArray(obj) && obj.length === 0;
68
+
69
+ return isNil(value) || isEmptyStr(value) || isEmptyObjOrArr(value);
70
+ };
71
+ /**
72
+ * @param {Array<String>} propsToCheck
73
+ * @param {Object} obj
74
+ * @returns {(null | String)}
75
+ */
76
+
77
+
78
+ const validatePropertiesOfObject = (propsToCheck, obj) => {
79
+ const emptyProps = propsToCheck.filter(prop => isEmptyOrNil(obj[prop]));
80
+
81
+ if (emptyProps.length) {
82
+ const displayMissingProps = emptyProps.reduce((prev, current, i) => {
83
+ const isLastEl = i === emptyProps.length - 1;
84
+ return isLastEl ? "".concat(prev).concat(current, ".") : "".concat(prev).concat(current, ", ");
85
+ }, '');
86
+ return "The following props are required: ".concat(displayMissingProps);
87
+ }
88
+
89
+ return null;
90
+ };
91
+ /**
92
+ * @function findScript
93
+ * @param {String} stage
94
+ * @returns {(HTMLElement | undefined)}
95
+ */
96
+
97
+
98
+ const findScript = function findScript() {
99
+ let stage = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultOptions.stage;
100
+ const {
101
+ URL,
102
+ REGEX
103
+ } = SCRIPTS[stage];
104
+ const scripts = Array.from(document.querySelectorAll("script[src^=\"".concat(URL, "\"]")));
105
+ const matchedScript = scripts.find(_ref => {
106
+ let {
107
+ src
108
+ } = _ref;
109
+ return REGEX.test(src);
110
+ });
111
+ return matchedScript;
112
+ };
113
+ /**
114
+ * @function updateOffer
115
+ * @param {Object} options
116
+ */
117
+
118
+
119
+ exports.findScript = findScript;
120
+
121
+ const updateOffer = options => {
122
+ if (window.Buddy) {
123
+ try {
124
+ window.Buddy.updateOffer(options);
125
+ } catch (error) {
126
+ // eslint-disable-next-line no-console
127
+ console.error(error);
128
+ }
129
+ }
130
+ };
131
+ /**
132
+ * @function createOffer
133
+ * @param {Object} options
134
+ */
135
+
136
+
137
+ exports.updateOffer = updateOffer;
138
+
139
+ const createOffer = options => {
140
+ if (window.Buddy) {
141
+ try {
142
+ window.Buddy.createOffer(options);
143
+ } catch (error) {
144
+ // eslint-disable-next-line no-console
145
+ console.error(error);
146
+ }
147
+ }
148
+ };
149
+ /**
150
+ * @function injectScript
151
+ * @param {Object} options
152
+ * @returns {HTMLElement}
153
+ */
154
+
155
+
156
+ const injectScript = options => {
157
+ const finalOptions = _objectSpread(_objectSpread({}, defaultOptions), options);
158
+
159
+ const script = document.createElement('script');
160
+ script.src = "".concat(SCRIPTS[finalOptions.stage].URL);
161
+ document.body.appendChild(script);
162
+
163
+ script.onload = () => createOffer(finalOptions);
164
+
165
+ return script;
166
+ };
167
+ /**
168
+ * @type {(Promise | null)}
169
+ */
170
+
171
+
172
+ let scriptPromise = null;
173
+ /**
174
+ * @function loadScript
175
+ * @param {Object} options
176
+ * @returns {Promise}
177
+ */
178
+
179
+ const loadScript = options => {
180
+ // Ensure that we only attempt to script once
181
+ if (scriptPromise !== null) {
182
+ return scriptPromise;
183
+ }
184
+
185
+ scriptPromise = new Promise((resolve, reject) => {
186
+ if (typeof window === 'undefined') {
187
+ // Resolve to null when imported server side. This makes the module
188
+ // safe to import in an isomorphic code base.
189
+ resolve(null);
190
+ return;
191
+ }
192
+
193
+ if (window.Buddy) {
194
+ // eslint-disable-next-line no-console
195
+ console.warn(EXISTING_SCRIPT_MESSAGE);
196
+ resolve(window.Buddy);
197
+ return;
198
+ }
199
+
200
+ try {
201
+ const requiredProps = ['ion', 'partnerID'];
202
+ const missingRequiredProps = validatePropertiesOfObject(requiredProps, options);
203
+
204
+ if (missingRequiredProps) {
205
+ reject(new Error(missingRequiredProps));
206
+ return;
207
+ }
208
+
209
+ if (!(options !== null && options !== void 0 && options.stage)) {
210
+ // eslint-disable-next-line no-console
211
+ console.warn("No stage passed to BuddyOfferElement. Using default stage: ".concat(defaultOptions.stage));
212
+ } // If stage is passed, ensure it is one of the prescribed options.
213
+
214
+
215
+ if (options !== null && options !== void 0 && options.stage && !STAGES.includes(options.stage)) {
216
+ const msg = "The stage prop must be one of: [".concat(STAGES.join(' , '), "], but received \"").concat(options.stage, ".\"");
217
+ reject(new Error(msg));
218
+ return;
219
+ }
220
+
221
+ let script = findScript(options === null || options === void 0 ? void 0 : options.stage);
222
+
223
+ if (script) {
224
+ // eslint-disable-next-line no-console
225
+ console.warn(EXISTING_SCRIPT_MESSAGE);
226
+ } else if (!script) {
227
+ script = injectScript(options);
228
+ }
229
+
230
+ script.addEventListener('load', () => {
231
+ if (window.Buddy) {
232
+ resolve(window.Buddy);
233
+ } else {
234
+ reject(new Error('Buddy Offer Element not available'));
235
+ }
236
+ });
237
+ script.addEventListener('error', () => {
238
+ reject(new Error('Failed to load Buddy Offer Element'));
239
+ });
240
+ } catch (error) {
241
+ reject(error);
242
+ }
243
+ });
244
+ return scriptPromise;
245
+ };
246
+
247
+ exports.loadScript = loadScript;
package/lib/types.d.ts ADDED
@@ -0,0 +1,111 @@
1
+ export = BuddyOfferElement
2
+
3
+ /**
4
+ * @property [customer] - customer data
5
+ * @property [policy] - policy data
6
+ */
7
+ declare type DataObject = {
8
+ customer?: any;
9
+ policy?: any;
10
+ };
11
+
12
+ /**
13
+ * A callback to be called with a payload object when users opt into the offer.
14
+ */
15
+ declare type AddToCartFunction = () => DataObject;
16
+
17
+ /**
18
+ * A callback to be called with a payload object when users opt out of the offer.
19
+ */
20
+ declare type RemoveFromCartFunction = () => DataObject;
21
+
22
+ /**
23
+ * @property [webFonts] - array of url strings linking to web fonts.
24
+ * @property [styles] - Object Styles object for overriding any css.
25
+ * @property [colors] - Object for overriding individual color options.
26
+ */
27
+ declare type OverridesObject = {
28
+ webFonts?: String[];
29
+ styles?: any;
30
+ colors?: any;
31
+ };
32
+
33
+ /**
34
+ * @example
35
+ * const theme = {
36
+ * baseTheme: 'base',
37
+ * palette: 'buddy',
38
+ * overrides: {
39
+ * webFonts: [
40
+ * 'https://fonts.googleapis.com/css2?family=Rubik:wght@700&display=swap',
41
+ * ],
42
+ * styles: {
43
+ * h1: {
44
+ * color: 'var(--color-text-primary)',
45
+ * fontFamily: 'Rubik, sans-serif',
46
+ * fontSize: '2rem',
47
+ * '@media (min-width: 992px)': {
48
+ * fontSize: '3rem',
49
+ * },
50
+ * '&:hover': {
51
+ * boxShadow: 'none',
52
+ * backgroundColor: '#FBF9EF',
53
+ * },
54
+ * },
55
+ * body: { color: '#0A242D' },
56
+ * '.input-text': {
57
+ * border: 'none',
58
+ * color: '#333333',
59
+ * },
60
+ * },
61
+ * colors: { textPrimary: '#0A242D' },
62
+ * },
63
+ * };
64
+ * @property [baseTheme = 'base'] - The base theme to style with. Can be used simply as a started and overwritten.
65
+ * @property [palette = 'base'] - The base color scheme to style with. Can be used simply as a started and overwritten.
66
+ */
67
+ declare type ThemeObject = {
68
+ baseTheme?: string;
69
+ palette?: string;
70
+ overrides?: OverridesObject;
71
+ };
72
+
73
+ /**
74
+ * @property ion - The ion id for the offering.
75
+ * @property partnerID - The partner ID required for instantiating the Offer
76
+ * @property [viewType = paginated] - establishes how the offer should display to the user. One of: 'paginated', 'single-form' or 'offer-only.'
77
+ * @property [data] - Any customer or policy data to pre-fill the offer with. Refer to your individual ION for data structure.
78
+ * @property [onAddToCart] - -
79
+ */
80
+ declare type BuddyOfferElementProps = {
81
+ ion: string;
82
+ partnerID: string;
83
+ viewType?: string;
84
+ data?: DataObject;
85
+ onAddToCart?: AddToCartFunction;
86
+ onRemoveFromCart?: RemoveFromCartFunction;
87
+ theme?: ThemeObject;
88
+ };
89
+
90
+ declare function BuddyOfferElement(options: BuddyOfferElementProps): FunctionComponent<BuddyOfferElementProps>;
91
+
92
+ declare const defaultOptions: {
93
+ stage: string;
94
+ };
95
+
96
+ declare function isEmptyOrNil(value: any): boolean;
97
+
98
+ declare function validatePropertiesOfObject(propsToCheck: String[], obj: any): null | string;
99
+
100
+ declare function findScript(stage: string): HTMLElement | undefined;
101
+
102
+ declare function updateOffer(options: any): void;
103
+
104
+ declare function createOffer(options: any): void;
105
+
106
+ declare function injectScript(options: any): HTMLElement;
107
+
108
+ declare var scriptPromise: Promise | null;
109
+
110
+ declare function loadScript(options: any): Promise;
111
+
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@buddy-technology/offer-component",
3
+ "version": "0.2.5",
4
+ "author": "Buddy Technology",
5
+ "main": "dist/index.js",
6
+ "types": "./lib/types.d.ts",
7
+ "license": "UNLICENSED",
8
+ "private": false,
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git://github.com/Buddy-Technology/offer-component.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Buddy-Technology/offer-component/issues"
15
+ },
16
+ "homepage": "https://github.com/Buddy-Technology/offer-component#readme",
17
+ "scripts": {
18
+ "build": "rm -rf dist && NODE_ENV=production babel src --out-dir dist",
19
+ "dev": "concurrently \"yarn watch:src\" \"cd test-app && PORT=3006 yarn dev\"",
20
+ "deploy": "yarn version",
21
+ "document": "jsdoc2md -c jsdoc.config.json src/index.js > API.md && node createTypesFile",
22
+ "postversion": "yarn document && yarn build && ./deploy.sh",
23
+ "watch:src": "rm -rf dist && NODE_ENV=development babel src --out-dir dist --watch",
24
+ "start": "ESLINT_NO_DEV_ERRORS=true react-scripts start",
25
+ "test": "react-scripts test",
26
+ "eject": "react-scripts eject"
27
+ },
28
+ "eslintConfig": {
29
+ "extends": [
30
+ "react-app",
31
+ "react-app/jest"
32
+ ]
33
+ },
34
+ "browserslist": {
35
+ "production": [
36
+ ">0.2%",
37
+ "not dead",
38
+ "not op_mini all"
39
+ ],
40
+ "development": [
41
+ "last 1 chrome version",
42
+ "last 1 firefox version",
43
+ "last 1 safari version"
44
+ ]
45
+ },
46
+ "devDependencies": {
47
+ "@babel/cli": "^7.17.10",
48
+ "@babel/core": "^7.17.10",
49
+ "@babel/preset-env": "^7.17.10",
50
+ "concurrently": "^7.3.0",
51
+ "core-js": "^3.22.4",
52
+ "eslint-config-airbnb": "^19.0.4",
53
+ "eslint-plugin-import": "^2.26.0",
54
+ "eslint-plugin-jsx-a11y": "^6.5.1",
55
+ "eslint-plugin-react": "^7.29.4",
56
+ "jsdoc": "^3.6.10",
57
+ "jsdoc-to-markdown": "^7.1.1",
58
+ "jsdoc-tsimport-plugin": "^1.0.5",
59
+ "react": "^18.1.0",
60
+ "react-scripts": "5.0.1",
61
+ "regenerator-runtime": "^0.13.9",
62
+ "tsd-jsdoc": "^2.5.0"
63
+ },
64
+ "peerDependencies": {
65
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
66
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
67
+ }
68
+ }