@primer/doctocat-nextjs 0.0.2-rc.1da8f9a → 0.0.2-rc.60745e5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,4 +6,12 @@
6
6
 
7
7
  - [#4](https://github.com/primer/doctocat-nextjs/pull/4) [`4f28982`](https://github.com/primer/doctocat-nextjs/commit/4f28982e327e75f199f28fad987f1e827deafeb2) Thanks [@joseph-lozano](https://github.com/joseph-lozano)! - Wrap links with Next's Link component
8
8
 
9
+ - [`6f21970`](https://github.com/primer/doctocat-nextjs/commit/6f21970c74f7635be89fc4cd20376d7fe5ca35e7) Thanks [@rezrah](https://github.com/rezrah)! - Switch hero image order with description and reduce `h2` block start margin
10
+
11
+ - [#6](https://github.com/primer/doctocat-nextjs/pull/6) [`afd4e17`](https://github.com/primer/doctocat-nextjs/commit/afd4e1762f6294a14942d415c693319a874cd3fb) Thanks [@rezrah](https://github.com/rezrah)! - - Add MDX to HTML overrides mechanism and apply to headings and anchors
12
+
13
+ - Added anchor links to headings to match current functionality on primer.style
14
+
15
+ - [`b49f218`](https://github.com/primer/doctocat-nextjs/commit/b49f218e9bbc2de720476e21888956bee6081967) Thanks [@rezrah](https://github.com/rezrah)! - Removed sidebar links and added skip to main content a11y link
16
+
9
17
  - [#2](https://github.com/primer/doctocat-nextjs/pull/2) [`2742b32`](https://github.com/primer/doctocat-nextjs/commit/2742b3214e7a53416d23f0459dc389f7c22cf5a1) Thanks [@rezrah](https://github.com/rezrah)! - Remove code blocks stylesheet, now merged into global.css
@@ -4,7 +4,7 @@ import {Heading, Stack, Text} from '@primer/react-brand'
4
4
  import clsx from 'clsx'
5
5
  import {MdxFile, PageMapItem} from 'nextra'
6
6
  import type {PageItem} from 'nextra/normalize-pages'
7
- import React, {useEffect, useMemo} from 'react'
7
+ import React, {useCallback, useEffect, useMemo} from 'react'
8
8
  import {debounce} from 'lodash'
9
9
 
10
10
  import Link from 'next/link'
@@ -37,6 +37,12 @@ export function Header({pageMap, docsDirectories, siteTitle}: HeaderProps) {
37
37
  const [searchTerm, setSearchTerm] = React.useState<string | undefined>('')
38
38
  const [activeDescendant] = React.useState<number>(-1)
39
39
 
40
+ useEffect(() => {
41
+ if (isSearchOpen && inputRef.current) {
42
+ inputRef.current.focus()
43
+ }
44
+ }, [isSearchOpen])
45
+
40
46
  useEffect(() => {
41
47
  const handleKeyDown = (event: KeyboardEvent) => {
42
48
  if (event.key === 'Escape') {
@@ -152,6 +158,10 @@ export function Header({pageMap, docsDirectories, siteTitle}: HeaderProps) {
152
158
  alert(`Name: ${inputRef.current.value}`)
153
159
  }
154
160
 
161
+ const handleSearchButtonOpenClick = useCallback(() => {
162
+ setIsSearchOpen(true)
163
+ }, [])
164
+
155
165
  return (
156
166
  <nav className={clsx(styles.Header, isSearchOpen && styles['Header--searchAreaOpen'])}>
157
167
  <Link href="/" className={styles.Header__siteTitle}>
@@ -292,7 +302,7 @@ export function Header({pageMap, docsDirectories, siteTitle}: HeaderProps) {
292
302
  variant="invisible"
293
303
  aria-label={`Open search`}
294
304
  sx={{display: ['flex', null, 'none']}}
295
- onClick={() => setIsSearchOpen(true)}
305
+ onClick={handleSearchButtonOpenClick}
296
306
  />
297
307
  <Box sx={{display: ['flex', null, 'none']}}>
298
308
  <IconButton
@@ -2,9 +2,11 @@ import React, {useMemo} from 'react'
2
2
  import NextLink from 'next/link'
3
3
  import Head from 'next/head'
4
4
  import type {Folder, MdxFile, NextraThemeLayoutProps} from 'nextra'
5
+ import {MDXProvider} from 'nextra/mdx'
5
6
  import {useFSRoute} from 'nextra/hooks'
6
7
  import {PencilIcon} from '@primer/octicons-react'
7
8
  import {BaseStyles, Box as PRCBox, Breadcrumbs, PageLayout, ThemeProvider} from '@primer/react'
9
+
8
10
  import {
9
11
  Animate,
10
12
  AnimationProvider,
@@ -32,6 +34,8 @@ import {TableOfContents} from '../table-of-contents/TableOfContents'
32
34
  import bodyStyles from '../../../css/prose.module.css'
33
35
  import {IndexCards} from '../index-cards/IndexCards'
34
36
  import {useColorMode} from '../../context/color-modes/useColorMode'
37
+ import {getComponents} from '../../mdx-components/mdx-components'
38
+ import {SkipToMainContent} from '../skip-to-main-content/SkipToMainContent'
35
39
 
36
40
  const {publicRuntimeConfig} = getConfig()
37
41
 
@@ -78,6 +82,7 @@ export function Theme({children, pageOpts}: NextraThemeLayoutProps) {
78
82
  zIndex: 99,
79
83
  }}
80
84
  >
85
+ <SkipToMainContent href="#main">Skip to main content</SkipToMainContent>
81
86
  <Header
82
87
  pageMap={pageMap}
83
88
  docsDirectories={docsDirectories}
@@ -87,7 +92,7 @@ export function Theme({children, pageOpts}: NextraThemeLayoutProps) {
87
92
  </PRCBox>
88
93
  <PageLayout rowGap="none" columnGap="none" padding="none" containerWidth="full">
89
94
  <PageLayout.Content padding="normal">
90
- <main>
95
+ <main id="main">
91
96
  <PRCBox sx={!isHomePage && {display: 'flex', maxWidth: 1600, margin: '0 auto'}}>
92
97
  <PRCBox sx={!isHomePage && {maxWidth: 800, width: '100%', margin: '0 auto'}}>
93
98
  <Stack direction="vertical" padding="none" gap="spacious">
@@ -132,16 +137,16 @@ export function Theme({children, pageOpts}: NextraThemeLayoutProps) {
132
137
  {frontMatter.title}
133
138
  </Heading>
134
139
  )}
135
- {frontMatter.image && (
136
- <Box paddingBlockEnd={16}>
137
- <Hero.Image src={frontMatter.image} alt={frontMatter['image-alt']} />
138
- </Box>
139
- )}
140
140
  {frontMatter.description && (
141
141
  <Text as="p" variant="muted" size="300">
142
142
  {frontMatter.description}
143
143
  </Text>
144
144
  )}
145
+ {frontMatter.image && (
146
+ <Box paddingBlockStart={16}>
147
+ <Hero.Image src={frontMatter.image} alt={frontMatter['image-alt']} />
148
+ </Box>
149
+ )}
145
150
  {frontMatter['action-1-text'] && (
146
151
  <Box paddingBlockStart={16}>
147
152
  <ButtonGroup>
@@ -162,7 +167,11 @@ export function Theme({children, pageOpts}: NextraThemeLayoutProps) {
162
167
  </>
163
168
  )}
164
169
  <article className={route !== '/' && !isIndexPage ? bodyStyles.Prose : ''}>
165
- {isIndexPage ? <IndexCards folderData={flatDocsDirectories} route={route} /> : children}
170
+ {isIndexPage ? (
171
+ <IndexCards folderData={flatDocsDirectories} route={route} />
172
+ ) : (
173
+ <MDXProvider components={getComponents()}>{children}</MDXProvider>
174
+ )}
166
175
  </article>
167
176
  <footer>
168
177
  <Box marginBlockStart={64}>
@@ -6,17 +6,7 @@ import {useRouter} from 'next/router'
6
6
  import getConfig from 'next/config'
7
7
 
8
8
  import styles from './Sidebar.module.css'
9
- import {
10
- BookmarkIcon,
11
- BrowserIcon,
12
- Icon,
13
- ImageIcon,
14
- LinkExternalIcon,
15
- OrganizationIcon,
16
- RepoIcon,
17
- StackIcon,
18
- StarIcon,
19
- } from '@primer/octicons-react'
9
+ import {LinkExternalIcon} from '@primer/octicons-react'
20
10
  import type {ThemeConfig} from '../../../index'
21
11
 
22
12
  type SidebarProps = {
@@ -36,29 +26,6 @@ type DocsItem = (MdxFile | FolderWithoutChildren) & {
36
26
 
37
27
  const {publicRuntimeConfig} = getConfig()
38
28
 
39
- function getOcticonForType(type?: string): Icon | undefined {
40
- if (!type) return undefined
41
-
42
- switch (type) {
43
- case 'repo':
44
- return RepoIcon
45
- case 'org':
46
- return OrganizationIcon
47
- case 'bookmark':
48
- return BookmarkIcon
49
- case 'star':
50
- return StarIcon
51
- case 'browser':
52
- return BrowserIcon
53
- case 'stack':
54
- return StackIcon
55
- case 'img':
56
- return ImageIcon
57
- default:
58
- return StarIcon
59
- }
60
- }
61
-
62
29
  export function Sidebar({pageMap}: SidebarProps) {
63
30
  const router = useRouter()
64
31
 
@@ -167,9 +134,7 @@ export function Sidebar({pageMap}: SidebarProps) {
167
134
  {sidebarLinks && sidebarLinks.length > 0 && (
168
135
  <NavList.Group title="" sx={{mb: 24}}>
169
136
  {sidebarLinks.map(link => {
170
- const {leadingIcon} = link
171
137
  const isExternalUrl = link.href.startsWith('http')
172
- const LeadingIcon = getOcticonForType(leadingIcon)
173
138
 
174
139
  return (
175
140
  <NavList.Item
@@ -178,7 +143,6 @@ export function Sidebar({pageMap}: SidebarProps) {
178
143
  href={link.href}
179
144
  target={isExternalUrl ? '_blank' : undefined}
180
145
  >
181
- <NavList.LeadingVisual>{LeadingIcon && <LeadingIcon />}</NavList.LeadingVisual>
182
146
  {link.title}
183
147
  {isExternalUrl && (
184
148
  <NavList.TrailingVisual>
@@ -0,0 +1,14 @@
1
+ .SkipToMainContent {
2
+ position: absolute;
3
+ top: 0;
4
+ z-index: 999;
5
+ padding: var(--base-size-16);
6
+ background-color: var(--base-color-scale-blue-5);
7
+ color: var(--base-color-scale-white-0);
8
+ opacity: 0;
9
+ }
10
+ .SkipToMainContent:focus {
11
+ left: 0;
12
+ opacity: 1;
13
+ opacity: 1;
14
+ }
@@ -0,0 +1,14 @@
1
+ import React, {HTMLAttributes, PropsWithChildren} from 'react'
2
+ import styles from './SkipToMainContent.module.css'
3
+
4
+ type SkipToMainContentProps = {
5
+ href: string
6
+ } & HTMLAttributes<HTMLAnchorElement>
7
+
8
+ export function SkipToMainContent({children, href, ...rest}: PropsWithChildren<SkipToMainContentProps>) {
9
+ return (
10
+ <a href={href} className={styles.SkipToMainContent} {...rest}>
11
+ {children}
12
+ </a>
13
+ )
14
+ }
@@ -1,4 +1,4 @@
1
- import React, {useEffect} from 'react'
1
+ import React, {useEffect, useMemo} from 'react'
2
2
  import {NavList} from '@primer/react'
3
3
  import {Text} from '@primer/react-brand'
4
4
  import {Heading as HeadingType} from 'nextra'
@@ -10,6 +10,8 @@ type TableOfContentsProps = {
10
10
  }
11
11
 
12
12
  export function TableOfContents({headings}: TableOfContentsProps) {
13
+ const depth2Headings = useMemo(() => headings.filter(heading => heading.depth === 2), [headings])
14
+
13
15
  useEffect(() => {
14
16
  const observer = new IntersectionObserver(
15
17
  entries => {
@@ -40,7 +42,7 @@ export function TableOfContents({headings}: TableOfContentsProps) {
40
42
  },
41
43
  )
42
44
 
43
- for (const heading of headings) {
45
+ for (const heading of depth2Headings) {
44
46
  const el = document.getElementById(heading.id)
45
47
  if (el) {
46
48
  observer.observe(el)
@@ -50,7 +52,7 @@ export function TableOfContents({headings}: TableOfContentsProps) {
50
52
  return () => {
51
53
  observer.disconnect()
52
54
  }
53
- }, [headings])
55
+ }, [depth2Headings])
54
56
 
55
57
  return (
56
58
  <aside className={styles.wrapper}>
@@ -58,7 +60,7 @@ export function TableOfContents({headings}: TableOfContentsProps) {
58
60
  On this page
59
61
  </Text>
60
62
  <NavList>
61
- {headings.map(heading => (
63
+ {depth2Headings.map(heading => (
62
64
  <NavList.Item
63
65
  className={styles.item}
64
66
  key={heading.id}
@@ -0,0 +1,14 @@
1
+ .Heading__anchor {
2
+ color: var(--brand-color-text-default) !important;
3
+ text-decoration: none !important;
4
+ }
5
+
6
+ .Heading__anchorIcon {
7
+ visibility: hidden;
8
+ margin-left: var(--base-size-8);
9
+ vertical-align: middle !important;
10
+ }
11
+
12
+ .Heading__anchor:hover .Heading__anchorIcon {
13
+ visibility: visible;
14
+ }
@@ -0,0 +1,43 @@
1
+ import React from 'react'
2
+ import type {ComponentProps, ReactElement} from 'react'
3
+ import clsx from 'clsx'
4
+ import type {Components} from 'nextra/mdx'
5
+
6
+ import {LinkIcon} from '@primer/octicons-react'
7
+ import NextLink from 'next/link'
8
+
9
+ import styles from './mdx-components.module.css'
10
+
11
+ // Anchor links for Headings
12
+ function HeadingLink({
13
+ tag: Tag,
14
+ context,
15
+ children,
16
+ id,
17
+ className,
18
+ ...props
19
+ }: ComponentProps<'h2'> & {
20
+ tag: `h${2 | 3 | 4 | 5 | 6}`
21
+ context: {index: number}
22
+ }): ReactElement {
23
+ return (
24
+ <Tag {...props} id={id} className={clsx(className)}>
25
+ <NextLink href={`#${id}`} className={styles.Heading__anchor}>
26
+ {children} <LinkIcon className={styles.Heading__anchorIcon} size={16} />
27
+ </NextLink>
28
+ </Tag>
29
+ )
30
+ }
31
+
32
+ export const Link = ({href = '', className, ...props}) => <NextLink href={href} {...props} />
33
+ export const getComponents = (): Components => {
34
+ const context = {index: 0}
35
+ return {
36
+ h2: props => <HeadingLink tag="h2" context={context} {...props} />,
37
+ h3: props => <HeadingLink tag="h3" context={context} {...props} />,
38
+ h4: props => <HeadingLink tag="h4" context={context} {...props} />,
39
+ h5: props => <HeadingLink tag="h5" context={context} {...props} />,
40
+ h6: props => <HeadingLink tag="h6" context={context} {...props} />,
41
+ a: Link,
42
+ }
43
+ }
@@ -129,15 +129,12 @@
129
129
  letter-spacing: var(--brand-heading-letterSpacing-300);
130
130
  }
131
131
 
132
- .Prose :is(h1, h2, h3) {
133
- --spacing: var(--base-size-64);
134
- }
135
-
136
132
  .Prose :is(h1) {
133
+ --spacing: var(--base-size-64);
137
134
  margin-block-end: var(--spacing);
138
135
  }
139
136
 
140
- .Prose :is(h1, h2, h3) + * {
137
+ .Prose :is(h2, h3) + * {
141
138
  --spacing: var(--base-size-40);
142
139
  }
143
140
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/doctocat-nextjs",
3
- "version": "0.0.2-rc.1da8f9a",
3
+ "version": "0.0.2-rc.60745e5",
4
4
  "description": "A Next.js theme for building Primer documentation sites",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/types.ts CHANGED
@@ -3,6 +3,5 @@ export type ThemeConfig = {
3
3
  sidebarLinks?: {
4
4
  title: string
5
5
  href: string
6
- leadingIcon?: 'repo' | 'org' | 'bookmark' | 'star' | 'img' | 'browser' | 'stack'
7
6
  }[]
8
7
  }