Skip to content

Commit 5aebc70

Browse files
chore(dev-hub)-header Improvements (#3002)
* chore(dev-hub)-header Improvements * requested changes * fix: wrong provider order * feat: search button component * fix: nit * fix: theme import * fix: formatting * fix: props * feat: default content for search component * fix: cache params * fix: remove unused imports --------- Co-authored-by: Alexandru Cambose <[email protected]>
1 parent 383b585 commit 5aebc70

File tree

8 files changed

+190
-80
lines changed

8 files changed

+190
-80
lines changed

apps/developer-hub/src/components/Root/index.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { AppShell } from "@pythnetwork/component-library/AppShell";
22
import { RootProvider as FumadocsRootProvider } from "fumadocs-ui/provider";
33
import { NuqsAdapter } from "nuqs/adapters/next/app";
44
import type { ReactNode } from "react";
5-
import "./global.css";
65

76
import {
87
AMPLITUDE_API_KEY,
98
ENABLE_ACCESSIBILITY_REPORTING,
109
GOOGLE_ANALYTICS_ID,
1110
} from "../../config/server";
11+
import { SearchButton } from "../search-button";
12+
13+
import "./global.css";
1214

1315
export const TABS = [
1416
{ segment: "", children: "Home" },
@@ -23,14 +25,28 @@ type Props = {
2325
};
2426

2527
export const Root = ({ children }: Props) => (
26-
<AppShell
27-
appName="Developer Hub"
28-
amplitudeApiKey={AMPLITUDE_API_KEY}
29-
googleAnalyticsId={GOOGLE_ANALYTICS_ID}
30-
enableAccessibilityReporting={ENABLE_ACCESSIBILITY_REPORTING}
31-
providers={[NuqsAdapter]}
32-
tabs={TABS}
28+
<FumadocsRootProvider
29+
search={{
30+
enabled: true,
31+
options: {
32+
api: "/api/search",
33+
},
34+
}}
3335
>
34-
<FumadocsRootProvider>{children}</FumadocsRootProvider>
35-
</AppShell>
36+
<AppShell
37+
appName="Developer Hub"
38+
amplitudeApiKey={AMPLITUDE_API_KEY}
39+
googleAnalyticsId={GOOGLE_ANALYTICS_ID}
40+
enableAccessibilityReporting={ENABLE_ACCESSIBILITY_REPORTING}
41+
extraCta={<SearchButton />}
42+
mainCta={{
43+
label: "Insights",
44+
href: "https://insights.pyth.network/",
45+
}}
46+
providers={[NuqsAdapter]}
47+
tabs={TABS}
48+
>
49+
{children}
50+
</AppShell>
51+
</FumadocsRootProvider>
3652
);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use client";
2+
3+
import { SearchButton as SearchButtonComponent } from "@pythnetwork/component-library/SearchButton";
4+
import DefaultSearchDialog from "fumadocs-ui/components/dialog/search-default";
5+
import { useCallback, useState } from "react";
6+
7+
export const SearchButton = () => {
8+
const [open, setOpen] = useState(false);
9+
10+
const handleSearch = useCallback(() => {
11+
setOpen(true);
12+
}, []);
13+
14+
return (
15+
<>
16+
<SearchButtonComponent
17+
size="sm"
18+
smallScreenContent="Search"
19+
largeScreenContent="Search"
20+
onClick={handleSearch}
21+
/>
22+
<DefaultSearchDialog
23+
open={open}
24+
onOpenChange={setOpen}
25+
api="/api/search"
26+
/>
27+
</>
28+
);
29+
};

apps/developer-hub/src/config/layout.config.tsx

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,20 @@ import { source } from "../source";
55

66
export const baseOptions: BaseLayoutProps = {
77
nav: {
8-
enabled: false,
8+
enabled: true,
99
},
1010
themeSwitch: {
1111
enabled: false,
1212
},
13+
searchToggle: {
14+
enabled: false,
15+
},
1316
};
1417

1518
export const docsOptions: DocsLayoutProps = {
1619
...baseOptions,
1720
tree: source.pageTree,
1821
sidebar: {
19-
tabs: {
20-
transform(option, node) {
21-
const meta = source.getNodeMeta(node);
22-
if (!meta || !node.icon) return option;
23-
24-
return {
25-
...option,
26-
icon: (
27-
<div className="[&_svg]:size-6.5 md:[&_svg]:size-5">
28-
{node.icon}
29-
</div>
30-
),
31-
};
32-
},
33-
},
22+
tabs: false,
3423
},
3524
};

apps/insights/src/components/Root/search-button.module.scss

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
11
@use "@pythnetwork/component-library/theme";
22

3-
.searchButton {
4-
.largeScreenSearchButton {
5-
display: none;
6-
7-
@include theme.breakpoint("md") {
8-
display: inline-flex;
9-
}
10-
}
11-
12-
.smallScreenSearchButton {
13-
@include theme.breakpoint("md") {
14-
display: none;
15-
}
16-
}
17-
}
18-
193
.searchDialogContents {
204
gap: theme.spacing(1);
215
display: flex;

apps/insights/src/components/Root/search-button.tsx

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
"use client";
22

3-
import { MagnifyingGlass } from "@phosphor-icons/react/dist/ssr/MagnifyingGlass";
43
import { XCircle } from "@phosphor-icons/react/dist/ssr/XCircle";
54
import { Badge } from "@pythnetwork/component-library/Badge";
65
import type { Props as ButtonProps } from "@pythnetwork/component-library/Button";
76
import { Button } from "@pythnetwork/component-library/Button";
87
import { NoResults } from "@pythnetwork/component-library/NoResults";
8+
import { SearchButton as SearchButtonComponent } from "@pythnetwork/component-library/SearchButton";
99
import { SearchInput } from "@pythnetwork/component-library/SearchInput";
1010
import { SingleToggleGroup } from "@pythnetwork/component-library/SingleToggleGroup";
11-
import { Skeleton } from "@pythnetwork/component-library/Skeleton";
1211
import {
1312
ListLayout,
1413
Virtualizer,
@@ -23,7 +22,6 @@ import { useLogger } from "@pythnetwork/component-library/useLogger";
2322
import { matchSorter } from "match-sorter";
2423
import type { ReactNode } from "react";
2524
import { useCallback, useEffect, useMemo, useState } from "react";
26-
import { useIsSSR } from "react-aria";
2725

2826
import { Cluster, ClusterToName } from "../../services/pyth";
2927
import { AssetClassBadge } from "../AssetClassBadge";
@@ -74,31 +72,7 @@ const ResolvedSearchButton = (props: ResolvedSearchButtonProps) => {
7472

7573
const SearchButtonImpl = (
7674
props: Omit<ButtonProps<typeof UnstyledButton>, "children">,
77-
) => (
78-
<div className={styles.searchButton}>
79-
<Button
80-
className={styles.largeScreenSearchButton ?? ""}
81-
variant="outline"
82-
beforeIcon={<MagnifyingGlass />}
83-
size="sm"
84-
rounded
85-
{...props}
86-
>
87-
<SearchShortcutText />
88-
</Button>
89-
<Button
90-
className={styles.smallScreenSearchButton ?? ""}
91-
hideText
92-
variant="ghost"
93-
beforeIcon={<MagnifyingGlass />}
94-
size="sm"
95-
rounded
96-
{...props}
97-
>
98-
Search
99-
</Button>
100-
</div>
101-
);
75+
) => <SearchButtonComponent size="sm" {...props} />;
10276

10377
const useSearchDrawer = ({ feeds, publishers }: ResolvedSearchButtonProps) => {
10478
const drawer = useDrawer();
@@ -152,16 +126,6 @@ const useSearchHotkey = (openSearchDrawer: () => void) => {
152126
}, [handleKeyDown]);
153127
};
154128

155-
const SearchShortcutText = () => {
156-
const isSSR = useIsSSR();
157-
return isSSR ? <Skeleton width={7} /> : <SearchTextImpl />;
158-
};
159-
160-
const SearchTextImpl = () => {
161-
const isMac = useMemo(() => navigator.userAgent.includes("Mac"), []);
162-
return isMac ? "⌘ K" : "Ctrl K";
163-
};
164-
165129
type SearchDialogContentsProps = ResolvedSearchButtonProps;
166130

167131
const SearchDialogContents = ({
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@use "../theme";
2+
3+
.searchButton {
4+
.largeScreenSearchButton {
5+
display: none;
6+
7+
@include theme.breakpoint("md") {
8+
display: inline-flex;
9+
}
10+
}
11+
12+
.smallScreenSearchButton {
13+
@include theme.breakpoint("md") {
14+
display: none;
15+
}
16+
}
17+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as icons from "@phosphor-icons/react/dist/ssr";
2+
import type { Meta, StoryObj } from "@storybook/react";
3+
4+
import { SearchButton as SearchButtonComponent } from "./index.jsx";
5+
import { SIZES } from "../Button/index.jsx";
6+
7+
const iconControl = {
8+
control: "select",
9+
options: Object.keys(icons),
10+
mapping: Object.fromEntries(
11+
Object.entries(icons).map(([iconName, Icon]) => [
12+
iconName,
13+
<Icon key={iconName} weights={new Map()} />,
14+
]),
15+
),
16+
} as const;
17+
18+
const meta = {
19+
component: SearchButtonComponent,
20+
argTypes: {
21+
size: {
22+
control: "inline-radio",
23+
options: SIZES,
24+
table: {
25+
category: "Variant",
26+
},
27+
},
28+
beforeIcon: {
29+
...iconControl,
30+
table: {
31+
category: "Contents",
32+
},
33+
},
34+
},
35+
} satisfies Meta<typeof SearchButtonComponent>;
36+
export default meta;
37+
38+
export const SearchButton = {
39+
args: {
40+
largeScreenContent: "Search",
41+
smallScreenContent: "Search",
42+
size: "sm",
43+
},
44+
} satisfies StoryObj<typeof SearchButtonComponent>;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"use client";
2+
3+
import { MagnifyingGlass } from "@phosphor-icons/react/dist/ssr/MagnifyingGlass";
4+
import clsx from "clsx";
5+
import type { ReactNode } from "react";
6+
import { useMemo } from "react";
7+
import { useIsSSR } from "react-aria";
8+
import { Button as BaseButton } from "react-aria-components";
9+
10+
import type { Props as ButtonProps } from "../Button";
11+
import { Button } from "../Button";
12+
import { Skeleton } from "../Skeleton";
13+
import styles from "./index.module.scss";
14+
15+
type OwnProps = {
16+
largeScreenContent?: ReactNode;
17+
smallScreenContent?: ReactNode;
18+
};
19+
20+
type Props = Pick<
21+
ButtonProps<typeof BaseButton>,
22+
"beforeIcon" | "size" | "onClick" | "className" | "isPending"
23+
> &
24+
OwnProps;
25+
26+
const SearchShortcutText = () => {
27+
const isSSR = useIsSSR();
28+
return isSSR ? <Skeleton width={7} /> : <SearchTextImpl />;
29+
};
30+
31+
const SearchTextImpl = () => {
32+
const isMac = useMemo(() => navigator.userAgent.includes("Mac"), []);
33+
return isMac ? "⌘ K" : "Ctrl K";
34+
};
35+
36+
export const SearchButton = ({
37+
beforeIcon,
38+
largeScreenContent,
39+
smallScreenContent,
40+
...props
41+
}: Props) => {
42+
return (
43+
<div className={styles.searchButton}>
44+
<Button
45+
className={clsx(styles.largeScreenSearchButton, props.className)}
46+
variant="outline"
47+
beforeIcon={beforeIcon ?? <MagnifyingGlass />}
48+
size="sm"
49+
rounded
50+
{...props}
51+
>
52+
{largeScreenContent ?? <SearchShortcutText />}
53+
</Button>
54+
<Button
55+
className={clsx(styles.smallScreenSearchButton, props.className)}
56+
hideText
57+
variant="ghost"
58+
beforeIcon={beforeIcon ?? <MagnifyingGlass />}
59+
size="sm"
60+
rounded
61+
{...props}
62+
>
63+
{smallScreenContent ?? <SearchShortcutText />}
64+
</Button>
65+
</div>
66+
);
67+
};

0 commit comments

Comments
 (0)