Skip to content

Commit

Permalink
NAS-128457 / 24.10 / Elements for new enclosure (#10080)
Browse files Browse the repository at this point in the history
  • Loading branch information
undsoft authored May 20, 2024
1 parent 6c733bf commit 930a6a8
Show file tree
Hide file tree
Showing 95 changed files with 354 additions and 26 deletions.
9 changes: 7 additions & 2 deletions src/app/modules/ix-table/directives/ix-body-cell.directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
AfterViewInit,
AfterViewInit, ChangeDetectorRef,
Directive,
Input,
ViewContainerRef,
Expand All @@ -14,7 +14,10 @@ export class IxTableBodyCellDirective<T> implements AfterViewInit {
@Input() row: T;
@Input() column: Column<T, ColumnComponent<T>>;

constructor(private viewContainer: ViewContainerRef) {}
constructor(
private viewContainer: ViewContainerRef,
private cdr: ChangeDetectorRef,
) {}

ngAfterViewInit(): void {
this.createComponent();
Expand All @@ -34,6 +37,8 @@ export class IxTableBodyCellDirective<T> implements AfterViewInit {
// TODO: Replace never.
componentRef.instance[key] = this.column[key] as never;
});

this.cdr.detectChanges();
}

static ngTemplateContextGuard<T>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe('SnapshotListComponent', () => {
{
selector: selectGeneralConfig,
value: {
timezone: 'America/Alaska',
timezone: 'Europe/Kiev',
},
},
],
Expand Down Expand Up @@ -122,11 +122,10 @@ describe('SnapshotListComponent', () => {
await slideToggle.toggle();

// TODO: Add column names when better mechanism to hide/show columns is available
// TODO: Add column values when sizeColumn and dateColumn will return values.
const expectedRows = [
['', 'Dataset', 'Snapshot', '', '', ''],
['', 'test-dataset', 'second-snapshot', '', '', ''],
['', 'test-dataset', 'first-snapshot', '', '', ''],
['', 'test-dataset', 'second-snapshot', '1.49 TiB', '2021-10-18 19:51:43', '1.49 TiB'],
['', 'test-dataset', 'first-snapshot', '1.49 TiB', '2021-10-18 19:51:54', '1.49 TiB'],
];
expect(await table.getCellTexts()).toEqual(expectedRows);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,9 @@ export class ViewElementsMenuComponent {

readonly views = computed<ViewOption[]>(() => {
const enclosure = this.enclosure();
const supportedViews = [
EnclosureElementType.ArrayDeviceSlot,
EnclosureElementType.TemperatureSensors,
EnclosureElementType.Cooling,
EnclosureElementType.VoltageSensor,
EnclosureElementType.PowerSupply,
EnclosureElementType.SasConnector,
EnclosureElementType.EnclosureServicesControllerElectronics,
] as const;

return supportedViews
.filter((view) => enclosure.elements[view])
.map((view) => {

return Object.keys(enclosure.elements)
.map((view: EnclosureElementType) => {
let href = `${enclosure.id}/${view}`;

if (view === EnclosureElementType.ArrayDeviceSlot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,22 @@
<ix-enclosure-header [title]="title()"></ix-enclosure-header>
</mat-card-header>
<mat-card-content>
Selected elements view {{ currentView() }}
<ix-empty
*ngIf="!viewElements(); else table"
[conf]="noView"
></ix-empty>
<ng-template #table>
<ix-table>
<thead
ix-table-head
[columns]="columns"
[dataProvider]="dataProvider()"
></thead>
<tbody
ix-table-body
[columns]="columns"
[dataProvider]="dataProvider()"
></tbody>
</ix-table>
</ng-template>
</mat-card-content>
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import {
createRoutingFactory,
mockProvider,
SpectatorRouting,
} from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import { EmptyType } from 'app/enums/empty-type.enum';
import { EnclosureElementType } from 'app/enums/enclosure-slot-status.enum';
import { EmptyComponent } from 'app/modules/empty/empty.component';
import { IxTableHarness } from 'app/modules/ix-table/components/ix-table/ix-table.harness';
import { IxTableModule } from 'app/modules/ix-table/ix-table.module';
import {
EnclosureHeaderComponent,
} from 'app/pages/system/enclosure/components/enclosure-header/enclosure-header.component';
import { ElementsComponent } from 'app/pages/system/enclosure/components/views/elements-view/elements.component';
import { EnclosureStore } from 'app/pages/system/enclosure/services/enclosure.store';

describe('ElementsComponent', () => {
let spectator: SpectatorRouting<ElementsComponent>;
let loader: HarnessLoader;
const createComponent = createRoutingFactory({
component: ElementsComponent,
imports: [
IxTableModule,
],
params: {
view: 'Voltage Sensor',
},
declarations: [
MockComponent(EmptyComponent),
MockComponent(EnclosureHeaderComponent),
],
providers: [
mockProvider(EnclosureStore, {
enclosureLabel: () => 'M40',
selectedEnclosure: () => ({
elements: {
[EnclosureElementType.VoltageSensor]: {
45: {
descriptor: '5V Sensor',
status: 'OK',
value: '5.06V',
},
46: {
descriptor: '12V Sensor',
status: 'OK',
value: '12.01V',
},
},
},
}),
}),
],
});

beforeEach(() => {
spectator = createComponent();
loader = TestbedHarnessEnvironment.loader(spectator.fixture);
});

it('renders header with name of the current view', () => {
const header = spectator.query(EnclosureHeaderComponent);
expect(header.title).toBe('Voltage on M40');
});

it('renders enclosure elements for the view route parameter', async () => {
const table = await loader.getHarness(IxTableHarness);
const cells = await table.getCellTexts();
expect(cells).toEqual([
['Descriptor', 'Status', 'Value'],
['5V Sensor', 'OK', '5.06V'],
['12V Sensor', 'OK', '12.01V'],
]);
});

it('renders an error when view from route param is not available for current enclosure', () => {
spectator.setRouteParam('view', 'Cooling Fan');

const empty = spectator.query(EmptyComponent);
expect(empty).toExist();
expect(empty.conf).toEqual({
large: true,
message: 'This view is not available for this enclosure.',
title: 'N/A',
type: EmptyType.Errors,
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { map } from 'rxjs/operators';
import { EnclosureElementType } from 'app/enums/enclosure-slot-status.enum';
import { EmptyType } from 'app/enums/empty-type.enum';
import { EnclosureElementType, enclosureElementTypeLabels } from 'app/enums/enclosure-slot-status.enum';
import { EmptyConfig } from 'app/interfaces/empty-config.interface';
import { EnclosureElement } from 'app/interfaces/enclosure.interface';
import { ArrayDataProvider } from 'app/modules/ix-table/classes/array-data-provider/array-data-provider';
import { textColumn } from 'app/modules/ix-table/components/ix-table-body/cells/ix-cell-text/ix-cell-text.component';
import { createTable } from 'app/modules/ix-table/utils';
import { EnclosureStore } from 'app/pages/system/enclosure/services/enclosure.store';

@Component({
Expand All @@ -12,20 +18,63 @@ import { EnclosureStore } from 'app/pages/system/enclosure/services/enclosure.st
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ElementsComponent {
// TODO: Error state for missing view.

readonly currentView = toSignal(
protected readonly currentView = toSignal(
this.route.paramMap.pipe(map((params) => params.get('view') as EnclosureElementType)),
{ initialValue: undefined },
);

readonly title = computed(() => {
protected readonly title = computed(() => {
const view = enclosureElementTypeLabels.has(this.currentView())
? enclosureElementTypeLabels.get(this.currentView())
: this.currentView();

return this.translate.instant('{view} on {enclosure}', {
view: this.currentView(), // TODO: Translate
view,
enclosure: this.store.enclosureLabel(),
});
});

protected readonly noView: EmptyConfig = {
title: this.translate.instant('N/A'),
message: this.translate.instant('This view is not available for this enclosure.'),
large: true,
type: EmptyType.Errors,
};

protected readonly viewElements = computed(() => {
return this.store.selectedEnclosure().elements[this.currentView()];
});

protected readonly columns = createTable<EnclosureElement>(
[
textColumn({
title: this.translate.instant('Descriptor'),
propertyName: 'descriptor',
sortable: true,
}),
textColumn({
title: this.translate.instant('Status'),
propertyName: 'status',
sortable: true,
}),
textColumn({
title: this.translate.instant('Value'),
propertyName: 'value',
sortable: true,
}),
],
{
rowTestId: (element) => element.descriptor,
},
);

protected readonly dataProvider = computed(() => {
const dataProvider = new ArrayDataProvider<EnclosureElement>();
const elements = Object.values(this.viewElements()) as EnclosureElement[];
dataProvider.setRows(elements);
return dataProvider;
});

constructor(
private route: ActivatedRoute,
private translate: TranslateService,
Expand Down
2 changes: 2 additions & 0 deletions src/app/pages/system/enclosure/enclosure.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
import { MatTooltip } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { CommonDirectivesModule } from 'app/directives/common/common-directives.module';
import { EmptyComponent } from 'app/modules/empty/empty.component';
import { IxFormsModule } from 'app/modules/ix-forms/ix-forms.module';
import { IxIconModule } from 'app/modules/ix-icon/ix-icon.module';
import { IxTableModule } from 'app/modules/ix-table/ix-table.module';
Expand Down Expand Up @@ -56,6 +57,7 @@ import { routing } from 'app/pages/system/enclosure/enclosure.routing';
ReactiveFormsModule,
MatDialogClose,
MatDialogTitle,
EmptyComponent,
],
declarations: [
EnclosureDashboardComponent,
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/af.json
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@
"Descriptive identifier for this certificate authority.": "",
"Descriptive identifier for this certificate.": "",
"Descriptive name for the replication.": "",
"Descriptor": "",
"Destination": "",
"Destination Dataset Read-only Policy": "",
"Destination Location": "",
Expand Down Expand Up @@ -4067,6 +4068,7 @@
"This system cannot communicate externally.": "",
"This system will restart when the update completes.": "",
"This value represents the threshold block size for including small file blocks into the special allocation class. Blocks smaller than or equal to this value will be assigned to the special allocation class while greater blocks will be assigned to the regular class. Valid values are zero or a power of two from 512B up to 1M. The default size is 0 which means no small file blocks will be allocated in the special class. Before setting this property, a special class vdev must be added to the pool. See <a href=\"https://zfsonlinux.org/manpages/0.7.13/man8/zpool.8.html\" target=\"_blank\">zpool(8)</a> for more details on the special allocation": "",
"This view is not available for this enclosure.": "",
"Thread #": "",
"Thread responsible for syncing db transactions not running on other node.": "",
"Thread responsible for syncing db transactions not running on this node.": "",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@
"Descriptive identifier for this certificate authority.": "",
"Descriptive identifier for this certificate.": "",
"Descriptive name for the replication.": "",
"Descriptor": "",
"Destination": "",
"Destination Dataset Read-only Policy": "",
"Destination Location": "",
Expand Down Expand Up @@ -4067,6 +4068,7 @@
"This system cannot communicate externally.": "",
"This system will restart when the update completes.": "",
"This value represents the threshold block size for including small file blocks into the special allocation class. Blocks smaller than or equal to this value will be assigned to the special allocation class while greater blocks will be assigned to the regular class. Valid values are zero or a power of two from 512B up to 1M. The default size is 0 which means no small file blocks will be allocated in the special class. Before setting this property, a special class vdev must be added to the pool. See <a href=\"https://zfsonlinux.org/manpages/0.7.13/man8/zpool.8.html\" target=\"_blank\">zpool(8)</a> for more details on the special allocation": "",
"This view is not available for this enclosure.": "",
"Thread #": "",
"Thread responsible for syncing db transactions not running on other node.": "",
"Thread responsible for syncing db transactions not running on this node.": "",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/ast.json
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@
"Descriptive identifier for this certificate authority.": "",
"Descriptive identifier for this certificate.": "",
"Descriptive name for the replication.": "",
"Descriptor": "",
"Destination": "",
"Destination Dataset Read-only Policy": "",
"Destination Location": "",
Expand Down Expand Up @@ -4067,6 +4068,7 @@
"This system cannot communicate externally.": "",
"This system will restart when the update completes.": "",
"This value represents the threshold block size for including small file blocks into the special allocation class. Blocks smaller than or equal to this value will be assigned to the special allocation class while greater blocks will be assigned to the regular class. Valid values are zero or a power of two from 512B up to 1M. The default size is 0 which means no small file blocks will be allocated in the special class. Before setting this property, a special class vdev must be added to the pool. See <a href=\"https://zfsonlinux.org/manpages/0.7.13/man8/zpool.8.html\" target=\"_blank\">zpool(8)</a> for more details on the special allocation": "",
"This view is not available for this enclosure.": "",
"Thread #": "",
"Thread responsible for syncing db transactions not running on other node.": "",
"Thread responsible for syncing db transactions not running on this node.": "",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/az.json
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@
"Descriptive identifier for this certificate authority.": "",
"Descriptive identifier for this certificate.": "",
"Descriptive name for the replication.": "",
"Descriptor": "",
"Destination": "",
"Destination Dataset Read-only Policy": "",
"Destination Location": "",
Expand Down Expand Up @@ -4067,6 +4068,7 @@
"This system cannot communicate externally.": "",
"This system will restart when the update completes.": "",
"This value represents the threshold block size for including small file blocks into the special allocation class. Blocks smaller than or equal to this value will be assigned to the special allocation class while greater blocks will be assigned to the regular class. Valid values are zero or a power of two from 512B up to 1M. The default size is 0 which means no small file blocks will be allocated in the special class. Before setting this property, a special class vdev must be added to the pool. See <a href=\"https://zfsonlinux.org/manpages/0.7.13/man8/zpool.8.html\" target=\"_blank\">zpool(8)</a> for more details on the special allocation": "",
"This view is not available for this enclosure.": "",
"Thread #": "",
"Thread responsible for syncing db transactions not running on other node.": "",
"Thread responsible for syncing db transactions not running on this node.": "",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/be.json
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@
"Descriptive identifier for this certificate authority.": "",
"Descriptive identifier for this certificate.": "",
"Descriptive name for the replication.": "",
"Descriptor": "",
"Destination": "",
"Destination Dataset Read-only Policy": "",
"Destination Location": "",
Expand Down Expand Up @@ -4067,6 +4068,7 @@
"This system cannot communicate externally.": "",
"This system will restart when the update completes.": "",
"This value represents the threshold block size for including small file blocks into the special allocation class. Blocks smaller than or equal to this value will be assigned to the special allocation class while greater blocks will be assigned to the regular class. Valid values are zero or a power of two from 512B up to 1M. The default size is 0 which means no small file blocks will be allocated in the special class. Before setting this property, a special class vdev must be added to the pool. See <a href=\"https://zfsonlinux.org/manpages/0.7.13/man8/zpool.8.html\" target=\"_blank\">zpool(8)</a> for more details on the special allocation": "",
"This view is not available for this enclosure.": "",
"Thread #": "",
"Thread responsible for syncing db transactions not running on other node.": "",
"Thread responsible for syncing db transactions not running on this node.": "",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@
"Descriptive identifier for this certificate authority.": "",
"Descriptive identifier for this certificate.": "",
"Descriptive name for the replication.": "",
"Descriptor": "",
"Destination": "",
"Destination Dataset Read-only Policy": "",
"Destination Location": "",
Expand Down Expand Up @@ -4067,6 +4068,7 @@
"This system cannot communicate externally.": "",
"This system will restart when the update completes.": "",
"This value represents the threshold block size for including small file blocks into the special allocation class. Blocks smaller than or equal to this value will be assigned to the special allocation class while greater blocks will be assigned to the regular class. Valid values are zero or a power of two from 512B up to 1M. The default size is 0 which means no small file blocks will be allocated in the special class. Before setting this property, a special class vdev must be added to the pool. See <a href=\"https://zfsonlinux.org/manpages/0.7.13/man8/zpool.8.html\" target=\"_blank\">zpool(8)</a> for more details on the special allocation": "",
"This view is not available for this enclosure.": "",
"Thread #": "",
"Thread responsible for syncing db transactions not running on other node.": "",
"Thread responsible for syncing db transactions not running on this node.": "",
Expand Down
Loading

0 comments on commit 930a6a8

Please sign in to comment.