AAE-41496 Deprecate icon component (#11550)

* AAE-41496 Deprecate icon component

* AAE-41496 Improve template

* AAE-41496 Fix tests
This commit is contained in:
Diogo Bastos
2026-01-21 11:34:54 +00:00
committed by GitHub
parent 69b73c8490
commit d838d93abc
39 changed files with 211 additions and 255 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
---
Title: Icon Component
Added: v3.0.0
Status: Active
Last reviewed: 2025-12-11
Status: Deprecated
Last reviewed: 2026-01-20
---
# [Icon Component](../../../lib/core/src/lib/icon/icon.component.ts "Defined in icon.component.ts")
@@ -11,10 +11,11 @@
[attr.aria-label]="isActive() ? getTooltipTranslation(col?.title) + ' (' + ('SEARCH.SEARCH_HEADER.ACTIVE_FILTER' | translate) + ')' : getTooltipTranslation(col?.title)"
[title]="getTooltipTranslation(col?.title)"
>
<adf-icon
[value]="isActive() ? 'adf:custom_filter_filled' : 'adf:custom_filter'"
[ngClass]="{ 'adf-icon-active': isActive() || menuTrigger.menuOpen }"
<mat-icon
[svgIcon]="isActive() ? 'adf:custom_filter_filled' : 'adf:custom_filter'"
[ngClass]="{ 'adf-filter-icon-active': isActive() || menuTrigger.menuOpen }"
class="adf-filter-icon"
aria-hidden="true"
/>
</button>
@@ -4,22 +4,14 @@
.adf-filter {
&-button:has(.adf-filter-icon) {
margin-left: -7px;
}
.adf-icon {
opacity: 1;
color: var(--adf-theme-foreground-icon-color);
&-icon {
opacity: 1;
color: var(--adf-theme-foreground-icon-color);
&.adf-icon-active {
color: var(--theme-primary-color);
span {
border: 2px solid var(--adf-theme-background-card-color);
width: 10px;
height: 10px;
top: -3px;
right: -6px;
}
}
&-active {
color: var(--theme-primary-color);
}
}
@@ -28,8 +28,9 @@ import { MatMenuHarness } from '@angular/material/menu/testing';
import { MatButtonHarness } from '@angular/material/button/testing';
import { MatInputHarness } from '@angular/material/input/testing';
import { provideRouter } from '@angular/router';
import { IconComponent, UnitTestingUtils } from '@alfresco/adf-core';
import { UnitTestingUtils } from '@alfresco/adf-core';
import { MatIconRegistry } from '@angular/material/icon';
import { MatIconHarness } from '@angular/material/icon/testing';
const mockCategory: SearchCategory = {
id: 'queryName',
@@ -149,18 +150,20 @@ describe('SearchFilterContainerComponent', () => {
expect(eventRaised).toBe(true);
});
it('should display correct icon based on active state of the filter', () => {
it('should display correct icon based on active state of the filter', async () => {
component.isActive = () => false;
fixture.detectChanges();
const inactiveIcon: IconComponent = unitTestingUtils.getByCSS('.adf-filter-icon').componentInstance;
expect(inactiveIcon.value).toBe('adf:custom_filter');
const icon = await loader.getHarnessOrNull(MatIconHarness.with({ selector: '.adf-filter-icon' }));
expect(await icon.getNamespace()).toBe('adf');
expect(await icon.getName()).toBe('custom_filter');
component.isActive = () => true;
fixture.detectChanges();
const activeIcon: IconComponent = unitTestingUtils.getByCSS('.adf-filter-icon').componentInstance;
expect(activeIcon.value).toBe('adf:custom_filter_filled');
expect(await icon.getNamespace()).toBe('adf');
expect(await icon.getName()).toBe('custom_filter_filled');
});
it('should register custom icons when component is initialized', () => {
@@ -17,7 +17,7 @@
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
import { DataColumn, IconComponent, TranslationService } from '@alfresco/adf-core';
import { DataColumn, TranslationService } from '@alfresco/adf-core';
import { SearchWidgetContainerComponent } from '../search-widget-container/search-widget-container.component';
import { SearchHeaderQueryBuilderService } from '../../services/search-header-query-builder.service';
import { SearchCategory } from '../../models/search-category.interface';
@@ -28,11 +28,19 @@ import { MatButtonModule } from '@angular/material/button';
import { TranslatePipe } from '@ngx-translate/core';
import { MatDialogModule } from '@angular/material/dialog';
import { DomSanitizer } from '@angular/platform-browser';
import { MatIconRegistry } from '@angular/material/icon';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
@Component({
selector: 'adf-search-filter-container',
imports: [CommonModule, MatButtonModule, MatMenuModule, IconComponent, SearchWidgetContainerComponent, TranslatePipe, MatDialogModule],
imports: [
CommonModule,
MatButtonModule,
MatMenuModule,
MatIconModule,
SearchWidgetContainerComponent,
TranslatePipe,
MatDialogModule
],
templateUrl: './search-filter-container.component.html',
styleUrls: ['./search-filter-container.component.scss'],
encapsulation: ViewEncapsulation.None
@@ -22,9 +22,10 @@
color="primary"
mode="indeterminate"
[diameter]="24"
*ngIf="node.isLoading; else loadMoreIcon" />
*ngIf="node.isLoading; else loadMoreIcon"
/>
<ng-template #loadMoreIcon>
<adf-icon [value]="'chevron_right'" />
<mat-icon adf-icon="chevron_right" aria-hidden="true" />
</ng-template>
</button>
</div>
@@ -46,19 +47,23 @@
matTreeNodePadding
[adf-context-menu]="contextMenuOptions"
[adf-context-menu-enabled]="!!contextMenuOptions"
(contextmenu)="contextMenuSource = node">
(contextmenu)="contextMenuSource = node"
>
<div class="adf-tree-expand-collapse-container">
<button *ngIf="node.hasChildren"
class="adf-tree-expand-collapse-button"
(click)="expandCollapseNode(node)"
mat-icon-button>
<button
*ngIf="node.hasChildren"
class="adf-tree-expand-collapse-button"
(click)="expandCollapseNode(node)"
mat-icon-button
>
<mat-progress-spinner
color="primary"
mode="indeterminate"
[diameter]="24"
*ngIf="node.isLoading; else expandCollapseIcon" />
*ngIf="node.isLoading; else expandCollapseIcon"
/>
<ng-template #expandCollapseIcon>
<adf-icon [value]="expandCollapseIconValue(node)" />
<mat-icon [adf-icon]="expandCollapseIconValue(node)" aria-hidden="true" />
</ng-template>
</button>
</div>
@@ -30,6 +30,7 @@ import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
import { MatIconHarness } from '@angular/material/icon/testing';
describe('TreeComponent', () => {
let fixture: ComponentFixture<TreeComponent<TreeNode>>;
@@ -141,18 +142,17 @@ describe('TreeComponent', () => {
expect(matSpinnerElement).not.toBeNull();
});
it('should show provided expand/collapse icons', () => {
it('should show provided expand/collapse icons', async () => {
component.treeService.treeNodes = Array.from(treeNodesMockExpanded);
component.expandIcon = 'folder';
component.collapseIcon = 'chevron_left';
component.treeService.collapseNode(component.treeService.treeNodes[0]);
fixture.detectChanges();
let nodeIcons: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(nodeIcons[0].nativeElement.innerText).toContain('folder');
const icon = await loader.getHarnessOrNull(MatIconHarness.with({ ancestor: '.adf-tree-expand-collapse-button' }));
expect(await icon.getName()).toContain('folder');
spyOn(component.treeService.treeControl, 'isExpanded').and.returnValue(true);
fixture.detectChanges();
nodeIcons = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(nodeIcons[0].nativeElement.innerText).toContain('chevron_left');
expect(await icon.getName()).toContain('chevron_left');
});
it('when node has more items to load loadMore node should appear', () => {
@@ -31,7 +31,7 @@ import {
import { BehaviorSubject, merge, Observable, Subject } from 'rxjs';
import { TreeNode, TreeNodeType } from '../models/tree-node.interface';
import { TreeService } from '../services/tree.service';
import { ContextMenuDirective, IconComponent, IconModule, PaginationModel, UserPreferencesService } from '@alfresco/adf-core';
import { ContextMenuDirective, IconModule, PaginationModel, UserPreferencesService } from '@alfresco/adf-core';
import { SelectionChange, SelectionModel } from '@angular/cdk/collections';
import { TreeResponse } from '../models/tree-response.interface';
import { MatCheckbox, MatCheckboxModule } from '@angular/material/checkbox';
@@ -52,7 +52,6 @@ import { MatMenuModule } from '@angular/material/menu';
MatTreeModule,
MatProgressSpinnerModule,
MatButtonModule,
IconComponent,
ContextMenuDirective,
MatCheckboxModule,
MatMenuModule,
@@ -1,7 +1,7 @@
<div class="adf-file-uploading-row" [attr.aria-label]="file.name + ' ' + file.status">
<mat-icon *ngIf="mimeType === 'default'" matListItemIcon class="adf-file-uploading-row__type" adf-icon="insert_drive_file" />
<adf-icon *ngIf="mimeType !== 'default'" value="adf:{{ mimeType }}" />
<mat-icon *ngIf="mimeType !== 'default'" svgIcon="adf:{{ mimeType }}" aria-hidden="true" />
<span
class="adf-file-uploading-row__name"
@@ -19,7 +19,7 @@ import { FileModel, FileUploadStatus } from '../../common/models/file.model';
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatListModule } from '@angular/material/list';
import { FileSizePipe, IconComponent, IconModule } from '@alfresco/adf-core';
import { FileSizePipe, IconModule } from '@alfresco/adf-core';
import { MatChipsModule } from '@angular/material/chips';
import { TranslatePipe } from '@ngx-translate/core';
import { ToggleIconDirective } from '../directives/toggle-icon.directive';
@@ -32,7 +32,6 @@ import { FileUploadErrorPipe } from '../pipes/file-upload-error.pipe';
CommonModule,
IconModule,
MatListModule,
IconComponent,
MatChipsModule,
TranslatePipe,
ToggleIconDirective,
@@ -125,11 +125,13 @@
cdkDragHandle
[ngClass]="{ 'adf-datatable-cell-header-drag-icon': !isResizing }"
>
<adf-icon
<mat-icon
*ngIf="hoveredHeaderColumnIndex === columnIndex && !isResizing"
value="adf:drag_indicator"
svgIcon="adf:drag_indicator"
class="adf-datatable-cell-header-drag-icon-visible"
[attr.data-automation-id]="'adf-datatable-cell-header-drag-icon-'+col.key" />
[attr.data-automation-id]="'adf-datatable-cell-header-drag-icon-'+col.key"
aria-hidden="true"
/>
</span>
</div>
<div
@@ -68,7 +68,6 @@ import { TranslatePipe } from '@ngx-translate/core';
import { FileTypePipe, LocalizedDatePipe } from '../../../pipes';
import { DropZoneDirective } from '../../directives/drop-zone.directive';
import { ResizableDirective } from '../../directives/resizable/resizable.directive';
import { IconComponent } from '../../../icon';
import { ResizeHandleDirective } from '../../directives/resizable/resize-handle.directive';
import { MatButtonModule } from '@angular/material/button';
import { UploadDirective } from '../../../directives';
@@ -104,7 +103,6 @@ export enum ShowHeaderMode {
DropZoneDirective,
ResizableDirective,
CdkDragHandle,
IconComponent,
ResizeHandleDirective,
MatButtonModule,
MatMenuModule,
@@ -1,80 +0,0 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, moduleMetadata, StoryObj } from '@storybook/angular';
import { IconComponent } from './icon.component';
import { provideStoryCore } from '../stories/core-story.providers';
const meta: Meta<IconComponent> = {
component: IconComponent,
title: 'Core/Icon/Icon',
decorators: [
moduleMetadata({
imports: [IconComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Provides a universal way of rendering registered and named icons.`
}
}
},
argTypes: {
color: {
control: 'radio',
options: ['primary', 'accent', 'warn', undefined],
description: 'icon color',
defaultValue: undefined,
table: {
type: { summary: 'ThemePalette' },
defaultValue: { summary: 'undefined' }
}
},
value: {
description: 'icon name',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'settings' }
}
}
}
};
export default meta;
type Story = StoryObj<IconComponent>;
export const DefaultIcon: Story = {
render: (args) => ({
props: args
}),
args: {
value: ''
}
};
export const CustomIcon: Story = {
render: (args) => ({
props: args
}),
args: {
value: 'cloud_download'
}
};
+2
View File
@@ -22,6 +22,8 @@ import { ICON_ALIAS_MAP_TOKEN } from './icon-alias-map.token';
export const DEFAULT_ICON_VALUE = 'settings';
/** @deprecated Use material icon with aria-hidden="true" attribute instead. */
@Component({
selector: 'adf-icon',
imports: [MatIconModule],
@@ -1,5 +1,5 @@
<div class="adf-empty-content" [title]="title | translate">
<adf-icon class="adf-empty-content__icon" [value]="icon" />
<mat-icon class="adf-empty-content__icon" [adf-icon]="icon" aria-hidden="true" />
<div class="adf-empty-content__title">{{ title | translate }}</div>
<div class="adf-empty-content__subtitle">{{ subtitle | translate }}</div>
<ng-content />
@@ -11,6 +11,7 @@
font-size: var(--theme-display-3-font-size);
height: var(--theme-display-3-font-size);
width: var(--theme-display-3-font-size);
opacity: 0.6;
}
&__title {
@@ -30,8 +31,4 @@
white-space: normal;
text-align: center;
}
.adf-icon {
opacity: 0.6;
}
}
@@ -17,11 +17,11 @@
import { Component, ChangeDetectionStrategy, ViewEncapsulation, Input } from '@angular/core';
import { TranslatePipe } from '@ngx-translate/core';
import { IconComponent } from '../../icon';
import { IconModule } from '../../icon';
@Component({
selector: 'adf-empty-content',
imports: [TranslatePipe, IconComponent],
imports: [TranslatePipe, IconModule],
templateUrl: './empty-content.component.html',
styleUrls: ['./empty-content.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -55,7 +55,6 @@ import { ViewerSidebarComponent } from './viewer-sidebar.component';
import { ViewerToolbarComponent } from './viewer-toolbar.component';
import { ViewerToolbarActionsComponent } from './viewer-toolbar-actions.component';
import { ViewerToolbarCustomActionsComponent } from './viewer-toolbar-custom-actions.component';
import { IconComponent } from '../../icon';
import { ThumbnailService } from '../../common';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { IconModule } from '../../icon/icon.module';
@@ -88,8 +87,7 @@ const DEFAULT_NON_PREVIEW_CONFIG = {
ViewerToolbarComponent,
ViewerSidebarComponent,
ViewerToolbarActionsComponent,
ViewerToolbarCustomActionsComponent,
IconComponent
ViewerToolbarCustomActionsComponent
],
providers: [ViewUtilService]
})
@@ -8,13 +8,19 @@
<span *ngIf="showTitle"> {{ 'ADF_CLOUD_EDIT_PROCESS_FILTER.TITLE' | translate}}</span>
<div *ngIf="showFilterActions" class="adf-cloud-edit-process-filter-actions">
<ng-container *ngIf="toggleFilterActions">
<button *ngFor="let filterAction of processFilterActions"
mat-icon-button
[title]="filterAction.tooltip | translate"
[attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType"
[disabled]="isDisabledAction(filterAction)"
(click)="executeFilterActions($event, filterAction)">
<adf-icon [value]="filterAction.icon" />
<button
*ngFor="let filterAction of processFilterActions"
mat-icon-button
[title]="filterAction.tooltip | translate"
[attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType"
[disabled]="isDisabledAction(filterAction)"
(click)="executeFilterActions($event, filterAction)"
>
@if (filterAction.icon.includes(':')) {
<mat-icon [svgIcon]="filterAction.icon" aria-hidden="true" />
} @else {
<mat-icon [adf-icon]="filterAction.icon" aria-hidden="true" />
}
</button>
</ng-container>
</div>
@@ -29,7 +29,7 @@ import {
ProcessFilterProperties,
ProcessSortFilterProperty
} from '../../models/process-filter-cloud.model';
import { DateFnsUtils, IconComponent, IconModule, TranslationService, UserPreferencesService } from '@alfresco/adf-core';
import { DateFnsUtils, IconModule, TranslationService, UserPreferencesService } from '@alfresco/adf-core';
import { ProcessFilterCloudService } from '../../services/process-filter-cloud.service';
import { ProcessFilterDialogCloudComponent } from '../process-filter-dialog/process-filter-dialog-cloud.component';
import { ProcessCloudService } from '../../../services/process-cloud.service';
@@ -80,7 +80,6 @@ interface ProcessFilterFormProps {
@Component({
selector: 'adf-cloud-edit-process-filter',
imports: [
IconComponent,
MatProgressSpinnerModule,
TranslatePipe,
MatButtonModule,
@@ -10,10 +10,7 @@
>
<div class="adf-process-filters__entry">
<div>
<adf-icon
data-automation-id="adf-filter-icon"
*ngIf="showIcons"
[value]="filter.icon" />
<mat-icon data-automation-id="adf-filter-icon" *ngIf="showIcons" [adf-icon]="filter.icon" aria-hidden="true" />
<span
data-automation-id="adf-filter-label"
class="adf-filter-action-button__label">
@@ -27,6 +27,9 @@ import { mockProcessFilters } from '../../mock/process-filters-cloud.mock';
import { AppConfigService, AppConfigServiceMock } from '@alfresco/adf-core';
import { ProcessListCloudService } from '../../../process-list/services/process-list-cloud.service';
import { ApolloTestingModule } from 'apollo-angular/testing';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatIconHarness } from '@angular/material/icon/testing';
const ProcessFilterCloudServiceMock = {
getProcessFilters: () => of(mockProcessFilters),
@@ -40,6 +43,7 @@ describe('ProcessFiltersCloudComponent', () => {
let fixture: ComponentFixture<ProcessFiltersCloudComponent>;
let getProcessFiltersSpy: jasmine.Spy;
let getProcessNotificationSubscriptionSpy: jasmine.Spy;
let loader: HarnessLoader;
const configureTestingModule = (searchApiMethod: 'GET' | 'POST') => {
TestBed.configureTestingModule({
@@ -58,6 +62,7 @@ describe('ProcessFiltersCloudComponent', () => {
]
});
fixture = TestBed.createComponent(ProcessFiltersCloudComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
component = fixture.componentInstance;
component.searchApiMethod = searchApiMethod;
@@ -88,11 +93,11 @@ describe('ProcessFiltersCloudComponent', () => {
await fixture.whenStable();
expect(component.filters.length).toBe(3);
const filters = fixture.nativeElement.querySelectorAll('.adf-icon');
expect(filters.length).toBe(3);
expect(filters[0].innerText).toContain('adjust');
expect(filters[1].innerText).toContain('inbox');
expect(filters[2].innerText).toContain('done');
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(3);
expect(await filterIcons[0].getName()).toContain('adjust');
expect(await filterIcons[1].getName()).toContain('inbox');
expect(await filterIcons[2].getName()).toContain('done');
});
it('should not attach icons for each filter if hasIcon is false', async () => {
@@ -103,8 +108,8 @@ describe('ProcessFiltersCloudComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0);
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(0);
});
it('should display the filters', async () => {
@@ -245,11 +250,11 @@ describe('ProcessFiltersCloudComponent', () => {
await fixture.whenStable();
expect(component.filters.length).toBe(3);
const filters = fixture.nativeElement.querySelectorAll('.adf-icon');
expect(filters.length).toBe(3);
expect(filters[0].innerText).toContain('adjust');
expect(filters[1].innerText).toContain('inbox');
expect(filters[2].innerText).toContain('done');
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(3);
expect(await filterIcons[0].getName()).toContain('adjust');
expect(await filterIcons[1].getName()).toContain('inbox');
expect(await filterIcons[2].getName()).toContain('done');
});
it('should not attach icons for each filter if hasIcon is false', async () => {
@@ -260,8 +265,8 @@ describe('ProcessFiltersCloudComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0);
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(0);
});
it('should display the filters', async () => {
@@ -19,7 +19,7 @@ import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit,
import { Observable } from 'rxjs';
import { ProcessFilterCloudService } from '../../services/process-filter-cloud.service';
import { ProcessFilterCloudModel } from '../../models/process-filter-cloud.model';
import { AppConfigService, IconComponent, TranslationService } from '@alfresco/adf-core';
import { AppConfigService, IconModule, TranslationService } from '@alfresco/adf-core';
import { FilterParamsModel } from '../../../../task/task-filters/models/filter-cloud.model';
import { debounceTime, tap } from 'rxjs/operators';
import { ProcessListCloudService } from '../../../process-list/services/process-list-cloud.service';
@@ -32,7 +32,7 @@ import { MatListModule } from '@angular/material/list';
@Component({
selector: 'adf-cloud-process-filters',
imports: [TranslatePipe, IconComponent, NgIf, MatProgressSpinnerModule, NgForOf, MatListModule, AsyncPipe],
imports: [TranslatePipe, IconModule, NgIf, MatProgressSpinnerModule, NgForOf, MatListModule, AsyncPipe],
templateUrl: './process-filters-cloud.component.html',
styleUrls: ['./process-filters-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
@@ -8,13 +8,19 @@
<span *ngIf="showTitle">{{ 'ADF_CLOUD_EDIT_TASK_FILTER.TITLE' | translate}}</span>
<div *ngIf="showFilterActions" class="adf-cloud-edit-task-filter-actions">
<ng-container *ngIf="toggleFilterActions">
<button *ngFor="let filterAction of taskFilterActions"
mat-icon-button
[title]="filterAction.tooltip | translate"
[attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType"
[disabled]="isDisabledAction(filterAction)"
(click)="executeFilterActions(filterAction)">
<adf-icon [value]="filterAction.icon" />
<button
*ngFor="let filterAction of taskFilterActions"
mat-icon-button
[title]="filterAction.tooltip | translate"
[attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType"
[disabled]="isDisabledAction(filterAction)"
(click)="executeFilterActions(filterAction)"
>
@if (filterAction.icon.includes(':')) {
<mat-icon [svgIcon]="filterAction.icon" aria-hidden="true" />
} @else {
<mat-icon [adf-icon]="filterAction.icon" aria-hidden="true" />
}
</button>
</ng-container>
</div>
@@ -25,7 +25,7 @@ import { CommonModule } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatButtonModule } from '@angular/material/button';
import { IconComponent, IconModule } from '@alfresco/adf-core';
import { IconModule } from '@alfresco/adf-core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
@@ -44,7 +44,6 @@ import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter
TranslatePipe,
MatExpansionModule,
MatButtonModule,
IconComponent,
MatProgressSpinnerModule,
ReactiveFormsModule,
MatFormFieldModule,
@@ -8,13 +8,19 @@
<span *ngIf="showTitle">{{ 'ADF_CLOUD_EDIT_TASK_FILTER.TITLE' | translate}}</span>
<div *ngIf="showFilterActions" class="adf-cloud-edit-task-filter-actions">
<ng-container *ngIf="toggleFilterActions">
<button *ngFor="let filterAction of taskFilterActions"
mat-icon-button
[title]="filterAction.tooltip | translate"
[attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType"
[disabled]="isDisabledAction(filterAction)"
(click)="executeFilterActions(filterAction)">
<adf-icon [value]="filterAction.icon" />
<button
*ngFor="let filterAction of taskFilterActions"
mat-icon-button
[title]="filterAction.tooltip | translate"
[attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType"
[disabled]="isDisabledAction(filterAction)"
(click)="executeFilterActions(filterAction)"
>
@if (filterAction.icon.includes(':')) {
<mat-icon [svgIcon]="filterAction.icon" aria-hidden="true" />
} @else {
<mat-icon [adf-icon]="filterAction.icon" aria-hidden="true" />
}
</button>
</ng-container>
</div>
@@ -28,7 +28,7 @@ import { CommonModule } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatButtonModule } from '@angular/material/button';
import { IconComponent, IconModule } from '@alfresco/adf-core';
import { IconModule } from '@alfresco/adf-core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatFormFieldModule } from '@angular/material/form-field';
import { ReactiveFormsModule } from '@angular/forms';
@@ -47,7 +47,6 @@ import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter
TranslatePipe,
MatExpansionModule,
MatButtonModule,
IconComponent,
MatProgressSpinnerModule,
MatFormFieldModule,
ReactiveFormsModule,
@@ -6,21 +6,21 @@
[attr.aria-label]="filter.name | translate"
[id]="filter.id"
[attr.data-automation-id]="filter.key + '_filter'"
[class.adf-active]="currentFilter === filter">
[class.adf-active]="currentFilter === filter"
>
<div class="adf-task-filters__entry">
<div class="adf-task-filters__entry-label">
<adf-icon data-automation-id="adf-filter-icon"
*ngIf="showIcons"
[value]="filter.icon"
/>
<mat-icon data-automation-id="adf-filter-icon" *ngIf="showIcons" [adf-icon]="filter.icon" aria-hidden="true" />
<span data-automation-id="adf-filter-label">
{{ filter.name | translate }}
</span>
</div>
<span *ngIf="counters[filter.key]"
<span
*ngIf="counters[filter.key]"
[attr.data-automation-id]="filter.key + '_filter-counter'"
class="adf-task-filters__entry-counter"
[class.adf-active]="wasFilterUpdated(filter.key)">
[class.adf-active]="wasFilterUpdated(filter.key)"
>
{{ counters[filter.key] }}
</span>
</div>
@@ -25,6 +25,9 @@ import { fakeGlobalServiceFilters } from '../../mock/task-filters-cloud.mock';
import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
import { ServiceTaskFiltersCloudComponent } from './service-task-filters-cloud.component';
import { NoopAuthModule, NoopTranslateModule } from '@alfresco/adf-core';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatIconHarness } from '@angular/material/icon/testing';
describe('ServiceTaskFiltersCloudComponent', () => {
let serviceTaskFilterCloudService: ServiceTaskFilterCloudService;
@@ -32,6 +35,7 @@ describe('ServiceTaskFiltersCloudComponent', () => {
let component: ServiceTaskFiltersCloudComponent;
let fixture: ComponentFixture<ServiceTaskFiltersCloudComponent>;
let loader: HarnessLoader;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -39,6 +43,7 @@ describe('ServiceTaskFiltersCloudComponent', () => {
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
});
fixture = TestBed.createComponent(ServiceTaskFiltersCloudComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
component = fixture.componentInstance;
serviceTaskFilterCloudService = TestBed.inject(ServiceTaskFilterCloudService);
@@ -64,11 +69,11 @@ describe('ServiceTaskFiltersCloudComponent', () => {
expect(component.filters.length).toBe(3);
const filters = fixture.nativeElement.querySelectorAll('.adf-icon');
expect(filters.length).toBe(3);
expect(filters[0].innerText).toContain('adjust');
expect(filters[1].innerText).toContain('done');
expect(filters[2].innerText).toContain('inbox');
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(3);
expect(await filterIcons[0].getName()).toContain('adjust');
expect(await filterIcons[1].getName()).toContain('done');
expect(await filterIcons[2].getName()).toContain('inbox');
});
it('should not attach icons for each filter if hasIcon is false', async () => {
@@ -79,8 +84,8 @@ describe('ServiceTaskFiltersCloudComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0);
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(0);
});
it('should display the filters', async () => {
@@ -20,7 +20,7 @@ import { Observable } from 'rxjs';
import { FilterParamsModel, ServiceTaskFilterCloudModel } from '../../models/filter-cloud.model';
import { BaseTaskFiltersCloudComponent } from '../base-task-filters-cloud.component';
import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
import { IconComponent, TranslationService } from '@alfresco/adf-core';
import { IconModule, TranslationService } from '@alfresco/adf-core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
@@ -29,7 +29,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@Component({
selector: 'adf-cloud-service-task-filters',
imports: [CommonModule, TranslatePipe, MatListModule, IconComponent, MatProgressSpinnerModule],
imports: [CommonModule, TranslatePipe, MatListModule, IconModule, MatProgressSpinnerModule],
templateUrl: './service-task-filters-cloud.component.html',
styleUrls: ['./service-task-filters-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
@@ -6,21 +6,21 @@
[attr.aria-label]="filter.name | translate"
[id]="filter.id"
[attr.data-automation-id]="filter.key + '_filter'"
[class.adf-active]="currentFilter === filter">
[class.adf-active]="currentFilter === filter"
>
<div class="adf-task-filters__entry">
<div class="adf-task-filters__entry-label">
<adf-icon data-automation-id="adf-filter-icon"
*ngIf="showIcons"
[value]="filter.icon"
/>
<mat-icon data-automation-id="adf-filter-icon" *ngIf="showIcons" [adf-icon]="filter.icon" aria-hidden="true" />
<span data-automation-id="adf-filter-label">
{{ filter.name | translate }}
</span>
</div>
<span *ngIf="counters[filter.key]"
<span
*ngIf="counters[filter.key]"
[attr.data-automation-id]="filter.key + '_filter-counter'"
class="adf-task-filters__entry-counter"
[class.adf-active]="wasFilterUpdated(filter.key)">
[class.adf-active]="wasFilterUpdated(filter.key)"
>
{{ counters[filter.key] }}
</span>
</div>
@@ -32,6 +32,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { TaskFilterCloudAdapter } from '../../../../models/filter-cloud-model';
import { ApolloTestingModule } from 'apollo-angular/testing';
import { TaskFilterCloudModel } from '../../models/filter-cloud.model';
import { MatIconHarness } from '@angular/material/icon/testing';
describe('TaskFiltersCloudComponent', () => {
let loader: HarnessLoader;
@@ -89,11 +90,11 @@ describe('TaskFiltersCloudComponent', () => {
expect(component.filters.length).toBe(3);
const filters = fixture.nativeElement.querySelectorAll('.adf-icon');
expect(filters.length).toBe(3);
expect(filters[0].innerText).toContain('adjust');
expect(filters[1].innerText).toContain('done');
expect(filters[2].innerText).toContain('inbox');
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(3);
expect(await filterIcons[0].getName()).toContain('adjust');
expect(await filterIcons[1].getName()).toContain('done');
expect(await filterIcons[2].getName()).toContain('inbox');
});
it('should not attach icons for each filter if hasIcon is false', async () => {
@@ -104,8 +105,8 @@ describe('TaskFiltersCloudComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0);
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(0);
});
it('should display the filters', async () => {
@@ -251,25 +252,24 @@ describe('TaskFiltersCloudComponent', () => {
component.appName = 'my-app-1';
});
it('should attach specific icon for each filter if hasIcon is true', () => {
it('should attach specific icon for each filter if hasIcon is true', async () => {
fixture.detectChanges();
const filters = fixture.nativeElement.querySelectorAll('.adf-icon');
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(component.filters.length).toBe(3);
expect(filters.length).toBe(3);
expect(filters[0].innerText).toContain('adjust');
expect(filters[1].innerText).toContain('done');
expect(filters[2].innerText).toContain('inbox');
expect(filterIcons.length).toBe(3);
expect(await filterIcons[0].getName()).toContain('adjust');
expect(await filterIcons[1].getName()).toContain('done');
expect(await filterIcons[2].getName()).toContain('inbox');
});
it('should not attach icons for each filter if showIcons is false', () => {
it('should not attach icons for each filter if showIcons is false', async () => {
component.showIcons = false;
fixture.detectChanges();
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0);
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(0);
});
it('should display the filters', () => {
@@ -19,7 +19,7 @@ import { Component, EventEmitter, inject, Input, OnChanges, OnInit, Output, Simp
import { Observable } from 'rxjs';
import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
import { FilterParamsModel, TaskFilterCloudModel } from '../../models/filter-cloud.model';
import { AppConfigService, IconComponent, TranslationService } from '@alfresco/adf-core';
import { AppConfigService, IconModule, TranslationService } from '@alfresco/adf-core';
import { debounceTime, tap } from 'rxjs/operators';
import { BaseTaskFiltersCloudComponent } from '../base-task-filters-cloud.component';
import { TaskDetailsCloudModel } from '../../../models/task-details-cloud.model';
@@ -34,7 +34,7 @@ import { MatListModule } from '@angular/material/list';
@Component({
selector: 'adf-cloud-task-filters',
imports: [CommonModule, MatProgressSpinnerModule, TranslatePipe, IconComponent, MatListModule],
imports: [CommonModule, MatProgressSpinnerModule, TranslatePipe, IconModule, MatListModule],
templateUrl: './task-filters-cloud.component.html',
styleUrls: ['./task-filters-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
@@ -5,8 +5,9 @@
[id]="filter.id"
[attr.data-automation-id]="filter.name + '_filter'"
mat-button
class="adf-filter-action-button adf-full-width">
<adf-icon data-automation-id="adf-filter-icon" *ngIf="showIcon" [value]="getFilterIcon(filter.icon)" />
class="adf-filter-action-button adf-full-width"
>
<mat-icon data-automation-id="adf-filter-icon" *ngIf="showIcon" aria-hidden="true">{{ getFilterIcon(filter.icon) }}</mat-icon>
<span data-automation-id="adf-filter-label" class="adf-filter-action-button__label">{{ filter.name | translate }}</span>
</button>
</div>
@@ -21,10 +21,12 @@ import { AppsProcessService } from '../../../services/apps-process.service';
import { ProcessFilterService } from '../../services/process-filter.service';
import { ProcessFiltersComponent } from './process-filters.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NavigationStart, Router } from '@angular/router';
import { ProcessInstanceFilterRepresentation, UserProcessInstanceFilterRepresentation } from '@alfresco/js-api';
import { AlfrescoApiService, AlfrescoApiServiceMock } from '@alfresco/adf-content-services';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatIconHarness } from '@angular/material/icon/testing';
const fakeProcessFilters: UserProcessInstanceFilterRepresentation[] = [
{
@@ -55,6 +57,7 @@ describe('ProcessFiltersComponent', () => {
let router: Router;
let getProcessFiltersSpy: jasmine.Spy;
let getDeployedApplicationsByNameSpy: jasmine.Spy;
let loader: HarnessLoader;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -73,6 +76,7 @@ describe('ProcessFiltersComponent', () => {
router = TestBed.inject(Router);
fixture = TestBed.createComponent(ProcessFiltersComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
filterList = fixture.componentInstance;
});
@@ -278,11 +282,11 @@ describe('ProcessFiltersComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
expect(filterList.filters.length).toBe(3);
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(3);
expect(filters[0].nativeElement.innerText).toContain('dashboard');
expect(filters[1].nativeElement.innerText).toContain('shuffle');
expect(filters[2].nativeElement.innerText).toContain('check_circle');
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(3);
expect(await filterIcons[0].getName()).toContain('dashboard');
expect(await filterIcons[1].getName()).toContain('shuffle');
expect(await filterIcons[2].getName()).toContain('check_circle');
});
it('should not attach icons for each filter if hasIcon is false', async () => {
@@ -291,8 +295,8 @@ describe('ProcessFiltersComponent', () => {
filterList.ngOnChanges({ appId: change });
fixture.detectChanges();
await fixture.whenStable();
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0);
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(0);
});
it('should set isProcessActive to true when activeRoute includes "processes"', () => {
@@ -25,12 +25,12 @@ import { filter } from 'rxjs/operators';
import { CommonModule, Location } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { IconComponent } from '@alfresco/adf-core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatIconModule } from '@angular/material/icon';
@Component({
selector: 'adf-process-instance-filters',
imports: [CommonModule, TranslatePipe, MatButtonModule, IconComponent],
imports: [CommonModule, TranslatePipe, MatButtonModule, MatIconModule],
templateUrl: './process-filters.component.html',
styleUrls: ['./process-filters.component.scss'],
encapsulation: ViewEncapsulation.None
@@ -5,9 +5,10 @@
[id]="filter.id"
[attr.data-automation-id]="filter.name + '_filter'"
mat-button
class="adf-filter-action-button adf-full-width">
class="adf-filter-action-button adf-full-width"
>
<ng-container *ngIf="showIcon">
<adf-icon data-automation-id="adf-filter-icon" [value]="getFilterIcon(filter.icon)" />
<mat-icon data-automation-id="adf-filter-icon" aria-hidden="true">{{ getFilterIcon(filter.icon) }}</mat-icon>
</ng-container>
<span data-automation-id="adf-filter-label" class="adf-filter-action-button__label">{{ filter.name | translate }}</span>
</button>
@@ -23,9 +23,11 @@ import { of, throwError } from 'rxjs';
import { TaskListService } from '../../services/tasklist.service';
import { TaskFilterService } from '../../services/task-filter.service';
import { TaskFiltersComponent } from './task-filters.component';
import { By } from '@angular/platform-browser';
import { NavigationStart, provideRouter, Router } from '@angular/router';
import { UserTaskFilterRepresentation } from '@alfresco/js-api';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatIconHarness } from '@angular/material/icon/testing';
const fakeTaskFilters = [
new UserTaskFilterRepresentation({
@@ -55,6 +57,7 @@ describe('TaskFiltersComponent', () => {
let component: TaskFiltersComponent;
let fixture: ComponentFixture<TaskFiltersComponent>;
let router: Router;
let loader: HarnessLoader;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -65,6 +68,7 @@ describe('TaskFiltersComponent', () => {
appConfig.config.bpmHost = 'http://localhost:9876/bpm';
fixture = TestBed.createComponent(TaskFiltersComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
component = fixture.componentInstance;
taskListService = TestBed.inject(TaskListService);
@@ -341,11 +345,11 @@ describe('TaskFiltersComponent', () => {
await fixture.whenStable();
expect(component.filters.length).toBe(3);
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(3);
expect(filters[0].nativeElement.innerText).toContain('format_align_left');
expect(filters[1].nativeElement.innerText).toContain('check_circle');
expect(filters[2].nativeElement.innerText).toContain('inbox');
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(3);
expect(await filterIcons[0].getName()).toContain('format_align_left');
expect(await filterIcons[1].getName()).toContain('check_circle');
expect(await filterIcons[2].getName()).toContain('inbox');
});
it('should not attach icons for each filter if showIcon is false', async () => {
@@ -356,8 +360,8 @@ describe('TaskFiltersComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0);
const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' }));
expect(filterIcons.length).toBe(0);
});
it('should reset selection when filterParam is a filter that does not exist', async () => {
@@ -26,13 +26,13 @@ import { UserTaskFilterRepresentation } from '@alfresco/js-api';
import { CommonModule } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { IconComponent } from '@alfresco/adf-core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { forkJoin, Observable } from 'rxjs';
import { MatIconModule } from '@angular/material/icon';
@Component({
selector: 'adf-task-filters',
imports: [CommonModule, TranslatePipe, MatButtonModule, IconComponent],
imports: [CommonModule, TranslatePipe, MatButtonModule, MatIconModule],
templateUrl: './task-filters.component.html',
styleUrls: ['./task-filters.component.scss'],
encapsulation: ViewEncapsulation.None