diff --git a/projects/components/src/select/select-option.component.ts b/projects/components/src/select/select-option.component.ts index afc8c020c..372793772 100644 --- a/projects/components/src/select/select-option.component.ts +++ b/projects/components/src/select/select-option.component.ts @@ -27,6 +27,9 @@ export class SelectOptionComponent implements OnChanges, SelectOption { @Input() public iconColor?: string; + @Input() + public disabled?: boolean; + private readonly optionChangeSubject$: Subject = new Subject(); public readonly optionChange$: Observable = this.optionChangeSubject$.asObservable(); diff --git a/projects/components/src/select/select-option.ts b/projects/components/src/select/select-option.ts index 23aa6f419..ab8be64f4 100644 --- a/projects/components/src/select/select-option.ts +++ b/projects/components/src/select/select-option.ts @@ -4,4 +4,5 @@ export interface SelectOption { selectedLabel?: string; icon?: string; iconColor?: string; + disabled?: boolean; } diff --git a/projects/components/src/select/select.component.scss b/projects/components/src/select/select.component.scss index 1b43dd42f..5d40321ca 100644 --- a/projects/components/src/select/select.component.scss +++ b/projects/components/src/select/select.component.scss @@ -165,6 +165,11 @@ justify-content: space-between; white-space: nowrap; + &.disabled { + color: $gray-5; + cursor: not-allowed; + } + .select-option-info { display: flex; flex-direction: row; diff --git a/projects/components/src/select/select.component.test.ts b/projects/components/src/select/select.component.test.ts index af86bcf6c..5ee890849 100644 --- a/projects/components/src/select/select.component.test.ts +++ b/projects/components/src/select/select.component.test.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { fakeAsync, flush } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { IconLibraryTestingModule, IconType } from '@hypertrace/assets-library'; -import { NavigationService } from '@hypertrace/common'; +import { MemoizeModule, NavigationService } from '@hypertrace/common'; import { IconComponent } from '@hypertrace/components'; import { createHostFactory, mockProvider, SpectatorHost } from '@ngneat/spectator/jest'; import { MockComponent } from 'ng-mocks'; @@ -15,7 +15,7 @@ import { SelectModule } from './select.module'; describe('Select Component', () => { const hostFactory = createHostFactory>({ component: SelectComponent, - imports: [SelectModule, HttpClientTestingModule, IconLibraryTestingModule], + imports: [SelectModule, HttpClientTestingModule, IconLibraryTestingModule, MemoizeModule], declareComponent: false, declarations: [MockComponent(IconComponent)], providers: [ @@ -282,4 +282,44 @@ describe('Select Component', () => { expect(onChange).toHaveBeenCalledWith('none-id'); flush(); })); + + test('should disable select options as expected', fakeAsync(() => { + const onChange = jest.fn(); + + spectator = hostFactory( + ` + + + `, + { + hostProps: { + options: [ + { label: 'first', value: 'first-value' }, + { label: 'second', value: 'second-value', disabled: true }, + { + label: 'third', + value: 'third-value', + selectedLabel: 'Third Value!!!', + icon: 'test-icon', + iconColor: 'red' + } + ], + onChange: onChange + } + } + ); + + spectator.tick(); + spectator.click('.trigger-content'); + + const optionElements = spectator.queryAll('.select-option', { root: true }); + expect(optionElements.length).toBe(3); + expect(optionElements[0]).not.toHaveClass('disabled'); + expect(optionElements[1]).toHaveClass('disabled'); + expect(optionElements[2]).not.toHaveClass('disabled'); + spectator.click(optionElements[1]); + + expect(onChange).not.toHaveBeenCalled(); + flush(); + })); }); diff --git a/projects/components/src/select/select.component.ts b/projects/components/src/select/select.component.ts index 79cdc9ef9..63549d86f 100644 --- a/projects/components/src/select/select.component.ts +++ b/projects/components/src/select/select.component.ts @@ -114,7 +114,7 @@ import { SelectSize } from './select-size'; *ngFor="let item of items" (click)="this.onSelectionChange(item)" class="select-option" - [ngClass]="this.size" + [ngClass]="this.getStyleClassesForSelectItem | htMemoize: this.size:item" >
implements AfterContentInit, OnChanges { } public onSelectionChange(item: SelectOptionComponent): void { + if (item.disabled) { + return; + } + this.selected = item.value; this.selected$ = this.buildObservableOfSelected(); this.selectedChange.emit(this.selected); @@ -255,6 +259,16 @@ export class SelectComponent implements AfterContentInit, OnChanges { return this.items.find(item => item.value === value); } + + public getStyleClassesForSelectItem(size: SelectSize, item: SelectOptionComponent): string[] { + const styles: string[] = [size]; + + if (item.disabled) { + styles.push('disabled'); + } + + return styles; + } } export const enum SelectTriggerDisplayMode { diff --git a/projects/components/src/select/select.module.ts b/projects/components/src/select/select.module.ts index 75088454e..7a49b5802 100644 --- a/projects/components/src/select/select.module.ts +++ b/projects/components/src/select/select.module.ts @@ -1,6 +1,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { MemoizeModule } from '@hypertrace/common'; import { DividerModule } from '../divider/divider.module'; import { IconModule } from '../icon/icon.module'; import { LabelModule } from '../label/label.module'; @@ -21,7 +22,8 @@ import { SelectComponent } from './select.component'; LetAsyncModule, PopoverModule, TooltipModule, - DividerModule + DividerModule, + MemoizeModule ], declarations: [SelectComponent, SelectOptionComponent, SelectGroupComponent, SelectControlOptionComponent], exports: [SelectComponent, SelectOptionComponent, SelectGroupComponent, SelectControlOptionComponent]