-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
migrate header files from support-dotcom-components
- Loading branch information
Showing
8 changed files
with
766 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
dotcom-rendering/src/components/marketing/header/Header.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/** | ||
* @file | ||
* This file was migrated from: | ||
* https://github.com/guardian/support-dotcom-components/blob/4925ef1e0ced5d221f1122afe79f93bd7448e0e5/packages/modules/src/modules/headers/Header.stories.tsx | ||
*/ | ||
import type { Meta, StoryFn } from '@storybook/react'; | ||
import { HeaderDecorator } from './common/HeaderDecorator'; | ||
import { HeaderUnvalidated as Header } from './Header'; | ||
|
||
export default { | ||
component: Header, | ||
title: 'Headers/Header', | ||
decorators: [HeaderDecorator], | ||
} as Meta<typeof Header>; | ||
|
||
const Template: StoryFn<typeof Header> = (props) => <Header {...props} />; | ||
|
||
export const DefaultHeader = Template.bind({}); | ||
DefaultHeader.args = { | ||
content: { | ||
heading: 'Support the Guardian', | ||
subheading: 'Available for everyone, funded by readers', | ||
primaryCta: { | ||
baseUrl: 'https://support.theguardian.com/contribute', | ||
text: 'Contribute', | ||
}, | ||
secondaryCta: { | ||
baseUrl: '', | ||
text: 'Subscribe', | ||
}, | ||
}, | ||
mobileContent: { | ||
heading: '', | ||
subheading: '', | ||
primaryCta: { | ||
baseUrl: 'https://support.theguardian.com/contribute', | ||
text: 'Support us', | ||
}, | ||
}, | ||
tracking: { | ||
ophanPageId: 'pvid', | ||
platformId: 'GUARDIAN_WEB', | ||
referrerUrl: 'https://theguardian.com/uk', | ||
clientName: 'dcr', | ||
abTestName: 'test-name', | ||
abTestVariant: 'variant-name', | ||
campaignCode: 'campaign-code', | ||
componentType: 'ACQUISITIONS_HEADER', | ||
}, | ||
countryCode: 'GB', | ||
}; |
141 changes: 141 additions & 0 deletions
141
dotcom-rendering/src/components/marketing/header/Header.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/** | ||
* @file | ||
* This file was migrated from: | ||
* https://github.com/guardian/support-dotcom-components/blob/4925ef1e0ced5d221f1122afe79f93bd7448e0e5/packages/modules/src/modules/headers/Header.tsx | ||
*/ | ||
import { css } from '@emotion/react'; | ||
import { | ||
from, | ||
headline, | ||
palette as sourcePalette, | ||
textSans, | ||
} from '@guardian/source-foundations'; | ||
import { | ||
Hide, | ||
LinkButton, | ||
SvgArrowRightStraight, | ||
themeButtonReaderRevenueBrand, | ||
} from '@guardian/source-react-components'; | ||
import type { ReactComponent } from '../lib/ReactComponent'; | ||
import type { HeaderRenderProps } from './HeaderWrapper'; | ||
import { headerWrapper, validatedHeaderWrapper } from './HeaderWrapper'; | ||
|
||
const messageStyles = (isThankYouMessage: boolean) => css` | ||
color: ${sourcePalette.brandAlt[400]}; | ||
${headline.xxsmall({ fontWeight: 'bold' })}; | ||
margin-bottom: 3px; | ||
${from.desktop} { | ||
${headline.xsmall({ fontWeight: 'bold' })} | ||
} | ||
${from.leftCol} { | ||
${isThankYouMessage | ||
? headline.small({ fontWeight: 'bold' }) | ||
: headline.medium({ fontWeight: 'bold' })} | ||
} | ||
`; | ||
|
||
const linkStyles = css` | ||
height: 32px; | ||
min-height: 32px; | ||
${textSans.small({ fontWeight: 'bold' })}; | ||
border-radius: 16px; | ||
padding: 0 12px 0 12px; | ||
line-height: 18px; | ||
margin-right: 10px; | ||
margin-bottom: 6px; | ||
svg { | ||
width: 24px; | ||
} | ||
`; | ||
|
||
const subMessageStyles = css` | ||
color: ${sourcePalette.neutral[100]}; | ||
${textSans.medium()}; | ||
margin: 5px 0; | ||
`; | ||
|
||
// override user agent styles | ||
const headingStyles = css` | ||
margin: 0; | ||
font-size: 100%; | ||
`; | ||
|
||
const Header: ReactComponent<HeaderRenderProps> = ( | ||
props: HeaderRenderProps, | ||
) => { | ||
const { heading, subheading, primaryCta, secondaryCta } = props.content; | ||
|
||
const onClick = () => { | ||
props.onCtaClick?.(); | ||
}; | ||
return ( | ||
<div> | ||
<Hide below="tablet"> | ||
<div css={messageStyles(false)}> | ||
<h2 css={headingStyles}>{heading}</h2> | ||
</div> | ||
|
||
<div css={subMessageStyles}> | ||
<div>{subheading}</div> | ||
</div> | ||
</Hide> | ||
|
||
{primaryCta && ( | ||
<> | ||
<Hide until="mobileLandscape"> | ||
<LinkButton | ||
theme={themeButtonReaderRevenueBrand} | ||
priority="primary" | ||
href={primaryCta.ctaUrl} | ||
onClick={onClick} | ||
icon={<SvgArrowRightStraight />} | ||
iconSide="right" | ||
nudgeIcon={true} | ||
css={linkStyles} | ||
> | ||
{primaryCta.ctaText} | ||
</LinkButton> | ||
</Hide> | ||
|
||
<Hide from="mobileLandscape"> | ||
<LinkButton | ||
theme={themeButtonReaderRevenueBrand} | ||
priority="primary" | ||
href={ | ||
props.mobileContent?.primaryCta?.ctaUrl ?? | ||
primaryCta.ctaUrl | ||
} | ||
css={linkStyles} | ||
> | ||
{props.mobileContent?.primaryCta?.ctaText ?? | ||
primaryCta.ctaText} | ||
</LinkButton> | ||
</Hide> | ||
</> | ||
)} | ||
|
||
{secondaryCta && ( | ||
<Hide until="tablet"> | ||
<LinkButton | ||
theme={themeButtonReaderRevenueBrand} | ||
priority="primary" | ||
href={secondaryCta.ctaUrl} | ||
icon={<SvgArrowRightStraight />} | ||
iconSide="right" | ||
nudgeIcon={true} | ||
css={linkStyles} | ||
> | ||
{secondaryCta.ctaText} | ||
</LinkButton> | ||
</Hide> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
const unvalidated = headerWrapper(Header); | ||
const validated = validatedHeaderWrapper(Header); | ||
export { validated as Header, unvalidated as HeaderUnvalidated }; |
186 changes: 186 additions & 0 deletions
186
dotcom-rendering/src/components/marketing/header/HeaderWrapper.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/** | ||
* @file | ||
* This file was migrated from: | ||
* https://github.com/guardian/support-dotcom-components/blob/4925ef1e0ced5d221f1122afe79f93bd7448e0e5/packages/modules/src/modules/headers/HeaderWrapper.tsx | ||
*/ | ||
import type { | ||
Cta, | ||
HeaderProps, | ||
OphanAction, | ||
} from '@guardian/support-dotcom-components/dist/shared/src/types'; | ||
import { headerPropsSchema } from '@guardian/support-dotcom-components/dist/shared/src/types'; | ||
import { useCallback, useEffect } from 'react'; | ||
import { type HasBeenSeen, useHasBeenSeen } from '../hooks/useHasBeenSeen'; | ||
import type { ReactComponent } from '../lib/ReactComponent'; | ||
import { | ||
addRegionIdAndTrackingParamsToSupportUrl, | ||
addTrackingParamsToProfileUrl, | ||
createClickEventFromTracking, | ||
isProfileUrl, | ||
} from '../lib/tracking'; | ||
import { withParsedProps } from '../shared/ModuleWrapper'; | ||
|
||
export interface HeaderEnrichedCta { | ||
ctaUrl: string; | ||
ctaText: string; | ||
} | ||
|
||
export interface HeaderRenderedContent { | ||
heading: string; | ||
subheading: string; | ||
primaryCta: HeaderEnrichedCta | null; | ||
secondaryCta: HeaderEnrichedCta | null; | ||
benefits: string[] | null; | ||
} | ||
|
||
export interface HeaderRenderProps { | ||
content: HeaderRenderedContent; | ||
mobileContent?: HeaderRenderedContent; | ||
onCtaClick?: () => void; // only used by sign in prompt header | ||
} | ||
|
||
export const headerWrapper = ( | ||
Header: ReactComponent<HeaderRenderProps>, | ||
): ReactComponent<HeaderProps> => { | ||
const Wrapped: ReactComponent<HeaderProps> = ({ | ||
content, | ||
mobileContent, | ||
tracking, | ||
countryCode, | ||
submitComponentEvent, | ||
numArticles, | ||
}) => { | ||
const buildEnrichedCta = (cta: Cta): HeaderEnrichedCta => { | ||
if (isProfileUrl(cta.baseUrl)) { | ||
return { | ||
ctaUrl: addTrackingParamsToProfileUrl( | ||
cta.baseUrl, | ||
tracking, | ||
), | ||
ctaText: cta.text, | ||
}; | ||
} | ||
return { | ||
ctaUrl: addRegionIdAndTrackingParamsToSupportUrl( | ||
cta.baseUrl, | ||
tracking, | ||
numArticles, | ||
countryCode, | ||
), | ||
ctaText: cta.text, | ||
}; | ||
}; | ||
|
||
const primaryCta = content.primaryCta | ||
? buildEnrichedCta(content.primaryCta) | ||
: null; | ||
const secondaryCta = content.secondaryCta | ||
? buildEnrichedCta(content.secondaryCta) | ||
: null; | ||
const benefits = content.benefits ?? null; | ||
|
||
const renderedContent: HeaderRenderedContent = { | ||
heading: content.heading, | ||
subheading: content.subheading, | ||
primaryCta, | ||
secondaryCta, | ||
benefits, | ||
}; | ||
|
||
const mobilePrimaryCta = mobileContent?.primaryCta | ||
? buildEnrichedCta(mobileContent.primaryCta) | ||
: primaryCta; | ||
|
||
const mobileSecondaryCta = mobileContent?.secondaryCta | ||
? buildEnrichedCta(mobileContent.secondaryCta) | ||
: secondaryCta; | ||
|
||
const renderedMobileContent = mobileContent | ||
? ({ | ||
heading: mobileContent.heading, | ||
subheading: mobileContent.subheading, | ||
primaryCta: mobilePrimaryCta, | ||
secondaryCta: mobileSecondaryCta, | ||
} as HeaderRenderedContent) | ||
: undefined; | ||
|
||
const { abTestName, abTestVariant, componentType, campaignCode } = | ||
tracking; | ||
|
||
const onCtaClick = (componentId: string) => { | ||
return (): void => { | ||
const componentClickEvent = createClickEventFromTracking( | ||
tracking, | ||
`${componentId} : cta`, | ||
); | ||
if (submitComponentEvent) { | ||
submitComponentEvent(componentClickEvent); | ||
} | ||
}; | ||
}; | ||
|
||
const sendOphanEvent = useCallback( | ||
(action: OphanAction): void => { | ||
if (submitComponentEvent) { | ||
submitComponentEvent({ | ||
component: { | ||
componentType, | ||
id: campaignCode, | ||
campaignCode, | ||
}, | ||
action, | ||
abTest: { | ||
name: abTestName, | ||
variant: abTestVariant, | ||
}, | ||
}); | ||
} | ||
}, | ||
[ | ||
abTestName, | ||
abTestVariant, | ||
campaignCode, | ||
componentType, | ||
submitComponentEvent, | ||
], | ||
); | ||
|
||
const [hasBeenSeen, setNode] = useHasBeenSeen( | ||
{ | ||
threshold: 0, | ||
}, | ||
true, | ||
) as HasBeenSeen; | ||
|
||
useEffect(() => { | ||
if (hasBeenSeen) { | ||
sendOphanEvent('VIEW'); | ||
} | ||
}, [hasBeenSeen, sendOphanEvent]); | ||
|
||
useEffect(() => { | ||
sendOphanEvent('INSERT'); | ||
}, [sendOphanEvent]); | ||
|
||
return ( | ||
<div ref={setNode}> | ||
<Header | ||
content={renderedContent} | ||
mobileContent={renderedMobileContent} | ||
onCtaClick={onCtaClick(campaignCode)} | ||
/> | ||
</div> | ||
); | ||
}; | ||
return Wrapped; | ||
}; | ||
|
||
const validate = (props: unknown): props is HeaderProps => { | ||
const result = headerPropsSchema.safeParse(props); | ||
return result.success; | ||
}; | ||
|
||
export const validatedHeaderWrapper = ( | ||
Header: ReactComponent<HeaderRenderProps>, | ||
): ReactComponent<HeaderProps> => | ||
withParsedProps(headerWrapper(Header), validate); |
Oops, something went wrong.