Skip to content

Commit bdd8489

Browse files
authored
Merge branch 'master' into mpopov/fix-calendar-date-localization-styles
2 parents 2f8d7ae + fac1238 commit bdd8489

File tree

8 files changed

+230
-26
lines changed

8 files changed

+230
-26
lines changed

CHANGELOG.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,14 @@ All notable changes for each version of this project will be documented in this
44

55
## 21.0.0
66

7-
### Themes
8-
9-
- `IgxButton`
10-
- **Breaking Change**
11-
- The following shadow-related parameters were removed from the `outlined-button-theme` and `flat-button-theme`:
12-
- `resting-shadow`
13-
- `hover-shadow`
14-
- `focus-shadow`
15-
- `active-shadow`
16-
17-
## 21.0.0
18-
197
### New Features
208

9+
- **New component** `IgxChat`:
10+
- A component that provides complete solution for building conversational interfaces in your applications. Read up more information in the [ReadMe](https://github.com/IgniteUI/igniteui-angular/tree/master/projects/igniteui-angular/chat/README.md)
11+
2112
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
2213
- Added PDF export functionality to grid components. Grids can now be exported to PDF format alongside the existing Excel and CSV export options.
23-
14+
2415
The new `IgxPdfExporterService` follows the same pattern as Excel and CSV exporters:
2516

2617
```ts
@@ -60,9 +51,16 @@ All notable changes for each version of this project will be documented in this
6051
- **Landscape orientation** by default (suitable for wide grids)
6152
- **Internationalization** support for all 19 supported languages
6253
- Respects all grid export options (ignoreFiltering, ignoreSorting, ignoreColumnsVisibility, etc.)
63-
54+
6455
### Breaking Changes
6556

57+
- `IgxButton`
58+
- The following shadow-related parameters were removed from the `outlined-button-theme` and `flat-button-theme`:
59+
- `resting-shadow`
60+
- `hover-shadow`
61+
- `focus-shadow`
62+
- `active-shadow`
63+
6664
#### Dependency Injection Refactor
6765
- All internal DI now uses the `inject()` API across `igniteui-angular` (no more constructor DI in library code).
6866
- If you extend our components/services or call their constructors directly, remove DI params and switch to `inject()` (e.g., `protected foo = inject(FooService);`).

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,13 @@ Developer support is provided as part of the commercial, paid-for license via [I
335335
Community support for open source usage of this product is available at [StackOverflow](https://stackoverflow.com/questions/tagged/ignite-ui-angular).
336336

337337
## License
338-
This is a commercial product, requiring a valid paid-for license for commercial use.
339-
This product is free to use for non-commercial educational use for students in K through 12 grades or University programs, and for educators to use in a classroom setting as examples / tools in their curriculum.
340-
In order for us to verify your eligibility for free usage, please [register for trial](https://Infragistics.com/Angular) and open a support ticket with a request for free license.
338+
This software package is offered under a dual-license model, which allows for both commercial and permissive open-source use, depending on the components, modules, directives, and services being used.
341339

342-
To acquire a license for commercial usage, please [register for trial](https://Infragistics.com/Angular) and refer to the purchasing options in the pricing section on the product page.
340+
It is crucial to understand which license applies to which part of the package.
343341

344342
© Copyright 2025 INFRAGISTICS. All Rights Reserved.
345343
The Infragistics Ultimate license & copyright applies to this distribution.
346-
For information on that license, please go to our website [https://www.infragistics.com/legal/license](https://www.infragistics.com/legal/license).
344+
For information on that license, please go to [LICENSE](LICENSE).
347345

348346

349347

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
2+
# IgxChat
3+
4+
**IgxChat** is a component that provides a chat interface, wrapping the **IgcChat** web component.
5+
6+
A walkthrough of how to get started can be found [here](https://www.infragistics.com/products/ignite-ui-angular/angular/components/chat)
7+
8+
# Usage
9+
10+
```html
11+
<igx-chat
12+
[messages]="messages"
13+
[draftMessage]="draft"
14+
[options]="chatOptions"
15+
[templates]="chatTemplates"
16+
(messageCreated)="onMessageCreated($event)">
17+
</igx-chat>
18+
```
19+
20+
# API Summary
21+
The following tables summarize the **igx-chat** inputs, outputs and directives.
22+
23+
### Inputs
24+
The following inputs are available in the **igx-chat** component:
25+
26+
| Name | Type | Description |
27+
| :--- | :--- | :--- |
28+
| `messages` | `IgcChatMessage[]` | Array of chat messages to display |
29+
| `draftMessage` | `{ text: string; attachments?: IgcChatMessageAttachment[] } \| undefined` | Draft message with text and optional attachments |
30+
| `options` | `IgxChatOptions` | Configuration options for the chat component |
31+
| `templates` | `IgxChatTemplates` | Custom templates for rendering chat elements |
32+
33+
### Outputs
34+
The following outputs are available in the **igx-chat** component:
35+
36+
| Name | Description | Parameters |
37+
| :--- | :--- | :--- |
38+
| `messageCreated` | Emitted when a new message is created | `IgcChatMessage` |
39+
| `messageReact` | Emitted when a user reacts to a message | `IgcChatMessageReaction` |
40+
| `attachmentClick` | Emitted when an attachment is clicked | `IgcChatMessageAttachment` |
41+
| `attachmentDrag` | Emitted when attachment drag starts | `void` |
42+
| `attachmentDrop` | Emitted when attachment is dropped | `void` |
43+
| `typingChange` | Emitted when typing indicator state changes | `boolean` |
44+
| `inputFocus` | Emitted when the input receives focus | `void` |
45+
| `inputBlur` | Emitted when the input loses focus | `void` |
46+
| `inputChange` | Emitted when the input value changes | `string` |
47+
48+
### Directives
49+
The following directives are available for type checking in templates:
50+
51+
| Name | Selector | Description |
52+
| :--- | :--- | :--- |
53+
| `IgxChatMessageContextDirective` | `[igxChatMessageContext]` | Provides type information for chat message template contexts |
54+
| `IgxChatAttachmentContextDirective` | `[igxChatAttachmentContext]` | Provides type information for chat attachment template contexts |
55+
| `IgxChatInputContextDirective` | `[igxChatInputContext]` | Provides type information for chat input template contexts |
56+
57+
# Chat Extras
58+
59+
The **chat-extras** module provides additional utilities for enhancing chat functionality.
60+
61+
## MarkdownPipe
62+
63+
The `MarkdownPipe` transforms markdown text into HTML, allowing you to render formatted messages in the chat.
64+
65+
### Usage
66+
67+
```typescript
68+
import { MarkdownPipe } from 'igniteui-angular/chat-extras';
69+
70+
@Component({
71+
standalone: true,
72+
imports: [IgxChatComponent, MarkdownPipe, AsyncPipe],
73+
template: `
74+
<igx-chat [messages]="messages" [templates]="templates">
75+
<ng-template #renderer igxChatMessageContext let-message>
76+
<div [innerHTML]="message.text | fromMarkdown | async"></div>
77+
</ng-template>
78+
</igx-chat>
79+
`
80+
})
81+
```
82+
83+
### Supported Markdown Features
84+
85+
The pipe supports common markdown syntax including:
86+
- **Bold** text (`**text**`)
87+
- *Italic* text (`*text*`)
88+
- Headings (`# H1`, `## H2`, etc.)
89+
- Lists (ordered and unordered)
90+
- Links (`[text](url)`)
91+
- Code blocks and inline code
92+
- Blockquotes
93+
- And more...

projects/igniteui-angular/directives/src/directives/tooltip/tooltip.common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export class TooltipPositionStrategy extends AutoPositionStrategy {
185185
return;
186186
}
187187

188-
const arrow = tooltip.querySelector('[data-arrow="true"]') as HTMLElement;
188+
const arrow = Array.from(tooltip.children).find(el => el.matches('[data-arrow="true"]')) as HTMLElement;
189189

190190
// If display is none -> tooltipTarget's hasArrow is false
191191
if (!arrow || arrow.style.display === 'none') {

projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { DebugElement } from '@angular/core';
22
import { fakeAsync, TestBed, tick, flush, waitForAsync, ComponentFixture } from '@angular/core/testing';
33
import { By } from '@angular/platform-browser';
44
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
5-
import { IgxTooltipSingleTargetComponent, IgxTooltipMultipleTargetsComponent, IgxTooltipPlainStringComponent, IgxTooltipWithToggleActionComponent, IgxTooltipMultipleTooltipsComponent, IgxTooltipWithCloseButtonComponent, IgxTooltipWithNestedContentComponent } from '../../../../test-utils/tooltip-components.spec';
5+
import { IgxTooltipSingleTargetComponent, IgxTooltipMultipleTargetsComponent, IgxTooltipPlainStringComponent, IgxTooltipWithToggleActionComponent, IgxTooltipMultipleTooltipsComponent, IgxTooltipWithCloseButtonComponent, IgxTooltipWithNestedContentComponent, IgxTooltipNestedTooltipsComponent } from '../../../../test-utils/tooltip-components.spec';
66
import { UIInteractions } from '../../../../test-utils/ui-interactions.spec';
77
import { HorizontalAlignment, VerticalAlignment, AutoPositionStrategy } from '../../../../core/src/services/public_api';
88
import { IgxTooltipDirective } from './tooltip.directive';
@@ -29,7 +29,8 @@ describe('IgxTooltip', () => {
2929
IgxTooltipPlainStringComponent,
3030
IgxTooltipWithToggleActionComponent,
3131
IgxTooltipWithCloseButtonComponent,
32-
IgxTooltipWithNestedContentComponent
32+
IgxTooltipWithNestedContentComponent,
33+
IgxTooltipNestedTooltipsComponent
3334
]
3435
}).compileComponents();
3536
UIInteractions.clearOverlay();
@@ -533,6 +534,69 @@ describe('IgxTooltip', () => {
533534
}));
534535
});
535536

537+
describe('Nested tooltips', () => {
538+
let tooltipTarget1: IgxTooltipTargetDirective;
539+
let tooltipTarget2: IgxTooltipTargetDirective;
540+
let tooltipTarget3: IgxTooltipTargetDirective;
541+
542+
let tooltip1: IgxTooltipDirective;
543+
let tooltip2: IgxTooltipDirective;
544+
let tooltip3: IgxTooltipDirective;
545+
546+
// Handles getting the left offset when tooltip placement is Bottom
547+
const getArrowLeftOffset = (tooltip: IgxTooltipDirective) => {
548+
const tooltipRect = tooltip.element.getBoundingClientRect();
549+
const arrowRect = tooltip.arrow.getBoundingClientRect();
550+
551+
const offset = tooltipRect.width / 2 - arrowRect.width / 2;
552+
return Math.round(offset);
553+
};
554+
555+
const verifyTooltipArrowAlignment = (tooltip: IgxTooltipDirective) => {
556+
const arrowClassName = 'igx-tooltip--bottom';
557+
const arrowTopOffset = '-4px';
558+
const arrowLeftOffset = getArrowLeftOffset(tooltip) + 'px';
559+
560+
expect(tooltip.arrow.classList.contains(arrowClassName)).toBeTrue();
561+
expect(tooltip.arrow.style.top).toBe(arrowTopOffset);
562+
expect(tooltip.arrow.style.left).toBe(arrowLeftOffset);
563+
};
564+
565+
beforeEach(waitForAsync(() => {
566+
fix = TestBed.createComponent(IgxTooltipNestedTooltipsComponent);
567+
fix.detectChanges();
568+
569+
tooltipTarget1 = fix.componentInstance.targetLevel1;
570+
tooltipTarget2 = fix.componentInstance.targetLevel2;
571+
tooltipTarget3 = fix.componentInstance.targetLevel3;
572+
573+
tooltip1 = fix.componentInstance.tooltipLevel1;
574+
tooltip2 = fix.componentInstance.tooltipLevel2;
575+
tooltip3 = fix.componentInstance.tooltipLevel3;
576+
}));
577+
578+
it('should show arrow for each tooltip', fakeAsync(() => {
579+
hoverElement(tooltipTarget1);
580+
flush();
581+
verifyTooltipVisibility(tooltip1.element, tooltipTarget1, true);
582+
expect(tooltipTarget1.hasArrow).toBeTrue();
583+
verifyTooltipArrowAlignment(tooltip1);
584+
585+
hoverElement(tooltipTarget2);
586+
flush();
587+
verifyTooltipVisibility(tooltip2.element, tooltipTarget2, true);
588+
expect(tooltipTarget2.hasArrow).toBeTrue();
589+
verifyTooltipArrowAlignment(tooltip2);
590+
591+
hoverElement(tooltipTarget3);
592+
flush();
593+
verifyTooltipVisibility(tooltip3.element, tooltipTarget3, true);
594+
expect(tooltipTarget3.hasArrow).toBeTrue();
595+
verifyTooltipArrowAlignment(tooltip3);
596+
597+
}));
598+
});
599+
536600
describe('Multiple targets with single tooltip', () => {
537601
let targetOne: IgxTooltipTargetDirective;
538602
let targetTwo: IgxTooltipTargetDirective;

projects/igniteui-angular/grids/core/src/services/csv/char-separated-value-data.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,7 @@ export class CharSeparatedValueData {
5252
this._escapeCharacters.push(this._delimiter);
5353

5454
const headers = columns && columns.length ?
55-
/* When column groups are present, always use the field as it indicates the group the column belongs to.
56-
* Otherwise, in PivotGrid scenarios we can end up with many duplicated column names without a hint what they represent.
57-
*/
58-
columns.map(c => c.columnGroupParent ? c.field : c.header ?? c.field) :
55+
columns.map(c => c.header ?? c.field) :
5956
keys;
6057

6158
this._headerRecord = this.processHeaderRecord(headers, this._data.length);

projects/igniteui-angular/grids/core/src/services/csv/csv-exporter-grid.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { IgxPivotGridComponent } from 'igniteui-angular/grids/pivot-grid';
2121
import { IgxGridNavigationService, IgxPivotNumericAggregate } from 'igniteui-angular/grids/core';
2222
import { DefaultSortingStrategy, FilteringExpressionsTree, FilteringLogic, IgxNumberFilteringOperand, IgxStringFilteringOperand, SortingDirection } from 'igniteui-angular/core';
2323
import { CSVWrapper } from './csv-verification-wrapper.spec';
24+
import { OneGroupThreeColsGridComponent } from '../../../../../test-utils/grid-mch-sample.spec';
2425

2526
describe('CSV Grid Exporter', () => {
2627
let exporter: IgxCsvExporterService;
@@ -388,6 +389,20 @@ describe('CSV Grid Exporter', () => {
388389
expect(ExportUtilities.saveBlobToFile).toHaveBeenCalledTimes(1);
389390
});
390391

392+
it('should print column headers when available when column groups are present.', async () => {
393+
const fix = TestBed.createComponent(OneGroupThreeColsGridComponent);
394+
fix.componentInstance.data = [];
395+
fix.detectChanges();
396+
397+
fix.componentInstance.grid.getColumnByName('City').header = 'Test Header';
398+
fix.detectChanges();
399+
400+
const grid = fix.componentInstance.grid;
401+
402+
const wrapper = await getExportedData(grid, options);
403+
wrapper.verifyData('Country,Region,Test Header', 'Only headers should be exported.');
404+
});
405+
391406
describe('Tree Grid CSV export', () => {
392407
let fix;
393408
let treeGrid: IgxTreeGridComponent;

projects/igniteui-angular/test-utils/tooltip-components.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,42 @@ export class IgxTooltipWithNestedContentComponent {
168168
@ViewChild(IgxTooltipDirective, { static: true }) public tooltip!: IgxTooltipDirective;
169169
@ViewChild(IgxTooltipTargetDirective, { static: true }) public tooltipTarget!: IgxTooltipTargetDirective;
170170
}
171+
172+
@Component({
173+
template: `
174+
<button #targetLevel1 [igxTooltipTarget]="tooltipLevel1" [hasArrow]="true">
175+
Parent
176+
</button>
177+
178+
<!-- Level 1 Tooltip -->
179+
<div #tooltipLevel1="tooltip" igxTooltip>
180+
Parent tooltip
181+
<button #targetLevel2 [igxTooltipTarget]="tooltipLevel2" [hasArrow]="true">
182+
Child
183+
</button>
184+
185+
<!-- Level 2 Tooltip -->
186+
<div #tooltipLevel2="tooltip" igxTooltip>
187+
Child tooltip
188+
<button #targetLevel3 [igxTooltipTarget]="tooltipLevel3" [hasArrow]="true">
189+
Grandchild
190+
</button>
191+
192+
<!-- Level 3 Tooltip -->
193+
<div #tooltipLevel3="tooltip" igxTooltip>Grandchild tooltip</div>
194+
195+
</div>
196+
</div>
197+
`,
198+
imports: [IgxTooltipDirective, IgxTooltipTargetDirective],
199+
standalone: true
200+
})
201+
export class IgxTooltipNestedTooltipsComponent {
202+
@ViewChild('targetLevel1', { read: IgxTooltipTargetDirective, static: true }) public targetLevel1: IgxTooltipTargetDirective;
203+
@ViewChild('targetLevel2', { read: IgxTooltipTargetDirective, static: true }) public targetLevel2: IgxTooltipTargetDirective;
204+
@ViewChild('targetLevel3', { read: IgxTooltipTargetDirective, static: true }) public targetLevel3: IgxTooltipTargetDirective;
205+
206+
@ViewChild('tooltipLevel1', { read: IgxTooltipDirective, static: true }) public tooltipLevel1: IgxTooltipDirective;
207+
@ViewChild('tooltipLevel2', { read: IgxTooltipDirective, static: true }) public tooltipLevel2: IgxTooltipDirective;
208+
@ViewChild('tooltipLevel3', { read: IgxTooltipDirective, static: true }) public tooltipLevel3: IgxTooltipDirective;
209+
}

0 commit comments

Comments
 (0)