Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

Commit d2cfca6

Browse files
committed
feat: refactored Navbar by implementing NavbarGeneric
1 parent a8a423f commit d2cfca6

18 files changed

+417
-66
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"dev": "pnpm storybook",
1313
"test": "test-storybook",
1414
"build": "tsc && vite build",
15+
"build:watch": "tsc && vite build --watch",
1516
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
1617
"format": "prettier --write --ignore-unknown .",
1718
"preview": "vite",

src/primitives/Navbar/Navbar.mdx src/components/Navbar/Navbar.mdx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Meta, Title, Primary, Stories, Controls, Story } from "@storybook/blocks";
2+
23
import { Navbar } from "./Navbar";
34
import * as NavbarStories from "./Navbar.stories";
45

@@ -20,4 +21,4 @@ The Navbar component displays a navigation bar with optional logos and text. It
2021

2122
# Other variations
2223

23-
<Stories/>
24+
<Stories />

src/primitives/Navbar/Navbar.stories.tsx src/components/Navbar/Navbar.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Navbar } from "./Navbar";
99
// Adjust the path as needed
1010

1111
const meta = {
12-
title: "Primitives/Navbar",
12+
title: "Components/Navbar",
1313
component: Navbar,
1414
args: {
1515
text: {

src/components/Navbar/Navbar.tsx

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
5+
import { NavbarGeneric } from "./components/NavbarGeneric";
6+
import { NavbarLogo, NavbarLogoProps } from "./components/NavbarLogo";
7+
import {
8+
NavbarCenterSection,
9+
NavbarEndSection,
10+
NavbarStartSection,
11+
} from "./components/NavbarSections";
12+
import { NavbarSeparator } from "./components/NavbarSeparator";
13+
import { NavbarTitle, NavbarTitleProps } from "./components/NavbarTitle";
14+
15+
export interface NavbarProps {
16+
className?: string;
17+
primaryLogo?: NavbarLogoProps;
18+
secondaryLogo?: NavbarLogoProps;
19+
showDivider?: boolean;
20+
text?: NavbarTitleProps;
21+
children?: React.ReactNode;
22+
}
23+
24+
export const Navbar = ({
25+
className,
26+
primaryLogo,
27+
secondaryLogo,
28+
showDivider = true,
29+
text,
30+
children,
31+
}: NavbarProps) => {
32+
return (
33+
<NavbarGeneric behavior="sticky" className={className}>
34+
<NavbarStartSection>
35+
<NavbarLogo {...primaryLogo} />
36+
{showDivider && <NavbarSeparator />}
37+
<div className="flex items-center gap-2">
38+
{secondaryLogo && <NavbarLogo {...secondaryLogo} />}
39+
{text && <NavbarTitle {...text} />}
40+
</div>
41+
</NavbarStartSection>
42+
<NavbarCenterSection>{"hola"}</NavbarCenterSection>
43+
<NavbarEndSection>{children}</NavbarEndSection>
44+
</NavbarGeneric>
45+
);
46+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Meta, Story, Canvas, Controls } from "@storybook/blocks";
2+
3+
import * as NavbarGenericStories from "./NavbarGeneric.stories";
4+
5+
<Meta of={NavbarGenericStories} />
6+
7+
# NavbarGeneric
8+
9+
A flexible navbar component that handles layout and positioning of navbar sections.
10+
11+
Key features:
12+
13+
- Section order doesn't matter - they'll always render in Start, Center, End order
14+
- Sections can be omitted - empty sections will be created automatically
15+
- Configurable behavior (static, sticky, fixed)
16+
17+
## Usage
18+
19+
```tsx
20+
import {
21+
NavbarGeneric,
22+
NavbarStartSection,
23+
NavbarCenterSection,
24+
NavbarEndSection,
25+
} from "@/components/Navbar";
26+
27+
export const MyNavbar = () => (
28+
<NavbarGeneric>
29+
<NavbarStartSection>
30+
<Logo />
31+
</NavbarStartSection>
32+
<NavbarCenterSection>
33+
<Navigation />
34+
</NavbarCenterSection>
35+
<NavbarEndSection>
36+
<UserMenu />
37+
</NavbarEndSection>
38+
</NavbarGeneric>
39+
);
40+
```
41+
42+
```tsx
43+
import {
44+
NavbarGeneric,
45+
NavbarStartSection,
46+
NavbarCenterSection,
47+
NavbarEndSection,
48+
} from "@/components/Navbar";
49+
50+
export const MyNavbar = () => (
51+
<NavbarGeneric behavior="sticky">
52+
{/* Order doesn't matter */}
53+
<NavbarEndSection>End</NavbarEndSection>
54+
<NavbarStartSection>Start</NavbarStartSection>
55+
56+
{/* Center section can be omitted */}
57+
</NavbarGeneric>
58+
);
59+
```
60+
61+
## Props
62+
63+
<Controls />
64+
65+
### Default
66+
67+
<Canvas of={NavbarGenericStories.Default} />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import * as React from "react";
2+
3+
import type { Meta, StoryObj } from "@storybook/react";
4+
5+
import { NavbarGeneric } from "./NavbarGeneric";
6+
import { NavbarCenterSection, NavbarEndSection, NavbarStartSection } from "./NavbarSections";
7+
8+
const meta = {
9+
title: "Components/NavbarGeneric",
10+
component: NavbarGeneric,
11+
argTypes: {
12+
behavior: {
13+
control: "radio",
14+
options: ["static", "sticky", "fixed"],
15+
description: "Behavior of the navbar",
16+
table: {
17+
defaultValue: { summary: "sticky" },
18+
},
19+
},
20+
variant: {
21+
control: "radio",
22+
options: ["default"],
23+
description: "Visual variant of the navbar",
24+
table: {
25+
defaultValue: { summary: "default" },
26+
},
27+
},
28+
},
29+
} satisfies Meta<typeof NavbarGeneric>;
30+
31+
export default meta;
32+
33+
type Story = StoryObj<typeof NavbarGeneric>;
34+
35+
const DefaultContent = () => (
36+
<>
37+
<NavbarStartSection>
38+
<div className="rounded-sm bg-grey-500 p-2">Start Content</div>
39+
</NavbarStartSection>
40+
<NavbarCenterSection>
41+
<div className="rounded-sm bg-grey-500 p-2">Center Content</div>
42+
</NavbarCenterSection>
43+
<NavbarEndSection>
44+
<div className="rounded-sm bg-grey-500 p-2">End Content</div>
45+
</NavbarEndSection>
46+
</>
47+
);
48+
49+
const ScrollDecorator = (Story: React.ComponentType) => (
50+
<div className="h-[200vh]">
51+
<Story />
52+
<div className="p-4">Scroll content</div>
53+
</div>
54+
);
55+
56+
export const Default: Story = {
57+
args: {
58+
children: <DefaultContent />,
59+
},
60+
};
61+
62+
export const Static: Story = {
63+
args: {
64+
behavior: "static",
65+
children: <DefaultContent />,
66+
},
67+
argTypes: {
68+
children: {
69+
table: {
70+
disable: true,
71+
},
72+
},
73+
},
74+
decorators: [ScrollDecorator],
75+
};
76+
77+
export const Sticky: Story = {
78+
args: {
79+
behavior: "sticky",
80+
children: <DefaultContent />,
81+
},
82+
argTypes: {
83+
children: {
84+
table: {
85+
disable: true,
86+
},
87+
},
88+
},
89+
decorators: [ScrollDecorator],
90+
};
91+
92+
export const Fixed: Story = {
93+
args: {
94+
behavior: "fixed",
95+
children: <DefaultContent />,
96+
},
97+
argTypes: {
98+
children: {
99+
table: {
100+
disable: true,
101+
},
102+
},
103+
},
104+
decorators: [ScrollDecorator],
105+
};
106+
107+
export const RandomOrder: Story = {
108+
render: (args) => (
109+
<NavbarGeneric {...args}>
110+
<NavbarEndSection>
111+
<div className="bg-grey-500 p-2">End First</div>
112+
</NavbarEndSection>
113+
<NavbarStartSection>
114+
<div className="bg-grey-500 p-2">Start Second</div>
115+
</NavbarStartSection>
116+
<NavbarCenterSection>
117+
<div className="bg-grey-500 p-2">Center Last</div>
118+
</NavbarCenterSection>
119+
</NavbarGeneric>
120+
),
121+
};
122+
123+
export const OmittedSections: Story = {
124+
render: (args) => (
125+
<NavbarGeneric {...args}>
126+
<NavbarStartSection>
127+
<div className="bg-grey-500 p-2">Only Start</div>
128+
</NavbarStartSection>
129+
<NavbarEndSection>
130+
<div className="bg-grey-500 p-2">And End</div>
131+
</NavbarEndSection>
132+
</NavbarGeneric>
133+
),
134+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
5+
import { tv, type VariantProps } from "tailwind-variants";
6+
7+
import { cn } from "@/lib";
8+
9+
import { filterAndSortChildren } from "../utils/filterAndSortChildren";
10+
11+
const navbarVariants = tv({
12+
base: "flex h-16 w-full items-center justify-between px-20 py-2.5",
13+
variants: {
14+
behavior: {
15+
static: "",
16+
sticky: "sticky top-0 z-50",
17+
fixed: "fixed left-0 right-0 top-0 z-50",
18+
},
19+
variant: {
20+
default: "bg-navbar shadow-navbar backdrop-blur-navbar",
21+
},
22+
},
23+
defaultVariants: {
24+
behavior: "sticky",
25+
variant: "default",
26+
},
27+
});
28+
29+
export interface NavbarGenericProps
30+
extends React.HTMLAttributes<HTMLElement>,
31+
VariantProps<typeof navbarVariants> {
32+
/** Children should be NavbarStartSection, NavbarCenterSection, or NavbarEndSection components */
33+
children?: React.ReactNode;
34+
/** Position of the navbar: static, sticky, or fixed */
35+
behavior?: "static" | "sticky" | "fixed";
36+
/** Visual variant of the navbar */
37+
variant?: "default";
38+
}
39+
40+
export const NavbarGeneric = React.forwardRef<HTMLElement, NavbarGenericProps>(
41+
({ className, behavior, variant, children, ...props }, ref) => {
42+
const navbarStyle = navbarVariants({ behavior, variant });
43+
const filteredChildren = filterAndSortChildren(children);
44+
return (
45+
<nav ref={ref} className={cn(navbarStyle, className)} {...props}>
46+
{filteredChildren}
47+
</nav>
48+
);
49+
},
50+
);
51+
NavbarGeneric.displayName = "NavbarGeneric";

src/primitives/Navbar/components/NavbarLogo.tsx src/components/Navbar/components/NavbarLogo.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
"use client";
2-
31
import { GitcoinLogo } from "@/assets/logos/gitcoin";
42

53
const DefaultLogo = GitcoinLogo;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as React from "react";
2+
3+
import { tv } from "tailwind-variants";
4+
5+
import { cn } from "@/lib";
6+
7+
const navbarSectionStyle = tv({
8+
base: "flex items-center gap-4",
9+
variants: {
10+
section: {
11+
start: "",
12+
center: "hidden md:block",
13+
end: "",
14+
},
15+
},
16+
});
17+
18+
export const NavbarStartSection = React.forwardRef<
19+
HTMLDivElement,
20+
React.HTMLAttributes<HTMLDivElement>
21+
>(({ className, ...props }, ref) => (
22+
<div ref={ref} className={cn(navbarSectionStyle({ section: "start" }), className)} {...props} />
23+
));
24+
NavbarStartSection.displayName = "NavbarStartSection";
25+
26+
export const NavbarCenterSection = React.forwardRef<
27+
HTMLDivElement,
28+
React.HTMLAttributes<HTMLDivElement>
29+
>(({ className, ...props }, ref) => (
30+
<div ref={ref} className={cn(navbarSectionStyle({ section: "center" }), className)} {...props} />
31+
));
32+
NavbarCenterSection.displayName = "NavbarCenterSection";
33+
34+
export const NavbarEndSection = React.forwardRef<
35+
HTMLDivElement,
36+
React.HTMLAttributes<HTMLDivElement>
37+
>(({ className, ...props }, ref) => (
38+
<div ref={ref} className={cn(navbarSectionStyle({ section: "end" }), className)} {...props} />
39+
));
40+
NavbarEndSection.displayName = "NavbarEndSection";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as React from "react";
2+
3+
import { tv } from "tailwind-variants";
4+
5+
import { cn } from "@/lib";
6+
7+
const separatorStyle = tv({
8+
base: "h-4 w-0 border-1.5 border-grey-900 bg-grey-900",
9+
});
10+
11+
const NavbarSeparator = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
12+
({ className, ...props }, ref) => (
13+
<div ref={ref} className={cn(separatorStyle(), className)} {...props} />
14+
),
15+
);
16+
NavbarSeparator.displayName = "NavbarSeparator";
17+
18+
export { NavbarSeparator };

0 commit comments

Comments
 (0)