Support Portal & Docs Collaboration: Virtual Assistant in Docs (#35772)
Co-authored-by: Peter Bengtsson <peterbe@github.com> Co-authored-by: Peter Bengtsson <mail@peterbe.com>
This commit is contained in:
@@ -23,6 +23,7 @@ import { Breadcrumbs } from 'components/page-header/Breadcrumbs'
|
||||
import { Link } from 'components/Link'
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { LinkPreviewPopover } from 'components/LinkPreviewPopover'
|
||||
import { SupportPortalVaIframe } from 'components/article/SupportPortalVaIframe'
|
||||
|
||||
const ClientSideRefresh = dynamic(() => import('components/ClientSideRefresh'), {
|
||||
ssr: false,
|
||||
@@ -43,12 +44,17 @@ export const ArticlePage = () => {
|
||||
productVideoUrl,
|
||||
miniTocItems,
|
||||
currentLearningTrack,
|
||||
supportPortalVaIframeProps,
|
||||
} = useArticleContext()
|
||||
const isLearningPath = !!currentLearningTrack?.trackName
|
||||
const { t } = useTranslation(['pages'])
|
||||
|
||||
return (
|
||||
<DefaultLayout>
|
||||
{supportPortalVaIframeProps.supportPortalUrl &&
|
||||
supportPortalVaIframeProps.vaFlowUrlParameter && (
|
||||
<SupportPortalVaIframe supportPortalVaIframeProps={supportPortalVaIframeProps} />
|
||||
)}
|
||||
<LinkPreviewPopover />
|
||||
{isDev && <ClientSideRefresh />}
|
||||
<ClientSideHighlight />
|
||||
|
||||
7
components/article/SupportPortalVaIframe.module.scss
Normal file
7
components/article/SupportPortalVaIframe.module.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
.supportPortalIframe {
|
||||
height: 95px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
display: none;
|
||||
}
|
||||
95
components/article/SupportPortalVaIframe.tsx
Normal file
95
components/article/SupportPortalVaIframe.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import styles from './SupportPortalVaIframe.module.scss'
|
||||
export interface SupportPortalVaIframeProps {
|
||||
supportPortalUrl: string
|
||||
vaFlowUrlParameter: string
|
||||
}
|
||||
|
||||
const fullIframeHeight = '750px'
|
||||
|
||||
export function SupportPortalVaIframe({
|
||||
supportPortalVaIframeProps,
|
||||
}: {
|
||||
supportPortalVaIframeProps: SupportPortalVaIframeProps
|
||||
}) {
|
||||
const [autoStartVa, setAutoStartVa] = useState(false)
|
||||
const router = useRouter()
|
||||
enum vaIframeMessageType {
|
||||
OPEN = 'open',
|
||||
START = 'start',
|
||||
STOP = 'stop',
|
||||
}
|
||||
|
||||
const [showIframe, setIframe] = useState(true)
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
setAutoStartVa(router.query.autoStartVa === 'true')
|
||||
}, [router.query])
|
||||
|
||||
useEffect(() => {
|
||||
function eventHandler(event: MessageEvent<{ type: vaIframeMessageType }>) {
|
||||
// An extra security measure which double checks that the events originate from the Support Portal domain
|
||||
if (event.origin !== supportPortalVaIframeProps.supportPortalUrl) return
|
||||
const message = event.data
|
||||
switch (message.type) {
|
||||
case vaIframeMessageType.OPEN:
|
||||
// We need to set the display to inline from a ref explicitly to prevent the component from rerendering
|
||||
// The iframe is hidden by default to allow Support Portal to disable the iframe remotely
|
||||
if (iframeRef.current) {
|
||||
iframeRef.current.style.display = 'inline'
|
||||
}
|
||||
break
|
||||
case vaIframeMessageType.START:
|
||||
// We need to set the height explicitly from a ref to prevent the component from rerendering
|
||||
if (iframeRef.current) {
|
||||
iframeRef.current.style.height = fullIframeHeight
|
||||
}
|
||||
break
|
||||
case vaIframeMessageType.STOP:
|
||||
// Effectively hide iframe. If preferred the element can also be deleted or display set to hidden.
|
||||
setIframe(false)
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
window.addEventListener('message', eventHandler)
|
||||
return () => {
|
||||
window.removeEventListener('message', eventHandler)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// Communicate to Support Portal that the iframe is ready to receive messages
|
||||
// If the iframe feature is disabled on the Support Portal, no message will be sent back iframe will remain hidden
|
||||
if (iframeRef.current?.contentWindow) {
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
'ready',
|
||||
supportPortalVaIframeProps.supportPortalUrl
|
||||
)
|
||||
}
|
||||
}, [iframeRef.current?.contentWindow])
|
||||
|
||||
if (!showIframe) {
|
||||
return null
|
||||
}
|
||||
|
||||
const usp = new URLSearchParams({ flow: supportPortalVaIframeProps.vaFlowUrlParameter })
|
||||
usp.set('flow', supportPortalVaIframeProps.vaFlowUrlParameter)
|
||||
if (autoStartVa) {
|
||||
usp.set('autoStartVa', 'true')
|
||||
}
|
||||
|
||||
const src = `${supportPortalVaIframeProps.supportPortalUrl}/iframe/docs_va?${usp}`
|
||||
|
||||
return (
|
||||
<iframe
|
||||
className={styles.supportPortalIframe}
|
||||
ref={iframeRef}
|
||||
title="support-portal-va"
|
||||
id="support-portal-iframe"
|
||||
src={src}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { SupportPortalVaIframeProps } from 'components/article/SupportPortalVaIframe'
|
||||
import { createContext, useContext } from 'react'
|
||||
|
||||
export type LearningTrack = {
|
||||
@@ -36,6 +37,7 @@ export type ArticleContextT = {
|
||||
detectedPlatforms: Array<string>
|
||||
detectedTools: Array<string>
|
||||
allTools: Record<string, string>
|
||||
supportPortalVaIframeProps: SupportPortalVaIframeProps
|
||||
}
|
||||
|
||||
export const ArticleContext = createContext<ArticleContextT | null>(null)
|
||||
@@ -50,6 +52,11 @@ export const useArticleContext = (): ArticleContextT => {
|
||||
return context
|
||||
}
|
||||
|
||||
const PagePathToVaFlowMapping: Record<string, string> = {
|
||||
'content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/why-are-my-contributions-not-showing-up-on-my-profile.md':
|
||||
'contribution_troubleshooting',
|
||||
}
|
||||
|
||||
export const getArticleContextFromRequest = (req: any): ArticleContextT => {
|
||||
const page = req.context.page
|
||||
|
||||
@@ -61,6 +68,17 @@ export const getArticleContextFromRequest = (req: any): ArticleContextT => {
|
||||
}
|
||||
}
|
||||
|
||||
const supportPortalUrl =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? 'https://support.github.com'
|
||||
: // Assume that a developer is not testing the VA iframe locally if this env var is not set
|
||||
process.env.SUPPORT_PORTAL_URL || ''
|
||||
|
||||
const supportPortalVaIframeProps = {
|
||||
supportPortalUrl,
|
||||
vaFlowUrlParameter: PagePathToVaFlowMapping[req.context.page.fullPath] || '',
|
||||
}
|
||||
|
||||
return {
|
||||
title: page.title,
|
||||
intro: page.intro,
|
||||
@@ -78,5 +96,6 @@ export const getArticleContextFromRequest = (req: any): ArticleContextT => {
|
||||
detectedPlatforms: page.detectedPlatforms || [],
|
||||
detectedTools: page.detectedTools || [],
|
||||
allTools: page.allToolsParsed || [], // this is set at the page level, see lib/page.js
|
||||
supportPortalVaIframeProps,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user