2
0

first commit

This commit is contained in:
2024-08-09 00:39:27 +02:00
commit 79688abe2e
5698 changed files with 497838 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
import { classNames } from "@calcom/lib";
interface TableProps {
children: React.ReactNode;
}
interface DynamicWidth {
widthClassNames?: string;
}
const Header = ({ children }: TableProps) => (
<thead className="rounded-md">
<tr className="bg-default">{children}</tr>
</thead>
);
const ColumnTitle = ({ children, widthClassNames }: TableProps & DynamicWidth) => (
<th
scope="col"
className={classNames(
"text-default p-3 text-left text-xs font-medium uppercase",
!widthClassNames ? "w-auto" : widthClassNames
)}>
{children}
</th>
);
const Body = ({ children }: TableProps) => (
<tbody className="divide-subtle divide-y rounded-md">{children}</tbody>
);
const Row = ({ children }: TableProps) => <tr>{children}</tr>;
const Cell = ({ children, widthClassNames }: TableProps & DynamicWidth) => (
<td
className={classNames(
"text-default relative px-3 py-2 text-sm font-medium",
!widthClassNames ? "w-auto" : widthClassNames
)}>
{children}
</td>
);
export const Table = ({ children }: TableProps) => (
<div className="bg-default border-subtle overflow-x-auto overflow-y-hidden rounded-md border">
<table className="divide-subtle w-full divide-y rounded-md">{children}</table>
</div>
);
Table.Header = Header;
Table.ColumnTitle = ColumnTitle;
Table.Body = Body;
Table.Row = Row;
Table.Cell = Cell;

View File

@@ -0,0 +1,104 @@
import type { FC } from "react";
import React from "react";
import type { IconName } from "../..";
import type { ButtonBaseProps } from "../button";
import { Button } from "../button";
import {
Dropdown,
DropdownItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuTrigger,
} from "../form/dropdown";
export type ActionType = {
id: string;
icon?: IconName;
iconClassName?: string;
label: string;
disabled?: boolean;
color?: ButtonBaseProps["color"];
bookingId?: number;
} & (
| { href: string; onClick?: never; actions?: never }
| { href?: never; onClick: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void; actions?: never }
| { actions?: ActionType[]; href?: never; onClick?: never }
);
interface Props {
actions: ActionType[];
}
const defaultAction = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.stopPropagation();
};
export const DropdownActions = ({
actions,
actionTrigger,
}: {
actions: ActionType[];
actionTrigger?: React.ReactNode;
}) => {
return (
<Dropdown>
{!actionTrigger ? (
<DropdownMenuTrigger asChild>
<Button type="button" color="secondary" variant="icon" StartIcon="ellipsis" />
</DropdownMenuTrigger>
) : (
<DropdownMenuTrigger asChild>{actionTrigger}</DropdownMenuTrigger>
)}
<DropdownMenuPortal>
<DropdownMenuContent>
{actions.map((action) => (
<DropdownMenuItem key={action.id}>
<DropdownItem
type="button"
color={action.color}
data-testid={action.id}
StartIcon={action.icon}
href={action.href}
data-bookingid={action.bookingId}
onClick={action.onClick || defaultAction}>
{action.label}
</DropdownItem>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
);
};
export const TableActions: FC<Props> = ({ actions }) => {
return (
<>
<div className="flex space-x-2 rtl:space-x-reverse">
{actions.map((action) => {
const button = (
<Button
className="whitespace-nowrap"
key={action.id}
data-testid={action.id}
href={action.href}
onClick={action.onClick || defaultAction}
StartIcon={action.icon}
{...(action?.actions ? { EndIcon: "chevron-down" } : null)}
disabled={action.disabled}
data-bookingid={action.bookingId}
color={action.color || "secondary"}>
{action.label}
</Button>
);
if (!action.actions) {
return button;
}
return <DropdownActions key={action.id} actions={action.actions} actionTrigger={button} />;
})}
</div>
</>
);
};

View File

@@ -0,0 +1,83 @@
import { Table } from "./Table";
import { TableActions } from "./TableActions";
import {
Table as TableNew,
TableHeader,
TableRow,
TableHead,
TableBody,
TableCell,
TableFooter,
TableCaption,
} from "./TableNew";
export const TableNewExampleComponent = () => (
<TableNew>
<TableHeader>
<TableRow>
<TableHead>Header Column 1</TableHead>
<TableHead>Header Column 2</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Row 1, Cell 1</TableCell>
<TableCell>Row 1, Cell 2</TableCell>
</TableRow>
<TableRow>
<TableCell>Row 2, Cell 1</TableCell>
<TableCell>Row 2, Cell 2</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell>Row 3(footer), Cell 1</TableCell>
<TableCell>Row 3(footer), Cell 2</TableCell>
</TableRow>
</TableFooter>
<TableCaption>Table Caption</TableCaption>
</TableNew>
);
export const TableExampleComponent = () => (
<Table>
<Table.Header>
<Table.Row>
<Table.ColumnTitle>Title Column 1</Table.ColumnTitle>
<Table.ColumnTitle>Title Column 2</Table.ColumnTitle>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>Row 1, Cell 1</Table.Cell>
<Table.Cell>Row 1, Cell 2</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Row 2, Cell 1</Table.Cell>
<Table.Cell>Row 2, Cell 2</Table.Cell>
</Table.Row>
<Table.Row>
<TableActions
actions={[
{
id: "action1",
label: "Action 1",
href: "#1",
},
{
id: "action2",
label: "Action 2",
actions: [
{
id: "action3",
label: "Action 3",
href: "#nested-action",
},
],
},
]}
/>
</Table.Row>
</Table.Body>
</Table>
);

View File

@@ -0,0 +1,89 @@
import * as React from "react";
import { classNames } from "@calcom/lib";
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
({ className, ...props }, ref) => (
<div className="w-full overflow-auto md:overflow-visible">
<table
ref={ref}
className={classNames("border-subtle w-full caption-bottom border text-sm", className)}
{...props}
/>
</div>
)
);
Table.displayName = "Table";
const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<thead
ref={ref}
className={classNames("[&_tr]:bg-subtle md:z-10 [&_tr]:border-b", className)}
{...props}
/>
)
);
TableHeader.displayName = "TableHeader";
const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<tbody ref={ref} className={classNames("[&_tr:last-child]:border-0", className)} {...props} />
)
);
TableBody.displayName = "TableBody";
const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<tfoot ref={ref} className={classNames("bg-default text-emphasis font-medium", className)} {...props} />
)
);
TableFooter.displayName = "TableFooter";
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
({ className, ...props }, ref) => (
<tr
ref={ref}
className={classNames(
"hover:bg-muted data-[state=selected]:bg-subtle border-subtle border-b",
className
)}
{...props}
/>
)
);
TableRow.displayName = "TableRow";
const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(
({ className, ...props }, ref) => (
<th
ref={ref}
className={classNames(
"text-default h-12 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
)
);
TableHead.displayName = "TableHead";
const TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
({ className, ...props }, ref) => (
<td
ref={ref}
className={classNames("text-default px-2 py-2.5 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
)
);
TableCell.displayName = "TableCell";
const TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(
({ className, ...props }, ref) => (
<caption ref={ref} className={classNames("text-default mt-4 text-sm", className)} {...props} />
)
);
TableCaption.displayName = "TableCaption";
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };

View File

@@ -0,0 +1,48 @@
import { Canvas, Meta, Story } from "@storybook/addon-docs";
import {
Examples,
Example,
Title,
VariantsTable,
CustomArgsTable,
VariantRow,
} from "@calcom/storybook/components";
import { Table } from "./Table";
import { TableExampleComponent } from "./TableExamples";
<Meta title="UI/Table/Table" component={Table} />
<Title title="Table" suffix="Brief" subtitle="Version 1.0 — Last Update: 21 Aug 2023" />
## Definition
The `Table` component is a HTML table that utilizes Tailwind classes to enhance its styles.
## Structure
The `Table` component should be used with other table sub-components (present as object attributes) that follow the same pattern.
These components render common elements that are styled with Tailwind. These components include:
- `Body` -> (`tbody`)
- `Header` -> (`thead`)
- `Row` -> (`tr`)
- `ColumnTitle` -> (`th`)
- `Cell` -> (`td`)
<CustomArgsTable of={Table} />
<Title offset title="Table" suffix="Variants" />
<Canvas>
<Story name="Table">
{(props) => (
<VariantsTable titles={["Default"]} columnMinWidth={150}>
<VariantRow>
<TableExampleComponent />
</VariantRow>
</VariantsTable>
)}
</Story>
</Canvas>

View File

@@ -0,0 +1,52 @@
/* eslint-disable playwright/missing-playwright-await */
import { fireEvent, render } from "@testing-library/react";
import { vi } from "vitest";
import { TableActions } from "@calcom/ui";
import { TableNewExampleComponent } from "./TableExamples";
const mockActions = [
{
id: "action1",
label: "Action 1",
href: "#",
},
{
id: "action2",
label: "Action 2",
onClick: vi.fn(),
},
];
describe("tests for Table component", () => {
test("Should render Table component correctly", () => {
const { getByRole, getByText } = render(<TableNewExampleComponent />);
const headerElement1 = getByRole("columnheader", { name: "Header Column 1" });
const headerElement2 = getByRole("columnheader", { name: "Header Column 2" });
expect(headerElement1).toBeInTheDocument();
expect(headerElement2).toBeInTheDocument();
expect(getByText("Row 1, Cell 1")).toBeInTheDocument();
expect(getByText("Row 1, Cell 2")).toBeInTheDocument();
expect(getByText("Row 2, Cell 1")).toBeInTheDocument();
expect(getByText("Row 2, Cell 2")).toBeInTheDocument();
const captionElement = getByText("Table Caption");
expect(captionElement).toBeInTheDocument();
});
test("Should render TableActions component correctly", () => {
const { getByText } = render(<TableActions actions={mockActions} />);
const action1Button = getByText("Action 1");
const action2Button = getByText("Action 2");
expect(action1Button).toBeInTheDocument();
expect(action2Button).toBeInTheDocument();
fireEvent.click(action2Button);
expect(mockActions[1].onClick).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,50 @@
import { Canvas, Meta, Story } from "@storybook/addon-docs";
import {
Examples,
Example,
Title,
VariantsTable,
CustomArgsTable,
VariantRow,
} from "@calcom/storybook/components";
import { TableNewExampleComponent } from "./TableExamples";
import { Table } from "./TableNew";
<Meta title="UI/Table/TableNew" component={Table} />
<Title title="Table New" suffix="Brief" subtitle="Version 1.0 — Last Update: 21 Aug 2023" />
## Definition
The `Table` component (from TableNew.tsx) is a HTML table that utilizes Tailwind classes to enhance its styles.
## Structure
The `Table` component should be used with other table sub-components (exported in the same file) that follow the same pattern.
These components render common elements that are styled with Tailwind. These components include:
- `TableBody` -> (`tbody`)
- `TableHeader` -> (`thead`)
- `TableRow` -> (`tr`)
- `TableHead` -> (`th`)
- `TableCell` -> (`td`)
- `TableFooter` -> (`tfoot`)
- `TableCaption` -> (`caption`)
<CustomArgsTable of={Table} />
<Title offset title="Table New" suffix="Variants" />
<Canvas>
<Story name="Table New">
{(props) => (
<VariantsTable titles={["Default"]} columnMinWidth={150}>
<VariantRow>
<TableNewExampleComponent />
</VariantRow>
</VariantsTable>
)}
</Story>
</Canvas>