Skip to content

Commit 5a5c3b7

Browse files
authored
Merge pull request #40656 from github/repo-sync
Repo sync
2 parents 95612ba + e9e2157 commit 5a5c3b7

File tree

3 files changed

+113
-3
lines changed

3 files changed

+113
-3
lines changed

.github/workflows/generate-code-scanning-query-lists.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,10 @@ jobs:
153153
--title "Update CodeQL query tables" \
154154
--repo github/docs-internal \
155155
--label "codeql-query-tables,skip FR board,ready-for-doc-review,workflow-generated" \
156-
--body '👋 humans. This PR updates the **CodeQL query table reusables** with the latest changes in preparation for the next **CodeQL CLI** release.
156+
--body '👋 humans. This PR updates the **CodeQL query table reusables** with the latest changes in preparation for the next **CodeQL CLI** release. (Synced from codeql@${{ steps.codeql.outputs.OPENAPI_COMMIT_SHA }})
157157
158-
No action is required from the first responder for the Docs content team. This PR will be reviewed and merged by the Code scanning and GHAS focus team as part of the next release of CodeQL CLI. (Synced from codeql@${{ steps.codeql.outputs.OPENAPI_COMMIT_SHA }})
159158
160-
If CI does not pass or other problems arise, contact #docs-engineering on slack.'
159+
No action is required from the first responder for the Docs content team. This PR is automatically added to the Docs content review board. Any writer can review this by checking that the PR looks sensible. If CI does not pass or other problems arise, contact #docs-engineering on slack.
160+
161+
162+
When the DRI for the CodeQL CLI release is ready to publish, they will ask us to merge this PR in #docs-content.'
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { useEffect } from 'react'
2+
import { useRouter } from 'next/router'
3+
4+
type UtmPreserverProps = {
5+
// CSS selector for links that should preserve UTM parameters
6+
linkSelector?: string
7+
// Specific page paths where this component should be active
8+
activePaths?: string[]
9+
}
10+
11+
export const UtmPreserver = ({
12+
linkSelector = 'a[href*="github.com/copilot"], a[href*="github.com/github-copilot"]',
13+
activePaths = ['/copilot/get-started/plans'],
14+
}: UtmPreserverProps) => {
15+
const router = useRouter()
16+
17+
useEffect(() => {
18+
// Check if current page should have UTM preservation
19+
const shouldPreserveUtm = activePaths.some((path) => router.asPath.includes(path))
20+
if (!shouldPreserveUtm) return
21+
22+
// Extract UTM parameters from current URL
23+
const getUtmParams = (): URLSearchParams => {
24+
const urlParams = new URLSearchParams(window.location.search)
25+
const utmParams = new URLSearchParams()
26+
27+
for (const [key, value] of urlParams) {
28+
if (key.startsWith('utm_')) {
29+
utmParams.set(key, value)
30+
}
31+
}
32+
33+
return utmParams
34+
}
35+
36+
// Add UTM parameters to a URL
37+
const addUtmParamsToUrl = (url: string, utmParams: URLSearchParams): string => {
38+
try {
39+
const urlObj = new URL(url)
40+
41+
for (const [key, value] of utmParams) {
42+
urlObj.searchParams.set(key, value)
43+
}
44+
45+
return urlObj.toString()
46+
} catch {
47+
// If URL parsing fails, return original URL
48+
return url
49+
}
50+
}
51+
52+
// Apply UTM parameters to relevant links
53+
const applyUtmToLinks = (): void => {
54+
const utmParams = getUtmParams()
55+
56+
if (utmParams.toString() === '') return
57+
58+
const links = document.querySelectorAll<HTMLAnchorElement>(linkSelector)
59+
60+
links.forEach((link) => {
61+
if (link.href && (link.href.startsWith('http://') || link.href.startsWith('https://'))) {
62+
link.href = addUtmParamsToUrl(link.href, utmParams)
63+
}
64+
})
65+
}
66+
67+
// Handle click events for dynamic link modification
68+
const handleLinkClick = (event: Event): void => {
69+
const link = (event.target as Element)?.closest('a') as HTMLAnchorElement
70+
if (!link) return
71+
72+
// Check if this link matches our selector
73+
if (!link.matches(linkSelector)) return
74+
75+
const utmParams = getUtmParams()
76+
if (utmParams.toString() === '') return
77+
78+
if (link.href && (link.href.startsWith('http://') || link.href.startsWith('https://'))) {
79+
link.href = addUtmParamsToUrl(link.href, utmParams)
80+
}
81+
}
82+
83+
// Apply UTM parameters immediately to existing links
84+
applyUtmToLinks()
85+
86+
// Also handle clicks for any dynamically added links
87+
document.addEventListener('click', handleLinkClick, true)
88+
89+
// Re-apply when the route changes (for single-page navigation)
90+
const handleRouteChange = () => {
91+
// Small delay to ensure DOM has updated
92+
setTimeout(applyUtmToLinks, 100)
93+
}
94+
95+
router.events.on('routeChangeComplete', handleRouteChange)
96+
97+
// Cleanup
98+
return () => {
99+
document.removeEventListener('click', handleLinkClick, true)
100+
router.events.off('routeChangeComplete', handleRouteChange)
101+
}
102+
}, [router.asPath, router.events, linkSelector, activePaths])
103+
104+
// This component doesn't render anything
105+
return null
106+
}

src/frame/components/article/ArticlePage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { Breadcrumbs } from '@/frame/components/page-header/Breadcrumbs'
2121
import { Link } from '@/frame/components/Link'
2222
import { useTranslation } from '@/languages/components/useTranslation'
2323
import { LinkPreviewPopover } from '@/links/components/LinkPreviewPopover'
24+
import { UtmPreserver } from '@/frame/components/UtmPreserver'
2425

2526
const ClientSideRefresh = dynamic(() => import('@/frame/components/ClientSideRefresh'), {
2627
ssr: false,
@@ -101,6 +102,7 @@ export const ArticlePage = () => {
101102
return (
102103
<DefaultLayout>
103104
<LinkPreviewPopover />
105+
<UtmPreserver />
104106
{isDev && <ClientSideRefresh />}
105107
{router.pathname.includes('/rest/') && <RestRedirect />}
106108
{currentLayout === 'inline' ? (

0 commit comments

Comments
 (0)