Files
fleet/frontend/components/TableContainer/DataTable/ActionButton/ActionButton.tsx
T
Steven Palmesano 62ed3583e6 Clear button styles (#49292)
**Related issue:** Resolves #49276

**New features**
- Added new "Secondary" (bordered, off-white fill) and "Subdued"
(borderless, low-emphasis) button variants to match the Figma spec,
alongside the existing Primary style.
- Allowed rows to be selected in Controls > OS updates.

**Cleanup**
- Once nothing referenced the old styles anymore, fully removed the old
`text-icon`, `brand-inverse-icon`, `inverse-alert`, `inverse`, and
`icon` button variants (type, styles, and Storybook entries) from the
shared `Button` component.
- Removed the `iconStroke` prop, which had become a no-op once the old
variants it supported were gone.
- Renamed `ActionsDropdown`'s variants
(`button`/`brand-button`/`small-button`) to
`subdued`/`primary`/`secondary` to match the same naming used everywhere
else.
- Replaced a one-off dropdown implementation on the Software title page
with the shared `ActionsDropdown` component, instead of maintaining
duplicate styling logic.
- Changed the button name on Host details > Reports > Report details
from "View data for all hosts" to "View report for all hosts" (to match
the previous page's Actions drop-down options).


# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.


## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

<img width="1475" height="241" alt="Screenshot 2026-07-21 at 06 35 49"
src="https://github.com/user-attachments/assets/7cfbd444-7837-40e8-854e-bc5989d57d85"
/>
<img width="661" height="306" alt="Screenshot 2026-07-21 at 06 37 18"
src="https://github.com/user-attachments/assets/5d0c4873-8179-4089-b115-7e8cd3a53b4d"
/>
<img width="1427" height="423" alt="Screenshot 2026-07-21 at 06 37 30"
src="https://github.com/user-attachments/assets/a4850a60-f44a-4902-b45e-0094f23a52f8"
/>
<img width="1427" height="640" alt="Screenshot 2026-07-21 at 06 37 46"
src="https://github.com/user-attachments/assets/738a4a7f-cd7d-4162-b659-6f649c32204d"
/>
<img width="1445" height="479" alt="Screenshot 2026-07-22 at 07 03 22"
src="https://github.com/user-attachments/assets/4f672dc0-5c6d-4eb8-8465-ed5233fcd1b2"
/>
<img width="811" height="871" alt="Screenshot 2026-07-21 at 06 41 20"
src="https://github.com/user-attachments/assets/5421c96e-2dab-492a-af26-be0e5a7791ca"
/>
2026-07-23 07:11:59 -05:00

119 lines
3.1 KiB
TypeScript

import React, { useCallback } from "react";
import { kebabCase, noop } from "lodash";
import classnames from "classnames";
import { ButtonVariant } from "components/buttons/Button/Button";
import Icon from "components/Icon/Icon";
import { IconNames } from "components/icons";
import TooltipWrapper from "components/TooltipWrapper";
import { Colors } from "styles/var/colors";
import Button from "../../../buttons/Button";
const baseClass = "action-button";
export interface IActionButtonProps {
name: string;
buttonText: string | ((targetIds: number[]) => string);
onClick?: (ids: number[]) => void;
targetIds?: number[]; // TODO figure out undefined case
variant?: ButtonVariant;
hideButton?: boolean | ((targetIds: number[]) => boolean);
iconSvg?: IconNames;
iconColor?: Colors;
iconPosition?: string;
isDisabled?: boolean;
tooltipContent?: React.ReactNode;
}
function useActionCallback(
callbackFn: (targetIds: number[]) => void | undefined
) {
return useCallback(
(targetIds: number[]) => {
callbackFn(targetIds);
},
[callbackFn]
);
}
const ActionButton = (buttonProps: IActionButtonProps): JSX.Element | null => {
const {
name,
buttonText,
onClick,
targetIds = [],
variant = "default",
hideButton,
iconSvg,
iconColor,
iconPosition,
isDisabled,
tooltipContent,
} = buttonProps;
const resolvedButtonText =
typeof buttonText === "function" ? buttonText(targetIds) : buttonText;
const onButtonClick = useActionCallback(onClick || noop);
// hideButton is intended to provide a flexible way to specify show/hide conditions via a boolean or a function that evaluates to a boolean
// currently it is typed to accept an array of targetIds but this typing could easily be expanded to include other use cases
const isHidden = (
hideButtonProp: boolean | ((ids: number[]) => boolean) | undefined
) => {
if (typeof hideButtonProp === "function") {
return hideButtonProp(targetIds);
}
return Boolean(hideButtonProp);
};
if (isHidden(hideButton)) {
return null;
}
const buttonClasses = classnames(
baseClass,
`${baseClass}__${kebabCase(name)}`,
{ [`${baseClass}__disabled`]: isDisabled }
);
const renderButton = () => (
<div className={buttonClasses}>
<Button
onClick={() => onButtonClick(targetIds)}
variant={variant}
size="small"
>
<>
{iconPosition === "left" && iconSvg && (
<Icon name={iconSvg} color={iconColor} size="small" />
)}
{resolvedButtonText}
{iconPosition !== "left" && iconSvg && (
<Icon name={iconSvg} color={iconColor} size="small" />
)}
</>
</Button>
</div>
);
if (tooltipContent) {
return (
<div className={baseClass}>
<TooltipWrapper
tipContent={tooltipContent}
position="top"
fixedPositionStrategy
underline={false}
clickable={false}
showArrow
>
{renderButton()}
</TooltipWrapper>
</div>
);
}
return renderButton();
};
export default ActionButton;