Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,20 @@
</fdp-table-header>
<fdp-table-cell *fdpCellDef="let item">{{ item.name }}</fdp-table-cell>
</fdp-column>
<fdp-column name="description" key="description" [sortable]="true">
<fdp-column name="description" key="description" [sortable]="true" [announceEmptyCell]="false">
<fdp-table-header *fdpHeaderCellDef> Description </fdp-table-header>
<fdp-table-cell *fdpCellDef="let item">
<fdp-input
type="text"
name="description"
placeholder="Enter the description"
[(ngModel)]="item.description"
>
</fdp-input>
@if (!item.description) {
<span [attr.aria-label]="'Empty Cell'"></span>
} @else {
<fdp-input
type="text"
name="description"
placeholder="Enter the description"
[(ngModel)]="item.description"
>
</fdp-input>
}
</fdp-table-cell>
</fdp-column>
<fdp-column name="price" key="price.value" [nonInteractive]="true">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ const ITEMS: ExampleItem[] = [
},
{
name: 'Beam Breaker B-1',
description: 'fermentum donec ut',
description: '',
price: {
value: 36.56,
currency: 'NZD'
Expand Down
4 changes: 4 additions & 0 deletions libs/docs/platform/table/platform-table-docs.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
<p>This example shows custom column definition implementation</p>
<p>Developers are able to change the appearance of a regular cell and header cell as well.</p>
<p>Additionally, developers can define custom renderer of the header cell popover.</p>
<p>
The <code>[announceEmptyCell]</code> allows you to control the screen reader announcement for empty cells, which
is particularly useful for columns that use a custom cell template (<code>*fdpCellDef</code>).
</p>
</description>
<component-example>
<fdp-platform-table-custom-column-example></fdp-platform-table-custom-column-example>
Expand Down
8 changes: 7 additions & 1 deletion libs/platform/table-helpers/table-column.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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<boolean>;

/** @hidden */
abstract _freezed: boolean;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
Optional,
SimpleChanges,
TemplateRef,
ViewEncapsulation
ViewEncapsulation,
input
} from '@angular/core';
import { Nullable } from '@fundamental-ngx/cdk/utils';
import {
Expand Down Expand Up @@ -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<TemplateRef<any>>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()"
Expand All @@ -163,7 +164,7 @@
></span>
}
@if (row.state === 'readonly') {
@if (tableTextContainer.innerText?.trim() === '') {
@if (_announceEmptyCell()) {
<span
[ngStyle]="{
position: 'absolute',
Expand Down
41 changes: 40 additions & 1 deletion libs/platform/table/components/table-row/table-row.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
ViewChildren,
ViewEncapsulation,
computed,
inject
inject,
signal
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms';
Expand Down Expand Up @@ -63,6 +64,7 @@ import {
isTreeRow,
isTreeRowFirstCell
} from '@fundamental-ngx/platform/table-helpers';
import { get } from 'lodash-es';
import { Subject, fromEvent, merge } from 'rxjs';
import { filter, startWith, switchMap, takeUntil } from 'rxjs/operators';
import { TableEditableCellComponent } from '../table-editable-cell/table-editable-cell.component';
Expand Down Expand Up @@ -180,6 +182,9 @@ export class TableRowComponent<T> extends TableRowDirective implements OnInit, A
/** @hidden */
_rowSelectionHelperTextId = `rowSelectionHelper-${uuidv4()}`;

/** @hidden */
readonly _announceEmptyCell = signal(false);

/** @hidden */
readonly _isTreeRowFirstCell = isTreeRowFirstCell;

Expand Down Expand Up @@ -324,6 +329,19 @@ export class TableRowComponent<T> extends TableRowDirective implements OnInit, A
return retVal;
};

/** @hidden */
_handleCellFocused(
event: FocusableItemPosition,
index: number,
row: TableRow<T>,
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)) {
Expand Down Expand Up @@ -367,4 +385,25 @@ export class TableRowComponent<T> extends TableRowDirective implements OnInit, A
event.shiftKey ? 'group' : 'shift'
);
}

/** @hidden */
private _setAnnounceEmptyCell(row: TableRow<T>, 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);
}
}