AAE-28910 Add subtitle for columns (#10479)

* AAE-28910 Add subtitle for column and column selector

* Add test

* Change subTitle name

* fix units

* fix units
This commit is contained in:
Bartosz Sekula 2024-12-09 11:29:58 -05:00 committed by GitHub
parent ce5cdb40a8
commit d1d72cb575
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 145 additions and 52 deletions

View File

@ -45,7 +45,23 @@
[checked]="!column.isHidden"
[disabled]="isCheckboxDisabled(column)"
(change)="changeColumnVisibility(column)">
<div class="adf-columns-selector-list-content">{{column.title | translate}}</div>
<div class="adf-columns-selector-list-content">
{{column.title | translate}}
</div>
<div
*ngIf="column.subtitle"
class="
adf-columns-selector-list-content
adf-columns-selector-subtitle
"
>
<mat-icon class="adf-columns-selector-subtitle-icon">
device_hub
</mat-icon>
<span>
{{column.subtitle | translate}}
</span>
</div>
</mat-checkbox>
</div>
</div>

View File

@ -27,8 +27,7 @@ $adf-columns-selector-space: 12px;
&-list-container {
max-height: 350px;
overflow-x: hidden;
overflow-y: auto;
overflow: hidden auto;
}
&-list-item {
@ -47,6 +46,22 @@ $adf-columns-selector-space: 12px;
width: 210px;
}
&-subtitle {
color: var(--adf-theme-foreground-text-color);
font-size: var(--theme-caption-font-size);
opacity: 0.6;
display: flex;
column-gap: 3px;
align-items: center;
}
&-subtitle-icon {
margin-top: 3px;
height: var(--theme-caption-font-size);
width: var(--theme-caption-font-size);
font-size: var(--theme-caption-font-size);
}
&-column-checkbox {
padding: 0 20px;
}

View File

@ -153,10 +153,10 @@ describe('ColumnsSelectorComponent', () => {
const checkBoxName = await firstColumnCheckbox.getLabelText();
const toggledColumnItem = component.columnItems.find((item) => item.title === checkBoxName);
expect(toggledColumnItem.isHidden).toBeFalsy();
expect(toggledColumnItem?.isHidden).toBe(undefined);
await firstColumnCheckbox.toggle();
expect(toggledColumnItem.isHidden).toBe(true);
expect(toggledColumnItem?.isHidden).toBeTrue();
});
describe('checkboxes', () => {
@ -207,9 +207,11 @@ describe('ColumnsSelectorComponent', () => {
fixture.detectChanges();
const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);
const labeTextOne = await checkboxes[0].getLabelText();
const labeTextTwo = await checkboxes[1].getLabelText();
expect(await checkboxes[0].getLabelText()).toBe(shownDataColumn.title);
expect(await checkboxes[1].getLabelText()).toBe(hiddenDataColumn.title);
expect(labeTextOne).toBe(shownDataColumn.title!);
expect(labeTextTwo).toBe(hiddenDataColumn.title!);
});
it('should NOT show hidden columns at the end of the list if sorting is disabled', async () => {
@ -219,9 +221,31 @@ describe('ColumnsSelectorComponent', () => {
fixture.detectChanges();
const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);
const labeTextOne = await checkboxes[0].getLabelText();
const labeTextTwo = await checkboxes[1].getLabelText();
expect(await checkboxes[0].getLabelText()).toBe(hiddenDataColumn.title);
expect(await checkboxes[1].getLabelText()).toBe(shownDataColumn.title);
expect(labeTextOne).toBe(hiddenDataColumn.title!);
expect(labeTextTwo).toBe(shownDataColumn.title!);
});
it('should show subtitle', async () => {
const column: DataColumn = {
id: 'shownDataColumn',
title: 'title',
subtitle: 'subtitle',
key: 'shownDataColumn',
type: 'text'
};
component.columns = [column];
component.columnsSorting = false;
menuOpenedTrigger.next();
fixture.detectChanges();
const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);
const labeTextOne = await checkboxes[0].getLabelText();
expect(labeTextOne).toBe(`${column.title} device_hub ${column.subtitle}`);
});
});
});

View File

@ -58,7 +58,7 @@ export class ColumnsSelectorComponent implements OnInit {
columnItems: DataColumn[] = [];
searchInputControl = new UntypedFormControl('');
searchQuery = '';
private readonly destroyRef = inject(DestroyRef);
ngOnInit(): void {

View File

@ -4,7 +4,8 @@
class="adf-full-width adf-datatable-list"
[class.adf-sticky-header]="isStickyHeaderEnabled()"
[class.adf-datatable--empty]="(isEmpty() && !isHeaderVisible()) || loading"
[class.adf-datatable--empty--header-visible]="isEmpty() && isHeaderVisible()">
[class.adf-datatable--empty--header-visible]="isEmpty() && isHeaderVisible()"
>
<div *ngIf="isHeaderVisible()" class="adf-datatable-header" role="rowgroup" [ngClass]="{ 'adf-sr-only': !isHeaderVisible() }">
<adf-datatable-row
cdkDropList
@ -38,29 +39,31 @@
let lastColumn = last"
>
<div
class="adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-cell-header adf-datatable-cell-data"
*ngIf="col.title || !showProvidedActions"
[attr.data-automation-id]="'auto_id_' + col.key"
[ngClass]="{
'adf-sortable': col.sortable,
'adf-datatable__cursor--pointer': !isResizing,
'adf-datatable__header--sorted-asc': isColumnSorted(col, 'asc'),
'adf-datatable__header--sorted-desc': isColumnSorted(col, 'desc')}"
[ngStyle]="(col.width) && !lastColumn && {'flex': getFlexValue(col)}"
[attr.aria-label]="col.title | translate"
(click)="onColumnHeaderClick(col, $event)"
(keyup.enter)="onColumnHeaderClick(col, $event)"
role="columnheader"
[attr.tabindex]="isHeaderVisible() ? 0 : null"
[attr.aria-sort]="col.sortable ? (getAriaSort(col) | translate) : null"
cdkDrag
cdkDragLockAxis="x"
(cdkDragStarted)="isDraggingHeaderColumn = true"
(cdkDragDropped)="onDropHeaderColumn($event)"
[cdkDragDisabled]="!col.draggable"
(mouseenter)="hoveredHeaderColumnIndex = columnIndex"
(mouseleave)="hoveredHeaderColumnIndex = -1"
adf-drop-zone dropTarget="header" [dropColumn]="col">
class="adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-cell-header adf-datatable-cell-data"
*ngIf="col.title || !showProvidedActions"
[attr.data-automation-id]="'auto_id_' + col.key"
[ngClass]="{
'adf-sortable': col.sortable,
'adf-datatable__cursor--pointer': !isResizing,
'adf-datatable__header--sorted-asc': isColumnSorted(col, 'asc'),
'adf-datatable__header--sorted-desc': isColumnSorted(col, 'desc')}"
[ngStyle]="(col.width) && !lastColumn && {'flex': getFlexValue(col)}"
[attr.aria-label]="col.title | translate"
(click)="onColumnHeaderClick(col, $event)"
(keyup.enter)="onColumnHeaderClick(col, $event)"
role="columnheader"
[attr.tabindex]="isHeaderVisible() ? 0 : null"
[attr.aria-sort]="col.sortable ? (getAriaSort(col) | translate) : null"
cdkDrag
cdkDragLockAxis="x"
(cdkDragStarted)="isDraggingHeaderColumn = true"
(cdkDragDropped)="onDropHeaderColumn($event)"
[cdkDragDisabled]="!col.draggable"
(mouseenter)="hoveredHeaderColumnIndex = columnIndex"
(mouseleave)="hoveredHeaderColumnIndex = -1"
adf-drop-zone dropTarget="header"
[dropColumn]="col"
>
<div
adf-resizable
@ -77,7 +80,21 @@
!isResizing && col.sortable}"
>
<ng-container *ngIf="!col.header">
<span *ngIf="col.title" title="{{col.title | translate}}" class="adf-datatable-cell-value">{{col.title | translate}}</span>
<span
*ngIf="col.title"
title="{{col.title | translate}}"
class="adf-datatable-cell-value"
>
{{col.title | translate}}
</span>
<span
*ngIf="col.subtitle"
title="{{col.subtitle | translate}}"
class="adf-datatable-cell-value adf-datatable-cell-header_subtitle"
>
({{col.subtitle | translate}})
</span>
<span *ngIf="col.title && col.sortable && isDraggingHeaderColumn" class="adf-sr-only" aria-live="polite">
{{ getSortLiveAnnouncement(col) | translate: { string: col.title | translate } }}
@ -232,20 +249,23 @@
{{ 'ADF-DATATABLE.ACCESSIBILITY.SELECT_FILE' | translate }}
</mat-checkbox>
</label>
<div *ngFor="let col of getVisibleColumns(); let lastColumn = last;"
role="gridcell"
class="adf-datatable-cell adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-cell-data"
[attr.title]="col.title | translate"
[attr.data-automation-id]="getAutomationValue(row)"
[attr.aria-selected]="row.isSelected"
[attr.aria-label]="col.title ? (col.title | translate) : null"
(click)="onRowClick(row, $event)"
tabindex="0"
(keydown.enter)="onEnterKeyPressed(row, $any($event))"
[adf-context-menu]="getContextMenuActions(row, col)"
[adf-context-menu-enabled]="contextMenu"
adf-drop-zone dropTarget="cell" [dropColumn]="col" [dropRow]="row"
[ngStyle]="(col.width) && !lastColumn && {'flex': getFlexValue(col)}">
<div
*ngFor="let col of getVisibleColumns(); let lastColumn = last;"
role="gridcell"
class="adf-datatable-cell adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-cell-data"
[attr.title]="col.title | translate"
[attr.data-automation-id]="getAutomationValue(row)"
[attr.aria-selected]="row.isSelected"
[attr.aria-label]="col.title ? (col.title | translate) : null"
(click)="onRowClick(row, $event)"
tabindex="0"
(keydown.enter)="onEnterKeyPressed(row, $any($event))"
[adf-context-menu]="getContextMenuActions(row, col)"
[adf-context-menu-enabled]="contextMenu"
adf-drop-zone dropTarget="cell" [dropColumn]="col" [dropRow]="row"
[ngStyle]="(col.width) && !lastColumn && {'flex': getFlexValue(col)}"
>
<div *ngIf="!col.template" class="adf-datatable-cell-container">
<ng-container [ngSwitch]="data.getColumnType(row, col)">
<div *ngSwitchCase="'image'" class="adf-cell-value">
@ -271,16 +291,22 @@
</ng-template>
</ng-template>
</div>
<div *ngSwitchCase="'icon'" class="adf-cell-value">
<adf-icon-cell
[data]="data"
[column]="col"
[row]="row"
[resolverFn]="resolverFn"
[tooltip]="getCellTooltip(row, col)" />
[tooltip]="getCellTooltip(row, col)"
/>
</div>
<div *ngSwitchCase="'date'" class="adf-cell-value adf-cell-date" [attr.tabindex]="data.getValue(row, col, resolverFn)? 0 : -1"
[attr.data-automation-id]="'date_' + (data.getValue(row, col, resolverFn) | adfLocalizedDate: 'medium') ">
<div
*ngSwitchCase="'date'"
class="adf-cell-value adf-cell-date"
[attr.tabindex]="data.getValue(row, col, resolverFn)? 0 : -1"
[attr.data-automation-id]="'date_' + (data.getValue(row, col, resolverFn) | adfLocalizedDate: 'medium') "
>
<adf-date-cell class="adf-datatable-center-date-column-ie"
[data]="data"
[column]="col"
@ -289,6 +315,7 @@
[tooltip]="getCellTooltip(row, col)"
[dateConfig]="col.dateConfig" />
</div>
<div *ngSwitchCase="'location'" [attr.tabindex]="data.getValue(row, col, resolverFn)? 0 : -1" class="adf-cell-value"
[attr.data-automation-id]="'location' + data.getValue(row, col, resolverFn)">
<adf-location-cell

View File

@ -524,6 +524,11 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width-1 !default;
color: var(--adf-theme-foreground-text-color);
box-sizing: border-box;
&_subtitle {
margin-left: 3px;
opacity: 0.6;
}
&.adf-sortable {
@include adf-no-select;

View File

@ -76,6 +76,9 @@ export class DataColumnComponent implements OnInit {
@Input()
title: string = '';
@Input()
subtitle: string = '';
@ContentChild(TemplateRef)
template: any;

View File

@ -27,6 +27,7 @@ export interface DataColumn<T = unknown> {
format?: string;
sortable?: boolean;
title?: string;
subtitle?: string;
srTitle?: string;
cssClass?: string;
template?: TemplateRef<any>;

View File

@ -27,6 +27,7 @@ export class ObjectDataColumn<T = unknown> implements DataColumn<T> {
format: string;
sortable: boolean;
title: string;
subtitle?: string;
srTitle: string;
cssClass: string;
template?: TemplateRef<any>;
@ -52,6 +53,7 @@ export class ObjectDataColumn<T = unknown> implements DataColumn<T> {
this.format = input.format;
this.sortable = input.sortable;
this.title = input.title;
this.subtitle = input.subtitle;
this.srTitle = input.srTitle;
this.cssClass = input.cssClass;
this.template = input.template;