Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Preserve search parameters in external links #258

Merged
merged 16 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const createJestConfig = nextJest({

/** @type {import('jest').Config} */
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
moduleNameMapper: {
'^@/public/(.*)$': '<rootDir>/public/$1',
'^@/(.*)$': '<rootDir>/src/$1',
Expand Down
3 changes: 3 additions & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Used for __tests__/testing-library.js
// Learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect'
15 changes: 3 additions & 12 deletions src/components/Campaign/Hero/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Button, Chip, Container, Grid, Typography } from '@mui/material'
import Link from 'next/link'
import css from './styles.module.css'
import type { TypeHeroSkeleton } from '@/contentful/types'
import type { Entry } from 'contentful'
Expand All @@ -9,14 +8,11 @@ import RichText from '@/components/Campaign/RichText'
import { createImageData } from '@/lib/createImageData'
import { SOCIAL_LOGIN_EVENTS } from '@/services/analytics/events/socialLogin'
import { trackEvent } from '@/services/analytics/trackEvent'
import { useContext } from 'react'
import SearchParamsContext from '@/contexts/SearchParamsContext'
import { appendSearchParamsToURL } from '@/lib/appendSearchParamsToURL'
import SafeLink from '@/components/common/SafeLink'

type HeroEntry = Entry<TypeHeroSkeleton, undefined, string>

const Hero = (props: HeroEntry) => {
const searchParams = useContext(SearchParamsContext)
const { caption, title, description, image, button, trustedBy, hasMoreComing, availableOn } = props.fields

const trustedByData = createImageData(trustedBy)
Expand Down Expand Up @@ -48,12 +44,7 @@ const Hero = (props: HeroEntry) => {
</Typography>

{isEntryTypeButton(button) ? (
<Link
href={appendSearchParamsToURL(button.fields.btnHref, searchParams)}
target="_blank"
rel="noreferrer"
passHref
>
<SafeLink href={button.fields.btnHref}>
<Button
variant="contained"
size="large"
Expand All @@ -66,7 +57,7 @@ const Hero = (props: HeroEntry) => {
>
{button.fields.btnCopy}
</Button>
</Link>
</SafeLink>
) : undefined}
</Grid>

Expand Down
15 changes: 3 additions & 12 deletions src/components/Campaign/TextBlockBanner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,15 @@ import css from './styles.module.css'
import { isAsset, isEntryTypeButton } from '@/lib/typeGuards'
import type { Entry } from 'contentful'
import Image from 'next/image'
import Link from 'next/link'
import type { TypeTextBlockBannerSkeleton } from '@/contentful/types'
import { trackEvent } from '@/services/analytics/trackEvent'
import { SOCIAL_LOGIN_EVENTS } from '@/services/analytics/events/socialLogin'
import SearchParamsContext from '@/contexts/SearchParamsContext'
import { useContext } from 'react'
import { appendSearchParamsToURL } from '@/lib/appendSearchParamsToURL'
import SafeLink from '@/components/common/SafeLink'

type TextBlockBannerEntry = Entry<TypeTextBlockBannerSkeleton, undefined, string>

const TextBlockBanner = (props: TextBlockBannerEntry) => {
const { logo, title, description, button } = props.fields
const searchParams = useContext(SearchParamsContext)

return (
<div className={css.gradient}>
Expand All @@ -35,12 +31,7 @@ const TextBlockBanner = (props: TextBlockBannerEntry) => {
</Typography>

{isEntryTypeButton(button) ? (
<Link
href={appendSearchParamsToURL(button.fields.btnHref, searchParams)}
target="_blank"
rel="noreferrer"
passHref
>
<SafeLink href={button.fields.btnHref}>
<Button
variant="contained"
size="large"
Expand All @@ -54,7 +45,7 @@ const TextBlockBanner = (props: TextBlockBannerEntry) => {
>
{button.fields.btnCopy}
</Button>
</Link>
</SafeLink>
) : undefined}
</div>
</Container>
Expand Down
15 changes: 3 additions & 12 deletions src/components/Campaign/TextBlockCentered/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import { Button, Container, Typography } from '@mui/material'
import Image from 'next/image'
import Link from 'next/link'
import css from './styles.module.css'
import layoutCss from '@/components/common/styles.module.css'
import type { Entry } from 'contentful'
import type { TypeTextBlockCenteredSkeleton } from '@/contentful/types'
import { isAsset, isEntryTypeButton } from '@/lib/typeGuards'
import { trackEvent } from '@/services/analytics/trackEvent'
import { SOCIAL_LOGIN_EVENTS } from '@/services/analytics/events/socialLogin'
import SearchParamsContext from '@/contexts/SearchParamsContext'
import { useContext } from 'react'
import { appendSearchParamsToURL } from '@/lib/appendSearchParamsToURL'
import SafeLink from '@/components/common/SafeLink'

type TextBlockCenteredEntry = Entry<TypeTextBlockCenteredSkeleton, undefined, string>

const TextBlockCentered = (props: TextBlockCenteredEntry) => {
const { logo, cta, title, description, button } = props.fields
const searchParams = useContext(SearchParamsContext)

return (
<Container className={layoutCss.containerMedium}>
Expand All @@ -36,12 +32,7 @@ const TextBlockCentered = (props: TextBlockCenteredEntry) => {
<Typography color="primary.light">{description}</Typography>

{isEntryTypeButton(button) ? (
<Link
href={appendSearchParamsToURL(button.fields.btnHref, searchParams)}
target="_blank"
rel="noreferrer"
passHref
>
<SafeLink href={button.fields.btnHref}>
<Button
variant="contained"
size="large"
Expand All @@ -54,7 +45,7 @@ const TextBlockCentered = (props: TextBlockCenteredEntry) => {
>
{button.fields.btnCopy}
</Button>
</Link>
</SafeLink>
) : undefined}
</div>
</Container>
Expand Down
22 changes: 7 additions & 15 deletions src/components/Wallet/Intro/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { useContext, type ReactElement } from 'react'
import { type ReactElement } from 'react'
import { Button, Container, Divider, Grid, Typography } from '@mui/material'
import { IOS_LINK, GPLAY_LINK } from '@/config/constants'
import IOSDownload from '@/public/images/ios-download.svg'
import GPlayDownload from '@/public/images/google-play-download.svg'
import type { BaseBlock } from '@/components/Home/types'
import css from './styles.module.css'
import HeaderParticles from '@/public/images/header_particles.svg'
import SearchParamsContext from '@/contexts/SearchParamsContext'
import { appendSearchParamsToURL } from '@/lib/appendSearchParamsToURL'
import SafeLink from '@/components/common/SafeLink'

const Intro = ({ image, title, buttons }: BaseBlock): ReactElement => {
const searchParams = useContext(SearchParamsContext)

return (
<Container>
<Grid
Expand All @@ -37,16 +34,11 @@ const Intro = ({ image, title, buttons }: BaseBlock): ReactElement => {
<Grid container gap={{ xs: 4, md: '10px' }}>
<Grid item>
{buttons?.map(({ text, href }) => (
<Button
key={text}
href={appendSearchParamsToURL(href, searchParams)}
variant="contained"
target="_blank"
rel="noreferrer"
className={css.button}
>
{text}
</Button>
<SafeLink key={text} href={href}>
<Button variant="contained" className={css.button}>
{text}
</Button>
</SafeLink>
))}
</Grid>
<div className={css.downloads}>
Expand Down
11 changes: 3 additions & 8 deletions src/components/Wallet/TextRadialAnimation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,9 @@ import layoutCss from '@/components/common/styles.module.css'
import type { BaseBlock } from '@/components/Home/types'
import RadialAnimation from './RadialAnimation'
import LinkButton from '@/components/common/LinkButton'
import Link from 'next/link'
import SearchParamsContext from '@/contexts/SearchParamsContext'
import { useContext } from 'react'
import { appendSearchParamsToURL } from '@/lib/appendSearchParamsToURL'
import SafeLink from '@/components/common/SafeLink'

const TextRadialAnimation = ({ title, text, link }: BaseBlock) => {
const searchParams = useContext(SearchParamsContext)

return (
<Container>
<Grid
Expand All @@ -25,9 +20,9 @@ const TextRadialAnimation = ({ title, text, link }: BaseBlock) => {
</Typography>
<Typography mb={{ xs: 3, md: 5 }}>{text}</Typography>
{link && (
<Link href={appendSearchParamsToURL(link.href, searchParams)} passHref target="_blank" rel="noreferrer">
<SafeLink href={link.href}>
<LinkButton>{link.title}</LinkButton>
</Link>
</SafeLink>
)}
</Grid>
<Grid item md={1} display={{ xs: 'none', md: 'block' }} />
Expand Down
17 changes: 3 additions & 14 deletions src/components/common/ButtonsWrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import type { Button as ButtonType } from '@/components/Home/types'
import LinkButton from '@/components/common/LinkButton'
import { Button } from '@mui/material'
import Link from 'next/link'
import css from './styles.module.css'
import SearchParamsContext from '@/contexts/SearchParamsContext'
import { useContext } from 'react'
import { appendSearchParamsToURL } from '@/lib/appendSearchParamsToURL'
import SafeLink from '@/components/common/SafeLink'

type ButtonsWrapperProps = {
buttons?: ButtonType[]
mobileDirection?: 'column' | 'row'
}

const ButtonsWrapper = ({ buttons, mobileDirection }: ButtonsWrapperProps) => {
const searchParams = useContext(SearchParamsContext)

if (!buttons || buttons.length === 0) return null

return (
Expand All @@ -24,21 +19,15 @@ const ButtonsWrapper = ({ buttons, mobileDirection }: ButtonsWrapperProps) => {
const isButton = variant === 'button'

return (
<Link
key={index}
href={appendSearchParamsToURL(href, searchParams)}
target="_blank"
rel="noreferrer"
passHref
>
<SafeLink key={index} href={href}>
{isButton ? (
<Button variant="contained" size="large" color={color}>
{text}
</Button>
) : (
<LinkButton>{text}</LinkButton>
)}
</Link>
</SafeLink>
)
})}
</div>
Expand Down
11 changes: 3 additions & 8 deletions src/components/common/HeaderCTA/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import type { BaseBlock } from '@/components/Home/types'
import LinkButton from '@/components/common/LinkButton'
import { Grid, Typography } from '@mui/material'
import Link from 'next/link'
import css from './styles.module.css'
import SearchParamsContext from '@/contexts/SearchParamsContext'
import { useContext } from 'react'
import { appendSearchParamsToURL } from '@/lib/appendSearchParamsToURL'
import SafeLink from '@/components/common/SafeLink'

type HeaderCTAProps = BaseBlock & {
bigTitle?: boolean
onClick?: () => void
}

const HeaderCTA = (props: HeaderCTAProps) => {
const searchParams = useContext(SearchParamsContext)

return (
<Grid container mb={{ sm: 5, md: 7 }}>
<Grid item xs={12} md={!props.bigTitle ? 8 : undefined}>
Expand All @@ -27,11 +22,11 @@ const HeaderCTA = (props: HeaderCTAProps) => {
</Grid>
{props.link && (
<Grid item xs={12} md={4} className={`${css.linkButton} ${!props.bigTitle && css.alignEnd}`}>
<Link href={appendSearchParamsToURL(props.link.href, searchParams)} target="_blank" rel="noreferrer" passHref>
<SafeLink href={props.link.href}>
<LinkButton className={css.shortPadding} onClick={props.onClick}>
{props.link.title}
</LinkButton>
</Link>
</SafeLink>
</Grid>
)}
</Grid>
Expand Down
18 changes: 18 additions & 0 deletions src/components/common/SafeLink/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import SearchParamsContext from '@/contexts/SearchParamsContext'
import { type ReactNode, useContext } from 'react'
import Link from 'next/link'
import { appendSearchParamsToURL } from '@/lib/appendSearchParamsToURL'

const SafeLink = ({ href, children }: { href: string; children: ReactNode }) => {
const searchParams = useContext(SearchParamsContext)

const finalHref = appendSearchParamsToURL(href, searchParams)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useMemo perhaps?


return (
<Link href={finalHref} target="_blank" rel="noreferrer">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove rel="noreferrer" for app.safe.global links.

{children}
</Link>
)
}

export default SafeLink
4 changes: 2 additions & 2 deletions src/contexts/SearchParamsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { type ReactNode, createContext, useEffect, useState } from 'react'
const SEARCH_PARAMS_KEY = 'searchParams'
const sessionSearchParams = sessionItem<string>(SEARCH_PARAMS_KEY)

const SearchParamsContext = createContext<any>(undefined)
const SearchParamsContext = createContext<string>('')

export const SearchParamsContextProvider = ({ children }: { children: ReactNode }) => {
const searchParams = useSearchParams()
const [searchParamsValue, setSearchParamsValue] = useState<string>()
const [searchParamsValue, setSearchParamsValue] = useState<string>('')

useEffect(() => {
const initialSearchParams = sessionSearchParams.get() || ''
Expand Down
Loading
Loading