diff --git a/packages/components/src/components/data-grid/cell-handlers/html-cell.tsx b/packages/components/src/components/data-grid/cell-handlers/html-cell.tsx
index f77e71ce8e..30a55f8b45 100644
--- a/packages/components/src/components/data-grid/cell-handlers/html-cell.tsx
+++ b/packages/components/src/components/data-grid/cell-handlers/html-cell.tsx
@@ -16,11 +16,45 @@ import { Cell } from './cell-interface';
export const HTMLCell: Cell = {
defaults: {},
- getLongestContent({ rows, columnIndex }) {
- // Skip check as content width is always the same
+ getLongestContent({ rows, columnIndex, field }) {
+ if (field.display === 'inline') {
+ let maxLength = -1;
+ let longestContent;
+ rows.forEach((row) => {
+ const content = row[columnIndex];
+ const length = content?.textContent?.length || 0;
+ if (content && length > maxLength) {
+ longestContent = content;
+ maxLength = length;
+ }
+ });
+ return longestContent;
+ }
+
+ // Skip check as nested HTML content width is always the same toggle button.
return rows[0][columnIndex];
},
- render: ({ content, component, localization }) => {
+ render: ({ field, content, component, localization }) => {
+ if (field.display === 'inline') {
+ return (
+ content && (
+
{
+ if (el) {
+ let child = el.lastElementChild;
+ while (child) {
+ el.removeChild(child);
+ child = el.lastElementChild;
+ }
+ el.appendChild(content);
+ }
+ }}
+ >
+ )
+ );
+ }
+
const getAriaLabel = () => {
if (localization?.expand && localization?.collapse) {
return content.isExpanded
diff --git a/packages/components/src/components/data-grid/data-grid.css b/packages/components/src/components/data-grid/data-grid.css
index 5584eb27e4..dc6025a5f1 100644
--- a/packages/components/src/components/data-grid/data-grid.css
+++ b/packages/components/src/components/data-grid/data-grid.css
@@ -308,6 +308,12 @@
width: auto;
}
+.tbody__html-cell--inline {
+ display: inline-flex;
+ align-items: center;
+ max-width: 100%;
+}
+
.tbody__nested {
white-space: nowrap;
padding: 0px;
diff --git a/packages/components/src/components/data-grid/data-grid.spec.ts b/packages/components/src/components/data-grid/data-grid.spec.ts
new file mode 100644
index 0000000000..2060279544
--- /dev/null
+++ b/packages/components/src/components/data-grid/data-grid.spec.ts
@@ -0,0 +1,108 @@
+/**
+ * @license
+ * Scale https://github.com/telekom/scale
+ *
+ * Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+import { newSpecPage } from '@stencil/core/testing';
+import { DataGrid } from './data-grid';
+import { HTMLCell } from './cell-handlers/html-cell';
+
+class ResizeObserverMock {
+ observe() {}
+ unobserve() {}
+}
+
+describe('DataGrid', () => {
+ beforeEach(() => {
+ (globalThis as any).ResizeObserver = ResizeObserverMock;
+ });
+
+ it('renders html cell content inline when display is inline', async () => {
+ const inlineButton = document.createElement('button');
+ inlineButton.textContent = 'Open details';
+
+ const page = await newSpecPage({
+ components: [DataGrid],
+ html: '',
+ });
+
+ page.root.fields = [
+ {
+ type: 'html',
+ display: 'inline',
+ label: 'Action',
+ width: 120,
+ },
+ ];
+ page.root.rows = [[inlineButton]];
+
+ await page.waitForChanges();
+
+ expect(page.root.shadowRoot.querySelector('.tbody__nested')).toBeNull();
+ expect(
+ page.root.shadowRoot.querySelector('.tbody__cell button').textContent
+ ).toBe('Open details');
+ });
+
+ it('keeps html cell content nested by default', async () => {
+ const nestedContent = document.createElement('div');
+ nestedContent.textContent = 'Nested details';
+
+ const page = await newSpecPage({
+ components: [DataGrid],
+ html: '',
+ });
+
+ page.root.fields = [
+ {
+ type: 'html',
+ label: 'Details',
+ width: 96,
+ },
+ ];
+ page.root.rows = [[nestedContent]];
+
+ await page.waitForChanges();
+
+ expect(
+ page.root.shadowRoot.querySelector('.tbody__cell scale-button')
+ ).toBeTruthy();
+ expect(
+ page.root.shadowRoot.querySelector('.tbody__nested div').textContent
+ ).toBe('Nested details');
+ });
+
+ it('uses the longest inline html content for auto width checks', () => {
+ const shortButton = document.createElement('button');
+ shortButton.textContent = 'Open';
+ const longButton = document.createElement('button');
+ longButton.textContent = 'Open ticket details';
+
+ const longestContent = HTMLCell.getLongestContent({
+ rows: [[shortButton], [longButton]],
+ columnIndex: 0,
+ field: { display: 'inline' },
+ });
+
+ expect(longestContent).toBe(longButton);
+ });
+
+ it('uses inline html content with empty text for auto width checks', () => {
+ const iconOnlyButton = document.createElement('button');
+ iconOnlyButton.setAttribute('aria-label', 'Open details');
+
+ const longestContent = HTMLCell.getLongestContent({
+ rows: [[iconOnlyButton]],
+ columnIndex: 0,
+ field: { display: 'inline' },
+ });
+
+ expect(longestContent).toBe(iconOnlyButton);
+ });
+});
diff --git a/packages/components/src/components/data-grid/data-grid.tsx b/packages/components/src/components/data-grid/data-grid.tsx
index 52fde0cfc7..2cd6202953 100644
--- a/packages/components/src/components/data-grid/data-grid.tsx
+++ b/packages/components/src/components/data-grid/data-grid.tsx
@@ -1187,8 +1187,8 @@ export class DataGrid {
if (!visible) {
return;
}
- // Add rows nested tables to array
- if (field.type === 'html') {
+ // Add non-inline HTML cells to the nested content row.
+ if (field.type === 'html' && field.display !== 'inline') {
if (!cellContent) {
return this.renderTableCell(
field,
diff --git a/packages/storybook-vue/stories/components/data-grid/data-grid.md b/packages/storybook-vue/stories/components/data-grid/data-grid.md
index 50b0d2a844..f134eed8e3 100644
--- a/packages/storybook-vue/stories/components/data-grid/data-grid.md
+++ b/packages/storybook-vue/stories/components/data-grid/data-grid.md
@@ -66,6 +66,8 @@ To aid readability, you can highlight the rows when hovering over them.
If you fill in the HTML slot in a table row, it will add an expand icon at the end of the row. If users click on the expand icon, it will display the HTML content of this slot.
+If you want to render an `HTMLElement` directly inside a data grid cell instead, use the `html` cell type with `display: "inline"` in the field configuration. The default behavior stays expandable nested content.
+
#### Pagination (8)
With the help of pagination, users can move through the entire data set in a deliberate way.
diff --git a/packages/storybook-vue/stories/components/data-grid/data-grid_de.md b/packages/storybook-vue/stories/components/data-grid/data-grid_de.md
index 6265a349cf..311212b0a0 100644
--- a/packages/storybook-vue/stories/components/data-grid/data-grid_de.md
+++ b/packages/storybook-vue/stories/components/data-grid/data-grid_de.md
@@ -59,6 +59,8 @@ Der Inhalt einer Zeile bildet eine Dateneinheit und unterscheidet sich sowohl in
Füllst du den HTML-Slot in einer Tabellenzeile aus, wird am Ende der Zeile ein Expand-Icon hinzugefügt. Klicken/tippen Nutzer\*innen auf das Expand-Icon, blendet sich der HTML-Inhalt dieses Slots ein.
+Wenn du ein `HTMLElement` stattdessen direkt in einer Data-Grid-Zelle darstellen möchtest, verwende den `html`-Zelltyp mit `display: "inline"` in der Feldkonfiguration. Das Standardverhalten bleibt aufklappbarer Nested Content.
+
#### Pagination (8)
Mit Hilfe der Pagination bewegen sich Nutzer\*innen gezielt durch den gesamten Datensatz.