Skip to content

Commit e7365b5

Browse files
feat: Add Beta and New badge components for sidebar navigation (#15437)
- Add beta and new frontmatter fields to FrontMatter type - Add badge styling (.beta-badge and .new-badge) to sidebar styles - Update SidebarLink to render badges based on props - Update CollapsibleSidebarLink to accept and pass through beta/isNew props - Update dynamicNav to pass badge props from context - Update platformSidebar to map frontmatter badge fields This allows documentation pages to display BETA or NEW badges in the sidebar by setting beta: true or new: true in their frontmatter. <img width="316" height="615" alt="CleanShot 2025-11-09 at 10  00 46@2x" src="https://github.com/user-attachments/assets/4e4b08f2-f71c-46d3-9cbd-7a69079071d5" /> --------- Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
1 parent 2e3b0f2 commit e7365b5

File tree

6 files changed

+113
-10
lines changed

6 files changed

+113
-10
lines changed

src/components/sidebar/collapsibleSidebarLink.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ interface SidebarLinkProps {
1616
*/
1717
title: string;
1818
to: string;
19+
20+
/**
21+
* Shows a beta badge next to the title
22+
*/
23+
beta?: boolean;
24+
1925
/**
2026
* Children represent the additional links nested under this sidebar link
2127
*/
@@ -26,6 +32,11 @@ interface SidebarLinkProps {
2632
* Indicates that the links are currently hidden. Overridden by isActive
2733
*/
2834
collapsed?: boolean | null;
35+
36+
/**
37+
* Shows a new badge next to the title
38+
*/
39+
isNew?: boolean;
2940
}
3041

3142
/**
@@ -39,6 +50,8 @@ export function CollapsibleSidebarLink({
3950
path,
4051
collapsed = null,
4152
className = '',
53+
beta = false,
54+
isNew = false,
4255
}: SidebarLinkProps) {
4356
const isActive = path?.indexOf(to) === 0;
4457
const enableSubtree = isActive || collapsed === false;
@@ -54,6 +67,8 @@ export function CollapsibleSidebarLink({
5467
isActive={to === getUnversionedPath(path)}
5568
collapsible={hasSubtree}
5669
title={title}
70+
beta={beta}
71+
isNew={isNew}
5772
onClick={() => {
5873
// Allow toggling the sidebar subtree only if the item is selected
5974
if (path === to) {

src/components/sidebar/dynamicNav.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ type Node = {
1111
[key: string]: any;
1212
context: {
1313
[key: string]: any;
14+
beta?: boolean;
15+
new?: boolean;
1416
sidebar_hidden?: boolean;
1517
sidebar_order?: number;
1618
sidebar_title?: string;
@@ -63,7 +65,7 @@ export const renderChildren = (
6365
showDepth: number = 0,
6466
depth: number = 0
6567
): React.ReactNode[] => {
66-
return sortPages(
68+
const sortedChildren = sortPages(
6769
children.filter(
6870
({name, node}) =>
6971
node &&
@@ -73,23 +75,32 @@ export const renderChildren = (
7375
!node.context.sidebar_hidden
7476
),
7577
({node}) => node!
76-
).map(({node, children: nodeChildren}) => {
78+
);
79+
80+
const result: React.ReactNode[] = [];
81+
82+
sortedChildren.forEach(({node, children: nodeChildren}) => {
7783
// will not be null because of the filter above
7884
if (!node) {
79-
return null;
85+
return;
8086
}
81-
return (
87+
88+
result.push(
8289
<CollapsibleSidebarLink
8390
to={node.path}
8491
key={node.path}
8592
title={node.context.sidebar_title || node.context.title!}
8693
collapsed={depth >= showDepth}
8794
path={path}
95+
beta={node.context.beta}
96+
isNew={node.context.new}
8897
>
8998
{renderChildren(nodeChildren, exclude, path, showDepth, depth + 1)}
9099
</CollapsibleSidebarLink>
91100
);
92101
});
102+
103+
return result;
93104
};
94105

95106
type ChildrenProps = {

src/components/sidebar/platformSidebar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export function PlatformSidebar({
2222
sidebar_order: n.frontmatter.sidebar_order,
2323
sidebar_title: n.frontmatter.sidebar_title,
2424
sidebar_hidden: n.frontmatter.sidebar_hidden,
25+
beta: n.frontmatter.beta,
26+
new: n.frontmatter.new,
2527
},
2628
path: '/' + n.path + '/',
2729
};

src/components/sidebar/sidebarLink.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ export function SidebarLink({
1313
collapsible,
1414
onClick,
1515
topLevel = false,
16+
beta = false,
17+
isNew = false,
1618
}: {
1719
href: string;
1820
title: string;
21+
beta?: boolean;
1922
collapsible?: boolean;
2023
isActive?: boolean;
24+
isNew?: boolean;
2125
onClick?: () => void;
2226
topLevel?: boolean;
2327
}) {
@@ -33,12 +37,16 @@ export function SidebarLink({
3337
}`}
3438
data-sidebar-link
3539
>
36-
<div>{title}</div>
40+
<div className={styles['sidebar-link-content']}>
41+
<span>{title}</span>
42+
{beta && <span className={styles['beta-badge']}>BETA</span>}
43+
{isNew && <span className={styles['new-badge']}>NEW</span>}
44+
</div>
3745
{collapsible && <NavChevron direction={isActive ? 'down' : 'right'} />}
3846
</LinkComponent>
3947
);
4048
}
4149

4250
export function SidebarSeparator() {
43-
return <hr className={`${styles['sidebar-separator']} mt-3 mb-3`} />;
51+
return <hr className={styles['sidebar-separator']} />;
4452
}

src/components/sidebar/style.module.scss

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@
9191
}
9292
}
9393

94-
.sidebar-separator {
95-
border-top: 1px solid var(--border-color);
96-
}
97-
9894
.toc {
9995
font-size: 0.875rem;
10096
flex: 1;
@@ -159,3 +155,57 @@
159155
background-color: var(--brandDecoration);
160156
}
161157
}
158+
159+
.sidebar-link-content {
160+
display: flex;
161+
align-items: center;
162+
gap: 0.5rem;
163+
flex: 1;
164+
min-width: 0;
165+
}
166+
167+
.beta-badge {
168+
display: inline-flex;
169+
align-items: center;
170+
padding: 0.0625rem 0.375rem;
171+
font-size: 0.625rem;
172+
font-weight: 500;
173+
letter-spacing: 0.02em;
174+
color: #fafaf9; /* off-white */
175+
background-color: transparent;
176+
border: 1px solid #f59e0b; /* amber-500 warning color */
177+
border-radius: 0.25rem;
178+
white-space: nowrap;
179+
flex-shrink: 0;
180+
}
181+
182+
:global(.dark) .beta-badge {
183+
color: #fafaf9; /* off-white */
184+
border-color: #fbbf24; /* amber-400 for dark mode */
185+
}
186+
187+
.new-badge {
188+
display: inline-flex;
189+
align-items: center;
190+
padding: 0.0625rem 0.375rem;
191+
font-size: 0.625rem;
192+
font-weight: 500;
193+
letter-spacing: 0.02em;
194+
color: #fafaf9; /* off-white */
195+
background-color: transparent;
196+
border: 1px solid #10b981; /* emerald-500 success green */
197+
border-radius: 0.25rem;
198+
white-space: nowrap;
199+
flex-shrink: 0;
200+
}
201+
202+
:global(.dark) .new-badge {
203+
color: #fafaf9; /* off-white */
204+
border-color: #34d399; /* emerald-400 for dark mode */
205+
}
206+
207+
.sidebar-separator {
208+
margin: 1rem 0;
209+
border: none;
210+
border-top: 1px solid var(--border-color);
211+
}

src/types/frontmatter.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,36 @@ export interface FrontMatter {
1313
* Document title - used in <title> as well as things like search titles.
1414
*/
1515
title: string;
16+
/**
17+
* Set this to true to show a "beta" badge next to the title in the sidebar
18+
*/
19+
beta?: boolean;
1620
/**
1721
* A description to use in the <meta> header, as well as in auto generated page grids.
1822
*/
1923
customCanonicalTag?: string;
24+
2025
/** Add this if you want to add a canonical tag (without this it will default to the page url). Should be a relative path without the domain (e.g. `/platforms/react/options/`) */
2126
description?: string;
27+
2228
/**
2329
* Set this to true to mark this page as a draft, and hide it from various other components (such as the PageGrid).
2430
*/
2531
draft?: boolean;
32+
2633
/**
2734
* Set this to true to take all the available width for the page content.
2835
*/
2936
fullWidth?: boolean;
37+
3038
/**
3139
* A list of keywords for indexing with search.
3240
*/
3341
keywords?: string[];
42+
/**
43+
* Set this to true to show a "new" badge next to the title in the sidebar
44+
*/
45+
new?: boolean;
3446

3547
/**
3648
* The next page in the bottom pagination navigation.
@@ -69,6 +81,11 @@ export interface FrontMatter {
6981
*/
7082
previousPage?: PaginationNavNode;
7183

84+
/**
85+
* Set this to true to show a separator/divider below this item in the sidebar
86+
*/
87+
section_end_divider?: boolean;
88+
7289
/**
7390
* The next page in the sidebar navigation.
7491
*/

0 commit comments

Comments
 (0)