chore(site): use <TableEmpty /> for table empty states (#27583)

## Summary
Standardize table empty and error rows on shared `TableEmpty` so they
share the same large empty treatment instead of one-off markup or manual
`EmptyState` wrappers.

- Replace hand-rolled `text-center` empties (External Auth, OAuth2
apps/secrets/authorized apps, Tasks)
- Swap `TableRow`/`TableCell` + `EmptyState` wrappers to `TableEmpty`
across audit/connection logs, groups, roles, IdP sync, provisioners,
users, versions, permissions, banners, and workspace sharing
- Drop redundant padding wrappers on Users and Versions tables
- Add an Empty story for External Auth settings
This commit is contained in:
Jake Howell
2026-08-05 14:28:53 +00:00
committed by GitHub
parent 5ed73f06ad
commit 120ec1f318
21 changed files with 207 additions and 303 deletions
@@ -20,7 +20,6 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "#/components/DropdownMenu/DropdownMenu";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import {
Select,
SelectContent,
@@ -37,6 +36,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { TableLoader } from "#/components/TableLoader/TableLoader";
import { getGroupSubtitle } from "#/modules/groups";
@@ -198,15 +198,11 @@ export const WorkspaceSharingForm: FC<WorkspaceSharingFormProps> = ({
if (sharingSettingsQuery.data.sharing_disabled) {
return (
<TableBody>
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="This workspace cannot be shared"
description="Workspace sharing has been disabled for this organization."
isCompact={isCompact}
/>
</TableCell>
</TableRow>
<TableEmpty
message="This workspace cannot be shared"
description="Workspace sharing has been disabled for this organization."
isCompact={isCompact}
/>
</TableBody>
);
}
@@ -232,15 +228,11 @@ export const WorkspaceSharingForm: FC<WorkspaceSharingFormProps> = ({
{!workspaceACL ? (
<TableLoader />
) : isEmpty ? (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="No shared members or groups yet"
description="Add a member or group using the controls above."
isCompact={isCompact}
/>
</TableCell>
</TableRow>
<TableEmpty
message="No shared members or groups yet"
description="Add a member or group using the controls above."
isCompact={isCompact}
/>
) : (
<>
{workspaceACL.group.map((group) => (
+4 -21
View File
@@ -1,6 +1,5 @@
import type { ComponentProps, FC } from "react";
import type { AuditLog } from "#/api/typesGenerated";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { Margins } from "#/components/Margins/Margins";
import {
PageHeader,
@@ -12,12 +11,8 @@ import {
type PaginationResult,
} from "#/components/PaginationWidget/PaginationContainer";
import { PaywallPremium } from "#/components/Paywall/PaywallPremium";
import {
Table,
TableBody,
TableCell,
TableRow,
} from "#/components/Table/Table";
import { Table, TableBody } from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { TableLoader } from "#/components/TableLoader/TableLoader";
import { Timeline } from "#/components/Timeline/Timeline";
import { docs } from "#/utils/docs";
@@ -114,13 +109,7 @@ const AuditTableBody: FC<AuditTableBodyProps> = ({
}) => {
// An error renders as an empty table.
if (error) {
return (
<TableRow>
<TableCell colSpan={999}>
<EmptyState message="An error occurred while loading audit logs" />
</TableCell>
</TableRow>
);
return <TableEmpty message="An error occurred while loading audit logs" />;
}
if (isLoading) {
return <TableLoader />;
@@ -129,13 +118,7 @@ const AuditTableBody: FC<AuditTableBodyProps> = ({
const emptyMessage = isNonInitialPage
? "No audit logs available on this page"
: "No audit logs available";
return (
<TableRow>
<TableCell colSpan={999}>
<EmptyState message={emptyMessage} />
</TableCell>
</TableRow>
);
return <TableEmpty message={emptyMessage} />;
}
if (!auditLogs) {
return null;
@@ -1,6 +1,5 @@
import type { ComponentProps, FC } from "react";
import type { ConnectionLog } from "#/api/typesGenerated";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { Margins } from "#/components/Margins/Margins";
import {
PageHeader,
@@ -12,12 +11,8 @@ import {
type PaginationResult,
} from "#/components/PaginationWidget/PaginationContainer";
import { PaywallPremium } from "#/components/Paywall/PaywallPremium";
import {
Table,
TableBody,
TableCell,
TableRow,
} from "#/components/Table/Table";
import { Table, TableBody } from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { TableLoader } from "#/components/TableLoader/TableLoader";
import { Timeline } from "#/components/Timeline/Timeline";
import { docs } from "#/utils/docs";
@@ -113,11 +108,7 @@ const ConnectionLogTableBody: FC<ConnectionLogTableBodyProps> = ({
// An error renders as an empty table.
if (error) {
return (
<TableRow>
<TableCell colSpan={999}>
<EmptyState message="An error occurred while loading connection logs" />
</TableCell>
</TableRow>
<TableEmpty message="An error occurred while loading connection logs" />
);
}
if (isLoading) {
@@ -127,13 +118,7 @@ const ConnectionLogTableBody: FC<ConnectionLogTableBodyProps> = ({
const emptyMessage = isNonInitialPage
? "No connection logs available on this page"
: "No connection logs available";
return (
<TableRow>
<TableCell colSpan={999}>
<EmptyState message={emptyMessage} />
</TableCell>
</TableRow>
);
return <TableEmpty message={emptyMessage} />;
}
if (!connectionLogs) {
return null;
@@ -3,16 +3,15 @@ import { type FC, useState } from "react";
import type { BannerConfig } from "#/api/typesGenerated";
import { Button } from "#/components/Button/Button";
import { ConfirmDialog } from "#/components/Dialog/ConfirmDialog/ConfirmDialog";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { Link } from "#/components/Link/Link";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { AnnouncementBannerDialog } from "./AnnouncementBannerDialog";
import { AnnouncementBannerItem } from "./AnnouncementBannerItem";
@@ -91,12 +90,10 @@ export const AnnouncementBannerSettings: FC<
</TableHeader>
<TableBody>
{!isEntitled || banners.length < 1 ? (
<TableCell colSpan={999}>
<EmptyState
className="min-h-[160px]"
message="No announcement banners"
/>
</TableCell>
<TableEmpty
className="min-h-[160px]"
message="No announcement banners"
/>
) : (
banners.map((banner, i) => (
<AnnouncementBannerItem
@@ -39,3 +39,11 @@ export default meta;
type Story = StoryObj<typeof ExternalAuthSettingsPageView>;
export const Page: Story = {};
export const Empty: Story = {
args: {
config: {
external_auth: [],
},
},
};
@@ -19,6 +19,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { docs } from "#/utils/docs";
type ExternalAuthSettingsPageViewProps = {
@@ -68,16 +69,10 @@ export const ExternalAuthSettingsPageView: FC<
</TableRow>
</TableHeader>
<TableBody>
{((config.external_auth === null ||
config.external_auth?.length === 0) && (
<TableRow>
<TableCell colSpan={999}>
<div className="text-center">
No providers have been configured!
</div>
</TableCell>
</TableRow>
)) ||
{config.external_auth === null ||
config.external_auth?.length === 0 ? (
<TableEmpty message="No providers have been configured!" />
) : (
config.external_auth?.map((git: ExternalAuthConfig) => {
const name = git.id || git.type;
return (
@@ -87,7 +82,8 @@ export const ExternalAuthSettingsPageView: FC<
<TableCell>{git.regex || "Not Set"}</TableCell>
</TableRow>
);
})}
})
)}
</TableBody>
</Table>
</>
@@ -25,7 +25,6 @@ import {
DialogHeader,
DialogTitle,
} from "#/components/Dialog/Dialog";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import {
HelpPopover,
HelpPopoverContent,
@@ -49,6 +48,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import {
Tooltip,
TooltipContent,
@@ -408,19 +408,15 @@ const IdpMappingTable: FC<IdpMappingTableProps> = ({ isEmpty, children }) => {
</TableHeader>
<TableBody>
{isEmpty ? (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="No organization mappings"
isCompact
cta={
<Link href={docs("/admin/users/idp-sync#organization-sync")}>
How to set up IdP organization sync
</Link>
}
/>
</TableCell>
</TableRow>
<TableEmpty
message="No organization mappings"
isCompact
cta={
<Link href={docs("/admin/users/idp-sync#organization-sync")}>
How to set up IdP organization sync
</Link>
}
/>
) : (
children
)}
@@ -67,6 +67,28 @@ export const Default: Story = {
},
};
export const EmptySecrets: Story = {
parameters: {
queries: [
{ key: oauth2ProviderAppKey(appId), data: mockApp },
{
key: oauth2ProviderAppSecretsKey(appId),
data: [],
},
],
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await expect(await canvas.findByText(mockApp.name)).toBeVisible();
await expect(
canvas.getByRole("table", { name: "OAuth2 client secrets" }),
).toBeVisible();
await expect(
canvas.getByText("No client secrets have been generated."),
).toBeVisible();
},
};
export const Loading: Story = {
parameters: {
queries: [],
@@ -33,7 +33,6 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "#/components/DropdownMenu/DropdownMenu";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { UsersFilter } from "#/components/Filter/UsersFilter";
import { LastSeen } from "#/components/LastSeen/LastSeen";
import { MultiMemberSelect } from "#/components/MultiUserSelect/MultiUserSelect";
@@ -47,6 +46,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { useAuthenticated } from "#/hooks/useAuthenticated";
import { useFeatureVisibility } from "#/modules/dashboard/useFeatureVisibility";
import { isEveryoneGroup } from "#/modules/groups";
@@ -198,11 +198,7 @@ const GroupMembersPage: FC = () => {
<TableBody>
{members.length === 0 ? (
<TableRow>
<TableCell colSpan={999}>
<EmptyState message="No members found" />
</TableCell>
</TableRow>
<TableEmpty message="No members found" />
) : (
membersWithSpend.map((member) => (
<GroupMemberRow
+19 -23
View File
@@ -8,7 +8,6 @@ import { AvatarData } from "#/components/Avatar/AvatarData";
import { AvatarDataSkeleton } from "#/components/Avatar/AvatarDataSkeleton";
import { Badge } from "#/components/Badge/Badge";
import { Button } from "#/components/Button/Button";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { PaywallPremium } from "#/components/Paywall/PaywallPremium";
import { Skeleton } from "#/components/Skeleton/Skeleton";
import {
@@ -19,6 +18,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import {
TableLoaderSkeleton,
TableRowSkeleton,
@@ -137,28 +137,24 @@ const GroupsTableBody: FC<GroupsTableBodyProps> = ({
}
if (groups.length === 0) {
return (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="No groups yet"
description={
canCreateGroup
? "Create your first group"
: "You don't have permission to create a group"
}
cta={
canCreateGroup && (
<Button asChild>
<RouterLink to="create">
<PlusIcon className="size-icon-sm" />
Create group
</RouterLink>
</Button>
)
}
/>
</TableCell>
</TableRow>
<TableEmpty
message="No groups yet"
description={
canCreateGroup
? "Create your first group"
: "You don't have permission to create a group"
}
cta={
canCreateGroup && (
<Button asChild>
<RouterLink to="create">
<PlusIcon className="size-icon-sm" />
Create group
</RouterLink>
</Button>
)
}
/>
);
}
return (
@@ -10,7 +10,6 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "#/components/DropdownMenu/DropdownMenu";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { PaywallPremium } from "#/components/Paywall/PaywallPremium";
import { Skeleton } from "#/components/Skeleton/Skeleton";
import {
@@ -21,6 +20,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import {
TableLoaderSkeleton,
TableRowSkeleton,
@@ -282,31 +282,27 @@ const RoleTableBody: FC<RoleTableProps> = ({
}
if (roles.length === 0) {
return (
<TableRow className="h-14">
<TableCell colSpan={999}>
<EmptyState
message="No custom roles yet"
description={
canCreateOrgRole && isCustomRolesEnabled
? "Create your first custom role"
: !isCustomRolesEnabled
? "Upgrade to a premium license to create a custom role"
: "You don't have permission to create a custom role"
}
cta={
canCreateOrgRole &&
isCustomRolesEnabled && (
<Button asChild>
<RouterLink to="create">
<PlusIcon />
Create custom role
</RouterLink>
</Button>
)
}
/>
</TableCell>
</TableRow>
<TableEmpty
message="No custom roles yet"
description={
canCreateOrgRole && isCustomRolesEnabled
? "Create your first custom role"
: !isCustomRolesEnabled
? "Upgrade to a premium license to create a custom role"
: "You don't have permission to create a custom role"
}
cta={
canCreateOrgRole &&
isCustomRolesEnabled && (
<Button asChild>
<RouterLink to="create">
<PlusIcon />
Create custom role
</RouterLink>
</Button>
)
}
/>
);
}
return (
@@ -1,5 +1,4 @@
import type { FC } from "react";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { Link } from "#/components/Link/Link";
import {
Table,
@@ -8,6 +7,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { docs } from "#/utils/docs";
interface IdpMappingTableProps {
@@ -37,23 +37,19 @@ export const IdpMappingTable: FC<IdpMappingTableProps> = ({
</TableHeader>
<TableBody>
{rowCount === 0 ? (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message={`No ${type.toLocaleLowerCase()} mappings`}
isCompact
cta={
<Link
href={docs(
`/admin/users/idp-sync#${type.toLocaleLowerCase()}-sync`,
)}
>
How to setup IdP {type.toLocaleLowerCase()} sync
</Link>
}
/>
</TableCell>
</TableRow>
<TableEmpty
message={`No ${type.toLocaleLowerCase()} mappings`}
isCompact
cta={
<Link
href={docs(
`/admin/users/idp-sync#${type.toLocaleLowerCase()}-sync`,
)}
>
How to setup IdP {type.toLocaleLowerCase()} sync
</Link>
}
/>
) : (
children
)}
@@ -36,6 +36,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import {
Tooltip,
TooltipContent,
@@ -191,25 +192,17 @@ const OrganizationProvisionerJobsPageView: FC<
/>
))
) : (
<TableRow>
<TableCell colSpan={999}>
<EmptyState message="No provisioner jobs found" />
</TableCell>
</TableRow>
<TableEmpty message="No provisioner jobs found" />
)
) : error ? (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="Error loading the provisioner jobs"
cta={
<Button size="sm" onClick={onRetry}>
Retry
</Button>
}
/>
</TableCell>
</TableRow>
<TableEmpty
message="Error loading the provisioner jobs"
cta={
<Button size="sm" onClick={onRetry}>
Retry
</Button>
}
/>
) : (
<TableRow>
<TableCell colSpan={999}>
@@ -6,7 +6,6 @@ import {
ProvisionerKeyIDUserAuth,
} from "#/api/typesGenerated";
import { Button } from "#/components/Button/Button";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { Link } from "#/components/Link/Link";
import { Loader } from "#/components/Loader/Loader";
import { PaywallPremium } from "#/components/Paywall/PaywallPremium";
@@ -23,6 +22,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { docs } from "#/utils/docs";
import { ProvisionerKeyRow } from "./ProvisionerKeyRow";
@@ -77,14 +77,10 @@ export const OrganizationProvisionerKeysPageView: FC<
<TableBody>
{filteredProvisionerKeyDaemons ? (
filteredProvisionerKeyDaemons.length === 0 ? (
<TableRow>
<TableCell colSpan={5}>
<EmptyState
message="No provisioner keys"
description="Create your first provisioner key to authenticate external provisioner daemons."
/>
</TableCell>
</TableRow>
<TableEmpty
message="No provisioner keys"
description="Create your first provisioner key to authenticate external provisioner daemons."
/>
) : (
filteredProvisionerKeyDaemons.map((pkd) => (
<ProvisionerKeyRow
@@ -96,18 +92,14 @@ export const OrganizationProvisionerKeysPageView: FC<
))
)
) : error ? (
<TableRow>
<TableCell colSpan={5}>
<EmptyState
message="Error loading provisioner keys"
cta={
<Button onClick={onRetry} size="sm">
Retry
</Button>
}
/>
</TableCell>
</TableRow>
<TableEmpty
message="Error loading provisioner keys"
cta={
<Button onClick={onRetry} size="sm">
Retry
</Button>
}
/>
) : (
<TableRow>
<TableCell colSpan={999}>
@@ -4,7 +4,6 @@ import type { ProvisionerDaemon } from "#/api/typesGenerated";
import { Badge } from "#/components/Badge/Badge";
import { Button } from "#/components/Button/Button";
import { Checkbox } from "#/components/Checkbox/Checkbox";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { Link } from "#/components/Link/Link";
import { Loader } from "#/components/Loader/Loader";
import { PaywallPremium } from "#/components/Paywall/PaywallPremium";
@@ -21,6 +20,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import {
Tooltip,
TooltipContent,
@@ -145,35 +145,27 @@ export const OrganizationProvisionersPageView: FC<
/>
))
) : (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="No provisioners found"
description="A provisioner is required before you can create templates and workspaces. You can connect your first provisioner by following our documentation."
cta={
<Button size="sm" asChild>
<Link href={docs("/admin/provisioners")}>
Create a provisioner
</Link>
</Button>
}
/>
</TableCell>
</TableRow>
<TableEmpty
message="No provisioners found"
description="A provisioner is required before you can create templates and workspaces. You can connect your first provisioner by following our documentation."
cta={
<Button size="sm" asChild>
<Link href={docs("/admin/provisioners")}>
Create a provisioner
</Link>
</Button>
}
/>
)
) : error ? (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="Error loading the provisioner jobs"
cta={
<Button onClick={onRetry} size="sm">
Retry
</Button>
}
/>
</TableCell>
</TableRow>
<TableEmpty
message="Error loading the provisioner jobs"
cta={
<Button onClick={onRetry} size="sm">
Retry
</Button>
}
/>
) : (
<TableRow>
<TableCell colSpan={999}>
+15 -32
View File
@@ -26,6 +26,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import {
TableLoaderSkeleton,
TableRowSkeleton,
@@ -131,43 +132,25 @@ type TasksErrorBodyProps = {
const TasksErrorBody: FC<TasksErrorBodyProps> = ({ error, onRetry }) => {
return (
<TableRow>
<TableCell colSpan={999} className="text-center">
<div className="rounded-lg w-full min-h-80 flex items-center justify-center">
<div className="flex flex-col items-center">
<h3 className="m-0 font-medium text-content-primary text-base">
{getErrorMessage(error, "Error loading tasks")}
</h3>
<span className="text-content-secondary text-sm">
{getErrorDetail(error) ?? "Please try again"}
</span>
<Button size="sm" onClick={onRetry} className="mt-4">
<RotateCcwIcon />
Try again
</Button>
</div>
</div>
</TableCell>
</TableRow>
<TableEmpty
message={getErrorMessage(error, "Error loading tasks")}
description={getErrorDetail(error) ?? "Please try again"}
cta={
<Button size="sm" onClick={onRetry}>
<RotateCcwIcon />
Try again
</Button>
}
/>
);
};
const TasksEmpty: FC = () => {
return (
<TableRow>
<TableCell colSpan={4} className="text-center">
<div className="w-full min-h-80 p-4 flex items-center justify-center">
<div className="flex flex-col items-center">
<h3 className="m-0 font-medium text-content-primary text-base">
No tasks found
</h3>
<span className="text-content-secondary text-sm">
Use the form above to run a task
</span>
</div>
</div>
</TableCell>
</TableRow>
<TableEmpty
message="No tasks found"
description="Use the form above to run a task"
/>
);
};
@@ -1,12 +1,7 @@
import type { FC } from "react";
import type * as TypesGen from "#/api/typesGenerated";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import {
Table,
TableBody,
TableCell,
TableRow,
} from "#/components/Table/Table";
import { Table, TableBody } from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { TableLoader } from "#/components/TableLoader/TableLoader";
import { Timeline } from "#/components/Timeline/Timeline";
import { VersionRow } from "./VersionRow";
@@ -65,13 +60,7 @@ export const VersionsTable: FC<VersionsTableProps> = ({
)}
{versions && versions.length === 0 && (
<TableRow>
<TableCell colSpan={999}>
<div className="p-8">
<EmptyState message="No versions found" />
</div>
</TableCell>
</TableRow>
<TableEmpty message="No versions found" />
)}
</TableBody>
</Table>
@@ -17,7 +17,6 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "#/components/DropdownMenu/DropdownMenu";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import {
Select,
SelectContent,
@@ -39,6 +38,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { TableLoader } from "#/components/TableLoader/TableLoader";
import { getGroupSubtitle } from "#/modules/groups";
import {
@@ -302,14 +302,10 @@ const MembersTableBody: FC<MembersTableBodyProps> = ({
templateACL.users.length === 0 && templateACL.group.length === 0;
if (isEmpty) {
return (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="No members yet"
description="Add a member using the controls above"
/>
</TableCell>
</TableRow>
<TableEmpty
message="No members yet"
description="Add a member using the controls above"
/>
);
}
@@ -34,3 +34,12 @@ export const Apps: Story = {
revoke: () => undefined,
},
};
export const Empty: Story = {
args: {
isLoading: false,
error: undefined,
apps: [],
revoke: () => undefined,
},
};
@@ -11,6 +11,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import { TableLoader } from "#/components/TableLoader/TableLoader";
type OAuth2ProviderPageViewProps = {
@@ -43,13 +44,7 @@ const OAuth2ProviderPageView: FC<OAuth2ProviderPageViewProps> = ({
<OAuth2AppRow key={app.id} app={app} revoke={revoke} />
))}
{apps?.length === 0 && (
<TableRow>
<TableCell colSpan={999}>
<div className="text-center">
No OAuth2 applications have been authorized.
</div>
</TableCell>
</TableRow>
<TableEmpty message="No OAuth2 applications have been authorized." />
)}
</TableBody>
</Table>
+2 -10
View File
@@ -15,7 +15,6 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "#/components/DropdownMenu/DropdownMenu";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import { LastSeen } from "#/components/LastSeen/LastSeen";
import { Skeleton } from "#/components/Skeleton/Skeleton";
import {
@@ -26,6 +25,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableEmpty } from "#/components/TableEmpty/TableEmpty";
import {
TableLoaderSkeleton,
TableRowSkeleton,
@@ -136,15 +136,7 @@ const UsersTableBody: React.FC<UsersTableProps> = ({
}
if (!users || users.length === 0) {
return (
<TableRow>
<TableCell colSpan={999}>
<div className="p-8">
<EmptyState message="No users found" />
</div>
</TableCell>
</TableRow>
);
return <TableEmpty message="No users found" />;
}
return (