diff --git a/docs/core/components/icon.component.md b/docs/core/components/icon.component.md index 3338e21a47..f3a29d60d5 100644 --- a/docs/core/components/icon.component.md +++ b/docs/core/components/icon.component.md @@ -2,7 +2,7 @@ Title: Icon Component Added: v3.0.0 Status: Active -Last reviewed: 2019-02-08 +Last reviewed: 2025-12-11 --- # [Icon Component](../../../lib/core/src/lib/icon/icon.component.ts "Defined in icon.component.ts") @@ -29,7 +29,9 @@ Provides a universal way of rendering registered and named icons. | Name | Type | Default value | Description | | ---- | ---- | ------------- | ----------- | | color | [`ThemePalette`](https://github.com/angular/components/blob/master/src/material/core/common-behaviors/color.ts) | | Theme color palette for the component. | +| fontSet | `string` | | Icon font set. | | value | `string` | | Icon value, which can be either a ligature name or a custom icon in the format `[namespace]:[name]`. | +| isSvg | `boolean` | false | Is icon of type svg. | ## Details @@ -78,6 +80,36 @@ using the `adf:` namespace. ``` +### Icon alias mapping + +The Icon Alias Mapping feature allows you to provide custom icon value mappings at runtime using the `ICON_ALIAS_MAP_TOKEN` injection token. When an icon value matches a key in the alias map, the component automatically replaces it with the mapped value. This is useful for creating consistent icon conventions across your application without modifying component code. + +**Example alias map:** + +```ts +import { ICON_ALIAS_MAP_TOKEN } from '@alfresco/adf-core'; + +function getProviders() { + return [ + { + provide: ICON_ALIAS_MAP_TOKEN, + useValue: { + 'icon-mock': 'alias-mock', + 'old-icon': 'new-icon' + } + } + ] +} +``` + +**Usage in your template:** + +```html + +``` + +The component will replace `icon-mock` with `alias-mock`. If the icon value doesn't match any key in the alias map, the original value is used. + ## See also - [Thumbnail service](../services/thumbnail.service.md) diff --git a/lib/core/src/lib/icon/icon.module.ts b/lib/core/src/lib/icon/icon-alias-map.token.ts similarity index 67% rename from lib/core/src/lib/icon/icon.module.ts rename to lib/core/src/lib/icon/icon-alias-map.token.ts index e6c530ecf2..14caf8403d 100644 --- a/lib/core/src/lib/icon/icon.module.ts +++ b/lib/core/src/lib/icon/icon-alias-map.token.ts @@ -15,15 +15,7 @@ * limitations under the License. */ -import { NgModule } from '@angular/core'; -import { IconComponent } from './icon.component'; +import { InjectionToken } from '@angular/core'; +import { IconAliasMap } from './icon-alias-map.type'; -/** - * @deprecated this Module is deprecated and should no longer be used. - * Consider importing components directly instead. - */ -@NgModule({ - imports: [IconComponent], - exports: [IconComponent] -}) -export class IconModule {} +export const ICON_ALIAS_MAP_TOKEN = new InjectionToken('icon_alias_map'); diff --git a/lib/core/src/lib/icon/icon-alias-map.type.ts b/lib/core/src/lib/icon/icon-alias-map.type.ts new file mode 100644 index 0000000000..31762c6bf6 --- /dev/null +++ b/lib/core/src/lib/icon/icon-alias-map.type.ts @@ -0,0 +1,18 @@ +/*! + * @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. + */ + +export type IconAliasMap = Record; diff --git a/lib/core/src/lib/icon/icon.component.html b/lib/core/src/lib/icon/icon.component.html index e5b0bb09a9..1b8be079fe 100644 --- a/lib/core/src/lib/icon/icon.component.html +++ b/lib/core/src/lib/icon/icon.component.html @@ -1,7 +1,5 @@ - +@if (isSvg) { - - +} @else { - +} diff --git a/lib/core/src/lib/icon/icon.component.spec.ts b/lib/core/src/lib/icon/icon.component.spec.ts new file mode 100644 index 0000000000..065a355c35 --- /dev/null +++ b/lib/core/src/lib/icon/icon.component.spec.ts @@ -0,0 +1,117 @@ +/*! + * @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 { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DEFAULT_ICON_VALUE, IconComponent } from './icon.component'; +import { ICON_ALIAS_MAP_TOKEN } from './icon-alias-map.token'; +import { IconType, MatIconHarness, MatIconTestingModule } from '@angular/material/icon/testing'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; + +describe('IconComponent', () => { + let component: IconComponent; + let fixture: ComponentFixture; + let loader: HarnessLoader; + + /** + * @param value value input for the component + */ + function setValueInput(value: string) { + fixture.componentRef.setInput('value', value); + + fixture.detectChanges(); + } + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [IconComponent, MatIconTestingModule], + providers: [ + { + provide: ICON_ALIAS_MAP_TOKEN, + useValue: { + mock_icon_v1: 'mock_alias_v1', + mock_icon_v2: 'mock_alias_v2' + } + } + ] + }); + + fixture = TestBed.createComponent(IconComponent); + component = fixture.componentInstance; + loader = TestbedHarnessEnvironment.loader(fixture); + + fixture.detectChanges(); + }); + + it('should set value to default if no value input is provided', async () => { + expect(component.value).toBe(DEFAULT_ICON_VALUE); + expect(component.isSvg).toBe(false); + + const iconHarness = await loader.getHarness(MatIconHarness); + + expect(await iconHarness.getType()).toBe(IconType.FONT); + expect(await iconHarness.getName()).toContain(DEFAULT_ICON_VALUE); + }); + + it('should set value to input value', async () => { + setValueInput('mock_icon'); + + expect(component.value).toBe('mock_icon'); + expect(component.isSvg).toBe(false); + + const iconHarness = await loader.getHarness(MatIconHarness); + + expect(await iconHarness.getType()).toBe(IconType.FONT); + expect(await iconHarness.getName()).toContain('mock_icon'); + }); + + it('should set value to mapped value and use svg if alias map is provided and input value is a property of the map', async () => { + setValueInput('mock_icon_v1'); + + expect(component.value).toBe('mock_alias_v1'); + expect(component.isSvg).toBe(true); + + const iconHarness = await loader.getHarness(MatIconHarness); + + expect(await iconHarness.getType()).toBe(IconType.SVG); + expect(await iconHarness.getName()).toContain('mock_alias_v1'); + }); + + it('should set value to input value if alias map is provided but input value is NOT a property of the map', async () => { + setValueInput('mock_icon_v3'); + + expect(component.value).toBe('mock_icon_v3'); + expect(component.isSvg).toBe(false); + + const iconHarness = await loader.getHarness(MatIconHarness); + + expect(await iconHarness.getType()).toBe(IconType.FONT); + expect(await iconHarness.getName()).toContain('mock_icon_v3'); + }); + + it('should use svg if value input is custom icon', async () => { + setValueInput('adf:mock_custom_icon'); + + expect(component.value).toBe('adf:mock_custom_icon'); + expect(component.isSvg).toBe(true); + + const iconHarness = await loader.getHarness(MatIconHarness); + + expect(await iconHarness.getType()).toBe(IconType.SVG); + expect(await iconHarness.getName()).toContain('mock_custom_icon'); + }); +}); diff --git a/lib/core/src/lib/icon/icon.component.ts b/lib/core/src/lib/icon/icon.component.ts index f4b2c4edcd..f1caaeb490 100644 --- a/lib/core/src/lib/icon/icon.component.ts +++ b/lib/core/src/lib/icon/icon.component.ts @@ -15,22 +15,26 @@ * limitations under the License. */ -import { Component, Input, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core'; +import { Component, Input, ViewEncapsulation, ChangeDetectionStrategy, inject } from '@angular/core'; import { ThemePalette } from '@angular/material/core'; import { MatIconModule } from '@angular/material/icon'; -import { NgIf } from '@angular/common'; +import { ICON_ALIAS_MAP_TOKEN } from './icon-alias-map.token'; + +export const DEFAULT_ICON_VALUE = 'settings'; @Component({ selector: 'adf-icon', - imports: [MatIconModule, NgIf], + imports: [MatIconModule], templateUrl: './icon.component.html', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'adf-icon' } }) export class IconComponent { - private _value = ''; - private _isCustom = false; + private readonly ALIAS_MAP = inject(ICON_ALIAS_MAP_TOKEN, { optional: true }); + + private _value = DEFAULT_ICON_VALUE; + private _isSvg = false; /** Theme color palette for the component. */ @Input() @@ -47,11 +51,25 @@ export class IconComponent { /** Icon value, which can be either a ligature name or a custom icon in the format `[namespace]:[name]`. */ @Input() set value(value: string) { - this._value = value || 'settings'; - this._isCustom = this._value.includes(':'); + this._value = this.hasMappedAlias(value) ? this.ALIAS_MAP[value] : value; + this._isSvg = this.hasMappedAlias(value) || this.isCustom(value); } - get isCustom(): boolean { - return this._isCustom; + get isSvg() { + return this._isSvg; + } + + /** Is icon of svg type */ + @Input() + set isSvg(isSvg: boolean) { + this._isSvg = isSvg; + } + + private hasMappedAlias(value: string): boolean { + return !!this.ALIAS_MAP?.[value]; + } + + private isCustom(value: string): boolean { + return value.includes(':'); } } diff --git a/lib/core/src/lib/icon/public-api.ts b/lib/core/src/lib/icon/public-api.ts index 82dc195c40..4585d00822 100644 --- a/lib/core/src/lib/icon/public-api.ts +++ b/lib/core/src/lib/icon/public-api.ts @@ -16,4 +16,5 @@ */ export * from './icon.component'; -export * from './icon.module'; +export * from './icon-alias-map.type'; +export * from './icon-alias-map.token';