Fleet UI: Add textarea variant 'code' and apply to various modals (#27930)

This commit is contained in:
RachelElysia
2025-04-10 11:25:28 -04:00
committed by GitHub
parent 9de88274b0
commit ae94bc88c6
10 changed files with 92 additions and 61 deletions
@@ -1,3 +1,5 @@
// Used on: Dashboard > activity, Host details > past activity
import React from "react";
import { useQuery } from "react-query";
@@ -115,16 +117,17 @@ export const AppInstallDetails = ({
</div>
{showCommandPayload && (
<div className={`${baseClass}__script-output`}>
Request payload:
<Textarea className={`${baseClass}__output-textarea`}>
<Textarea label="Request payload:" variant="code">
{result.payload}
</Textarea>
</div>
)}
{showCommandResponse && (
<div className={`${baseClass}__script-output`}>
The response from {formattedHost}:
<Textarea className={`${baseClass}__output-textarea`}>
<Textarea
label={`The response from ${formattedHost}:`}
variant="code"
>
{result.result}
</Textarea>
</div>
@@ -10,20 +10,7 @@
gap: $pad-small;
margin: 0;
.icon {
padding-top: 3px;
align-self: flex-start;
}
}
&__script-output {
padding-top: $pad-xlarge;
.textarea {
margin-top: $pad-medium;
overflow-wrap: break-word;
}
}
&__output-textarea {
max-height: 300px;
overflow-y: auto;
}
}
@@ -1,3 +1,5 @@
// Used on: Dashboard > activity, Host details > past activity
import React from "react";
import { useQuery } from "react-query";
import { formatDistanceToNow } from "date-fns";
@@ -79,8 +81,10 @@ const Output = ({
}) => {
return (
<div className={`${baseClass}__script-output`}>
{SOFTWARE_INSTALL_OUTPUT_DISPLAY_LABELS[displayKey]}:
<Textarea className={`${baseClass}__output-textarea`}>
<Textarea
label={`${SOFTWARE_INSTALL_OUTPUT_DISPLAY_LABELS[displayKey]}:`}
variant="code"
>
{result[displayKey]}
</Textarea>
</div>
@@ -126,24 +130,22 @@ export const SoftwareInstallDetails = ({
return (
<>
<div className={`${baseClass}__software-install-details`}>
<StatusMessage
result={
result.host_display_name ? result : { ...result, host_display_name } // prefer result.host_display_name (it may be empty if the host was deleted) otherwise default to whatever we received via props
}
/>
{result.status !== "pending_install" && (
<>
{result.pre_install_query_output && (
<Output displayKey="pre_install_query_output" result={result} />
)}
{result.output && <Output displayKey="output" result={result} />}
{result.post_install_script_output && (
<Output displayKey="post_install_script_output" result={result} />
)}
</>
)}
</div>
<StatusMessage
result={
result.host_display_name ? result : { ...result, host_display_name } // prefer result.host_display_name (it may be empty if the host was deleted) otherwise default to whatever we received via props
}
/>
{result.status !== "pending_install" && (
<>
{result.pre_install_query_output && (
<Output displayKey="pre_install_query_output" result={result} />
)}
{result.output && <Output displayKey="output" result={result} />}
{result.post_install_script_output && (
<Output displayKey="post_install_script_output" result={result} />
)}
</>
)}
</>
);
};
@@ -1,21 +1,19 @@
.software-install-details {
overflow-wrap: anywhere; // Prevent long software name overflow
&__modal-content {
display: flex;
flex-direction: column;
gap: $pad-medium;
}
&__status-message {
display: flex;
align-items: center;
gap: $pad-small;
margin: 0;
.icon {
padding-top: 3px;
align-self: flex-start;
}
}
&__script-output {
padding-top: $pad-xlarge;
.textarea {
margin-top: $pad-medium;
overflow-wrap: break-word;
}
}
}
@@ -1,3 +1,5 @@
// Used on: Dashboard > activity, Host details > past activity
import Button from "components/buttons/Button";
import DataError from "components/DataError";
import Icon from "components/Icon";
@@ -127,15 +129,13 @@ const SoftwareUninstallDetailsModal = ({
{!isPendingStatus(status) && scriptResult && (
<>
<div className={`${baseClass}__script-output`}>
Uninstall script content:
<Textarea className={`${baseClass}__output-textarea`}>
<Textarea label="Uninstall script content:" variant="code">
{scriptResult.script_contents}
</Textarea>
</div>
<div className={`${baseClass}__script-output`}>
Uninstall script output:
<Textarea className={`${baseClass}__output-textarea`}>
<Textarea label="Uninstall script output:" variant="code">
{scriptResult.output}
</Textarea>
</div>
@@ -13,11 +13,4 @@
align-self: flex-start;
}
}
&__script-output {
.textarea {
margin-top: $pad-medium;
overflow-wrap: break-word;
font-family: "SourceCodePro", $monospace;
}
}
}
@@ -0,0 +1,24 @@
import { Meta, StoryObj } from "@storybook/react";
import Textarea from ".";
import "../../index.scss";
const meta: Meta<typeof Textarea> = {
component: Textarea,
title: "Components/Textarea",
args: {
children: "Default textarea content",
},
};
export default meta;
type Story = StoryObj<typeof Textarea>;
export const ExampleWithLabelAndCodeVariant: Story = {
args: {
variant: "code",
label: "Textarea label:",
children:
"const example = 'This is code styled text';\n// With line breaks preserved.\n\n Text with\r\ncarriage returns\r\nconverted to\nline breaks",
},
};
+15 -3
View File
@@ -6,10 +6,13 @@ const baseClass = "textarea";
interface ITextareaProps {
children: React.ReactNode;
className?: string;
label?: React.ReactNode;
/** code - code font, max height 300px */
variant?: "code" | "default";
}
// A textarea component that encapsulates common styles and functionality.
const Textarea = ({ children, className }: ITextareaProps) => {
const Textarea = ({ children, className, label, variant }: ITextareaProps) => {
// this is to preserve line breaks when we encounter a carriage return character.
// We could not find a way to preserve line breaks in the CSS alone for this
// character.
@@ -17,8 +20,17 @@ const Textarea = ({ children, className }: ITextareaProps) => {
children = children.replace(/\r/g, "\n");
}
const classNames = classnames(baseClass, className);
return <div className={classNames}>{children}</div>;
const wrapperClasses = classnames(`${baseClass}-wrapper`, className);
const textareaClasses = classnames(baseClass, {
[`${baseClass}--code`]: variant === "code",
});
return (
<div className={wrapperClasses}>
{label && <div className={`${baseClass}__label`}>{label}</div>}
<div className={textareaClasses}>{children}</div>
</div>
);
};
export default Textarea;
+13
View File
@@ -4,4 +4,17 @@
border: 1px solid $ui-fleet-black-10;
background-color: $ui-off-white;
white-space: pre-wrap;
&-wrapper {
display: flex;
flex-direction: column;
gap: $pad-medium;
}
&--code {
overflow-wrap: break-word;
overflow-y: auto;
font-family: "SourceCodePro", $monospace;
max-height: 300px;
}
}
@@ -268,8 +268,7 @@ const ScriptDetailsModal = ({
className={`${baseClass}__script-content modal-scrollable-content`}
ref={topDivRef}
>
<span>Script content:</span>
<Textarea className={`${baseClass}__script-content-textarea`}>
<Textarea label="Script content" variant="code">
{scriptContent}
</Textarea>
{runScriptHelpText && (