Skip to content

Commit adb1c79

Browse files
authored
feat(list): add selected property to the list-item component (#15414)
1 parent ce35ce4 commit adb1c79

File tree

11 files changed

+192
-11
lines changed

11 files changed

+192
-11
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Ignite UI for Angular Change Log
22

33
All notable changes for each version of this project will be documented in this file.
4+
## 19.1.1
5+
### New Features
6+
- IgxListItem
7+
- Added a new `selected` input property, making it easier to indicate when a list item is selected by applying styling responsible for that state.
8+
49
## 19.1.0
510
### General
611
- `IgxCarousel`

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"@types/source-map": "0.5.2",
7575
"express": "^4.21.1",
7676
"fflate": "^0.8.1",
77-
"igniteui-theming": "^15.0.0",
77+
"igniteui-theming": "^15.1.1",
7878
"igniteui-trial-watermark": "^3.0.2",
7979
"lodash-es": "^4.17.21",
8080
"rxjs": "^7.8.0",

projects/igniteui-angular/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"tslib": "^2.3.0",
7474
"igniteui-trial-watermark": "^3.0.2",
7575
"lodash-es": "^4.17.21",
76-
"igniteui-theming": "^15.0.0",
76+
"igniteui-theming": "^15.1.1",
7777
"@igniteui/material-icons-extended": "^3.1.0"
7878
},
7979
"peerDependencies": {

projects/igniteui-angular/src/lib/core/styles/components/list/_list-component.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
@extend %igx-list-item-base--active !optional;
2929
}
3030

31+
// css class `igx-list__item-base--selected
32+
@include e(item-base, $m: selected) {
33+
@extend %igx-list-item-base--selected !optional;
34+
}
35+
3136
// css class 'igx-list__item-right' applied to the panning container shown when the list item is panned left
3237
@include e(item-right) {
3338
@extend %igx-list-item-pan !optional;

projects/igniteui-angular/src/lib/core/styles/components/list/_list-theme.scss

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,27 @@
1919
/// @param {Color} $item-background [null] - The list item background color.
2020
/// @param {Color} $item-background-hover [null] - The list item hover background color.
2121
/// @param {Color} $item-background-active [null] - The active list item background color.
22+
/// @param {Color} $item-background-selected [null] - The selected list item background color.
2223
/// @param {Color} $item-text-color [null] - The list item text color.
2324
/// @param {Color} $item-text-color-hover [null] - The list item hover text color.
2425
/// @param {Color} $item-text-color-active [null] - The active list item text color.
26+
/// @param {Color} $item-text-color-selected [null] - The selected list item text color.
2527
/// @param {Color} $item-title-color [null] - The list item title color.
2628
/// @param {Color} $item-title-color-hover [null] - The list item hover title color.
2729
/// @param {Color} $item-title-color-active [null] - The active list item title color.
30+
/// @param {Color} $item-title-color-selected [null] - The selected list item title color.
2831
/// @param {Color} $item-subtitle-color [null] - The list item subtitle color.
2932
/// @param {Color} $item-subtitle-color-hover [null] - The list item hover subtitle color.
3033
/// @param {Color} $item-subtitle-color-active [null] - The active list item subtitle color.
34+
/// @param {Color} $item-subtitle-color-selected [null] - The selected list item subtitle color.
3135
/// @param {Color} $item-action-color [null] - The list item action color.
3236
/// @param {Color} $item-action-color-hover [null] - The list item hover action color.
3337
/// @param {Color} $item-action-color-active [null] - The active list item action color.
38+
/// @param {Color} $item-action-color-selected [null] - The selected list item action color.
3439
/// @param {Color} $item-thumbnail-color [null] - The list item thumbnail color.
3540
/// @param {Color} $item-thumbnail-color-hover [null] - The list item hover thumbnail color.
3641
/// @param {Color} $item-thumbnail-color-active [null] - The active list item thumbnail color.
42+
/// @param {Color} $item-thumbnail-color-selected [null] - The selected list item thumbnail color.
3743
/// @param {List} $border-radius [null] - The border radius used for list component.
3844
/// @param {List} $item-border-radius [null] - The border radius used for list item.
3945
/// @param {Color} $border-width [null] - The list border width.
@@ -54,21 +60,27 @@
5460
$item-background: null,
5561
$item-background-hover: null,
5662
$item-background-active: null,
63+
$item-background-selected: null,
5764
$item-text-color: null,
5865
$item-text-color-hover: null,
5966
$item-text-color-active: null,
67+
$item-text-color-selected: null,
6068
$item-title-color: null,
6169
$item-title-color-hover: null,
6270
$item-title-color-active: null,
71+
$item-title-color-selected: null,
6372
$item-subtitle-color: null,
6473
$item-subtitle-color-hover: null,
6574
$item-subtitle-color-active: null,
75+
$item-subtitle-color-selected: null,
6676
$item-action-color: null,
6777
$item-action-color-hover: null,
6878
$item-action-color-active: null,
79+
$item-action-color-selected: null,
6980
$item-thumbnail-color: null,
7081
$item-thumbnail-color-hover: null,
7182
$item-thumbnail-color-active: null,
83+
$item-thumbnail-color-selected: null,
7284
$border-color: null,
7385
$border-width: null,
7486
) {
@@ -120,6 +132,16 @@
120132
}
121133
}
122134

135+
@if not($item-background-selected) and $item-background {
136+
@if meta.type-of($item-background) == 'color' {
137+
@if luminance($item-background) < .5 {
138+
$item-background-selected: color.scale($item-background, $lightness: 8%);
139+
} @else {
140+
$item-background-selected: color.scale($item-background, $lightness: -8%);
141+
}
142+
}
143+
}
144+
123145
@if not($header-text-color) and $header-background {
124146
$header-text-color: text-contrast($header-background);
125147
}
@@ -180,28 +202,54 @@
180202
$item-text-color-active: text-contrast($item-background-active);
181203
}
182204

205+
@if not($item-text-color-selected) and $item-background-selected {
206+
$item-text-color-selected: text-contrast($item-background-selected);
207+
}
208+
183209
@if not($item-title-color-active) and $item-background-active {
184210
$item-title-color-active: text-contrast($item-background-active);
185211
}
186212

213+
@if not($item-title-color-selected) and $item-background-selected {
214+
$item-title-color-selected: text-contrast($item-background-selected);
215+
}
216+
187217
@if not($item-action-color-active) and $item-background-active {
188218
$item-action-color-active: text-contrast($item-background-active);
189219
}
190220

221+
@if not($item-action-color-selected) and $item-background-selected {
222+
$item-action-color-selected: text-contrast($item-background-selected);
223+
}
224+
191225
@if not($item-thumbnail-color-active) and $item-background-active {
192226
$item-thumbnail-color-active: text-contrast($item-background-active);
193227
}
194228

229+
@if not($item-thumbnail-color-selected) and $item-background-selected {
230+
$item-thumbnail-color-selected: text-contrast($item-background-selected);
231+
}
232+
195233
@if not($item-subtitle-color-active) and $item-background-active {
196234
@if meta.type-of($item-background-active) == 'color' {
197235
$item-subtitle-color-active: rgba(text-contrast($item-background-active), .74);
198236
}
199237
}
200238

239+
@if not($item-subtitle-color-selected) and $item-background-selected {
240+
@if meta.type-of($item-background-selected) == 'color' {
241+
$item-subtitle-color-selected: rgba(text-contrast($item-background-selected), .74);
242+
}
243+
}
244+
201245
@if not($item-subtitle-color-active) and $item-text-color-active {
202246
$item-subtitle-color-active: $item-text-color-active;
203247
}
204248

249+
@if not($item-subtitle-color-selected) and $item-text-color-selected {
250+
$item-subtitle-color-selected: $item-text-color-selected;
251+
}
252+
205253
@return extend($theme, (
206254
name: $name,
207255
border-radius: $border-radius,
@@ -212,21 +260,27 @@
212260
item-background: $item-background,
213261
item-background-hover: $item-background-hover,
214262
item-background-active: $item-background-active,
263+
item-background-selected: $item-background-selected,
215264
item-text-color: $item-text-color,
216265
item-text-color-hover: $item-text-color-hover,
217266
item-text-color-active: $item-text-color-active,
267+
item-text-color-selected: $item-text-color-selected,
218268
item-title-color:$item-title-color,
219269
item-title-color-hover:$item-title-color-hover,
220270
item-title-color-active:$item-title-color-active,
271+
item-title-color-selected:$item-title-color-selected,
221272
item-subtitle-color: $item-subtitle-color,
222273
item-subtitle-color-hover: $item-subtitle-color-hover,
223274
item-subtitle-color-active: $item-subtitle-color-active,
275+
item-subtitle-color-selected: $item-subtitle-color-selected,
224276
item-action-color: $item-action-color,
225277
item-action-color-hover: $item-action-color-hover,
226278
item-action-color-active: $item-action-color-active,
279+
item-action-color-selected: $item-action-color-selected,
227280
item-thumbnail-color: $item-thumbnail-color,
228281
item-thumbnail-color-hover: $item-thumbnail-color-hover,
229282
item-thumbnail-color-active: $item-thumbnail-color-active,
283+
item-thumbnail-color-selected: $item-thumbnail-color-selected,
230284
border-color: $border-color,
231285
border-width: $border-width,
232286
theme: map.get($schema, '_meta', 'theme'),
@@ -423,6 +477,12 @@
423477
}
424478
}
425479

480+
%igx-list-item-base--selected {
481+
%igx-list-item-content {
482+
@extend %igx-list-item-content--selected;
483+
}
484+
}
485+
426486
%igx-list-item-pan {
427487
position: absolute;
428488
visibility: hidden;
@@ -550,6 +610,37 @@
550610
}
551611
}
552612

613+
%igx-list-item-content--selected {
614+
color: var-get($theme, 'item-text-color-selected');
615+
background: var-get($theme, 'item-background-selected');
616+
z-index: 3;
617+
618+
%igx-list__item-line-title {
619+
color: var-get($theme, 'item-title-color-selected')
620+
}
621+
622+
%igx-list__item-line-subtitle {
623+
color: var-get($theme, 'item-subtitle-color-selected')
624+
}
625+
626+
%igx-list__item-actions {
627+
color: var-get($theme, 'item-action-color-selected');
628+
629+
igx-icon,
630+
igc-icon {
631+
color: var-get($theme, 'item-action-color-selected')}
632+
}
633+
634+
%igx-list__item-thumbnail {
635+
color: var-get($theme, 'item-thumbnail-color-selected');
636+
637+
igx-icon,
638+
igc-icon {
639+
color: var-get($theme, 'item-thumbnail-color-selected')
640+
}
641+
}
642+
}
643+
553644
%igx-list-item-content--inactive {
554645
transition: transform .3s $out-quad;
555646
}

projects/igniteui-angular/src/lib/list/list-item.component.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export class IgxListItemComponent implements IListChild {
133133
private lastPanDir = IgxListPanState.NONE;
134134

135135
private _role: string = '';
136+
private _selected = false;;
136137

137138
/**
138139
* Gets the `panState` of a `list item`.
@@ -281,6 +282,30 @@ export class IgxListItemComponent implements IListChild {
281282
this._role = val;
282283
}
283284

285+
/**
286+
* Sets/gets whether the `list item` is selected.
287+
* Selection is only applied to non-header items.
288+
* When selected, the CSS class 'igx-list__item-base--selected' is added to the item.
289+
* ```html
290+
* <igx-list-item [selected]="true">Selected Item</igx-list-item>
291+
* ```
292+
* ```typescript
293+
* let isSelected = this.listItem.selected;
294+
* this.listItem.selected = true;
295+
* ```
296+
*
297+
* @memberof IgxListItemComponent
298+
*/
299+
@HostBinding('class.igx-list__item-base--selected')
300+
@Input({ transform: booleanAttribute })
301+
public get selected() {
302+
return this._selected && !this.isHeader;
303+
}
304+
305+
public set selected(value: boolean) {
306+
this._selected = value;
307+
}
308+
284309
/**
285310
* Indicates whether `list item` should have header style.
286311
* ```typescript

projects/igniteui-angular/src/lib/list/list.component.spec.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
ListWithIgxForAndScrollingComponent,
2525
TwoHeadersListComponent,
2626
TwoHeadersListNoPanningComponent,
27-
ListDirectivesComponent
27+
ListDirectivesComponent,
28+
ListWithSelectedItemComponent
2829
} from '../test-utils/list-components.spec';
2930
import { configureTestSuite } from '../test-utils/configure-suite';
3031
import { wait } from '../test-utils/ui-interactions.spec';
@@ -44,7 +45,8 @@ describe('List', () => {
4445
TwoHeadersListNoPanningComponent,
4546
ListWithPanningTemplatesComponent,
4647
ListWithIgxForAndScrollingComponent,
47-
ListDirectivesComponent
48+
ListDirectivesComponent,
49+
ListWithSelectedItemComponent
4850
]
4951
});
5052
});
@@ -661,6 +663,40 @@ describe('List', () => {
661663
}
662664
}));
663665

666+
it('should properly set and get the selected property of list items', () => {
667+
const fixture = TestBed.createComponent(ListWithSelectedItemComponent);
668+
const list = fixture.componentInstance.list;
669+
fixture.detectChanges();
670+
671+
// Get all list items
672+
const items = list.children.toArray();
673+
const headerItem = items[0];
674+
const firstItem = items[1];
675+
const secondItem = items[2];
676+
677+
// Verify initial selected state
678+
expect(headerItem.selected).toBe(false); // Headers should never be selected even if selected=true
679+
expect(firstItem.selected).toBe(true);
680+
expect(secondItem.selected).toBe(false);
681+
682+
// Check if the selected class is applied correctly
683+
expect(headerItem.element.classList.contains('igx-list__item-base--selected')).toBe(false);
684+
expect(firstItem.element.classList.contains('igx-list__item-base--selected')).toBe(true);
685+
expect(secondItem.element.classList.contains('igx-list__item-base--selected')).toBe(false);
686+
687+
// Change selected state programmatically
688+
secondItem.selected = true;
689+
fixture.detectChanges();
690+
expect(secondItem.selected).toBe(true);
691+
expect(secondItem.element.classList.contains('igx-list__item-base--selected')).toBe(true);
692+
693+
// Try to select a header item (should not apply)
694+
headerItem.selected = true;
695+
fixture.detectChanges();
696+
expect(headerItem.selected).toBe(false);
697+
expect(headerItem.element.classList.contains('igx-list__item-base--selected')).toBe(false);
698+
});
699+
664700
it('Initializes igxListThumbnail directive', () => {
665701
const fixture = TestBed.createComponent(ListDirectivesComponent);
666702
fixture.detectChanges();

projects/igniteui-angular/src/lib/test-utils/list-components.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ export class BasicListComponent {
6363
export class ListWithHeaderComponent extends BasicListComponent {
6464
}
6565

66+
@Component({
67+
template: `
68+
<div #wrapper>
69+
<igx-list>
70+
<igx-list-item selected [isHeader]="true">Header</igx-list-item>
71+
<igx-list-item selected>Item 1</igx-list-item>
72+
<igx-list-item>Item 2</igx-list-item>
73+
<igx-list-item>Item 3</igx-list-item>
74+
</igx-list>
75+
</div>`,
76+
imports: [IgxListComponent, IgxListItemComponent]
77+
})
78+
export class ListWithSelectedItemComponent extends BasicListComponent {
79+
}
80+
6681
@Component({
6782
template: `
6883
<div #wrapper>

0 commit comments

Comments
 (0)