Skip to content

Commit 1323bff

Browse files
committed
fix mobile
1 parent 754dac3 commit 1323bff

File tree

7 files changed

+288
-32
lines changed

7 files changed

+288
-32
lines changed

src/components/DocsNavigation/DocsNavigationMobile/DocsPickerMobile.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,6 @@ export function ProductNavigation({ path }: Props) {
5353
const label = subProductTrigger?.label || "Resources"
5454
const icon = subProductTrigger?.label ? subProductTrigger.icon : defaultLogo.src
5555

56-
// Helper function to recursively map pages and preserve all metadata
57-
const mapPageWithChildren = (page: any): any => ({
58-
label: page.label,
59-
href: page.href,
60-
sdkLang: page.sdkLang,
61-
chainTypes: page.chainTypes,
62-
highlightAsCurrent: page.highlightAsCurrent,
63-
children: page.children ? page.children.map(mapPageWithChildren) : [],
64-
})
65-
6656
useEffect(() => {
6757
const foundSubProduct = productsNav.categories.find((category) =>
6858
category.items.some((item) => item.subProducts && isMatchedPath(path, item.href))
@@ -75,7 +65,7 @@ export function ProductNavigation({ path }: Props) {
7565
const items = subProduct.subProducts.map((subProductItem) => ({
7666
label: subProductItem.label,
7767
href: "#",
78-
pages: subProductItem.items.map(mapPageWithChildren),
68+
pages: subProductItem.items,
7969
}))
8070

8171
const safeSubProducts: SubProducts = {

src/components/Header/getNavigationProps.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import nodesLogo from "../../assets/product-logos/node-logo.svg"
1313
import quickstartLogo from "../../assets/product-logos/quickstart-logo.svg"
1414
import { SIDEBAR as sidebar } from "../../config/sidebar.ts"
1515
import type { ChainType } from "../../config/types.js"
16+
import { propagateChainTypes } from "../../utils/chainType.js"
1617

1718
interface Page {
1819
label: string
@@ -53,10 +54,14 @@ const mapContents = (contents: SidebarContent[], pageSdkLangMap: Map<string, str
5354
}
5455

5556
const getSubProducts = (sectionData, pageSdkLangMap: Map<string, string>) => {
56-
const structuredData = sectionData.map((item) => ({
57-
label: item.section,
58-
items: mapContents(item.contents, pageSdkLangMap),
59-
}))
57+
const structuredData = sectionData.map((item) => {
58+
// Propagate chainTypes from parent to children for consistent filtering
59+
const contentsWithPropagatedChainTypes = propagateChainTypes(item.contents)
60+
return {
61+
label: item.section,
62+
items: mapContents(contentsWithPropagatedChainTypes, pageSdkLangMap),
63+
}
64+
})
6065
return structuredData
6166
}
6267

src/content/ccip/concepts/architecture/onchain/index.mdx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,4 @@ metadata:
1111
isIndex: true
1212
---
1313

14-
This section details the onchain components of the CCIP architecture, covering both EVM-compatible chains and SVM-based chains such as Solana.
15-
16-
- **[EVM Architecture](/ccip/concepts/architecture/onchain/evm)**: Learn about the onchain components specific to EVM environments.
17-
- **[SVM Architecture](/ccip/concepts/architecture/onchain/svm)**: Explore the onchain programs and components specific for SVM environments.
18-
- **[Aptos Architecture](/ccip/concepts/architecture/onchain/aptos)**: Understand the onchain components and message structures for Aptos environments.
14+
This section details the onchain components of the CCIP architecture, including routers, onramps, offramps, commit stores, token pools, and receiver contracts or programs. These components operate directly on blockchains to enable secure cross-chain communication. The implementation varies by blockchain family (EVM, Solana, Aptos), but the core architecture remains consistent across all supported chains.

src/content/ccip/concepts/best-practices/index.mdx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,4 @@ metadata:
1111
isIndex: true
1212
---
1313

14-
This section outlines recommended practices for using Chainlink CCIP effectively and securely.
15-
16-
- **[EVM Best Practices](/ccip/concepts/best-practices/evm)**: Recommended guidelines for interacting with CCIP on EVM-compatible chains.
17-
- **[SVM Best Practices](/ccip/concepts/best-practices/svm)**: Recommended guidelines for interacting with CCIP on SVM-based chains like Solana.
18-
- **[Aptos Best Practices](/ccip/concepts/best-practices/aptos)**: Recommended guidelines for interacting with CCIP on Aptos chain.
14+
This section outlines recommended practices for using Chainlink CCIP effectively and securely across different blockchain families. Learn about security considerations, message verification, token administration, liquidity management, multi-signature patterns, and blockchain-specific guidelines to build robust cross-chain applications on EVM-compatible chains, Solana, and Aptos.

src/content/ccip/concepts/cross-chain-token/index.mdx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@ metadata:
1010
isIndex: true
1111
---
1212

13-
This section explains the Cross-Chain Token (CCT) standard, a Chainlink CCIP feature enabling secure and reliable cross-chain token transfers. Learn how to make your tokens compatible with CCIP.
13+
This section explains the Cross-Chain Token (CCT) standard, a Chainlink CCIP feature enabling secure and reliable cross-chain token transfers. Learn about the architecture, token requirements, token pool types (Lock and Release, Burn and Mint), registration processes, administrative roles, and upgrade mechanisms needed to make your tokens compatible with CCIP across different blockchain families.
1414

15-
- **[Overview](/ccip/concepts/cross-chain-token/overview)**: Get a high-level summary of the CCT standard and its benefits.
16-
- **[Architecture](/ccip/concepts/cross-chain-token/evm/architecture)**: Explore the CCT architecture components.
17-
- **[Tokens](/ccip/concepts/cross-chain-token/evm/tokens)**: Learn about token requirements and compatibility.
18-
- **[Token Pools](/ccip/concepts/cross-chain-token/evm/token-pools)**: Understand token pool types and deployment.
19-
- **[Registration & Administration](/ccip/concepts/cross-chain-token/evm/registration-administration)**: Details on registering tokens and managing administrative roles.
20-
- **[Upgradability](/ccip/concepts/cross-chain-token/evm/upgradability)**: Information on upgrading token pool contracts.
15+
For a high-level summary of the CCT standard and its benefits, see the [Overview](/ccip/concepts/cross-chain-token/overview) page.

src/utils/chainType.test.ts

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import { expect, test, describe } from "@jest/globals"
2+
import { propagateChainTypes } from "./chainType.js"
3+
import type { SectionContent } from "~/config/sidebar.js"
4+
5+
describe("propagateChainTypes", () => {
6+
test("children without chainTypes inherit from parent", () => {
7+
const input: SectionContent[] = [
8+
{
9+
title: "API Reference",
10+
url: "ccip/api-reference/evm",
11+
chainTypes: ["evm"],
12+
children: [
13+
{ title: "Messages", url: "ccip/api-reference/evm/messages" },
14+
{ title: "Router", url: "ccip/api-reference/evm/router" },
15+
],
16+
},
17+
]
18+
19+
const result = propagateChainTypes(input)
20+
21+
expect(result[0].children![0].chainTypes).toEqual(["evm"])
22+
expect(result[0].children![1].chainTypes).toEqual(["evm"])
23+
})
24+
25+
test("children with explicit chainTypes are not overridden", () => {
26+
const input: SectionContent[] = [
27+
{
28+
title: "Parent",
29+
url: "parent",
30+
chainTypes: ["evm"],
31+
children: [
32+
{ title: "Child1", url: "child1" }, // Should inherit
33+
{ title: "Child2", url: "child2", chainTypes: ["solana", "aptos"] }, // Should keep its own
34+
],
35+
},
36+
]
37+
38+
const result = propagateChainTypes(input)
39+
40+
expect(result[0].children![0].chainTypes).toEqual(["evm"])
41+
expect(result[0].children![1].chainTypes).toEqual(["solana", "aptos"])
42+
})
43+
44+
test("nested children (grandchildren) inherit correctly", () => {
45+
const input: SectionContent[] = [
46+
{
47+
title: "Grandparent",
48+
url: "grandparent",
49+
chainTypes: ["evm"],
50+
children: [
51+
{
52+
title: "Parent",
53+
url: "parent",
54+
children: [
55+
{ title: "Child", url: "child" },
56+
{ title: "AnotherChild", url: "another-child" },
57+
],
58+
},
59+
],
60+
},
61+
]
62+
63+
const result = propagateChainTypes(input)
64+
65+
// Parent inherits from grandparent
66+
expect(result[0].children![0].chainTypes).toEqual(["evm"])
67+
// Children inherit from parent (which inherited from grandparent)
68+
expect(result[0].children![0].children![0].chainTypes).toEqual(["evm"])
69+
expect(result[0].children![0].children![1].chainTypes).toEqual(["evm"])
70+
})
71+
72+
test("parent with chainTypes, intermediate child without, grandchild should inherit from parent", () => {
73+
const input: SectionContent[] = [
74+
{
75+
title: "API Reference",
76+
url: "api",
77+
chainTypes: ["solana"],
78+
children: [
79+
{
80+
title: "v1.6.0",
81+
url: "v1.6.0",
82+
// No chainTypes - should inherit solana
83+
children: [
84+
{ title: "Messages", url: "messages" }, // Should inherit solana
85+
],
86+
},
87+
],
88+
},
89+
]
90+
91+
const result = propagateChainTypes(input)
92+
93+
expect(result[0].children![0].chainTypes).toEqual(["solana"])
94+
expect(result[0].children![0].children![0].chainTypes).toEqual(["solana"])
95+
})
96+
97+
test("universal items (no chainTypes) remain universal", () => {
98+
const input: SectionContent[] = [
99+
{
100+
title: "Overview",
101+
url: "overview",
102+
// No chainTypes - should remain undefined (universal)
103+
},
104+
]
105+
106+
const result = propagateChainTypes(input)
107+
108+
expect(result[0].chainTypes).toBeUndefined()
109+
})
110+
111+
test("universal parent with chain-specific children", () => {
112+
const input: SectionContent[] = [
113+
{
114+
title: "Universal Parent",
115+
url: "parent",
116+
// No chainTypes
117+
children: [
118+
{ title: "EVM Child", url: "evm-child", chainTypes: ["evm"] },
119+
{ title: "Solana Child", url: "solana-child", chainTypes: ["solana"] },
120+
],
121+
},
122+
]
123+
124+
const result = propagateChainTypes(input)
125+
126+
// Parent remains universal
127+
expect(result[0].chainTypes).toBeUndefined()
128+
// Children keep their explicit chainTypes
129+
expect(result[0].children![0].chainTypes).toEqual(["evm"])
130+
expect(result[0].children![1].chainTypes).toEqual(["solana"])
131+
})
132+
133+
test("empty array of contents returns empty array", () => {
134+
const input: SectionContent[] = []
135+
const result = propagateChainTypes(input)
136+
expect(result).toEqual([])
137+
})
138+
139+
test("items without children are handled correctly", () => {
140+
const input: SectionContent[] = [
141+
{ title: "Item1", url: "item1", chainTypes: ["evm"] },
142+
{ title: "Item2", url: "item2", chainTypes: ["solana"] },
143+
]
144+
145+
const result = propagateChainTypes(input)
146+
147+
expect(result[0].chainTypes).toEqual(["evm"])
148+
expect(result[1].chainTypes).toEqual(["solana"])
149+
})
150+
151+
test("multiple chain types are preserved", () => {
152+
const input: SectionContent[] = [
153+
{
154+
title: "Multi-chain Parent",
155+
url: "parent",
156+
chainTypes: ["evm", "solana", "aptos"],
157+
children: [{ title: "Child", url: "child" }],
158+
},
159+
]
160+
161+
const result = propagateChainTypes(input)
162+
163+
expect(result[0].children![0].chainTypes).toEqual(["evm", "solana", "aptos"])
164+
})
165+
166+
test("does not mutate original input", () => {
167+
const input: SectionContent[] = [
168+
{
169+
title: "Parent",
170+
url: "parent",
171+
chainTypes: ["evm"],
172+
children: [{ title: "Child", url: "child" }],
173+
},
174+
]
175+
176+
const originalChildChainTypes = input[0].children![0].chainTypes
177+
178+
propagateChainTypes(input)
179+
180+
// Original should not have been mutated
181+
expect(input[0].children![0].chainTypes).toBe(originalChildChainTypes)
182+
})
183+
184+
test("real-world scenario: CCIP API Reference structure", () => {
185+
// Simulates the actual CCIP sidebar structure
186+
const input: SectionContent[] = [
187+
{
188+
title: "Tools and Resources",
189+
url: "ccip/tools-resources",
190+
children: [
191+
{
192+
title: "API Reference",
193+
url: "ccip/api-reference/svm",
194+
chainTypes: ["solana"],
195+
children: [
196+
{
197+
title: "v1.6.0",
198+
url: "ccip/api-reference/svm/v1.6.0",
199+
isCollapsible: true,
200+
children: [
201+
{ title: "Messages", url: "ccip/api-reference/svm/v1.6.0/messages" },
202+
{ title: "Router", url: "ccip/api-reference/svm/v1.6.0/router" },
203+
{ title: "Receiver", url: "ccip/api-reference/svm/v1.6.0/receiver" },
204+
],
205+
},
206+
],
207+
},
208+
],
209+
},
210+
]
211+
212+
const result = propagateChainTypes(input)
213+
214+
// Top-level has no chainTypes (universal)
215+
expect(result[0].chainTypes).toBeUndefined()
216+
217+
// API Reference has solana
218+
expect(result[0].children![0].chainTypes).toEqual(["solana"])
219+
220+
// v1.6.0 inherits solana
221+
expect(result[0].children![0].children![0].chainTypes).toEqual(["solana"])
222+
223+
// All API endpoints inherit solana
224+
const endpoints = result[0].children![0].children![0].children!
225+
expect(endpoints[0].chainTypes).toEqual(["solana"]) // Messages
226+
expect(endpoints[1].chainTypes).toEqual(["solana"]) // Router
227+
expect(endpoints[2].chainTypes).toEqual(["solana"]) // Receiver
228+
})
229+
})

src/utils/chainType.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,51 @@ export function filterContentByChainType(contents: SectionContent[], selectedCha
5555
.filter((item): item is SectionContent => item !== null)
5656
}
5757

58+
/**
59+
* Recursively propagates chainTypes from parent to children
60+
*
61+
* This ensures that children inherit their parent's chainTypes if they don't have their own.
62+
* This is critical for mobile navigation where items are rendered as siblings rather than
63+
* nested in DOM hierarchy, so CSS cascade doesn't provide implicit filtering.
64+
*
65+
* Rules:
66+
* - If child has explicit chainTypes: Keep them (don't override)
67+
* - If child has no chainTypes but parent does: Inherit from parent
68+
* - If neither child nor parent has chainTypes: Remain universal (no chainTypes)
69+
* - Recursively process all descendants
70+
*
71+
* Examples:
72+
* Parent: { title: "API Reference", chainTypes: ['evm'], children: [...] }
73+
* Child: { title: "Messages" } // No chainTypes
74+
* Result: { title: "Messages", chainTypes: ['evm'] } // Inherited
75+
*
76+
* Parent: { title: "API Reference", chainTypes: ['evm'], children: [...] }
77+
* Child: { title: "Router", chainTypes: ['evm', 'solana'] } // Has own
78+
* Result: { title: "Router", chainTypes: ['evm', 'solana'] } // Preserved
79+
*
80+
* @param contents - Array of sidebar content items
81+
* @param parentChainTypes - ChainTypes from parent (used in recursion)
82+
* @returns Contents with propagated chainTypes
83+
*/
84+
export function propagateChainTypes(contents: SectionContent[], parentChainTypes?: ChainType[]): SectionContent[] {
85+
return contents.map((item) => {
86+
// Create new item to avoid mutation
87+
const processed: SectionContent = { ...item }
88+
89+
// If item doesn't have chainTypes, inherit from parent
90+
if (!processed.chainTypes && parentChainTypes) {
91+
processed.chainTypes = parentChainTypes
92+
}
93+
94+
// Recursively process children, passing this item's chainTypes as parent
95+
if (processed.children) {
96+
processed.children = propagateChainTypes(processed.children, processed.chainTypes)
97+
}
98+
99+
return processed
100+
})
101+
}
102+
58103
/**
59104
* Filters sidebar DOM elements by chain type using show/hide approach
60105
*

0 commit comments

Comments
 (0)