Skip to content

Commit

Permalink
Merge pull request #634 from vtex-apps/feature/add-event-to-custom-se…
Browse files Browse the repository at this point in the history
…arch

Add page view event to custom search
  • Loading branch information
thalytafabrine authored Jun 29, 2023
2 parents aa031e1 + 6a23cc4 commit f7fec42
Show file tree
Hide file tree
Showing 7 changed files with 489 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added
- `pageInfo` event for custom search pages.

## [3.122.6] - 2023-06-26

### Changed
Expand Down
110 changes: 106 additions & 4 deletions react/components/SearchResultCustomQueryWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import React, { Fragment } from 'react'
import React, { Fragment, useMemo } from 'react'
import type { RuntimeWithRoute } from 'vtex.render-runtime'
import { useRuntime, Helmet } from 'vtex.render-runtime'
import { canUseDOM, useRuntime, Helmet } from 'vtex.render-runtime'
import queryString from 'query-string'

import useDataPixel from '../hooks/useDataPixel'
import { usePageView } from '../hooks/usePageView'
import type { SearchQuery } from '../utils/searchMetadata'
import {
getCategoryMetadata,
getDepartmentMetadata,
getPageEventName,
getSearchMetadata,
getTitleTag,
} from '../utils/searchMetadata'

interface GetHelmetLinkParams {
canonicalLink: string | undefined
page: number
Expand Down Expand Up @@ -96,30 +107,121 @@ function isNotLastPage({ products, to, recordsFiltered }: IsNotLastPageParams) {
return to + 1 < recordsFiltered
}

const getSearchIdentifier = (
searchQuery: SearchQuery,
orderBy?: string,
page?: string
) => {
const { variables } = searchQuery

if (!variables) {
return
}

const { query, map } = variables

return query + map + (orderBy ?? '') + (page ?? '')
}

const SearchResultCustomQueryWrapper = (props: any) => {
const { localSearchQueryData, children } = props

const {
maxItemsPerPage,
searchQuery,
searchQuery: {
data: {
searchMetadata: { titleTag = '' } = {},
productSearch: {
products: currProducts = undefined,
recordsFiltered: currRecords = undefined,
} = {},
facets: { recordsFiltered: legacyRecords },
facets: { recordsFiltered: legacyRecords, queryArgs },
products: legacyProducts,
},
variables: { fullText },
},
orderBy,
facetsLoading,
} = localSearchQueryData

const loading = searchQuery ? searchQuery.loading : undefined
const products = currProducts ?? legacyProducts
const recordsFiltered = currRecords ?? legacyRecords

const {
query: { page: pageFromQuery = 1 },
getSettings,
account,
query: { page: pageFromQuery = '1' },
route: { title: pageTitle },
} = useRuntime() as RuntimeWithRoute

const settings = getSettings('vtex.store') || {}
const {
titleTag: defaultStoreTitle,
storeName,
enablePageNumberTitle = false,
removeStoreNameTitle = false,
} = settings

const title = getTitleTag({
titleTag,
storeTitle: storeName || defaultStoreTitle,
term: fullText,
pageTitle,
pageNumber: enablePageNumberTitle ? Number(pageFromQuery) : 0,
removeStoreNameTitle,
})

const pixelEvents = useMemo(() => {
if (!canUseDOM || !currProducts || !queryArgs || facetsLoading) {
return null
}

const event = getPageEventName(
currProducts,
localSearchQueryData.searchQuery.variables
)

const pageInfoEvent = {
event: 'pageInfo',
eventType: event,
accountName: account,
pageUrl: window.location.href,
orderBy,
page: pageFromQuery,
category: searchQuery?.data
? getCategoryMetadata(searchQuery.data)
: null,
department: searchQuery?.data
? getDepartmentMetadata(searchQuery.data)
: null,
search: searchQuery?.data ? getSearchMetadata(searchQuery.data) : null,
}

return [
pageInfoEvent,
{
event,
products: currProducts,
},
]
}, [
currProducts,
queryArgs,
facetsLoading,
localSearchQueryData.searchQuery.variables,
account,
orderBy,
pageFromQuery,
searchQuery.data,
])

const pixelCacheKey = getSearchIdentifier(searchQuery, orderBy, pageFromQuery)

usePageView({ title, cacheKey: pixelCacheKey })
useDataPixel(pixelEvents, pixelCacheKey, loading)

const canonicalLink = useCanonicalLink()
const pageNumber = Number(pageFromQuery)

Expand Down
33 changes: 33 additions & 0 deletions react/hooks/useDataPixel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable no-restricted-imports */
import { useEffect, useRef } from 'react'
import { usePixel } from 'vtex.pixel-manager/PixelContext'
import { isEmpty } from 'ramda'

type Data = unknown[] | unknown

const useDataPixel = (data: Data, pageIdentifier = '', isLoading = false) => {
const { push } = usePixel()
const previousIdRef = useRef<string | null>(null)

const previousId = previousIdRef.current

useEffect(() => {
if (!pageIdentifier || isLoading || previousId === pageIdentifier) {
return
}

if (!data || isEmpty(data)) {
return
}

if (Array.isArray(data)) {
data.forEach(push)
} else {
push(data)
}

previousIdRef.current = pageIdentifier
}, [data, isLoading, pageIdentifier, previousId, push])
}

export default useDataPixel
41 changes: 41 additions & 0 deletions react/hooks/usePageView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* eslint-disable no-restricted-globals */
import { useMemo } from 'react'
import { useRuntime, canUseDOM } from 'vtex.render-runtime'

import useDataPixel from './useDataPixel'

interface UsePageViewArgs {
title?: string
cacheKey?: string
skip?: boolean
}

export const usePageView = ({
title,
cacheKey,
skip,
}: UsePageViewArgs = {}) => {
const { route, account } = useRuntime()
const pixelCacheKey = cacheKey ?? route.routeId

const eventData = useMemo(() => {
if (!canUseDOM || skip) {
return null
}

return {
event: 'pageView',
pageTitle: title ?? document.title,
pageUrl: location.href,
referrer:
document.referrer.indexOf(location.origin) === 0
? undefined
: document.referrer,
accountName: account,
routeId: route?.routeId ? route.routeId : '',
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [account, title, canUseDOM, pixelCacheKey])

useDataPixel(skip ? null : eventData, pixelCacheKey)
}
7 changes: 7 additions & 0 deletions react/typings/vtex.pixel-manager.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module 'vtex.pixel-manager/PixelContext' {
interface Pixel {
push(data: unknown): void
}

export function usePixel(): Pixel
}
1 change: 1 addition & 0 deletions react/typings/vtex.render-runtime.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ declare module 'vtex.render-runtime' {

export function useRuntime(): Runtime
export const Helmet
export const canUseDOM: boolean
}
Loading

0 comments on commit f7fec42

Please sign in to comment.