Files
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

168 lines
4.7 KiB
TypeScript

import React from "react";
import classnames from "classnames";
import { IFileDetails } from "utilities/file/fileUtils";
import Button from "components/buttons/Button";
import { ISupportedGraphicNames } from "components/FileUploader/FileUploader";
import Graphic from "components/Graphic";
import Icon from "components/Icon";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
export type IFileDetailsSupportedGraphicNames =
| ISupportedGraphicNames
| "app-store"; // For VPP apps (non-editable)
interface IFileDetailsProps {
graphicNames:
| IFileDetailsSupportedGraphicNames
| IFileDetailsSupportedGraphicNames[];
fileDetails: IFileDetails;
canEdit: boolean;
/** If present, will default to a custom editor section instead of edit icon */
customEditor?: () => React.ReactNode;
/** If present, replaces the default Graphic on the left of the file
* details (e.g. to render a preview thumbnail of an uploaded image). */
customPreview?: React.ReactNode;
/** If present, will show a trash icon */
onDeleteFile?: () => void;
onFileSelect?: (e: React.ChangeEvent<HTMLInputElement>) => void;
accept?: string;
progress?: number;
/** Set to false for one instance we allow users to edit a file as it shows them the YAML */
gitopsCompatible?: boolean;
gitOpsModeEnabled?: boolean;
}
const baseClass = "file-details";
const FileDetails = ({
graphicNames,
fileDetails,
canEdit,
customEditor,
customPreview,
onDeleteFile,
onFileSelect,
accept,
progress,
gitopsCompatible = true,
gitOpsModeEnabled = false,
}: IFileDetailsProps) => {
const inputRef = React.useRef<HTMLInputElement | null>(null);
const handleClickEdit = (disabled?: boolean) => {
if (disabled) return;
inputRef.current?.click();
};
const infoClasses = classnames(`${baseClass}__info`, {
[`${baseClass}__info--disabled-by-gitops-mode`]:
gitOpsModeEnabled && gitopsCompatible,
});
const renderEditButton = (disabled?: boolean) => {
if (customEditor) {
return (
<div
onClick={(e) => {
e.stopPropagation();
}}
>
{customEditor()}
</div>
);
}
return (
<div className={`${baseClass}__edit`}>
<Button
disabled={disabled}
className={`${baseClass}__edit-button`}
variant="subdued"
onClick={() => handleClickEdit(disabled)}
title="Replace file"
>
<Icon name="pencil" />
</Button>
<input
ref={inputRef}
type="file"
accept={accept}
onChange={onFileSelect}
className="file-input-visually-hidden"
/>
</div>
);
};
return (
<div className={baseClass}>
{/* disabling at this level preserves funcitonality of GitOpsModeTooltipWrapper around the edit icon */}
<div className={infoClasses}>
{customPreview ?? (
<Graphic
name={
typeof graphicNames === "string" ? graphicNames : graphicNames[0]
}
/>
)}
<div className={`${baseClass}__content`}>
<div className={`${baseClass}__name`}>{fileDetails.name}</div>
{fileDetails.description && (
<div className={`${baseClass}__description`}>
{fileDetails.description}
</div>
)}
</div>
</div>
{!progress &&
canEdit &&
onFileSelect &&
(gitopsCompatible ? (
<GitOpsModeTooltipWrapper
position="top"
tipOffset={8}
renderChildren={(disableChildren) =>
renderEditButton(disableChildren)
}
/>
) : (
renderEditButton()
))}
{!progress && onDeleteFile && (
<div className={`${baseClass}__delete`}>
<Button
className={`${baseClass}__delete-button`}
variant="subdued"
onClick={onDeleteFile}
>
<label htmlFor="delete-file">
<Icon name="trash" color="ui-fleet-black-75" />
</label>
</Button>
</div>
)}
{!!progress && (
<div className={`${baseClass}__progress-wrapper`}>
<div className={`${baseClass}__progress-bar`}>
<div
className={`${baseClass}__progress-bar--uploaded`}
style={{
width: `${progress * 100}%`,
}}
title="upload progress bar"
/>
</div>
<div className={`${baseClass}__progress-text`}>
{Math.round(progress * 100)}%
</div>
</div>
)}
</div>
);
};
export default FileDetails;