diff --git a/packages/ui-components/src/components/DescriptionDefinition/DescriptionDefinition.component.tsx b/packages/ui-components/src/components/DescriptionDefinition/DescriptionDefinition.component.tsx
new file mode 100644
index 0000000000..9b66fb0bd2
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionDefinition/DescriptionDefinition.component.tsx
@@ -0,0 +1,28 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { ReactNode } from "react"
+import "./description-definition.css"
+
+export interface DescriptionDefinitionProps {
+ /**
+ * Content to be displayed as the description, accommodating text or more complex nodes to explain or define the associated term.
+ */
+ children: ReactNode
+ /**
+ * Additional class names for applying custom styles or overriding default styles on the
element.
+ */
+ className?: string
+}
+
+/**
+ * Represents the definition or description in a description list, rendering as an HTML element.
+ * Pairs with DescriptionTerm to complete the term-description association, offering flexible content styling.
+ */
+export const DescriptionDefinition: React.FC = ({ children, className = "" }) => (
+ {children}
+)
+
+export const DD = DescriptionDefinition
diff --git a/packages/ui-components/src/components/DescriptionDefinition/DescriptionDefinition.test.tsx b/packages/ui-components/src/components/DescriptionDefinition/DescriptionDefinition.test.tsx
new file mode 100644
index 0000000000..b6e305f76e
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionDefinition/DescriptionDefinition.test.tsx
@@ -0,0 +1,39 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from "react"
+import { render, screen } from "@testing-library/react"
+import { describe, it, expect } from "vitest"
+import { DescriptionDefinition } from "../DescriptionDefinition"
+
+describe("DescriptionDefinition", () => {
+ it("renders the children correctly", () => {
+ render(Test Description)
+ expect(screen.getByText("Test Description")).toBeInTheDocument()
+ })
+
+ it("applies custom className", () => {
+ const customClass = "custom-class"
+ render(Test Description)
+ const ddElement = screen.getByText("Test Description")
+ expect(ddElement).toHaveClass(customClass)
+ })
+
+ it("renders within a element", () => {
+ render(Test Description)
+ const ddElement = screen.getByText("Test Description")
+ expect(ddElement.tagName).toBe("DD")
+ })
+
+ it("can render complex children", () => {
+ render(
+
+ Complex Content
+
+ )
+ expect(screen.getByText("Complex")).toBeInTheDocument()
+ expect(screen.getByText("Content")).toBeInTheDocument()
+ })
+})
diff --git a/packages/ui-components/src/components/DescriptionDefinition/description-definition.css b/packages/ui-components/src/components/DescriptionDefinition/description-definition.css
new file mode 100644
index 0000000000..8b835c1c72
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionDefinition/description-definition.css
@@ -0,0 +1,15 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+.dd {
+ display: grid;
+ align-items: center;
+ border-bottom: 1px solid var(--color-dd-dt-border);
+ background-color: var(--color-dd-background);
+ color: var(--color-dd-text);
+ gap: 0.25rem;
+ grid-column: 2;
+ height: 2rem;
+}
diff --git a/packages/ui-components/src/components/DescriptionDefinition/index.ts b/packages/ui-components/src/components/DescriptionDefinition/index.ts
new file mode 100644
index 0000000000..b419dbeb63
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionDefinition/index.ts
@@ -0,0 +1,6 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { DescriptionDefinition, type DescriptionDefinitionProps } from "./DescriptionDefinition.component"
diff --git a/packages/ui-components/src/components/DescriptionList/DescriptionList.component.tsx b/packages/ui-components/src/components/DescriptionList/DescriptionList.component.tsx
new file mode 100644
index 0000000000..c3844a5458
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionList/DescriptionList.component.tsx
@@ -0,0 +1,45 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { ReactElement } from "react"
+import { DescriptionTermProps } from "../DescriptionTerm"
+import { DescriptionDefinitionProps } from "../DescriptionDefinition"
+import "./description-list.css"
+
+export interface DescriptionListProps {
+ /**
+ * Child components must be either DescriptionTerm or DescriptionDefinition to maintain semantic structure.
+ * Supports multiple instances to create a detailed list of terms and definitions.
+ */
+ children:
+ | ReactElement
+ | Array>
+ | ReactElement<"div">
+ /**
+ * Determines the alignment of terms within the list. Align terms to the left or right based on preference for display style.
+ */
+ alignTerms?: "left" | "right"
+ /**
+ * Additional custom class names to apply styles to the element or to extend styling from the design system.
+ */
+ className?: string
+}
+
+/**
+ * A wrapper component that semantically represents a list of terms and their corresponding descriptions using HTML elements.
+ * This component enforces structure by expecting child elements of DescriptionTerm or DescriptionDefinition,
+ * aligning them according to the specified terms alignment.
+ */
+export const DescriptionList: React.FC = ({ children, alignTerms = "right", className = "" }) => (
+
+ {children}
+
+)
+
+export const DL = DescriptionList
diff --git a/packages/ui-components/src/components/DescriptionList/DescriptionList.stories.tsx b/packages/ui-components/src/components/DescriptionList/DescriptionList.stories.tsx
new file mode 100644
index 0000000000..f2dd4d296f
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionList/DescriptionList.stories.tsx
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from "react"
+import type { Meta, StoryObj } from "@storybook/react-vite"
+import { DescriptionList } from "./DescriptionList.component"
+import { DT } from "../DescriptionTerm/DescriptionTerm.component"
+import { DD } from "../DescriptionDefinition/DescriptionDefinition.component"
+
+const meta: Meta = {
+ title: "Components/DescriptionList",
+ component: DescriptionList,
+ argTypes: {
+ children: {
+ control: false,
+ },
+ },
+}
+
+export default meta
+type Story = StoryObj
+
+export const Default: Story = {
+ render: (args) => (
+
+ - Warranty
+ - 2 years limited warranty with options for extension.
+
+ ),
+}
+
+/**
+ * You can use many definitions per term.
+ */
+export const MultipleDefinitions: Story = {
+ render: (args) => (
+
+ - Shipping
+ - Standard shipping: 5-7 business days.
+ - Payment Options
+ - Credit/Debit cards, PayPal, and bank transfer.
+
+
- Delivery Time
+ - 1 day, 2 days, 3 days.
+
+
+ ),
+}
diff --git a/packages/ui-components/src/components/DescriptionList/DescriptionList.test.tsx b/packages/ui-components/src/components/DescriptionList/DescriptionList.test.tsx
new file mode 100644
index 0000000000..c4226e85d3
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionList/DescriptionList.test.tsx
@@ -0,0 +1,70 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from "react"
+import { render, screen } from "@testing-library/react"
+import { describe, it, expect } from "vitest"
+import { DescriptionList } from "./DescriptionList.component"
+import { DescriptionTerm } from "../DescriptionTerm"
+import { DescriptionDefinition } from "../DescriptionDefinition"
+
+describe("DescriptionList", () => {
+ it("renders child DescriptionTerm and DescriptionDefinition components correctly", () => {
+ render(
+
+ Term 1
+ Definition 1
+
+ )
+ expect(screen.getByText("Term 1")).toBeInTheDocument()
+ expect(screen.getByText("Definition 1")).toBeInTheDocument()
+ })
+
+ it("applies custom className to the element", () => {
+ const customClass = "custom-class"
+ render(
+
+ Term 2
+ Definition 2
+
+ )
+
+ const dlElement = screen.getByTestId("description-list")
+ expect(dlElement).toHaveClass(customClass)
+ })
+
+ it("aligns terms to the right by default", () => {
+ render(
+
+ Term 3
+ Definition 3
+
+ )
+ })
+
+ it("aligns terms to the left when specified", () => {
+ render(
+
+ Left Term
+ Definition for Left Term
+
+ )
+ })
+
+ it("renders multiple terms and definitions in a single list", () => {
+ render(
+
+ Term 4
+ Definition 4
+ Term 5
+ Definition 5
+
+ )
+ expect(screen.getByText("Term 4")).toBeInTheDocument()
+ expect(screen.getByText("Definition 4")).toBeInTheDocument()
+ expect(screen.getByText("Term 5")).toBeInTheDocument()
+ expect(screen.getByText("Definition 5")).toBeInTheDocument()
+ })
+})
diff --git a/packages/ui-components/src/components/DescriptionList/description-list.css b/packages/ui-components/src/components/DescriptionList/description-list.css
new file mode 100644
index 0000000000..ad1f8526b9
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionList/description-list.css
@@ -0,0 +1,22 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+.dl {
+ display: grid;
+ grid-template-columns: 1fr 2fr;
+ padding: 0.5rem;
+}
+
+.dl > div {
+ display: contents;
+}
+
+.dl > div > .dt {
+ grid-column: 1;
+}
+
+.dl > div > .dd {
+ grid-column: 2;
+}
diff --git a/packages/ui-components/src/components/DescriptionList/index.ts b/packages/ui-components/src/components/DescriptionList/index.ts
new file mode 100644
index 0000000000..d8d66b9ab9
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionList/index.ts
@@ -0,0 +1,6 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { DescriptionList, type DescriptionListProps } from "./DescriptionList.component"
diff --git a/packages/ui-components/src/components/DescriptionTerm/DescriptionTerm.component.tsx b/packages/ui-components/src/components/DescriptionTerm/DescriptionTerm.component.tsx
new file mode 100644
index 0000000000..f9c5dea1d1
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionTerm/DescriptionTerm.component.tsx
@@ -0,0 +1,28 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { ReactNode } from "react"
+import "./description-term.css"
+
+export interface DescriptionTermProps {
+ /**
+ * Content to be displayed as the term, which could be simple text or any ReactNode, providing semantic meaning to the associated description.
+ */
+ children: ReactNode
+ /**
+ * Custom class names to apply additional styling to the - element, useful for overrides or custom styles.
+ */
+ className?: string
+}
+
+/**
+ * Represents a term in a description list, rendering an HTML
- element.
+ * Used to denote terms, headers, or keys in a semantic way, allowing for flexible styling.
+ */
+export const DescriptionTerm: React.FC = ({ children, className = "" }) => (
+
- {children}
+)
+
+export const DT = DescriptionTerm
diff --git a/packages/ui-components/src/components/DescriptionTerm/DescriptionTerm.test.tsx b/packages/ui-components/src/components/DescriptionTerm/DescriptionTerm.test.tsx
new file mode 100644
index 0000000000..a20e775f8f
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionTerm/DescriptionTerm.test.tsx
@@ -0,0 +1,39 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from "react"
+import { render, screen } from "@testing-library/react"
+import { describe, it, expect } from "vitest"
+import { DescriptionTerm } from "./DescriptionTerm.component"
+
+describe("DescriptionTerm", () => {
+ it("renders the children properly", () => {
+ render(Test Term)
+ expect(screen.getByText("Test Term")).toBeInTheDocument()
+ })
+
+ it("applies custom className to the - element", () => {
+ const customClass = "custom-class"
+ render(Styled Term)
+ const dtElement = screen.getByText("Styled Term")
+ expect(dtElement).toHaveClass(customClass)
+ })
+
+ it("renders within a
- element", () => {
+ render(Term Element)
+ const dtElement = screen.getByText("Term Element")
+ expect(dtElement.tagName.toLowerCase()).toBe("dt")
+ })
+
+ it("can render complex children", () => {
+ render(
+
+ Complex Term Content
+
+ )
+ expect(screen.getByText("Complex Term")).toBeInTheDocument()
+ expect(screen.getByText("Content")).toBeInTheDocument()
+ })
+})
diff --git a/packages/ui-components/src/components/DescriptionTerm/description-term.css b/packages/ui-components/src/components/DescriptionTerm/description-term.css
new file mode 100644
index 0000000000..8c0cdf0245
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionTerm/description-term.css
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+.dt {
+ display: grid;
+ align-items: center;
+ border-bottom: 1px solid var(--color-dd-dt-border);
+ background-color: var(--color-dt-background);
+ color: var(--color-dt-text);
+ font-weight: bold;
+ gap: 0.25rem;
+ grid-column: 1;
+ height: 2rem;
+ white-space: nowrap;
+}
diff --git a/packages/ui-components/src/components/DescriptionTerm/index.ts b/packages/ui-components/src/components/DescriptionTerm/index.ts
new file mode 100644
index 0000000000..99c3cb2656
--- /dev/null
+++ b/packages/ui-components/src/components/DescriptionTerm/index.ts
@@ -0,0 +1,6 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { DescriptionTerm, type DescriptionTermProps } from "./DescriptionTerm.component"
diff --git a/packages/ui-components/src/components/Modal/Modal.stories.tsx b/packages/ui-components/src/components/Modal/Modal.stories.tsx
index 5e78781455..a3d97f1fb5 100644
--- a/packages/ui-components/src/components/Modal/Modal.stories.tsx
+++ b/packages/ui-components/src/components/Modal/Modal.stories.tsx
@@ -115,6 +115,35 @@ export const SimpleConfirmDialogWithDisabledButtons: Story = {
},
}
+export const DisabledCloseButton: Story = {
+ render: Template,
+ args: {
+ title: "Disabled Close Button Modal",
+ children:
This Modal has a disabled top-right close button.
,
+ disableCloseButton: true,
+ cancelButtonLabel: "Cancel",
+ },
+}
+
+export const NonCloseable: Story = {
+ render: Template,
+ args: {
+ title: "Non-Closeable Modal",
+ children:
+ "Use only if all else fails. If you need to inform users about something, in 99.9% of cases is the better choice.",
+ closeable: false,
+ },
+}
+
+export const CloseOnBackdropClick: Story = {
+ render: Template,
+ args: {
+ title: "Close on Backdrop Click",
+ children: This Modal closes when clicking the backdrop.
,
+ closeOnBackdropClick: true,
+ },
+}
+
export const AutoFocusDialog: Story = {
render: Template,
args: {
@@ -209,35 +238,6 @@ export const XXLWithForm: Story = {
},
}
-export const NonCloseable: Story = {
- render: Template,
- args: {
- title: "Non-Closeable Modal",
- children:
- "Use only if all else fails. If you need to inform users about something, in 99.9% of cases is the better choice.",
- closeable: false,
- },
-}
-
-export const CloseOnBackdropClick: Story = {
- render: Template,
- args: {
- title: "Close on Backdrop Click",
- children: This Modal closes when clicking the backdrop.
,
- closeOnBackdropClick: true,
- },
-}
-
-export const DisabledCloseButton: Story = {
- render: Template,
- args: {
- title: "Disabled Close Button Modal",
- children: This Modal has a disabled top-right close button.
,
- disableCloseButton: true,
- cancelButtonLabel: "Cancel",
- },
-}
-
export const Login: Story = {
render: Template,
args: {
diff --git a/packages/ui-components/src/global.css b/packages/ui-components/src/global.css
index a23bdd9675..095e7035c9 100644
--- a/packages/ui-components/src/global.css
+++ b/packages/ui-components/src/global.css
@@ -14,6 +14,9 @@
@import "./components/ThemeToggle/themeToggle.css" layer(utilities);
@import "./components/DataGridRow/data-grid-row.css" layer(utilities);
@import "./components/SideNavigationItem/sidenavigationitem.css" layer(utilities);
+@import "./components/DescriptionList/description-list.css" layer(utilities);
+@import "./components/DescriptionTerm/description-term.css" layer(utilities);
+@import "./components/DescriptionDefinition/description-definition.css" layer(utilities);
:root,
:host {
@@ -406,7 +409,7 @@
--text-color-theme-sidenavigation-item-active: var(--color-sidenavigation-item-active);
/* Component Border Colors: */
- --border-color-theme-default: var(--color-default-border);
+ --border-color-theme-default: var(--color-dd-dt-border);
--border-color-theme-box-default: var(--color-box-border);
@@ -722,6 +725,13 @@
/* LT Box Shadow */
--box-shadow-default: 0 1px 2px 0 rgba(34, 54, 73, 0.3);
--box-shadow-hover: 0 0 0 1px var(--color-color-shadow, rgba(79, 79, 79, 0.3)), 0 2px 8px 0 rgba(0, 0, 0, 0.3);
+ /* LT Description - DescriptionList, DescriptionTerm, DescriptionDefinition */
+ --color-dt-background: var(--color-background-lvl-1);
+ --color-dt-text: var(--color-text-high);
+ --color-dd-background: var(--color-transparent);
+ --color-dd-text: var(--color-text-high);
+ --color-dd-dt-border: var(--color-border-default);
+ --color-border-default: var(--color-juno-grey-light-7);
}
/* JUNO THEME: DARK MODE */
@@ -960,6 +970,13 @@
/* DT Box Shadow */
--box-shadow-default: 0 1px 2px 0 rgba(34, 54, 73, 0.3);
--box-shadow-hover: 0 1px 2px 0 rgba(34, 54, 73, 0.3);
+ /* DT Description - DescriptionList, DescriptionTerm, DescriptionDefinition */
+ --color-dt-background: var(--color-background-lvl-0);
+ --color-dt-text: var(--color-text-high);
+ --color-dd-background: var(--color-transparent);
+ --color-dd-text: var(--color-text-high);
+ --color-dd-dt-border: var(--color-border-default);
+ --color-border-default: var(--color-juno-grey-blue-3);
}
/*
diff --git a/packages/ui-components/src/theme.css b/packages/ui-components/src/theme.css
index d9049faf95..03f5718531 100644
--- a/packages/ui-components/src/theme.css
+++ b/packages/ui-components/src/theme.css
@@ -398,7 +398,7 @@
--text-color-theme-sidenavigation-item-active: var(--color-sidenavigation-item-active);
/* Component Border Colors: */
- --border-color-theme-default: var(--color-default-border);
+ --border-color-theme-default: var(--color-dd-dt-border);
--border-color-theme-box-default: var(--color-box-border);
@@ -715,6 +715,13 @@
/* LT Box Shadow */
--box-shadow-default: 0 1px 2px 0 rgba(34, 54, 73, 0.3);
--box-shadow-hover: 0 0 0 1px var(--color-color-shadow, rgba(79, 79, 79, 0.3)), 0 2px 8px 0 rgba(0, 0, 0, 0.3);
+ /* LT Description - DescriptionList, DescriptionTerm, DescriptionDefinition */
+ --color-dt-background: var(--color-background-lvl-1);
+ --color-dt-text: var(--color-text-high);
+ --color-dd-background: var(--color-transparent);
+ --color-dd-text: var(--color-text-high);
+ --color-dd-dt-border: var(--color-border-default);
+ --color-border-default: var(--color-juno-grey-light-7);
}
/* JUNO THEME: DARK MODE */
@@ -953,6 +960,13 @@
/* DT Box Shadow */
--box-shadow-default: 0 1px 2px 0 rgba(34, 54, 73, 0.3);
--box-shadow-hover: 0 1px 2px 0 rgba(34, 54, 73, 0.3);
+ /* DT Description - DescriptionList, DescriptionTerm, DescriptionDefinition */
+ --color-dt-background: var(--color-background-lvl-0);
+ --color-dt-text: var(--color-text-high);
+ --color-dd-background: var(--color-transparent);
+ --color-dd-text: var(--color-text-high);
+ --color-dd-dt-border: var(--color-border-default);
+ --color-border-default: var(--color-juno-grey-blue-3);
}
/*