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} - -
      -
      - -
        {getFields()}
      -
      -
      - ) - } - - 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')