diff --git a/.eslintrc.js b/.eslintrc.js
index 2fd1da435ef..a2b42dc5091 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -12,28 +12,17 @@ module.exports = {
'plugin:import/recommended',
'prettier',
],
+ settings: {
+ import: {
+ ignore: ['^theme$'],
+ },
+ },
rules: {
'react/prop-types': 'off',
+ // our theme use exports which dont work with import/no-unresolved
+ 'import/no-unresolved': ['error', {ignore: ['^theme$']}],
},
overrides: [
- // {
- // files: ['**/src/**/*.js'],
- // // env: {
- // // commonjs: false,
- // // browser: true,
- // // },
- // // parserOptions: {
- // // sourceType: 'module',
- // // },
- // },
- // {
- // files: ['**/gatsby-*.js'],
- // // env: {node: true},
- // },
- // {
- // files: ['**/test/*', '**/__tests__/*'],
- // env: {jest: true},
- // },
{
files: ['src/shared.js'],
rules: {
diff --git a/.gitignore b/.gitignore
index 4d2cd0e1288..76179b2041b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@
!/CONTRIBUTING.md
!/docs/
!/gatsby-*.js
+!/jest*.js
!/lib/
!/LICENSE*
!/map.js
@@ -33,7 +34,7 @@
!/release-please-config.json
!/scripts/
!/SECURITY.md
-!/src/
+!/src
!/static/
!/tap-snapshots/
!/test/
diff --git a/content/index.mdx b/content/index.mdx
index 746ef7ff540..74e66ee27b0 100644
--- a/content/index.mdx
+++ b/content/index.mdx
@@ -3,7 +3,7 @@ title: npm Documentation
edit_on_github: false
---
-import HeroLayout from 'theme/src/layout-hero'
+import HeroLayout from 'theme/layout/hero.js'
export default HeroLayout
diff --git a/theme/jest-preprocess.js b/jest-preprocess.js
similarity index 100%
rename from theme/jest-preprocess.js
rename to jest-preprocess.js
diff --git a/theme/jest-setup.js b/jest-setup.js
similarity index 100%
rename from theme/jest-setup.js
rename to jest-setup.js
diff --git a/theme/jest.config.js b/jest.config.js
similarity index 100%
rename from theme/jest.config.js
rename to jest.config.js
diff --git a/scripts/template-oss/index.js b/scripts/template-oss/index.js
index c396a5638da..c22d709269f 100644
--- a/scripts/template-oss/index.js
+++ b/scripts/template-oss/index.js
@@ -38,12 +38,13 @@ module.exports = {
devDependencies: [],
},
allowPaths: [
+ '/src',
'/.reuse/',
- '/src/',
'/static/',
'/content/',
'/LICENSE*',
'/gatsby-*.js',
+ '/jest*.js',
'/CODE_OF_CONDUCT.md',
'/CONTRIBUTING.md',
'/CONTENT-MODEL.md',
diff --git a/src/shared.js b/src/shared.js
index 65ee2c8e0cf..0f94370d755 100644
--- a/src/shared.js
+++ b/src/shared.js
@@ -1,7 +1,5 @@
import React from 'react'
-import {Link} from '@primer/react'
-import Screenshot from 'theme/src/mdx/screenshot'
-import Note from 'theme/src/mdx/note'
+import {Link, Note, Screenshot} from 'theme'
const shared = {
/* User login */
diff --git a/theme/.gitignore b/theme/.gitignore
index 0cb3fab190d..fcff975a1b0 100644
--- a/theme/.gitignore
+++ b/theme/.gitignore
@@ -12,7 +12,6 @@
!/CHANGELOG*
!/docs/
!/gatsby-*.js
-!/jest*.js
!/lib/
!/LICENSE*
!/map.js
diff --git a/theme/gatsby-config.js b/theme/gatsby-config.js
index ea5d97c15c2..03c891f85e4 100644
--- a/theme/gatsby-config.js
+++ b/theme/gatsby-config.js
@@ -53,7 +53,7 @@ module.exports = ({icon}) => ({
options: {
extensions: ['.mdx', '.md'],
defaultLayouts: {
- default: require.resolve('./src/layout-default.js'),
+ default: require.resolve('./src/layout/default.js'),
},
},
},
diff --git a/theme/package.json b/theme/package.json
index c5d05f56e75..108d5bad3a9 100644
--- a/theme/package.json
+++ b/theme/package.json
@@ -7,7 +7,10 @@
"type": "git"
},
"private": true,
- "main": "index.js",
+ "exports": {
+ ".": "./src/mdx/index.js",
+ "./layout/*.js": "./src/layout/*.js"
+ },
"license": "MIT",
"scripts": {
"test": "jest",
diff --git a/theme/scripts/template-oss/index.js b/theme/scripts/template-oss/index.js
index cfd63a1727a..cbbbcc00e13 100644
--- a/theme/scripts/template-oss/index.js
+++ b/theme/scripts/template-oss/index.js
@@ -1,4 +1,4 @@
module.exports = {
...require('../../../scripts/template-oss'),
- allowPaths: ['/src', '/gatsby-*.js', '/jest*.js'],
+ allowPaths: ['/src', '/gatsby-*.js'],
}
diff --git a/theme/src/components/contributors.js b/theme/src/components/contributors.js
index 7c190cf2e5f..93e243eef0b 100644
--- a/theme/src/components/contributors.js
+++ b/theme/src/components/contributors.js
@@ -1,6 +1,5 @@
-import {Avatar, Link, Text, Tooltip} from '@primer/react'
+import {Box, Avatar, Link, Text, Tooltip} from '@primer/react'
import React from 'react'
-import Flex from '../components/flex'
const pluralize = (word, count) => `${word}${count === 1 ? '' : 's'}`
@@ -23,7 +22,7 @@ const format = d => `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`
function Contributors({logins, latestCommit}) {
return (
-
+
{logins.length} {pluralize('contributor', logins.length)}
@@ -35,7 +34,7 @@ function Contributors({logins, latestCommit}) {
))}
-
+
{latestCommit ? (
Last edited by {latestCommit.login} on{' '}
diff --git a/theme/src/components/flex.js b/theme/src/components/flex.js
deleted file mode 100644
index 810efd827e5..00000000000
--- a/theme/src/components/flex.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import React from 'react'
-import {Box} from '@primer/react'
-
-const Flex = props =>
-
-export default Flex
diff --git a/theme/src/components/header.js b/theme/src/components/header.js
index d6f96876665..82032bebae1 100644
--- a/theme/src/components/header.js
+++ b/theme/src/components/header.js
@@ -6,7 +6,6 @@ import MobileSearch from './mobile-search'
import NavDrawer from './nav-drawer'
import Search from './search'
import NpmLogo from './npm-logo'
-import Flex from './flex'
import useSearch from '../hooks/use-search'
import useSiteMetadata from '../hooks/use-site-metadata'
import headerNavItems from '../header-nav.yml'
@@ -33,14 +32,15 @@ function Header({location, repositoryUrl}) {
return (
-
-
+
{siteMetadata.title}
@@ -48,30 +48,30 @@ function Header({location, repositoryUrl}) {
-
-
+
+
-
+
-
-
-
+
+
+
)
}
function HeaderNavItems({items}) {
return (
-
+
{items.map((item, index) => (
{item.title}
))}
-
+
)
}
diff --git a/theme/src/components/mobile-search.js b/theme/src/components/mobile-search.js
index 420f0521f7c..2252f1fc157 100644
--- a/theme/src/components/mobile-search.js
+++ b/theme/src/components/mobile-search.js
@@ -3,7 +3,6 @@ import {Box} from '@primer/react'
import {XIcon, SearchIcon} from '@primer/octicons-react'
import {AnimatePresence, motion} from 'framer-motion'
import {FocusOn} from 'react-focus-on'
-import Flex from './flex'
import DarkButton from './dark-button'
import DarkTextInput from './dark-text-input'
import SearchResults from './search-results'
@@ -35,8 +34,8 @@ function MobileSearch({onDismiss, ...props}) {
zIndex={-1}
onClick={handleDismiss}
/>
-
-
+
+
-
-
+
{isOpen ? : null}
-
-
+
+
)
diff --git a/theme/src/components/nav-drawer.js b/theme/src/components/nav-drawer.js
index 715361a295b..0dac98cfb28 100644
--- a/theme/src/components/nav-drawer.js
+++ b/theme/src/components/nav-drawer.js
@@ -1,12 +1,11 @@
import React from 'react'
-import {Link} from '@primer/react'
+import {Box, Link} from '@primer/react'
import BorderBox from './border-box'
import {XIcon, ThreeBarsIcon} from '@primer/octicons-react'
import {Link as GatsbyLink} from 'gatsby'
import DarkButton from './dark-button'
import Drawer from './drawer'
import NavItems from './nav-items'
-import Flex from './flex'
import navItems from '../nav.yml'
import headerNavItems from '../header-nav.yml'
import useSiteMetadata from '../hooks/use-site-metadata'
@@ -37,35 +36,45 @@ function NavDrawer({location, repositoryUrl}) {
-
-
+
-
+
{siteMetadata.title}
-
+
{navItems.length > 0 ? (
-
+
-
+
) : null}
-
+
{headerNavItems.length > 0 ? (
-
+
-
+
) : null}
-
+
>
)
diff --git a/theme/src/components/nav-items.js b/theme/src/components/nav-items.js
index 29fd77a35b9..6d4a9c12b84 100644
--- a/theme/src/components/nav-items.js
+++ b/theme/src/components/nav-items.js
@@ -4,7 +4,6 @@ import {Box, StyledOcticon, Link, themeGet} from '@primer/react'
import {LinkExternalIcon} from '@primer/octicons-react'
import styled from 'styled-components'
import BorderBox from './border-box'
-import Flex from './flex'
import NavHierarchy from '../util/nav-hierarchy'
const getActiveProps = className => props => {
@@ -90,12 +89,12 @@ function topLevelItems(items, path) {
return (
-
+
{item.title}
{secondLevelItems(children, path)}
-
+
)
})}
@@ -109,7 +108,7 @@ function secondLevelItems(items, path) {
}
return (
-
+
{items.map(item => {
const children = NavHierarchy.isActiveUrl(path, item.url)
? NavHierarchy.getHierarchy(item, {path, hideVariants: true})
@@ -128,7 +127,7 @@ function secondLevelItems(items, path) {
)
})}
-
+
)
}
@@ -138,7 +137,7 @@ function thirdLevelItems(items) {
}
return (
-
+
{items.map(item => (
@@ -146,7 +145,7 @@ function thirdLevelItems(items) {
))}
-
+
)
}
@@ -159,10 +158,10 @@ function NavItems({location, repositoryUrl}) {
{topLevelItems(items, path)}
-
+
GitHub
-
+
>
diff --git a/theme/src/components/search-results.js b/theme/src/components/search-results.js
index 326e916fbed..901621f34c2 100644
--- a/theme/src/components/search-results.js
+++ b/theme/src/components/search-results.js
@@ -1,6 +1,5 @@
import React from 'react'
-import {Text} from '@primer/react'
-import Flex from './flex'
+import {Box, Text} from '@primer/react'
import useSiteMetadata from '../hooks/use-site-metadata'
import NavHierarchy from '../util/nav-hierarchy'
@@ -16,7 +15,8 @@ function SearchResults({results, getItemProps, highlightedIndex}) {
}
return results.map((item, index) => (
-
{item.title}
-
+
))
}
diff --git a/theme/src/components/sidebar.js b/theme/src/components/sidebar.js
index 85901eaf20f..041543e1cc8 100644
--- a/theme/src/components/sidebar.js
+++ b/theme/src/components/sidebar.js
@@ -1,6 +1,5 @@
import {Box} from '@primer/react'
import React from 'react'
-import Flex from './flex'
import {HEADER_HEIGHT} from './header'
import NavItems from './nav-items'
import BorderBox from './border-box'
@@ -18,9 +17,9 @@ function Sidebar({location, repositoryUrl}) {
role="navigation"
>
-
+
-
+
)
diff --git a/theme/src/components/variant-select.js b/theme/src/components/variant-select.js
index 9f305113449..2e58945980e 100644
--- a/theme/src/components/variant-select.js
+++ b/theme/src/components/variant-select.js
@@ -8,41 +8,27 @@ import NavHierarchy from '../util/nav-hierarchy'
// second folder acts as a variant. If you use
// then you'll get a selection for the different variants (v1.0, v2.0).
-function VariantSelect(props) {
+const VariantSelect = ({variantPages, path}) => {
const [open, setOpen] = React.useState(false)
- const path = NavHierarchy.getPath(props.location.pathname)
- const vp = NavHierarchy.getVariantAndPage(props.root, path)
-
- if (!vp) {
- return null
- }
-
- const variantPages = NavHierarchy.getVariantsForPage(props.root, vp.page)
- const items = []
- let selectedItem = variantPages[0]
-
- if (variantPages.length === 0) {
- return null
- }
-
- function anchorClickHandler(event, url) {
+ const anchorClickHandler = React.useCallback((event, url) => {
event.preventDefault()
window.location.href = `${url}?v=true`
- }
+ }, [])
- function onItemEnterKey(event, url) {
+ const onItemEnterKey = React.useCallback((event, url) => {
if (event.key === 'Enter') {
window.location.href = `${url}?v=true`
}
- }
+ }, [])
- for (const [index, match] of variantPages.entries()) {
+ let selectedItem = variantPages[0]
+ const items = variantPages.map((match, index) => {
let active = false
if (match.page.url === path) {
selectedItem = match
active = true
}
- items.push(
+ return (
onItemEnterKey(e, match.page.url)}
onClick={e => anchorClickHandler(e, match.page.url)}
@@ -51,9 +37,9 @@ function VariantSelect(props) {
active={active}
>
{match.variant.title}
- ,
+
)
- }
+ })
return (
<>
@@ -75,4 +61,16 @@ function VariantSelect(props) {
)
}
-export default VariantSelect
+const VariantSelectLocation = ({root, location}) => {
+ const path = NavHierarchy.getPath(location.pathname)
+ const vp = NavHierarchy.getVariantAndPage(root, path)
+ const variantPages = vp ? NavHierarchy.getVariantsForPage(root, vp.page) : []
+
+ if (!variantPages.length) {
+ return null
+ }
+
+ return
+}
+
+export default VariantSelectLocation
diff --git a/theme/src/hooks/use-scroll-size.js b/theme/src/hooks/use-scroll-size.js
new file mode 100644
index 00000000000..1c232ed1754
--- /dev/null
+++ b/theme/src/hooks/use-scroll-size.js
@@ -0,0 +1,37 @@
+import {createRef, useState, useEffect} from 'react'
+
+/**
+ * Resize the scroll handle to the size of the code contents, since the former has to be positioned absolutely.
+ */
+const useScrollSize = () => {
+ const scrollRef = createRef()
+ const paddingRef = createRef()
+ const [size, setSize] = useState({})
+
+ useEffect(() => {
+ const scrollNode = scrollRef.current
+ const paddingNode = paddingRef.current
+
+ if (!scrollNode || !paddingNode || typeof size.width !== 'undefined') {
+ return
+ }
+
+ const parent = scrollNode.parentElement
+ const button = paddingNode.firstChild
+
+ parent.style.position = 'relative'
+ const parentStyle = getComputedStyle(parent)
+ const paddingTop = parseInt(parentStyle.paddingTop, 10)
+ const paddingBottom = parseInt(parentStyle.paddingBottom, 10)
+ const paddingRight = parseInt(parentStyle.paddingRight, 10)
+
+ setSize({
+ height: parent.clientHeight - paddingTop - paddingBottom,
+ width: parent.scrollWidth - paddingRight + button.clientWidth,
+ })
+ }, [scrollRef, paddingRef, size])
+
+ return {scrollRef, paddingRef, size}
+}
+
+export default useScrollSize
diff --git a/theme/src/layout-hero.js b/theme/src/layout-hero.js
deleted file mode 100644
index ada637e4ba2..00000000000
--- a/theme/src/layout-hero.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react'
-import {Box} from '@primer/react'
-import Container from './components/container'
-import Head from './components/head'
-import Header from './components/header'
-import Hero from './components/hero'
-import Sidebar from './components/sidebar'
-import Flex from './components/flex'
-import * as Slugger from './hooks/use-slugger'
-
-function HeroLayout({children, pageContext, location}) {
- return (
-
-
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
- )
-}
-
-export default HeroLayout
diff --git a/theme/src/layout-default.js b/theme/src/layout/default.js
similarity index 72%
rename from theme/src/layout-default.js
rename to theme/src/layout/default.js
index 400fa264d50..d1a96cb34aa 100644
--- a/theme/src/layout-default.js
+++ b/theme/src/layout/default.js
@@ -1,30 +1,30 @@
import React from 'react'
import {Box, Heading, StyledOcticon, Text, Details} from '@primer/react'
import {ChevronDownIcon, ChevronRightIcon} from '@primer/octicons-react'
-import Head from './components/head'
-import Header, {HEADER_HEIGHT} from './components/header'
-import PageFooter from './components/page-footer'
-import Sidebar from './components/sidebar'
-import TableOfContents from './components/table-of-contents'
-import VariantSelect from './components/variant-select'
-import BorderBox from './components/border-box'
-import Flex from './components/flex'
-import * as Slugger from './hooks/use-slugger'
-import NavHierarchy from './util/nav-hierarchy'
+import Head from '../components/head'
+import Header, {HEADER_HEIGHT} from '../components/header'
+import PageFooter from '../components/page-footer'
+import Sidebar from '../components/sidebar'
+import TableOfContents from '../components/table-of-contents'
+import VariantSelect from '../components/variant-select'
+import BorderBox from '../components/border-box'
+import * as Slugger from '../hooks/use-slugger'
+import NavHierarchy from '../util/nav-hierarchy'
function Layout({children, pageContext, location}) {
- const {title, description} = pageContext.frontmatter
+ const {repositoryUrl, editUrl, contributors, frontmatter, tableOfContents} = pageContext
+ const {title, description} = frontmatter
const variantRoot = NavHierarchy.getVariantRoot(location.pathname)
return (
-
+
-
-
+
+
-
+
) : null}
- {pageContext.tableOfContents ? (
+ {tableOfContents ? (
Table of contents
-
+
) : null}
- {pageContext.tableOfContents ? (
+ {tableOfContents ? (
{({open}) => (
@@ -93,7 +93,7 @@ function Layout({children, pageContext, location}) {
Table of contents
-
+
>
)}
@@ -101,11 +101,11 @@ function Layout({children, pageContext, location}) {
) : null}
{children}
-
+
-
-
+
+
)
}
diff --git a/theme/src/layout/hero.js b/theme/src/layout/hero.js
new file mode 100644
index 00000000000..f5e99dd700b
--- /dev/null
+++ b/theme/src/layout/hero.js
@@ -0,0 +1,28 @@
+import React from 'react'
+import {Box} from '@primer/react'
+import Container from '../components/container'
+import Head from '../components/head'
+import Header from '../components/header'
+import Hero from '../components/hero'
+import Sidebar from '../components/sidebar'
+import * as Slugger from '../hooks/use-slugger'
+
+const HeroLayout = ({children, location, pageContext: {repositoryUrl}}) => (
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+)
+
+export default HeroLayout
diff --git a/theme/src/mdx/blockquote.js b/theme/src/mdx/blockquote.js
deleted file mode 100644
index 11c6f32768d..00000000000
--- a/theme/src/mdx/blockquote.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import styled from 'styled-components'
-import {themeGet} from '@primer/react'
-
-const Blockquote = styled.blockquote`
- margin: 0 0 ${themeGet('space.3')};
- padding: 0 ${themeGet('space.3')};
- color: ${themeGet('colors.gray.5')};
- border-left: 0.25em solid ${themeGet('colors.gray.2')};
-
- > :first-child {
- margin-top: 0;
- }
-
- > :last-child {
- margin-bottom: 0;
- }
-`
-
-export default Blockquote
diff --git a/theme/src/mdx/code.js b/theme/src/mdx/code.js
index 303ab5926cc..5978e12b65d 100644
--- a/theme/src/mdx/code.js
+++ b/theme/src/mdx/code.js
@@ -1,46 +1,11 @@
+import React from 'react'
import {Box, Text} from '@primer/react'
import Highlight, {defaultProps} from 'prism-react-renderer'
import githubTheme from 'prism-react-renderer/themes/github'
-import React, {useState, useEffect} from 'react'
import ClipboardCopy from '../components/clipboard-copy'
-import BorderBox from '../components/border-box'
+import useScrollSize from '../hooks/use-scroll-size'
-/**
- * Resize the scroll handle to the size of the code contents, since the former has to be positioned absolutely.
- */
-const useScrollSize = () => {
- const scrollRef = React.createRef()
- const paddingRef = React.createRef()
- const [size, setSize] = useState({})
-
- useEffect(() => {
- const scrollNode = scrollRef.current
- const paddingNode = paddingRef.current
-
- if (!scrollNode || !paddingNode || typeof size.width !== 'undefined') {
- return
- }
-
- const parent = scrollNode.parentElement
- const button = paddingNode.firstChild
-
- parent.style.position = 'relative'
- const parentStyle = getComputedStyle(parent)
- const paddingTop = parseInt(parentStyle.paddingTop, 10)
- const paddingBottom = parseInt(parentStyle.paddingBottom, 10)
- const paddingRight = parseInt(parentStyle.paddingRight, 10)
-
- setSize({
- height: parent.clientHeight - paddingTop - paddingBottom,
- width: parent.scrollWidth - paddingRight + button.clientWidth,
- })
- }, [scrollRef, paddingRef, size])
-
- return {scrollRef, paddingRef, size}
-}
-
-function Code({className: parentClass, children}) {
- const language = parentClass ? parentClass.replace(/language-/, '') : ''
+function Code({className: language = '', children}) {
const code = children.trim()
const {scrollRef, paddingRef, size} = useScrollSize()
@@ -51,9 +16,22 @@ function Code({className: parentClass, children}) {
-
+
{({className, style, tokens, getLineProps, getTokenProps}) => (
-
+
{/* This is the scroll handle, it is supposed to be focused with keyboard and scroll a wide codebox horizontally */}
{tokens.map((line, i) => (
@@ -63,7 +41,7 @@ function Code({className: parentClass, children}) {
))}
))}
-
+
)}
diff --git a/theme/src/mdx/description-list.js b/theme/src/mdx/description-list.js
deleted file mode 100644
index 6709271dbf6..00000000000
--- a/theme/src/mdx/description-list.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import styled from 'styled-components'
-import {themeGet} from '@primer/react'
-
-const DescriptionList = styled.dl`
- padding: 0;
-
- dt {
- padding: 0;
- margin-top: ${themeGet('space.3')};
- font-size: 1em;
- font-style: italic;
- font-weight: ${themeGet('fontWeights.bold')};
- }
-
- dd {
- padding: 0 ${themeGet('space.3')};
- margin: 0 0 ${themeGet('space.3')};
- }
-`
-
-export default DescriptionList
diff --git a/theme/src/mdx/heading.js b/theme/src/mdx/heading.js
deleted file mode 100644
index e27a5426f70..00000000000
--- a/theme/src/mdx/heading.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import {Heading, Link, themeGet} from '@primer/react'
-import {LinkIcon} from '@primer/octicons-react'
-import React from 'react'
-import textContent from 'react-addons-text-content'
-import styled from 'styled-components'
-import {HEADER_HEIGHT} from '../components/header'
-import {useSlugger} from '../hooks/use-slugger'
-
-const StyledHeading = styled(Heading)`
- margin-top: ${themeGet('space.4')};
- margin-bottom: ${themeGet('space.3')};
- scroll-margin-top: ${HEADER_HEIGHT + 24}px;
-
- & .octicon-link {
- visibility: hidden;
- }
-
- &:hover .octicon-link,
- &:focus-within .octicon-link {
- visibility: visible;
- }
-`
-
-function MarkdownHeading({children, ...props}) {
- const slugger = useSlugger()
- const text = children ? textContent(children) : ''
- const id = text ? slugger.slug(text) : ''
-
- return (
-
-
-
-
- {children}
-
- )
-}
-
-const StyledH1 = styled(StyledHeading).attrs({as: 'h1'})`
- padding-bottom: ${themeGet('space.1')};
- font-size: ${themeGet('fontSizes.5')};
- border-bottom: 1px solid ${themeGet('colors.gray.2')};
-`
-
-const StyledH2 = styled(StyledHeading).attrs({as: 'h2'})`
- padding-bottom: ${themeGet('space.1')};
- font-size: ${themeGet('fontSizes.4')};
- border-bottom: 1px solid ${themeGet('colors.gray.2')};
-`
-
-const StyledH3 = styled(StyledHeading).attrs({as: 'h3'})`
- font-size: ${themeGet('fontSizes.3')};
-`
-
-const StyledH4 = styled(StyledHeading).attrs({as: 'h4'})`
- font-size: ${themeGet('fontSizes.2')};
-`
-
-const StyledH5 = styled(StyledHeading).attrs({as: 'h5'})`
- font-size: ${themeGet('fontSizes.1')};
-`
-
-const StyledH6 = styled(StyledHeading).attrs({as: 'h6'})`
- font-size: ${themeGet('fontSizes.1')};
- color: ${themeGet('colors.gray.5')};
-`
-
-export const H1 = props =>
-export const H2 = props =>
-export const H3 = props =>
-export const H4 = props =>
-export const H5 = props =>
-export const H6 = props =>
diff --git a/theme/src/mdx/horizontal-rule.js b/theme/src/mdx/horizontal-rule.js
deleted file mode 100644
index 58892f12898..00000000000
--- a/theme/src/mdx/horizontal-rule.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import styled from 'styled-components'
-import {themeGet} from '@primer/react'
-
-const HorizontalRule = styled.hr`
- height: ${themeGet('space.1')};
- padding: 0;
- margin: ${themeGet('space.4')} 0;
- background-color: ${themeGet('colors.gray.2')};
- border: 0;
-`
-
-export default HorizontalRule
diff --git a/theme/src/mdx/image.js b/theme/src/mdx/image.js
deleted file mode 100644
index 54f9948946c..00000000000
--- a/theme/src/mdx/image.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import {themeGet} from '@primer/react'
-import styled from 'styled-components'
-
-const Image = styled.img`
- max-width: 100%;
- box-sizing: content-box;
- background-color: ${themeGet('colors.white')};
-`
-
-export default Image
diff --git a/theme/src/mdx/index.js b/theme/src/mdx/index.js
index a012796a2ea..eaf98a08908 100644
--- a/theme/src/mdx/index.js
+++ b/theme/src/mdx/index.js
@@ -1,50 +1,286 @@
import React from 'react'
-import {useLocation} from '@reach/router' // eslint-disable-line import/no-unresolved
-import {Box, Link} from '@primer/react'
-import {Link as GatsbyLink} from 'gatsby'
-import NavHierarchy from '../util/nav-hierarchy'
-
-function showHierarchy(items, props, depth = 1) {
- let hierarchy
-
- if (props.depth && depth > props.depth) {
- return null
- }
-
- return (
-
- {items.map(item => (
-
-
- {item.title}
-
- {item.description != null ? (
- <>
- {item.description}
- >
- ) : null}
- {(hierarchy = NavHierarchy.getHierarchy(item, props)) != null
- ? showHierarchy(hierarchy, props, depth + 1)
- : null}
-
- ))}
-
- )
+import {Box, Heading, themeGet, Text, Link as PrimerLink} from '@primer/react'
+import styled from 'styled-components'
+import {withPrefix} from 'gatsby'
+import {LinkIcon} from '@primer/octicons-react'
+import textContent from 'react-addons-text-content'
+import Code from './code'
+import NavHierarchy from './nav-hierarchy'
+import {HEADER_HEIGHT} from '../components/header'
+import {useSlugger} from '../hooks/use-slugger'
+
+const required = (prop, name) => {
+ if (!prop) {
+ throw new Error(`${name} prop is required`)
+ }
+ return prop
+}
+
+export const Link = props => {
+ return
}
-function Index(props) {
- const location = useLocation()
- const path = NavHierarchy.getLocation(location.pathname)
- const root = (props.root ? props.root : path).replace(/\/+$/g, '')
+export {Code, NavHierarchy as Index}
+
+export const Pre = ({children}) => children
+
+const SkipLinkBase = props => (
+
+ Skip to content
+
+)
+
+export const SkipLink = styled(SkipLinkBase)`
+ z-index: 20;
+ width: auto;
+ height: auto;
+ clip: auto;
+ position: absolute;
+ overflow: hidden;
- const rootItem = NavHierarchy.getItem(root)
- const hierarchy = NavHierarchy.getHierarchy(rootItem, props)
+ // The following rules are to ensure that the element
+ // is visually hidden, unless it has focus. This is the recommended
+ // way to hide content from:
+ // https://webaim.org/techniques/css/invisiblecontent/#techniques
- if (!hierarchy) {
- throw new Error(`could not find entry for ${root}`)
+ &:not(:focus) {
+ clip: rect(1px, 1px, 1px, 1px);
+ clip-path: inset(50%);
+ height: 1px;
+ width: 1px;
+ margin: -1px;
+ padding: 0;
}
+`
+
+const StyledHeading = styled(Heading)`
+ margin-top: ${themeGet('space.4')};
+ margin-bottom: ${themeGet('space.3')};
+ scroll-margin-top: ${HEADER_HEIGHT + 24}px;
+
+ & .octicon-link {
+ visibility: hidden;
+ }
+
+ &:hover .octicon-link,
+ &:focus-within .octicon-link {
+ visibility: visible;
+ }
+`
+
+const Headings = {
+ Markdown: ({children, ...props}) => {
+ const slugger = useSlugger()
+ const text = children ? textContent(children) : ''
+ const id = text ? slugger.slug(text) : ''
- return showHierarchy(hierarchy, props)
+ return (
+
+
+
+
+ {children}
+
+ )
+ },
+ h1: styled(StyledHeading).attrs({as: 'h1'})`
+ padding-bottom: ${themeGet('space.1')};
+ font-size: ${themeGet('fontSizes.5')};
+ border-bottom: 1px solid ${themeGet('colors.gray.2')};
+ `,
+ h2: styled(StyledHeading).attrs({as: 'h2'})`
+ padding-bottom: ${themeGet('space.1')};
+ font-size: ${themeGet('fontSizes.4')};
+ border-bottom: 1px solid ${themeGet('colors.gray.2')};
+ `,
+ h3: styled(StyledHeading).attrs({as: 'h3'})`
+ font-size: ${themeGet('fontSizes.3')};
+ `,
+ h4: styled(StyledHeading).attrs({as: 'h4'})`
+ font-size: ${themeGet('fontSizes.2')};
+ `,
+ h5: styled(StyledHeading).attrs({as: 'h5'})`
+ font-size: ${themeGet('fontSizes.1')};
+ `,
+ h6: styled(StyledHeading).attrs({as: 'h6'})`
+ font-size: ${themeGet('fontSizes.1')};
+ color: ${themeGet('colors.gray.5')};
+ `,
+ wrap(as) {
+ return props =>
+ },
}
-export default Index
+export const H1 = Headings.wrap('h1')
+export const H2 = Headings.wrap('h2')
+export const H3 = Headings.wrap('h3')
+export const H4 = Headings.wrap('h4')
+export const H5 = Headings.wrap('h5')
+export const H6 = Headings.wrap('h6')
+
+export const Blockquote = styled.blockquote`
+ margin: 0 0 ${themeGet('space.3')};
+ padding: 0 ${themeGet('space.3')};
+ color: ${themeGet('colors.gray.5')};
+ border-left: 0.25em solid ${themeGet('colors.gray.2')};
+
+ > :first-child {
+ margin-top: 0;
+ }
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+`
+
+export const DescriptionList = styled.dl`
+ padding: 0;
+
+ dt {
+ padding: 0;
+ margin-top: ${themeGet('space.3')};
+ font-size: 1em;
+ font-style: italic;
+ font-weight: ${themeGet('fontWeights.bold')};
+ }
+
+ dd {
+ padding: 0 ${themeGet('space.3')};
+ margin: 0 0 ${themeGet('space.3')};
+ }
+`
+
+export const HorizontalRule = styled.hr`
+ height: ${themeGet('space.1')};
+ padding: 0;
+ margin: ${themeGet('space.4')} 0;
+ background-color: ${themeGet('colors.gray.2')};
+ border: 0;
+`
+
+export const Image = styled.img`
+ max-width: 100%;
+ box-sizing: content-box;
+ background-color: ${themeGet('colors.white')};
+`
+
+export const InlineCode = styled.code`
+ padding: 0.2em 0.4em;
+ font-family: ${themeGet('fonts.mono')};
+ font-size: 85%;
+ background-color: ${themeGet('colors.gray.1')};
+ border-radius: ${themeGet('radii.1')};
+`
+
+export const UnorderedList = styled.ul`
+ padding-left: 2em;
+
+ ul,
+ ol {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+
+ li {
+ word-wrap: break-all;
+ }
+
+ li > p {
+ margin-top: ${themeGet('space.3')};
+ }
+
+ li + li {
+ margin-top: ${themeGet('space.1')};
+ }
+`
+
+export const OrderedList = UnorderedList.withComponent('ol')
+
+export const Paragraph = styled.p`
+ margin: 0 0 ${themeGet('space.3')};
+`
+
+export const Table = styled.table`
+ display: block;
+ width: 100%;
+ margin: 0 0 ${themeGet('space.3')};
+ overflow: auto;
+
+ th {
+ font-weight: ${themeGet('fontWeights.bold')};
+ }
+
+ th,
+ td {
+ padding: ${themeGet('space.2')} ${themeGet('space.3')};
+ border: 1px solid ${themeGet('colors.gray.2')};
+ }
+
+ tr {
+ background-color: ${themeGet('colors.white')};
+ border-top: 1px solid ${themeGet('colors.gray.2')};
+
+ &:nth-child(2n) {
+ background-color: ${themeGet('colors.gray.1')};
+ }
+ }
+
+ img {
+ background-color: transparent;
+ }
+`
+
+export const Note = ({children}) => (
+
+ {React.Children.toArray(children).map((child, index, list) =>
+ React.cloneElement(child, {
+ style: index === list.length - 1 ? {marginBottom: '0'} : null,
+ }),
+ )}
+
+)
+
+export const Prompt = ({children}) => (
+
+
+ {children}
+
+
+)
+
+export const PromptReply = ({children}) => {children}
+
+export const Screenshot = props => (
+
+
![{required(props.alt,]({withPrefix(required(props.src,)
+
+)
diff --git a/theme/src/mdx/inline-code.js b/theme/src/mdx/inline-code.js
deleted file mode 100644
index 02690e2e759..00000000000
--- a/theme/src/mdx/inline-code.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import styled from 'styled-components'
-import {themeGet} from '@primer/react'
-
-const InlineCode = styled.code`
- padding: 0.2em 0.4em;
- font-family: ${themeGet('fonts.mono')};
- font-size: 85%;
- background-color: ${themeGet('colors.gray.1')};
- border-radius: ${themeGet('radii.1')};
-`
-
-export default InlineCode
diff --git a/theme/src/mdx/list.js b/theme/src/mdx/list.js
deleted file mode 100644
index 20cc2c0e0da..00000000000
--- a/theme/src/mdx/list.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import styled from 'styled-components'
-import {themeGet} from '@primer/react'
-
-const List = styled.ul`
- padding-left: 2em;
-
- ul,
- ol {
- margin-top: 0;
- margin-bottom: 0;
- }
-
- li {
- word-wrap: break-all;
- }
-
- li > p {
- margin-top: ${themeGet('space.3')};
- }
-
- li + li {
- margin-top: ${themeGet('space.1')};
- }
-`
-
-export default List
diff --git a/theme/src/mdx/nav-hierarchy.js b/theme/src/mdx/nav-hierarchy.js
new file mode 100644
index 00000000000..a21c06d7816
--- /dev/null
+++ b/theme/src/mdx/nav-hierarchy.js
@@ -0,0 +1,48 @@
+import React from 'react'
+import {useLocation} from '@reach/router' // eslint-disable-line import/no-unresolved
+import {Box, Link} from '@primer/react'
+import {Link as GatsbyLink} from 'gatsby'
+import NavHierarchy from '../util/nav-hierarchy'
+
+function showHierarchy(items, props, depth = 1) {
+ let hierarchy
+
+ if (props.depth && depth > props.depth) {
+ return null
+ }
+
+ return (
+
+ {items.map(item => (
+
+
+ {item.title}
+
+ {item.description != null ? (
+ {item.description}
+ ) : null}
+ {(hierarchy = NavHierarchy.getHierarchy(item, props)) != null
+ ? showHierarchy(hierarchy, props, depth + 1)
+ : null}
+
+ ))}
+
+ )
+}
+
+function Index(props) {
+ const location = useLocation()
+ const path = NavHierarchy.getLocation(location.pathname)
+ const root = (props.root ? props.root : path).replace(/\/+$/g, '')
+
+ const rootItem = NavHierarchy.getItem(root)
+ const hierarchy = NavHierarchy.getHierarchy(rootItem, props)
+
+ if (!hierarchy) {
+ throw new Error(`could not find entry for ${root}`)
+ }
+
+ return showHierarchy(hierarchy, props)
+}
+
+export default Index
diff --git a/theme/src/mdx/note.js b/theme/src/mdx/note.js
deleted file mode 100644
index e260be3495a..00000000000
--- a/theme/src/mdx/note.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react'
-import BorderBox from '../components/border-box'
-
-function Note({children}) {
- return (
-
- {React.Children.toArray(children).map((child, index, list) =>
- React.cloneElement(child, {
- style: index === list.length - 1 ? {marginBottom: '0'} : null,
- }),
- )}
-
- )
-}
-
-export default Note
diff --git a/theme/src/mdx/paragraph.js b/theme/src/mdx/paragraph.js
deleted file mode 100644
index d561ed2c398..00000000000
--- a/theme/src/mdx/paragraph.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import styled from 'styled-components'
-import {themeGet} from '@primer/react'
-
-const Paragraph = styled.p`
- margin: 0 0 ${themeGet('space.3')};
-`
-
-export default Paragraph
diff --git a/theme/src/mdx/prompt-reply.js b/theme/src/mdx/prompt-reply.js
deleted file mode 100644
index 732caa0368d..00000000000
--- a/theme/src/mdx/prompt-reply.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from 'react'
-
-function PromptReply({children}) {
- return {children}
-}
-
-export default PromptReply
diff --git a/theme/src/mdx/prompt.js b/theme/src/mdx/prompt.js
deleted file mode 100644
index efcb5247a50..00000000000
--- a/theme/src/mdx/prompt.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react'
-import {Text} from '@primer/react'
-import BorderBox from '../components/border-box'
-
-function Prompt({children}) {
- return (
-
-
- {children}
-
-
- )
-}
-
-export default Prompt
diff --git a/theme/src/mdx/screenshot.js b/theme/src/mdx/screenshot.js
deleted file mode 100644
index 24ceda8f6cc..00000000000
--- a/theme/src/mdx/screenshot.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react'
-import {withPrefix} from 'gatsby'
-
-function Screenshot(props) {
- if (!props.src) {
- throw new Error('src is required')
- }
-
- if (!props.alt) {
- throw new Error('alt text is required')
- }
-
- return (
-
-
![{props.alt}]({withPrefix(props.src)})
-
- )
-}
-
-export default Screenshot
diff --git a/theme/src/mdx/table.js b/theme/src/mdx/table.js
deleted file mode 100644
index 99c4dfb68d4..00000000000
--- a/theme/src/mdx/table.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import styled from 'styled-components'
-import {themeGet} from '@primer/react'
-
-const Table = styled.table`
- display: block;
- width: 100%;
- margin: 0 0 ${themeGet('space.3')};
- overflow: auto;
-
- th {
- font-weight: ${themeGet('fontWeights.bold')};
- }
-
- th,
- td {
- padding: ${themeGet('space.2')} ${themeGet('space.3')};
- border: 1px solid ${themeGet('colors.gray.2')};
- }
-
- tr {
- background-color: ${themeGet('colors.white')};
- border-top: 1px solid ${themeGet('colors.gray.2')};
-
- &:nth-child(2n) {
- background-color: ${themeGet('colors.gray.1')};
- }
- }
-
- img {
- background-color: transparent;
- }
-`
-
-export default Table
diff --git a/theme/src/page-element.js b/theme/src/page-element.js
index 35f063765af..358952ddec0 100644
--- a/theme/src/page-element.js
+++ b/theme/src/page-element.js
@@ -1,6 +1,7 @@
-import {BaseStyles, Link} from '@primer/react'
import React from 'react'
-import styled, {createGlobalStyle} from 'styled-components'
+import {BaseStyles} from '@primer/react'
+import {createGlobalStyle} from 'styled-components'
+import {SkipLink} from './mdx'
const GlobalStyle = createGlobalStyle`
::placeholder {
@@ -8,43 +9,12 @@ const GlobalStyle = createGlobalStyle`
}
`
-const SkipLinkBase = props => (
-
- Skip to content
-
+const PageElement = ({element}) => (
+
+
+
+ {element}
+
)
-const SkipLink = styled(SkipLinkBase)`
- z-index: 20;
- width: auto;
- height: auto;
- clip: auto;
- position: absolute;
- overflow: hidden;
-
- // The following rules are to ensure that the element
- // is visually hidden, unless it has focus. This is the recommended
- // way to hide content from:
- // https://webaim.org/techniques/css/invisiblecontent/#techniques
-
- &:not(:focus) {
- clip: rect(1px, 1px, 1px, 1px);
- clip-path: inset(50%);
- height: 1px;
- width: 1px;
- margin: -1px;
- padding: 0;
- }
-`
-
-function PageElement({element}) {
- return (
-
-
-
- {element}
-
- )
-}
-
export default PageElement
diff --git a/theme/src/root-element.js b/theme/src/root-element.js
index 05212773487..dc81ab31b3e 100644
--- a/theme/src/root-element.js
+++ b/theme/src/root-element.js
@@ -1,63 +1,41 @@
import React from 'react'
import {MDXProvider} from '@mdx-js/react'
-import {Link, SSRProvider, ThemeProvider, theme} from '@primer/react'
-import Blockquote from './mdx/blockquote'
-import Code from './mdx/code'
-import DescriptionList from './mdx/description-list'
-import {H1, H2, H3, H4, H5, H6} from './mdx/heading'
-import HorizontalRule from './mdx/horizontal-rule'
-import Image from './mdx/image'
-import InlineCode from './mdx/inline-code'
-import List from './mdx/list'
-import Paragraph from './mdx/paragraph'
-import Table from './mdx/table'
-import Index from './mdx/index'
-import Note from './mdx/note'
-import Prompt from './mdx/prompt'
-import PromptReply from './mdx/prompt-reply'
-import Screenshot from './mdx/screenshot'
-
-function UnderlinedLink(props) {
- return
-}
-
-console.log(theme)
+import {SSRProvider, ThemeProvider} from '@primer/react'
+import * as Components from './mdx'
const components = {
- a: UnderlinedLink,
- pre: props => props.children,
- code: Code,
- inlineCode: InlineCode,
- table: Table,
- img: Image,
- p: Paragraph,
- hr: HorizontalRule,
- blockquote: Blockquote,
- h1: H1,
- h2: H2,
- h3: H3,
- h4: H4,
- h5: H5,
- h6: H6,
- ul: List,
- ol: List.withComponent('ol'),
- dl: DescriptionList,
- Index,
- Note,
- Prompt,
- PromptReply,
- Screenshot,
- Link: UnderlinedLink,
+ a: Components.Link,
+ pre: Components.Pre,
+ code: Components.Code,
+ inlineCode: Components.InlineCode,
+ table: Components.Table,
+ img: Components.Image,
+ p: Components.Paragraph,
+ hr: Components.HorizontalRule,
+ blockquote: Components.Blockquote,
+ h1: Components.H1,
+ h2: Components.H2,
+ h3: Components.H3,
+ h4: Components.H4,
+ h5: Components.H5,
+ h6: Components.H6,
+ ul: Components.UnorderedList,
+ ol: Components.OrderedList,
+ dl: Components.DescriptionList,
+ Index: Components.Index,
+ Note: Components.Note,
+ Prompt: Components.Prompt,
+ PromptReply: Components.PromptReply,
+ Screenshot: Components.Screenshot,
+ Link: Components.Link,
}
-function RootElement({element}) {
- return (
-
-
- {element}
-
-
- )
-}
+const RootElement = ({element}) => (
+
+
+ {element}
+
+
+)
export default RootElement