mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore(site): minor refactor to the resource metadata code (#11746)
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { MockWorkspaceResource } from "testHelpers/entities";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { ResourceMetadata } from "./ResourceMetadata";
|
||||
|
||||
const meta: Meta<typeof ResourceMetadata> = {
|
||||
title: "pages/WorkspacePage/ResourceMetadata",
|
||||
component: ResourceMetadata,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ResourceMetadata>;
|
||||
|
||||
export const Markdown: Story = {
|
||||
args: {
|
||||
resource: {
|
||||
...MockWorkspaceResource,
|
||||
metadata: [
|
||||
{ key: "text", value: "hello", sensitive: false },
|
||||
{ key: "link", value: "[hello](#)", sensitive: false },
|
||||
{ key: "b/i", value: "_hello_, **friend**!", sensitive: false },
|
||||
{ key: "coder", value: "`beep boop`", sensitive: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithLongStrings: Story = {
|
||||
args: {
|
||||
resource: {
|
||||
...MockWorkspaceResource,
|
||||
metadata: [
|
||||
{
|
||||
key: "xxxxxxxxxxxx",
|
||||
value: "14",
|
||||
sensitive: false,
|
||||
},
|
||||
{
|
||||
key: "Long",
|
||||
value: "The quick brown fox jumped over the lazy dog",
|
||||
sensitive: false,
|
||||
},
|
||||
{
|
||||
key: "Really long",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
sensitive: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
import { MemoizedInlineMarkdown } from "components/Markdown/Markdown";
|
||||
import { SensitiveValue } from "components/Resources/SensitiveValue";
|
||||
import { CopyableValue } from "components/CopyableValue/CopyableValue";
|
||||
import { WorkspaceResource } from "api/typesGenerated";
|
||||
import { Children, FC, HTMLAttributes, PropsWithChildren } from "react";
|
||||
import { Interpolation, Theme } from "@emotion/react";
|
||||
|
||||
type ResourceMetadataProps = Omit<HTMLAttributes<HTMLElement>, "resource"> & {
|
||||
resource: WorkspaceResource;
|
||||
};
|
||||
|
||||
export const ResourceMetadata: FC<ResourceMetadataProps> = ({
|
||||
resource,
|
||||
...headerProps
|
||||
}) => {
|
||||
const metadata = resource.metadata ? [...resource.metadata] : [];
|
||||
|
||||
if (resource.daily_cost > 0) {
|
||||
metadata.push({
|
||||
key: "Daily cost",
|
||||
value: resource.daily_cost.toString(),
|
||||
sensitive: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (metadata.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<header css={styles.root} {...headerProps}>
|
||||
{metadata.map((meta) => {
|
||||
return (
|
||||
<div css={styles.item} key={meta.key}>
|
||||
<div css={styles.value}>
|
||||
{meta.sensitive ? (
|
||||
<SensitiveValue value={meta.value} />
|
||||
) : (
|
||||
<MemoizedInlineMarkdown components={{ p: MetaValue }}>
|
||||
{meta.value}
|
||||
</MemoizedInlineMarkdown>
|
||||
)}
|
||||
</div>
|
||||
<div css={styles.label}>{meta.key}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
const MetaValue = ({ children }: PropsWithChildren) => {
|
||||
const childrenArray = Children.toArray(children);
|
||||
if (childrenArray.every((child) => typeof child === "string")) {
|
||||
return (
|
||||
<CopyableValue value={childrenArray.join("")}>{children}</CopyableValue>
|
||||
);
|
||||
}
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
const styles = {
|
||||
root: (theme) => ({
|
||||
padding: 24,
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
gap: 48,
|
||||
rowGap: 24,
|
||||
marginBottom: 24,
|
||||
fontSize: 14,
|
||||
background: `linear-gradient(180deg, ${theme.palette.background.default} 0%, rgba(0, 0, 0, 0) 100%)`,
|
||||
}),
|
||||
|
||||
item: () => ({
|
||||
lineHeight: "1.5",
|
||||
}),
|
||||
|
||||
label: (theme) => ({
|
||||
fontSize: 13,
|
||||
color: theme.palette.text.secondary,
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
}),
|
||||
|
||||
value: () => ({
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import Button from "@mui/material/Button";
|
||||
import AlertTitle from "@mui/material/AlertTitle";
|
||||
import { PropsWithChildren, type FC, Children } from "react";
|
||||
import { type FC } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import type * as TypesGen from "api/typesGenerated";
|
||||
import { Alert, AlertDetail } from "components/Alert/Alert";
|
||||
@@ -21,9 +21,7 @@ import HubOutlined from "@mui/icons-material/HubOutlined";
|
||||
import { ResourcesSidebar } from "./ResourcesSidebar";
|
||||
import { WorkspacePermissions } from "./permissions";
|
||||
import { resourceOptionValue, useResourcesNav } from "./useResourcesNav";
|
||||
import { MemoizedInlineMarkdown } from "components/Markdown/Markdown";
|
||||
import { SensitiveValue } from "components/Resources/SensitiveValue";
|
||||
import { CopyableValue } from "components/CopyableValue/CopyableValue";
|
||||
import { ResourceMetadata } from "./ResourceMetadata";
|
||||
|
||||
export interface WorkspaceProps {
|
||||
handleStart: (buildParameters?: TypesGen.WorkspaceBuildParameter[]) => void;
|
||||
@@ -187,7 +185,10 @@ export const Workspace: FC<WorkspaceProps> = ({
|
||||
<div css={styles.content}>
|
||||
<div css={styles.dotBackground}>
|
||||
{selectedResource && (
|
||||
<WorkspaceResourceData resource={selectedResource} />
|
||||
<ResourceMetadata
|
||||
resource={selectedResource}
|
||||
css={{ margin: "-48px 0 24px -48px" }}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
css={{
|
||||
@@ -282,55 +283,6 @@ export const Workspace: FC<WorkspaceProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const WorkspaceResourceData: FC<{ resource: TypesGen.WorkspaceResource }> = ({
|
||||
resource,
|
||||
}) => {
|
||||
const metadata = resource.metadata ? [...resource.metadata] : [];
|
||||
|
||||
if (resource.daily_cost > 0) {
|
||||
metadata.push({
|
||||
key: "Daily cost",
|
||||
value: resource.daily_cost.toString(),
|
||||
sensitive: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (metadata.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<header css={styles.resourceData}>
|
||||
{metadata.map((meta) => {
|
||||
return (
|
||||
<div css={styles.resourceDataItem} key={meta.key}>
|
||||
<div css={styles.resourceDataItemValue}>
|
||||
{meta.sensitive ? (
|
||||
<SensitiveValue value={meta.value} />
|
||||
) : (
|
||||
<MemoizedInlineMarkdown components={{ p: MetaValue }}>
|
||||
{meta.value}
|
||||
</MemoizedInlineMarkdown>
|
||||
)}
|
||||
</div>
|
||||
<div css={styles.resourceDataItemLabel}>{meta.key}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
const MetaValue = ({ children }: PropsWithChildren) => {
|
||||
const childrenArray = Children.toArray(children);
|
||||
if (childrenArray.every((child) => typeof child === "string")) {
|
||||
return (
|
||||
<CopyableValue value={childrenArray.join("")}>{children}</CopyableValue>
|
||||
);
|
||||
}
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
const countAgents = (resource: TypesGen.WorkspaceResource) => {
|
||||
return resource.agents ? resource.agents.length : 0;
|
||||
};
|
||||
@@ -365,34 +317,4 @@ const styles = {
|
||||
flexDirection: "column",
|
||||
},
|
||||
}),
|
||||
|
||||
resourceData: (theme) => ({
|
||||
padding: 24,
|
||||
margin: "-48px 0 0 -48px",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
gap: 48,
|
||||
rowGap: 24,
|
||||
marginBottom: 24,
|
||||
fontSize: 14,
|
||||
background: `linear-gradient(180deg, ${theme.palette.background.default} 0%, rgba(0, 0, 0, 0) 100%)`,
|
||||
}),
|
||||
|
||||
resourceDataItem: () => ({
|
||||
lineHeight: "1.5",
|
||||
}),
|
||||
|
||||
resourceDataItemLabel: (theme) => ({
|
||||
fontSize: 13,
|
||||
color: theme.palette.text.secondary,
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
}),
|
||||
|
||||
resourceDataItemValue: () => ({
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
Reference in New Issue
Block a user