Skip to content
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

Feat/fct 16897/new tab layout #360

Merged
merged 22 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 17 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
4 changes: 2 additions & 2 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const config: StorybookConfig = {
titlePrefix: "Components",
},
{
directory: "../src/examples",
titlePrefix: "Examples",
directory: "../src/playground",
titlePrefix: "Playground",
},
{
directory: "../src/experiments",
Expand Down
25 changes: 25 additions & 0 deletions lib/components/Blocks/Header/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Meta, StoryObj } from "@storybook/react"
import { Header } from "."

const meta = {
component: Header,
tags: ["autodocs"],
args: {
title: "Alba Horneros",
subtitle: "Product Designer",
src: "https://github.com/dani-moreno.png",
alt: "DM",
},
} satisfies Meta<typeof Header>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
RamProg marked this conversation as resolved.
Show resolved Hide resolved
args: {
title: "Alba Horneros",
subtitle: "Product Designer",
src: "https://github.com/dani-moreno.png",
alt: "DM",
},
}
23 changes: 23 additions & 0 deletions lib/components/Blocks/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Avatar } from "@/components/Information/Avatar"
import { forwardRef } from "react"

interface HeaderProps {
title: string
subtitle: string
src: string
alt: string
}

export const Header = forwardRef<HTMLDivElement, HeaderProps>(
({ title, subtitle, src, alt }, ref) => {
return (
<div className="flex px-10 py-6" ref={ref}>
<Avatar size="xlarge" src={src} alt={alt} />
<div className="flex flex-col gap-2 pl-5">
<h1 className="pt-2 text-2xl font-medium text-foreground">{title}</h1>
<h2 className="text-lg font-normal text-intermediate">{subtitle}</h2>
</div>
</div>
)
}
)
33 changes: 7 additions & 26 deletions lib/components/Information/Avatar/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react"

import { Avatar, AvatarFallback, AvatarImage, sizes } from "@/ui/avatar"
import { Avatar } from "."

const meta = {
component: Avatar,
Expand All @@ -10,13 +10,14 @@ const meta = {
tags: ["autodocs"],
args: {
size: "medium",
src: "https://github.com/dani-moreno.png",
RamProg marked this conversation as resolved.
Show resolved Hide resolved
alt: "DM",
},
argTypes: {
size: {
control: {
type: "select",
},
options: [...sizes],
},
},
} satisfies Meta<typeof Avatar>
Expand All @@ -25,33 +26,13 @@ export default meta
type Story = StoryObj<typeof meta>

export const Basic: Story = {
render({ size }) {
return (
<Avatar size={size}>
<AvatarImage
src="https://github.com/dani-moreno.png"
alt="@dani-moreno"
/>
<AvatarFallback>DM</AvatarFallback>
</Avatar>
)
render({ size, src, alt }) {
return <Avatar size={size} src={src} alt={alt} />
},
}

Basic.args = {
size: "medium",
}

export const Fallback: Story = {
render({ size }) {
return (
<Avatar size={size}>
<AvatarFallback>DM</AvatarFallback>
</Avatar>
)
render({ size, alt }) {
return <Avatar size={size} alt={alt} />
},
}

Fallback.args = {
size: "medium",
}
24 changes: 23 additions & 1 deletion lib/components/Information/Avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
export * from "@/ui/avatar"
import {
Avatar as AvatarComponent,
AvatarFallback,
AvatarImage,
} from "@/ui/avatar"
import { forwardRef } from "react"

interface AvatarType {
alt: string
src?: string
size?: "medium" | "small" | "xsmall" | "large" | "xlarge" | "xxlarge"
}
RamProg marked this conversation as resolved.
Show resolved Hide resolved
RamProg marked this conversation as resolved.
Show resolved Hide resolved

export const Avatar = forwardRef<HTMLDivElement, AvatarType>(
({ src, alt, size }, ref) => {
return (
<AvatarComponent size={size} ref={ref}>
<AvatarImage src={src} alt={alt} />
<AvatarFallback>{alt}</AvatarFallback>
</AvatarComponent>
)
}
)
20 changes: 20 additions & 0 deletions lib/components/Navigation/Breadcrumb/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Meta, StoryObj } from "@storybook/react"

import { User } from "@/icons"
import { Breadcrumb } from "."

const meta = {
component: Breadcrumb,
tags: ["autodocs"],
} satisfies Meta<typeof Breadcrumb>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
route: "Employees",
title: "Alba Horneros",
icon: User,
},
}
29 changes: 29 additions & 0 deletions lib/components/Navigation/Breadcrumb/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Icon, IconType } from "@/components/Utilities/Icon"
import { ChevronRight } from "@/icons"
import { forwardRef } from "react"

interface BreadcrumbsType {
icon: IconType
route: string
title: string
}

export const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbsType>(
RamProg marked this conversation as resolved.
Show resolved Hide resolved
({ icon, route, title }, ref) => {
return (
<div
ref={ref}
className="flex items-center gap-1 px-5 py-3 text-[0.81rem]/5"
>
<span className="flex items-center text-primary-foreground">
<Icon size="md" icon={icon} />
</span>
<p className="text-foreground">{route}</p>
<span className="flex items-center text-secondary-foreground">
<Icon size="sm" icon={ChevronRight} />
</span>
<p className="text-intermediate">{title}</p>
</div>
)
}
)
76 changes: 76 additions & 0 deletions src/playground/Pages/Tabs/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { AreaChartWidget } from "@/components/Widgets/Charts/AreaChartWidget"
RamProg marked this conversation as resolved.
Show resolved Hide resolved
import AreaChartWidgetStoriesMeta from "@/components/Widgets/Charts/AreaChartWidget/index.stories"
import { LineChartWidget } from "@/components/Widgets/Charts/LineChartWidget"
import LineChartWidgetStoriesMeta from "@/components/Widgets/Charts/LineChartWidget/index.stories"
import { PieChartWidget } from "@/components/Widgets/Charts/PieChartWidget"
import PieChartWidgetStoriesMeta from "@/components/Widgets/Charts/PieChartWidget/index.stories"
import { VerticalBarChartWidget } from "@/components/Widgets/Charts/VerticalBarChartWidget"
import VerticalBarChartWidgetStoriesMeta from "@/components/Widgets/Charts/VerticalBarChartWidget/index.stories"
import { Dashboard } from "@/components/Widgets/Dashboard"
import type { Meta, StoryObj } from "@storybook/react"
import { Tabs } from "."

const meta = {
component: Tabs,
tags: ["autodocs"],
} satisfies Meta<typeof Tabs>

export default meta
type Story = StoryObj<typeof meta>

const renderWidget = (index: number) => {
const Widgets = [
() => <AreaChartWidget {...AreaChartWidgetStoriesMeta.args} />,
() => <LineChartWidget {...LineChartWidgetStoriesMeta.args} />,
() => <PieChartWidget {...PieChartWidgetStoriesMeta.args} />,
() => (
<VerticalBarChartWidget {...VerticalBarChartWidgetStoriesMeta.args} />
),
]

const Component = Widgets[index % Widgets.length]
return <Component />
}

const Overview = () => (
<div className="grid grid-cols-[2fr_1fr] divide-x divide-y-0 divide-dashed divide-muted">
<div className="pl-10 pr-8 pt-6">
<Dashboard>
<>{Array.from({ length: 6 }).map((_, i) => renderWidget(i))}</>

Check failure on line 39 in src/playground/Pages/Tabs/index.stories.tsx

View workflow job for this annotation

GitHub Actions / test

Type 'ReactElement<any, any>' is missing the following properties from type 'ReactNode[]': length, pop, push, concat, and 29 more.
RamProg marked this conversation as resolved.
Show resolved Hide resolved
</Dashboard>
</div>
<div className="pl-8 pr-10 pt-6">
Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2
Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2
Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2
Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2
Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2 Column 2
Column 2 Column 2 Column 2 Column 2 Column 2 Column 2
</div>
</div>
)

const Profile = () => <div>Profile</div>
const Personal = () => <div>Personal</div>
const Agreement = () => <div>Agreement</div>
const TimeOff = () => <div>Time off</div>
const Competencies = () => <div>Competencies</div>
const Activity = () => <div>Activity</div>

export const Default: Story = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you feel about calling this whole story EmployeeProfile to make it more explicit? Tabs looks too generic to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was aiming for generic, my reasoning was that the same layout could be reused in a different screen.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand! The thing is that this would make sense once we promote this to a component. Then we can find a meaningful name. If it lives in the playground, I think it's probably better to put it a very explicit name just to convey that's an "example".

I mean obviously not a blocker!

args: {
tabs: [
{ name: "Overview", key: "overview", content: <Overview /> },
{ name: "Profile", key: "profile", content: <Profile /> },
{ name: "Personal", key: "personal", content: <Personal /> },
{ name: "Agreement", key: "agreement", content: <Agreement /> },
{ name: "Time off", key: "time-off", content: <TimeOff /> },
{ name: "Competencies", key: "competencies", content: <Competencies /> },
{ name: "Activity", key: "activity", content: <Activity /> },
],
title: "Alba Horneros",
subtitle: "Product Designer",
src: "https://github.com/dani-moreno.png",
alt: "DM",
},
}
54 changes: 54 additions & 0 deletions src/playground/Pages/Tabs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Header } from "@/components/Blocks/Header"
import { Breadcrumb } from "@/components/Navigation/Breadcrumb"
import {
Tabs as TabsComponent,
TabsContent,
TabsList,
TabsTrigger,
} from "@/components/Navigation/Tabs"
RamProg marked this conversation as resolved.
Show resolved Hide resolved
import { User } from "@/icons"
import React, { forwardRef } from "react"

interface TabsProps {
tabs: TabType[]
title: string
subtitle: string
src: string
alt: string
defaultTab?: string
}

interface TabType {
name: string
key: string
content: React.ReactNode
}

export const Tabs = forwardRef<HTMLDivElement, TabsProps>(
({ tabs, title, subtitle, src, alt, defaultTab = tabs[0].key }, ref) => (
<main ref={ref}>
<Breadcrumb icon={User} route={"Employees"} title={"Alba Horneros"} />
<Header title={title} subtitle={subtitle} src={src} alt={alt}></Header>
<TabsComponent defaultValue={defaultTab}>
<TabsList className="h-auto w-full justify-start rounded-none border-b border-l-0 border-r-0 border-t-0 border-solid border-b-muted bg-transparent px-10 py-3">
{tabs.map((tab: TabType) => (
<TabsTrigger
RamProg marked this conversation as resolved.
Show resolved Hide resolved
className="flex text-intermediate data-[state=active]:rounded-lg data-[state=active]:bg-muted data-[state=active]:text-foreground data-[state=active]:underline data-[state=active]:underline-offset-[1.45rem] data-[state=active]:shadow-none"
value={tab.key}
key={tab.key}
>
{tab.name}
</TabsTrigger>
))}
</TabsList>
<div>
{tabs.map((tab: TabType) => (
<TabsContent value={tab.key} key={tab.key}>
{tab.content}
</TabsContent>
))}
</div>
</TabsComponent>
</main>
)
)
Loading