Skip to content

Commit

Permalink
[docs-infra] Move Ads component to TS (#42842)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfauquette authored Jul 15, 2024
1 parent 6482a29 commit 42d4aad
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 77 deletions.
2 changes: 2 additions & 0 deletions docs/nextConfigDocsInfra.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ function withDocsInfra(nextConfig) {
NETLIFY_DEPLOY_URL: process.env.DEPLOY_URL,
// Name of the site, its Netlify subdomain; for example, material-ui-docs
NETLIFY_SITE_NAME: process.env.SITE_NAME,
// The ratio of ads display reported to Google Analytics. Used to avoid an exceed on the Google Analytics quotas.
GA_ADS_DISPLAY_RATIO: 0.1,
},
experimental: {
scrollRestoration: true,
Expand Down
1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"@types/react-transition-group": "^4.4.10",
"@types/react-window": "^1.8.8",
"@types/stylis": "^4.2.0",
"@types/gtag.js": "^0.0.20",
"chai": "^4.4.1",
"cross-fetch": "^4.0.0",
"gm": "^1.25.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@ import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import AdCarbon from 'docs/src/modules/components/AdCarbon';
import AdInHouse from 'docs/src/modules/components/AdInHouse';
import { GA_ADS_DISPLAY_RATIO } from 'docs/src/modules/constants';
import { AdContext, adShape } from 'docs/src/modules/components/AdManager';
import { useTranslate } from '@mui/docs/i18n';

function PleaseDisableAdblock(props) {
function PleaseDisableAdblock() {
const t = useTranslate();

return (
<Paper
component="span"
elevation={0}
sx={{ display: 'block', p: 1.5, border: '2px solid', borderColor: 'primary.main' }}
{...props}
>
<Typography variant="body2" component="span" gutterBottom sx={{ display: 'block' }}>
{t('likeMui')}
Expand Down Expand Up @@ -74,7 +72,10 @@ const inHouseAds = [
},
];

class AdErrorBoundary extends React.Component {
class AdErrorBoundary extends React.Component<{
eventLabel: string | null;
children?: React.ReactNode | undefined;
}> {
static propTypes = {
children: PropTypes.node.isRequired,
eventLabel: PropTypes.string,
Expand Down Expand Up @@ -120,8 +121,8 @@ function isBot() {
}

export default function Ad() {
const [adblock, setAdblock] = React.useState(null);
const [carbonOut, setCarbonOut] = React.useState(null);
const [adblock, setAdblock] = React.useState<null | boolean>(null);
const [carbonOut, setCarbonOut] = React.useState<null | boolean>(null);

const { current: randomAdblock } = React.useRef(Math.random());
const { current: randomInHouse } = React.useRef(Math.random());
Expand Down Expand Up @@ -150,7 +151,7 @@ export default function Ad() {
const ad = React.useContext(AdContext);
const eventLabel = label ? `${label}-${ad.placement}-${adShape}` : null;

const timerAdblock = React.useRef();
const timerAdblock = React.useRef<NodeJS.Timeout>();

const checkAdblock = React.useCallback(
(attempt = 1) => {
Expand All @@ -162,7 +163,7 @@ export default function Ad() {
) {
if (
document.querySelector('#carbonads a') &&
document.querySelector('#carbonads a').getAttribute('href') ===
document.querySelector('#carbonads a')?.getAttribute('href') ===
'https://material-ui-next.com/discover-more/backers'
) {
setCarbonOut(true);
Expand Down Expand Up @@ -198,7 +199,7 @@ export default function Ad() {

React.useEffect(() => {
// Avoid an exceed on the Google Analytics quotas.
if (Math.random() > GA_ADS_DISPLAY_RATIO || !eventLabel) {
if (Math.random() > ((process.env.GA_ADS_DISPLAY_RATIO ?? 0.1) as number) || !eventLabel) {
return undefined;
}

Expand Down Expand Up @@ -231,10 +232,6 @@ export default function Ad() {
display: 'flex',
alignItems: 'flex-end',
}),
...(adShape === 'inline2' && {
display: 'flex',
alignItems: 'flex-end',
}),
})}
data-ga-event-category="ad"
data-ga-event-action="click"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import loadScript from 'docs/src/modules/utils/loadScript';
import AdDisplay from 'docs/src/modules/components/AdDisplay';
import { adStylesObject } from 'docs/src/modules/components/ad.styles';

type CarbonAd = {
pixel: string;
timestamp: string;
statimp: string;
statlink: string;
image: string;
company: string;
description: string;
};
const CarbonRoot = styled('span')(({ theme }) => {
const styles = adStylesObject['body-image'](theme);

Expand All @@ -18,7 +27,6 @@ const CarbonRoot = styled('span')(({ theme }) => {
display: 'none',
},
'& #carbonads': {
display: 'block',
...styles.root,
'& .carbon-img': styles.imgWrapper,
'& img': styles.img,
Expand Down Expand Up @@ -54,8 +62,8 @@ function AdCarbonImage() {
return <CarbonRoot ref={ref} />;
}

export function AdCarbonInline(props) {
const [ad, setAd] = React.useState(null);
export function AdCarbonInline() {
const [ad, setAd] = React.useState<CarbonAd | null>(null);

React.useEffect(() => {
let active = true;
Expand All @@ -79,8 +87,8 @@ export function AdCarbonInline(props) {
const data = await response.json();
// Inspired by https://github.com/Semantic-Org/Semantic-UI-React/blob/2c7134128925dd831de85011e3eb0ec382ba7f73/docs/src/components/CarbonAd/CarbonAdNative.js#L9
const sanitizedAd = data.ads
.filter((item) => Object.keys(item).length > 0)
.filter((item) => item.statlink)
.filter((item: any) => Object.keys(item).length > 0)
.filter((item: any) => item.statlink)
.filter(Boolean)[0];

if (!sanitizedAd) {
Expand Down Expand Up @@ -117,7 +125,6 @@ export function AdCarbonInline(props) {
/>
))}
<AdDisplay
{...props}
className="carbonads"
shape="inline"
ad={{
Expand All @@ -131,7 +138,7 @@ export function AdCarbonInline(props) {
/>
</React.Fragment>
) : (
<div {...props} style={{ minHeight: 52 }} />
<div style={{ minHeight: 52 }} />
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { styled } from '@mui/material/styles';
import { adShape } from 'docs/src/modules/components/AdManager';
import { GA_ADS_DISPLAY_RATIO } from 'docs/src/modules/constants';
import { adStylesObject } from 'docs/src/modules/components/ad.styles';
import { useTranslate } from '@mui/docs/i18n';

const InlineShape = styled('span')(({ theme }) => {
const styles = adStylesObject['body-inline'](theme);
Expand Down Expand Up @@ -31,12 +31,27 @@ const ImageShape = styled('span')(({ theme }) => {
};
});

export default function AdDisplay(props) {
export interface Ad {
name: string;
link: string;
img?: string;
description: string;
poweredby: string;
label: string;
}
interface AdDisplayProps {
ad: Ad;
className?: string;
shape?: 'auto' | 'inline' | 'image';
}

export default function AdDisplay(props: AdDisplayProps) {
const { ad, className, shape: shapeProp = 'auto' } = props;
const t = useTranslate();

React.useEffect(() => {
// Avoid an exceed on the Google Analytics quotas.
if (Math.random() > GA_ADS_DISPLAY_RATIO || !ad.label) {
if (Math.random() > ((process.env.GA_ADS_DISPLAY_RATIO ?? 0.1) as number) || !ad.label) {
return;
}

Expand All @@ -48,15 +63,9 @@ export default function AdDisplay(props) {

const shape = shapeProp === 'auto' ? adShape : shapeProp;

let Root;
if (shape === 'inline') {
Root = InlineShape;
}
if (shape === 'image') {
Root = ImageShape;
}
const Root = shape === 'image' ? ImageShape : InlineShape;

/* eslint-disable material-ui/no-hardcoded-labels, react/no-danger */
/* eslint-disable react/no-danger */
return (
<Root className={className}>
<a
Expand All @@ -79,10 +88,12 @@ export default function AdDisplay(props) {
dangerouslySetInnerHTML={{ __html: ad.description }}
/>
</a>
<span className="AdDisplay-poweredby">ad by {ad.poweredby}</span>
<span className="AdDisplay-poweredby">
{t('adPublisher').replace('{{publisher}}', ad.poweredby)}
</span>
</Root>
);
/* eslint-enable material-ui/no-hardcoded-labels, react/no-danger */
/* eslint-enable react/no-danger */
}

AdDisplay.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import PropTypes from 'prop-types';
import Portal from '@mui/material/Portal';
import { AdContext } from 'docs/src/modules/components/AdManager';

export default function AdGuest(props) {
interface AdGuestProps {
/**
* The querySelector use to target the element which will include the ad.
*/
classSelector?: string;
children?: React.ReactNode | undefined;
}

export default function AdGuest(props: AdGuestProps) {
const { classSelector = '.description', children } = props;
const ad = React.useContext(AdContext);

Expand All @@ -16,10 +24,12 @@ export default function AdGuest(props) {
container={() => {
const element = document.querySelector(classSelector);

if (ad.element === element) {
element.classList.add('ad');
} else {
element.classList.remove('ad');
if (element) {
if (ad.element === element) {
element.classList.add('ad');
} else {
element.classList.remove('ad');
}
}

return ad.element;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import AdDisplay from 'docs/src/modules/components/AdDisplay';
import AdDisplay, { Ad } from 'docs/src/modules/components/AdDisplay';

export default function AdInHouse(props) {
export default function AdInHouse(props: { ad: Omit<Ad, 'poweredby' | 'label'> }) {
const { ad } = props;

return <AdDisplay ad={{ poweredby: 'MUI', label: `in-house-${ad.name}`, ...ad }} />;
Expand Down
30 changes: 0 additions & 30 deletions docs/src/modules/components/AdManager.js

This file was deleted.

43 changes: 43 additions & 0 deletions docs/src/modules/components/AdManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/material/utils';

type AdPortal = {
placement: 'body-top';
element: Element | null;
};

interface AdManagerProps {
/**
* The querySelector use to target the element which will include the ad.
*/
classSelector?: string;
children?: React.ReactNode | undefined;
}

export const AdContext = React.createContext<AdPortal>({ placement: 'body-top', element: null });

// Persisted for the whole session.
// The state is used to use different ad placements.
const randomSession = Math.random();

// Distribution profile:
// 20% body-inline
// 80% body-image
export const adShape = randomSession < 0.2 ? 'inline' : 'image';

export default function AdManager({ classSelector = '.description', children }: AdManagerProps) {
const [portal, setPortal] = React.useState<AdPortal>({ placement: 'body-top', element: null });

useEnhancedEffect(() => {
const container = document.querySelector(classSelector);
setPortal({ placement: 'body-top', element: container });
}, [classSelector]);

return <AdContext.Provider value={portal}>{children}</AdContext.Provider>;
}

AdManager.propTypes = {
children: PropTypes.node,
classSelector: PropTypes.string,
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { alpha } from '@mui/material/styles';
import { alpha, Theme } from '@mui/material/styles';
import { adShape } from 'docs/src/modules/components/AdManager';

const adBodyImageStyles = (theme) => ({
const adBodyImageStyles = (theme: Theme) => ({
root: {
display: 'block',
overflow: 'hidden',
Expand Down Expand Up @@ -44,7 +44,7 @@ const adBodyImageStyles = (theme) => ({
},
});

const adBodyInlineStyles = (theme) => {
const adBodyInlineStyles = (theme: Theme) => {
const baseline = adBodyImageStyles(theme);

return {
Expand Down
4 changes: 0 additions & 4 deletions docs/src/modules/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,8 @@ const LANGUAGES_LABEL = [
},
];

// The ratio of ads display sending event to Google Analytics
const GA_ADS_DISPLAY_RATIO = 0.1;

module.exports = {
CODE_VARIANTS,
LANGUAGES_LABEL,
CODE_STYLING,
GA_ADS_DISPLAY_RATIO,
};
7 changes: 7 additions & 0 deletions docs/types/ga.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Gtag from '@types/gtag.js';

declare global {
interface Window {
gtag: Gtag.Gtag;
}
}
Loading

0 comments on commit 42d4aad

Please sign in to comment.