Skip to content

Commit c429f14

Browse files
committed
Add icons + refactor and improve how navigation groups are computed
1 parent c0697ea commit c429f14

File tree

8 files changed

+399
-314
lines changed

8 files changed

+399
-314
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "graph-docs",
33
"private": true,
4-
"packageManager": "pnpm@10.1.0",
4+
"packageManager": "pnpm@10.2.0",
55
"scripts": {
66
"dev": "turbo run dev",
77
"build": "NODE_OPTIONS='--max_old_space_size=4096' turbo run build",

packages/og-image/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
"yoga-wasm-web": "^0.3.3"
1717
},
1818
"devDependencies": {
19-
"@cloudflare/workers-types": "^4.20250129.0",
19+
"@cloudflare/workers-types": "^4.20250204.0",
2020
"@types/react": "^18.3.18",
2121
"jest-image-snapshot": "^6.4.0",
2222
"tsx": "^4.19.2",
2323
"typescript": "^5.7.3",
2424
"vitest": "^1.6.1",
25-
"wrangler": "^3.107.2"
25+
"wrangler": "^3.107.3"
2626
}
2727
}

pnpm-lock.yaml

Lines changed: 249 additions & 226 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/next.config.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// @ts-check
2+
13
import nextra from 'nextra'
24

35
import { defaultLocale, translate } from '@edgeandnode/gds'
@@ -28,37 +30,57 @@ const withNextra = nextra({
2830
defaultShowCopyCode: false,
2931
readingTime: true,
3032
transformPageMap(pageMap) {
31-
const route = pageMap[0]?.route
33+
const route = pageMap[0] && 'route' in pageMap[0] ? pageMap[0].route : undefined
3234
const locale = typeof route === 'string' ? route.slice(1, 3) : defaultLocale
33-
const t = (key) => translate(translations, locale, key)
35+
/** @type {(key: string) => string} */
36+
const t = (key) =>
37+
translate(
38+
translations,
39+
/** @type {import('@edgeandnode/gds').Locale} */
40+
(locale),
41+
/** @type {any} */
42+
(key),
43+
)
3444

3545
const metaFile = {
3646
index: t('index.title'),
3747
about: '',
3848
'supported-networks': '',
3949
contracts: '',
50+
'---1': {
51+
type: 'separator',
52+
},
4053
subgraphs: {
4154
type: 'children',
4255
title: t('global.sidebar.subgraphs'),
4356
},
57+
'---2': {
58+
type: 'separator',
59+
},
4460
substreams: {
4561
type: 'children',
4662
title: t('global.sidebar.substreams'),
4763
},
64+
'---3': {
65+
type: 'separator',
66+
},
4867
sps: {
4968
type: 'children',
5069
title: t('global.sidebar.sps'),
5170
},
71+
'---4': {
72+
type: 'separator',
73+
},
5274
indexing: {
5375
type: 'children',
5476
title: t('global.sidebar.indexing'),
5577
},
56-
'---1': {
78+
'---5': {
5779
type: 'separator',
58-
title: t('global.sidebar.resources'),
5980
},
6081
resources: {
6182
type: 'children',
83+
title: t('global.sidebar.resources'),
6284
},
6385
archived: {
6486
type: 'children',
@@ -94,7 +116,7 @@ export default withNextra({
94116
reactStrictMode: true,
95117
basePath: env.BASE_PATH,
96118
trailingSlash: true,
97-
redirects: () => [
119+
redirects: async () => [
98120
{
99121
source: '/',
100122
destination: '/en/',

website/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@
2121
"lodash": "^4.17.21",
2222
"react-intersection-observer": "^9.15.1",
2323
"react-use": "^17.6.0",
24-
"@edgeandnode/common": "^6.50.0",
25-
"@edgeandnode/gds": "^6.2.0",
26-
"@edgeandnode/go": "^8.7.0",
24+
"@edgeandnode/common": "^6.51.0",
25+
"@edgeandnode/gds": "^6.2.1",
26+
"@edgeandnode/go": "^8.8.0",
2727
"@emotion/react": "^11.14.0",
2828
"@graphprotocol/contracts": "6.2.1",
2929
"@pinax/graph-networks-registry": "^0.6.7",
30-
"mixpanel-browser": "^2.59.0",
30+
"mixpanel-browser": "^2.60.0",
3131
"next": "^14.2.23",
3232
"next-seo": "^6.6.0",
3333
"next-sitemap": "^4.2.3",

website/src/NextraLayout.tsx

Lines changed: 108 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
type ComponentPropsWithoutRef,
99
type ReactElement,
1010
type ReactNode,
11-
type RefObject,
1211
useCallback,
1312
useContext,
1413
useMemo,
@@ -29,8 +28,18 @@ import {
2928
Locale,
3029
type NestedStrings,
3130
} from '@edgeandnode/gds'
31+
import {
32+
BookOpenText,
33+
Files,
34+
House,
35+
RoleIndexer,
36+
Stack,
37+
Subgraph,
38+
Substreams,
39+
SubstreamsPoweredSubgraph,
40+
TheGraph,
41+
} from '@edgeandnode/gds/icons'
3242

33-
import { TheGraphLogo } from '@/assets/TheGraphLogo'
3443
import {
3544
Difficulty,
3645
DocSearch,
@@ -203,24 +212,50 @@ export default function NextraLayout({ children, pageOpts, pageProps }: NextraTh
203212
return result
204213
}, [fsPath, pageMap, locale])
205214

215+
const activeRoute = normalizePagesResult.activePath.at(-1)?.route ?? null
216+
206217
type NavigationItem = WithOptional<(typeof normalizePagesResult.directories)[number], 'children'>
207218
type NavigationGroup = {
208219
title: string | undefined
220+
icon: ReactNode | undefined
209221
route: string | undefined
210222
items: NavigationItem[]
211223
}
212-
const navigationItemIsExpandable = (item: NavigationItem) => {
213-
if (!item.children?.length) return false
214-
switch (item.type) {
215-
case 'children':
216-
/**
217-
* This is hacky: we want to render an item of type `children` without a `NavigationItem` wrapper only when
218-
* it doesn't have a title, but Nextra defaults `title` to `name` so it's never actually empty.
219-
*/
220-
return item.title !== item.name
221-
default:
222-
return true
224+
const getNavigationItemIcon = (item: NavigationItem) => {
225+
const routeWithoutLocale = item.route.slice(3) || '/'
226+
if (routeWithoutLocale === '/') {
227+
return <House variant="fill" alt="" />
228+
}
229+
if (routeWithoutLocale === '/about' || routeWithoutLocale.startsWith('/about/')) {
230+
return <TheGraph alt="" />
231+
}
232+
if (routeWithoutLocale === '/supported-networks' || routeWithoutLocale.startsWith('/supported-networks/')) {
233+
return <Stack alt="" />
234+
}
235+
if (routeWithoutLocale === '/contracts' || routeWithoutLocale.startsWith('/contracts/')) {
236+
return <Files alt="" />
237+
}
238+
if (routeWithoutLocale === '/subgraphs' || routeWithoutLocale.startsWith('/subgraphs/')) {
239+
return <Subgraph alt="" />
240+
}
241+
if (routeWithoutLocale === '/substreams' || routeWithoutLocale.startsWith('/substreams/')) {
242+
return <Substreams alt="" />
243+
}
244+
if (routeWithoutLocale === '/sps' || routeWithoutLocale.startsWith('/sps/')) {
245+
return <SubstreamsPoweredSubgraph alt="" />
223246
}
247+
if (routeWithoutLocale === '/indexing' || routeWithoutLocale.startsWith('/indexing/')) {
248+
return <RoleIndexer alt="" />
249+
}
250+
if (
251+
routeWithoutLocale === '/resources' ||
252+
routeWithoutLocale.startsWith('/resources/') ||
253+
routeWithoutLocale === '/archived' ||
254+
routeWithoutLocale.startsWith('/archived/')
255+
) {
256+
return <BookOpenText alt="" />
257+
}
258+
return null
224259
}
225260
const getNavigationItemRoute = (item: NavigationItem) => {
226261
let currentItem = item
@@ -237,33 +272,55 @@ export default function NextraLayout({ children, pageOpts, pageProps }: NextraTh
237272
}
238273
const navigationGroups = normalizePagesResult.directories.reduce<NavigationGroup[]>(
239274
(groups, item: NavigationItem) => {
240-
if (item.type === 'separator') {
241-
groups.push({ title: item.title, route: undefined, items: [] })
242-
} else {
275+
const itemHasChildren = Boolean(item.children?.length)
276+
if (item.type === 'children' && !itemHasChildren) return groups
277+
/**
278+
* When an item of type `children` doesn't have a title set in the meta file, we want to render it without a `NavigationItem` wrapper.
279+
* Unfortunately, Nextra defaults `title` to `name`, so the way to check if it's empty is to check if it's equal to `name`, which should
280+
* never happen with an explicitly set `title` because it should be title case whereas `name` will always be lowercase.
281+
*/
282+
const itemHasNoWrapper = item.type === 'children' && item.title === item.name
283+
let currentGroup = groups[groups.length - 1]
284+
/**
285+
* Create a new group if it's the first one, if the current item is a separator, or if the current group has some items but no title
286+
* (meaning its items are rendered at the top level instead of in an expandable panel) while the current item has children and a wrapper.
287+
*/
288+
if (
289+
!currentGroup ||
290+
item.type === 'separator' ||
291+
(currentGroup.title === undefined && currentGroup.items.length > 0 && itemHasChildren && !itemHasNoWrapper)
292+
) {
293+
groups.push({
294+
title: item.type === 'separator' ? item.title : undefined,
295+
icon: undefined,
296+
route: undefined,
297+
items: [],
298+
})
299+
}
300+
if (item.type === 'separator') return groups
301+
currentGroup = groups[groups.length - 1]!
302+
if (currentGroup.title === undefined && currentGroup.items.length === 0 && itemHasChildren && !itemHasNoWrapper) {
303+
/**
304+
* If the group has no title and the first item we want to add to it has children and a wrapper, give the item's title, icon, route
305+
* (if different from the first child's), and children to the group, instead of adding the item itself.
306+
*/
307+
currentGroup.title = item.title
308+
currentGroup.icon = getNavigationItemIcon(item)
243309
const route = getNavigationItemRoute(item)
244-
let currentGroup = groups[groups.length - 1]
245-
if (
246-
!currentGroup ||
247-
currentGroup.items.some(navigationItemIsExpandable) ||
248-
(currentGroup.title === undefined && navigationItemIsExpandable(item))
249-
) {
250-
groups.push({
251-
title: undefined,
252-
route,
253-
items: [],
254-
})
255-
}
256-
currentGroup = groups[groups.length - 1]!
310+
const firstChildRoute = getNavigationItemRoute(item.children![0]!)
311+
currentGroup.route = route !== firstChildRoute ? route : undefined
312+
currentGroup.items.push(...item.children!)
313+
} else if (itemHasNoWrapper) {
314+
// Otherwise, if the first or next item to add to the group has no wrapper, just add its children to it
315+
currentGroup.items.push(...(item.children ?? []))
316+
} else {
317+
// Otherwise, add the item (which may or may not have children) to the group
257318
currentGroup.items.push(item)
258-
if (currentGroup.route === undefined && route !== undefined) {
259-
currentGroup.route = route
260-
}
261319
}
262320
return groups
263321
},
264322
[],
265323
)
266-
const activeRoute = normalizePagesResult.activePath.at(-1)?.route ?? null
267324

268325
// Provide `markOutlineItem` to the `DocumentContext` so child `Heading` components can mark outline items as "in or above view" or not
269326
const [
@@ -324,7 +381,7 @@ export default function NextraLayout({ children, pageOpts, pageProps }: NextraTh
324381
>
325382
<header className="flex h-16 items-center border-b border-white/8 pe-4 ps-6">
326383
<div>
327-
<TheGraphLogo alt="" size={8} />
384+
<TheGraph alt="" size={8} />
328385
<ButtonOrLink href="https://thegraph.com" className="absolute -inset-2">
329386
<span className="sr-only">The Graph</span>
330387
</ButtonOrLink>
@@ -382,33 +439,33 @@ export default function NextraLayout({ children, pageOpts, pageProps }: NextraTh
382439
</div>
383440
)}
384441
</DocSearch>
385-
{/* TODO: Add icons to some navigation items */}
386442
{navigationGroups.map((group, groupIndex) => {
387443
if (group.items.length === 0) {
388444
return null
389445
}
390446
const groupContent = group.items.map((groupItem) =>
391-
(function renderItem(item: typeof groupItem): ReactNode {
392-
const childrenContent = item.children?.length ? item.children.map(renderItem) : null
393-
if (childrenContent && !navigationItemIsExpandable(item)) {
394-
return childrenContent
395-
} else {
396-
return (
397-
<NavigationItem
398-
key={item.name}
399-
title={item.title}
400-
href={getNavigationItemRoute(item)}
401-
selected={item.route === activeRoute}
402-
children={childrenContent}
403-
/>
404-
)
405-
}
447+
(function renderItem(item: typeof groupItem) {
448+
return (
449+
<NavigationItem
450+
key={item.name}
451+
title={item.title}
452+
icon={getNavigationItemIcon(item)}
453+
href={getNavigationItemRoute(item)}
454+
selected={item.route === activeRoute}
455+
children={item.children?.length ? <>{item.children.map(renderItem)}</> : undefined}
456+
/>
457+
)
406458
})(groupItem),
407459
)
408460
return (
409461
<NavigationGroup key={groupIndex}>
410462
{group.title !== undefined ? (
411-
<NavigationItem title={group.title} href={group.route}>
463+
<NavigationItem
464+
title={group.title}
465+
icon={group.icon ?? getNavigationItemIcon(group.items[0]!)}
466+
href={group.route ?? getNavigationItemRoute(group.items[0]!)}
467+
selected={group.route === activeRoute}
468+
>
412469
{groupContent}
413470
</NavigationItem>
414471
) : (

website/src/assets/TheGraphLogo.tsx

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)