mirror-cockpit-project-cockpit/pkg/lib/cockpit-components-privileged.tsx
Jelle van der Waa 9a706af793 lib: Port cockpit-components-privileged to TypeScript
The only major change is that `exactOptionalPropertyTypes` is enabled in
our TypeScript configuration disallowing us to pass optional properties
as undefined.

Assisted-by: Claude Opus 4.6
2026-04-23 11:18:20 +02:00

88 lines
2.8 KiB
TypeScript

/*
* Copyright (C) 2019 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
import React from 'react';
import { Button, type ButtonProps } from "@patternfly/react-core/dist/esm/components/Button/index.js";
import { Tooltip, TooltipPosition } from "@patternfly/react-core/dist/esm/components/Tooltip/index.js";
import cockpit from "cockpit";
import { superuser } from 'superuser';
import { useEvent, useLoggedInUser } from "hooks";
/**
* UI element wrapper for something that requires privilege. When access is not
* allowed, then wrap the element into a Tooltip.
*
* Note that the wrapped element itself needs to be disabled explicitly, this
* wrapper cannot do this (unfortunately wrapping it into a disabled span does
* not inherit).
*/
export function Privileged({
excuse,
allowed,
placement,
tooltipId,
children
}: {
excuse: React.ReactNode;
allowed: boolean | null;
placement?: TooltipPosition | `${TooltipPosition}` | undefined;
tooltipId?: string | undefined;
children: React.ReactNode;
}) {
// wrap into extra <span> so that a disabled child keeps the tooltip working
let contents = <span id={allowed ? undefined : tooltipId}>{ children }</span>;
if (!allowed) {
contents = (
<Tooltip position={ placement || TooltipPosition.top} id={ tooltipId + "_tooltip" }
content={ excuse }>
{ contents }
</Tooltip>);
}
return contents;
}
/**
* Convenience element for a Privilege wrapped Button
*/
export const PrivilegedButton = ({
tooltipId,
placement,
excuse,
buttonId,
onClick,
ariaLabel,
variant,
isDanger,
children
}: {
excuse: string; // must contain a $0, replaced with user name
onClick?: React.MouseEventHandler<HTMLButtonElement>;
variant?: ButtonProps["variant"];
isDanger?: boolean;
placement?: TooltipPosition | `${TooltipPosition}` | undefined;
buttonId?: string;
tooltipId?: string;
ariaLabel?: string;
children: React.ReactNode;
}) => {
const user = useLoggedInUser();
useEvent(superuser, "changed");
return (
<Privileged allowed={ superuser.allowed } tooltipId={ tooltipId } placement={ placement }
excuse={ cockpit.format(excuse, user?.name ?? '') }>
<Button isInline isDisabled={ !superuser.allowed }
{...(buttonId !== undefined && { id: buttonId })}
{...(variant !== undefined && { variant })}
{...(isDanger !== undefined && { isDanger })}
{...(onClick !== undefined && { onClick })}
{...(ariaLabel !== undefined && { "aria-label": ariaLabel })}
>
{ children }
</Button>
</Privileged>
);
};