Skip to content

Commit b76166c

Browse files
committed
Move _document to mui-docs and make styled-components setup optional
1 parent 811a1ec commit b76166c

6 files changed

Lines changed: 252 additions & 201 deletions

File tree

docs/pages/_document.js

Lines changed: 7 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -1,208 +1,15 @@
1-
import * as React from 'react';
2-
import Script from 'next/script';
3-
import { documentGetInitialProps } from '@mui/material-nextjs/v13-pagesRouter';
4-
import { ServerStyleSheet } from 'styled-components';
5-
import Document, { Html, Head, Main, NextScript } from 'next/document';
6-
import GlobalStyles from '@mui/material/GlobalStyles';
7-
import MuiInitColorSchemeScript from '@mui/material/InitColorSchemeScript';
1+
import NextDocument from 'next/document';
2+
import { Document as MuiDocsDocument, createGetInitialProps } from '@mui/docs/Document';
83
import JoyInitColorSchemeScript from '@mui/joy/InitColorSchemeScript';
9-
import { pathnameToLanguage } from '@mui/docs/helpers';
10-
import createEmotionCache from '@mui/docs/createEmotionCache';
11-
import { getMetaThemeColor } from '@mui/docs/branding';
12-
import { fontClasses } from '@mui/docs/nextFonts';
134

14-
const PRODUCTION_GA =
15-
process.env.DEPLOY_ENV === 'production' || process.env.DEPLOY_ENV === 'staging';
5+
export default class MuiDocument extends NextDocument {
6+
static getInitialProps = createGetInitialProps({ setupStyledComponents: true });
167

17-
const GOOGLE_ANALYTICS_ID_V4 = PRODUCTION_GA ? 'G-5NXDQLC2ZK' : 'G-XJ83JQEK7J';
18-
const APOLLO_TRACKING_ID = PRODUCTION_GA ? '691c2e920c5e20000d7801b6' : 'dev-id';
19-
20-
export default class MyDocument extends Document {
218
render() {
22-
const { canonicalAsServer, userLanguage } = this.props;
23-
249
return (
25-
<Html lang={userLanguage} data-mui-color-scheme="light" data-joy-color-scheme="light">
26-
<Head>
27-
{/*
28-
manifest.json provides metadata used when your web app is added to the
29-
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
30-
*/}
31-
<link rel="manifest" href="/static/manifest.json" />
32-
{/* PWA primary color */}
33-
<meta
34-
name="theme-color"
35-
content={getMetaThemeColor('light')}
36-
media="(prefers-color-scheme: light)"
37-
/>
38-
<meta
39-
name="theme-color"
40-
content={getMetaThemeColor('dark')}
41-
media="(prefers-color-scheme: dark)"
42-
/>
43-
<link rel="icon" href="/static/favicon.ico" />
44-
{/* iOS Icon */}
45-
<link rel="apple-touch-icon" sizes="180x180" href="/static/icons/180x180.png" />
46-
{/* SEO */}
47-
<link
48-
rel="canonical"
49-
href={`https://mui.com${
50-
userLanguage === 'en' ? '' : `/${userLanguage}`
51-
}${canonicalAsServer}`}
52-
/>
53-
<link rel="alternate" href={`https://mui.com${canonicalAsServer}`} hrefLang="x-default" />
54-
<GlobalStyles
55-
styles={{
56-
// First SSR paint
57-
'.only-light-mode': {
58-
display: 'block',
59-
},
60-
'.only-dark-mode': {
61-
display: 'none',
62-
},
63-
// Post SSR Hydration
64-
'.mode-dark .only-light-mode': {
65-
display: 'none',
66-
},
67-
'.mode-dark .only-dark-mode': {
68-
display: 'block',
69-
},
70-
// TODO migrate to .only-dark-mode to .only-dark-mode-v2
71-
'[data-mui-color-scheme="light"] .only-dark-mode-v2': {
72-
display: 'none',
73-
},
74-
'[data-mui-color-scheme="dark"] .only-light-mode-v2': {
75-
display: 'none',
76-
},
77-
'.plan-pro, .plan-premium': {
78-
display: 'inline-block',
79-
height: '0.9em',
80-
width: '1em',
81-
verticalAlign: 'middle',
82-
marginLeft: '0.3em',
83-
marginBottom: '0.08em',
84-
backgroundSize: 'contain',
85-
backgroundRepeat: 'no-repeat',
86-
flexShrink: 0,
87-
},
88-
'.plan-pro': {
89-
backgroundImage: 'url(/static/x/pro.svg)',
90-
},
91-
'.plan-premium': {
92-
backgroundImage: 'url(/static/x/premium.svg)',
93-
},
94-
}}
95-
/>
96-
</Head>
97-
<body className={fontClasses}>
98-
<MuiInitColorSchemeScript defaultMode="system" />
99-
<JoyInitColorSchemeScript defaultMode="system" />
100-
<Main />
101-
<script
102-
// eslint-disable-next-line react/no-danger
103-
dangerouslySetInnerHTML={{
104-
__html: `
105-
window.dataLayer = window.dataLayer || [];
106-
function gtag(){dataLayer.push(arguments);}
107-
window.gtag = gtag;
108-
109-
${/* Set default consent to denied (Google Consent Mode v2) */ ''}
110-
gtag('consent', 'default', {
111-
'ad_storage': 'denied',
112-
'ad_user_data': 'denied',
113-
'ad_personalization': 'denied',
114-
'analytics_storage': 'denied',
115-
'wait_for_update': 500
116-
});
117-
gtag('set', 'ads_data_redaction', true);
118-
gtag('set', 'url_passthrough', true);
119-
120-
gtag('js', new Date());
121-
gtag('config', '${GOOGLE_ANALYTICS_ID_V4}', {
122-
send_page_view: false,
123-
});
124-
125-
${/* Apollo initialization - called by AnalyticsProvider when consent is granted */ ''}
126-
window.initApollo = function() {
127-
if (window.apolloInitialized) return;
128-
window.apolloInitialized = true;
129-
var n = Math.random().toString(36).substring(7),
130-
o = document.createElement('script');
131-
o.src = 'https://assets.apollo.io/micro/website-tracker/tracker.iife.js?nocache=' + n;
132-
o.async = true;
133-
o.defer = true;
134-
o.onload = function () {
135-
window.trackingFunctions.onLoad({ appId: '${APOLLO_TRACKING_ID}' });
136-
};
137-
document.head.appendChild(o);
138-
};
139-
140-
${/* Check localStorage for existing consent and initialize if already granted */ ''}
141-
(function() {
142-
try {
143-
var consent = localStorage.getItem('docs-cookie-consent');
144-
if (consent === 'analytics') {
145-
window.initApollo();
146-
}
147-
} catch (e) {}
148-
})();
149-
`,
150-
}}
151-
/>
152-
{/**
153-
* A better alternative to <script async>, to delay its execution
154-
* https://developer.chrome.com/blog/script-component/
155-
*/}
156-
<Script
157-
strategy="afterInteractive"
158-
src={`https://www.googletagmanager.com/gtag/js?id=${GOOGLE_ANALYTICS_ID_V4}`}
159-
/>
160-
<NextScript />
161-
</body>
162-
</Html>
10+
<MuiDocsDocument {...this.props}>
11+
<JoyInitColorSchemeScript defaultMode="system" />
12+
</MuiDocsDocument>
16313
);
16414
}
16515
}
166-
167-
MyDocument.getInitialProps = async (ctx) => {
168-
const styledComponentsSheet = new ServerStyleSheet();
169-
170-
try {
171-
const finalProps = await documentGetInitialProps(ctx, {
172-
emotionCache: createEmotionCache(),
173-
plugins: [
174-
{
175-
// styled-components
176-
enhanceApp: (App) => (props) => styledComponentsSheet.collectStyles(<App {...props} />),
177-
resolveProps: async (initialProps) => ({
178-
...initialProps,
179-
styles: [styledComponentsSheet.getStyleElement(), ...initialProps.styles],
180-
}),
181-
},
182-
],
183-
});
184-
185-
// All the URLs should have a leading /.
186-
// This is missing in the Next.js static export.
187-
let url = ctx.req.url;
188-
if (url[url.length - 1] !== '/') {
189-
url += '/';
190-
}
191-
192-
return {
193-
...finalProps,
194-
canonicalAsServer: pathnameToLanguage(url).canonicalAsServer,
195-
userLanguage: ctx.query.userLanguage || 'en',
196-
styles: [
197-
<style id="material-icon-font" key="material-icon-font" />,
198-
<style id="font-awesome-css" key="font-awesome-css" />,
199-
...finalProps.emotionStyleTags,
200-
<style id="app-search" key="app-search" />,
201-
<style id="prismjs" key="prismjs" />,
202-
...React.Children.toArray(finalProps.styles),
203-
],
204-
};
205-
} finally {
206-
styledComponentsSheet.seal();
207-
}
208-
};

packages/mui-docs/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,15 @@
6565
"chai": "^6.0.1",
6666
"csstype": "^3.1.3",
6767
"next": "^13.5.1 || ^14 || ^15.0.0 || ^16.0.0",
68-
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
68+
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
69+
"styled-components": "^5.3.0 || ^6.0.0"
6970
},
7071
"peerDependenciesMeta": {
7172
"@types/react": {
7273
"optional": true
74+
},
75+
"styled-components": {
76+
"optional": true
7377
}
7478
},
7579
"publishConfig": {
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import * as React from 'react';
2+
import Script from 'next/script';
3+
import { Html, Head, Main, NextScript } from 'next/document';
4+
import GlobalStyles from '@mui/material/GlobalStyles';
5+
import MuiInitColorSchemeScript from '@mui/material/InitColorSchemeScript';
6+
import { getMetaThemeColor } from '@mui/docs/branding';
7+
import { fontClasses } from '@mui/docs/nextFonts';
8+
9+
export type DocumentProps = {
10+
canonicalAsServer: string;
11+
analytics: {
12+
google: string;
13+
apollo: string;
14+
};
15+
userLanguage: string;
16+
children?: React.ReactNode;
17+
};
18+
19+
export function Document({ canonicalAsServer, userLanguage, analytics, children }: DocumentProps) {
20+
return (
21+
<Html lang={userLanguage} data-mui-color-scheme="light" data-joy-color-scheme="light">
22+
<Head>
23+
{/*
24+
manifest.json provides metadata used when your web app is added to the
25+
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
26+
*/}
27+
<link rel="manifest" href="/static/manifest.json" />
28+
{/* PWA primary color */}
29+
<meta
30+
name="theme-color"
31+
content={getMetaThemeColor('light')}
32+
media="(prefers-color-scheme: light)"
33+
/>
34+
<meta
35+
name="theme-color"
36+
content={getMetaThemeColor('dark')}
37+
media="(prefers-color-scheme: dark)"
38+
/>
39+
<link rel="icon" href="/static/favicon.ico" />
40+
{/* iOS Icon */}
41+
<link rel="apple-touch-icon" sizes="180x180" href="/static/icons/180x180.png" />
42+
{/* SEO */}
43+
<link
44+
rel="canonical"
45+
href={`https://mui.com${
46+
userLanguage === 'en' ? '' : `/${userLanguage}`
47+
}${canonicalAsServer}`}
48+
/>
49+
<link rel="alternate" href={`https://mui.com${canonicalAsServer}`} hrefLang="x-default" />
50+
<GlobalStyles
51+
styles={{
52+
// First SSR paint
53+
'.only-light-mode': {
54+
display: 'block',
55+
},
56+
'.only-dark-mode': {
57+
display: 'none',
58+
},
59+
// Post SSR Hydration
60+
'.mode-dark .only-light-mode': {
61+
display: 'none',
62+
},
63+
'.mode-dark .only-dark-mode': {
64+
display: 'block',
65+
},
66+
// TODO migrate to .only-dark-mode to .only-dark-mode-v2
67+
'[data-mui-color-scheme="light"] .only-dark-mode-v2': {
68+
display: 'none',
69+
},
70+
'[data-mui-color-scheme="dark"] .only-light-mode-v2': {
71+
display: 'none',
72+
},
73+
'.plan-pro, .plan-premium': {
74+
display: 'inline-block',
75+
height: '0.9em',
76+
width: '1em',
77+
verticalAlign: 'middle',
78+
marginLeft: '0.3em',
79+
marginBottom: '0.08em',
80+
backgroundSize: 'contain',
81+
backgroundRepeat: 'no-repeat',
82+
flexShrink: 0,
83+
},
84+
'.plan-pro': {
85+
backgroundImage: 'url(/static/x/pro.svg)',
86+
},
87+
'.plan-premium': {
88+
backgroundImage: 'url(/static/x/premium.svg)',
89+
},
90+
}}
91+
/>
92+
</Head>
93+
<body className={fontClasses}>
94+
<MuiInitColorSchemeScript defaultMode="system" />
95+
{children}
96+
<Main />
97+
<script
98+
// eslint-disable-next-line react/no-danger
99+
dangerouslySetInnerHTML={{
100+
__html: `
101+
window.dataLayer = window.dataLayer || [];
102+
function gtag(){dataLayer.push(arguments);}
103+
window.gtag = gtag;
104+
105+
${/* Set default consent to denied (Google Consent Mode v2) */ ''}
106+
gtag('consent', 'default', {
107+
'ad_storage': 'denied',
108+
'ad_user_data': 'denied',
109+
'ad_personalization': 'denied',
110+
'analytics_storage': 'denied',
111+
'wait_for_update': 500
112+
});
113+
gtag('set', 'ads_data_redaction', true);
114+
gtag('set', 'url_passthrough', true);
115+
116+
gtag('js', new Date());
117+
gtag('config', '${analytics.google}', {
118+
send_page_view: false,
119+
});
120+
121+
${/* Apollo initialization - called by AnalyticsProvider when consent is granted */ ''}
122+
window.initApollo = function() {
123+
if (window.apolloInitialized) return;
124+
window.apolloInitialized = true;
125+
var n = Math.random().toString(36).substring(7),
126+
o = document.createElement('script');
127+
o.src = 'https://assets.apollo.io/micro/website-tracker/tracker.iife.js?nocache=' + n;
128+
o.async = true;
129+
o.defer = true;
130+
o.onload = function () {
131+
window.trackingFunctions.onLoad({ appId: '${analytics.apollo}' });
132+
};
133+
document.head.appendChild(o);
134+
};
135+
136+
${/* Check localStorage for existing consent and initialize if already granted */ ''}
137+
(function() {
138+
try {
139+
var consent = localStorage.getItem('docs-cookie-consent');
140+
if (consent === 'analytics') {
141+
window.initApollo();
142+
}
143+
} catch (e) {}
144+
})();
145+
`,
146+
}}
147+
/>
148+
{/**
149+
* A better alternative to <script async>, to delay its execution
150+
* https://developer.chrome.com/blog/script-component/
151+
*/}
152+
<Script
153+
strategy="afterInteractive"
154+
src={`https://www.googletagmanager.com/gtag/js?id=${analytics.google}`}
155+
/>
156+
<NextScript />
157+
</body>
158+
</Html>
159+
);
160+
}

0 commit comments

Comments
 (0)