@primer/doctocat-nextjs 0.0.0-20250625111642 → 0.0.0-20250625130205

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
@@ -1,12 +1,12 @@
1
1
  # @primer/doctocat-nextjs
2
2
 
3
- ## 0.0.0-20250625111642
3
+ ## 0.0.0-20250625130205
4
4
 
5
5
  ### Patch Changes
6
6
 
7
7
  - Fake entry to force publishing
8
8
 
9
- ## 0.0.0-20250625111640
9
+ ## 0.0.0-20250625130203
10
10
 
11
11
  ### Patch Changes
12
12
 
@@ -19,8 +19,11 @@
19
19
  ```
20
20
 
21
21
  - [#48](https://github.com/primer/doctocat-nextjs/pull/48) [`ce73c24`](https://github.com/primer/doctocat-nextjs/commit/ce73c24b2e4e924667bf7446a504bd88d8f2ccf0) Thanks [@rezrah](https://github.com/rezrah)! - - Fix inline code font-size in markdown headings. Now inherits size used in the heading.
22
+
22
23
  - Increased spacing below React code blocks, which was previously too small.
23
24
 
25
+ - [#50](https://github.com/primer/doctocat-nextjs/pull/50) [`5d67989`](https://github.com/primer/doctocat-nextjs/commit/5d679895408c1a58342419692db4234dfddefd80) Thanks [@rezrah](https://github.com/rezrah)! - Add `menu-position` frontmatter support for custom sidebar navigation ordering
26
+
24
27
  ## 0.5.3
25
28
 
26
29
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  'use client'
2
- import React, {PropsWithChildren, useCallback, useState, useRef, useEffect, useId} from 'react'
2
+ import React, {type PropsWithChildren, useCallback, useState, useRef, useEffect, useId} from 'react'
3
3
  import clsx from 'clsx'
4
4
  import {LiveProvider, LiveEditor, LiveError, LivePreview} from 'react-live'
5
5
  import {useColorMode} from '../../context/color-modes/useColorMode'
@@ -21,6 +21,15 @@ type ReactCodeBlockProps = {
21
21
  jsxScope: Record<string, unknown>
22
22
  } & PropsWithChildren<HTMLElement>
23
23
 
24
+ const getFocusableElements = () => {
25
+ const focusableElementsQuery = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
26
+
27
+ return Array.from(document.querySelectorAll<HTMLElement>(focusableElementsQuery)).filter(el => {
28
+ const style = window.getComputedStyle(el)
29
+ return style.display !== 'none' && style.visibility !== 'hidden' && !el.hasAttribute('disabled')
30
+ })
31
+ }
32
+
24
33
  export function ReactCodeBlock(props: ReactCodeBlockProps) {
25
34
  const uniqueId = useId()
26
35
  const {colorMode, setColorMode} = useColorMode()
@@ -31,6 +40,7 @@ export function ReactCodeBlock(props: ReactCodeBlockProps) {
31
40
  const [isCodePaneCollapsed, setIsCodePaneCollapsed] = useState<boolean | null>(null)
32
41
  const [initialPosition, setInitialPosition] = useState<number | null>(null)
33
42
  const editorRef = useRef<HTMLDivElement>(null)
43
+ const resetButtonRef = useRef<HTMLButtonElement>(null)
34
44
  const shouldShowPreview = ['tsx', 'jsx'].includes(props['data-language'])
35
45
 
36
46
  // scroll back to the initial y pos on collapse state change
@@ -84,6 +94,41 @@ export function ReactCodeBlock(props: ReactCodeBlockProps) {
84
94
 
85
95
  const noInline = props['data-filename'] === 'noinline' || false
86
96
 
97
+ useEffect(() => {
98
+ const editor = editorRef.current
99
+
100
+ if (!editor) return
101
+
102
+ const onKeyDown = (e: KeyboardEvent) => {
103
+ if (e.key !== 'Tab') {
104
+ return
105
+ }
106
+
107
+ if (e.shiftKey) {
108
+ e.preventDefault()
109
+ // We know that the previous focusable element is always the reset button
110
+ resetButtonRef.current?.focus()
111
+ return
112
+ }
113
+
114
+ const focusableElements = getFocusableElements()
115
+
116
+ const currentIndex = focusableElements.findIndex(el => el === resetButtonRef.current)
117
+
118
+ if (currentIndex !== -1) {
119
+ e.preventDefault()
120
+ const nextIndex = currentIndex + 1
121
+ focusableElements[nextIndex]?.focus()
122
+ }
123
+ }
124
+
125
+ editor.addEventListener('keydown', onKeyDown)
126
+
127
+ return () => {
128
+ editor.removeEventListener('keydown', onKeyDown)
129
+ }
130
+ }, [])
131
+
87
132
  return (
88
133
  <>
89
134
  <LiveProvider transformCode={transformCodeWithBasePath} code={code} scope={props.jsxScope} noInline={noInline}>
@@ -117,7 +162,7 @@ export function ReactCodeBlock(props: ReactCodeBlockProps) {
117
162
  <Button size="small" leadingVisual={CopyIcon} onClick={handleCopy}>
118
163
  Copy
119
164
  </Button>
120
- <Button size="small" leadingVisual={UndoIcon} onClick={handleReset}>
165
+ <Button size="small" leadingVisual={UndoIcon} onClick={handleReset} ref={resetButtonRef}>
121
166
  Reset
122
167
  </Button>
123
168
  </div>
@@ -82,7 +82,29 @@ export function Sidebar({pageMap}: SidebarProps) {
82
82
  <NextLink href={item.route}>{subNavName}</NextLink>
83
83
  </NavList.GroupHeading>
84
84
  {item.children
85
- .sort((a, b) => ((a as MdxFile).name === 'index' ? -1 : (b as MdxFile).name === 'index' ? 1 : 0)) // puts index page first
85
+ .sort((a, b) => {
86
+ // make sure index page is first
87
+ if ((a as MdxFile).name === 'index') return -1
88
+ if ((b as MdxFile).name === 'index') return 1
89
+
90
+ // Check for menu-position property in frontmatter
91
+ const aPos = (a as MdxFile).frontMatter?.['menu-position']
92
+ const bPos = (b as MdxFile).frontMatter?.['menu-position']
93
+
94
+ // If both have menu-position, sort by menu-position
95
+ if (typeof aPos === 'number' && typeof bPos === 'number') {
96
+ return aPos - bPos
97
+ }
98
+
99
+ // If only one has menu-position, it comes first
100
+ if (typeof aPos === 'number') return -1
101
+ if (typeof bPos === 'number') return 1
102
+
103
+ // Neither has menu-position, sort alphabetically by title or name
104
+ const aTitle = (a as MdxFile).frontMatter?.title || (a as MdxFile).name
105
+ const bTitle = (b as MdxFile).frontMatter?.title || (b as MdxFile).name
106
+ return aTitle.localeCompare(bTitle)
107
+ })
86
108
  // only show index page if it has show-tabs
87
109
  .filter(child => (child as MdxFile).name !== 'index' || hasShowTabs(child as ExtendedPageItem))
88
110
  .map(child => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/doctocat-nextjs",
3
- "version": "0.0.0-20250625111642",
3
+ "version": "0.0.0-20250625130205",
4
4
  "description": "A Next.js theme for building Primer documentation sites",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/types.ts CHANGED
@@ -31,6 +31,7 @@ export type FrontMatter = {
31
31
  description?: string
32
32
  filePath?: string
33
33
  keywords?: string[]
34
+ menu_position?: number
34
35
  related?: {
35
36
  title: string
36
37
  href: string