@wf-financing/ui 1.2.0 → 1.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wf-financing/ui",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./dist/index.es.js",
@@ -32,14 +32,16 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@tanstack/react-query": "5.81.5",
35
- "@wayflyer/flyui": "196.0.0",
36
- "@wayflyer/flyui-icons": "1.3.2",
35
+ "@wayflyer/flyui-icons": "1.5.0",
37
36
  "framer-motion": "^12.23.0",
38
37
  "react-aria": "^3.41.1",
39
38
  "react-intl": "^6.2.5",
40
39
  "styled-components": "^6.1.19",
41
40
  "@wf-financing/embedded-types": "0.2.6"
42
41
  },
42
+ "peerDependencies": {
43
+ "@wayflyer/flyui": "202.1.0"
44
+ },
43
45
  "publishConfig": {
44
46
  "access": "public"
45
47
  },
package/src/App.tsx CHANGED
@@ -13,6 +13,7 @@ type AppPropsType = {
13
13
  mockedMode?: MockedModeType;
14
14
  partnerCallback: PartnerCallbackType;
15
15
  onWidgetClose: () => void;
16
+ portalContainer: HTMLElement;
16
17
  };
17
18
 
18
19
  const queryClient = new QueryClient();
@@ -20,7 +21,14 @@ const messages = {
20
21
  en: enMessages,
21
22
  };
22
23
 
23
- export const App = ({ partnerDesignId, companyToken, mockedMode, partnerCallback, onWidgetClose }: AppPropsType) => {
24
+ export const App = ({
25
+ partnerDesignId,
26
+ companyToken,
27
+ mockedMode,
28
+ partnerCallback,
29
+ onWidgetClose,
30
+ portalContainer,
31
+ }: AppPropsType) => {
24
32
  const locale = 'en';
25
33
 
26
34
  const intl: IntlShape = createIntl(
@@ -33,7 +41,7 @@ export const App = ({ partnerDesignId, companyToken, mockedMode, partnerCallback
33
41
 
34
42
  return (
35
43
  <QueryClientProvider client={queryClient}>
36
- <PartnerContext.Provider value={{ companyToken, mockedMode, partnerCallback, onWidgetClose }}>
44
+ <PartnerContext.Provider value={{ companyToken, mockedMode, partnerCallback, onWidgetClose, portalContainer }}>
37
45
  <FlyUIProvider theme={partnerDesignId} intl={intl}>
38
46
  <CtaWidget />
39
47
  </FlyUIProvider>
@@ -1,17 +1,15 @@
1
1
  import { Flex, Text } from '@wayflyer/flyui';
2
- import { IconCheck16Line } from '@wayflyer/flyui-icons/16/line';
2
+ import { IconCheck12Line } from '@wayflyer/flyui-icons/12/line';
3
3
 
4
- export const BulletList = ({ items }: { items: string[] }) => {
5
- return (
6
- <Flex gap="4">
7
- {items.map((bullet_point: string) => (
8
- <Flex key={bullet_point} gap="1" align="center">
9
- <IconCheck16Line />
10
- <Text size="sm" fontWeight="medium" fontStyle="regular" lineHeight="tight">
11
- {bullet_point}
12
- </Text>
13
- </Flex>
14
- ))}
15
- </Flex>
16
- );
17
- };
4
+ export const BulletList = ({ items }: { items: string[] }) => (
5
+ <Flex gap="4">
6
+ {items.map((bullet_point: string) => (
7
+ <Flex key={bullet_point} gap="1" align="center">
8
+ <IconCheck12Line />
9
+ <Text size="sm" fontWeight="medium" fontStyle="regular" lineHeight="tight">
10
+ {bullet_point}
11
+ </Text>
12
+ </Flex>
13
+ ))}
14
+ </Flex>
15
+ );
@@ -4,11 +4,15 @@ import { SdkScenarios, MockedModeType } from '@wf-financing/embedded-types';
4
4
  import { App } from '../../App';
5
5
  import { CtaBanner } from './CtaBanner';
6
6
 
7
+ const portalContainer = document.createElement('div');
8
+ document.body.append(portalContainer);
9
+
7
10
  const defaultArgs = {
8
11
  partnerDesignId: 'whiteLabel' as Themes,
9
12
  companyToken: 'demo-token',
10
13
  partnerCallback: () => {},
11
14
  onWidgetClose: () => {},
15
+ portalContainer,
12
16
  };
13
17
 
14
18
  type CtaBannerStoryArgs = typeof defaultArgs & { mockedMode: MockedModeType };
@@ -7,16 +7,16 @@ import { HeaderActions } from './HeaderActions';
7
7
 
8
8
  type BannerContainerPropTypes = {
9
9
  theme: Theme;
10
- isMobile: boolean;
10
+ $isMobile: boolean;
11
11
  };
12
12
 
13
13
  const BannerContainer = styled.aside<BannerContainerPropTypes>`
14
- padding: ${({ theme, isMobile }) => theme.spacing(isMobile ? ['3', '3', '3', '4'] : ['4', '4', '4', '6'])};
14
+ padding: ${({ theme, $isMobile }) => theme.spacing($isMobile ? ['3', '3', '3', '4'] : ['4', '4', '4', '6'])};
15
15
  box-shadow: ${({ theme }) => theme.effects.shadow};
16
16
  border-radius: ${({ theme }) => theme.borderRadius(['md'])};
17
17
  display: flex;
18
- flex-direction: ${({ isMobile }) => (isMobile ? 'column' : 'row')};
19
- gap: ${({ isMobile, theme }) => (isMobile ? theme.spacing(['4']) : '0')};
18
+ flex-direction: ${({ $isMobile }) => ($isMobile ? 'column' : 'row')};
19
+ gap: ${({ $isMobile, theme }) => ($isMobile ? theme.spacing(['4']) : '0')};
20
20
  background-color: ${({ theme }) => theme.palette.utility.primaryBrand};
21
21
  `;
22
22
 
@@ -25,7 +25,7 @@ export const CtaBanner = () => {
25
25
  const theme = useTheme();
26
26
 
27
27
  return (
28
- <BannerContainer isMobile={isMobile} theme={theme}>
28
+ <BannerContainer $isMobile={isMobile} theme={theme}>
29
29
  <Flex gap="4" align="center" justify="space-between" width="100%">
30
30
  <CtaBannerContent isMobile={isMobile} isTablet={isTablet} />
31
31
  <HeaderActions />
@@ -17,7 +17,7 @@ export const CtaBannerContent = ({ isMobile, isTablet }: CtaBannerContentProps)
17
17
  <>
18
18
  {!sdk.isLoading && (
19
19
  <Flex direction="column" gap="1">
20
- <Text size={isMobile ? 'lg' : 'xl'} fontStyle="regular" fontWeight="medium" lineHeight="normal">
20
+ <Text size={isMobile ? 'base' : 'lg'} fontStyle="regular" fontWeight="medium" lineHeight="normal">
21
21
  {ctaData?.data?.config?.text}
22
22
  </Text>
23
23
  {!(isTablet || isMobile) && ctaData?.data?.config?.bullet_points && (
@@ -1,5 +1,19 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
+
2
3
  import { ConsentModal } from './ConsentModal';
4
+ import { PartnerContext } from '../../utils';
5
+ import { Themes } from '@wayflyer/flyui';
6
+
7
+ const portalContainer = document.createElement('div');
8
+ document.body.append(portalContainer);
9
+
10
+ const appArgs = {
11
+ partnerDesignId: 'whiteLabel' as Themes,
12
+ companyToken: 'demo-token',
13
+ partnerCallback: () => {},
14
+ onWidgetClose: () => {},
15
+ portalContainer,
16
+ };
3
17
 
4
18
  const fn = (): void => {};
5
19
 
@@ -21,7 +35,11 @@ export const Default: Story = {
21
35
  isModalOpen: true,
22
36
  setIsModalOpen: fn,
23
37
  },
24
- render: (args) => <ConsentModal {...args} />,
38
+ render: (args) => (
39
+ <PartnerContext.Provider value={{ ...appArgs }}>
40
+ <ConsentModal {...args} />
41
+ </PartnerContext.Provider>
42
+ ),
25
43
  };
26
44
 
27
45
  export const WithContent: Story = {
@@ -30,13 +48,17 @@ export const WithContent: Story = {
30
48
  setIsModalOpen: fn,
31
49
  },
32
50
  render: (args) => (
33
- <ConsentModal {...args}>
34
- <div style={{ padding: '20px', background: 'white' }}>
35
- <h2>Consent Agreement</h2>
36
- <p>Please agree to the terms to proceed.</p>
37
- <button>Continue to Wayflyer</button>
38
- <button>Cancel</button>
39
- </div>
40
- </ConsentModal>
51
+ <>
52
+ <PartnerContext.Provider value={{ ...appArgs }}>
53
+ <ConsentModal {...args}>
54
+ <div style={{ padding: '20px', background: 'white' }}>
55
+ <h2>Consent Agreement</h2>
56
+ <p>Please agree to the terms to proceed.</p>
57
+ <button>Continue to Wayflyer</button>
58
+ <button>Cancel</button>
59
+ </div>
60
+ </ConsentModal>
61
+ </PartnerContext.Provider>
62
+ </>
41
63
  ),
42
64
  };
@@ -1,25 +1,33 @@
1
1
  import { Flex, Image, Text, useDetectDeviceSize } from '@wayflyer/flyui';
2
2
  import { useIntl } from 'react-intl';
3
3
 
4
+ import { styled } from 'styled-components';
5
+ import { useDetectSmallScreen } from '../../hooks';
4
6
  import { FundingSteps } from './FundingSteps';
5
7
  import { Modal } from './Modal';
6
8
  import { ModalFooter } from './ModalFooter';
7
- import { useDetectSmallScreen } from '../../hooks';
8
9
 
9
10
  type ConsentModalProps = {
10
11
  isModalOpen: boolean;
11
12
  setIsModalOpen: (isOpen: boolean) => void;
12
13
  };
13
14
 
15
+ const ImageContainer = styled(Image)`
16
+ width: 100%;
17
+ max-width: none;
18
+ `;
19
+
14
20
  export const ConsentModal = ({ isModalOpen, setIsModalOpen }: ConsentModalProps) => {
15
21
  const { formatMessage } = useIntl();
16
22
  const isSmallScreen = useDetectSmallScreen();
17
23
  const { isMobile } = useDetectDeviceSize();
18
24
 
25
+ const isReducedSpacing = isSmallScreen || isMobile;
26
+
19
27
  return (
20
28
  <Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen}>
21
- {!isSmallScreen && <Image src="https://static.wayflyer.com/flyui-assets/logos/wayflyer-ef.png" />}
22
- <Flex direction="column" gap="8" padding={isSmallScreen || isMobile ? '4' : '6'}>
29
+ {!isSmallScreen && <ImageContainer src="https://static.wayflyer.com/flyui-assets/logos/wayflyer-ef.png" />}
30
+ <Flex direction="column" gap="8" padding={isReducedSpacing ? '4' : '6'}>
23
31
  <Flex direction="column" gap="6">
24
32
  <Text fontStyle="regular" fontWeight="medium" lineHeight="tight" size="2xl">
25
33
  {formatMessage({ id: 'ctaModal.title' })}
@@ -7,7 +7,7 @@ export const FundingSteps = () => {
7
7
  const { formatMessage } = useIntl();
8
8
 
9
9
  return (
10
- <Flex direction="column" gap="3">
10
+ <Flex direction="column" gap="2">
11
11
  {MODAL_ITEMS.map(({ id, title, subtitle }) => (
12
12
  <Flex key={id} direction="column">
13
13
  <Text fontWeight="medium">{formatMessage(title)}</Text>
@@ -2,9 +2,11 @@ import { Dialog } from '@wayflyer/flyui';
2
2
  import { AnimatePresence, LazyMotion, domAnimation } from 'framer-motion';
3
3
  import { ReactNode, useId, useRef } from 'react';
4
4
  import { UNSAFE_PortalProvider as PortalProvider } from 'react-aria';
5
+ import { usePartnerContext } from '../../hooks';
5
6
  import { DialogBody } from './modalEnhancements';
6
7
 
7
8
  import { MotionModalOverlay, MotionModalPrimitive, fullSize, opaque, small, transparent } from './modalEnhancements';
9
+ import { useRemoveInerted } from '../../hooks';
8
10
 
9
11
  type ModalProps = {
10
12
  isModalOpen: boolean;
@@ -14,13 +16,16 @@ type ModalProps = {
14
16
 
15
17
  export const Modal = ({ isModalOpen, setIsModalOpen, children }: ModalProps) => {
16
18
  const modalId = useId();
19
+ const { portalContainer } = usePartnerContext();
17
20
  const scrollableElRef = useRef(null);
18
21
 
22
+ useRemoveInerted(isModalOpen, portalContainer);
23
+
19
24
  return (
20
25
  <LazyMotion features={domAnimation}>
21
26
  <AnimatePresence>
22
27
  {isModalOpen && (
23
- <PortalProvider getContainer={() => document.getElementById('app-container') ?? document.body}>
28
+ <PortalProvider getContainer={() => portalContainer}>
24
29
  <MotionModalPrimitive
25
30
  isDismissable
26
31
  shouldCloseOnInteractOutside={(e) => e.getAttribute('data-id') === 'modal-mask'}
@@ -31,7 +36,7 @@ export const Modal = ({ isModalOpen, setIsModalOpen, children }: ModalProps) =>
31
36
  animate={fullSize}
32
37
  exit={small}
33
38
  transition={{ duration: 0.3 }}
34
- style={{ width: '438px', border: 'none', maxWidth: '90vw', maxHeight: '90vh' }}
39
+ style={{ width: '438px', border: 'none', maxWidth: '90vw', maxHeight: '100vh' }}
35
40
  >
36
41
  <MotionModalOverlay
37
42
  data-id="modal-mask"
@@ -1,4 +1,4 @@
1
- import { Button, Flex, Link, Text } from '@wayflyer/flyui';
1
+ import { Button, Flex, Link, Text, useDetectDeviceSize } from '@wayflyer/flyui';
2
2
  import { IconArrowOnSquareUpRight16Line } from '@wayflyer/flyui-icons/16/line';
3
3
  import { StartHostedApplicationResponseType } from '@wf-financing/embedded-types';
4
4
  import { FormattedMessage } from 'react-intl';
@@ -10,6 +10,7 @@ type ModalFooterType = {
10
10
  };
11
11
 
12
12
  export const ModalFooter = ({ setOpen }: ModalFooterType) => {
13
+ const { isMobile } = useDetectDeviceSize();
13
14
  const startHostedAppMutation = useStartHostedApplication();
14
15
  const isSmallScreen = useDetectSmallScreen();
15
16
 
@@ -31,7 +32,7 @@ export const ModalFooter = ({ setOpen }: ModalFooterType) => {
31
32
 
32
33
  return (
33
34
  <Flex direction="column" gap="3">
34
- <Flex gap="2" direction={isSmallScreen ? 'column' : 'row'}>
35
+ <Flex gap="2" direction={isSmallScreen || isMobile ? 'column' : 'row'}>
35
36
  <Button fullWidth onClick={handleStartApplication} variant="Primary" external>
36
37
  <FormattedMessage id="ctaModal.action" />
37
38
  <IconArrowOnSquareUpRight16Line />
@@ -0,0 +1,18 @@
1
+ const dmSansParams = {
2
+ fontFamily: 'DM Sans',
3
+ fontUrl: 'https://static.wayflyer.com/flyui-assets/fonts/dm-sans/DMSans-VariableFont_opsz,wght.ttf',
4
+ };
5
+ const sourceSansParams = {
6
+ fontFamily: 'Source Sans Pro',
7
+ fontUrl: 'https://static.wayflyer.com/flyui-assets/fonts/source-sans/SourceSans3-VariableFont_wght.ttf',
8
+ };
9
+ const merrionSansParams = {
10
+ fontFamily: 'Merrion Sans',
11
+ fontUrl: 'https://static.wayflyer.com/flyui-assets/fonts/merrion-sans/Merrion_Sans-Medium.woff2',
12
+ };
13
+
14
+ export const FONT_PARAMS = {
15
+ dmSansParams,
16
+ sourceSansParams,
17
+ merrionSansParams,
18
+ };
@@ -1,2 +1,4 @@
1
1
  export { WAYFLYER_HEADLESS_SDK_ID } from './scriptId';
2
2
  export { HEADLESS_SDK_URL } from './url';
3
+ export { ROOTS_CONFIG } from './rootsParameters';
4
+ export { FONT_PARAMS } from './fontParameters';
@@ -0,0 +1,19 @@
1
+ const STYLED_COMPONENTS_STYLES_ROOT_PARAMS = {
2
+ rootName: 'styledComponentsStyleRoot',
3
+ rootElement: 'div',
4
+ rootElementId: 'styled-component-style-target',
5
+ };
6
+
7
+ const PORTAL_ROOT_PARAMS = {
8
+ rootName: 'portalRoot',
9
+ rootElement: 'div',
10
+ rootElementId: 'portal-root',
11
+ };
12
+
13
+ const BANNER_ROOT_PARAMS = {
14
+ rootName: 'bannerRoot',
15
+ rootElement: 'div',
16
+ rootElementId: 'banner-root',
17
+ };
18
+
19
+ export const ROOTS_CONFIG = [STYLED_COMPONENTS_STYLES_ROOT_PARAMS, PORTAL_ROOT_PARAMS, BANNER_ROOT_PARAMS];
@@ -3,3 +3,4 @@ export { useCtaBanner } from './useCtaBanner';
3
3
  export { useStartHostedApplication } from './useStartHostedApplication';
4
4
  export { useDetectSmallScreen } from './useDetectSmallScreen';
5
5
  export { useContinueHostedApplication } from './useContinueHostedApplication';
6
+ export { useRemoveInerted } from './useRemoveInerted';
@@ -0,0 +1,24 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ export const useRemoveInerted = (isModalOpen: boolean, portalContainer: HTMLElement) => {
4
+ const inertedElements = useRef<HTMLElement[]>([]);
5
+
6
+ useEffect(() => {
7
+ if (!isModalOpen || !portalContainer) return;
8
+
9
+ inertedElements.current = [];
10
+ let hostElement: HTMLElement | null = (portalContainer.getRootNode() as ShadowRoot)?.host as HTMLElement;
11
+ while (hostElement) {
12
+ if (hostElement.hasAttribute?.('inert')) {
13
+ inertedElements.current.push(hostElement);
14
+ hostElement.removeAttribute('inert');
15
+ }
16
+ hostElement = hostElement.parentElement;
17
+ }
18
+
19
+ return () => {
20
+ inertedElements.current.forEach((el) => el.setAttribute('inert', ''));
21
+ inertedElements.current = [];
22
+ };
23
+ }, [isModalOpen, portalContainer]);
24
+ };
package/src/main.tsx CHANGED
@@ -1,36 +1,48 @@
1
1
  import type { Themes } from '@wayflyer/flyui';
2
2
  import { PartnerCallbackType, MockedModeType } from '@wf-financing/embedded-types';
3
3
  import ReactDOM from 'react-dom/client';
4
+ import { StyleSheetManager } from 'styled-components';
4
5
 
5
6
  import { App } from './App';
7
+ import { createRoots } from './utils';
6
8
 
7
9
  let root: ReactDOM.Root | undefined = undefined;
8
10
 
9
- export const mountToTarget = (
11
+ export const mountToTarget = async (
10
12
  targetId: string,
11
13
  partnerDesignId: Themes,
12
14
  partnerCallback: PartnerCallbackType,
13
15
  companyToken: string,
14
16
  mockedMode?: MockedModeType,
15
- ) => {
16
- const targetElement = document.getElementById(targetId);
17
- if (!targetElement) throw new Error(`Target element with id "${targetId}" not found.`);
17
+ ): Promise<void> => {
18
+ const hostEl = document.getElementById(targetId);
19
+ if (!hostEl) throw new Error(`Target element with id "${targetId}" not found.`);
18
20
 
19
- if (!root) root = ReactDOM.createRoot(targetElement);
21
+ const shadow = hostEl.shadowRoot ?? hostEl.attachShadow({ mode: 'open' });
20
22
 
21
- function handleCloseWidget() {
23
+ // TODO remove after resolving an issue with CORS
24
+ // await applyFont(shadow, partnerDesignId);
25
+
26
+ const { styledComponentsStyleRoot, portalRoot, bannerRoot } = createRoots(shadow);
27
+
28
+ if (!root) root = ReactDOM.createRoot(bannerRoot);
29
+
30
+ const handleCloseWidget = () => {
22
31
  if (!root) throw new Error('Root is not found');
23
32
  root.unmount();
24
33
  root = undefined;
25
- }
34
+ };
26
35
 
27
36
  root.render(
28
- <App
29
- partnerDesignId={partnerDesignId}
30
- companyToken={companyToken}
31
- mockedMode={mockedMode}
32
- partnerCallback={partnerCallback}
33
- onWidgetClose={handleCloseWidget}
34
- />,
37
+ <StyleSheetManager target={styledComponentsStyleRoot}>
38
+ <App
39
+ partnerDesignId={partnerDesignId}
40
+ companyToken={companyToken}
41
+ mockedMode={mockedMode}
42
+ partnerCallback={partnerCallback}
43
+ onWidgetClose={handleCloseWidget}
44
+ portalContainer={portalRoot}
45
+ />
46
+ </StyleSheetManager>,
35
47
  );
36
48
  };
@@ -0,0 +1,26 @@
1
+ import type { Themes } from '@wayflyer/flyui';
2
+
3
+ import { loadFont } from './loadFont';
4
+ import { FONT_PARAMS } from '../config';
5
+
6
+ type ApplyFontsType = (shadow: ShadowRoot, partnerDesignId: Themes) => Promise<void>;
7
+
8
+ export const applyFont: ApplyFontsType = async (shadow, partnerDesignId) => {
9
+ const { dmSansParams, sourceSansParams, merrionSansParams } = FONT_PARAMS;
10
+
11
+ switch (partnerDesignId) {
12
+ case 'whiteLabel':
13
+ await loadFont(shadow, dmSansParams);
14
+ break;
15
+ case 'bigCommerce':
16
+ await loadFont(shadow, sourceSansParams);
17
+ break;
18
+ case 'wayflyer':
19
+ case 'staff':
20
+ case 'defaultTheme':
21
+ await loadFont(shadow, merrionSansParams);
22
+ break;
23
+ default:
24
+ await loadFont(shadow, merrionSansParams);
25
+ }
26
+ };
@@ -0,0 +1,27 @@
1
+ import { ROOTS_CONFIG } from '../config';
2
+
3
+ type CreateRootsReturnType = {
4
+ styledComponentsStyleRoot: HTMLElement;
5
+ portalRoot: HTMLElement;
6
+ bannerRoot: HTMLElement;
7
+ };
8
+
9
+ type CreateRootsType = (shadow: ShadowRoot) => CreateRootsReturnType;
10
+
11
+ export const createRoots: CreateRootsType = (shadow) => {
12
+ return ROOTS_CONFIG.reduce((acc, element) => {
13
+ const { rootElement, rootElementId, rootName } = element;
14
+ let root = shadow.getElementById(rootElementId);
15
+
16
+ if (!root) {
17
+ root = document.createElement(rootElement);
18
+ root.id = rootElementId;
19
+ shadow.appendChild(root);
20
+ }
21
+
22
+ return {
23
+ ...acc,
24
+ [rootName]: root,
25
+ };
26
+ }, {}) as CreateRootsReturnType;
27
+ };
@@ -1,3 +1,5 @@
1
1
  export { initializeHeadlessSdk } from './initializeHeadlessSdk';
2
2
  export { loadScriptAndInitializeSdk } from './loadScriptAndInitializeSdk';
3
3
  export { PartnerContext, type PartnerContextType } from './partnerContext';
4
+ export { createRoots } from './createRoots';
5
+ export { applyFont } from './applyFont';
@@ -0,0 +1,28 @@
1
+ type FontParamsType = {
2
+ fontFamily: string;
3
+ fontUrl: string;
4
+ };
5
+
6
+ type LoadFontType = (shadow: ShadowRoot, fontParams: FontParamsType) => Promise<void>;
7
+
8
+ export const loadFont: LoadFontType = async (shadow, fontParams) => {
9
+ const { fontFamily, fontUrl } = fontParams;
10
+ const fontStylesId = 'font-styles';
11
+
12
+ const font = new FontFace(fontFamily, `url(${fontUrl})`);
13
+
14
+ await font.load();
15
+ document.fonts.add(font);
16
+
17
+ let fontStyles = shadow.getElementById(fontStylesId);
18
+ if (!fontStyles) {
19
+ fontStyles = document.createElement('style');
20
+ fontStyles.id = fontStylesId;
21
+ fontStyles.textContent = `
22
+ :host { font-family: "${fontFamily}", sans-serif; }
23
+ ::slotted(*) { font-family: inherit; }
24
+ `;
25
+
26
+ shadow.appendChild(fontStyles);
27
+ }
28
+ };
@@ -7,6 +7,7 @@ export type PartnerContextType = {
7
7
  partnerCallback: PartnerCallbackType;
8
8
  mockedMode?: MockedModeType;
9
9
  onWidgetClose: () => void;
10
+ portalContainer: HTMLElement;
10
11
  };
11
12
 
12
13
  type PartnerContextWithDefaultType = PartnerContextType | null;
@@ -1,31 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import { CtaBannerContent } from './CtaBannerContent';
3
-
4
- const meta: Meta<typeof CtaBannerContent> = {
5
- title: 'BannerContent',
6
- component: CtaBannerContent,
7
- argTypes: {
8
- isMobile: {
9
- control: 'boolean',
10
- description: 'Render mobile version',
11
- defaultValue: false,
12
- },
13
- isTablet: {
14
- control: 'boolean',
15
- description: 'Render tablet version',
16
- defaultValue: false,
17
- },
18
- },
19
- };
20
-
21
- export default meta;
22
-
23
- type Story = StoryObj<typeof CtaBannerContent>;
24
-
25
- export const Default: Story = {
26
- args: {
27
- isMobile: false,
28
- isTablet: false,
29
- },
30
- render: (args) => <CtaBannerContent {...args} />,
31
- };