-
Notifications
You must be signed in to change notification settings - Fork 566
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial components and dependencies for the GA4 ecommerce demo (#824
) * Add the global context object of the GA4 ecommerce demo * Wrap every page using a StoreProvider object used by eCommerce demo. * Add CSS variables used by eCommerce demo. * Add dependencies for eCommerce demo app. * Add "Go to Cart" button component. * Add "Navigation bar" component. * Add "header" component. * Add "footer" component. * Add "Google Analytics Console" component which displays all ecommerce events generated by the demo app. * Do not lint CSS * Add support for CSS modules * Address type check errors by introducing interfaces. * Address type check errors by introducing interfaces. * add EOF
- Loading branch information
Showing
16 changed files
with
733 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
src/images/* | ||
**/*.json | ||
**/*.css | ||
**/*.lock | ||
lib/build/* | ||
node_modules/ |
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 |
---|---|---|
@@ -1,7 +1,15 @@ | ||
import * as React from "react" | ||
import {StoreProvider} from "./src/components/ga4/EnhancedEcommerce/store-context" | ||
import CustomLayout from "./gatsby/wrapRootElement.js" | ||
import "./src/styles/ecommerce/variables.css" | ||
|
||
// TODO - look into making this work like gatsby-node & use typescript for the | ||
// things that are imported/exported. | ||
|
||
export { onInitialClientRender } from "./gatsby/onInitialClientRender" | ||
export const wrapPageElement = CustomLayout | ||
|
||
// Wrap every page using a StoreProvider object used by eCommerce demo. | ||
export const wrapRootElement = ({ element }) => ( | ||
<StoreProvider>{element}</StoreProvider> | ||
) |
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
37 changes: 37 additions & 0 deletions
37
src/components/ga4/EnhancedEcommerce/cart-button.module.css
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,37 @@ | ||
.cartButton { | ||
color: var(--text-color-secondary); | ||
grid-area: cartButton; | ||
width: var(--size-input); | ||
height: var(--size-input); | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
position: relative; | ||
align-self: center; | ||
} | ||
|
||
.cartButton:hover { | ||
color: var(--text-color); | ||
} | ||
|
||
.badge { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
background-color: var(--primary); | ||
box-shadow: 0 0 0 2px white; | ||
color: var(--text-color-inverted); | ||
font-size: var(--text-xs); | ||
font-weight: var(--bold); | ||
border-radius: var(--radius-rounded); | ||
position: absolute; | ||
bottom: 4px; | ||
right: 4px; | ||
height: 16px; | ||
min-width: 16px; | ||
padding: 0 var(--space-sm); | ||
} | ||
|
||
.cartButton[aria-current="page"] { | ||
color: var(--primary); | ||
} |
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,20 @@ | ||
import * as React from "react" | ||
import {Link} from "gatsby" | ||
import {badge, cartButton} from "./cart-button.module.css" | ||
import {MdShoppingCart} from 'react-icons/md'; | ||
import IconButton from "@material-ui/core/IconButton" | ||
|
||
export function CartButton({quantity}) { | ||
return ( | ||
<Link | ||
aria-label={`Shopping Cart with ${quantity} items`} | ||
to="/ga4/enhanced-ecommerce/cart" | ||
className={cartButton} | ||
> | ||
<IconButton> | ||
<MdShoppingCart/> | ||
</IconButton> | ||
{quantity > 0 && <div className={badge}>{quantity}</div>} | ||
</Link> | ||
) | ||
} |
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,8 @@ | ||
.footerStyle { | ||
margin-top: 100px; | ||
} | ||
|
||
.gaConsole { | ||
padding: var(--size-gutter-raw); | ||
|
||
} |
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,11 @@ | ||
import * as React from "react" | ||
import {footerStyle, gaConsole} from "./footer.module.css" | ||
import {GaConsole} from "@/components/ga4/EnhancedEcommerce/ga-console"; | ||
|
||
export function Footer() { | ||
return ( | ||
<footer className={footerStyle}> | ||
<GaConsole className={gaConsole}/> | ||
</footer> | ||
) | ||
} |
47 changes: 47 additions & 0 deletions
47
src/components/ga4/EnhancedEcommerce/ga-console.module.css
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,47 @@ | ||
.gaConsoleStyle { | ||
align-self: stretch; | ||
height: 100px; | ||
align-items: center; | ||
background: black; | ||
color: white; | ||
overflow: auto; | ||
padding: var(--size-gap) var(--size-gutter); | ||
position: fixed; | ||
bottom: 0; | ||
width: 80%; | ||
opacity: 0.7; | ||
} | ||
|
||
.emptyEvents { | ||
text-align: center; | ||
font-weight: var(--medium); | ||
} | ||
|
||
.eventLine { | ||
display: flex; | ||
flex-direction: row; | ||
white-space: nowrap; | ||
} | ||
|
||
.eventTimestamp { | ||
padding-right: var(--space-md); | ||
} | ||
|
||
.eventName { | ||
padding-right: var(--space-md); | ||
font-weight: var(--semibold); | ||
} | ||
|
||
.eventDescription { | ||
padding-right: var(--space-md); | ||
} | ||
|
||
.eventSnippet { | ||
white-space: nowrap; | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
font-style: italic; | ||
color: var(--grey-50); | ||
text-decoration: dotted underline; | ||
cursor: pointer; | ||
} |
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,131 @@ | ||
import * as React from "react" | ||
import {StoreContext} from "./store-context" | ||
import {emptyEvents, eventDescription, eventLine, eventName, eventSnippet, eventTimestamp, gaConsoleStyle} from "@/components/ga4/EnhancedEcommerce/ga-console.module.css"; | ||
import Dialog from '@material-ui/core/Dialog'; | ||
import Tabs from '@material-ui/core/Tabs'; | ||
import Tab from '@material-ui/core/Tab'; | ||
import DialogActions from '@material-ui/core/DialogActions'; | ||
import DialogContent from '@material-ui/core/DialogContent'; | ||
import DialogContentText from '@material-ui/core/DialogContentText'; | ||
import DialogTitle from '@material-ui/core/DialogTitle'; | ||
import Button from '@material-ui/core/Button'; | ||
import TextField from '@material-ui/core/TextField'; | ||
import {Link} from "gatsby"; | ||
import {Box, Typography} from "@material-ui/core"; | ||
|
||
function TabPanel(props) { | ||
const {children, value, index, ...other} = props; | ||
|
||
return ( | ||
<div | ||
role="tabpanel" | ||
hidden={value !== index} | ||
id={`simple-tabpanel-${index}`} | ||
{...other} | ||
> | ||
{value === index && ( | ||
<Box p={3}> | ||
<Typography>{children}</Typography> | ||
</Box> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
export function GaConsole({className}) { | ||
const {events} = React.useContext(StoreContext) | ||
const [open, setOpen] = React.useState(false); | ||
|
||
const [selectedEvent, setSelectedEvent] = React.useState({ | ||
key: 0, | ||
timestamp: '', | ||
name: '', | ||
description: '', | ||
snippet: '' | ||
}); | ||
|
||
const [value, setValue] = React.useState(0); | ||
|
||
const handleClickOpen = (eventKey) => () => { | ||
setSelectedEvent(events[events.length - eventKey - 1]) | ||
setOpen(true); | ||
}; | ||
const handleClose = () => { | ||
setOpen(false); | ||
}; | ||
const handleChange = (event, newValue) => { | ||
setValue(newValue); | ||
}; | ||
|
||
return ( | ||
<div className={[gaConsoleStyle, className].join(" ")}> | ||
{events.length ? events.map((event) => ( | ||
<div key={event.key} className={eventLine}> | ||
<div className={eventTimestamp}>{event.timestamp}</div> | ||
<div className={eventName}> | ||
<Link | ||
to={`https://developers.google.com/gtagjs/reference/ga4-events#${event.name}`} | ||
aria-label={`GA4 event reference documentation`} | ||
target='_blank' | ||
> | ||
{event.name} | ||
</Link></div> | ||
<div | ||
className={eventDescription}>{event.description}</div> | ||
<div onClick={handleClickOpen(event.key)} | ||
className={eventSnippet}>{event.snippet}</div> | ||
</div> | ||
)) : <div className={emptyEvents}>Start interacting with the store | ||
to see Google Analytics eCommerce events here.</div>} | ||
|
||
<Dialog | ||
open={open} | ||
onClose={handleClose} | ||
aria-labelledby="scroll-dialog-title" | ||
aria-describedby="scroll-dialog-description" | ||
> | ||
<DialogTitle id="scroll-dialog-title">Google Analytics eCommerce | ||
event details</DialogTitle> | ||
<DialogContent> | ||
<DialogContentText | ||
tabIndex={-1} | ||
> | ||
<p>{selectedEvent?.timestamp} | ||
<Link | ||
to={`https://developers.google.com/gtagjs/reference/ga4-events#${selectedEvent?.name}`} | ||
aria-label={`GA4 event reference documentation`} | ||
target='_blank' | ||
> | ||
{selectedEvent?.name} | ||
</Link> {selectedEvent?.description}</p> | ||
<Tabs value={value} onChange={handleChange}> | ||
<Tab label="gtag.js Code"/> | ||
<Tab label="Google Tag Manager Code" disabled/> | ||
|
||
</Tabs> | ||
<TabPanel index={0}> | ||
Item One | ||
</TabPanel> | ||
<TextField | ||
multiline | ||
defaultValue={selectedEvent?.snippet} | ||
variant="filled" | ||
fullWidth={true} | ||
InputProps={{ | ||
readOnly: true, | ||
}} | ||
/> | ||
</DialogContentText> | ||
</DialogContent> | ||
<DialogActions> | ||
<Button color="primary"> | ||
Copy | ||
</Button> | ||
<Button onClick={handleClose} color="primary"> | ||
Close | ||
</Button> | ||
</DialogActions> | ||
</Dialog> | ||
</div> | ||
) | ||
} |
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,28 @@ | ||
.container { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} | ||
|
||
.header { | ||
display: grid; | ||
width: 100%; | ||
padding: var(--size-gap) var(--size-gutter); | ||
grid-template-columns: 1fr; | ||
grid-template-areas: "cartButton" "navHeader"; | ||
align-items: start; | ||
background-color: var(--background); | ||
} | ||
|
||
@media (min-width: 640px) { | ||
.header { | ||
grid-template-columns: 1fr min-content; | ||
grid-template-areas: "navHeader cartButton"; | ||
} | ||
} | ||
|
||
|
||
.nav { | ||
grid-area: navHeader; | ||
align-self: stretch; | ||
} |
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,26 @@ | ||
import * as React from "react" | ||
import {StoreContext} from "./store-context" | ||
import {CartButton} from "./cart-button" | ||
import {Navigation} from "./navigation" | ||
|
||
import {container, header, nav,} from "./header.module.css" | ||
|
||
export function Header() { | ||
const {cart} = React.useContext(StoreContext) | ||
|
||
const items = cart ? cart : [] | ||
|
||
const quantity = items.reduce((total, item) => { | ||
return total + item.quantity | ||
}, 0) | ||
|
||
return ( | ||
|
||
<div className={container}> | ||
<header className={header}> | ||
<Navigation className={nav}/> | ||
<CartButton quantity={quantity}/> | ||
</header> | ||
</div> | ||
) | ||
} |
36 changes: 36 additions & 0 deletions
36
src/components/ga4/EnhancedEcommerce/navigation.module.css
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,36 @@ | ||
.navStyle { | ||
display: flex; | ||
flex-direction: row; | ||
align-items: center; | ||
overflow-x: auto; | ||
white-space: nowrap; | ||
font-weight: var(--medium); | ||
} | ||
|
||
.navLink { | ||
cursor: pointer; | ||
text-decoration: none; | ||
height: var(--size-input); | ||
display: flex; | ||
color: var(--text-color-secondary); | ||
align-items: center; | ||
padding-left: var(--space-md); | ||
padding-right: var(--space-md); | ||
} | ||
|
||
.navLink:hover { | ||
color: var(--text-color); | ||
} | ||
|
||
.activeLink, | ||
.navLink[aria-active="page"] { | ||
color: var(--primary); | ||
text-decoration: underline; | ||
text-decoration-thickness: 2px; | ||
text-underline-offset: 4px; | ||
} | ||
|
||
.activeLink:hover, | ||
.navLink[aria-active="page"]:hover { | ||
color: var(--primary); | ||
} |
Oops, something went wrong.