diff --git a/projects/components/src/table/controls/table-controls.component.ts b/projects/components/src/table/controls/table-controls.component.ts
index 6289032b4..12d7319dc 100644
--- a/projects/components/src/table/controls/table-controls.component.ts
+++ b/projects/components/src/table/controls/table-controls.component.ts
@@ -6,7 +6,8 @@ import {
IterableDiffer,
IterableDiffers,
OnChanges,
- Output
+ Output,
+ TemplateRef
} from '@angular/core';
import { IconType } from '@hypertrace/assets-library';
import { TypedSimpleChanges } from '@hypertrace/common';
@@ -14,6 +15,7 @@ import { IconSize } from '../../icon/icon-size';
import { MultiSelectJustify } from '../../multi-select/multi-select-justify';
import { MultiSelectSearchMode, TriggerLabelDisplayMode } from '../../multi-select/multi-select.component';
import { ToggleItem } from '../../toggle-group/toggle-item';
+import { StatefulTableRow } from '../table-api';
import {
TableCheckboxChange,
TableCheckboxControl,
@@ -86,6 +88,13 @@ import {
[activeItem]="this.activeViewItem"
(activeItemChange)="this.onViewChange($event)"
>
+
+
+
+
+
`
@@ -114,6 +123,12 @@ export class TableControlsComponent implements OnChanges {
@Input()
public activeViewItem?: ToggleItem;
+ @Input()
+ public selectedRows?: StatefulTableRow[] = [];
+
+ @Input()
+ public customControlContent?: TemplateRef<{ selectedRows?: StatefulTableRow[] }>;
+
@Output()
public readonly searchChange: EventEmitter = new EventEmitter();
diff --git a/projects/components/src/table/table.component.test.ts b/projects/components/src/table/table.component.test.ts
index fa153597e..2ca704b10 100644
--- a/projects/components/src/table/table.component.test.ts
+++ b/projects/components/src/table/table.component.test.ts
@@ -60,7 +60,7 @@ describe('Table component', () => {
queryParamMap: EMPTY
}),
mockProvider(DomElementMeasurerService, {
- measureHtmlElement: (): ClientRect => ({
+ measureHtmlElement: (): Partial => ({
top: 0,
left: 0,
bottom: 20,
@@ -397,37 +397,6 @@ describe('Table component', () => {
});
}));
- test('should trigger toggle row selection for multi row select config', () => {
- const columns = buildColumns();
- const spectator = createHost(
- ``,
- {
- hostProps: {
- columnConfigs: columns,
- data: buildData(),
- selectionMode: TableSelectionMode.Multiple,
- mode: TableMode.Flat
- }
- }
- );
-
- const row: StatefulTableRow = {
- $$state: {
- parent: undefined,
- expanded: false,
- selected: false,
- root: false,
- leaf: true,
- depth: 1
- }
- };
-
- const spyToggleRowSelection = spyOn(spectator.component, 'toggleRowSelected');
- spectator.component.onDataCellClick(row);
- expect(spyToggleRowSelection).toHaveBeenCalledWith(row);
- });
-
test('should emit selections on toggle select', () => {
const mockSelectionsChange = jest.fn();
const columns = buildColumns();
diff --git a/projects/components/src/table/table.component.ts b/projects/components/src/table/table.component.ts
index 2788aca62..46b164bba 100644
--- a/projects/components/src/table/table.component.ts
+++ b/projects/components/src/table/table.component.ts
@@ -272,6 +272,9 @@ export class TableComponent
@Input()
public pageSize?: number = 50;
+ @Output()
+ public readonly rowClicked: EventEmitter = new EventEmitter();
+
@Output()
public readonly selectionsChange: EventEmitter = new EventEmitter();
@@ -548,9 +551,7 @@ export class TableComponent
}
public onDataRowClick(row: StatefulTableRow): void {
- if (this.hasSelectableRows()) {
- this.toggleRowSelected(row);
- }
+ this.rowClicked.emit(row);
}
public onDataRowMouseEnter(row: StatefulTableRow): void {
@@ -695,10 +696,6 @@ export class TableComponent
return this.isDetailType() && row.$$state.expanded;
}
- public hasSelectableRows(): boolean {
- return this.hasSingleSelect() || this.hasMultiSelect();
- }
-
public hasSingleSelect(): boolean {
return this.selectionMode === TableSelectionMode.Single;
}
diff --git a/projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-selection.model.test.ts b/projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-interaction.model.test.ts
similarity index 76%
rename from projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-selection.model.test.ts
rename to projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-interaction.model.test.ts
index 296adceae..544b4bfb5 100644
--- a/projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-selection.model.test.ts
+++ b/projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-interaction.model.test.ts
@@ -1,8 +1,8 @@
-import { TableWidgetRowSelectionModel } from './table-widget-row-selection.model';
+import { TableWidgetRowInteractionModel } from './table-widget-row-interaction.model';
-describe('TableWidgetRowSelectionModel applies to current row depth', () => {
+describe('TableWidgetRowInteractionModel applies to current row depth', () => {
test('works for default properties', () => {
- const model = new TableWidgetRowSelectionModel();
+ const model = new TableWidgetRowInteractionModel();
model.handler = {
execute: jest.fn()
};
@@ -13,7 +13,7 @@ describe('TableWidgetRowSelectionModel applies to current row depth', () => {
});
test('works for higher row depth', () => {
- const model = new TableWidgetRowSelectionModel();
+ const model = new TableWidgetRowInteractionModel();
model.handler = {
execute: jest.fn()
};
@@ -26,7 +26,7 @@ describe('TableWidgetRowSelectionModel applies to current row depth', () => {
});
test('works correctly when applyToChildRows is false', () => {
- const model = new TableWidgetRowSelectionModel();
+ const model = new TableWidgetRowInteractionModel();
model.handler = {
execute: jest.fn()
};
diff --git a/projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-selection.model.ts b/projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-interaction.model.ts
similarity index 90%
rename from projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-selection.model.ts
rename to projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-interaction.model.ts
index 8d6f00595..47176d1d2 100644
--- a/projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-selection.model.ts
+++ b/projects/observability/src/shared/dashboard/widgets/table/selections/table-widget-row-interaction.model.ts
@@ -2,10 +2,10 @@ import { BOOLEAN_PROPERTY, Model, ModelProperty, ModelPropertyType, NUMBER_PROPE
import { InteractionHandler } from '../../../interaction/interaction-handler';
@Model({
- type: 'table-widget-row-selection',
+ type: 'table-widget-row-interaction',
displayName: 'Row Selection'
})
-export class TableWidgetRowSelectionModel {
+export class TableWidgetRowInteractionModel {
@ModelProperty({
key: 'handler',
displayName: 'Selection Handler',
diff --git a/projects/observability/src/shared/dashboard/widgets/table/table-widget-base.model.ts b/projects/observability/src/shared/dashboard/widgets/table/table-widget-base.model.ts
index cb499423b..e732c85db 100644
--- a/projects/observability/src/shared/dashboard/widgets/table/table-widget-base.model.ts
+++ b/projects/observability/src/shared/dashboard/widgets/table/table-widget-base.model.ts
@@ -17,7 +17,7 @@ import {
} from '@hypertrace/hyperdash';
import { ModelInject, MODEL_API } from '@hypertrace/hyperdash-angular';
import { Observable } from 'rxjs';
-import { TableWidgetRowSelectionModel } from './selections/table-widget-row-selection.model';
+import { TableWidgetRowInteractionModel } from './selections/table-widget-row-interaction.model';
import { SpecificationBackedTableColumnDef } from './table-widget-column.model';
import { TableWidgetControlCheckboxOptionModel } from './table-widget-control-checkbox-option.model';
import { TableWidgetControlSelectOptionModel } from './table-widget-control-select-option.model';
@@ -121,16 +121,11 @@ export abstract class TableWidgetBaseModel extends BaseModel {
return this.api.getData>();
}
- public getRowSelectionHandlers(_row: TableRow): TableWidgetRowSelectionModel[] {
+ public getRowSelectionHandlers(_row: TableRow): TableWidgetRowInteractionModel[] {
// No-op here, but can be overridden
return [];
}
- public getSelectionMode(): TableSelectionMode {
- // No-op here, but can be overridden
- return TableSelectionMode.Single;
- }
-
public setView(_view: string): void {
// No-op here, but can be overridden
return;
@@ -141,6 +136,11 @@ export abstract class TableWidgetBaseModel extends BaseModel {
return [];
}
+ public abstract getRowClickHandlers(): TableWidgetRowInteractionModel[];
+ public abstract getSelectionMode(): TableSelectionMode;
+ public abstract isCustomControlPresent(): boolean;
+ public abstract getCustomControlWidgetModel(selectedRows?: TableRow[]): object | undefined;
+
public getSearchAttribute(): string | undefined {
return this.searchAttribute;
}
diff --git a/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts b/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts
index 7e6eb5082..9c04d9b4a 100644
--- a/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts
+++ b/projects/observability/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts
@@ -22,7 +22,6 @@ import {
TableRow,
TableSelectChange,
TableSelectControl,
- TableSelectionMode,
TableStyle,
ToggleItem,
toInFilter
@@ -36,6 +35,7 @@ import { filter, map, pairwise, share, startWith, switchMap, take, tap, withLate
import { AttributeMetadata, toFilterAttributeType } from '../../../graphql/model/metadata/attribute-metadata';
import { MetadataService } from '../../../services/metadata/metadata.service';
import { InteractionHandler } from '../../interaction/interaction-handler';
+import { TableWidgetRowInteractionModel } from './selections/table-widget-row-interaction.model';
import { TableWidgetBaseModel } from './table-widget-base.model';
import { SpecificationBackedTableColumnDef } from './table-widget-column.model';
import { TableWidgetViewToggleModel } from './table-widget-view-toggle.model';
@@ -61,6 +61,8 @@ import { TableWidgetModel } from './table-widget.model';
[searchPlaceholder]="this.api.model.getSearchPlaceholder()"
[selectControls]="this.selectControls$ | async"
[checkboxControls]="this.checkboxControls$ | async"
+ [selectedRows]="this.selectedRows"
+ [customControlContent]="(this.isCustomControlPresent | htMemoize) ? customControlDetail : undefined"
[viewItems]="this.viewItems"
(searchChange)="this.onSearchChange($event)"
(selectChange)="this.onSelectChange($event)"
@@ -83,6 +85,7 @@ import { TableWidgetModel } from './table-widget.model';
[resizable]="this.api.model.isResizable()"
[detailContent]="childDetail"
[syncWithUrl]="this.syncWithUrl"
+ (rowClicked)="this.onRowClicked($event)"
(selectionsChange)="this.onRowSelection($event)"
(columnConfigsChange)="this.onColumnsChange($event)"
>
@@ -93,6 +96,10 @@ import { TableWidgetModel } from './table-widget.model';
+
+
+
+
`
})
export class TableWidgetRendererComponent
@@ -107,6 +114,8 @@ export class TableWidgetRendererComponent
public columnConfigs$!: Observable;
public combinedFilters$!: Observable;
+ public selectedRows?: StatefulTableRow[] = [];
+
private readonly toggleFilterSubject: Subject = new BehaviorSubject([]);
private readonly searchFilterSubject: Subject = new BehaviorSubject([]);
private readonly selectFilterSubject: BehaviorSubject = new BehaviorSubject([]);
@@ -116,8 +125,6 @@ export class TableWidgetRendererComponent
>({});
public queryProperties$: Observable> = this.queryPropertiesSubject.asObservable();
- private selectedRowInteractionHandler?: InteractionHandler;
-
public constructor(
@Inject(RENDERER_API) api: RendererApi,
changeDetectorRef: ChangeDetectorRef,
@@ -152,6 +159,11 @@ export class TableWidgetRendererComponent
public getChildModel = (row: TableRow): object | undefined => this.model.getChildModel(row);
+ public isCustomControlPresent = (): boolean => this.model.isCustomControlPresent();
+
+ public getCustomControlWidgetModel = (selectedRows?: TableRow[]): object | undefined =>
+ this.model.getCustomControlWidgetModel(selectedRows);
+
protected fetchData(): Observable | undefined> {
return this.model.getData().pipe(
startWith(undefined),
@@ -368,28 +380,30 @@ export class TableWidgetRendererComponent
}
}
+ public onRowClicked(row: StatefulTableRow): void {
+ this.getRowClickInteractionHandler(row)?.execute(row);
+ }
+
public onRowSelection(selections: StatefulTableRow[]): void {
- if (this.api.model.getSelectionMode() === TableSelectionMode.Single) {
- /**
- * Execute selection handler for single selection mode only
- */
- let selectedRow;
- if (selections.length > 0) {
- selectedRow = selections[0];
- this.selectedRowInteractionHandler = this.getInteractionHandler(selectedRow);
- }
-
- this.selectedRowInteractionHandler?.execute(selectedRow);
- }
+ this.selectedRows = selections;
+ /**
+ * Todo: Stich this with selection handlers
+ */
+ }
+
+ private getRowClickInteractionHandler(selectedRow: StatefulTableRow): InteractionHandler | undefined {
+ return this.getInteractionHandler(selectedRow, this.api.model.getRowClickHandlers());
}
- private getInteractionHandler(selectedRow: StatefulTableRow): InteractionHandler | undefined {
- const matchedSelectionHandlers = this.api.model
- .getRowSelectionHandlers(selectedRow)
- ?.filter(selectionModel => selectionModel.appliesToCurrentRowDepth(selectedRow.$$state.depth))
+ private getInteractionHandler(
+ row: StatefulTableRow,
+ rowHandlers: TableWidgetRowInteractionModel[] = []
+ ): InteractionHandler | undefined {
+ const matchedHandlers = rowHandlers
+ .filter(interactionModel => interactionModel.appliesToCurrentRowDepth(row.$$state.depth))
.sort((model1, model2) => model2.rowDepth - model1.rowDepth);
- return !isEmpty(matchedSelectionHandlers) ? matchedSelectionHandlers[0].handler : undefined;
+ return !isEmpty(matchedHandlers) ? matchedHandlers[0].handler : undefined;
}
private pickPersistColumnProperties(column: TableColumnConfig): Pick {
diff --git a/projects/observability/src/shared/dashboard/widgets/table/table-widget-view-toggle.model.ts b/projects/observability/src/shared/dashboard/widgets/table/table-widget-view-toggle.model.ts
index d8a1cd484..826d578dd 100644
--- a/projects/observability/src/shared/dashboard/widgets/table/table-widget-view-toggle.model.ts
+++ b/projects/observability/src/shared/dashboard/widgets/table/table-widget-view-toggle.model.ts
@@ -1,9 +1,9 @@
-import { TableDataSource, TableRow } from '@hypertrace/components';
+import { TableDataSource, TableRow, TableSelectionMode } from '@hypertrace/components';
import { ArrayPropertyTypeInstance } from '@hypertrace/dashboards';
import { ARRAY_PROPERTY, Model, ModelApi, ModelOnInit, ModelProperty, ModelPropertyType } from '@hypertrace/hyperdash';
import { ModelInject, MODEL_API } from '@hypertrace/hyperdash-angular';
import { NEVER, Observable } from 'rxjs';
-import { TableWidgetRowSelectionModel } from './selections/table-widget-row-selection.model';
+import { TableWidgetRowInteractionModel } from './selections/table-widget-row-interaction.model';
import { SpecificationBackedTableColumnDef } from './table-widget-column.model';
import { TableWidgetControlCheckboxOptionModel } from './table-widget-control-checkbox-option.model';
import { TableWidgetControlSelectOptionModel } from './table-widget-control-select-option.model';
@@ -73,7 +73,23 @@ export class TableWidgetViewToggleModel extends TableWidgetModel implements Mode
return this.delegateModel && this.delegateModel?.getChildModel(row);
}
- public getRowSelectionHandlers(row: TableRow): TableWidgetRowSelectionModel[] {
+ public getSelectionMode(): TableSelectionMode {
+ return this.delegateModel?.getSelectionMode() ?? TableSelectionMode.Single;
+ }
+
+ public isCustomControlPresent(): boolean {
+ return this.delegateModel?.isCustomControlPresent() ?? false;
+ }
+
+ public getCustomControlWidgetModel(selectedRows?: TableRow[]): object | undefined {
+ return this.delegateModel && this.delegateModel?.getCustomControlWidgetModel(selectedRows);
+ }
+
+ public getRowClickHandlers(): TableWidgetRowInteractionModel[] {
+ return this.delegateModel?.getRowClickHandlers() ?? [];
+ }
+
+ public getRowSelectionHandlers(row: TableRow): TableWidgetRowInteractionModel[] {
return this.delegateModel && this.delegateModel?.getRowSelectionHandlers(row).length > 0
? this.delegateModel?.getRowSelectionHandlers(row)
: [];
diff --git a/projects/observability/src/shared/dashboard/widgets/table/table-widget.model.ts b/projects/observability/src/shared/dashboard/widgets/table/table-widget.model.ts
index e6b7f8efe..7a6c89958 100644
--- a/projects/observability/src/shared/dashboard/widgets/table/table-widget.model.ts
+++ b/projects/observability/src/shared/dashboard/widgets/table/table-widget.model.ts
@@ -18,7 +18,7 @@ import { ModelInject } from '@hypertrace/hyperdash-angular';
import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { InteractionHandler } from '../../interaction/interaction-handler';
-import { TableWidgetRowSelectionModel } from './selections/table-widget-row-selection.model';
+import { TableWidgetRowInteractionModel } from './selections/table-widget-row-interaction.model';
import { TableWidgetColumnsService } from './services/table-widget-columns.service';
import { TableWidgetBaseModel } from './table-widget-base.model';
import { SpecificationBackedTableColumnDef, TableWidgetColumnModel } from './table-widget-column.model';
@@ -63,19 +63,33 @@ export class TableWidgetModel extends TableWidgetBaseModel {
})
public selectionHandler?: InteractionHandler;
+ @ModelProperty({
+ key: 'row-click-handlers',
+ displayName: 'Row click Handlers',
+ // tslint:disable-next-line: no-object-literal-type-assertion
+ type: {
+ key: ARRAY_PROPERTY.type,
+ subtype: {
+ key: ModelPropertyType.TYPE,
+ defaultModelClass: TableWidgetRowInteractionModel
+ }
+ } as ArrayPropertyTypeInstance
+ })
+ public rowClickHandlers?: TableWidgetRowInteractionModel[];
+
@ModelProperty({
key: 'row-selection-handlers',
- displayName: 'Row selection Handlers',
+ displayName: 'Row selection handlers',
// tslint:disable-next-line: no-object-literal-type-assertion
type: {
key: ARRAY_PROPERTY.type,
subtype: {
key: ModelPropertyType.TYPE,
- defaultModelClass: TableWidgetRowSelectionModel
+ defaultModelClass: TableWidgetRowInteractionModel
}
} as ArrayPropertyTypeInstance
})
- public rowSelectionHandlers?: TableWidgetRowSelectionModel[];
+ public rowSelectionHandlers?: TableWidgetRowInteractionModel[];
@ModelProperty({
key: 'child-template',
@@ -83,6 +97,12 @@ export class TableWidgetModel extends TableWidgetBaseModel {
})
public childTemplate?: ModelJson;
+ @ModelProperty({
+ key: 'custom-control-widget',
+ type: ModelTemplatePropertyType.TYPE
+ })
+ public customControlModelJson?: ModelJson;
+
@ModelProperty({
key: 'fetchEditableColumns',
displayName: 'Query for additional columns not provided',
@@ -97,10 +117,33 @@ export class TableWidgetModel extends TableWidgetBaseModel {
return this.api.getData>();
}
- public getRowSelectionHandlers(_row: TableRow): TableWidgetRowSelectionModel[] {
+ public getRowClickHandlers(): TableWidgetRowInteractionModel[] {
+ return this.rowClickHandlers ?? [];
+ }
+
+ public getRowSelectionHandlers(_row: TableRow): TableWidgetRowInteractionModel[] {
return this.rowSelectionHandlers ?? [];
}
+ public getSelectionMode(): TableSelectionMode {
+ return this.selectionMode;
+ }
+
+ public isCustomControlPresent(): boolean {
+ return !!this.customControlModelJson;
+ }
+
+ public getCustomControlWidgetModel(selectedRows?: TableRow[]): object | undefined {
+ if (this.customControlModelJson) {
+ const childModel = this.api.createChild