diff --git a/components/page-header/Header.tsx b/components/page-header/Header.tsx
index f8a8fd59b4..74a64554dd 100644
--- a/components/page-header/Header.tsx
+++ b/components/page-header/Header.tsx
@@ -22,16 +22,15 @@ import styles from './Header.module.scss'
export const Header = () => {
const router = useRouter()
const { error } = useMainContext()
- const { allVersions } = useMainContext()
+ const { currentProduct, allVersions } = useMainContext()
const { currentVersion } = useVersion()
const { t } = useTranslation(['header', 'homepage'])
+ const isRestPage = currentProduct && currentProduct.id === 'rest'
const [isMenuOpen, setIsMenuOpen] = useState(
router.pathname !== '/' && router.query.query && true
)
const [scroll, setScroll] = useState(false)
-
const { hasAccount } = useHasAccount()
-
const signupCTAVisible =
hasAccount === false && // don't show if `null`
(currentVersion === DEFAULT_VERSION || currentVersion === 'enterprise-cloud@latest')
@@ -92,8 +91,8 @@ export const Header = () => {
-
-
+
+
{signupCTAVisible && (
{
- {allVersions[currentVersion].apiVersions.length > 0 && (
+ {isRestPage && allVersions[currentVersion].apiVersions.length > 0 && (
)}
diff --git a/components/page-header/LanguagePicker.tsx b/components/page-header/LanguagePicker.tsx
index 87c08e19ce..aa4f35c300 100644
--- a/components/page-header/LanguagePicker.tsx
+++ b/components/page-header/LanguagePicker.tsx
@@ -2,8 +2,8 @@ import { useRouter } from 'next/router'
import Cookies from 'js-cookie'
import { useLanguages } from 'components/context/LanguagesContext'
-import { Picker } from 'components/ui/Picker'
import { useTranslation } from 'components/hooks/useTranslation'
+import { Picker } from 'components/ui/Picker'
import { USER_LANGUAGE_COOKIE_NAME } from '../../lib/constants.js'
function rememberPreferredLanguage(value: string) {
@@ -31,7 +31,7 @@ function rememberPreferredLanguage(value: string) {
}
type Props = {
- variant?: 'inline'
+ variant: 'inline' | 'header'
}
export const LanguagePicker = ({ variant }: Props) => {
@@ -61,13 +61,20 @@ export const LanguagePicker = ({ variant }: Props) => {
({
+ items={langs.map((lang) => ({
text: lang.nativeName || lang.name,
selected: lang === selectedLang,
- locale: lang.code,
- href: `${routerPath}`,
- onselect: rememberPreferredLanguage,
+ href: `/${lang.code}${routerPath}`,
+ extra: {
+ locale: lang.code,
+ },
}))}
+ onSelect={(item) => {
+ if (item.extra?.locale) rememberPreferredLanguage(item.extra.locale)
+ }}
+ dataTestId="field"
+ ariaLabel="Select field type"
+ alignment="center"
/>
)
diff --git a/components/page-header/ProductPicker.tsx b/components/page-header/ProductPicker.tsx
index aaa1d4365a..8fe06fb806 100644
--- a/components/page-header/ProductPicker.tsx
+++ b/components/page-header/ProductPicker.tsx
@@ -1,8 +1,9 @@
import { useRouter } from 'next/router'
+import { LinkExternalIcon } from '@primer/octicons-react'
import { useMainContext } from 'components/context/MainContext'
-import { Picker } from 'components/ui/Picker'
import { useTranslation } from 'components/hooks/useTranslation'
+import { Picker } from 'components/ui/Picker'
export const ProductPicker = () => {
const router = useRouter()
@@ -14,12 +15,27 @@ export const ProductPicker = () => {
({
+ items={activeProducts.map((product) => ({
text: product.name,
selected: product.name === currentProduct?.name,
- external: product.external,
href: `${product.external ? '' : `/${router.locale}`}${product.href}`,
+ extra: {
+ external: product.external,
+ },
}))}
+ alignment="end"
+ dataTestId="field"
+ ariaLabel="Select field type"
+ renderItem={(item) => {
+ return item.extra?.external ? (
+ <>
+ {item.text}
+
+ >
+ ) : (
+ item.text
+ )
+ }}
/>
)
diff --git a/components/page-header/VersionPicker.tsx b/components/page-header/VersionPicker.tsx
index 276c782635..316b4a4509 100644
--- a/components/page-header/VersionPicker.tsx
+++ b/components/page-header/VersionPicker.tsx
@@ -1,4 +1,5 @@
import { useRouter } from 'next/router'
+import { ArrowRightIcon, InfoIcon } from '@primer/octicons-react'
import { useMainContext } from 'components/context/MainContext'
import { DEFAULT_VERSION, useVersion } from 'components/hooks/useVersion'
@@ -6,7 +7,7 @@ import { useTranslation } from 'components/hooks/useTranslation'
import { Picker } from 'components/ui/Picker'
type Props = {
- variant?: 'inline'
+ variant: 'inline' | 'header'
}
export const VersionPicker = ({ variant }: Props) => {
@@ -23,8 +24,10 @@ export const VersionPicker = ({ variant }: Props) => {
text: allVersions[permalink.pageVersion].versionTitle,
selected: currentVersion === permalink.pageVersion,
href: permalink.href,
- arrow: false,
- info: false,
+ extra: {
+ arrow: false,
+ info: false,
+ },
}))
const hasEnterpriseVersions = (page.permalinks || []).some((permalink) =>
@@ -35,9 +38,11 @@ export const VersionPicker = ({ variant }: Props) => {
allLinks.push({
text: t('all_enterprise_releases'),
selected: false,
- arrow: true,
href: `/${router.locale}/${enterpriseServerVersions[0]}/admin/all-releases`,
- info: false,
+ extra: {
+ arrow: true,
+ info: false,
+ },
})
}
@@ -47,15 +52,35 @@ export const VersionPicker = ({ variant }: Props) => {
allLinks.push({
text: t('about_versions'),
selected: false,
- arrow: false,
- info: true,
href: `/${router.locale}${currentVersionPathSegment}/get-started/learning-about-github/about-versions-of-github-docs`,
+ extra: {
+ arrow: false,
+ info: true,
+ },
})
}
return (
-
+
{
+ return (
+
+ {item.text}
+ {item.extra?.arrow && (
+
+ )}
+ {item.extra?.info &&
}
+
+ )
+ }}
+ />
)
}
diff --git a/components/sidebar/ApiVersionPicker.tsx b/components/sidebar/ApiVersionPicker.tsx
index 289da6779d..f8ebdd1c19 100644
--- a/components/sidebar/ApiVersionPicker.tsx
+++ b/components/sidebar/ApiVersionPicker.tsx
@@ -1,6 +1,7 @@
import { useRouter } from 'next/router'
import cx from 'classnames'
import Cookies from 'js-cookie'
+import { InfoIcon } from '@primer/octicons-react'
import { useMainContext } from 'components/context/MainContext'
import { DEFAULT_VERSION, useVersion } from 'components/hooks/useVersion'
@@ -13,7 +14,7 @@ import styles from './SidebarProduct.module.scss'
const API_VERSION_SUFFIX = ' (latest)'
type Props = {
- variant?: 'inline'
+ variant: 'inline' | 'header'
width?: number
}
@@ -67,19 +68,23 @@ export const ApiVersionPicker = ({ variant, width }: Props) => {
text: dateDisplayText,
selected: router.query.apiVersion === date,
href: itemLink,
- info: false,
- onselect: rememberApiVersion,
+ extra: {
+ info: false,
+ currentDate,
+ },
}
})
apiVersionLinks.push({
text: t('rest.versioning.about_versions'),
selected: false,
- info: true,
href: `/${router.locale}${
currentVersion === DEFAULT_VERSION ? '' : `/${currentVersion}`
}/rest/overview/api-versions`,
- onselect: rememberApiVersion,
+ extra: {
+ info: true,
+ currentDate,
+ },
})
// This only shows the REST Version picker if it's calendar date versioned
@@ -99,9 +104,26 @@ export const ApiVersionPicker = ({ variant, width }: Props) => {
{
+ if (item.extra?.currentDate) rememberApiVersion(item.extra.currentDate)
+ }}
+ renderItem={(item) => {
+ return item.extra?.info ? (
+
+ {item.text}
+
+
+ ) : (
+ item.text
+ )
+ }}
/>
diff --git a/components/sidebar/SidebarProduct.tsx b/components/sidebar/SidebarProduct.tsx
index e025d28623..e18431d267 100644
--- a/components/sidebar/SidebarProduct.tsx
+++ b/components/sidebar/SidebarProduct.tsx
@@ -109,7 +109,7 @@ export const SidebarProduct = () => {
)
return (
<>
-
+
{conceptualPages.map((childPage, i) => {
diff --git a/components/ui/Picker/Fields.tsx b/components/ui/Picker/Fields.tsx
new file mode 100644
index 0000000000..b361ab1356
--- /dev/null
+++ b/components/ui/Picker/Fields.tsx
@@ -0,0 +1,33 @@
+import { ReactNode } from 'react'
+import { ActionList } from '@primer/react'
+
+import { PickerItem } from './Picker'
+import { Link } from 'components/Link'
+
+export const Fields = (fieldProps: {
+ open: boolean
+ setOpen: React.Dispatch>
+ items: PickerItem[]
+ onSelect?: (item: PickerItem) => void
+ renderItem?: (item: PickerItem) => ReactNode | string
+}) => {
+ const { open, setOpen, items, onSelect, renderItem } = fieldProps
+
+ return (
+
+ {items.map((item) => (
+ {
+ if (onSelect) onSelect(item)
+ setOpen(!open)
+ }}
+ >
+ {renderItem ? renderItem(item) : item.text}
+
+ ))}
+
+ )
+}
diff --git a/components/ui/Picker/Picker.tsx b/components/ui/Picker/Picker.tsx
index 35b799e2d4..d742a878a6 100644
--- a/components/ui/Picker/Picker.tsx
+++ b/components/ui/Picker/Picker.tsx
@@ -1,105 +1,90 @@
-import React, { useState } from 'react'
-import { ActionList, ActionMenu, Box, Details, Text, useDetails } from '@primer/react'
-import { ArrowRightIcon, ChevronDownIcon, InfoIcon, LinkExternalIcon } from '@primer/octicons-react'
+import React, { ReactNode, useState } from 'react'
import cx from 'classnames'
+import { ActionMenu, Box, Details, Text, useDetails } from '@primer/react'
+import { ChevronDownIcon } from '@primer/octicons-react'
+import { AnchorAlignment } from '@primer/behaviors'
-import { Link } from 'components/Link'
+import { Fields } from './Fields'
-export type PickerOptionsTypeT = {
- text: string
- href: string
- locale?: string
- external?: boolean
- arrow?: boolean
- info?: boolean
- selected?: boolean
- onselect?: Function | void
-}
-
-export type PickerPropsT = {
- variant?: 'inline'
- apiVersion?: boolean
+interface Props {
+ variant: 'inline' | 'header'
+ items: PickerItem[]
+ onSelect?: (item: PickerItem) => void
+ buttonBorder?: boolean
+ pickerLabel?: string
+ dataTestId: string
defaultText: string
- options: Array
+ ariaLabel: string
+ alignment: AnchorAlignment
+ renderItem?: (item: PickerItem) => ReactNode | string
}
-export function Picker({ variant, apiVersion, defaultText, options }: PickerPropsT) {
+export interface PickerItem {
+ href: string
+ text: string
+ selected: boolean
+ extra?: {
+ [key: string]: any
+ }
+}
+
+export const Picker = ({
+ variant,
+ items,
+ ariaLabel,
+ pickerLabel,
+ dataTestId,
+ defaultText,
+ onSelect,
+ buttonBorder,
+ alignment,
+ renderItem,
+}: Props) => {
const [open, setOpen] = useState(false)
const { getDetailsProps } = useDetails({ closeOnOutsideClick: true })
- const selectedOption = options.find((opt) => opt.selected === true)
+ const selectedOption = items.find((item) => item.selected === true)
- function getFields() {
- return (
-
- {options.map((option) => (
- {
- if (option.onselect) {
- if (apiVersion) {
- option.onselect(option.text)
- } else {
- option.onselect(option.locale)
- }
- }
- setOpen(!open)
- }}
- >
- {option.text}
- {option.external && }
- {option.info && }
- {option.arrow && }
-
- ))}
-
- )
- }
-
- function getInlinePicker() {
- return (
-
-
-
- {selectedOption?.text || defaultText}
-
-
-
-
-
-
-
- )
- }
-
- return (
-
- {variant === 'inline' ? (
- getInlinePicker()
- ) : (
-
-
- {`${apiVersion ? `Version: ` : ''}`}
-
- {selectedOption?.text || defaultText}
-
-
-
- {getFields()}
-
-
- )}
-
+ return variant === 'inline' ? (
+
+
+
+ {selectedOption?.text || defaultText}
+
+
+
+
+
+
+
+ ) : (
+
+
+ {pickerLabel && {`${pickerLabel}: `}}
+ {selectedOption?.text || defaultText}
+
+
+
+
+
)
}
diff --git a/components/ui/Picker/index.ts b/components/ui/Picker/index.ts
index 913dac72d0..9e2d9f9363 100644
--- a/components/ui/Picker/index.ts
+++ b/components/ui/Picker/index.ts
@@ -1 +1,2 @@
export { Picker } from './Picker'
+export { Fields } from './Fields'
diff --git a/package-lock.json b/package-lock.json
index ed05bc9df9..7253e3536d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"dependencies": {
"@elastic/elasticsearch": "7.11.0",
"@github/failbot": "0.8.0",
+ "@primer/behaviors": "^1.3.1",
"@primer/css": "^20.2.4",
"@primer/octicons": "17.7.0",
"@primer/octicons-react": "17.7.0",
@@ -4051,9 +4052,9 @@
}
},
"node_modules/@primer/behaviors": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@primer/behaviors/-/behaviors-1.1.3.tgz",
- "integrity": "sha512-WpCcjAkXG7Lv3ZbaCUgASWKHnCi/pmuSEiyTmHHb6f5xhwk1mliixNL5ZZHtDN6RCcT3VnXUsyek4GopG2lbZQ=="
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@primer/behaviors/-/behaviors-1.3.1.tgz",
+ "integrity": "sha512-aMRDUQ350lk0FxtL5gJWPFHHOSSzDbJ6uNJVIt8XSqiGe1pxuW5mVVfrEp1uvzZ0pCHkCdm9fycjnfOeMeIrOQ=="
},
"node_modules/@primer/css": {
"version": "20.2.4",
@@ -23127,9 +23128,9 @@
}
},
"@primer/behaviors": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@primer/behaviors/-/behaviors-1.1.3.tgz",
- "integrity": "sha512-WpCcjAkXG7Lv3ZbaCUgASWKHnCi/pmuSEiyTmHHb6f5xhwk1mliixNL5ZZHtDN6RCcT3VnXUsyek4GopG2lbZQ=="
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@primer/behaviors/-/behaviors-1.3.1.tgz",
+ "integrity": "sha512-aMRDUQ350lk0FxtL5gJWPFHHOSSzDbJ6uNJVIt8XSqiGe1pxuW5mVVfrEp1uvzZ0pCHkCdm9fycjnfOeMeIrOQ=="
},
"@primer/css": {
"version": "20.2.4",
diff --git a/package.json b/package.json
index e271b7bd42..02ac6d5a98 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"@elastic/elasticsearch": "7.11.0",
"@github/failbot": "0.8.0",
+ "@primer/behaviors": "^1.3.1",
"@primer/css": "^20.2.4",
"@primer/octicons": "17.7.0",
"@primer/octicons-react": "17.7.0",
diff --git a/tests/rendering/header.js b/tests/rendering/header.js
index 0351a999fc..0546ca0a15 100644
--- a/tests/rendering/header.js
+++ b/tests/rendering/header.js
@@ -11,7 +11,7 @@ describe('header', () => {
const $ = await getDOM(
'/en/get-started/importing-your-projects-to-github/importing-source-code-to-github/about-github-importer'
)
- const getStarted = $('div ul ul li a[href="/en/get-started"]')
+ const getStarted = $('details div li a[href="/en/get-started"]')
expect(getStarted.length).toBe(1)
expect(getStarted.text().trim()).toBe('Get started')