diff --git a/libs/docs/platform/table/examples/platform-table-custom-column-example.component.ts b/libs/docs/platform/table/examples/platform-table-custom-column-example.component.ts
index 313f0dda20b..7058d48695e 100644
--- a/libs/docs/platform/table/examples/platform-table-custom-column-example.component.ts
+++ b/libs/docs/platform/table/examples/platform-table-custom-column-example.component.ts
@@ -207,7 +207,7 @@ const ITEMS: ExampleItem[] = [
},
{
name: 'Beam Breaker B-1',
- description: 'fermentum donec ut',
+ description: '',
price: {
value: 36.56,
currency: 'NZD'
diff --git a/libs/docs/platform/table/platform-table-docs.component.html b/libs/docs/platform/table/platform-table-docs.component.html
index 149d71c7d18..fc6336c3748 100644
--- a/libs/docs/platform/table/platform-table-docs.component.html
+++ b/libs/docs/platform/table/platform-table-docs.component.html
@@ -47,6 +47,10 @@
This example shows custom column definition implementation
Developers are able to change the appearance of a regular cell and header cell as well.
Additionally, developers can define custom renderer of the header cell popover.
+
+ The [announceEmptyCell] allows you to control the screen reader announcement for empty cells, which
+ is particularly useful for columns that use a custom cell template (*fdpCellDef).
+
diff --git a/libs/platform/table-helpers/table-column.ts b/libs/platform/table-helpers/table-column.ts
index 38ae2f08c6c..e7386b3ab03 100644
--- a/libs/platform/table-helpers/table-column.ts
+++ b/libs/platform/table-helpers/table-column.ts
@@ -1,4 +1,4 @@
-import { TemplateRef } from '@angular/core';
+import { Signal, TemplateRef } from '@angular/core';
import { Nullable } from '@fundamental-ngx/cdk/utils';
import { Observable } from 'rxjs';
@@ -86,6 +86,12 @@ export abstract class TableColumn {
/** Column role attribute. */
abstract role: 'cell' | 'rowheader' | 'gridcell';
+ /**
+ * Whether to announce built-in empty cells to screen readers for columns with column templates.
+ * This provides option to turn off the announcement if the column template already includes its own
+ * */
+ abstract announceEmptyCell: Signal;
+
/** @hidden */
abstract _freezed: boolean;
diff --git a/libs/platform/table/components/table-column/table-column.component.ts b/libs/platform/table/components/table-column/table-column.component.ts
index c8372bc1f75..34d09751474 100644
--- a/libs/platform/table/components/table-column/table-column.component.ts
+++ b/libs/platform/table/components/table-column/table-column.component.ts
@@ -9,7 +9,8 @@ import {
Optional,
SimpleChanges,
TemplateRef,
- ViewEncapsulation
+ ViewEncapsulation,
+ input
} from '@angular/core';
import { Nullable } from '@fundamental-ngx/cdk/utils';
import {
@@ -148,6 +149,9 @@ export class TableColumnComponent extends TableColumn implements OnInit, OnChang
@Input()
role: 'cell' | 'rowheader' | 'gridcell' = 'gridcell';
+ /** Whether to announce empty cells to screen readers. Default is true. */
+ readonly announceEmptyCell = input(true);
+
/** Column cell template */
columnCellTemplate: Nullable>;
diff --git a/libs/platform/table/components/table-row/table-row.component.html b/libs/platform/table/components/table-row/table-row.component.html
index a220f60e48a..698b099ee57 100644
--- a/libs/platform/table/components/table-row/table-row.component.html
+++ b/libs/platform/table/components/table-row/table-row.component.html
@@ -136,7 +136,8 @@
"
[attr.aria-expanded]="_isTreeRowFirstCell($index, row) ? row.expanded : null"
[attr.data-nesting-level]="$index === 0 ? row.level + 1 : null"
- (cellFocused)="_tableRowService.cellFocused($event); _tableRowService.cellActivate($index, row)"
+ (cellFocused)="_handleCellFocused($event, $index, row, column, tableTextContainer)"
+ (focusout)="_announceEmptyCell.set(false)"
(click)="_tableRowService.cellClicked({ index: $index, row })"
(keydown.enter)="_isTreeRowFirstCell($index, row, $event) && _toggleGroupRow()"
(keydown.arrowLeft)="_tableRowService.scrollToOverlappedCell()"
@@ -163,7 +164,7 @@
>
}
@if (row.state === 'readonly') {
- @if (tableTextContainer.innerText?.trim() === '') {
+ @if (_announceEmptyCell()) {
extends TableRowDirective implements OnInit, A
/** @hidden */
_rowSelectionHelperTextId = `rowSelectionHelper-${uuidv4()}`;
+ /** @hidden */
+ readonly _announceEmptyCell = signal(false);
+
/** @hidden */
readonly _isTreeRowFirstCell = isTreeRowFirstCell;
@@ -324,6 +329,19 @@ export class TableRowComponent extends TableRowDirective implements OnInit, A
return retVal;
};
+ /** @hidden */
+ _handleCellFocused(
+ event: FocusableItemPosition,
+ index: number,
+ row: TableRow,
+ column: TableColumn,
+ tableTextContainer: HTMLElement
+ ): void {
+ this._setAnnounceEmptyCell(row, column, tableTextContainer);
+ this._tableRowService.cellFocused(event);
+ this._tableRowService.cellActivate(index, row);
+ }
+
/** @hidden */
protected _handleCellSpaceKey(colIdx: number, tableCellElement: HTMLTableCellElement, $event: Event): void {
if ($event.target === tableCellElement && isTreeRowFirstCell(colIdx, this.row, $event)) {
@@ -367,4 +385,25 @@ export class TableRowComponent extends TableRowDirective implements OnInit, A
event.shiftKey ? 'group' : 'shift'
);
}
+
+ /** @hidden */
+ private _setAnnounceEmptyCell(row: TableRow, column: TableColumn, tableTextContainer: HTMLElement): void {
+ if (row.state !== 'readonly') {
+ this._announceEmptyCell.set(false);
+ return;
+ }
+
+ let value: string;
+ // For non-column templates (data-driven), always check for empty content.
+ const isCellEmptyInNonColumnTemplate =
+ !column.columnCellTemplate &&
+ (!(value = get(row.value, column.key)) || (typeof value === 'string' && value.trim() === ''));
+
+ // For column templates (consumer-provided), check text content by default.
+ // Otherwise, determined by consumer by disabling default checking.
+ const isCellEmptyInColumnTemplate =
+ column.columnCellTemplate && column.announceEmptyCell() && tableTextContainer?.innerText?.trim() === '';
+
+ this._announceEmptyCell.set(isCellEmptyInColumnTemplate || isCellEmptyInNonColumnTemplate);
+ }
}