diff --git a/src/app/components/button/button.ts b/src/app/components/button/button.ts
index 7b073d12eb0..225e8c6dc78 100755
--- a/src/app/components/button/button.ts
+++ b/src/app/components/button/button.ts
@@ -63,7 +63,7 @@ export class ButtonDirective implements AfterViewInit, OnDestroy {
* Text of the button.
* @group Props
*/
- @Input() get label(): string {
+ @Input() get label(): string | undefined {
return this._label as string;
}
set label(val: string) {
diff --git a/src/app/components/fileupload/fileupload.ts b/src/app/components/fileupload/fileupload.ts
index 2582bce2b90..6eb20ef3e5b 100755
--- a/src/app/components/fileupload/fileupload.ts
+++ b/src/app/components/fileupload/fileupload.ts
@@ -140,7 +140,19 @@ import { FileBeforeUploadEvent, FileProgressEvent, FileRemoveEvent, FileSelectEv
diff --git a/src/app/components/inputmask/inputmask.spec.ts b/src/app/components/inputmask/inputmask.spec.ts
index ed2074130a4..bf853a6d728 100755
--- a/src/app/components/inputmask/inputmask.spec.ts
+++ b/src/app/components/inputmask/inputmask.spec.ts
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
@@ -20,7 +20,8 @@ describe('InputMask', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule, FormsModule],
- declarations: [InputMask, TestInputMaskComponent]
+ declarations: [InputMask, TestInputMaskComponent],
+ schemas: [NO_ERRORS_SCHEMA]
});
fixture = TestBed.createComponent(TestInputMaskComponent);
diff --git a/src/app/components/inputmask/inputmask.ts b/src/app/components/inputmask/inputmask.ts
index a6e8b27adc7..404e5520f11 100755
--- a/src/app/components/inputmask/inputmask.ts
+++ b/src/app/components/inputmask/inputmask.ts
@@ -94,7 +94,7 @@ export const INPUTMASK_VALUE_ACCESSOR: any = {
(keydown)="onInputKeydown($event)"
(keypress)="onKeyPress($event)"
pAutoFocus
- [attr.variant]="variant"
+ [variant]="variant"
[autofocus]="autofocus"
(input)="onInputChange($event)"
(paste)="handleInputChange($event)"
diff --git a/src/app/components/inputnumber/inputnumber.ts b/src/app/components/inputnumber/inputnumber.ts
index c5e68828dac..84c489f8c1a 100644
--- a/src/app/components/inputnumber/inputnumber.ts
+++ b/src/app/components/inputnumber/inputnumber.ts
@@ -1181,6 +1181,7 @@ export class InputNumber implements OnInit, AfterContentInit, OnChanges, Control
initCursor() {
let selectionStart = this.input?.nativeElement.selectionStart;
+ let selectionEnd = this.input?.nativeElement.selectionEnd;
let inputValue = this.input?.nativeElement.value;
let valueLength = inputValue.length;
let index = null;
@@ -1188,7 +1189,12 @@ export class InputNumber implements OnInit, AfterContentInit, OnChanges, Control
// remove prefix
let prefixLength = (this.prefixChar || '').length;
inputValue = inputValue.replace(this._prefix, '');
- selectionStart = selectionStart - prefixLength;
+
+ // Will allow selecting whole prefix. But not a part of it.
+ // Negative values will trigger clauses after this to fix the cursor position.
+ if (selectionStart === selectionEnd || selectionStart !== 0 || selectionEnd < prefixLength) {
+ selectionStart -= prefixLength;
+ }
let char = inputValue.charAt(selectionStart);
if (this.isNumeralChar(char)) {
diff --git a/src/app/components/inputswitch/inputswitch.spec.ts b/src/app/components/inputswitch/inputswitch.spec.ts
index d235f98b7e4..e6c599dcd52 100755
--- a/src/app/components/inputswitch/inputswitch.spec.ts
+++ b/src/app/components/inputswitch/inputswitch.spec.ts
@@ -1,4 +1,4 @@
-import { TestBed, ComponentFixture } from '@angular/core/testing';
+import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { InputSwitch } from './inputswitch';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@@ -29,6 +29,7 @@ describe('InputSwitch', () => {
fixture.detectChanges();
const onClickSpy = spyOn(inputswitch, 'onClick').and.callThrough();
+ const onModelChangeSpy = spyOn(inputswitch, 'onModelChange').and.callThrough();
const inputSwitchEl = fixture.debugElement.query(By.css('div')).nativeElement;
const inputEl = fixture.debugElement.query(By.css('input')).nativeElement;
inputSwitchEl.click();
@@ -36,7 +37,8 @@ describe('InputSwitch', () => {
expect(inputSwitchEl.className).toContain('p-disabled');
expect(inputEl.disabled).toEqual(true);
- expect(onClickSpy).not.toHaveBeenCalled();
+ expect(onClickSpy).toHaveBeenCalled();
+ expect(onModelChangeSpy).not.toHaveBeenCalled();
});
it('should change style and styleClass', () => {
@@ -120,6 +122,7 @@ describe('InputSwitch', () => {
fixture.detectChanges();
const onClickSpy = spyOn(inputswitch, 'onClick').and.callThrough();
+ const onModelChangeSpy = spyOn(inputswitch, 'onModelChange').and.callThrough();
const inputSwitchEl = fixture.debugElement.query(By.css('div')).nativeElement;
const inputEl = fixture.debugElement.query(By.css('input')).nativeElement;
inputSwitchEl.click();
@@ -127,20 +130,27 @@ describe('InputSwitch', () => {
expect(inputSwitchEl.className).toContain('p-disabled');
expect(inputEl.disabled).toEqual(true);
- expect(onClickSpy).not.toHaveBeenCalled();
+ expect(onClickSpy).toHaveBeenCalled();
+ expect(onModelChangeSpy).not.toHaveBeenCalled();
});
it('should toggle the modelValue and call necessary functions when not disabled and not readonly', () => {
- spyOn(inputswitch, 'onClick');
+ const onClickSpy = spyOn(inputswitch, 'onClick').and.callThrough();
+ const onChangeSpy = spyOn(inputswitch.onChange, 'emit').and.callThrough();
+ const onModelChangeSpy = spyOn(inputswitch, 'onModelChange').and.callThrough();
+
const divElement: HTMLElement = fixture.debugElement.query(By.css('div')).nativeElement;
divElement.click();
- expect(inputswitch.onClick).toHaveBeenCalledWith(jasmine.anything());
+ fixture.detectChanges();
+
+ expect(onClickSpy).toHaveBeenCalledWith(jasmine.anything());
const initialModelValue = inputswitch.modelValue;
inputswitch.onClick(new Event('click'));
+
expect(inputswitch.modelValue).toEqual(initialModelValue ? inputswitch.falseValue : inputswitch.trueValue);
- expect(inputswitch.onModelChange).toHaveBeenCalledWith(inputswitch.modelValue);
- expect(inputswitch.onChange.emit).toHaveBeenCalledWith({
+ expect(onModelChangeSpy).toHaveBeenCalled();
+ expect(onChangeSpy).toHaveBeenCalledWith({
originalEvent: jasmine.anything(),
checked: inputswitch.modelValue
});
diff --git a/src/app/components/orderlist/orderlist.spec.ts b/src/app/components/orderlist/orderlist.spec.ts
index 1be6025b1a4..26c2b0ef614 100755
--- a/src/app/components/orderlist/orderlist.spec.ts
+++ b/src/app/components/orderlist/orderlist.spec.ts
@@ -493,30 +493,24 @@ describe('OrderList', () => {
});
it('should select item with keyboard navigation', () => {
- const findNextItemSpy = spyOn(orderlist, 'findNextItem').and.callThrough();
- const findPrevItemSpy = spyOn(orderlist, 'findPrevItem').and.callThrough();
+ const onArrowDownKeySpy = spyOn(orderlist, 'onArrowDownKey').and.callThrough();
+ const onArrowUpKeySpy = spyOn(orderlist, 'onArrowUpKey').and.callThrough();
fixture.detectChanges();
- const itemListEl = fixture.debugElement.query(By.css('ul'));
- const bmwEl = itemListEl.queryAll(By.css('.p-orderlist-item'))[3].nativeElement;
- const event: any = document.createEvent('CustomEvent');
- event.which = 40;
- event.initEvent('keydown');
- bmwEl.dispatchEvent(event);
- fixture.detectChanges();
+ const itemListEl = fixture.debugElement.query(By.css('.p-orderlist-list')).nativeElement;
- event.which = 38;
- bmwEl.dispatchEvent(event);
+ itemListEl.dispatchEvent(new KeyboardEvent('keydown', { code: 'ArrowDown' }));
fixture.detectChanges();
-
- event.which = 13;
- bmwEl.dispatchEvent(event);
+ itemListEl.dispatchEvent(new KeyboardEvent('keydown', { code: 'ArrowDown' }));
+ fixture.detectChanges();
+ itemListEl.dispatchEvent(new KeyboardEvent('keydown', { code: 'ArrowUp' }));
+ fixture.detectChanges();
+ itemListEl.dispatchEvent(new KeyboardEvent('keydown', { code: 'Enter' }));
fixture.detectChanges();
expect(orderlist.selection.length).toEqual(1);
- expect(orderlist.selection[0].brand).toEqual('BMW');
- expect(findNextItemSpy).toHaveBeenCalled();
- expect(findPrevItemSpy).toHaveBeenCalled();
- expect(bmwEl.className).toContain('p-highlight');
+ expect(orderlist.selection[0].brand).toEqual('VW');
+ expect(onArrowDownKeySpy).toHaveBeenCalled();
+ expect(onArrowUpKeySpy).toHaveBeenCalled();
});
});
diff --git a/src/app/components/package.json b/src/app/components/package.json
index 699dcce1e42..a1d232c6b4a 100644
--- a/src/app/components/package.json
+++ b/src/app/components/package.json
@@ -1,6 +1,6 @@
{
"name": "primeng",
- "version": "17.16.1",
+ "version": "17.17.0",
"repository": {
"type": "git",
"url": "https://github.com/primefaces/primeng"
diff --git a/src/app/components/panelmenu/panelmenu.spec.ts b/src/app/components/panelmenu/panelmenu.spec.ts
index 3ac8a4687d5..c0c8a52b780 100755
--- a/src/app/components/panelmenu/panelmenu.spec.ts
+++ b/src/app/components/panelmenu/panelmenu.spec.ts
@@ -118,7 +118,7 @@ describe('PanelMenu', () => {
];
fixture.detectChanges();
- const panelHeaderLinkEl = fixture.debugElement.query(By.css('.p-panelmenu-header-link'));
+ const panelHeaderLinkEl = fixture.debugElement.query(By.css('.p-panelmenu-header-action'));
expect(panelHeaderLinkEl.nativeElement.href).toContain('primeng');
expect(panelHeaderLinkEl.nativeElement.title).toEqual('primeng');
expect(panelHeaderLinkEl.nativeElement.target).toEqual('primeng');
@@ -175,7 +175,7 @@ describe('PanelMenu', () => {
}
});
- it('should change child url target icon disabled and title', () => {
+ it('should change child url target icon disabled and label', () => {
panelmenu.model = [
{
label: 'File',
@@ -212,9 +212,10 @@ describe('PanelMenu', () => {
const panelHeaderLinkEl = fixture.debugElement.query(By.css('.p-menuitem-link'));
expect(panelHeaderLinkEl.nativeElement.href).toContain('primeng');
- expect(panelHeaderLinkEl.nativeElement.title).toEqual('primeng');
expect(panelHeaderLinkEl.nativeElement.target).toEqual('primeng');
+ expect(panelHeaderLinkEl.nativeElement.disabled).toBeTruthy;
expect(panelHeaderLinkEl.query(By.css('.p-menuitem-icon')).nativeElement.className).toContain('Primeng ROCKS!');
+ expect(panelHeaderLinkEl.nativeElement.innerHTML).toContain('New');
});
it('should show items and call toggle', () => {
@@ -241,12 +242,11 @@ describe('PanelMenu', () => {
headerLinks[0].nativeElement.click();
fixture.detectChanges();
- const iconEl = fixture.debugElement.query(By.css('.p-panelmenu-icon'));
- const firstSubMenuComponent = fixture.debugElement.query(By.css('.p-panelmenu-root-submenu')).componentInstance as PanelMenuSub;
+ const icon = fixture.debugElement.query(By.css('.p-icon'));
expect(handleClickSpy).toHaveBeenCalled();
expect(panelmenu.animating).toEqual(true);
expect(panelmenu.model[0].expanded).toEqual(true);
- expect(iconEl.nativeElement.tagName.toLowerCase()).toContain('svg');
+ expect(icon.nativeElement.tagName.toLowerCase()).toContain('svg');
});
it('should select multiple', () => {
@@ -265,16 +265,14 @@ describe('PanelMenu', () => {
]
}
];
+ panelmenu.multiple = true;
fixture.detectChanges();
- const headerLinks = fixture.debugElement.queryAll(By.css('.p-panelmenu-header-link'));
+ const headerLinks = fixture.debugElement.queryAll(By.css('.p-panelmenu-header-content'));
headerLinks[0].nativeElement.click();
headerLinks[1].nativeElement.click();
fixture.detectChanges();
- const subMenuEls = fixture.debugElement.queryAll(By.css('.p-panelmenu-root-submenu'));
- const firstSubMenuComponent = subMenuEls[0].componentInstance as PanelMenuSub;
- const seconSubMenuComponent = subMenuEls[1].componentInstance as PanelMenuSub;
const activeEls = fixture.debugElement.queryAll(By.css('.p-highlight'));
expect(activeEls.length).toEqual(2);
let x = 0;
@@ -284,9 +282,6 @@ describe('PanelMenu', () => {
}
expect(panelmenu.model[0].expanded).toEqual(true);
expect(panelmenu.model[1].expanded).toEqual(true);
- expect(subMenuEls.length).toEqual(2);
- // expect(firstSubMenuComponent.expanded).toEqual(true);
- // expect(seconSubMenuComponent.expanded).toEqual(true);
});
it('should not select multiple', () => {
@@ -308,7 +303,7 @@ describe('PanelMenu', () => {
panelmenu.multiple = false;
fixture.detectChanges();
- const headerLinks = fixture.debugElement.queryAll(By.css('.p-panelmenu-header-link'));
+ const headerLinks = fixture.debugElement.queryAll(By.css('.p-panelmenu-header-content'));
headerLinks[0].nativeElement.click();
headerLinks[1].nativeElement.click();
fixture.detectChanges();
diff --git a/src/app/components/table/table.ts b/src/app/components/table/table.ts
index 89c318ba1e2..cb7b0610d56 100644
--- a/src/app/components/table/table.ts
+++ b/src/app/components/table/table.ts
@@ -4711,7 +4711,7 @@ export class TableCheckbox {
constructor(public dt: Table, public tableService: TableService, public cd: ChangeDetectorRef) {
this.subscription = this.dt.tableService.selectionSource$.subscribe(() => {
- this.checked = this.dt.isSelected(this.value);
+ this.checked = this.dt.isSelected(this.value) && !this.disabled;
this.ariaLabel = this.ariaLabel || this.dt.config.translation.aria ? (this.checked ? this.dt.config.translation.aria.selectRow : this.dt.config.translation.aria.unselectRow) : undefined;
this.cd.markForCheck();
});
@@ -5244,7 +5244,7 @@ export class ColumnFilter implements AfterContentInit {
* Enables currency input.
* @group Props
*/
- @Input({ transform: booleanAttribute }) currency: boolean | undefined;
+ @Input() currency: string | undefined;
/**
* Defines the display of the currency input.
* @group Props
diff --git a/src/app/components/tooltip/tooltip.ts b/src/app/components/tooltip/tooltip.ts
index 4d59ee6c1eb..e95e2a7a3e0 100755
--- a/src/app/components/tooltip/tooltip.ts
+++ b/src/app/components/tooltip/tooltip.ts
@@ -550,13 +550,19 @@ export class Tooltip implements AfterViewInit, OnDestroy {
alignRight() {
this.preAlign('right');
- let hostOffset = this.getHostOffset();
- let left = hostOffset.left + DomHandler.getOuterWidth(this.el.nativeElement);
- let top = hostOffset.top + (DomHandler.getOuterHeight(this.el.nativeElement) - DomHandler.getOuterHeight(this.container)) / 2;
+ const el = this.activeElement;
+
+ const hostOffset = this.getHostOffset();
+ const left = hostOffset.left + DomHandler.getOuterWidth(el);
+ const top = hostOffset.top + (DomHandler.getOuterHeight(el) - DomHandler.getOuterHeight(this.container)) / 2;
this.container.style.left = left + this.getOption('positionLeft') + 'px';
this.container.style.top = top + this.getOption('positionTop') + 'px';
}
+ private get activeElement(): HTMLElement {
+ return this.el.nativeElement.nodeName.includes('P-') ? DomHandler.findSingle(this.el.nativeElement, '.p-component') : this.el.nativeElement;
+ }
+
alignLeft() {
this.preAlign('left');
let hostOffset = this.getHostOffset();
diff --git a/src/app/showcase/data/versions.json b/src/app/showcase/data/versions.json
index 218f23c53d3..eb887c2aeb6 100644
--- a/src/app/showcase/data/versions.json
+++ b/src/app/showcase/data/versions.json
@@ -1,11 +1,11 @@
[
{
- "version": "v17.16.1",
+ "version": "v17.17.0",
"name": "v17",
"url": "https://primeng.org"
},
{
- "version": "v16.9.9-lts",
+ "version": "v16.9.10-lts",
"name": "v16",
"url": "https://www.primefaces.org/primeng-v16-lts/"
},
diff --git a/src/app/showcase/doc/apidoc/index.json b/src/app/showcase/doc/apidoc/index.json
index 0779ac4e76d..284ac13ac3d 100644
--- a/src/app/showcase/doc/apidoc/index.json
+++ b/src/app/showcase/doc/apidoc/index.json
@@ -5087,13 +5087,23 @@
{
"parent": "button",
"name": "icon",
- "parameters": [],
+ "parameters": [
+ {
+ "name": "context",
+ "type": "{\n \t class: NgClass, // Icon class.\n }"
+ }
+ ],
"description": "Custom template of icon."
},
{
"parent": "button",
"name": "loadingicon",
- "parameters": [],
+ "parameters": [
+ {
+ "name": "context",
+ "type": "{\n \t class: NgClass, // Icon class.\n }"
+ }
+ ],
"description": "Custom template of loadingicon."
}
]
@@ -6961,7 +6971,7 @@
"name": "type",
"optional": false,
"readonly": false,
- "type": "string",
+ "type": "\"line\" | \"bar\" | \"scatter\" | \"bubble\" | \"pie\" | \"doughnut\" | \"polarArea\" | \"radar\"",
"description": "Type of the chart."
},
{
@@ -12084,7 +12094,7 @@
"type": "{\n \t $implicit: any, // File list.\n \t uploadedFiles: any, // Uploaded files list.\n \t chooseCallback: VoidFunction, // Callback to invoke on choose button click.\n \t clearCallback: VoidFunction, // Callback to invoke on clear button click.\n \t uploadCallback: VoidFunction, // Callback to invoke on upload.\n }"
}
],
- "description": "Custom template of header."
+ "description": "Custom template of file."
},
{
"parent": "fileupload",
@@ -21342,7 +21352,7 @@
"name": "size",
"optional": false,
"readonly": false,
- "type": "\"square\" | \"circle\"",
+ "type": "string",
"description": "Size of the skeleton."
},
{
@@ -27386,6 +27396,14 @@
"type": "string",
"description": "Transition options of the hide animation.",
"deprecated": "since v14.2.0 use overlayOptions property instead."
+ },
+ {
+ "name": "loading",
+ "optional": false,
+ "readonly": false,
+ "type": "boolean",
+ "default": "false",
+ "description": "Displays a loader to indicate data load is in progress."
}
]
},
@@ -27452,6 +27470,26 @@
],
"description": "Callback to invoke when data is filtered."
},
+ {
+ "name": "onFocus",
+ "parameters": [
+ {
+ "name": "event",
+ "type": "Event"
+ }
+ ],
+ "description": "Callback to invoke when treeselect gets focus."
+ },
+ {
+ "name": "onBlur",
+ "parameters": [
+ {
+ "name": "event",
+ "type": "Event"
+ }
+ ],
+ "description": "Callback to invoke when treeselect loses focus."
+ },
{
"name": "onNodeUnselect",
"parameters": [
diff --git a/src/app/showcase/doc/avatar/imagedoc.ts b/src/app/showcase/doc/avatar/imagedoc.ts
index cdf526c26de..fe9b42a023f 100644
--- a/src/app/showcase/doc/avatar/imagedoc.ts
+++ b/src/app/showcase/doc/avatar/imagedoc.ts
@@ -16,7 +16,7 @@ import { Code } from '@domain/code';
Gravatar
diff --git a/src/app/showcase/doc/fileupload/templatedoc.ts b/src/app/showcase/doc/fileupload/templatedoc.ts
index 1f12a64e645..e2b5bd02458 100644
--- a/src/app/showcase/doc/fileupload/templatedoc.ts
+++ b/src/app/showcase/doc/fileupload/templatedoc.ts
@@ -73,9 +73,9 @@ import { MessageService, PrimeNGConfig } from 'primeng/api';
export class TemplateDoc {
files = [];
- totalSize : number = 0;
+ totalSize: number = 0;
- totalSizePercent : number = 0;
+ totalSizePercent: number = 0;
constructor(private config: PrimeNGConfig, private messageService: MessageService) {}
diff --git a/src/app/showcase/doc/stepper/templatedoc.ts b/src/app/showcase/doc/stepper/templatedoc.ts
index 62e7dcf717a..34c3f5f307a 100644
--- a/src/app/showcase/doc/stepper/templatedoc.ts
+++ b/src/app/showcase/doc/stepper/templatedoc.ts
@@ -11,7 +11,7 @@ import { Code } from '@domain/code';
-