From e1539b5f5fcf0cc2dd6b9206ea6439c470cc9b0b Mon Sep 17 00:00:00 2001 From: Tuval Simha Date: Sun, 18 Aug 2024 17:33:37 +0300 Subject: [PATCH] Drop V2: Tabs component (#5229) --- .../src/components/layouts/organization.tsx | 32 ++++---- .../app/src/components/layouts/project.tsx | 22 +++--- .../web/app/src/components/layouts/target.tsx | 38 ++++----- packages/web/app/src/components/ui/tabs.tsx | 77 ++++++++++++++----- packages/web/app/src/components/v2/tabs.tsx | 52 ------------- packages/web/app/src/stories/tabs.stories.tsx | 64 +++++++++++++++ 6 files changed, 166 insertions(+), 119 deletions(-) delete mode 100644 packages/web/app/src/components/v2/tabs.tsx create mode 100644 packages/web/app/src/stories/tabs.stories.tsx diff --git a/packages/web/app/src/components/layouts/organization.tsx b/packages/web/app/src/components/layouts/organization.tsx index 8aa28be49..e66d3ab79 100644 --- a/packages/web/app/src/components/layouts/organization.tsx +++ b/packages/web/app/src/components/layouts/organization.tsx @@ -24,7 +24,6 @@ import { Input } from '@/components/ui/input'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { useToast } from '@/components/ui/use-toast'; import { UserMenu } from '@/components/ui/user-menu'; -import { Tabs } from '@/components/v2/tabs'; import { env } from '@/env/frontend'; import { graphql, useFragment } from '@/gql'; import { ProjectType } from '@/gql/graphql'; @@ -44,6 +43,7 @@ import { RateLimitWarn } from '../organization/billing/RateLimitWarn'; import { HiveLink } from '../ui/hive-link'; import { PlusIcon } from '../ui/icon'; import { QueryError } from '../ui/query-error'; +import { Tabs, TabsList, TabsTrigger } from '../ui/tabs'; import { OrganizationSelector } from './organization-selectors'; export enum Page { @@ -144,18 +144,18 @@ export function OrganizationLayout({
{currentOrganization && meInCurrentOrg ? ( - - - + + + Overview - + {canAccessOrganization(OrganizationAccessScope.Members, meInCurrentOrg) && ( - + Members - + )} {canAccessOrganization(OrganizationAccessScope.Settings, meInCurrentOrg) && ( <> - + Policy - - + + Settings - + )} {canAccessOrganization(OrganizationAccessScope.Read, meInCurrentOrg) && env.zendeskSupport && ( - + Support - + )} {getIsStripeEnabled() && canAccessOrganization(OrganizationAccessScope.Settings, meInCurrentOrg) && ( - + Subscription - + )} - + ) : (
diff --git a/packages/web/app/src/components/layouts/project.tsx b/packages/web/app/src/components/layouts/project.tsx index dbf677dce..31b68a0b0 100644 --- a/packages/web/app/src/components/layouts/project.tsx +++ b/packages/web/app/src/components/layouts/project.tsx @@ -15,7 +15,6 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from '@/component import { Input } from '@/components/ui/input'; import { useToast } from '@/components/ui/use-toast'; import { UserMenu } from '@/components/ui/user-menu'; -import { Tabs } from '@/components/v2/tabs'; import { graphql } from '@/gql'; import { canAccessProject, ProjectAccessScope, useProjectAccess } from '@/lib/access/project'; import { useToggle } from '@/lib/hooks'; @@ -25,6 +24,7 @@ import { Link, useRouter } from '@tanstack/react-router'; import { ProjectMigrationToast } from '../project/migration-toast'; import { HiveLink } from '../ui/hive-link'; import { PlusIcon } from '../ui/icon'; +import { Tabs, TabsList, TabsTrigger } from '../ui/tabs'; import { ProjectSelector } from './project-selector'; export enum Page { @@ -130,8 +130,8 @@ export function ProjectLayout({
{currentOrganization && currentProject ? ( - - + + Targets - + {canAccessProject(ProjectAccessScope.Alerts, currentOrganization.me) && ( - + Alerts - + )} {canAccessProject(ProjectAccessScope.Settings, currentOrganization.me) && ( <> - + Policy - - + + Settings - + )} - + ) : (
diff --git a/packages/web/app/src/components/layouts/target.tsx b/packages/web/app/src/components/layouts/target.tsx index 5bea82359..127ac3664 100644 --- a/packages/web/app/src/components/layouts/target.tsx +++ b/packages/web/app/src/components/layouts/target.tsx @@ -22,7 +22,6 @@ import { } from '@/components/ui/select'; import { UserMenu } from '@/components/ui/user-menu'; import { CopyValue, Tag } from '@/components/v2'; -import { Tabs } from '@/components/v2/tabs'; import { graphql } from '@/gql'; import { ProjectType } from '@/gql/graphql'; import { canAccessTarget, TargetAccessScope, useTargetAccess } from '@/lib/access/target'; @@ -32,6 +31,7 @@ import { useLastVisitedOrganizationWriter } from '@/lib/last-visited-org'; import { cn } from '@/lib/utils'; import { Link } from '@tanstack/react-router'; import { ProjectMigrationToast } from '../project/migration-toast'; +import { Tabs, TabsList, TabsTrigger } from '../ui/tabs'; import { TargetSelector } from './target-selector'; export enum Page { @@ -168,10 +168,10 @@ export const TargetLayout = ({
{currentOrganization && currentProject && currentTarget ? ( - + {canAccessSchema && ( <> - + Schema - - + + Checks - - + + Explorer - - + + History - - + + Insights - + {currentOrganization.isAppDeploymentsEnabled && ( - + Apps - + )} - + Laboratory - + )} {canAccessSettings && ( - + Settings - + )} - + ) : (
diff --git a/packages/web/app/src/components/ui/tabs.tsx b/packages/web/app/src/components/ui/tabs.tsx index 977042dfa..cd50d9605 100644 --- a/packages/web/app/src/components/ui/tabs.tsx +++ b/packages/web/app/src/components/ui/tabs.tsx @@ -1,21 +1,61 @@ -'use client'; - import React from 'react'; +import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/utils'; import * as TabsPrimitive from '@radix-ui/react-tabs'; +// Define variants for TabsList +const tabsListVariants = cva('relative flex items-center', { + variants: { + variant: { + default: + 'bg-muted text-muted-foreground inline-flex h-10 items-center justify-center rounded-md p-1', + menu: 'text-gray-700', + }, + }, + defaultVariants: { + variant: 'default', + }, +}); + +// Define variants for TabsTrigger +const tabsTriggerVariants = cva('cursor-pointer !appearance-none text-sm font-medium transition', { + variants: { + variant: { + default: + 'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 data-[state=active]:shadow-sm disabled:cursor-not-allowed active:disabled:pointer-events-none', + menu: 'text-white radix-state-active:border-b-orange-500 border-b-2 border-b-transparent px-4 py-3 hover:border-b-orange-900', + }, + }, + defaultVariants: { + variant: 'default', + }, +}); + +// Define variants for TabsContent +const tabsContentVariants = cva( + 'ring-offset-background focus-visible:ring-ring mt-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2', + { + variants: { + variant: { + default: 'py-7', + menu: '', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +); + const Tabs = TabsPrimitive.Root; const TabsList = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( + React.ComponentPropsWithoutRef & VariantProps +>(({ className, variant, ...props }, ref) => ( )); @@ -23,15 +63,12 @@ TabsList.displayName = TabsPrimitive.List.displayName; const TabsTrigger = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( + React.ComponentPropsWithoutRef & + VariantProps & { hasBorder?: boolean } +>(({ className, variant, hasBorder = true, ...props }, ref) => ( )); @@ -39,14 +76,12 @@ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; const TabsContent = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, ...props }, ref) => ( )); diff --git a/packages/web/app/src/components/v2/tabs.tsx b/packages/web/app/src/components/v2/tabs.tsx deleted file mode 100644 index c0138fb25..000000000 --- a/packages/web/app/src/components/v2/tabs.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { forwardRef, ReactElement } from 'react'; -import clsx from 'clsx'; -import { - Root, - Content as TabsContent, - TabsContentProps, - List as TabsList, - TabsListProps, - Trigger as TabsTrigger, - TabsTriggerProps, -} from '@radix-ui/react-tabs'; - -const List = ({ children, className, ...props }: TabsListProps): ReactElement => ( - - {children} - -); - -const Trigger = forwardRef & { hasBorder?: boolean }>( - ({ children, hasBorder = true, ...props }, forwardedRef /* when has asChild prop */) => ( - - {children} - - ), -); - -const Content = ({ - children, - className, - noPadding, - ...props -}: TabsContentProps & { noPadding?: boolean }): ReactElement => ( - - {children} - -); - -export const Tabs = Object.assign(Root, { - Content, - Trigger, - List, -}); diff --git a/packages/web/app/src/stories/tabs.stories.tsx b/packages/web/app/src/stories/tabs.stories.tsx new file mode 100644 index 000000000..346109d4d --- /dev/null +++ b/packages/web/app/src/stories/tabs.stories.tsx @@ -0,0 +1,64 @@ +import { MouseEvent, useCallback, useState } from 'react'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Meta, StoryObj } from '@storybook/react'; + +const meta: Meta = { + title: 'Components/Tabs', + component: Template, + argTypes: { + variant: { + control: { type: 'inline-radio' }, + options: ['default', 'menu'], + }, + tabs: { + control: { type: 'object' }, + }, + }, + args: { + tabs: ['Overview', 'Members', 'Policy', 'Settings', 'Support', 'Subscription'], + variant: 'default', + }, +}; + +export default meta; + +type Story = StoryObj; + +function Template({ tabs, variant }: { tabs: string[]; variant: 'default' | 'menu' }) { + const [page, setPage] = useState(tabs[0]); + + const handleClick = useCallback((event: MouseEvent) => { + setPage(event.currentTarget.dataset.value!); + }, []); + + return ( + + + {tabs.map(value => ( + + {value} + + ))} + + {tabs.map(value => ( + + {value} Tab Content + + ))} + + ); +} + +export const Default: Story = {}; + +export const Menu: Story = { + args: { + variant: 'menu', + }, +};