From d987d1a9913c57de64f170e2859a202030a519ac Mon Sep 17 00:00:00 2001 From: Alex Karpov Date: Fri, 27 Oct 2023 14:07:58 +0300 Subject: [PATCH] NAS-124670 / 24.04 / Reporting disks view improvements for large disk systems (#9076) * NAS-124670: Reporting disks view improvements for large disk systems * NAS-124670: PR update * NAS-124670: PR update * NAS-124670: PR update * NAS-124670: PR update --- .../ix-select/ix-select.component.html | 13 ++++ .../ix-select/ix-select.component.scss | 14 ++++ .../ix-select/ix-select.component.spec.ts | 14 ++++ .../ix-select/ix-select.component.ts | 67 ++++++++++++++++--- .../cloud-credentials-card.component.html | 2 +- .../reports-global-controls.component.html | 1 + .../reports-global-controls.component.ts | 2 - src/assets/styles/other/_tn-styles.scss | 2 +- 8 files changed, 100 insertions(+), 15 deletions(-) diff --git a/src/app/modules/ix-forms/components/ix-select/ix-select.component.html b/src/app/modules/ix-forms/components/ix-select/ix-select.component.html index ec777cdf891..337f82d364b 100644 --- a/src/app/modules/ix-forms/components/ix-select/ix-select.component.html +++ b/src/app/modules/ix-forms/components/ix-select/ix-select.component.html @@ -22,6 +22,19 @@ {{ selectedLabel }} +
+ + + {{ 'Select All' | translate }} +
+ { expect(currentValue).toBe('GBR, GRL'); expect(control.value).toEqual(['Great Britain', 'Greenland']); }); + + it('should select all options when "Select All" is checked', () => { + spectator.setInput('showSelectAll', true); + spectator.component.toggleSelectAll(true); + expect(spectator.component.value).toEqual(['Great Britain', 'Greenland', 'France']); + expect(spectator.component.selectAllState.checked).toBeTruthy(); + }); + + it('should unselect all options when "Select All" is unchecked', () => { + spectator.setInput('showSelectAll', true); + spectator.component.toggleSelectAll(false); + expect(spectator.component.value).toEqual([]); + expect(spectator.component.selectAllState.checked).toBeFalsy(); + }); }); }); diff --git a/src/app/modules/ix-forms/components/ix-select/ix-select.component.ts b/src/app/modules/ix-forms/components/ix-select/ix-select.component.ts index 40afb27b79d..e0047e00789 100644 --- a/src/app/modules/ix-forms/components/ix-select/ix-select.component.ts +++ b/src/app/modules/ix-forms/components/ix-select/ix-select.component.ts @@ -1,13 +1,13 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, - Component, Input, OnChanges, + Component, Input, OnChanges, OnInit, } from '@angular/core'; import { ControlValueAccessor, NgControl, } from '@angular/forms'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { EMPTY, Observable } from 'rxjs'; -import { catchError, tap } from 'rxjs/operators'; +import { catchError, debounceTime, tap } from 'rxjs/operators'; import { SelectOption, SelectOptionValueType } from 'app/interfaces/option.interface'; type IxSelectValue = SelectOptionValueType; @@ -19,7 +19,7 @@ type IxSelectValue = SelectOptionValueType; templateUrl: './ix-select.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class IxSelectComponent implements ControlValueAccessor, OnChanges { +export class IxSelectComponent implements ControlValueAccessor, OnInit, OnChanges { @Input() label: string; @Input() value: IxSelectValue; @Input() hint: string; @@ -29,12 +29,18 @@ export class IxSelectComponent implements ControlValueAccessor, OnChanges { @Input() multiple: boolean; @Input() emptyValue: string = null; @Input() hideEmpty = false; + @Input() showSelectAll = false; @Input() compareWith: (val1: unknown, val2: unknown) => boolean = (val1: unknown, val2: unknown) => val1 === val2; isDisabled = false; hasErrorInOptions = false; opts$: Observable; isLoading = false; + + selectAllState = { + checked: false, + }; + private opts: SelectOption[] = []; get selectedLabel(): string { @@ -60,10 +66,15 @@ export class IxSelectComponent implements ControlValueAccessor, OnChanges { return selectedLabels.length > 0 ? selectedLabels : []; } - constructor( - public controlDirective: NgControl, - private cdr: ChangeDetectorRef, - ) { + get disabledState(): boolean { + return this.isDisabled || !this.options; + } + + get isLoadingState(): boolean { + return this.isLoading || !this.options; + } + + constructor(public controlDirective: NgControl, private cdr: ChangeDetectorRef) { this.controlDirective.valueAccessor = this; } @@ -90,6 +101,15 @@ export class IxSelectComponent implements ControlValueAccessor, OnChanges { } } + ngOnInit(): void { + if (this.multiple) { + this.controlDirective.control.valueChanges.pipe(debounceTime(0), untilDestroyed(this)).subscribe(() => { + this.updateSelectAllState(); + this.cdr.markForCheck(); + }); + } + } + onChange: (value: IxSelectValue) => void = (): void => {}; onTouch: () => void = (): void => {}; @@ -115,11 +135,36 @@ export class IxSelectComponent implements ControlValueAccessor, OnChanges { event.stopPropagation(); } - get disabledState(): boolean { - return this.isDisabled || !this.options; + selectAll(): void { + if (this.multiple) { + this.value = this.opts.map(opt => opt.value) as SelectOptionValueType; + this.onChange(this.value); + } } - get isLoadingState(): boolean { - return this.isLoading || !this.options; + unselectAll(): void { + this.value = []; + this.onChange(this.value); + } + + toggleSelectAll(checked: boolean): void { + if (checked) { + this.selectAll(); + } else { + this.unselectAll(); + } + this.updateSelectAllState(); + } + + updateSelectAllState(): void { + if (Array.isArray(this.value)) { + if (this.value.length === 0) { + this.selectAllState.checked = false; + } else if (this.value.length === this.opts.length) { + this.selectAllState.checked = true; + } else { + this.selectAllState.checked = false; + } + } } } diff --git a/src/app/pages/credentials/backup-credentials/cloud-credentials-card/cloud-credentials-card.component.html b/src/app/pages/credentials/backup-credentials/cloud-credentials-card/cloud-credentials-card.component.html index e67d48c71ed..301f810454a 100644 --- a/src/app/pages/credentials/backup-credentials/cloud-credentials-card/cloud-credentials-card.component.html +++ b/src/app/pages/credentials/backup-credentials/cloud-credentials-card/cloud-credentials-card.component.html @@ -25,7 +25,7 @@

{{ 'Cloud Credentials' | translate }}

[isLoading]="dataProvider.isLoading$ | async" > - + diff --git a/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.html b/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.html index dd41cbbda4b..389605cf0e1 100644 --- a/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.html +++ b/src/app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component.html @@ -5,6 +5,7 @@ [label]="'Devices' | translate" [options]="diskDevices$" [multiple]="true" + [showSelectAll]="true" > , private reportsService: ReportsService, - private slideInService: IxSlideInService, private cdr: ChangeDetectorRef, ) {} diff --git a/src/assets/styles/other/_tn-styles.scss b/src/assets/styles/other/_tn-styles.scss index 480b700c92c..b4bc1e7059d 100644 --- a/src/assets/styles/other/_tn-styles.scss +++ b/src/assets/styles/other/_tn-styles.scss @@ -342,7 +342,7 @@ $primary-dark: darken(map-get($md-primary, 500), 8%); .mat-mdc-menu-content button.mat-mdc-menu-item:hover .mat-icon:not(.theme-picker-swatch-icon), .mat-mdc-menu-content button.mat-mdc-menu-item:focus, .mat-mdc-menu-content button.mat-mdc-menu-item:focus .mat-icon:not(.theme-picker-swatch-icon) { - background-color: var(--hover-bg); + background-color: var(--hover-bg) !important; } // Treelists in storage/volumes