Skip to content
This repository has been archived by the owner on Jun 2, 2022. It is now read-only.

Commit

Permalink
Adding partytown + gtm (#167)
Browse files Browse the repository at this point in the history
* Adding partytown + gtm

* EnablePartytown on GTM Script

* Add gtm container id to storeConfig

* Commit to be removed - only test

* Improve development

* Adding gatsby-plugin-offline back

* Add copy partytown scripts

* Create destination directory recursive

* Fix cypress tests

* Add comments to explain copy files process

* Update gatsby-node.js

Co-authored-by: Ícaro Azevedo <icazevedo10@gmail.com>

* Change console.log to console.warn

* Add partytown integration doc

* Improving the analytics setup

* Remove dead dependency

* Fix cypress tests

* Update README.md

Co-authored-by: Ícaro Azevedo <icazevedo10@gmail.com>

* Apply suggestions from code review

Co-authored-by: Ícaro Azevedo <icazevedo10@gmail.com>

Co-authored-by: Ícaro Azevedo <icazevedo10@gmail.com>
  • Loading branch information
igorbrasileiro and icazevedo committed Jan 10, 2022
1 parent 4be3964 commit 65f84d2
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 12 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"root": true,
"rules": {
"react/prop-types": "off"
}
},
"ignorePatterns": ["static/~partytown/*"]
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,5 @@ yarn-error.log

# lighthouse CI autogen files
.lighthouseci

static/~partytown
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,27 @@ Looking for more guidance? Full documentation for Faststore lives [on this GitHu
## ⚡ Performance & QA

This project has strict performance budgets. Right out of the box, this project performs around 95 on Google's Page Speed Insights website, which usually is way more strict than your laptop's chrome lighthouse. Every time you commit to the repository, our QA bots will run and evaluate your code quality. We recommend you NEVER put in production a code that breaks any of the bots. If a bot breaks and still you need to put the code into production, change the bot config (`lighthouserc.js`, `cypress.json`) to make it pass and merge. This way you ensure your website will keep performing well during the years to come.

## Adding third party scripts

Adding third-party scripts to a webpage usually makes it slow. To maintain great performance while third-party scripts are added, this project uses [Partytown](https://github.com/BuilderIO/partytown/), a lazy-load library that helps relocate intensive scripts into a web worker and off of the main thread.

To add scripts using Partytown, add the `type="text/partytown"` to the script tag and make sure to add it before the Partytown script or component.
Some third-party scripts execute expensive computations that may require some time to run, making pages few slow. If that's the case, wrap those in a function and reference it on the Partytown `forward` prop. By doing this, Partytown will run this function on a web worker so it doesn't block the main thread.

```tsx
export const onRenderBody = ({ setHeadComponents }) => {
// ...
setHeadComponents([
<script>
window.expensiveFunction = function() {/* expensive computation used by custom-script */}
</script>
<script key="custom-script" src="*://domain/path" type="text/partytown" />,
<Partytown key="partytown" forward={["expensiveFunction"]} />
])
// ...
}
```

For more information about integrating third-party scripts: [Partytown Wiki](https://github.com/BuilderIO/partytown/wiki)

24 changes: 14 additions & 10 deletions cypress/integration/analytics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ beforeEach(() => {

const dataLayerHasEvent = (eventName) => {
return cy.window().then((window) => {
const allEvents = window.dataLayer.map((evt) => evt.name)
const allEvents = window.dataLayer.map((evt) => evt.event)

expect(allEvents).to.include(eventName)
})
}

const eventDataHasCurrencyProperty = () => {
return cy.window().then((window) => {
const allEvents = window.dataLayer.map((evt) => evt.params || {})
const allEvents = window.dataLayer.map((evt) => evt.ecommerce || {})

allEvents.forEach((event) => {
if (event.value !== undefined) {
Expand All @@ -40,12 +40,12 @@ describe('add_to_cart event', () => {
cy.window().then((window) => {
const { dataLayer } = window

const event = dataLayer.find((e) => e.name === 'add_to_cart')
const event = dataLayer.find((e) => e.event === 'add_to_cart')

expect(event).to.not.be.null
expect(event.params).to.have.property('value')
expect(event.ecommerce).to.have.property('value')

const item = event.params.items.find((i) => i.item_variant === skuId)
const item = event.ecommerce.items.find((i) => i.item_variant === skuId)

expect(item).to.not.be.null
expect(item).to.have.property('currency')
Expand Down Expand Up @@ -96,12 +96,12 @@ describe('remove_from_cart event', () => {
cy.window().then((window) => {
const { dataLayer } = window

const event = dataLayer.find((e) => e.name === 'remove_from_cart')
const event = dataLayer.find((e) => e.event === 'remove_from_cart')

expect(event).to.not.be.null
expect(event.params).to.have.property('value')
expect(event.ecommerce).to.have.property('value')

const item = event.params.items.find((i) => i.item_variant === skuId)
const item = event.ecommerce.items.find((i) => i.item_variant === skuId)

expect(item).to.not.be.null
expect(item).to.have.property('currency')
Expand All @@ -110,6 +110,10 @@ describe('remove_from_cart event', () => {
}

context('when removing a product from cart', () => {
beforeEach(() => {
cy.getById('remove-from-cart-button').click()
})

it('adds remove_from_cart event in the data layer', () => {
cy.visit(pages.pdp, options)
cy.waitForHydration()
Expand Down Expand Up @@ -168,11 +172,11 @@ describe('select_item event', () => {
.then(() => {
cy.window().then((window) => {
const event = window.dataLayer.find(
({ name }) => name === 'select_item'
({ event: eventName }) => eventName === 'select_item'
)

expect(event).to.exist
expect(skuId).to.equal(event.params.items[0].item_variant)
expect(skuId).to.equal(event.ecommerce.items[0].item_variant)
})
})
})
Expand Down
51 changes: 51 additions & 0 deletions gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
const fs = require('fs')
const path = require('path')

exports.onCreateWebpackConfig = ({ actions: { setWebpackConfig }, stage }) => {
const profiling = process.env.GATSBY_STORE_PROFILING === 'true'

Expand All @@ -11,6 +14,12 @@ exports.onCreateWebpackConfig = ({ actions: { setWebpackConfig }, stage }) => {
},
})
}

// Copy @builder.io/partytown lib files to the <rootPath>/static/~partytown when gatsby stage is develop.
// Those files are used by the Partytown component.
if (stage === 'develop') {
copyPartytown()
}
}

exports.onCreateBabelConfig = ({ actions }) => {
Expand All @@ -19,3 +28,45 @@ exports.onCreateBabelConfig = ({ actions }) => {
options: {},
})
}

const copyRecursiveSync = (src, dest) => {
const exists = fs.existsSync(src)
const stats = exists && fs.statSync(src)
const isDirectory = exists && stats.isDirectory()
const destinationExists = fs.existsSync(dest)

if (destinationExists) {
return
}

if (isDirectory) {
fs.mkdirSync(dest, { recursive: true })
fs.readdirSync(src).forEach((childItemName) => {
copyRecursiveSync(
path.join(src, childItemName),
path.join(dest, childItemName)
)
})
} else {
fs.copyFileSync(src, dest)
}
}

/**
* Copy @builder.io/partytown/lib to <rootPath>/static/\~partytown.
* If folder static/\~partytown doesn't exist, it will be created.
*/
const copyPartytown = () => {
copyRecursiveSync(
path.join(__dirname, 'node_modules/@builder.io/partytown/lib'),
path.join(__dirname, 'static/~partytown')
)
}

exports.onPreBuild = ({ reporter }) => {
reporter.info('Copying Partytown Files')

// Copy @builder.io/partytown lib files to the <rootPath>/static/~partytown.
// Those files are used by the Partytown component.
copyPartytown()
}
17 changes: 17 additions & 0 deletions gatsby-ssr.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable react/jsx-filename-extension */
import { CartProvider, SessionProvider, UIProvider } from '@faststore/sdk'
import React from 'react'
import { GoogleTagManager, Partytown } from '@builder.io/partytown/react'

import Layout from './src/Layout'
import AnalyticsHandler from './src/sdk/analytics'
Expand Down Expand Up @@ -31,3 +32,19 @@ export const wrapRootElement = ({ element }) => (
)

export const wrapPageElement = ({ element }) => <Layout>{element}</Layout>

export const onRenderBody = ({ setHeadComponents }) => {
if (storeConfig.analytics.gtmContainerId) {
setHeadComponents([
<GoogleTagManager
key="gtm"
containerId={storeConfig.analytics.gtmContainerId}
enablePartytown
/>,
<Partytown key="party" />,
])
} else if (process.env.NODE_ENV === 'development') {
// eslint-disable-next-line
console.warn('Check the analytics section on your store.config.js file.')
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"node": ">=14"
},
"dependencies": {
"@builder.io/partytown": "^0.0.29",
"@envelop/core": "^1.2.0",
"@envelop/graphql-jit": "^1.1.1",
"@envelop/parser-cache": "^2.2.0",
Expand Down
2 changes: 1 addition & 1 deletion src/sdk/analytics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if (typeof window !== 'undefined') {

export const AnalyticsHandler = ({ children }: PropsWithChildren<unknown>) => {
useAnalyticsEvent((event) => {
window.dataLayer.push(event)
window.dataLayer.push({ event: event.name, ecommerce: event.params })
})

return children
Expand Down
5 changes: 5 additions & 0 deletions store.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,9 @@ module.exports = {
search: '/s?q=orange',
},
},

analytics: {
// https://developers.google.com/tag-platform/tag-manager/web#standard_web_page_installation,
gtmContainerId: 'GTM-PGHZ95N',
},
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,11 @@
"@babel/helper-validator-identifier" "^7.15.7"
to-fast-properties "^2.0.0"

"@builder.io/partytown@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@builder.io/partytown/-/partytown-0.0.29.tgz#3f3307bbe42b223a548223418b63d9a2db4453c0"
integrity sha512-u3GghrlUiaVYpQEEahioIM9EPpRZh26MKPr60WZ0sZgy9Q8VRTXOeHa1d80Gze/Kg4A6+Dd8lWCXuTU76Rcr8g==

"@bundle-stats/cli-utils@^3.2.1":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@bundle-stats/cli-utils/-/cli-utils-3.2.1.tgz#c7ccb0b48fa154593544565bf6d9d614372b7306"
Expand Down

0 comments on commit 65f84d2

Please sign in to comment.