From 3aee474006afd48e401bafe00ec70e56bc2c5673 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 12:38:11 +0200 Subject: [PATCH 01/18] feat(ui): streamline DatagridCheckboxCell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - don’t render Checkbox automatically, but accept children - always center vertically Signed-off-by: Franz Heidl --- .../DataGridCheckboxCell.component.tsx | 44 +++---- .../DataGridCheckboxCell.stories.tsx | 112 ++++++++++++++---- .../DataGridCheckboxCell.test.tsx | 21 +++- 3 files changed, 127 insertions(+), 50 deletions(-) diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx index 3c8f858eb9..604dfc3fcd 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx @@ -3,49 +3,35 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { ChangeEventHandler, HTMLAttributes, ReactNode } from "react" -import { Checkbox } from "../Checkbox/Checkbox.component" +import React, { HTMLAttributes, ReactNode } from "react" import { DataGridCell } from "../DataGridCell/DataGridCell.component" /** - * `DataGridCheckboxCell` integrates a checkbox within a `DataGrid` cell, ideal for selection-based interactions. - * It manages the selected state and supports custom behaviors upon state changes. - * @see https://cloudoperators.github.io/juno/?path=/docs/wip-datagrid-datagridcheckboxcell--docs + * `DataGridCheckboxCell` is a `DataGrid` cell pre-configured for holding a `Checkbox` component. + * Place a `Checkbox` as a child and wire up selection state and event handlers directly on it. + * @see https://cloudoperators.github.io/juno/?path=/docs/components-datagrid-datagridcheckboxcell--docs * @see {@link DataGridCheckboxCellProps} */ -export const DataGridCheckboxCell = ({ - selected = false, - disabled = false, - className = "", - onChange, - ...props -}: DataGridCheckboxCellProps): ReactNode => { +export const DataGridCheckboxCell = ({ className = "", children, ...props }: DataGridCheckboxCellProps): ReactNode => { return ( - - + // Always center vertically regardless of the parent DataGrid's cellVerticalAlignment context. + + {children} ) } -export interface DataGridCheckboxCellProps extends HTMLAttributes { - /** - * Indicates selected state of the associated row. - * @default false - */ - selected?: boolean - - /** - * Determines if the checkbox is disabled. - * @default false - */ - disabled?: boolean +// Omit onChange: it would silently spread onto the underlying div (a no-op) instead of reaching the Checkbox child, misleading consumers into thinking they're wiring up checkbox state. +export interface DataGridCheckboxCellProps extends Omit, "onChange"> { + /** A `Checkbox` component to render inside the cell. */ + children?: ReactNode /** * Additional class names for styling. * @default "" */ className?: string - - /** Event handler for changes in the checkbox's state. */ - onChange?: ChangeEventHandler } diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx index 14531955bb..9499463ddc 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx @@ -4,27 +4,22 @@ */ import type { Meta, StoryObj } from "@storybook/react-vite" -import React from "react" +import React, { useState } from "react" import { DataGrid } from "../DataGrid/index" import { DataGridRow } from "../DataGridRow/index" +import { DataGridCell } from "../DataGridCell/index" +import { DataGridHeadCell } from "../DataGridHeadCell/index" import { DataGridCheckboxCell } from "./index" +import { Checkbox } from "../Checkbox/index" const meta: Meta = { - title: "WiP/DataGrid/DataGridCheckboxCell", + title: "Components/DataGrid/DataGridCheckboxCell", component: DataGridCheckboxCell, - decorators: [ - (Story) => ( - - - - - - ), - ], - parameters: { - docs: { - source: { - excludeDecorators: false, + argTypes: { + children: { + control: false, + table: { + type: { summary: "ReactNode" }, }, }, }, @@ -33,26 +28,103 @@ const meta: Meta = { export default meta type Story = StoryObj +const items = [ + { id: "1", name: "node-prod-01", status: "Running" }, + { id: "2", name: "node-prod-02", status: "Stopped" }, + { id: "3", name: "node-staging-01", status: "Error" }, +] + export const Default: Story = { parameters: { docs: { description: { - story: "Juno DataGridCheckboxCell for use in DataGrid", + story: + "A `DataGridCheckboxCell` with a `Checkbox` child. Wire up selection state and all event handlers directly on the `Checkbox`. Use `minContentColumns` on the parent `DataGrid` to keep the checkbox cell as narrow as its content.", }, }, }, - args: {}, + render: () => { + const [selected, setSelected] = useState>({}) + return ( + + + + Name + Status + + {items.map((item) => ( + + + setSelected((s) => ({ ...s, [item.id]: e.target.checked }))} + /> + + {item.name} + {item.status} + + ))} + + ) + }, } export const Disabled: Story = { parameters: { docs: { description: { - story: "Disabled Juno DataGridCheckboxCell for use in DataGrid", + story: "A `DataGridCheckboxCell` with a disabled `Checkbox`. Disable the `Checkbox` directly.", }, }, }, - args: { - disabled: true, + render: () => ( + + + + Name + Status + + {items.map((item) => ( + + + + + {item.name} + {item.status} + + ))} + + ), +} + +export const AlwaysCentered: Story = { + parameters: { + docs: { + description: { + story: + '`DataGridCheckboxCell` always centers its content vertically, regardless of the parent `DataGrid`\'s `cellVerticalAlignment` setting. Here the grid is set to `cellVerticalAlignment="top"`, but the checkbox remains centered.', + }, + }, }, + render: () => ( + + + + Name + Notes + + {items.map((item) => ( + + + + + {item.name} + + This cell has multiple lines of content to make the row tall enough to demonstrate that the checkbox remains + vertically centered even when the grid is configured to align cell content to the top. + + + ))} + + ), } diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx index acc612c666..e5dc98497c 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx @@ -6,16 +6,35 @@ import * as React from "react" import { render, screen } from "@testing-library/react" import { DataGridCheckboxCell } from "./index" +import { Checkbox } from "../Checkbox" describe("DataGridCheckboxCell", () => { test("renders a DataGridCheckboxCell", () => { render() expect(screen.getByRole("gridcell")).toBeInTheDocument() + expect(screen.getByRole("gridcell")).toHaveClass("juno-datagrid-checkbox-cell") }) test("renders a custom className", () => { render() - expect(screen.getByRole("gridcell")).toBeInTheDocument() expect(screen.getByRole("gridcell")).toHaveClass("my-custom-class") }) + + test("renders a Checkbox child", () => { + render( + + + + ) + expect(screen.getByRole("checkbox")).toBeInTheDocument() + }) + + test("renders other children", () => { + render( + + + + ) + expect(screen.getByTestId("child")).toBeInTheDocument() + }) }) From 3abf2b24a99ef313604fd921c38120df123ab7b7 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 12:42:02 +0200 Subject: [PATCH 02/18] feat(ui): update DataGridHeader story to reflect change Signed-off-by: Franz Heidl --- .../DataGrid/DataGridHeader.stories.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx b/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx index 18e3d92839..76ea7d4abf 100644 --- a/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx +++ b/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx @@ -157,6 +157,7 @@ export const WithSearchOnly: Story = { export const FullyFeatured: Story = { render: () => { const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc") + const [selected, setSelected] = useState>({}) return ( <> @@ -250,7 +251,12 @@ export const FullyFeatured: Story = { {servers.map((s) => ( - + + setSelected((prev) => ({ ...prev, [s.id]: e.target.checked }))} + /> + {s.name} {s.region} {s.status} @@ -367,7 +373,12 @@ export const FullyFeatured: Story = { {servers.map((s) => ( - + + setSelected((prev) => ({ ...prev, [s.id]: e.target.checked }))} + /> + {s.name} {s.region} {s.status} From 021127b4bd07dbe1d09152a64ad4a2c70b199e4b Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 12:43:43 +0200 Subject: [PATCH 03/18] feat(ui): add verticalAlignment prop to DatagridCell allow for overriding the vertical alignment set on the parent Datagrid Signed-off-by: Franz Heidl --- .../DataGridCell/DataGridCell.component.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.component.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.component.tsx index 4d4c579ae0..1ab02735e0 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.component.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.component.tsx @@ -15,7 +15,7 @@ const cellBaseStyles = (nowrap: boolean, cellVerticalAlignment: CellVerticalAlig ? ` jn:justify-center jn:flex - jn:flex-col + jn:flex-col ` : "" } @@ -42,13 +42,13 @@ const cellCustomStyles = (colSpan: number | undefined) => { * @see {@link DataGridCellProps} */ export const DataGridCell = forwardRef( - ({ colSpan, nowrap = false, className = "", children, ...props }, ref) => { + ({ colSpan, nowrap = false, verticalAlignment, className = "", children, ...props }, ref) => { const dataGridContext = useDataGridContext() || {} - const cellVerticalAlignment = dataGridContext.cellVerticalAlignment + const effectiveVerticalAlignment = verticalAlignment ?? dataGridContext.cellVerticalAlignment return (
{ */ nowrap?: boolean + /** + * Overrides the parent `DataGrid`'s `cellVerticalAlignment` for this cell. + * When not set, the cell inherits the grid-level setting. + */ + verticalAlignment?: CellVerticalAlignmentType + /** Components or elements to render within the DataGridCell. */ children?: ReactNode From 880f00ff2ae9783aa4661aca7d5bb92eecb5f622 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 12:44:30 +0200 Subject: [PATCH 04/18] feat(ui): add tests for vertical alignment behaviour of DataGridCell Signed-off-by: Franz Heidl --- .../DataGridCell/DataGridCell.test.tsx | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx index 669b7aa6a0..af51944aae 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx @@ -6,16 +6,39 @@ import * as React from "react" import { render, screen } from "@testing-library/react" import { DataGridCell } from "./index" +import { DataGrid } from "../DataGrid/index" describe("DataGridCell", () => { test("renders a DataGridCell", () => { render() expect(screen.getByRole("gridcell")).toBeInTheDocument() + expect(screen.getByRole("gridcell")).toHaveClass("juno-datagrid-cell") }) test("renders a custom className", () => { render() - expect(screen.getByRole("gridcell")).toBeInTheDocument() expect(screen.getByRole("gridcell")).toHaveClass("my-custom-class") }) + + test("inherits cellVerticalAlignment from parent DataGrid context", () => { + render( + + + + ) + expect(screen.getByRole("gridcell")).toHaveClass("jn:flex") + expect(screen.getByRole("gridcell")).toHaveClass("jn:flex-col") + expect(screen.getByRole("gridcell")).toHaveClass("jn:justify-center") + }) + + test("verticalAlignment prop overrides parent DataGrid context", () => { + render( + + + + ) + expect(screen.getByRole("gridcell")).not.toHaveClass("jn:flex") + expect(screen.getByRole("gridcell")).not.toHaveClass("jn:flex-col") + expect(screen.getByRole("gridcell")).not.toHaveClass("jn:justify-center") + }) }) From f2eb4d1e5bc416fa03774678479add582b091018 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 12:46:35 +0200 Subject: [PATCH 05/18] feat(ui): update DataGridCell stories - render DataGrid column context - show verticalAlignment - demonstrate no-wrap - demonstrate truncation with ellipsis Signed-off-by: Franz Heidl --- .../DataGridCell/DataGridCell.stories.tsx | 135 ++++++++++++++---- 1 file changed, 106 insertions(+), 29 deletions(-) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx index 540fd98022..f183aceca5 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx @@ -7,6 +7,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite" import React from "react" import { DataGrid } from "../DataGrid/index" import { DataGridRow } from "../DataGridRow/index" +import { DataGridHeadCell } from "../DataGridHeadCell/index" import { DataGridCell } from "./index" const meta: Meta = { @@ -20,64 +21,140 @@ const meta: Meta = { }, }, }, - decorators: [ - (Story) => ( - - - - - - ), - ], - parameters: { - docs: { - source: { - excludeDecorators: false, - }, - }, - }, } export default meta type Story = StoryObj +const items = [ + { id: "1", name: "node-prod-01", status: "Running" }, + { id: "2", name: "node-prod-02", status: "Stopped" }, + { id: "3", name: "node-staging-01", status: "Error" }, +] + +const longItems = [ + { + id: "1", + name: "node-prod-01", + status: "Running — all systems operational, no issues detected, last checked 2 minutes ago", + }, + { + id: "2", + name: "node-prod-02", + status: "Stopped — scheduled maintenance window currently active, expected to resume at 06:00 UTC", + }, + { + id: "3", + name: "node-staging-01", + status: + "Error — health check failed on port 8080, automatic restart attempted 3 times, manual intervention required", + }, +] + export const Default: Story = { parameters: { docs: { description: { - story: "Juno DataGridCell for use in DataGrid", + story: "A standard `DataGridCell` inside a `DataGrid`.", }, }, }, - args: { - children: ["DataGridCell"], - }, + render: () => ( + + + Name + Status + ID + + {items.map((item) => ( + + {item.name} + {item.status} + {item.id} + + ))} + + ), } export const NoWrap: Story = { parameters: { docs: { description: { - story: "Juno DataGridCell with nowrap option (content has white-space: nowrap;)", + story: + "With `nowrap`, cell content will not wrap onto multiple lines. Non-wrapping cells push their column to the available maximum width, and overflowing content is visible by default — consumers are responsible for handling overflow. The last row demonstrates truncation with an ellipsis: wrap the cell content in a `` with `block` and `truncate` — the span must be a block element because `text-overflow: ellipsis` does not apply directly to flex containers.", }, }, }, - args: { - nowrap: true, - children: ["DataGridCell does not wrap"], - }, + render: () => ( + + + Name + Status + ID + + {longItems.map((item, index) => ( + + {item.name} + + {index === longItems.length - 1 ? {item.status} : item.status} + + {item.id} + + ))} + + ), } export const ColSpan: Story = { parameters: { docs: { description: { - story: "Juno DataGridCell with colspan", + story: "A `DataGridCell` with `colSpan` spanning multiple columns.", }, }, }, - args: { - colSpan: 3, - children: ["DataGridCell with colspan"], + render: () => ( + + + Name + Status + ID + + {items.map((item) => ( + + {item.name} + {item.id} + + ))} + + ), +} + +export const VerticalAlignmentOverride: Story = { + parameters: { + docs: { + description: { + story: + 'Use `verticalAlignment` to override the parent `DataGrid`\'s `cellVerticalAlignment` for individual cells. Here the grid is set to `cellVerticalAlignment="center"` (the default), but the description cell uses `verticalAlignment="top"` to align longer content to the top while the name cell remains centered.', + }, + }, }, + render: () => ( + + + Name + Description + + {items.map((item) => ( + + {item.name} + + This is a longer description for {item.name} that spans multiple lines to demonstrate that top alignment + works independently of the grid-level setting. + + + ))} + + ), } From bb2f8cb03d361fe2b49b25bcd7ddd5262ed80a7d Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 12:49:36 +0200 Subject: [PATCH 06/18] feat(ui): add a note re controlling width of DataGridCheckboxCell via parent Signed-off-by: Franz Heidl --- .../DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx index 9499463ddc..a87cf85553 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx @@ -23,6 +23,14 @@ const meta: Meta = { }, }, }, + parameters: { + docs: { + description: { + component: + "`DataGridCheckboxCell` is a `DataGrid` cell pre-configured for holding a `Checkbox`. Place a `Checkbox` as a child and wire up selection state and event handlers directly on it. To keep the cell as narrow as its content, use `minContentColumns` on the parent `DataGrid` — the cell itself does not control its own column width.", + }, + }, + }, } export default meta From 417f6d7753092ae49a8bc295cf485c00abc9bee2 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 14:44:14 +0200 Subject: [PATCH 07/18] feat(ui): add radio controls for vertical alignment prop in storybook Signed-off-by: Franz Heidl --- .../src/components/DataGridCell/DataGridCell.stories.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx index f183aceca5..96832511e6 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx @@ -20,6 +20,10 @@ const meta: Meta = { type: { summary: "ReactNode" }, }, }, + verticalAlignment: { + control: { type: "radio" }, + options: ["center", "top"], + }, }, } From 88e0cf4d5811d8d448bf2864f2a8462379ac9828 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 15:26:10 +0200 Subject: [PATCH 08/18] feat(ui): add more tests Signed-off-by: Franz Heidl --- .../components/DataGridCell/DataGridCell.test.tsx | 11 +++++++++++ .../DataGridCheckboxCell.test.tsx | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx index af51944aae..2458f0a0fc 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx @@ -41,4 +41,15 @@ describe("DataGridCell", () => { expect(screen.getByRole("gridcell")).not.toHaveClass("jn:flex-col") expect(screen.getByRole("gridcell")).not.toHaveClass("jn:justify-center") }) + + test("verticalAlignment='center' overrides parent DataGrid cellVerticalAlignment='top'", () => { + render( + + + + ) + expect(screen.getByRole("gridcell")).toHaveClass("jn:flex") + expect(screen.getByRole("gridcell")).toHaveClass("jn:flex-col") + expect(screen.getByRole("gridcell")).toHaveClass("jn:justify-center") + }) }) diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx index e5dc98497c..ed5ada76ef 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx @@ -7,6 +7,7 @@ import * as React from "react" import { render, screen } from "@testing-library/react" import { DataGridCheckboxCell } from "./index" import { Checkbox } from "../Checkbox" +import { DataGrid } from "../DataGrid" describe("DataGridCheckboxCell", () => { test("renders a DataGridCheckboxCell", () => { @@ -37,4 +38,15 @@ describe("DataGridCheckboxCell", () => { ) expect(screen.getByTestId("child")).toBeInTheDocument() }) + + test("always renders vertically centered regardless of parent DataGrid cellVerticalAlignment", () => { + render( + + + + ) + expect(screen.getByRole("gridcell")).toHaveClass("jn:flex") + expect(screen.getByRole("gridcell")).toHaveClass("jn:flex-col") + expect(screen.getByRole("gridcell")).toHaveClass("jn:justify-center") + }) }) From 9b62cbf886f558bc7444db986633f6cccd8646e3 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 15:32:27 +0200 Subject: [PATCH 09/18] feat(ui): use string literal for styles Signed-off-by: Franz Heidl --- .../DataGridCheckboxCell.component.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx index 604dfc3fcd..038fc3dee8 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx @@ -6,6 +6,12 @@ import React, { HTMLAttributes, ReactNode } from "react" import { DataGridCell } from "../DataGridCell/DataGridCell.component" +const checkboxCellStyles = ` + jn:flex + jn:flex-col + jn:justify-center +` + /** * `DataGridCheckboxCell` is a `DataGrid` cell pre-configured for holding a `Checkbox` component. * Place a `Checkbox` as a child and wire up selection state and event handlers directly on it. @@ -15,10 +21,7 @@ import { DataGridCell } from "../DataGridCell/DataGridCell.component" export const DataGridCheckboxCell = ({ className = "", children, ...props }: DataGridCheckboxCellProps): ReactNode => { return ( // Always center vertically regardless of the parent DataGrid's cellVerticalAlignment context. - + {children} ) From e8546a19b1bef20e614a68b39c2bab734e46eb5d Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 15:38:59 +0200 Subject: [PATCH 10/18] feat(ui): add tests for arbitrary props Signed-off-by: Franz Heidl --- .../src/components/DataGridCell/DataGridCell.test.tsx | 5 +++++ .../DataGridCheckboxCell/DataGridCheckboxCell.test.tsx | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx index 2458f0a0fc..c493ee960b 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx @@ -20,6 +20,11 @@ describe("DataGridCell", () => { expect(screen.getByRole("gridcell")).toHaveClass("my-custom-class") }) + test("renders arbitrary props", () => { + render() + expect(screen.getByTestId("my-cell")).toHaveAttribute("data-foo", "bar") + }) + test("inherits cellVerticalAlignment from parent DataGrid context", () => { render( diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx index ed5ada76ef..a2a039956c 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx @@ -21,6 +21,11 @@ describe("DataGridCheckboxCell", () => { expect(screen.getByRole("gridcell")).toHaveClass("my-custom-class") }) + test("renders arbitrary props", () => { + render() + expect(screen.getByTestId("my-checkbox-cell")).toHaveAttribute("data-foo", "bar") + }) + test("renders a Checkbox child", () => { render( From fb64db5e56e4f9cfcdda19b198bf9e80a988468e Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 15:44:45 +0200 Subject: [PATCH 11/18] feat(ui): add more tests Signed-off-by: Franz Heidl --- .../src/components/DataGridCell/DataGridCell.test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx index c493ee960b..19e749a76b 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx @@ -57,4 +57,14 @@ describe("DataGridCell", () => { expect(screen.getByRole("gridcell")).toHaveClass("jn:flex-col") expect(screen.getByRole("gridcell")).toHaveClass("jn:justify-center") }) + + test("renders nowrap class when nowrap is set", () => { + render() + expect(screen.getByRole("gridcell")).toHaveClass("jn:whitespace-nowrap") + }) + + test("renders colSpan via inline style", () => { + render() + expect(screen.getByRole("gridcell")).toHaveStyle({ gridColumn: "span 3 / span 3" }) + }) }) From dfa4bc42c723033b935d46f8f26d377bf9d268d5 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 16:54:08 +0200 Subject: [PATCH 12/18] feat(ui): add accesible labels to checkboxes in stories Signed-off-by: Franz Heidl --- .../src/components/DataGrid/DataGridHeader.stories.tsx | 2 ++ .../DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx b/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx index 76ea7d4abf..ac8b80fb29 100644 --- a/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx +++ b/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx @@ -253,6 +253,7 @@ export const FullyFeatured: Story = { setSelected((prev) => ({ ...prev, [s.id]: e.target.checked }))} /> @@ -375,6 +376,7 @@ export const FullyFeatured: Story = { setSelected((prev) => ({ ...prev, [s.id]: e.target.checked }))} /> diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx index a87cf85553..83b58aea94 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx @@ -64,6 +64,7 @@ export const Default: Story = { setSelected((s) => ({ ...s, [item.id]: e.target.checked }))} /> @@ -95,7 +96,7 @@ export const Disabled: Story = { {items.map((item) => ( - + {item.name} {item.status} @@ -124,7 +125,7 @@ export const AlwaysCentered: Story = { {items.map((item) => ( - + {item.name} From b557b49398cd7193ab4dd644a960d14c663d5333 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 16:56:32 +0200 Subject: [PATCH 13/18] feat(ui): add newly added verticalAlignment prop internally - also update comment and element type for omitting onChange Signed-off-by: Franz Heidl --- .../DataGridCheckboxCell.component.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx index 038fc3dee8..a0afe39567 100644 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx +++ b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx @@ -6,12 +6,6 @@ import React, { HTMLAttributes, ReactNode } from "react" import { DataGridCell } from "../DataGridCell/DataGridCell.component" -const checkboxCellStyles = ` - jn:flex - jn:flex-col - jn:justify-center -` - /** * `DataGridCheckboxCell` is a `DataGrid` cell pre-configured for holding a `Checkbox` component. * Place a `Checkbox` as a child and wire up selection state and event handlers directly on it. @@ -21,14 +15,14 @@ const checkboxCellStyles = ` export const DataGridCheckboxCell = ({ className = "", children, ...props }: DataGridCheckboxCellProps): ReactNode => { return ( // Always center vertically regardless of the parent DataGrid's cellVerticalAlignment context. - + {children} ) } -// Omit onChange: it would silently spread onto the underlying div (a no-op) instead of reaching the Checkbox child, misleading consumers into thinking they're wiring up checkbox state. -export interface DataGridCheckboxCellProps extends Omit, "onChange"> { +// Omit onChange: React's synthetic onChange bubbles, so it would fire when the child Checkbox changes, but the type would be ChangeEventHandler — misleading consumers into thinking they're wiring up checkbox state. Wire onChange directly on the Checkbox child instead. +export interface DataGridCheckboxCellProps extends Omit, "onChange"> { /** A `Checkbox` component to render inside the cell. */ children?: ReactNode From 1ce5f52d86aaadbe060c80465c113a695f758ae7 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Wed, 12 Aug 2026 17:15:02 +0200 Subject: [PATCH 14/18] Create clean-hoops-study.md Signed-off-by: Franz Heidl --- .changeset/clean-hoops-study.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/clean-hoops-study.md diff --git a/.changeset/clean-hoops-study.md b/.changeset/clean-hoops-study.md new file mode 100644 index 0000000000..55c9a8b6ba --- /dev/null +++ b/.changeset/clean-hoops-study.md @@ -0,0 +1,9 @@ +--- +"@cloudoperators/juno-ui-components": minor +--- + +feat(ui): Streamline `DataGridCheckboxCell` and add `verticalAlignment` prop to `DataGridCell`. + +`DataGridCheckboxCell` is now a plain container that centers its child vertically — place a `Checkbox` as a child and wire up state and handlers directly on it. Note: although this is a breaking change to the `DataGridCheckboxCell` API, the component was WIP-labelled and is not known to be used in any application. + +`DataGridCell` gains a `verticalAlignment` prop (`"center" | "top"`) that overrides the parent `DataGrid`'s `cellVerticalAlignment` for individual cells. From 08c4c60ab5b0245677ae6e51f5bbfba31de21ed2 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Thu, 13 Aug 2026 10:45:19 +0200 Subject: [PATCH 15/18] feat(ui): remove `DataGridChecboxCell` component for good Signed-off-by: Franz Heidl --- .../DataGrid/DataGridHeader.stories.tsx | 9 +- .../DataGridCheckboxCell.component.tsx | 34 ----- .../DataGridCheckboxCell.stories.tsx | 139 ------------------ .../DataGridCheckboxCell.test.tsx | 57 ------- .../components/DataGridCheckboxCell/index.ts | 6 - 5 files changed, 4 insertions(+), 241 deletions(-) delete mode 100644 packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx delete mode 100644 packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx delete mode 100644 packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx delete mode 100644 packages/ui-components/src/components/DataGridCheckboxCell/index.ts diff --git a/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx b/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx index ac8b80fb29..1de83ef402 100644 --- a/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx +++ b/packages/ui-components/src/components/DataGrid/DataGridHeader.stories.tsx @@ -9,7 +9,6 @@ import { DataGrid } from "./DataGrid.component" import { DataGridRow } from "../DataGridRow" import { DataGridCell } from "../DataGridCell" import { DataGridHeadCell } from "../DataGridHeadCell" -import { DataGridCheckboxCell } from "../DataGridCheckboxCell" import { DataGridToolbar } from "../DataGridToolbar" import { Stack } from "../Stack" import { Button } from "../Button" @@ -251,13 +250,13 @@ export const FullyFeatured: Story = { {servers.map((s) => ( - + setSelected((prev) => ({ ...prev, [s.id]: e.target.checked }))} /> - + {s.name} {s.region} {s.status} @@ -374,13 +373,13 @@ export const FullyFeatured: Story = { {servers.map((s) => ( - + setSelected((prev) => ({ ...prev, [s.id]: e.target.checked }))} /> - + {s.name} {s.region} {s.status} diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx deleted file mode 100644 index a0afe39567..0000000000 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.component.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { HTMLAttributes, ReactNode } from "react" -import { DataGridCell } from "../DataGridCell/DataGridCell.component" - -/** - * `DataGridCheckboxCell` is a `DataGrid` cell pre-configured for holding a `Checkbox` component. - * Place a `Checkbox` as a child and wire up selection state and event handlers directly on it. - * @see https://cloudoperators.github.io/juno/?path=/docs/components-datagrid-datagridcheckboxcell--docs - * @see {@link DataGridCheckboxCellProps} - */ -export const DataGridCheckboxCell = ({ className = "", children, ...props }: DataGridCheckboxCellProps): ReactNode => { - return ( - // Always center vertically regardless of the parent DataGrid's cellVerticalAlignment context. - - {children} - - ) -} - -// Omit onChange: React's synthetic onChange bubbles, so it would fire when the child Checkbox changes, but the type would be ChangeEventHandler — misleading consumers into thinking they're wiring up checkbox state. Wire onChange directly on the Checkbox child instead. -export interface DataGridCheckboxCellProps extends Omit, "onChange"> { - /** A `Checkbox` component to render inside the cell. */ - children?: ReactNode - - /** - * Additional class names for styling. - * @default "" - */ - className?: string -} diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx deleted file mode 100644 index 83b58aea94..0000000000 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.stories.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import type { Meta, StoryObj } from "@storybook/react-vite" -import React, { useState } from "react" -import { DataGrid } from "../DataGrid/index" -import { DataGridRow } from "../DataGridRow/index" -import { DataGridCell } from "../DataGridCell/index" -import { DataGridHeadCell } from "../DataGridHeadCell/index" -import { DataGridCheckboxCell } from "./index" -import { Checkbox } from "../Checkbox/index" - -const meta: Meta = { - title: "Components/DataGrid/DataGridCheckboxCell", - component: DataGridCheckboxCell, - argTypes: { - children: { - control: false, - table: { - type: { summary: "ReactNode" }, - }, - }, - }, - parameters: { - docs: { - description: { - component: - "`DataGridCheckboxCell` is a `DataGrid` cell pre-configured for holding a `Checkbox`. Place a `Checkbox` as a child and wire up selection state and event handlers directly on it. To keep the cell as narrow as its content, use `minContentColumns` on the parent `DataGrid` — the cell itself does not control its own column width.", - }, - }, - }, -} - -export default meta -type Story = StoryObj - -const items = [ - { id: "1", name: "node-prod-01", status: "Running" }, - { id: "2", name: "node-prod-02", status: "Stopped" }, - { id: "3", name: "node-staging-01", status: "Error" }, -] - -export const Default: Story = { - parameters: { - docs: { - description: { - story: - "A `DataGridCheckboxCell` with a `Checkbox` child. Wire up selection state and all event handlers directly on the `Checkbox`. Use `minContentColumns` on the parent `DataGrid` to keep the checkbox cell as narrow as its content.", - }, - }, - }, - render: () => { - const [selected, setSelected] = useState>({}) - return ( - - - - Name - Status - - {items.map((item) => ( - - - setSelected((s) => ({ ...s, [item.id]: e.target.checked }))} - /> - - {item.name} - {item.status} - - ))} - - ) - }, -} - -export const Disabled: Story = { - parameters: { - docs: { - description: { - story: "A `DataGridCheckboxCell` with a disabled `Checkbox`. Disable the `Checkbox` directly.", - }, - }, - }, - render: () => ( - - - - Name - Status - - {items.map((item) => ( - - - - - {item.name} - {item.status} - - ))} - - ), -} - -export const AlwaysCentered: Story = { - parameters: { - docs: { - description: { - story: - '`DataGridCheckboxCell` always centers its content vertically, regardless of the parent `DataGrid`\'s `cellVerticalAlignment` setting. Here the grid is set to `cellVerticalAlignment="top"`, but the checkbox remains centered.', - }, - }, - }, - render: () => ( - - - - Name - Notes - - {items.map((item) => ( - - - - - {item.name} - - This cell has multiple lines of content to make the row tall enough to demonstrate that the checkbox remains - vertically centered even when the grid is configured to align cell content to the top. - - - ))} - - ), -} diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx b/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx deleted file mode 100644 index a2a039956c..0000000000 --- a/packages/ui-components/src/components/DataGridCheckboxCell/DataGridCheckboxCell.test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as React from "react" -import { render, screen } from "@testing-library/react" -import { DataGridCheckboxCell } from "./index" -import { Checkbox } from "../Checkbox" -import { DataGrid } from "../DataGrid" - -describe("DataGridCheckboxCell", () => { - test("renders a DataGridCheckboxCell", () => { - render() - expect(screen.getByRole("gridcell")).toBeInTheDocument() - expect(screen.getByRole("gridcell")).toHaveClass("juno-datagrid-checkbox-cell") - }) - - test("renders a custom className", () => { - render() - expect(screen.getByRole("gridcell")).toHaveClass("my-custom-class") - }) - - test("renders arbitrary props", () => { - render() - expect(screen.getByTestId("my-checkbox-cell")).toHaveAttribute("data-foo", "bar") - }) - - test("renders a Checkbox child", () => { - render( - - - - ) - expect(screen.getByRole("checkbox")).toBeInTheDocument() - }) - - test("renders other children", () => { - render( - - - - ) - expect(screen.getByTestId("child")).toBeInTheDocument() - }) - - test("always renders vertically centered regardless of parent DataGrid cellVerticalAlignment", () => { - render( - - - - ) - expect(screen.getByRole("gridcell")).toHaveClass("jn:flex") - expect(screen.getByRole("gridcell")).toHaveClass("jn:flex-col") - expect(screen.getByRole("gridcell")).toHaveClass("jn:justify-center") - }) -}) diff --git a/packages/ui-components/src/components/DataGridCheckboxCell/index.ts b/packages/ui-components/src/components/DataGridCheckboxCell/index.ts deleted file mode 100644 index 1f372e622f..0000000000 --- a/packages/ui-components/src/components/DataGridCheckboxCell/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export { DataGridCheckboxCell, type DataGridCheckboxCellProps } from "./DataGridCheckboxCell.component" From 84d2fdfd64af77f27445d23620ffc09a153d72da Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Thu, 13 Aug 2026 10:54:17 +0200 Subject: [PATCH 16/18] feat(ui): add missing test Signed-off-by: Franz Heidl --- .../src/components/DataGridCell/DataGridCell.test.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx index 19e749a76b..c3a04e58d9 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.test.tsx @@ -63,6 +63,13 @@ describe("DataGridCell", () => { expect(screen.getByRole("gridcell")).toHaveClass("jn:whitespace-nowrap") }) + test("verticalAlignment prop works without a parent DataGrid context", () => { + render() + expect(screen.getByRole("gridcell")).toHaveClass("jn:flex") + expect(screen.getByRole("gridcell")).toHaveClass("jn:flex-col") + expect(screen.getByRole("gridcell")).toHaveClass("jn:justify-center") + }) + test("renders colSpan via inline style", () => { render() expect(screen.getByRole("gridcell")).toHaveStyle({ gridColumn: "span 3 / span 3" }) From ec37cae9ae422ed8f271cb7d273673b013a305c7 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Thu, 13 Aug 2026 15:04:03 +0200 Subject: [PATCH 17/18] feat(ui): improve verticalAlignment story Signed-off-by: Franz Heidl --- .../src/components/DataGridCell/DataGridCell.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx b/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx index 96832511e6..67fcb36b9b 100644 --- a/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx +++ b/packages/ui-components/src/components/DataGridCell/DataGridCell.stories.tsx @@ -152,8 +152,8 @@ export const VerticalAlignmentOverride: Story = { {items.map((item) => ( - {item.name} - + {item.name} + This is a longer description for {item.name} that spans multiple lines to demonstrate that top alignment works independently of the grid-level setting. From 32bd9ca3f79076bd1681942542ef8c2afa0f97e0 Mon Sep 17 00:00:00 2001 From: Franz Heidl Date: Thu, 13 Aug 2026 16:05:30 +0200 Subject: [PATCH 18/18] feat(ui): update changeset Signed-off-by: Franz Heidl --- .changeset/clean-hoops-study.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/clean-hoops-study.md b/.changeset/clean-hoops-study.md index 55c9a8b6ba..7e0636ae77 100644 --- a/.changeset/clean-hoops-study.md +++ b/.changeset/clean-hoops-study.md @@ -4,6 +4,6 @@ feat(ui): Streamline `DataGridCheckboxCell` and add `verticalAlignment` prop to `DataGridCell`. -`DataGridCheckboxCell` is now a plain container that centers its child vertically — place a `Checkbox` as a child and wire up state and handlers directly on it. Note: although this is a breaking change to the `DataGridCheckboxCell` API, the component was WIP-labelled and is not known to be used in any application. +`DataGridCheckboxCell` is now removed completely. As it has been WIP for all the time and there isn't one documented use, we will release this as a minor instead of major, even though it is technically breaking. -`DataGridCell` gains a `verticalAlignment` prop (`"center" | "top"`) that overrides the parent `DataGrid`'s `cellVerticalAlignment` for individual cells. +`DataGridCell` gets a `verticalAlignment` prop (`"center" | "top"`) that overrides the parent `DataGrid`'s `cellVerticalAlignment` for individual cells.