@moneylion/react-native-offer-carousel 1.3.1 → 1.4.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.
Files changed (66) hide show
  1. package/lib/commonjs/capabilities/core/src/system/cnfContext/schemas/Brand.js +44 -45
  2. package/lib/commonjs/capabilities/core/src/system/cnfContext/schemas/Brand.js.map +1 -1
  3. package/lib/commonjs/components/DynamicOffers/Render/DynamicOffersRender.js +1 -1
  4. package/lib/commonjs/components/DynamicOffers/Render/DynamicOffersRender.js.map +1 -1
  5. package/lib/commonjs/components/ErrorBoundary/index.js +34 -0
  6. package/lib/commonjs/components/ErrorBoundary/index.js.map +1 -0
  7. package/lib/commonjs/components/Layouts/HeadlineWithDescriptionCard/index.js.map +1 -1
  8. package/lib/commonjs/components/Modal/AllOffersModal.js +14 -7
  9. package/lib/commonjs/components/Modal/AllOffersModal.js.map +1 -1
  10. package/lib/commonjs/components/Modal/OfferDetailsModal.js +13 -6
  11. package/lib/commonjs/components/Modal/OfferDetailsModal.js.map +1 -1
  12. package/lib/commonjs/components/MoneyLionOfferCarousel.js +23 -7
  13. package/lib/commonjs/components/MoneyLionOfferCarousel.js.map +1 -1
  14. package/lib/commonjs/config/mocks/cnfContext.js +151 -44
  15. package/lib/commonjs/config/mocks/cnfContext.js.map +1 -1
  16. package/lib/commonjs/context/ThemeProvider.js +8 -2
  17. package/lib/commonjs/context/ThemeProvider.js.map +1 -1
  18. package/lib/commonjs/utils/getThemeColors.js +24 -0
  19. package/lib/commonjs/utils/getThemeColors.js.map +1 -0
  20. package/lib/module/capabilities/core/src/system/cnfContext/schemas/Brand.js +44 -45
  21. package/lib/module/capabilities/core/src/system/cnfContext/schemas/Brand.js.map +1 -1
  22. package/lib/module/components/DynamicOffers/Render/DynamicOffersRender.js +1 -1
  23. package/lib/module/components/DynamicOffers/Render/DynamicOffersRender.js.map +1 -1
  24. package/lib/module/components/ErrorBoundary/index.js +28 -0
  25. package/lib/module/components/ErrorBoundary/index.js.map +1 -0
  26. package/lib/module/components/Layouts/HeadlineWithDescriptionCard/index.js.map +1 -1
  27. package/lib/module/components/Modal/AllOffersModal.js +14 -7
  28. package/lib/module/components/Modal/AllOffersModal.js.map +1 -1
  29. package/lib/module/components/Modal/OfferDetailsModal.js +13 -6
  30. package/lib/module/components/Modal/OfferDetailsModal.js.map +1 -1
  31. package/lib/module/components/MoneyLionOfferCarousel.js +23 -8
  32. package/lib/module/components/MoneyLionOfferCarousel.js.map +1 -1
  33. package/lib/module/config/mocks/cnfContext.js +151 -44
  34. package/lib/module/config/mocks/cnfContext.js.map +1 -1
  35. package/lib/module/context/ThemeProvider.js +8 -2
  36. package/lib/module/context/ThemeProvider.js.map +1 -1
  37. package/lib/module/utils/getThemeColors.js +17 -0
  38. package/lib/module/utils/getThemeColors.js.map +1 -0
  39. package/lib/typescript/src/capabilities/core/src/system/cnfContext/schemas/Brand.d.ts +312 -89
  40. package/lib/typescript/src/capabilities/core/src/system/cnfContext/schemas/Brand.d.ts.map +1 -1
  41. package/lib/typescript/src/capabilities/ui/elements/src/components/MarkdownText/components.d.ts +2 -2
  42. package/lib/typescript/src/capabilities/ui/elements/src/components/MarkdownText/components.d.ts.map +1 -1
  43. package/lib/typescript/src/components/ErrorBoundary/index.d.ts +20 -0
  44. package/lib/typescript/src/components/ErrorBoundary/index.d.ts.map +1 -0
  45. package/lib/typescript/src/components/Layouts/HeadlineWithDescriptionCard/index.d.ts.map +1 -1
  46. package/lib/typescript/src/components/Modal/AllOffersModal.d.ts.map +1 -1
  47. package/lib/typescript/src/components/Modal/OfferDetailsModal.d.ts.map +1 -1
  48. package/lib/typescript/src/components/MoneyLionOfferCarousel.d.ts +8 -5
  49. package/lib/typescript/src/components/MoneyLionOfferCarousel.d.ts.map +1 -1
  50. package/lib/typescript/src/config/mocks/cnfContext.d.ts.map +1 -1
  51. package/lib/typescript/src/context/ThemeProvider.d.ts +3 -0
  52. package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -1
  53. package/lib/typescript/src/utils/getThemeColors.d.ts +8 -0
  54. package/lib/typescript/src/utils/getThemeColors.d.ts.map +1 -0
  55. package/package.json +1 -1
  56. package/src/capabilities/core/src/system/cnfContext/schemas/Brand.ts +51 -46
  57. package/src/capabilities/ui/elements/src/components/MarkdownText/components.tsx +1 -1
  58. package/src/components/DynamicOffers/Render/DynamicOffersRender.tsx +2 -2
  59. package/src/components/ErrorBoundary/index.tsx +40 -0
  60. package/src/components/Layouts/HeadlineWithDescriptionCard/index.tsx +1 -0
  61. package/src/components/Modal/AllOffersModal.tsx +19 -5
  62. package/src/components/Modal/OfferDetailsModal.tsx +12 -5
  63. package/src/components/MoneyLionOfferCarousel.tsx +49 -13
  64. package/src/config/mocks/cnfContext.ts +40 -44
  65. package/src/context/ThemeProvider.tsx +12 -1
  66. package/src/utils/getThemeColors.ts +29 -0
@@ -0,0 +1,8 @@
1
+ import type { ReshapedThemeColors } from "../capabilities/core/src/system/cnfContext/schemas/Brand";
2
+ import type { ThemeColors } from "../context/ThemeProvider";
3
+ /** This util checks for missing theme colors from reshapedThemeColors in the API response
4
+ * and merges them with fallback theme colors.
5
+ * It returns a ThemeColors object with the appropriate colors based on the isDarkTheme flag.
6
+ */
7
+ export declare const getThemeColors: (reshapedThemeColors: ReshapedThemeColors, isDarkTheme?: boolean) => ThemeColors;
8
+ //# sourceMappingURL=getThemeColors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getThemeColors.d.ts","sourceRoot":"","sources":["../../../../src/utils/getThemeColors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0DAA0D,CAAC;AAEpG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,cAAc,wBACL,mBAAmB,gBAC3B,OAAO,KAClB,WAiBF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneylion/react-native-offer-carousel",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "React Native components for the Engine by MoneyLion Mobile SDK",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -1,50 +1,55 @@
1
1
  import { Schema } from "effect";
2
2
 
3
- export type ThemeColors = Schema.Schema.Type<typeof ThemeColors>;
4
- const ThemeColors = Schema.Struct({
5
- backgroundPrimary: Schema.String,
6
- backgroundPrimaryFaded: Schema.String,
7
- backgroundPrimaryHighlighted: Schema.String,
8
- backgroundCritical: Schema.String,
9
- backgroundCriticalFaded: Schema.String,
10
- backgroundCriticalHighlighted: Schema.String,
11
- backgroundWarning: Schema.String,
12
- backgroundWarningFaded: Schema.String,
13
- backgroundWarningHighlighted: Schema.String,
14
- backgroundPositive: Schema.String,
15
- backgroundPositiveFaded: Schema.String,
16
- backgroundPositiveHighlighted: Schema.String,
17
- backgroundNeutral: Schema.String,
18
- backgroundNeutralFaded: Schema.String,
19
- backgroundNeutralHighlighted: Schema.String,
20
- backgroundDisabled: Schema.String,
21
- backgroundDisabledFaded: Schema.String,
22
- backgroundElevationBase: Schema.String,
23
- backgroundElevationRaised: Schema.String,
24
- backgroundElevationOverlay: Schema.String,
25
- backgroundPage: Schema.String,
26
- backgroundPageFaded: Schema.String,
27
- borderPrimary: Schema.String,
28
- borderPrimaryFaded: Schema.String,
29
- borderCritical: Schema.String,
30
- borderCriticalFaded: Schema.String,
31
- borderWarning: Schema.String,
32
- borderWarningFaded: Schema.String,
33
- borderPositive: Schema.String,
34
- borderPositiveFaded: Schema.String,
35
- borderNeutral: Schema.String,
36
- borderNeutralFaded: Schema.String,
37
- borderDisabled: Schema.String,
38
- foregroundPrimary: Schema.String,
39
- foregroundCritical: Schema.String,
40
- foregroundWarning: Schema.String,
41
- foregroundPositive: Schema.String,
42
- foregroundNeutral: Schema.String,
43
- foregroundNeutralFaded: Schema.String,
44
- foregroundDisabled: Schema.String,
45
- brand: Schema.String,
46
- white: Schema.String,
47
- black: Schema.String,
3
+ export type ReshapedThemeColorObject = Schema.Schema.Type<
4
+ typeof ReshapedThemeColorObject
5
+ >;
6
+ const ReshapedThemeColorObject = Schema.Struct({
7
+ hex: Schema.String,
8
+ hexDark: Schema.optional(Schema.String),
9
+ });
10
+
11
+ export type ReshapedThemeColors = Schema.Schema.Type<
12
+ typeof ReshapedThemeColors
13
+ >;
14
+ const ReshapedThemeColors = Schema.Struct({
15
+ backgroundCritical: ReshapedThemeColorObject,
16
+ backgroundCriticalFaded: ReshapedThemeColorObject,
17
+ backgroundDisabled: ReshapedThemeColorObject,
18
+ backgroundDisabledFaded: ReshapedThemeColorObject,
19
+ backgroundElevationBase: ReshapedThemeColorObject,
20
+ backgroundElevationOverlay: ReshapedThemeColorObject,
21
+ backgroundElevationRaised: ReshapedThemeColorObject,
22
+ backgroundNeutral: ReshapedThemeColorObject,
23
+ backgroundNeutralFaded: ReshapedThemeColorObject,
24
+ backgroundPage: ReshapedThemeColorObject,
25
+ backgroundPageFaded: ReshapedThemeColorObject,
26
+ backgroundPositive: ReshapedThemeColorObject,
27
+ backgroundPositiveFaded: ReshapedThemeColorObject,
28
+ backgroundPrimary: ReshapedThemeColorObject,
29
+ backgroundPrimaryFaded: ReshapedThemeColorObject,
30
+ backgroundWarning: ReshapedThemeColorObject,
31
+ backgroundWarningFaded: ReshapedThemeColorObject,
32
+ black: ReshapedThemeColorObject,
33
+ borderCritical: ReshapedThemeColorObject,
34
+ borderCriticalFaded: ReshapedThemeColorObject,
35
+ borderDisabled: ReshapedThemeColorObject,
36
+ borderNeutral: ReshapedThemeColorObject,
37
+ borderNeutralFaded: ReshapedThemeColorObject,
38
+ borderPositive: ReshapedThemeColorObject,
39
+ borderPositiveFaded: ReshapedThemeColorObject,
40
+ borderPrimary: ReshapedThemeColorObject,
41
+ borderPrimaryFaded: ReshapedThemeColorObject,
42
+ borderWarning: ReshapedThemeColorObject,
43
+ borderWarningFaded: ReshapedThemeColorObject,
44
+ brand: ReshapedThemeColorObject,
45
+ foregroundCritical: ReshapedThemeColorObject,
46
+ foregroundDisabled: ReshapedThemeColorObject,
47
+ foregroundNeutral: ReshapedThemeColorObject,
48
+ foregroundNeutralFaded: ReshapedThemeColorObject,
49
+ foregroundPositive: ReshapedThemeColorObject,
50
+ foregroundPrimary: ReshapedThemeColorObject,
51
+ foregroundWarning: ReshapedThemeColorObject,
52
+ white: ReshapedThemeColorObject,
48
53
  });
49
54
 
50
55
  export type Links = Schema.Schema.Type<typeof Links>;
@@ -120,7 +125,7 @@ const BrandFields = Schema.Struct({
120
125
  displayName: Schema.String,
121
126
  isLicensed: Schema.optional(Schema.Boolean),
122
127
  hideLicensedLogo: Schema.optional(Schema.Boolean),
123
- themeColors: ThemeColors,
128
+ reshapedThemeColors: ReshapedThemeColors,
124
129
  links: Schema.optional(Links),
125
130
  logos: Schema.optional(Logos),
126
131
  font: Schema.optional(Font),
@@ -10,7 +10,7 @@ import Text, {
10
10
  type TextWeight,
11
11
  type Variant,
12
12
  } from "../../../../../../components/Text";
13
- import type { ThemeColors } from "../../../../../core/src";
13
+ import type { ThemeColors } from "../../../../../../context/ThemeProvider";
14
14
 
15
15
  const BoldText = ({ children }: { children: React.ReactNode }) => {
16
16
  const processChildren = (_children: React.ReactNode) => {
@@ -291,13 +291,13 @@ export const DynamicOffersRender = ({
291
291
  isHorizontalScroll,
292
292
  });
293
293
  }, [
294
+ offers,
295
+ isHorizontalScroll,
294
296
  displayLayout,
295
297
  productTypeBuilder,
296
- offers,
297
298
  showProductTypeLabel,
298
299
  fallbackTemplate,
299
300
  showCardBorder,
300
- isHorizontalScroll,
301
301
  ]);
302
302
 
303
303
  // Wrapped offer components with layout measurement
@@ -0,0 +1,40 @@
1
+ import React, { Component } from "react";
2
+ import type { CustomError } from "../MoneyLionOfferCarousel";
3
+
4
+ interface Props {
5
+ children: React.ReactNode;
6
+ fallbackUI?: React.ReactNode;
7
+ onError: (error: CustomError) => void;
8
+ }
9
+
10
+ interface State {
11
+ hasError: boolean;
12
+ }
13
+
14
+ class ErrorBoundary extends Component<Props, State> {
15
+ constructor(props: Props) {
16
+ super(props);
17
+ this.state = { hasError: false };
18
+ }
19
+
20
+ static getDerivedStateFromError(_error: Error) {
21
+ return { hasError: true };
22
+ }
23
+
24
+ componentDidCatch(error: Error) {
25
+ this.props.onError({
26
+ message: error.message,
27
+ timestamp: new Date().toISOString(),
28
+ });
29
+ }
30
+
31
+ render() {
32
+ if (this.state.hasError) {
33
+ return this.props.fallbackUI ?? null;
34
+ }
35
+
36
+ return this.props.children;
37
+ }
38
+ }
39
+
40
+ export default ErrorBoundary;
@@ -16,6 +16,7 @@ type HeadlineWithDescriptionCardProps = {
16
16
  fullCardWidth?: boolean;
17
17
  isHorizontalScroll?: boolean;
18
18
  };
19
+
19
20
  export const HeadlineWithDescriptionCard = ({
20
21
  offer,
21
22
  productTypeBuilder,
@@ -14,6 +14,7 @@ import {
14
14
  import Text from "../Text";
15
15
  import { DynamicOffersContainer } from "../DynamicOffers/DynamicOffersContainer";
16
16
  import type { DynamicOfferProps } from "../DynamicOffers/DynamicOffers";
17
+ import { useTheme } from "../../context/ThemeProvider";
17
18
 
18
19
  type AllOffersModalProps = {
19
20
  visible: boolean;
@@ -50,6 +51,8 @@ export const AllOffersModal = ({
50
51
  onClose,
51
52
  config,
52
53
  }: AllOffersModalProps) => {
54
+ const { theme } = useTheme();
55
+
53
56
  const { brand, title } = config;
54
57
  const footerLinks = brand.links;
55
58
 
@@ -100,16 +103,28 @@ export const AllOffersModal = ({
100
103
  visible={visible}
101
104
  onRequestClose={onClose}
102
105
  >
103
- <SafeAreaView style={styles.modalContainer}>
106
+ <SafeAreaView
107
+ style={[
108
+ styles.modalContainer,
109
+ { backgroundColor: theme.backgroundPageFaded },
110
+ ]}
111
+ >
104
112
  <View style={styles.modalHeader}>
105
113
  <TouchableOpacity
106
114
  onPress={onClose}
107
115
  style={styles.closeXButton}
108
116
  hitSlop={{ top: 15, right: 15, bottom: 15, left: 15 }}
109
117
  >
110
- <Text style={styles.closeXText}>✕</Text>
118
+ <Text style={styles.closeXText} color={"foregroundNeutral"}>
119
+
120
+ </Text>
111
121
  </TouchableOpacity>
112
- <Text variant="title-3" weight="bold" style={styles.headerTitle}>
122
+ <Text
123
+ variant="title-3"
124
+ weight="bold"
125
+ color={"foregroundNeutral"}
126
+ style={styles.headerTitle}
127
+ >
113
128
  {title}
114
129
  </Text>
115
130
  </View>
@@ -157,7 +172,6 @@ export const AllOffersModal = ({
157
172
  const styles = StyleSheet.create({
158
173
  modalContainer: {
159
174
  flex: 1,
160
- backgroundColor: "#EEEEEE",
161
175
  },
162
176
  modalHeader: {
163
177
  flexDirection: "row",
@@ -173,11 +187,11 @@ const styles = StyleSheet.create({
173
187
  },
174
188
  closeXText: {
175
189
  fontSize: 20,
176
- color: "#000000",
177
190
  },
178
191
  headerTitle: {
179
192
  textAlign: "center",
180
193
  alignSelf: "center",
194
+ paddingHorizontal: 36, // To ensure title doesn't overlap with close button
181
195
  },
182
196
  contentWrapper: {
183
197
  flex: 1,
@@ -15,6 +15,7 @@ import { Disclaimer } from "./Disclaimer";
15
15
  import { CallToAction } from "../Common/DynamicOfferCard/CallToAction";
16
16
  import Divider from "../Divider";
17
17
  import type { BaseOffer } from "../../capabilities/offer-catalog/src";
18
+ import { useTheme } from "../../context/ThemeProvider";
18
19
 
19
20
  type OfferDetailsModalProps = {
20
21
  visible: boolean;
@@ -31,12 +32,16 @@ export const OfferDetailsModal = ({
31
32
  onClose,
32
33
  offerIndex,
33
34
  }: OfferDetailsModalProps) => {
35
+ const { theme } = useTheme();
36
+
34
37
  const showDescriptionPoints = Boolean(offer?.descriptionPoints?.length);
35
38
 
36
39
  const showDisclaimer = Boolean(offer?.legalLanguage);
37
40
 
38
41
  const showDivider = showDescriptionPoints && showDisclaimer;
39
42
 
43
+ const backgroundColor = theme.backgroundElevationOverlay;
44
+
40
45
  return (
41
46
  <Modal
42
47
  animationType="slide"
@@ -45,10 +50,10 @@ export const OfferDetailsModal = ({
45
50
  onRequestClose={onClose}
46
51
  statusBarTranslucent={true}
47
52
  >
48
- <SafeAreaView style={styles.modalContainer}>
53
+ <SafeAreaView style={[styles.modalContainer, { backgroundColor }]}>
49
54
  {visible && (
50
55
  <StatusBar
51
- backgroundColor={"#ffffff"}
56
+ backgroundColor={backgroundColor}
52
57
  barStyle={"dark-content"}
53
58
  translucent={false}
54
59
  />
@@ -59,7 +64,11 @@ export const OfferDetailsModal = ({
59
64
  style={styles.closeXButton}
60
65
  hitSlop={{ top: 15, right: 15, bottom: 15, left: 15 }}
61
66
  >
62
- <Text style={styles.closeXText}>✕</Text>
67
+ <Text
68
+ style={[styles.closeXText, { color: theme.foregroundNeutral }]}
69
+ >
70
+
71
+ </Text>
63
72
  </TouchableOpacity>
64
73
  </View>
65
74
 
@@ -91,7 +100,6 @@ export const OfferDetailsModal = ({
91
100
  const styles = StyleSheet.create({
92
101
  modalContainer: {
93
102
  flex: 1,
94
- backgroundColor: "#ffffff",
95
103
  marginTop: Platform.OS === "android" ? StatusBar.currentHeight : 0,
96
104
  },
97
105
  modalHeader: {
@@ -111,7 +119,6 @@ const styles = StyleSheet.create({
111
119
  },
112
120
  closeXText: {
113
121
  fontSize: 20,
114
- color: "#000000",
115
122
  },
116
123
  modalContent: {
117
124
  flex: 1,
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect, useMemo, useState } from "react";
2
2
  import { localCnfContext } from "../config/mocks/cnfContext";
3
3
  import { Text } from "react-native";
4
4
  import { getPageData } from "../pageData";
@@ -16,6 +16,15 @@ import {
16
16
  import { getConfigApiBaseUrl } from "../apiEnvironment";
17
17
  import { ConfigurationProvider } from "../context/ConfigurationProvider";
18
18
  import type { CnfContext } from "../capabilities/core/src/system/cnfContext/CnfContext";
19
+ import { getThemeColors } from "../utils/getThemeColors";
20
+ import type { ReshapedThemeColors } from "../capabilities/core/src";
21
+ import ErrorBoundary from "./ErrorBoundary";
22
+
23
+ export type CustomError = {
24
+ code?: number;
25
+ message: string;
26
+ timestamp: string;
27
+ };
19
28
 
20
29
  export type MoneyLionOfferCarouselProps = {
21
30
  channel: string;
@@ -34,11 +43,9 @@ export type MoneyLionOfferCarouselProps = {
34
43
  showDescriptionPoints?: boolean;
35
44
  title?: string;
36
45
  subtitle?: string;
37
- onError?: (error: {
38
- code?: number;
39
- message: string;
40
- timestamp: string;
41
- }) => void;
46
+ isDarkTheme?: boolean;
47
+ fallbackUI?: React.ReactNode;
48
+ onError?: (error: CustomError) => void;
42
49
  onLoad?: (numOffers: number) => void;
43
50
  };
44
51
 
@@ -91,8 +98,8 @@ const getConfiguration = async ({
91
98
  }
92
99
  };
93
100
 
94
- export const MoneyLionOfferCarousel = (
95
- props: MoneyLionOfferCarouselProps &
101
+ const InternalMoneyLionOfferCarousel = (
102
+ props: Omit<MoneyLionOfferCarouselProps, "fallbackUI"> &
96
103
  Omit<EventHandlerContextType, "rateTableUuid" | "leadUuid">
97
104
  ) => {
98
105
  const {
@@ -106,6 +113,7 @@ export const MoneyLionOfferCarousel = (
106
113
  onLoad,
107
114
  title,
108
115
  subtitle,
116
+ isDarkTheme = false,
109
117
  } = props;
110
118
 
111
119
  const {
@@ -227,6 +235,17 @@ export const MoneyLionOfferCarousel = (
227
235
  onLoad,
228
236
  ]);
229
237
 
238
+ const themeColors = useMemo(
239
+ () =>
240
+ context
241
+ ? getThemeColors(
242
+ context.brand.reshapedThemeColors || ({} as ReshapedThemeColors),
243
+ isDarkTheme
244
+ )
245
+ : getThemeColors({} as ReshapedThemeColors, isDarkTheme),
246
+ [context, isDarkTheme]
247
+ );
248
+
230
249
  if (isLoading) {
231
250
  return <DynamicOfferSkeleton displayLayout={"fixed"} />;
232
251
  }
@@ -264,12 +283,12 @@ export const MoneyLionOfferCarousel = (
264
283
  brand: context.brand,
265
284
  };
266
285
 
267
- const themeColor = {
268
- ...context.brand.themeColors,
269
- };
270
-
271
286
  return (
272
- <ThemeProvider themeColors={themeColor} fontFamily={fontFamily}>
287
+ <ThemeProvider
288
+ themeColors={themeColors}
289
+ fontFamily={fontFamily}
290
+ isDarkTheme={isDarkTheme}
291
+ >
273
292
  <EventHandlerProvider
274
293
  eventHandlers={{
275
294
  ...eventHandlers,
@@ -284,3 +303,20 @@ export const MoneyLionOfferCarousel = (
284
303
  </ThemeProvider>
285
304
  );
286
305
  };
306
+
307
+ export const MoneyLionOfferCarousel = (
308
+ props: MoneyLionOfferCarouselProps &
309
+ Omit<EventHandlerContextType, "rateTableUuid" | "leadUuid">
310
+ ) => {
311
+ const { fallbackUI, onError } = props;
312
+
313
+ const handleError = (err: CustomError): void => {
314
+ onError?.(err);
315
+ };
316
+
317
+ return (
318
+ <ErrorBoundary fallbackUI={fallbackUI} onError={handleError}>
319
+ <InternalMoneyLionOfferCarousel {...props} />
320
+ </ErrorBoundary>
321
+ );
322
+ };
@@ -36,50 +36,46 @@ export const localCnfContext: CnfContext = {
36
36
  icon: null,
37
37
  },
38
38
  type: "business",
39
- themeColors: {
40
- backgroundCritical: "#ff7200",
41
- backgroundCriticalFaded: "#fff2e8",
42
- backgroundCriticalHighlighted: "#ee6a00",
43
- backgroundDisabled: "#eeeeee",
44
- backgroundDisabledFaded: "#f6f6f6",
45
- backgroundElevationBase: "#ffffff",
46
- backgroundElevationOverlay: "#ffffff",
47
- backgroundElevationRaised: "#ffffff",
48
- backgroundNeutral: "#e2e2e2",
49
- backgroundNeutralFaded: "#f5f5f5",
50
- backgroundNeutralHighlighted: "#d7d7d7",
51
- backgroundPage: "#ffffff",
52
- backgroundPageFaded: "#f9f9f9",
53
- backgroundPositive: "#00e5c4",
54
- backgroundPositiveFaded: "#e8fffc",
55
- backgroundPositiveHighlighted: "#00d9b9",
56
- backgroundPrimary: "#00e5c4",
57
- backgroundPrimaryFaded: "#e8fffc",
58
- backgroundPrimaryHighlighted: "#00d9b9",
59
- backgroundWarning: "#facc15",
60
- backgroundWarningFaded: "#fffae9",
61
- backgroundWarningHighlighted: "#edc113",
62
- black: "#000000",
63
- borderCritical: "#d35d00",
64
- borderCriticalFaded: "#fce3ce",
65
- borderDisabled: "#e2e2e2",
66
- borderNeutral: "#bbbbbb",
67
- borderNeutralFaded: "#e1e1e1",
68
- borderPositive: "#00bfa3",
69
- borderPositiveFaded: "#b8faf2",
70
- borderPrimary: "#00bfa3",
71
- borderPrimaryFaded: "#b8faf2",
72
- borderWarning: "#cfa90f",
73
- borderWarningFaded: "#faedbb",
74
- brand: "#00e5c4",
75
- foregroundCritical: "#a94900",
76
- foregroundDisabled: "#cccccc",
77
- foregroundNeutral: "#181818",
78
- foregroundNeutralFaded: "#666666",
79
- foregroundPositive: "#007362",
80
- foregroundPrimary: "#007362",
81
- foregroundWarning: "#7b6305",
82
- white: "#ffffff",
39
+ // reshapedThemeColors is being used as fallback in the getThemeColors util
40
+ reshapedThemeColors: {
41
+ backgroundCritical: { hex: "#ff7200", hexDark: "#ff7200" },
42
+ backgroundCriticalFaded: { hex: "#fff2ec", hexDark: "#361e11" },
43
+ backgroundDisabled: { hex: "#eeeeed", hexDark: "#252523" },
44
+ backgroundDisabledFaded: { hex: "#f7f6f6", hexDark: "#1c1b19" },
45
+ backgroundElevationBase: { hex: "#ffffff", hexDark: "#171615" },
46
+ backgroundElevationOverlay: { hex: "#ffffff", hexDark: "#1e1e1c" },
47
+ backgroundElevationRaised: { hex: "#ffffff", hexDark: "#191917" },
48
+ backgroundNeutral: { hex: "#e2e2e2", hexDark: "#3f3c35" },
49
+ backgroundNeutralFaded: { hex: "#f4f4f4", hexDark: "#24221e" },
50
+ backgroundPage: { hex: "#ffffff", hexDark: "#0e0e0e" },
51
+ backgroundPageFaded: { hex: "#fbfbfb", hexDark: "#141413" },
52
+ backgroundPositive: { hex: "#00e5c4", hexDark: "#00e5c4" },
53
+ backgroundPositiveFaded: { hex: "#ebfff9", hexDark: "#122a24" },
54
+ backgroundPrimary: { hex: "#00e5c4", hexDark: "#00e5c4" },
55
+ backgroundPrimaryFaded: { hex: "#ebfff9", hexDark: "#122a24" },
56
+ backgroundWarning: { hex: "#facc15", hexDark: "#facc15" },
57
+ backgroundWarningFaded: { hex: "#fff6dd", hexDark: "#2b2410" },
58
+ black: { hex: "#000000" },
59
+ borderCritical: { hex: "#aa4a00", hexDark: "#f4b89b" },
60
+ borderCriticalFaded: { hex: "#f3dcd0", hexDark: "#553220" },
61
+ borderDisabled: { hex: "#e3e2e0", hexDark: "#282725" },
62
+ borderNeutral: { hex: "#0000001f", hexDark: "#ffffff24" },
63
+ borderNeutralFaded: { hex: "#00000014", hexDark: "#ffffff14" },
64
+ borderPositive: { hex: "#007c6a", hexDark: "#bbf7e7" },
65
+ borderPositiveFaded: { hex: "#cfede5", hexDark: "#21443c" },
66
+ borderPrimary: { hex: "#007c6a", hexDark: "#bbf7e7" },
67
+ borderPrimaryFaded: { hex: "#cfede5", hexDark: "#21443c" },
68
+ borderWarning: { hex: "#816802", hexDark: "#fbf3db" },
69
+ borderWarningFaded: { hex: "#ece2c4", hexDark: "#453c1e" },
70
+ brand: { hex: "#00e5c4" },
71
+ foregroundCritical: { hex: "#aa4a00", hexDark: "#f4b89b" },
72
+ foregroundDisabled: { hex: "#cdccc9", hexDark: "#4c4a47" },
73
+ foregroundNeutral: { hex: "#2e2e2e", hexDark: "#f4f4f4" },
74
+ foregroundNeutralFaded: { hex: "#6a6a6a", hexDark: "#c9c9c9" },
75
+ foregroundPositive: { hex: "#007c6a", hexDark: "#bbf7e7" },
76
+ foregroundPrimary: { hex: "#007c6a", hexDark: "#bbf7e7" },
77
+ foregroundWarning: { hex: "#816802", hexDark: "#fbf3db" },
78
+ white: { hex: "#ffffff" },
83
79
  },
84
80
  isLicensed: true,
85
81
  allowedMarks: { logo: true, name: true },
@@ -68,8 +68,10 @@ const defaultFontFamily: FontFamily = {
68
68
  interface ThemeContextType {
69
69
  theme: ThemeColors;
70
70
  fontFamily: FontFamily;
71
+ isDarkTheme: boolean;
71
72
  updateTheme: (newTheme: Partial<ThemeColors>) => void;
72
73
  updateFontFamily: (newFontFamily: Partial<FontFamily>) => void;
74
+ setIsDarkTheme: (isDark: boolean) => void;
73
75
  }
74
76
 
75
77
  export const ThemeContext = createContext<ThemeContextType | null>(null);
@@ -85,12 +87,14 @@ export const useTheme = () => {
85
87
  interface ThemeProviderProps {
86
88
  themeColors?: ThemeColors;
87
89
  fontFamily?: Partial<FontFamily>;
90
+ isDarkTheme?: boolean;
88
91
  children: ReactNode;
89
92
  }
90
93
 
91
94
  export const ThemeProvider: FC<ThemeProviderProps> = ({
92
- themeColors = {} as ThemeColors, // should be safe to assert as ThemePalette since the fallback colors are already handled by the consumers of ThemeProvider
95
+ themeColors = {} as ThemeColors,
93
96
  fontFamily = {},
97
+ isDarkTheme = false,
94
98
  children,
95
99
  }) => {
96
100
  const [theme, setTheme] = useState<ThemeColors>(themeColors);
@@ -98,6 +102,7 @@ export const ThemeProvider: FC<ThemeProviderProps> = ({
98
102
  ...defaultFontFamily,
99
103
  ...fontFamily,
100
104
  });
105
+ const [darkTheme, setDarkTheme] = useState<boolean>(isDarkTheme);
101
106
 
102
107
  const updateTheme = (newTheme: Partial<ThemeColors>) => {
103
108
  setTheme((prevTheme) => ({
@@ -113,11 +118,17 @@ export const ThemeProvider: FC<ThemeProviderProps> = ({
113
118
  }));
114
119
  };
115
120
 
121
+ const setIsDarkTheme = (isDark: boolean) => {
122
+ setDarkTheme(isDark);
123
+ };
124
+
116
125
  const contextValue = {
117
126
  theme,
118
127
  fontFamily: fonts,
128
+ isDarkTheme: darkTheme,
119
129
  updateTheme,
120
130
  updateFontFamily,
131
+ setIsDarkTheme,
121
132
  };
122
133
 
123
134
  return (
@@ -0,0 +1,29 @@
1
+ import type { ReshapedThemeColors } from "../capabilities/core/src/system/cnfContext/schemas/Brand";
2
+ import { localCnfContext } from "../config/mocks/cnfContext";
3
+ import type { ThemeColors } from "../context/ThemeProvider";
4
+
5
+ /** This util checks for missing theme colors from reshapedThemeColors in the API response
6
+ * and merges them with fallback theme colors.
7
+ * It returns a ThemeColors object with the appropriate colors based on the isDarkTheme flag.
8
+ */
9
+ export const getThemeColors = (
10
+ reshapedThemeColors: ReshapedThemeColors,
11
+ isDarkTheme: boolean = false
12
+ ): ThemeColors => {
13
+ const defaultThemeColors = localCnfContext.brand.reshapedThemeColors;
14
+
15
+ const themeColors = Object.keys(defaultThemeColors).reduce((acc, key) => {
16
+ const reshapedColor = reshapedThemeColors[key as keyof ReshapedThemeColors];
17
+ const defaultColor = defaultThemeColors[key as keyof ReshapedThemeColors];
18
+
19
+ const themeColor = reshapedColor ?? defaultColor;
20
+
21
+ acc[key as keyof ThemeColors] = isDarkTheme
22
+ ? (themeColor.hexDark ?? themeColor.hex)
23
+ : themeColor.hex;
24
+
25
+ return acc;
26
+ }, {} as ThemeColors);
27
+
28
+ return themeColors;
29
+ };