Skip to content

Commit 42822bc

Browse files
authored
feat: Add support for theme matching and scroll reporting via postMessage (#461)
* Add support for theme matching and scroll reporting
1 parent ad1ea26 commit 42822bc

File tree

2 files changed

+46
-50
lines changed

2 files changed

+46
-50
lines changed

src/replacements.js

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,50 @@
11
/*
2-
* Lighthouse only runs the theme checking function on load if a user is using
3-
* a dark theme. This change modifies the check to _always_ pass regardless of
4-
* theme preference.
5-
*
6-
* This ensures we always run the function on load, where we add additional
7-
* custom logic to test if a preferred theme was set as a querystring parameter,
8-
* falling back to a standard dark theme check.
2+
* Adds postMessage functionality for iframe communication
3+
* 1. We first check if the message origin is on our expected list.
4+
* 2. Next we listen for a message to tell us which theme the user is using in
5+
* the Netlify UI, and we toggle classes so the report matches.
6+
* 3. Finally we set up an intersection observer to send a message to the parent
7+
* window when the report footer is in view (triggers an Amplitude event to
8+
* log the report as been "viewed in full").
99
*/
10-
const forceThemeChecking = {
11-
source: `(prefers-color-scheme: dark)`,
12-
replacement: `(prefers-color-scheme)`,
13-
};
10+
const enablePostMessageCommunication = {
11+
source: `</body>`,
12+
replacement: `<script>
13+
const handlePostMessageData = (event) => {
14+
const validOrigins = [
15+
'http://localhost',
16+
'--app.netlify.app',
17+
'https://app.netlify.com',
18+
];
19+
const isValidOrigin = validOrigins.some((origin) =>
20+
event.origin.includes(origin)
21+
);
22+
if (!isValidOrigin) return;
1423
15-
/*
16-
* Relative line numbers of the replacements:
17-
* 1. Ensure original source line is retained
18-
* 2-3. We only want to trigger this on first run. This function is also run
19-
* each time the theme is manually toggled using the dropdown menu and
20-
* we don't want to interfere.
21-
* 4. Check the URL querystring for a light/dark theme preference.
22-
* 5-7. If we recognise the value, use it to set/remove the theme class.
23-
* 8-9. We made a change to the Lighthouse-supplied matchMedia check which
24-
* runs on page load, to always trigger this function regardless of theme.
25-
* This means we can't rely on the second parameter being passed to this
26-
* function being accurate. If we make it this far, we need to run our own
27-
* check to replicate that original functionality.
28-
*/
29-
const enableQuerystringThemeCheck = {
30-
source: `const n=e.rootEl;`,
31-
replacement: `const n=e.rootEl;
32-
if (!window.qsThemeChecked) {
33-
window.qsThemeChecked = true;
34-
const qsTheme = new URLSearchParams(window.location.search).get('theme');
35-
if (qsTheme === 'dark' || qsTheme === 'light') {
36-
return n.classList.toggle('lh-dark', qsTheme === 'dark');
24+
const theme = event.data;
25+
const rootEl = document.querySelector('.lh-root');
26+
if (rootEl && (theme === 'dark' || theme === 'light')) {
27+
document
28+
.querySelector('.lh-root')
29+
?.classList.toggle('lh-dark', theme === 'dark');
3730
}
38-
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
39-
return n.classList.toggle('lh-dark', prefersDark);
40-
}`,
31+
32+
const observer = new IntersectionObserver((matches) => {
33+
if (matches[0].isIntersecting) {
34+
event.source.postMessage(
35+
'appLighthouseReportFullyScrolled',
36+
event.origin
37+
);
38+
}
39+
});
40+
const footerEl = document.querySelector('.lh-footer');
41+
if (footerEl) observer.observe(footerEl);
42+
};
43+
window.addEventListener('message', handlePostMessageData);
44+
</script></body>`,
4145
};
4246

43-
const replacements = [forceThemeChecking, enableQuerystringThemeCheck];
47+
const replacements = [enablePostMessageCommunication];
4448

4549
const makeReplacements = (str) => {
4650
return replacements.reduce((acc, { source, replacement }) => {

src/replacements.test.js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
const { makeReplacements } = require('./replacements');
22

33
describe('replacements', () => {
4-
it('should make forceThemeChecking replacement', () => {
5-
const data = 'window.matchMedia("(prefers-color-scheme: dark)").matches';
6-
const result = 'window.matchMedia("(prefers-color-scheme)").matches';
7-
expect(makeReplacements(data)).toEqual(result);
8-
});
9-
10-
it('should make enableQuerystringThemeCheck replacement', () => {
11-
const data = 'prepended;const n=e.rootEl;appended';
12-
expect(makeReplacements(data)).toContain('prepended;const n=e.rootEl;');
13-
expect(makeReplacements(data)).toContain(
14-
'URLSearchParams(window.location.search)',
15-
);
16-
expect(makeReplacements(data)).toContain('appended');
4+
it('should make enablePostMessageCommunication replacement', () => {
5+
const data = '</div></body></html>';
6+
const replacedContent = makeReplacements(data);
7+
expect(replacedContent).toContain('</div><script>');
8+
expect(replacedContent).toContain('</script></body></html>');
179
});
1810
});

0 commit comments

Comments
 (0)