From edc98b4377d4f35fdc25db1f59dc59a1bf816994 Mon Sep 17 00:00:00 2001 From: Leonidas Deliolan Date: Thu, 21 May 2026 22:23:57 +0300 Subject: [PATCH 1/3] fix(splitbutton): add icon and item templates for custom SVG icons Forward #item to the overlay TieredMenu and support #icon on the default action button when icon is unset, matching other PrimeNG components. Update the icons showcase with inline SVG examples. Co-authored-by: Cursor --- apps/showcase/doc/splitbutton/icons-doc.ts | 71 ++++++++++++++++++- .../src/splitbutton/splitbutton.spec.ts | 57 +++++++++++++++ .../primeng/src/splitbutton/splitbutton.ts | 57 ++++++++++++++- .../types/splitbutton/splitbutton.types.ts | 13 +++- 4 files changed, 193 insertions(+), 5 deletions(-) diff --git a/apps/showcase/doc/splitbutton/icons-doc.ts b/apps/showcase/doc/splitbutton/icons-doc.ts index 2bf51335bc3..81754a56627 100644 --- a/apps/showcase/doc/splitbutton/icons-doc.ts +++ b/apps/showcase/doc/splitbutton/icons-doc.ts @@ -11,18 +11,61 @@ import { ToastModule } from 'primeng/toast'; imports: [AppCode, AppDocSectionText, SplitButtonModule, ToastModule], template: ` -

The buttons and menuitems have support to display icons.

+

The buttons and menuitems have support to display icons with CSS classes such as PrimeIcons.

+ + +

+ Custom icons such as inline SVGs are supported through the icon, dropdownicon, and item templates. When using a template, omit the corresponding + icon or dropdownIcon property. +

+
+
+ + + + + + + + + + + + {{ item.label }} + + + +
+ `, providers: [MessageService] }) export class IconsDoc { items: MenuItem[]; + svgItems: MenuItem[]; + itemIcons: Record = { + Update: 'M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12H1L4.96,16.03L9,12H6A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L17.65,6.35M12,20A8,8 0 0,0 20,12H23L19.04,7.97L15,12H18A6,6 0 0,1 12,18C10.34,18 8.86,17.31 7.78,16.22L6.35,17.65C7.8,19.1 9.79,20 12,20Z', + Delete: 'M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z', + Quit: 'M16,17V14H9V10H16V7L21,12L16,17M14,2A2,2 0 0,1 16,4V6H14V4H5V20H14V18H16V20A2,2 0 0,1 14,22H5A2,2 0 0,1 3,20V4A2,2 0 0,1 5,2H14Z' + }; constructor(private messageService: MessageService) { this.items = [ @@ -51,5 +94,31 @@ export class IconsDoc { } } ]; + + this.svgItems = [ + { + label: 'Update', + command: () => { + this.messageService.add({ severity: 'success', summary: 'Updated', detail: 'Data Updated', life: 3000 }); + } + }, + { + label: 'Delete', + command: () => { + this.messageService.add({ severity: 'warn', summary: 'Delete', detail: 'Data Deleted', life: 3000 }); + } + }, + { separator: true }, + { + label: 'Quit', + command: () => { + window.open('https://angular.io/', '_blank'); + } + } + ]; + } + + save() { + this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Data Saved', life: 3000 }); } } diff --git a/packages/primeng/src/splitbutton/splitbutton.spec.ts b/packages/primeng/src/splitbutton/splitbutton.spec.ts index 1d16a21c40c..a62444190f2 100644 --- a/packages/primeng/src/splitbutton/splitbutton.spec.ts +++ b/packages/primeng/src/splitbutton/splitbutton.spec.ts @@ -167,6 +167,40 @@ class TestContentTemplateSplitButtonComponent { model: MenuItem[] = [{ label: 'Template Action 1' }, { label: 'Template Action 2' }]; } +@Component({ + standalone: false, + template: ` + + + + + + + + + ` +}) +class TestIconTemplateSplitButtonComponent { + model: MenuItem[] = [{ label: 'Action 1' }]; +} + +@Component({ + standalone: false, + template: ` + + + + + {{ item.label }} + + + + ` +}) +class TestItemTemplateSplitButtonComponent { + model: MenuItem[] = [{ label: 'Update' }, { label: 'Delete' }]; +} + // Severity SplitButton Test @Component({ standalone: false, @@ -1057,6 +1091,29 @@ describe('SplitButton', () => { expect(() => splitButtonInstance.ngAfterContentInit()).not.toThrow(); expect(splitButtonInstance.templates).toBeDefined(); }); + + it('should render custom icon template on default button', async () => { + const iconTemplateFixture = TestBed.createComponent(TestIconTemplateSplitButtonComponent); + iconTemplateFixture.detectChanges(); + await iconTemplateFixture.whenStable(); + + const splitButtonInstance = iconTemplateFixture.debugElement.query(By.directive(SplitButton)).componentInstance; + expect(splitButtonInstance.iconTemplate).toBeDefined(); + expect(splitButtonInstance.hasIconTemplate).toBe(true); + + const customActionIcon = iconTemplateFixture.debugElement.query(By.css('[data-testid="custom-action-icon"]')); + expect(customActionIcon).toBeTruthy(); + }); + + it('should forward item template to tiered menu', async () => { + const itemTemplateFixture = TestBed.createComponent(TestItemTemplateSplitButtonComponent); + itemTemplateFixture.detectChanges(); + await itemTemplateFixture.whenStable(); + + const splitButtonInstance = itemTemplateFixture.debugElement.query(By.directive(SplitButton)).componentInstance; + expect(splitButtonInstance.itemTemplate).toBeDefined(); + expect(splitButtonInstance.menu?.itemTemplate).toBe(splitButtonInstance.itemTemplate); + }); }); }); diff --git a/packages/primeng/src/splitbutton/splitbutton.ts b/packages/primeng/src/splitbutton/splitbutton.ts index f73164feb98..8bccb5a6718 100755 --- a/packages/primeng/src/splitbutton/splitbutton.ts +++ b/packages/primeng/src/splitbutton/splitbutton.ts @@ -32,7 +32,9 @@ import { ChevronDownIcon } from 'primeng/icons'; import { Ripple } from 'primeng/ripple'; import { TieredMenu } from 'primeng/tieredmenu'; import { TooltipModule } from 'primeng/tooltip'; +import type { ButtonIconTemplateContext } from 'primeng/types/button'; import { ButtonProps, MenuButtonProps, SplitButtonPassThrough } from 'primeng/types/splitbutton'; +import type { TieredMenuItemTemplateContext } from 'primeng/types/tieredmenu'; import { SplitButtonStyle } from './style/splitbuttonstyle'; const SPLITBUTTON_INSTANCE = new InjectionToken('SPLITBUTTON_INSTANCE'); @@ -84,7 +86,7 @@ type SplitButtonIconPosition = 'left' | 'right'; [text]="text" [outlined]="outlined" [size]="size" - [icon]="icon" + [icon]="icon && !hasIconTemplate ? icon : undefined" [iconPos]="iconPos" [label]="label" (click)="onDefaultButtonClick($event)" @@ -97,7 +99,13 @@ type SplitButtonIconPosition = 'left' | 'right'; [tooltipOptions]="tooltipOptions" [pt]="ptm('pcButton')" [unstyled]="unstyled()" - > + > + + + +