@stigg/react-sdk 3.0.2 → 4.1.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.
@@ -6,6 +6,7 @@ export declare const getResolvedTheme: (customizedTheme?: {
6
6
  backgroundPaper?: string | undefined;
7
7
  backgroundHighlight?: string | undefined;
8
8
  backgroundSection?: string | undefined;
9
+ backgroundButton?: string | undefined;
9
10
  outlinedBorder?: string | undefined;
10
11
  outlinedRestingBorder?: string | undefined;
11
12
  outlinedHoverBackground?: string | undefined;
@@ -11,6 +11,7 @@ export declare type StiggTheme = {
11
11
  backgroundPaper: string;
12
12
  backgroundHighlight: string;
13
13
  backgroundSection: string;
14
+ backgroundButton: string;
14
15
  outlinedBorder: string;
15
16
  outlinedRestingBorder: string;
16
17
  outlinedHoverBackground: string;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.0.2",
2
+ "version": "4.1.0",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -85,7 +85,7 @@
85
85
  "@emotion/react": "^11.10.5",
86
86
  "@emotion/styled": "^11.10.5",
87
87
  "@mui/material": "^5.10.13",
88
- "@stigg/js-client-sdk": "2.9.1",
88
+ "@stigg/js-client-sdk": "2.15.0",
89
89
  "@types/styled-components": "^5.1.26",
90
90
  "classnames": "^2.3.2",
91
91
  "color": "^4.2.3",
@@ -33,7 +33,7 @@ export function getResolvedCustomerPortalLocalize(
33
33
  viewInvoiceHistory: 'View invoice history',
34
34
  editPaymentDetails: 'Edit',
35
35
  paywallSectionTitle: 'Plans',
36
- cancelScheduledUpdatesButtonTitle: 'Cancel update',
36
+ cancelScheduledUpdatesButtonTitle: 'Cancel',
37
37
  };
38
38
  return merge(customerPortalDefaultLocalization, localizeOverride);
39
39
  }
@@ -10,8 +10,6 @@ const PlanEntitlementsContainer = styled.div`
10
10
  display: flex;
11
11
  flex-direction: column;
12
12
  gap: 16px;
13
- margin-top: 33px;
14
- margin-bottom: 60px;
15
13
  `;
16
14
 
17
15
  function getTitle(plan: PaywallPlan, paywallLocale: PaywallLocalization) {
@@ -32,7 +32,7 @@ const PlanOfferingContainer = styled.div<{ $isHighlighted: boolean; $isCurrentPl
32
32
  `;
33
33
 
34
34
  const PlanHeader = styled(Typography)`
35
- padding-bottom: 16px;
35
+ padding-bottom: 8px;
36
36
  `;
37
37
 
38
38
  const PlanPriceContainer = styled(Typography)`
@@ -45,35 +45,58 @@ const UnitSpan = styled(Typography)`
45
45
 
46
46
  const PriceSpan = styled(Typography)`
47
47
  white-space: normal;
48
+ min-height: 39px;
48
49
  `;
49
50
 
50
51
  function PlanPrice({
52
+ showStartingAt,
53
+ withStartingAtRow,
51
54
  plan,
52
55
  billingPeriod,
53
56
  paywallLocale,
54
57
  locale,
55
- shouldShowMonthlyPriceAmount,
58
+ hasMonthlyPrice,
59
+ hasAnnuallyPrice,
56
60
  }: {
61
+ showStartingAt: boolean;
62
+ withStartingAtRow: boolean;
57
63
  plan: PaywallPlan;
58
64
  billingPeriod: BillingPeriod;
59
65
  paywallLocale: PaywallLocalization;
60
- shouldShowMonthlyPriceAmount: boolean;
66
+ hasMonthlyPrice: boolean;
67
+ hasAnnuallyPrice: boolean;
61
68
  locale: string;
62
69
  }) {
63
- const { price, unit } = getPlanPrice(plan, billingPeriod, paywallLocale, locale, shouldShowMonthlyPriceAmount);
70
+ const { price, unit } = getPlanPrice(plan, billingPeriod, paywallLocale, locale, hasMonthlyPrice);
64
71
 
65
72
  return (
66
73
  <PlanPriceContainer className="stigg-price-text">
67
74
  <>
68
- <PriceSpan span variant="h1">
69
- {price}
70
- </PriceSpan>
75
+ {withStartingAtRow && (
76
+ <Typography
77
+ style={{ minHeight: '20px' }}
78
+ className="stigg-starting-at-text"
79
+ variant="body1"
80
+ color="secondary"
81
+ >
82
+ {showStartingAt ? paywallLocale.price.startingAtCaption : EMPTY_CHAR}
83
+ </Typography>
84
+ )}
85
+
86
+ <PriceSpan variant="h1">{price}</PriceSpan>
87
+
71
88
  {unit && (
72
- <UnitSpan span variant="h6">
73
- {' '}
89
+ <UnitSpan span variant="h6" color="secondary">
74
90
  {unit}
75
91
  </UnitSpan>
76
92
  )}
93
+
94
+ <PriceBillingPeriod
95
+ plan={plan}
96
+ billingPeriod={billingPeriod}
97
+ hasAnnuallyPrice={hasAnnuallyPrice}
98
+ hasMonthlyPrice={hasMonthlyPrice}
99
+ />
77
100
  </>
78
101
  </PlanPriceContainer>
79
102
  );
@@ -91,27 +114,26 @@ function PriceBillingPeriod({ plan, billingPeriod, hasMonthlyPrice, hasAnnuallyP
91
114
 
92
115
  let content = EMPTY_CHAR;
93
116
  if (hasPrice && hasMonthlyPrice && hasAnnuallyPrice) {
94
- content = `billed ${billingPeriod.toLowerCase()}`;
117
+ content = `, billed ${billingPeriod.toLowerCase()}`;
95
118
  }
96
119
 
97
120
  return (
98
- <Typography className="stigg-price-billing-period-text" variant="body1" color="disabled">
121
+ <Typography span className="stigg-price-billing-period-text" variant="h6" color="secondary">
99
122
  {content}
100
123
  </Typography>
101
124
  );
102
125
  }
103
126
 
104
- const Divider = styled.div<{ $withMargin: boolean }>`
105
- margin-top: ${({ $withMargin }) => ($withMargin ? '30px' : '0')};
127
+ const Divider = styled.div`
106
128
  height: 2px;
107
- width: 50px;
108
- background-color: ${({ theme }) => theme.stigg.palette.primary};
129
+ width: 100%;
130
+ background-color: ${({ theme }) => theme.stigg.palette.outlinedBorder};
131
+ margin-bottom: 40px;
109
132
  `;
110
133
 
111
134
  const PlanDescription = styled(Typography)`
112
135
  text-align: ${({ theme }) => theme.stigg.layout.descriptionAlignment};
113
136
  min-height: ${({ theme }) => theme.stigg.layout.descriptionMinHeight};
114
- margin-top: 16px;
115
137
  `;
116
138
 
117
139
  const HighlightBadge = styled.div`
@@ -230,45 +252,38 @@ export function PlanOffering({
230
252
  {plan.displayName}
231
253
  </PlanHeader>
232
254
 
233
- {withStartingAtRow && (
234
- <Typography className="stigg-starting-at-text" variant="body1" color="secondary">
235
- {showStartingAt ? paywallLocale.price.startingAtCaption : EMPTY_CHAR}
236
- </Typography>
255
+ {shouldShowDescriptionSection && (
256
+ <PlanDescription className="stigg-plan-description" variant="h6" color="secondary">
257
+ {plan.description}
258
+ </PlanDescription>
237
259
  )}
238
260
 
239
261
  <PlanPrice
262
+ showStartingAt={showStartingAt}
263
+ withStartingAtRow={!!withStartingAtRow}
240
264
  plan={plan}
241
265
  billingPeriod={billingPeriod}
242
266
  paywallLocale={paywallLocale}
243
267
  locale={locale}
244
- shouldShowMonthlyPriceAmount={hasMonthlyPrice}
268
+ hasAnnuallyPrice={hasAnnuallyPrice}
269
+ hasMonthlyPrice={hasMonthlyPrice}
245
270
  />
246
- <PriceBillingPeriod
271
+
272
+ <PlanOfferingButton
273
+ isNextPlan={isNextPlan}
274
+ customer={customer}
247
275
  plan={plan}
276
+ currentSubscription={currentSubscription}
248
277
  billingPeriod={billingPeriod}
249
- hasAnnuallyPrice={hasAnnuallyPrice}
250
- hasMonthlyPrice={hasMonthlyPrice}
278
+ isCustomerOnTrial={isCustomerOnTrial}
279
+ onPlanSelected={onPlanSelected}
280
+ paywallLocale={paywallLocale}
251
281
  />
252
- {shouldShowDescriptionSection && (
253
- <PlanDescription className="stigg-plan-description" variant="h6" color="secondary">
254
- {plan.description}
255
- </PlanDescription>
256
- )}
257
- <Divider className="stigg-plan-header-divider" $withMargin={!shouldShowDescriptionSection} />
282
+
283
+ <Divider className="stigg-plan-header-divider" />
258
284
  </HeaderWrapper>
259
285
 
260
286
  <PlanEntitlements plan={plan} billingPeriod={billingPeriod} paywallLocale={paywallLocale} />
261
-
262
- <PlanOfferingButton
263
- isNextPlan={isNextPlan}
264
- customer={customer}
265
- plan={plan}
266
- currentSubscription={currentSubscription}
267
- billingPeriod={billingPeriod}
268
- isCustomerOnTrial={isCustomerOnTrial}
269
- onPlanSelected={onPlanSelected}
270
- paywallLocale={paywallLocale}
271
- />
272
287
  </PlanOfferingContainer>
273
288
  );
274
289
  }
@@ -1,14 +1,12 @@
1
- import React, { ReactNode, useState } from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { BillingPeriod, Customer, PricingType, Subscription } from '@stigg/js-client-sdk';
3
3
  import styled from '@emotion/styled/macro';
4
4
  import { css, Theme, useTheme } from '@emotion/react';
5
- import { ArrowRight } from 'react-feather';
6
5
  import { PaywallPlan, SubscribeIntentionType } from './types';
7
6
  import { PaywallLocalization } from './paywallTextOverrides';
8
7
  import { flexLayoutMapper } from '../../theme/getResolvedTheme';
9
8
  import ClipLoader from 'react-spinners/ClipLoader';
10
9
  import { Typography } from '../common/Typography';
11
- import Restore from '../../assets/restore.svg';
12
10
  import { getSubscriptionScheduleUpdateTexts } from '../utils/getSubscriptionScheduleUpdateTexts';
13
11
  import { isFunction } from 'lodash';
14
12
 
@@ -19,14 +17,13 @@ const LoadingIndicator = styled(ClipLoader)`
19
17
  const OfferingButton = styled.button<{ $disabled?: boolean }>`
20
18
  cursor: ${({ $disabled }) => ($disabled ? 'default' : 'pointer')};
21
19
  padding: 8px 12px;
22
- background-color: inherit;
20
+ background-color: ${({ theme }) => theme.stigg.palette.backgroundButton};
23
21
  border: ${({ theme }) => `1px solid ${theme.stigg.palette.outlinedRestingBorder}`};
24
- border-radius: 4px;
22
+ border-radius: 10px;
25
23
  display: flex;
26
24
  align-items: center;
27
25
  justify-content: center;
28
- width: fit-content;
29
- height: 43px;
26
+ min-height: 42px;
30
27
  min-width: 120px;
31
28
 
32
29
  ${({ $disabled }) =>
@@ -37,38 +34,41 @@ const OfferingButton = styled.button<{ $disabled?: boolean }>`
37
34
 
38
35
  &:hover {
39
36
  background-color: ${({ theme, $disabled }) =>
40
- $disabled ? 'inherit' : theme.stigg.palette.outlinedHoverBackground};
37
+ $disabled ? theme.stigg.palette.backgroundButton : theme.stigg.palette.outlinedHoverBackground};
41
38
  }
42
39
  `;
43
40
 
44
- const StyledArrowRight = styled(ArrowRight)`
45
- color: ${({ theme }) => theme.stigg.palette.primary};
46
- margin-left: 6px;
41
+ const ButtonText = styled(Typography)`
42
+ margin: 0;
43
+ font-size: 15px;
47
44
  `;
48
45
 
49
- const StyledRestoreIcon = styled(Restore)`
50
- margin-left: 6px;
51
- color: ${({ theme }) => theme.stigg.palette.primary};
52
- path {
53
- fill: ${({ theme }) => theme.stigg.palette.primary};
54
- }
46
+ const ScheduledUpdateText = styled(Typography)`
47
+ min-height: 87px;
55
48
  `;
56
49
 
57
- const ButtonText = styled(Typography)`
58
- margin: 0;
59
- font-size: 15px;
50
+ const CancelScheduledUpdateButton = styled.button<{ $disabled?: boolean }>`
51
+ background: none;
52
+ border: none;
53
+ padding: 0;
54
+ color: ${({ theme }) => theme.stigg.palette.text.secondary};
55
+
56
+ text-decoration: underline;
57
+ cursor: pointer;
60
58
  `;
61
59
 
62
60
  const TrialDaysLeft = styled(Typography)`
63
- margin: 0;
64
- padding: 16px 0;
61
+ margin-top: 8px;
62
+ margin-bottom: 17px;
63
+ min-height: 20px;
65
64
  `;
66
65
 
67
66
  const ButtonLayout = styled.div`
68
67
  display: flex;
69
68
  flex-direction: column;
70
- margin-top: auto;
69
+ margin-top: 40px;
71
70
  align-self: ${({ theme }) => flexLayoutMapper(theme.stigg.layout.ctaAlignment)};
71
+ width: 100%;
72
72
  `;
73
73
 
74
74
  type PlanOfferingButtonProps = {
@@ -109,12 +109,10 @@ export function PlanOfferingButton({
109
109
  const buttonProps: {
110
110
  title: string;
111
111
  disabled: boolean;
112
- icon: ReactNode | null;
113
112
  intentionType: SubscribeIntentionType;
114
113
  } = {
115
114
  title: customer ? (plan.isLowerThanCurrentPlan ? resolvedDowngrade : resolvedUpgrade) : startNew,
116
115
  disabled: false,
117
- icon: <StyledArrowRight />,
118
116
  intentionType: plan.isLowerThanCurrentPlan
119
117
  ? SubscribeIntentionType.DOWNGRADE_PLAN
120
118
  : isCustomerOnTrial
@@ -140,7 +138,6 @@ export function PlanOfferingButton({
140
138
  ) {
141
139
  buttonProps.title = currentPlan;
142
140
  buttonProps.disabled = true;
143
- buttonProps.icon = null;
144
141
  } else {
145
142
  buttonProps.title = switchToBillingPeriod(billingPeriod);
146
143
  buttonProps.intentionType = SubscribeIntentionType.CHANGE_BILLING_PERIOD;
@@ -154,47 +151,52 @@ export function PlanOfferingButton({
154
151
  buttonProps.disabled = false;
155
152
  buttonProps.title = cancelScheduledUpdate;
156
153
  buttonProps.intentionType = SubscribeIntentionType.CANCEL_SCHEDULED_UPDATES;
157
- buttonProps.icon = <StyledRestoreIcon />;
158
154
  }
159
155
 
160
156
  const isDisabled = isLoading || buttonProps.disabled;
161
157
  return (
162
158
  <>
163
- {isNextPlan && scheduledUpdateText && (
164
- <Typography
165
- color="secondary"
166
- variant="body1"
167
- style={{ marginBottom: 24 }}
168
- className="stigg-paywall-schedule-update-text"
169
- >
170
- {scheduledUpdateText}
171
- </Typography>
172
- )}
173
159
  <ButtonLayout className="stigg-paywall-plan-button-layout">
174
- {plan.isCurrentCustomerPlan && plan.trialDaysLeft && (
175
- <TrialDaysLeft className="stigg-trial-days-left-text" variant="h6" color="secondary">
176
- <b>{plan.trialDaysLeft} days</b> left on your free trial
177
- </TrialDaysLeft>
160
+ {isNextPlan && scheduledUpdateText ? (
161
+ <ScheduledUpdateText color="secondary" variant="body1" className="stigg-paywall-schedule-update-text">
162
+ {scheduledUpdateText}{' '}
163
+ <CancelScheduledUpdateButton
164
+ color="secondary"
165
+ onClick={() => {
166
+ setIsLoading(true);
167
+ Promise.resolve(onPlanSelected(buttonProps.intentionType)).finally(() => setIsLoading(false));
168
+ }}
169
+ >
170
+ {buttonProps.title}
171
+ </CancelScheduledUpdateButton>
172
+ </ScheduledUpdateText>
173
+ ) : (
174
+ <>
175
+ <OfferingButton
176
+ className="stigg-paywall-plan-button"
177
+ data-testid={`paywall-card-cta-${plan.displayName}`}
178
+ disabled={isDisabled}
179
+ $disabled={isDisabled}
180
+ onClick={() => {
181
+ setIsLoading(true);
182
+ Promise.resolve(onPlanSelected(buttonProps.intentionType)).finally(() => setIsLoading(false));
183
+ }}
184
+ >
185
+ <ButtonText className="stigg-paywall-plan-button-text" variant="h6" color="primary.main">
186
+ {buttonProps.title}
187
+ </ButtonText>
188
+ {isLoading && <LoadingIndicator color={theme.stigg.palette.text.disabled} loading size={16} />}
189
+ </OfferingButton>
190
+
191
+ <TrialDaysLeft className="stigg-trial-days-left-text" variant="h6" color="secondary">
192
+ {plan.isCurrentCustomerPlan && plan.trialDaysLeft && (
193
+ <>
194
+ <b>{plan.trialDaysLeft} days</b> left on your free trial
195
+ </>
196
+ )}
197
+ </TrialDaysLeft>
198
+ </>
178
199
  )}
179
- <OfferingButton
180
- className="stigg-paywall-plan-button"
181
- data-testid={`paywall-card-cta-${plan.displayName}`}
182
- disabled={isDisabled}
183
- $disabled={isDisabled}
184
- onClick={() => {
185
- setIsLoading(true);
186
- Promise.resolve(onPlanSelected(buttonProps.intentionType)).finally(() => setIsLoading(false));
187
- }}
188
- >
189
- <ButtonText className="stigg-paywall-plan-button-text" variant="h6" color="primary.main">
190
- {buttonProps.title}
191
- </ButtonText>
192
- {isLoading ? (
193
- <LoadingIndicator color={theme.stigg.palette.text.disabled} loading size={16} />
194
- ) : (
195
- buttonProps.icon
196
- )}
197
- </OfferingButton>
198
200
  </ButtonLayout>
199
201
  </>
200
202
  );
@@ -41,8 +41,10 @@ export function getResolvedPaywallLocalize(localizeOverride?: DeepPartial<Paywal
41
41
  custom: 'Contact us',
42
42
  currentPlan: 'Current plan',
43
43
  startNew: 'Get started',
44
- switchToBillingPeriod: billingPeriod => `Switch to ${billingPeriod.toLocaleLowerCase()}`,
45
- cancelScheduledUpdate: 'Cancel update',
44
+ switchToBillingPeriod: billingPeriod => {
45
+ return billingPeriod === BillingPeriod.Monthly ? 'Switch to monthly billing' : 'Switch to annual billing';
46
+ },
47
+ cancelScheduledUpdate: 'Cancel',
46
48
  },
47
49
  price: {
48
50
  startingAtCaption: 'Starts at',
@@ -35,7 +35,7 @@ export function getPaidPriceText({
35
35
  const featureUnit = price.feature?.units || '';
36
36
  return {
37
37
  price: priceString,
38
- unit: `/ ${featureUnit}`,
38
+ unit: `per ${featureUnit}`,
39
39
  };
40
40
  }
41
41
  }
@@ -10,7 +10,7 @@ import { BillingPeriodChangeVariables, UnitAmountChangeVariables } from '../../t
10
10
  import { CustomerPortalSubscriptionPriceFragment } from '@stigg/api-client-js/src/generated/sdk';
11
11
  import { compact, map } from 'lodash';
12
12
 
13
- const formatDate = (date: string) => moment.utc(date).format('MMM Do, yyyy');
13
+ const formatDate = (date: string) => moment.utc(date).format('MMMM Do, yyyy');
14
14
 
15
15
  type GetSubscriptionScheduleUpdateTextsProps = {
16
16
  scheduledUpdate: CustomerPortalSubscriptionScheduledUpdateDataFragment | undefined;
@@ -40,10 +40,10 @@ export function getSubscriptionScheduleUpdateTexts({
40
40
  const line1 = (
41
41
  <>
42
42
  Your subscription will be downgraded to the <b>{scheduledUpdate.targetPackage?.displayName} plan</b>{' '}
43
- {shouldShowDate ? <>on {formatDate(scheduledUpdate.scheduledExecutionTime)}</> : postfix}
43
+ {shouldShowDate ? <>on {formatDate(scheduledUpdate.scheduledExecutionTime)}</> : postfix}.
44
44
  </>
45
45
  );
46
- const line2 = `Until then you still have access to your current plan`;
46
+ const line2 = `Until then you still have access to your current plan.`;
47
47
 
48
48
  return { line1, line2 };
49
49
  }
@@ -53,7 +53,7 @@ export function getSubscriptionScheduleUpdateTexts({
53
53
  const line1 = (
54
54
  <>
55
55
  Your billing cycle will change to <b>{variables.billingPeriod?.toLocaleLowerCase()}</b>{' '}
56
- {withDate ? <>on {formatDate(scheduledUpdate.scheduledExecutionTime)}</> : postfix}
56
+ {withDate ? <>on {formatDate(scheduledUpdate.scheduledExecutionTime)}</> : postfix}.
57
57
  </>
58
58
  );
59
59
 
@@ -69,11 +69,11 @@ export function getSubscriptionScheduleUpdateTexts({
69
69
  const line1 = (
70
70
  <>
71
71
  Your subscription will be updated to <b>{variables.newUnitAmount}</b> {unitsText}{' '}
72
- {withDate ? <>on {formatDate(scheduledUpdate.scheduledExecutionTime)}</> : postfix}
72
+ {withDate ? <>on {formatDate(scheduledUpdate.scheduledExecutionTime)}</> : postfix}.
73
73
  </>
74
74
  );
75
75
 
76
- const line2 = `Until then you still have access to your current ${featureUnits} count`;
76
+ const line2 = `Until then you still have access to your current ${featureUnits} count.`;
77
77
 
78
78
  return { line1, line2 };
79
79
  }
@@ -21,6 +21,7 @@ export const getResolvedTheme = (customizedTheme?: CustomizedTheme): StiggTheme
21
21
  backgroundPaper: '#FFFFFF',
22
22
  backgroundHighlight: '#F5F6F9',
23
23
  backgroundSection: primaryColor.alpha(0.1).toString(),
24
+ backgroundButton: primaryColor.alpha(0.08).toString(),
24
25
  outlinedBorder: '#C4CBDD',
25
26
  outlinedRestingBorder: primaryColor.alpha(0.5).toString(),
26
27
  outlinedHoverBackground: primaryColor.lightness(95).hex(),
@@ -49,7 +50,8 @@ export const getResolvedTheme = (customizedTheme?: CustomizedTheme): StiggTheme
49
50
  },
50
51
  shadows: {
51
52
  light: '0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px rgba(0, 0, 0, 0.14), 0px 1px 3px rgba(0, 0, 0, 0.12)',
52
- popover: '0px 4px 5px -2px rgba(0, 30, 108, 0.2), 0px 7px 10px 1px rgba(0, 30, 108, 0.14), 0px 2px 16px 1px rgba(0, 30, 108, 0.12)',
53
+ popover:
54
+ '0px 4px 5px -2px rgba(0, 30, 108, 0.2), 0px 7px 10px 1px rgba(0, 30, 108, 0.14), 0px 2px 16px 1px rgba(0, 30, 108, 0.12)',
53
55
  },
54
56
  border: {
55
57
  radius: '10px',
@@ -11,6 +11,7 @@ export type StiggTheme = {
11
11
  backgroundPaper: string;
12
12
  backgroundHighlight: string;
13
13
  backgroundSection: string;
14
+ backgroundButton: string;
14
15
  outlinedBorder: string;
15
16
  outlinedRestingBorder: string;
16
17
  outlinedHoverBackground: string;