Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion app/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,32 @@ const gtagFn: GtagFn = import.meta.env.PROD
console.log('[gtag]', command, name, params)
}

interface Twq {
(...args: unknown[]): void
queue?: unknown[][]
}

const xPixelFn = import.meta.env.PROD
? () => {
const w = window as Window & { twq?: Twq }
if (typeof w.twq === 'function') {
w.twq('event', 'tw-ov0j6-ov0j9', {})
} else {
// Initialize stub + queue if the base X pixel script (uwt.js) has not loaded yet.
// Mirrors the queuing behavior of the X base IIFE so early calls are not lost.
const queue: unknown[][] = []
const stub: Twq = (...args: unknown[]) => {
queue.push(args)
}
stub.queue = queue
w.twq = stub
stub('event', 'tw-ov0j6-ov0j9', {})
}
}
: () => {
console.log('[x-pixel]', 'event', 'tw-ov0j6-ov0j9', {})
}

setupScrollDepthTracking({
gtagFn,
addScrollListener: (handler) => window.addEventListener('scroll', handler, { passive: true }),
Expand Down Expand Up @@ -83,4 +109,4 @@ setupMakamujoBannerTracking({
maxDelayMs: 500,
})

setupDownloadAdPopup({ whenReady, gtagFn })
setupDownloadAdPopup({ whenReady, gtagFn, xPixelFn })
35 changes: 35 additions & 0 deletions app/components/DownloadAdDialog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ function makeSetup(
startDownload?: ReturnType<typeof vi.fn>
hasDownloadUi?: () => boolean
gtagFn?: ReturnType<typeof vi.fn>
xPixelFn?: ReturnType<typeof vi.fn>
getPagePath?: () => string
baseUrl?: string
} = {},
Expand All @@ -119,6 +120,7 @@ function makeSetup(
},
startDownload: startDownloadFn,
gtagFn: opts.gtagFn,
xPixelFn: opts.xPixelFn,
getPagePath: opts.getPagePath,
baseUrl: opts.baseUrl,
})
Expand Down Expand Up @@ -277,6 +279,39 @@ describe('setupDownloadAdPopup', () => {
expect(gtagFn).not.toHaveBeenCalled()
})

it('fires X pixel conversion when xPixelFn is provided', () => {
const button = {
...makeFakeButton('./test.pdf'),
textContent: 'ダウンロード',
}
const xPixelFn = vi.fn()
const { triggerClick } = makeSetup(button, { xPixelFn })

triggerClick()

expect(xPixelFn).toHaveBeenCalledTimes(1)
})

it('fires both GA and X pixel on download when both fns provided', () => {
const button = makeFakeButton()
const gtagFn = vi.fn()
const xPixelFn = vi.fn()
const { triggerClick } = makeSetup(button, { gtagFn, xPixelFn })

triggerClick()
expect(gtagFn).toHaveBeenCalled()
expect(xPixelFn).toHaveBeenCalled()
})

it('does not fire X pixel when xPixelFn is omitted', () => {
const button = makeFakeButton()
const xPixelFn = vi.fn()
const { triggerClick } = makeSetup(button)

triggerClick()
expect(xPixelFn).not.toHaveBeenCalled()
})

it('opens cross-origin downloads in a new tab', () => {
const button = makeFakeButton('https://example.com/file.zip', { sameOrigin: false, newTab: true })
const { startDownloadFn, triggerClick } = makeSetup(button)
Expand Down
6 changes: 6 additions & 0 deletions app/components/DownloadAdDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export interface DownloadAdPopupOptions {
baseUrl?: string
/** Sends GA4 `file_download` events when a download button is clicked. */
gtagFn?: GtagFn
/** Sends X Ads conversion event when a download button is clicked. */
xPixelFn?: () => void
/** @internal Test override for the current page path sent as `link_id`. */
getPagePath?: () => string
}
Expand Down Expand Up @@ -128,6 +130,7 @@ export function setupDownloadAdPopup({
hasDownloadUi = defaultHasDownloadUi,
baseUrl,
gtagFn,
xPixelFn,
getPagePath = () => (typeof window !== 'undefined' ? window.location.pathname : ''),
}: DownloadAdPopupOptions): void {
whenReady(() => {
Expand All @@ -150,6 +153,9 @@ export function setupDownloadAdPopup({
const linkText = button.textContent?.trim() ?? ''
trackFileDownload(gtagFn, href, linkText, getPagePath())
}
if (xPixelFn) {
xPixelFn()
}
startDownloadFn(href, download, newTab)
})
})
Expand Down
14 changes: 14 additions & 0 deletions app/components/RootLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@ const gtagSnippets = {
`,
} as const

const xPixelSnippets = {
body: html`\
<!-- X conversion tracking base code -->
<script>
!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);
},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js',
a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');
twq('config','ov0j6');
</script>
<!-- End X conversion tracking base code -->
`,
} as const

const OpenGraph = ({ url, image }: OpenGraphData) =>
url || image ? (
<>
Expand Down Expand Up @@ -203,6 +216,7 @@ const Layout = (props: PropsWithChildren<Meta>) => html`
<body>
${props.children}
${import.meta.env.PROD ? gtagSnippets.body : html`<!-- -->\n`}\
${import.meta.env.PROD && props.downloadAdPopup ? xPixelSnippets.body : html`<!-- -->\n`}\
</body>
</html>
`
Expand Down