Skip to content

feat: build the tabs component (primary and secondary) #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions apps/docs/.storybook/modeDecorator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { useState, useCallback } from 'react';
import { Sun, Moon } from 'lucide-react';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const ModeDecorator = (Story: any) => {
const [isDarkMode, setIsDarkMode] = useState(false);

const toggleMode = useCallback(() => {
setIsDarkMode(!isDarkMode);
document.documentElement.classList.toggle('dark', !isDarkMode);
}, [isDarkMode]);

return (
<div className={isDarkMode ? 'dark' : ''}>
<button
onClick={toggleMode}
className="fixed top-4 right-4 z-50 p-2 rounded-full
bg-background border border-border shadow-lg
hover:scale-105 transition-transform"
aria-label="Toggle dark mode"
>
{isDarkMode ? (
<Sun className="size-5 text-foreground" />
) : (
<Moon className="size-5 text-foreground" />
)}
</button>
<div className="bg-background text-foreground min-h-screen p-8">
<Story />
</div>
</div>
);
};
5 changes: 4 additions & 1 deletion apps/docs/.storybook/preview.css
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
@import "../index.css";
@import '../index.css';
:root {
--color-transition: 200ms cubic-bezier(0.4, 0, 0.2, 1);
}
22 changes: 13 additions & 9 deletions apps/docs/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import "./preview.css";
import './preview.css';
import { ModeDecorator } from './modeDecorator';

// Configure Storybook parameters
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
export const tags = ["autodocs"];

export const decorators = [ModeDecorator];

export const tags = ['autodocs'];
2 changes: 2 additions & 0 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"@signozhq/button": "workspace:*",
"@signozhq/design-tokens": "latest",
"@signozhq/input": "workspace:*",
"@signozhq/tabs": "workspace:*",
"@signozhq/tailwind-config": "workspace:*",
"lucide-react": "^0.477.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down
183 changes: 183 additions & 0 deletions apps/docs/stories/tabs.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import Tabs from '@signozhq/tabs'; // Corrected import path
import {
AlertCircle,
Component,
History,
LayoutGrid,
List,
Settings,
Settings2,
} from 'lucide-react';

const meta: Meta<typeof Tabs> = {
title: 'Components/Tabs',
component: Tabs,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof Tabs>;

const playgroundItems = [
// Primary variant items
{
key: 'overview',
label: 'Overview',
children: 'Overview content panel',
prefixIcon: <Settings2 className="size-4" />,
},
{
key: 'issues',
label: 'Issues (Disabled)',
children: 'Issues content panel',
disabled: true,
prefixIcon: <AlertCircle className="size-4" />,
},
{
key: 'history',
label: 'History',
children: 'History content panel',
suffixIcon: <History className="size-4" />,
},
{
key: 'another',
label: 'Another Tab',
children: 'Another content panel',
},
{
key: 'all-endpoints',
label: 'All Endpoints',
children: 'Endpoints list panel',
variant: 'secondary',
},
{
key: 'details',
label: 'Endpoint Details',
children: 'Details content panel',
variant: 'secondary',
},
{
key: 'settings',
label: 'Settings',
children: 'Settings content panel',
variant: 'secondary',
prefixIcon: <Settings className="size-4" />,
disabled: true,
},
];

export const Default: Story = {
render: () => (
<div className="space-y-8">
<div>
<h2 className="mb-4 text-lg font-semibold">Primary Variant</h2>
<Tabs
items={playgroundItems.filter((i) => i.variant !== 'secondary')}
variant="primary"
defaultValue="overview"
/>
</div>

<div>
<h2 className="mb-4 text-lg font-semibold">Secondary Variant</h2>
<Tabs
items={playgroundItems
.filter((i) => i.variant === 'secondary')
.map((i) => ({ ...i, variant: undefined }))}
variant="secondary"
defaultValue="all-endpoints"
/>
</div>

<div>
<h2 className="mb-4 text-lg font-semibold">With Icons</h2>
<Tabs
items={[
{
key: 'apps',
label: 'Applications',
children: 'Applications list',
prefixIcon: <LayoutGrid className="size-4" />,
suffixIcon: <List className="size-4" />,
},
{
key: 'modules',
label: 'Modules',
children: 'Modules content',
prefixIcon: <Component className="size-4" />,
},
]}
variant="primary"
defaultValue="apps"
/>
</div>
</div>
),
};

const primaryItems = [
{
key: 'overview',
label: 'Overview',
children: 'Overview content',
prefixIcon: <Settings className="size-4" />,
},
{
key: 'issues',
label: 'Issues',
children: 'Issues content',
disabled: true,
prefixIcon: <AlertCircle className="size-4" />,
},
{
key: 'history',
label: 'History',
children: 'History content',
suffixIcon: <History className="size-4" />,
},
];

const secondaryItems = [
{ key: 'all', label: 'All Endpoints', children: 'All endpoints content' },
{
key: 'details',
label: 'Endpoint Details',
children: 'Endpoint details content',
},
];

export const Primary: Story = {
args: {
items: primaryItems,
variant: 'primary',
defaultValue: 'overview', // Added defaultValue for clarity
},
};

export const Secondary: Story = {
args: {
items: secondaryItems,
variant: 'secondary',
defaultValue: 'all', // Added defaultValue for clarity
},
};

export const DisabledState: Story = {
args: {
items: [
...primaryItems.slice(0, 2), // Keep first two items
{
key: 'disabled',
label: 'Disabled Tab',
children: 'Disabled content',
disabled: true,
},
primaryItems[2], // Add history back
],
variant: 'primary',
defaultValue: 'overview', // Added defaultValue for clarity
},
};
2 changes: 2 additions & 0 deletions apps/packages-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
},
"dependencies": {
"@signozhq/button": "workspace:*",
"@signozhq/tabs": "workspace:*",
"@signozhq/theme": "workspace:*",
"@signozhq/design-tokens": "latest",
"lucide-react": "^0.503.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand Down
10 changes: 7 additions & 3 deletions apps/packages-playground/src/App.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

.app.light {
background: #fff;
}
.app {
padding: 2rem;
}
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
margin: 2rem;
}

.logo {
Expand Down
Loading