feat: de-mui the <UserGroupsCell /> component (#24277)

This pull-request takes the `<UserGroupsCell />` component and removes
out the `@mui/material/List`/`@mui/material/ListItem` dependencies. One
step closer to being a mui-free codebase.

<img width="364" height="309" alt="image"
src="https://github.com/user-attachments/assets/999995ef-88b6-4e54-834a-aa03c1274da0"
/>
This commit is contained in:
Jake Howell
2026-04-14 13:39:48 +10:00
committed by GitHub
parent 3d8d89e56c
commit 60aed55eaa
@@ -1,18 +1,13 @@
import { useTheme } from "@emotion/react";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import { UsersIcon } from "lucide-react";
import type { FC } from "react";
import type { Group } from "#/api/typesGenerated";
import { Avatar } from "#/components/Avatar/Avatar";
import { OverflowY } from "#/components/OverflowY/OverflowY";
import { TableCell } from "#/components/Table/Table";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
Popover,
PopoverContent,
PopoverTrigger,
} from "#/components/Popover/Popover";
import { TableCell } from "#/components/Table/Table";
import { cn } from "#/utils/cn";
type GroupsCellProps = {
@@ -20,69 +15,66 @@ type GroupsCellProps = {
};
export const UserGroupsCell: FC<GroupsCellProps> = ({ userGroups }) => {
const theme = useTheme();
return (
<TableCell>
{userGroups === undefined ? (
<span>No groups</span>
) : (
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<button
className="cursor-pointer bg-transparent border-0 p-0 text-inherit leading-none"
type="button"
>
<div className="flex flex-row gap-2 items-center">
<UsersIcon
className={cn([
"size-4 opacity-50",
userGroups.length > 0 && "opacity-80",
])}
/>
<Popover>
<PopoverTrigger asChild>
<button
type="button"
className="cursor-pointer bg-transparent border-0 p-0 text-inherit leading-none"
aria-label={
userGroups.length === 0
? "No groups"
: `View ${userGroups.length} group${userGroups.length !== 1 ? "s" : ""}`
}
>
<div className="flex flex-row gap-2 items-center">
<UsersIcon
className={cn([
"size-4 opacity-50",
userGroups.length > 0 && "opacity-80",
])}
/>
<span>
{userGroups.length} Group{userGroups.length !== 1 && "s"}
</span>
</div>
</button>
</TooltipTrigger>
<span>
{userGroups.length} Group{userGroups.length !== 1 && "s"}
</span>
</div>
</button>
</PopoverTrigger>
<TooltipContent className="p-0 bg-surface-secondary border-surface-quaternary text-content-primary">
<OverflowY maxHeight={400}>
<List
component="ul"
className="flex flex-col flex-nowrap gap-0 px-0.5 py-1"
style={{
fontSize: theme.typography.body2.fontSize,
}}
>
{userGroups.map((group) => {
const groupName = group.display_name || group.name;
return (
<ListItem
key={group.id}
className="gap-x-[10px] items-center"
>
<Avatar
size="sm"
variant="icon"
src={group.avatar_url}
fallback={groupName}
/>
<PopoverContent
align="start"
sideOffset={8}
className="w-auto min-w-[240px] max-w-sm max-h-[400px] p-0"
>
<ul className="m-0 list-none flex flex-col flex-nowrap gap-0 px-0.5 py-1 text-sm">
{userGroups.map((group) => {
const groupName = group.display_name || group.name;
return (
<li
key={group.id}
className="flex gap-x-[10px] items-center px-2 py-1.5"
>
<Avatar
size="sm"
variant="icon"
src={group.avatar_url}
fallback={groupName}
/>
<span className="whitespace-nowrap text-ellipsis overflow-hidden leading-none m-0">
{groupName || <em>N/A</em>}
</span>
</ListItem>
);
})}
</List>
</OverflowY>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<span className="m-0 min-w-0 overflow-hidden text-ellipsis whitespace-nowrap leading-none">
{groupName || <em>N/A</em>}
</span>
</li>
);
})}
</ul>
</PopoverContent>
</Popover>
)}
</TableCell>
);