fix: support more className-related props at DataTable (#19953)

* fix: support more className-related props at DataTable

* update style

* fix type error

* use relative import

---------

Co-authored-by: Benny Joo <sldisek783@gmail.com>
This commit is contained in:
Eunjae Lee
2025-03-13 14:25:57 -04:00
committed by GitHub
co-authored by Benny Joo
parent be404436d7
commit d6a3661e58
11 changed files with 89 additions and 81 deletions
@@ -364,7 +364,7 @@ function BookingsContent({ status }: BookingsProps) {
table={table}
testId={`${status}-bookings`}
bodyTestId="bookings"
hideHeader={true}
headerClassName="hidden"
isPending={query.isPending}
totalRowCount={query.data?.totalCount}
variant="compact"
@@ -30,25 +30,29 @@ import classNames from "@calcom/ui/classNames";
import { useColumnSizingVars } from "../hooks";
import { usePersistentColumnResizing } from "../lib/resizing";
export type DataTableProps<TData> = {
export type DataTablePropsFromWrapper<TData> = {
table: ReactTableType<TData>;
tableContainerRef: React.RefObject<HTMLDivElement>;
isPending?: boolean;
onRowMouseclick?: (row: Row<TData>) => void;
onScroll?: (e: Pick<React.UIEvent<HTMLDivElement, UIEvent>, "target">) => void;
tableOverlay?: React.ReactNode;
variant?: "default" | "compact";
testId?: string;
bodyTestId?: string;
hideHeader?: boolean;
children?: React.ReactNode;
identifier?: string;
enableColumnResizing?: boolean;
className?: string;
containerClassName?: string;
headerClassName?: string;
rowClassName?: string;
paginationMode?: "infinite" | "standard";
};
export type DataTableProps<TData> = DataTablePropsFromWrapper<TData> & {
onRowMouseclick?: (row: Row<TData>) => void;
onScroll?: (e: Pick<React.UIEvent<HTMLDivElement, UIEvent>, "target">) => void;
tableOverlay?: React.ReactNode;
identifier?: string;
enableColumnResizing?: boolean;
};
export function DataTable<TData>({
table,
tableContainerRef,
@@ -57,13 +61,14 @@ export function DataTable<TData>({
onRowMouseclick,
onScroll,
children,
hideHeader,
identifier: _identifier,
enableColumnResizing,
testId,
bodyTestId,
className,
containerClassName,
headerClassName,
rowClassName,
paginationMode = "infinite",
...rest
}: DataTableProps<TData> & React.ComponentPropsWithoutRef<"div">) {
@@ -145,44 +150,42 @@ export function DataTable<TData>({
...columnSizingVars,
...(Boolean(enableColumnResizing) && { width: table.getTotalSize() }),
}}>
{!hideHeader && (
<TableHeader className="sticky top-0 z-10">
{table.getHeaderGroups().map((headerGroup: HeaderGroup<TData>) => (
<TableRow key={headerGroup.id} className="hover:bg-subtle flex w-full">
{headerGroup.headers.map((header: Header<TData, unknown>) => {
const { column } = header;
return (
<TableHead
key={header.id}
style={{
...(column.getIsPinned() === "left" && { left: `${column.getStart("left")}px` }),
...(column.getIsPinned() === "right" && { right: `${column.getStart("right")}px` }),
width: `var(--header-${kebabCase(header?.id)}-size)`,
}}
className={classNames(
"relative flex shrink-0 items-center",
"bg-subtle",
column.getIsPinned() && "top-0 z-20 sm:sticky"
)}>
<TableHeadLabel header={header} />
{Boolean(enableColumnResizing) && header.column.getCanResize() && (
<div
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
className={classNames(
"group absolute right-0 top-0 h-full w-[5px] cursor-col-resize touch-none select-none opacity-[0.1] hover:opacity-50",
header.column.getIsResizing() && "!opacity-75"
)}>
<div className="bg-inverted mx-auto h-full w-[1px]" />
</div>
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
)}
<TableHeader className={classNames("sticky top-0 z-10", headerClassName)}>
{table.getHeaderGroups().map((headerGroup: HeaderGroup<TData>) => (
<TableRow key={headerGroup.id} className="hover:bg-subtle flex w-full">
{headerGroup.headers.map((header: Header<TData, unknown>) => {
const { column } = header;
return (
<TableHead
key={header.id}
style={{
...(column.getIsPinned() === "left" && { left: `${column.getStart("left")}px` }),
...(column.getIsPinned() === "right" && { right: `${column.getStart("right")}px` }),
width: `var(--header-${kebabCase(header?.id)}-size)`,
}}
className={classNames(
"relative flex shrink-0 items-center",
"bg-subtle",
column.getIsPinned() && "top-0 z-20 sm:sticky"
)}>
<TableHeadLabel header={header} />
{Boolean(enableColumnResizing) && header.column.getCanResize() && (
<div
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
className={classNames(
"group absolute right-0 top-0 h-full w-[5px] cursor-col-resize touch-none select-none opacity-[0.1] hover:opacity-50",
header.column.getIsResizing() && "!opacity-75"
)}>
<div className="bg-inverted mx-auto h-full w-[1px]" />
</div>
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
{/* When resizing any column we will render this special memoized version of our table body */}
{table.getState().columnSizingInfo.isResizingColumn ? (
<MemoizedTableBody
@@ -194,6 +197,7 @@ export function DataTable<TData>({
isPending={isPending}
onRowMouseclick={onRowMouseclick}
paginationMode={paginationMode}
rowClassName={rowClassName}
/>
) : (
<DataTableBody
@@ -205,6 +209,7 @@ export function DataTable<TData>({
isPending={isPending}
onRowMouseclick={onRowMouseclick}
paginationMode={paginationMode}
rowClassName={rowClassName}
/>
)}
</TableNew>
@@ -224,7 +229,8 @@ const MemoizedTableBody = memo(
prev.variant === next.variant &&
prev.isPending === next.isPending &&
prev.onRowMouseclick === next.onRowMouseclick &&
prev.paginationMode === next.paginationMode
prev.paginationMode === next.paginationMode &&
prev.rowClassName === next.rowClassName
) as typeof DataTableBody;
type DataTableBodyProps<TData> = {
@@ -236,6 +242,7 @@ type DataTableBodyProps<TData> = {
isPending?: boolean;
onRowMouseclick?: (row: Row<TData>) => void;
paginationMode?: "infinite" | "standard";
rowClassName?: string;
};
type RowToRender<TData> = {
@@ -252,6 +259,7 @@ function DataTableBody<TData>({
isPending,
onRowMouseclick,
paginationMode,
rowClassName,
}: DataTableBodyProps<TData> & { paginationMode?: "infinite" | "standard" }) {
const { t } = useLocale();
const virtualItems = rowVirtualizer.getVirtualItems();
@@ -297,7 +305,7 @@ function DataTableBody<TData>({
width: "100%",
}),
}}
className={classNames(onRowMouseclick && "hover:cursor-pointer", "group")}>
className={classNames(onRowMouseclick && "hover:cursor-pointer", "group", rowClassName)}>
{row.getVisibleCells().map((cell) => {
const column = cell.column;
return (
@@ -1,33 +1,26 @@
"use client";
import type { Table as ReactTableType, VisibilityState } from "@tanstack/react-table";
import type { VisibilityState } from "@tanstack/react-table";
// eslint-disable-next-line no-restricted-imports
import { noop } from "lodash";
import { useEffect, useRef } from "react";
import {
DataTable,
DataTablePagination,
useFetchMoreOnBottomReached,
useDataTable,
useColumnFilters,
} from "@calcom/features/data-table";
import { useColumnFilters } from "../hooks/useColumnFilters";
import { useDataTable } from "../hooks/useDataTable";
import { useFetchMoreOnBottomReached } from "../hooks/useFetchMoreOnBottomReached";
import type { DataTablePropsFromWrapper } from "./DataTable";
import { DataTable } from "./DataTable";
import { DataTablePagination } from "./DataTablePagination";
type BaseDataTableWrapperProps<TData> = {
testId?: string;
bodyTestId?: string;
table: ReactTableType<TData>;
isPending: boolean;
hideHeader?: boolean;
variant?: "default" | "compact";
type BaseDataTableWrapperProps<TData> = Omit<
DataTablePropsFromWrapper<TData>,
"paginationMode" | "tableContainerRef"
> & {
totalRowCount?: number;
ToolbarLeft?: React.ReactNode;
ToolbarRight?: React.ReactNode;
EmptyView?: React.ReactNode;
LoaderView?: React.ReactNode;
className?: string;
containerClassName?: string;
children?: React.ReactNode;
tableContainerRef?: React.RefObject<HTMLDivElement>;
};
@@ -57,13 +50,14 @@ export function DataTableWrapper<TData>({
isFetching,
totalRowCount,
variant,
hideHeader,
ToolbarLeft,
ToolbarRight,
EmptyView,
LoaderView,
className,
containerClassName,
headerClassName,
rowClassName,
children,
tableContainerRef: externalRef,
paginationMode,
@@ -130,10 +124,11 @@ export function DataTableWrapper<TData>({
tableContainerRef={tableContainerRef}
isPending={isPending}
enableColumnResizing={true}
hideHeader={hideHeader}
variant={variant}
className={className}
containerClassName={containerClassName}
headerClassName={headerClassName}
rowClassName={rowClassName}
paginationMode={paginationMode}
onScroll={
paginationMode === "infinite"
@@ -1,7 +1,8 @@
import { useDataTable } from "@calcom/features/data-table";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, Tooltip } from "@calcom/ui";
import { useDataTable } from "../../hooks/useDataTable";
export const ClearFiltersButton = ({ exclude }: { exclude?: string[] }) => {
const { t } = useLocale();
const { activeFilters, clearAll } = useDataTable();
@@ -1,6 +1,7 @@
import logger from "@calcom/lib/logger";
import { HttpCode } from "./__handler";
import type { LazyModule, SWHMap } from "./__handler";
import logger from "@calcom/lib/logger";
type Data = SWHMap["invoice.paid"]["data"];
@@ -1,5 +1,4 @@
import { IS_PRODUCTION } from "@calcom/lib/constants";
import { prisma } from "@calcom/prisma";
import { InternalOrganizationBilling } from "./internal-organization-billing";
import { OrganizationBillingRepository } from "./organization-billing.repository";
@@ -7,4 +6,4 @@ import { StubOrganizationBilling } from "./stub-organization-billing";
export { OrganizationBillingRepository };
export const OrganizationBilling = IS_PRODUCTION ? InternalOrganizationBilling : StubOrganizationBilling;
export const OrganizationBilling = IS_PRODUCTION ? InternalOrganizationBilling : StubOrganizationBilling;
@@ -1,18 +1,19 @@
import type { Prisma } from "@prisma/client";
import type { z } from "zod";
import { safeStringify } from "@calcom/lib/safeStringify";
import { getRequestedSlugError } from "@calcom/app-store/stripepayment/lib/team-billing";
import { purchaseTeamOrOrgSubscription } from "@calcom/features/ee/teams/lib/payments";
import { MINIMUM_NUMBER_OF_ORG_SEATS, WEBAPP_URL } from "@calcom/lib/constants";
import { getMetadataHelpers } from "@calcom/lib/getMetadataHelpers";
import logger from "@calcom/lib/logger";
import { Redirect } from "@calcom/lib/redirect";
import { safeStringify } from "@calcom/lib/safeStringify";
import { OrganizationOnboardingRepository } from "@calcom/lib/server/repository/organizationOnboarding";
import prisma from "@calcom/prisma";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import billing from "..";
import { TeamBillingPublishResponseStatus, type TeamBilling, type TeamBillingInput } from "./team-billing";
import { OrganizationOnboardingRepository } from "@calcom/lib/server/repository/organizationOnboarding";
const log = logger.getSubLogger({ prefix: ["TeamBilling"] });
@@ -124,13 +125,15 @@ export class InternalTeamBilling implements TeamBilling {
const { id: teamId, metadata, isOrganization } = this.team;
const { url } = await this.checkIfTeamPaymentRequired();
const organizationOnboarding = await OrganizationOnboardingRepository.findByOrganizationId(this.team.id);
const organizationOnboarding = await OrganizationOnboardingRepository.findByOrganizationId(
this.team.id
);
log.debug("updateQuantity", safeStringify({ url, team: this.team }));
/**
* If there's no pending checkout URL it means that this team has not been paid.
* We cannot update the subscription yet, this will be handled on publish/checkout.
*
*
* An organization can only be created if it is paid for and updateQuantity is called only when we have an organization.
* For some old organizations, it is possible that they aren't paid for yet, but then they wouldn't have been published as well(i.e. slug would be null and are unusable)
* So, we can safely assume go forward for organizations.
@@ -109,9 +109,10 @@ export function RoutingFormResponsesTable() {
return (
<>
<div className="flex-1">
<DataTableWrapper
<DataTableWrapper<RoutingFormTableRow>
table={table}
isPending={isPending}
rowClassName="min-h-14"
paginationMode="standard"
totalRowCount={data?.total}
LoaderView={<DataTableSkeleton columns={4} columnWidths={[200, 200, 250, 250]} />}
@@ -329,7 +329,7 @@ function OutOfOfficeEntriesListContent() {
<>
<DataTableWrapper
testId="ooo-list-data-table"
hideHeader={selectedTab === OutOfOfficeTab.MINE}
rowClassName={selectedTab === OutOfOfficeTab.MINE ? "hidden" : ""}
table={table}
isPending={isPending}
hasNextPage={hasNextPage}
@@ -277,7 +277,7 @@ function UserListTableContent({ oAuthClientId }: PlatformManagedUsersTableProps)
return (
<>
<DataTableWrapper
<DataTableWrapper<PlatformManagedUserTableUser>
testId="managed-user-list-data-table"
table={table}
isPending={isPending}
@@ -512,7 +512,7 @@ function UserListTableContent() {
return (
<>
<DataTableWrapper
<DataTableWrapper<UserTableUser>
testId="user-list-data-table"
table={table}
isPending={isPending}