Skip to content
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { Meta, StoryObj } from "@storybook/react";
import { MockRoleWithOrgPermissions } from "testHelpers/entities";
import {
MockOrganizationAuditorRole,
MockRoleWithOrgPermissions,
} from "testHelpers/entities";
import { CustomRolesPageView } from "./CustomRolesPageView";

const meta: Meta<typeof CustomRolesPageView> = {
Expand All @@ -26,6 +29,14 @@ export const Enabled: Story = {
},
};

export const RoleWithoutPermissions: Story = {
args: {
roles: [MockOrganizationAuditorRole],
canAssignOrgRole: true,
isCustomRolesEnabled: true,
},
};

export const EmptyDisplayName: Story = {
args: {
roles: [
Expand All @@ -40,15 +51,15 @@ export const EmptyDisplayName: Story = {
},
};

export const EmptyRoleWithoutPermission: Story = {
export const EmptyTableUserWithoutPermission: Story = {
args: {
roles: [],
canAssignOrgRole: false,
isCustomRolesEnabled: true,
},
};

export const EmptyRoleWithPermission: Story = {
export const EmptyTableUserWithPermission: Story = {
args: {
roles: [],
canAssignOrgRole: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import type { FC } from "react";
import { Link as RouterLink, useNavigate } from "react-router-dom";
import { docs } from "utils/docs";
import { PermissionPillsList } from "./PermissionPillsList";

export type CustomRolesPageViewProps = {
roles: Role[] | undefined;
Expand All @@ -42,7 +43,6 @@ export const CustomRolesPageView: FC<CustomRolesPageViewProps> = ({
}) => {
const isLoading = roles === undefined;
const isEmpty = Boolean(roles && roles.length === 0);

return (
<>
<ChooseOne>
Expand All @@ -58,8 +58,8 @@ export const CustomRolesPageView: FC<CustomRolesPageViewProps> = ({
<Table>
<TableHead>
<TableRow>
<TableCell width="50%">Name</TableCell>
<TableCell width="49%">Permissions</TableCell>
<TableCell width="40%">Name</TableCell>
<TableCell width="59%">Permissions</TableCell>
<TableCell width="1%" />
</TableRow>
</TableHead>
Expand Down Expand Up @@ -129,8 +129,8 @@ const RoleRow: FC<RoleRowProps> = ({ role, onDelete, canAssignOrgRole }) => {
<TableRow data-testid={`role-${role.name}`}>
<TableCell>{role.display_name || role.name}</TableCell>

<TableCell css={styles.secondary}>
{role.organization_permissions.length}
<TableCell>
<PermissionPillsList permissions={role.organization_permissions} />
</TableCell>

<TableCell>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
import { MockRoleWithOrgPermissions } from "testHelpers/entities";
import { PermissionPillsList } from "./PermissionPillsList";

const meta: Meta<typeof PermissionPillsList> = {
title: "pages/OrganizationCustomRolesPage/PermissionPillsList",
component: PermissionPillsList,
};

export default meta;
type Story = StoryObj<typeof PermissionPillsList>;

export const Default: Story = {
args: {
permissions: MockRoleWithOrgPermissions.organization_permissions,
},
};

export const SinglePermission: Story = {
args: {
permissions: [
{
negate: false,
resource_type: "organization_member",
action: "create",
},
],
},
};

export const NoPermissions: Story = {
args: {
permissions: [],
},
};

export const HoverOverflowPill: Story = {
args: {
permissions: MockRoleWithOrgPermissions.organization_permissions,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.hover(canvas.getByTestId("overflow-permissions-pill"));
},
};

export const ShowAllResources: Story = {
args: {
permissions: MockRoleWithOrgPermissions.organization_permissions,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import Stack from "@mui/material/Stack";
import type { Permission } from "api/typesGenerated";
import { Pill } from "components/Pill/Pill";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "components/Popover/Popover";
import type { FC } from "react";

function getUniqueResourceTypes(jsonObject: readonly Permission[]) {
const resourceTypes = jsonObject.map((item) => item.resource_type);
return [...new Set(resourceTypes)];
}

interface PermissionPillsListProps {
permissions: readonly Permission[];
}

export const PermissionPillsList: FC<PermissionPillsListProps> = ({
permissions,
}) => {
const resourceTypes = getUniqueResourceTypes(permissions);

return (
<Stack direction="row" spacing={1}>
{permissions.length > 0 ? (
<PermissionsPill
resource={resourceTypes[0]}
permissions={permissions}
/>
) : (
<p>None</p>
)}

{resourceTypes.length > 1 && (
<OverflowPermissionPill
resources={resourceTypes.slice(1)}
permissions={permissions.slice(1)}
/>
)}
</Stack>
);
};

interface PermissionPillProps {
resource: string;
permissions: readonly Permission[];
}

const PermissionsPill: FC<PermissionPillProps> = ({
resource,
permissions,
}) => {
const actions = permissions.filter(
(p) => resource === p.resource_type && p.action,
);

return (
<Pill css={styles.permissionPill}>
<b>{resource}</b>: {actions.map((p) => p.action).join(", ")}
</Pill>
);
};

type OverflowPermissionPillProps = {
resources: string[];
permissions: readonly Permission[];
};

const OverflowPermissionPill: FC<OverflowPermissionPillProps> = ({
resources,
permissions,
}) => {
const theme = useTheme();

return (
<Popover mode="hover">
<PopoverTrigger>
<Pill
css={{
backgroundColor: theme.palette.background.paper,
borderColor: theme.palette.divider,
}}
data-testid="overflow-permissions-pill"
>
+{resources.length} more
</Pill>
</PopoverTrigger>

<PopoverContent
disableRestoreFocus
disableScrollLock
css={{
".MuiPaper-root": {
display: "flex",
flexFlow: "column wrap",
columnGap: 8,
rowGap: 12,
padding: "12px 16px",
alignContent: "space-around",
minWidth: "auto",
backgroundColor: theme.palette.background.default,
},
}}
anchorOrigin={{
vertical: -4,
horizontal: "center",
}}
transformOrigin={{
vertical: "bottom",
horizontal: "center",
}}
>
{resources.map((resource) => (
<PermissionsPill
key={resource}
resource={resource}
permissions={permissions}
/>
))}
</PopoverContent>
</Popover>
);
};

const styles = {
permissionPill: (theme) => ({
backgroundColor: theme.experimental.pillDefault.background,
borderColor: theme.experimental.pillDefault.outline,
color: theme.experimental.pillDefault.text,
width: "fit-content",
}),
} satisfies Record<string, Interpolation<Theme>>;
25 changes: 25 additions & 0 deletions site/src/testHelpers/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,31 @@ export const MockRoleWithOrgPermissions: TypesGen.Role = {
resource_type: "audit_log",
action: "read",
},
{
negate: false,
resource_type: "group",
action: "create",
},
{
negate: false,
resource_type: "group",
action: "delete",
},
{
negate: false,
resource_type: "group",
action: "read",
},
{
negate: false,
resource_type: "group",
action: "update",
},
{
negate: false,
resource_type: "provisioner_daemon",
action: "create",
},
],
user_permissions: [],
};
Expand Down
6 changes: 6 additions & 0 deletions site/src/theme/dark/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@ export default {
},
},
},

pillDefault: {
background: colors.zinc[800],
outline: colors.zinc[700],
text: colors.zinc[200],
},
} satisfies NewTheme;
6 changes: 6 additions & 0 deletions site/src/theme/darkBlue/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@ export default {
},
},
},

pillDefault: {
background: colors.gray[800],
outline: colors.gray[700],
text: colors.gray[200],
},
} satisfies NewTheme;
5 changes: 5 additions & 0 deletions site/src/theme/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ import type { InteractiveRole, Role } from "./roles";
export interface NewTheme {
l1: Role; // page background, things which sit at the "root level"
l2: InteractiveRole; // sidebars, table headers, navigation
pillDefault: {
background: string;
outline: string;
text: string;
};
}
6 changes: 6 additions & 0 deletions site/src/theme/light/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@ export default {
},
},
},

pillDefault: {
background: colors.zinc[200],
outline: colors.zinc[300],
text: colors.zinc[700],
},
} satisfies NewTheme;
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy