diff --git a/changes/45227-long-policy-res b/changes/45227-long-policy-res
new file mode 100644
index 0000000000..b0c36edd25
--- /dev/null
+++ b/changes/45227-long-policy-res
@@ -0,0 +1 @@
+* Long policy resolution text now wraps on the policy details page instead of being truncated.
diff --git a/frontend/components/DataSet/DataSet.stories.tsx b/frontend/components/DataSet/DataSet.stories.tsx
index 40dcb565bb..6571cc7251 100644
--- a/frontend/components/DataSet/DataSet.stories.tsx
+++ b/frontend/components/DataSet/DataSet.stories.tsx
@@ -1,3 +1,4 @@
+import React from "react";
import { Meta, StoryObj } from "@storybook/react";
import DataSet from "./DataSet";
@@ -22,3 +23,19 @@ export const HorizontalOrientation: Story = {
orientation: "horizontal",
},
};
+
+// Multiline values wrap instead of truncating with an ellipsis. Use for
+// free-form prose like "Resolve" or "Description".
+export const Multiline: Story = {
+ args: {
+ title: "Resolve",
+ value:
+ "Re-enable FileVault on the device. Open System Settings, navigate to Privacy & Security, and turn on FileVault. Store the recovery key in a safe place.",
+ multiline: true,
+ },
+ decorators: [
+ (Story) => (
+
{Story()}
+ ),
+ ],
+};
diff --git a/frontend/components/DataSet/DataSet.tests.tsx b/frontend/components/DataSet/DataSet.tests.tsx
new file mode 100644
index 0000000000..80fe824e90
--- /dev/null
+++ b/frontend/components/DataSet/DataSet.tests.tsx
@@ -0,0 +1,56 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+
+import DataSet from "./DataSet";
+
+describe("DataSet", () => {
+ it("renders title and value", () => {
+ render();
+ expect(screen.getByText("Author")).toBeInTheDocument();
+ expect(screen.getByText("Alice")).toBeInTheDocument();
+ });
+
+ it("uses vertical orientation by default (no horizontal class, no colon)", () => {
+ const { container } = render();
+ expect(container.querySelector(".data-set__horizontal")).toBeNull();
+ expect(container.querySelector("dt")?.textContent).toBe("Author");
+ });
+
+ it("applies horizontal orientation class and appends a colon to the title", () => {
+ const { container } = render(
+
+ );
+ expect(
+ container.querySelector(".data-set__horizontal")
+ ).toBeInTheDocument();
+ expect(container.querySelector("dt")?.textContent).toBe("Author:");
+ });
+
+ it("applies the text-only modifier when textOnly is set", () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector(".data-set--text-only")).toBeInTheDocument();
+ });
+
+ it("applies the multiline modifier when multiline is set", () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector(".data-set--multiline")).toBeInTheDocument();
+ });
+
+ it("does not apply the multiline modifier by default", () => {
+ const { container } = render();
+ expect(container.querySelector(".data-set--multiline")).toBeNull();
+ });
+
+ it("merges a custom className alongside the base class", () => {
+ const { container } = render(
+
+ );
+ const root = container.firstChild as HTMLElement;
+ expect(root).toHaveClass("data-set");
+ expect(root).toHaveClass("custom-class");
+ });
+});
diff --git a/frontend/components/DataSet/DataSet.tsx b/frontend/components/DataSet/DataSet.tsx
index ddc952c637..60279b0f39 100644
--- a/frontend/components/DataSet/DataSet.tsx
+++ b/frontend/components/DataSet/DataSet.tsx
@@ -13,6 +13,12 @@ interface IDataSetProps {
* baseline. Do NOT use when the value contains icons, buttons, or status
* indicators that need vertical centering. */
textOnly?: boolean;
+ /** When true, the value wraps onto multiple lines instead of truncating
+ * with an ellipsis. Use for free-form prose like "Resolve" or
+ * "Description" where the full text needs to be readable. Default behavior
+ * (single-line truncation with ellipsis) assumes the consumer wraps the
+ * value in a tooltip-on-truncate helper. */
+ multiline?: boolean;
className?: string;
}
@@ -21,11 +27,13 @@ const DataSet = ({
value,
orientation = "vertical",
textOnly = false,
+ multiline = false,
className,
}: IDataSetProps) => {
const classNames = classnames(baseClass, className, {
[`${baseClass}__horizontal`]: orientation === "horizontal",
[`${baseClass}--text-only`]: textOnly,
+ [`${baseClass}--multiline`]: multiline,
});
return (
diff --git a/frontend/components/DataSet/_styles.scss b/frontend/components/DataSet/_styles.scss
index 0ee63f3e41..d066c4acee 100644
--- a/frontend/components/DataSet/_styles.scss
+++ b/frontend/components/DataSet/_styles.scss
@@ -23,6 +23,12 @@
align-items: baseline;
}
+ &--multiline dd {
+ white-space: pre-wrap;
+ overflow: visible;
+ overflow-wrap: anywhere;
+ }
+
&__horizontal {
flex-direction: row;
}
diff --git a/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx b/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx
index 9860b114de..3dafe7c647 100644
--- a/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx
+++ b/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx
@@ -426,6 +426,7 @@ const PolicyDetailsPage = ({
className={`${baseClass}__resolve`}
title="Resolve"
value={lastEditedQueryResolution}
+ multiline
/>
)}
{renderAuthor()}