diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.scss b/src/app/admin/admin-sidebar/admin-sidebar.component.scss
index a679155c323..3f7d3b66b67 100644
--- a/src/app/admin/admin-sidebar/admin-sidebar.component.scss
+++ b/src/app/admin/admin-sidebar/admin-sidebar.component.scss
@@ -56,6 +56,20 @@
padding-bottom: var(--ds-admin-sidebar-item-padding);
}
+ // Menu item layout to align the optional icon and text label
+ .ds-menu-item {
+ display: flex;
+ width: 100%;
+ align-items: center;
+ box-sizing: border-box;
+ gap: var(--ds-admin-sidebar-item-padding);
+
+ span {
+ flex: 1;
+ min-width: 0;
+ }
+ }
+
// These classes handle the collapsing behavior
.sidebar-section-wrapper {
diff --git a/src/app/shared/menu/menu-item/external-link-menu-item.component.html b/src/app/shared/menu/menu-item/external-link-menu-item.component.html
index c95c63fdf5a..a9b3c56ad35 100644
--- a/src/app/shared/menu/menu-item/external-link-menu-item.component.html
+++ b/src/app/shared/menu/menu-item/external-link-menu-item.component.html
@@ -5,4 +5,8 @@
[href]="href"
[title]="item.text | translate"
(click)="$event.stopPropagation()"
->{{item.text | translate}}
+>@if (item.icon) {
+
+ }
+ {{item.text | translate}}
+
diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.html b/src/app/shared/menu/menu-item/link-menu-item.component.html
index f96084e6e13..7f15fe7e527 100644
--- a/src/app/shared/menu/menu-item/link-menu-item.component.html
+++ b/src/app/shared/menu/menu-item/link-menu-item.component.html
@@ -9,4 +9,8 @@
(keydown.enter)="navigate($event)"
href="javascript:void(0);"
tabindex="0"
->{{item.text | translate}}
+>@if (item.icon) {
+
+ }
+ {{item.text | translate}}
+
diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.spec.ts b/src/app/shared/menu/menu-item/link-menu-item.component.spec.ts
index be98fc884e4..30ab6b68f0a 100644
--- a/src/app/shared/menu/menu-item/link-menu-item.component.spec.ts
+++ b/src/app/shared/menu/menu-item/link-menu-item.component.spec.ts
@@ -87,4 +87,29 @@ describe('LinkMenuItemComponent', () => {
expect(routerParamsQuery.length).toBe(1);
expect(routerParamsQuery[0].queryParams).toBe(queryParams);
});
+
+ describe('icon rendering', () => {
+ beforeEach(() => {
+ component.item.icon = undefined;
+ fixture.detectChanges();
+ });
+
+ it('should render the icon when item.icon is provided', () => {
+ component.item.icon = 'cog';
+ fixture.detectChanges();
+
+ const icon = debugElement.query(By.css('i.fa-cog'));
+
+ expect(icon).toBeTruthy();
+ expect(icon.nativeElement.getAttribute('aria-hidden')).toBe('true');
+ });
+
+ it('should not render the icon when item.icon is not provided', () => {
+ fixture.detectChanges();
+
+ const icon = debugElement.query(By.css('i.fas'));
+
+ expect(icon).toBeFalsy();
+ });
+ });
});
diff --git a/src/app/shared/menu/menu-item/models/external-link.model.ts b/src/app/shared/menu/menu-item/models/external-link.model.ts
index 6e9e1dae1b3..0b1e86fe664 100644
--- a/src/app/shared/menu/menu-item/models/external-link.model.ts
+++ b/src/app/shared/menu/menu-item/models/external-link.model.ts
@@ -9,4 +9,5 @@ export class ExternalLinkMenuItemModel implements MenuItemModel {
disabled?: boolean;
text: string;
href: string;
+ icon?: string;
}
diff --git a/src/app/shared/menu/menu-item/models/link.model.ts b/src/app/shared/menu/menu-item/models/link.model.ts
index ff98fb4c533..51ea4a0e721 100644
--- a/src/app/shared/menu/menu-item/models/link.model.ts
+++ b/src/app/shared/menu/menu-item/models/link.model.ts
@@ -12,4 +12,5 @@ export class LinkMenuItemModel implements MenuItemModel {
text: string;
link: string;
queryParams?: Params | null;
+ icon?: string;
}
diff --git a/src/app/shared/menu/menu-item/models/menu-item.model.ts b/src/app/shared/menu/menu-item/models/menu-item.model.ts
index 3bfc632df47..0851825b549 100644
--- a/src/app/shared/menu/menu-item/models/menu-item.model.ts
+++ b/src/app/shared/menu/menu-item/models/menu-item.model.ts
@@ -6,4 +6,5 @@ import { MenuItemType } from '../../menu-item-type.model';
export interface MenuItemModel {
type: MenuItemType;
disabled?: boolean;
+ icon?: string;
}
diff --git a/src/app/shared/menu/menu-item/models/onclick.model.ts b/src/app/shared/menu/menu-item/models/onclick.model.ts
index 48fa80235b9..163d2abbebe 100644
--- a/src/app/shared/menu/menu-item/models/onclick.model.ts
+++ b/src/app/shared/menu/menu-item/models/onclick.model.ts
@@ -8,5 +8,6 @@ export class OnClickMenuItemModel implements MenuItemModel {
type = MenuItemType.ONCLICK;
disabled?: boolean;
text: string;
+ icon?: string;
function: () => void;
}
diff --git a/src/app/shared/menu/menu-item/models/text.model.ts b/src/app/shared/menu/menu-item/models/text.model.ts
index b5da66c4e21..9dac7193f58 100644
--- a/src/app/shared/menu/menu-item/models/text.model.ts
+++ b/src/app/shared/menu/menu-item/models/text.model.ts
@@ -8,4 +8,5 @@ export class TextMenuItemModel implements MenuItemModel {
type = MenuItemType.TEXT;
disabled?: boolean;
text: string;
+ icon?: string;
}
diff --git a/src/app/shared/menu/menu-item/onclick-menu-item.component.html b/src/app/shared/menu/menu-item/onclick-menu-item.component.html
index cada06e1b37..9c464ecb8e2 100644
--- a/src/app/shared/menu/menu-item/onclick-menu-item.component.html
+++ b/src/app/shared/menu/menu-item/onclick-menu-item.component.html
@@ -8,8 +8,17 @@
(keyup.space)="activate($event)"
(keyup.enter)="activate($event)"
[attr.data-test]="item.text"
- >{{item.text | translate}}
+ >@if (item.icon) {
+
+ }
+ {{item.text | translate}}
+
}
@if (item.disabled) {
- {{item.text | translate}}
+
}
diff --git a/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts b/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts
index a73a0cfff29..0ac94335cae 100644
--- a/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts
+++ b/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts
@@ -38,6 +38,7 @@ describe('OnClickMenuItemComponent', () => {
spyOn(item, 'function');
fixture = TestBed.createComponent(OnClickMenuItemComponent);
component = fixture.componentInstance;
+ component.item = item;
debugElement = fixture.debugElement;
fixture.detectChanges();
});
@@ -55,4 +56,41 @@ describe('OnClickMenuItemComponent', () => {
debugElement.query(By.css('a.ds-menu-item')).triggerEventHandler('click', new Event(('click')));
expect(item.function).toHaveBeenCalled();
});
+
+ describe('icon rendering', () => {
+ beforeEach(() => {
+ item.icon = undefined;
+ item.disabled = false;
+ fixture.detectChanges();
+ });
+
+ it('should render the icon when item.icon is provided and enabled', () => {
+ item.icon = 'users';
+ fixture.detectChanges();
+
+ const icon = debugElement.query(By.css('a.ds-menu-item i.fa-users'));
+
+ expect(icon).toBeTruthy();
+ expect(icon.nativeElement.getAttribute('aria-hidden')).toBe('true');
+ });
+
+ it('should render the icon when item.icon is provided and disabled', () => {
+ item.icon = 'users';
+ item.disabled = true;
+ fixture.detectChanges();
+
+ const icon = debugElement.query(By.css('span.ds-menu-item i.fa-users'));
+
+ expect(icon).toBeTruthy();
+ expect(icon.nativeElement.getAttribute('aria-hidden')).toBe('true');
+ });
+
+ it('should not render the icon when item.icon is not provided', () => {
+ fixture.detectChanges();
+
+ const icon = debugElement.query(By.css('i.fas'));
+
+ expect(icon).toBeFalsy();
+ });
+ });
});
diff --git a/src/app/shared/menu/menu-item/text-menu-item.component.html b/src/app/shared/menu/menu-item/text-menu-item.component.html
index e2dd334caf5..2c220607526 100644
--- a/src/app/shared/menu/menu-item/text-menu-item.component.html
+++ b/src/app/shared/menu/menu-item/text-menu-item.component.html
@@ -1 +1,9 @@
-
+