Skip to content

Commit

Permalink
Refactor display-platform-specific-content (#22665)
Browse files Browse the repository at this point in the history
* refactor display-platform-specific-content

* update PlatformPicker tests and cleanup
  • Loading branch information
mikesurowiec authored Nov 12, 2021
1 parent 12f5437 commit 7310fc9
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 189 deletions.
32 changes: 2 additions & 30 deletions components/article/ArticlePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { LearningTrackNav } from './LearningTrackNav'
import { MarkdownContent } from 'components/ui/MarkdownContent'
import { Lead } from 'components/ui/Lead'
import { ArticleGridLayout } from './ArticleGridLayout'
import { PlatformPicker } from 'components/article/PlatformPicker'

// Mapping of a "normal" article to it's interactive counterpart
const interactiveAlternatives: Record<string, { href: string }> = {
Expand All @@ -35,7 +36,6 @@ export const ArticlePage = () => {
contributor,
permissions,
includesPlatformSpecificContent,
defaultPlatform,
product,
miniTocItems,
currentLearningTrack,
Expand Down Expand Up @@ -87,35 +87,7 @@ export const ArticlePage = () => {
</div>
)}

{includesPlatformSpecificContent && (
<nav
className="UnderlineNav my-3"
data-default-platform={defaultPlatform || undefined}
>
<div className="UnderlineNav-body">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" className="UnderlineNav-item platform-switcher" data-platform="mac">
Mac
</a>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a
href="#"
className="UnderlineNav-item platform-switcher"
data-platform="windows"
>
Windows
</a>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a
href="#"
className="UnderlineNav-item platform-switcher"
data-platform="linux"
>
Linux
</a>
</div>
</nav>
)}
{includesPlatformSpecificContent && <PlatformPicker variant="underlinenav" />}

{product && (
<Callout
Expand Down
163 changes: 163 additions & 0 deletions components/article/PlatformPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { useEffect, useState } from 'react'
import Cookies from 'js-cookie'
import { SubNav, TabNav, UnderlineNav } from '@primer/components'
import { sendEvent, EventType } from 'components/lib/events'

import { useArticleContext } from 'components/context/ArticleContext'
import parseUserAgent from 'components/lib/user-agent'

const platforms = [
{ id: 'mac', label: 'Mac' },
{ id: 'windows', label: 'Windows' },
{ id: 'linux', label: 'Linux' },
]

// Imperatively modify article content to show only the selected platform
// find all platform-specific *block* elements and hide or show as appropriate
// example: {% mac } block content {% mac %}
function showPlatformSpecificContent(platform: string) {
const markdowns = Array.from(document.querySelectorAll<HTMLElement>('.extended-markdown'))
markdowns
.filter((el) => platforms.some((platform) => el.classList.contains(platform.id)))
.forEach((el) => {
el.style.display = el.classList.contains(platform) ? '' : 'none'
})

// find all platform-specific *inline* elements and hide or show as appropriate
// example: <span class="platform-mac">inline content</span>
const platformEls = Array.from(
document.querySelectorAll<HTMLElement>(
platforms.map((platform) => `.platform-${platform.id}`).join(', ')
)
)
platformEls.forEach((el) => {
el.style.display = el.classList.contains(`platform-${platform}`) ? '' : 'none'
})
}

// uses the order of the supportedPlatforms array to
// determine the default platform
const getFallbackPlatform = (detectedPlatforms: Array<string>): string => {
const foundPlatform = platforms.find((platform) => detectedPlatforms.includes(platform.id))
return foundPlatform?.id || 'linux'
}

type Props = {
variant?: 'subnav' | 'tabnav' | 'underlinenav'
}
export const PlatformPicker = ({ variant = 'subnav' }: Props) => {
const { defaultPlatform, detectedPlatforms } = useArticleContext()
const [currentPlatform, setCurrentPlatform] = useState(defaultPlatform || '')

// Run on mount for client-side only features
useEffect(() => {
let userAgent = parseUserAgent().os
if (userAgent === 'ios') {
userAgent = 'mac'
}

setCurrentPlatform(defaultPlatform || Cookies.get('osPreferred') || userAgent || 'linux')
}, [])

// Make sure we've always selected a platform that exists in the article
useEffect(() => {
// Only check *after* current platform has been determined
if (currentPlatform && !detectedPlatforms.includes(currentPlatform)) {
setCurrentPlatform(getFallbackPlatform(detectedPlatforms))
}
}, [currentPlatform, detectedPlatforms.join(',')])

const onClickPlatform = (platform: string) => {
setCurrentPlatform(platform)

// imperatively modify the article content
showPlatformSpecificContent(platform)

sendEvent({
type: EventType.preference,
preference_name: 'os',
preference_value: platform,
})

Cookies.set('osPreferred', platform, {
sameSite: 'strict',
secure: true,
})
}

// only show platforms that are in the current article
const platformOptions = platforms.filter((platform) => detectedPlatforms.includes(platform.id))

const sharedContainerProps = {
'data-testid': 'platform-picker',
'aria-label': 'Platform picker',
'data-default-platform': defaultPlatform,
className: 'mb-4',
}

if (variant === 'subnav') {
return (
<SubNav {...sharedContainerProps}>
<SubNav.Links>
{platformOptions.map((option) => {
return (
<SubNav.Link
key={option.id}
data-platform={option.id}
as="button"
selected={option.id === currentPlatform}
onClick={() => {
onClickPlatform(option.id)
}}
>
{option.label}
</SubNav.Link>
)
})}
</SubNav.Links>
</SubNav>
)
}

if (variant === 'underlinenav') {
return (
<UnderlineNav {...sharedContainerProps}>
{platformOptions.map((option) => {
return (
<UnderlineNav.Link
key={option.id}
data-platform={option.id}
as="button"
selected={option.id === currentPlatform}
onClick={() => {
onClickPlatform(option.id)
}}
>
{option.label}
</UnderlineNav.Link>
)
})}
</UnderlineNav>
)
}

return (
<TabNav {...sharedContainerProps}>
{platformOptions.map((option) => {
return (
<TabNav.Link
key={option.id}
data-platform={option.id}
as="button"
selected={option.id === currentPlatform}
onClick={() => {
onClickPlatform(option.id)
}}
>
{option.label}
</TabNav.Link>
)
})}
</TabNav>
)
}
2 changes: 2 additions & 0 deletions components/context/ArticleContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type ArticleContextT = {
defaultPlatform?: string
product?: string
currentLearningTrack?: LearningTrack
detectedPlatforms: Array<string>
}

export const ArticleContext = createContext<ArticleContextT | null>(null)
Expand Down Expand Up @@ -62,5 +63,6 @@ export const getArticleContextFromRequest = (req: any): ArticleContextT => {
defaultPlatform: page.defaultPlatform || '',
product: page.product || '',
currentLearningTrack: req.context.currentLearningTrack,
detectedPlatforms: page.detectedPlatforms || [],
}
}
132 changes: 0 additions & 132 deletions components/lib/display-platform-specific-content.ts

This file was deleted.

6 changes: 6 additions & 0 deletions lib/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,12 @@ class Page {
html.includes('extended-markdown windows') ||
html.includes('extended-markdown linux')

this.detectedPlatforms = [
html.includes('extended-markdown mac') && 'mac',
html.includes('extended-markdown windows') && 'windows',
html.includes('extended-markdown linux') && 'linux',
].filter(Boolean)

return html
}

Expand Down
2 changes: 0 additions & 2 deletions pages/[versionId]/[productId]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useRouter } from 'next/router'
// "legacy" javascript needed to maintain existing functionality
// typically operating on elements **within** an article.
import copyCode from 'components/lib/copy-code'
import displayPlatformSpecificContent from 'components/lib/display-platform-specific-content'
import displayToolSpecificContent from 'components/lib/display-tool-specific-content'
import localization from 'components/lib/localization'
import wrapCodeTerms from 'components/lib/wrap-code-terms'
Expand Down Expand Up @@ -41,7 +40,6 @@ import { useEffect } from 'react'

function initiateArticleScripts() {
copyCode()
displayPlatformSpecificContent()
displayToolSpecificContent()
localization()
wrapCodeTerms()
Expand Down
Loading

0 comments on commit 7310fc9

Please sign in to comment.