Skip to content

Commit

Permalink
Merge pull request #18 from white-label-loyalty/feat/sdk-cross-platfo…
Browse files Browse the repository at this point in the history
…rm-link-handling

Feat/sdk cross platform link handling
  • Loading branch information
iamgraeme authored Nov 16, 2024
2 parents 5ca2963 + 58945e5 commit 57c4466
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 49 deletions.
22 changes: 17 additions & 5 deletions lib/components/atoms/BaseBanner/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as React from 'react';
import { StyleSheet, View } from 'react-native';
import { Pressable, StyleSheet } from 'react-native';
import { useWllSdk } from '../../../context/WllSdkContext';
import { Tile } from '../../../types/tile';
import { useHandleTilePress } from '../../../hooks/useHandleTilePress';
import { BannerTileConfig, Tile } from '../../../types/tile';

const BannerContext = React.createContext<Tile | null>(null);

Expand All @@ -20,19 +21,30 @@ type BaseBannerProps = {

const BaseBanner: React.FC<BaseBannerProps> = ({ tile, children }) => {
const { theme } = useWllSdk();
const { ctaLink, ctaLinkTarget, title } =
tile.configuration as BannerTileConfig;

const handlePress = useHandleTilePress(ctaLink, ctaLinkTarget);

return (
<BannerContext.Provider value={tile}>
<View
style={[
<Pressable
style={({ pressed }) => [
styles.container,
{
backgroundColor: theme.surface,
borderRadius: theme.sizes.borderRadiusLg,
opacity: pressed ? 0.7 : 1,
},
]}
onPress={handlePress}
disabled={!ctaLink}
accessible={true}
accessibilityRole="button"
accessibilityLabel={`${title}${ctaLink ? ' - Click to open' : ''}`}
>
{children}
</View>
</Pressable>
</BannerContext.Provider>
);
};
Expand Down
30 changes: 22 additions & 8 deletions lib/components/atoms/BaseTile/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import React, { createContext, FC, ReactNode, useContext } from 'react';
import { FlexStyle, StyleSheet, View, ViewStyle } from 'react-native';
import {
FlexStyle,
Pressable,
StyleSheet,
View,
ViewStyle,
} from 'react-native';
import { useWllSdk } from '../../../context/WllSdkContext';
import { useHandleTilePress } from '../../../hooks/useHandleTilePress';
import { useTileSize } from '../../../hooks/useTileSize';
import { ImagePropsNoSource } from '../../../types/common';
import { ContentTileConfig, Tile } from '../../../types/tile';
Expand Down Expand Up @@ -53,11 +60,12 @@ const BaseTileRoot: FC<{ children: ReactNode; style?: ViewStyle }> = ({
const tile = useTileContext();
const { theme } = useWllSdk();
const { isHalfSize } = useTileSize(tile);
const { artworkUrl } = tile.configuration as ContentTileConfig;
const { ctaLink, ctaLinkTarget, title } =
tile.configuration as ContentTileConfig;
const handlePress = useHandleTilePress(ctaLink, ctaLinkTarget);

const layout: LayoutProps = {
flexDirection: 'column',
// Center content vertically for half tiles, start at top for full tiles
justifyContent: isHalfSize ? 'center' : 'flex-start',
alignItems: 'stretch',
};
Expand All @@ -71,20 +79,26 @@ const BaseTileRoot: FC<{ children: ReactNode; style?: ViewStyle }> = ({
});

return (
<View
style={[
<Pressable
style={({ pressed }) => [
styles.container,
{
backgroundColor: theme.surface,
borderRadius: responsiveStyles.borderRadius,
aspectRatio: isHalfSize ? 2 : 1,
opacity: pressed ? 0.7 : 1,
},
layout,
style,
]}
onPress={handlePress}
disabled={!ctaLink}
accessible={true}
accessibilityRole="button"
accessibilityLabel={`${title}${ctaLink ? ' - Click to open' : ''}`}
>
{children}
</View>
</Pressable>
);
};

Expand Down Expand Up @@ -170,7 +184,7 @@ const BaseTileMedia: FC<ImagePropsNoSource> = (props) => {
const BaseTileTitle: FC = () => {
const tile = useTileContext();
const { theme } = useWllSdk();
const { title, linkURL, artworkUrl } =
const { title, ctaLink, artworkUrl } =
tile.configuration as ContentTileConfig;
const { isHalfSize } = useTileSize(tile);

Expand All @@ -182,7 +196,7 @@ const BaseTileTitle: FC = () => {
<Text variant="title" accessibilityLabel={title}>
{title}
</Text>
{linkURL && (
{ctaLink && (
<Icon name="ChevronRight" color={theme.derivedSurfaceText[20]} />
)}
</>
Expand Down
16 changes: 8 additions & 8 deletions lib/components/molecules/Carousel/Carousel.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { SectionType } from '../../../types/section';
import { TileHeight, TileType, UrlTarget } from '../../../types/tile';
import { CTALinkTarget, TileHeight, TileType } from '../../../types/tile';
import Carousel from './index';

export default {
Expand Down Expand Up @@ -34,7 +34,7 @@ Default.args = {
configuration: {
defaultLocale: 'en',
details: [],
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
locale: 'en',
title: 'New iPhone 15 Pro',
ctaLink: '/products/iphone15pro',
Expand All @@ -56,7 +56,7 @@ Default.args = {
configuration: {
defaultLocale: 'en',
details: [],
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
locale: 'en',
title: 'Unlock Your Rewards',
ctaLink: '#member-card',
Expand All @@ -78,7 +78,7 @@ Default.args = {
configuration: {
defaultLocale: 'en',
details: [],
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
locale: 'en',
title: 'TechCon 2024',
ctaLink: 'https://techcon2024.com/register',
Expand Down Expand Up @@ -121,7 +121,7 @@ SingleItem.args = {
configuration: {
defaultLocale: 'en',
details: [],
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
locale: 'en',
title: 'TechCon 2024',
ctaLink: 'https://techcon2024.com/register',
Expand Down Expand Up @@ -164,7 +164,7 @@ ManyItems.args = {
configuration: {
defaultLocale: 'en',
details: [],
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
locale: 'en',
title: 'New iPhone 15 Pro',
ctaLink: '/products/iphone15pro',
Expand All @@ -186,7 +186,7 @@ ManyItems.args = {
configuration: {
defaultLocale: 'en',
details: [],
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
locale: 'en',
title: 'Unlock Your Rewards',
ctaLink: '#member-card',
Expand All @@ -208,7 +208,7 @@ ManyItems.args = {
configuration: {
defaultLocale: 'en',
details: [],
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
locale: 'en',
title: 'TechCon 2024',
ctaLink: 'https://techcon2024.com/register',
Expand Down
6 changes: 3 additions & 3 deletions lib/components/organisms/BannerTile/BannerTile.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Meta, StoryFn } from '@storybook/react';
import * as React from 'react';
import BannerTile from '.';
import { TileHeight, TileType, UrlTarget } from '../../../types/tile';
import { CTALinkTarget, TileHeight, TileType } from '../../../types/tile';

export default {
title: 'components/organisms/BannerTile',
Expand All @@ -27,7 +27,7 @@ Default.args = {
description: 'Get up to 50% off on selected items!',
ctaText: 'SHOP NOW',
ctaLink: 'https://www.example.com',
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
},
},
};
Expand All @@ -48,7 +48,7 @@ WithoutButton.args = {
title: 'Summer Sale',
description: 'Get up to 50% off on selected items!',
ctaLink: 'https://www.example.com',
ctaLinkTarget: UrlTarget.sameWindow,
ctaLinkTarget: CTALinkTarget.sameWindow,
ctaText: '',
},
},
Expand Down
21 changes: 5 additions & 16 deletions lib/components/organisms/BannerTile/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as React from 'react';
import { Linking, StyleSheet, View } from 'react-native';
import { StyleSheet, View } from 'react-native';
import BaseBanner from '../../../components/atoms/BaseBanner';
import { useWllSdk } from '../../../context/WllSdkContext';
import { useHandleTilePress } from '../../../hooks/useHandleTilePress';
import { BannerTileConfig, Tile } from '../../../types/tile';
import { createResponsiveStyle } from '../../../utils/responsiveHelper';
import { Button, ProgressiveImage, Text } from '../../atoms';
Expand Down Expand Up @@ -76,23 +77,11 @@ const BannerTileDescription: React.FC = () => {

const BannerTileCTA: React.FC = () => {
const { configuration } = useBannerContext();
const { ctaText, ctaLink } = configuration as BannerTileConfig;

const handleLinkPress = async (url: string) => {
if (!url) return;
if (await Linking.canOpenURL(url)) {
Linking.openURL(url);
}
};
const { ctaText, ctaLink, ctaLinkTarget } = configuration as BannerTileConfig;
const handlePress = useHandleTilePress(ctaLink, ctaLinkTarget);

if (!ctaText) return null;
return (
<Button
title={ctaText}
variant="accent"
onPress={() => handleLinkPress(ctaLink as string)}
/>
);
return <Button title={ctaText} variant="accent" onPress={handlePress} />;
};

const styles = StyleSheet.create({
Expand Down
8 changes: 5 additions & 3 deletions lib/components/organisms/ContentTile/ContentTile.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import ContentTile from '.';
import { TileHeight, TileType } from '../../../types/tile';
import { CTALinkTarget, TileHeight, TileType } from '../../../types/tile';
import { TileWrapper } from '../../../utils/storybookHelpers';

export default {
Expand Down Expand Up @@ -131,7 +131,8 @@ HalfSizeTextOnlyWithLink.args = {
configuration: {
title: 'Welcome Nick!',
body: 'Lorem ipsum dolor sit amet',
linkURL: 'https://www.example.com',
ctaLink: 'https://www.google.com',
ctaLinkTarget: CTALinkTarget.sameWindow,
},
},
};
Expand All @@ -151,7 +152,8 @@ FullSizeWithLink.args = {
body: 'Click here to learn more',
artworkUrl:
'https://images.pexels.com/photos/1362534/pexels-photo-1362534.jpeg',
linkURL: 'https://www.example.com',
ctaLink: 'https://www.google.com',
ctaLinkTarget: CTALinkTarget.newWindow,
},
},
};
19 changes: 17 additions & 2 deletions lib/context/WllSdkContext.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from 'react';
import { useNavigation } from '../hooks/useNavigationHandler';
import { TGroup } from '../types/group';
import { NavigationConfig } from '../types/navigation';
import { TSection } from '../types/section';
import { BaseThemeObject, ThemeContextType, ThemeObject } from '../types/theme';
import { Tile } from '../types/tile';
import { CTALinkTarget, Tile } from '../types/tile';
import {
useGetGroupByID,
useGetSectionByID,
Expand Down Expand Up @@ -38,12 +40,14 @@ type WllSdkContextType = ThemeContextType & {
getGroupByID: (id: string) => Promise<APIResponse<TGroup>>;
getSectionByID: (id: string) => Promise<APIResponse<TSection>>;
getTileByID: (id: string) => Promise<APIResponse<Tile>>;
handleNavigation: (link: string, target: CTALinkTarget) => void;
};

type WllSdkProviderProps = {
children: React.ReactNode;
theme?: Partial<BaseThemeObject>;
config: SDKConfig;
navigationConfig?: NavigationConfig;
};

const createTheme = (baseTheme: Partial<BaseThemeObject> = {}): ThemeObject => {
Expand Down Expand Up @@ -84,6 +88,7 @@ export const WllSdkProvider: React.FC<WllSdkProviderProps> = ({
children,
theme: providedTheme,
config,
navigationConfig = {},
}) => {
const [theme, setThemeState] = React.useState(() =>
createTheme(providedTheme || {})
Expand All @@ -103,15 +108,25 @@ export const WllSdkProvider: React.FC<WllSdkProviderProps> = ({
id: string
) => Promise<APIResponse<Tile>>;

const handleNavigation = useNavigation(navigationConfig);

const contextValue = React.useMemo(
() => ({
theme,
setTheme,
getGroupByID,
getSectionByID,
getTileByID,
handleNavigation,
}),
[theme, setTheme, getGroupByID, getSectionByID, getTileByID]
[
theme,
setTheme,
getGroupByID,
getSectionByID,
getTileByID,
handleNavigation,
]
);
return (
<WllSdkContext.Provider value={contextValue}>
Expand Down
17 changes: 17 additions & 0 deletions lib/hooks/useHandleTilePress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useCallback } from 'react';
import { useWllSdk } from '../context/WllSdkContext';
import { CTALinkTarget } from '../types/tile';

export const useHandleTilePress = (
ctaLink: string | null | undefined,
ctaLinkTarget?: string
) => {
const { handleNavigation } = useWllSdk();

return useCallback(() => {
if (ctaLink) {
const target = (ctaLinkTarget as CTALinkTarget) || 'SAME_FRAME';
handleNavigation(ctaLink, target);
}
}, [ctaLink, ctaLinkTarget, handleNavigation]);
};
20 changes: 20 additions & 0 deletions lib/hooks/useNavigationHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useCallback } from 'react';
import { NavigationConfig } from '../types/navigation';
import { CTALinkTarget } from '../types/tile';
import { parseNavigationLink } from '../utils/navigationHelpers';

export const useNavigation = (config: NavigationConfig) => {
const handleNavigation = useCallback(
(link: string, ctaTarget: CTALinkTarget) => {
const { type, target } = parseNavigationLink(link);
const windowTarget = ctaTarget === 'SAME_FRAME' ? '_self' : '_blank';

if (config.navigationHandlers?.[type]) {
return config.navigationHandlers[type]({ target, windowTarget });
}
},
[config]
);

return handleNavigation;
};
Loading

0 comments on commit 57c4466

Please sign in to comment.