From 6b15f787ee2cafbb4f8f6f85b9ceefe179c53c46 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Wed, 5 May 2021 12:08:01 -0700 Subject: [PATCH 1/5] feat: change table controls from select to multi-select dropdown filters --- .../common/src/utilities/lang/lang-utils.ts | 4 + .../src/table/controls/table-controls-api.ts | 89 +++++++++++-------- .../controls/table-controls.component.ts | 31 ++++--- ...table-control-options-data-source.model.ts | 8 +- ...le-widget-control-checkbox-option.model.ts | 14 +-- ...able-widget-control-select-option.model.ts | 26 +++++- .../table/table-widget-control.model.ts | 22 ++--- .../table/table-widget-renderer.component.ts | 53 +++++------ .../apis/endpoints/endpoint-list.dashboard.ts | 3 +- ...ies-attribute-options-data-source.model.ts | 26 ++---- 10 files changed, 149 insertions(+), 127 deletions(-) diff --git a/projects/common/src/utilities/lang/lang-utils.ts b/projects/common/src/utilities/lang/lang-utils.ts index da62acc3b..3c4f80514 100644 --- a/projects/common/src/utilities/lang/lang-utils.ts +++ b/projects/common/src/utilities/lang/lang-utils.ts @@ -35,3 +35,7 @@ const ignoreFunctions = (first: unknown, second: unknown) => { // tslint:disable-next-line: no-null-undefined-union export const isNonEmptyString = (str: string | undefined | null): str is string => str !== undefined && str !== null && str !== ''; + +export const hasOwnProperty = (obj: X, prop: Y): obj is X & Record => + // Since Typescript doesn't know how to type guard native hasOwnProperty, we wrap it here. + obj.hasOwnProperty(prop); diff --git a/projects/components/src/table/controls/table-controls-api.ts b/projects/components/src/table/controls/table-controls-api.ts index cde3e74e6..1ff4761d5 100644 --- a/projects/components/src/table/controls/table-controls-api.ts +++ b/projects/components/src/table/controls/table-controls-api.ts @@ -1,18 +1,53 @@ import { Dictionary } from '@hypertrace/common'; -import { SelectOption } from '../../select/select-option'; +import { FilterOperator } from '../../filtering/filter/filter-operators'; import { TableFilter } from '../table-api'; +export const enum TableControlOptionType { + Filter = 'filter', + Property = 'property', + Unset = 'unset' +} + +export type TableControlOption = TableUnsetControlOption | TableFilterControlOption | TablePropertyControlOption; + +export type TableFilterControlOption = { + type: TableControlOptionType.Filter; + label: string; + metaValue: TableFilter; +}; + +export type TableUnsetControlOption = { + type: TableControlOptionType.Unset; + label: string; + metaValue: string; +}; + +export type TablePropertyControlOption = { + type: TableControlOptionType.Property; + label: string; + metaValue: Dictionary; +}; + +/* + * Select Control + */ + export interface SelectControl { - placeholder?: string; - default?: SelectOption; - options: SelectOption[]; + placeholder: string; + options: TableSelectControlOption[]; } export interface SelectChange { select: SelectControl; - value: TableControlOption; + values: TableSelectControlOption[]; } +export type TableSelectControlOption = TableFilterControlOption; + +/* + * Checkbox Control + */ + export interface CheckboxControl { label: string; value: boolean; @@ -21,38 +56,22 @@ export interface CheckboxControl { export interface CheckboxChange { checkbox: CheckboxControl; - option: TableControlOption; -} - -export const enum TableControlOptionType { - Filter = 'filter', - Property = 'property', - UnsetFilter = 'unset-filter' + option: TableCheckboxControlOption; } -export type TableControlOption = - | TableUnsetFilterControlOption - | TableFilterControlOption - | TablePropertyControlOption; - -interface TableControlOptionBase { - value?: T; -} -export interface TableUnsetFilterControlOption extends TableControlOptionBase { - type: TableControlOptionType.UnsetFilter; - metaValue: string; -} +export type TableCheckboxControlOption = TableControlOption & { + value: T; +}; -export interface TableFilterControlOption extends TableControlOptionBase { - type: TableControlOptionType.Filter; - metaValue: TableFilter; -} - -export interface TablePropertyControlOption extends TableControlOptionBase { - type: TableControlOptionType.Property; - metaValue: Dictionary; -} +export type TableCheckboxOptions = [TableCheckboxControlOption, TableCheckboxControlOption]; -export type TableCheckboxControlOption = TableControlOption & { label: string }; +/* + * Util + */ -export type TableCheckboxOptions = [TableCheckboxControlOption, TableCheckboxControlOption]; +export const toInFilter = (tableFilters: TableFilter[]): TableFilter => + tableFilters.reduce((previousValue, currentValue) => ({ + field: previousValue.field, + operator: FilterOperator.In, + value: [...(Array.isArray(previousValue.value) ? previousValue.value : [previousValue.value]), currentValue.value] + })); diff --git a/projects/components/src/table/controls/table-controls.component.ts b/projects/components/src/table/controls/table-controls.component.ts index 7bae09da2..41ff9b3a4 100644 --- a/projects/components/src/table/controls/table-controls.component.ts +++ b/projects/components/src/table/controls/table-controls.component.ts @@ -14,9 +14,15 @@ import { Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { IconSize } from '../../icon/icon-size'; import { MultiSelectJustify } from '../../multi-select/multi-select-justify'; -import { TriggerLabelDisplayMode } from '../../multi-select/multi-select.component'; +import { MultiSelectSearchMode, TriggerLabelDisplayMode } from '../../multi-select/multi-select.component'; import { ToggleItem } from '../../toggle-group/toggle-item'; -import { CheckboxChange, CheckboxControl, SelectChange, SelectControl, TableControlOption } from './table-controls-api'; +import { + CheckboxChange, + CheckboxControl, + SelectChange, + SelectControl, + TableSelectControlOption +} from './table-controls-api'; @Component({ selector: 'ht-table-controls', @@ -36,19 +42,20 @@ import { CheckboxChange, CheckboxControl, SelectChange, SelectControl, TableCont > - - + @@ -179,15 +186,15 @@ export class TableControlsComponent implements OnChanges { this.searchDebounceSubject.next(text); } - public onSelectChange(select: SelectControl, value: TableControlOption): void { + public onMultiSelectChange(select: SelectControl, selections: TableSelectControlOption[]): void { this.selectChange.emit({ select: select, - value: value + values: selections }); } - public onCheckboxChange(checks: string[]): void { - const diff = this.checkboxDiffer?.diff(checks); + public onCheckboxChange(checked: string[]): void { + const diff = this.checkboxDiffer?.diff(checked); if (!diff) { return; } @@ -216,7 +223,7 @@ export class TableControlsComponent implements OnChanges { } }); - this.checkboxSelections = checks; + this.checkboxSelections = checked; this.checkboxDiffer?.diff(this.checkboxSelections); } diff --git a/projects/distributed-tracing/src/shared/dashboard/data/graphql/graphql-table-control-options-data-source.model.ts b/projects/distributed-tracing/src/shared/dashboard/data/graphql/graphql-table-control-options-data-source.model.ts index 21e87169c..a0ca6bbcf 100644 --- a/projects/distributed-tracing/src/shared/dashboard/data/graphql/graphql-table-control-options-data-source.model.ts +++ b/projects/distributed-tracing/src/shared/dashboard/data/graphql/graphql-table-control-options-data-source.model.ts @@ -1,9 +1,7 @@ +import { TableControlOption } from '@hypertrace/components'; import { Observable } from 'rxjs'; -import { LabeledTableControlOption } from '../../widgets/table/table-widget-control.model'; import { GraphQlDataSourceModel } from './graphql-data-source.model'; -export abstract class GraphqlTableControlOptionsDataSourceModel extends GraphQlDataSourceModel< - LabeledTableControlOption[] -> { - public abstract getData(): Observable; +export abstract class GraphqlTableControlOptionsDataSourceModel extends GraphQlDataSourceModel { + public abstract getData(): Observable; } diff --git a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control-checkbox-option.model.ts b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control-checkbox-option.model.ts index 3e57ebcb4..7f887bec5 100644 --- a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control-checkbox-option.model.ts +++ b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control-checkbox-option.model.ts @@ -1,4 +1,5 @@ -import { TableCheckboxOptions, TableControlOption } from '@hypertrace/components'; +import { hasOwnProperty } from '@hypertrace/common'; +import { TableCheckboxControlOption, TableCheckboxOptions, TableControlOption } from '@hypertrace/components'; import { BOOLEAN_PROPERTY, Model, ModelProperty } from '@hypertrace/hyperdash'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -7,7 +8,7 @@ import { TableWidgetControlModel } from './table-widget-control.model'; @Model({ type: 'table-widget-checkbox-option' }) -export class TableWidgetControlCheckboxOptionModel extends TableWidgetControlModel { +export class TableWidgetControlCheckboxOptionModel extends TableWidgetControlModel { @ModelProperty({ key: 'checked', displayName: 'Checked', @@ -19,7 +20,7 @@ export class TableWidgetControlCheckboxOptionModel extends TableWidgetControlMod public getOptions(): Observable { return super.getOptions().pipe( map(options => { - if (!this.isValidCheckboxControlOption(options)) { + if (!this.isValidCheckboxControlOptions(options)) { throw Error(`Invalid table widget checkbox data source for options '${JSON.stringify(options)}'`); } @@ -31,7 +32,10 @@ export class TableWidgetControlCheckboxOptionModel extends TableWidgetControlMod ); } - private isValidCheckboxControlOption(options: TableControlOption[]): options is TableCheckboxOptions { - return options.length === 2 && options.every(option => typeof option.value === 'boolean'); + private isValidCheckboxControlOptions(options: TableControlOption[]): options is TableCheckboxOptions { + return ( + options.length === 2 && + options.every(option => hasOwnProperty(option, 'value') && typeof option.value === 'boolean') + ); } } diff --git a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control-select-option.model.ts b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control-select-option.model.ts index 0e900581d..5022a54dc 100644 --- a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control-select-option.model.ts +++ b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control-select-option.model.ts @@ -1,14 +1,34 @@ +import { TableControlOption, TableControlOptionType, TableSelectControlOption } from '@hypertrace/components'; import { Model, ModelProperty, STRING_PROPERTY } from '@hypertrace/hyperdash'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { TableWidgetControlModel } from './table-widget-control.model'; @Model({ type: 'table-widget-select-option' }) -export class TableWidgetControlSelectOptionModel extends TableWidgetControlModel { +export class TableWidgetControlSelectOptionModel extends TableWidgetControlModel { @ModelProperty({ key: 'placeholder', displayName: 'Placeholder', - type: STRING_PROPERTY.type + type: STRING_PROPERTY.type, + required: true }) - public placeholder?: string; + public placeholder!: string; + + public getOptions(): Observable { + return super.getOptions().pipe( + map(options => { + if (!this.isValidSelectControlOptions(options)) { + throw Error(`Invalid table widget select data source for options '${JSON.stringify(options)}'`); + } + + return options; + }) + ); + } + + private isValidSelectControlOptions(options: TableControlOption[]): options is TableSelectControlOption[] { + return options.every(option => option.type === TableControlOptionType.Filter); + } } diff --git a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control.model.ts b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control.model.ts index 0931a4437..f96e00fb0 100644 --- a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control.model.ts +++ b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-control.model.ts @@ -5,7 +5,7 @@ import { uniqWith } from 'lodash-es'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -export abstract class TableWidgetControlModel { +export abstract class TableWidgetControlModel { @ModelProperty({ key: 'sort', type: BOOLEAN_PROPERTY.type @@ -29,24 +29,24 @@ export abstract class TableWidgetControlModel { @ModelInject(MODEL_API) protected readonly api!: ModelApi; - public getOptions(): Observable { - return this.api.getData().pipe( - map(options => (this.uniqueValues ? this.filterUniqueValues(options) : options)), + public getOptions(): Observable { + return this.api.getData().pipe( + map(options => (this.uniqueValues ? this.filterUnique(options) : options)), map(options => (this.sort ? this.applySort(options) : options)) ); } - private filterUniqueValues(options: LabeledTableControlOption[]): LabeledTableControlOption[] { - return uniqWith(options, (a, b) => a.value === b.value); + protected filterUnique(options: T[]): T[] { + return uniqWith(options, (a, b) => a.label === b.label); } - private applySort(options: LabeledTableControlOption[]): LabeledTableControlOption[] { + protected applySort(options: T[]): T[] { return options.sort((a, b) => { // Unset option always at the top - if (a.type === TableControlOptionType.UnsetFilter) { + if (a.type === TableControlOptionType.Unset) { return -1; } - if (b.type === TableControlOptionType.UnsetFilter) { + if (b.type === TableControlOptionType.Unset) { return 1; } @@ -54,7 +54,3 @@ export abstract class TableWidgetControlModel { }); } } - -export type LabeledTableControlOption = TableControlOption & { - label: string; -}; diff --git a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts index 23a7ed8d1..3ebff3334 100644 --- a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts +++ b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts @@ -16,13 +16,16 @@ import { SelectControl, StatefulTableRow, TableColumnConfig, + TableControlOption, TableControlOptionType, TableDataSource, TableFilter, + TableFilterControlOption, TableRow, TableSelectionMode, TableStyle, - ToggleItem + ToggleItem, + toInFilter } from '@hypertrace/components'; import { WidgetRenderer } from '@hypertrace/dashboards'; import { Renderer } from '@hypertrace/hyperdash'; @@ -35,7 +38,6 @@ import { MetadataService } from '../../../services/metadata/metadata.service'; import { InteractionHandler } from '../../interaction/interaction-handler'; import { TableWidgetBaseModel } from './table-widget-base.model'; import { SpecificationBackedTableColumnDef } from './table-widget-column.model'; -import { LabeledTableControlOption } from './table-widget-control.model'; import { TableWidgetViewToggleModel } from './table-widget-view-toggle.model'; import { TableWidgetModel } from './table-widget.model'; @@ -171,17 +173,10 @@ export class TableWidgetRendererComponent // Fetch the values for the selectFilter dropdown selectControlModel.getOptions().pipe( take(1), - map(options => { - const selectOptions = options.map(option => ({ - label: option.label, - value: option - })); - - return { - placeholder: selectControlModel.placeholder, - options: selectOptions - }; - }) + map(options => ({ + placeholder: selectControlModel.placeholder, + options: options + })) ) ) ); @@ -277,21 +272,15 @@ export class TableWidgetRendererComponent } public onSelectChange(changed: SelectChange): void { - switch (changed.value.type) { - case TableControlOptionType.UnsetFilter: - this.selectFilterSubject.next( - this.selectFilterSubject.getValue().filter(existingFilter => existingFilter.field !== changed.value.metaValue) - ); - break; - case TableControlOptionType.Filter: - this.selectFilterSubject.next(this.mergeFilters(changed.value.metaValue)); - break; - case TableControlOptionType.Property: - this.queryPropertiesSubject.next(this.mergeQueryProperties(changed.value.metaValue)); - break; - default: - assertUnreachable(changed.value); + if (changed.values.length === 0) { + // tslint:disable-next-line: no-void-expression + return this.selectFilterSubject.next(this.removeFilters(changed.select.options[0].metaValue.field)); } + + const tableFilters: TableFilter[] = changed.values.map((option: TableFilterControlOption) => option.metaValue); + + // tslint:disable-next-line: no-void-expression + return this.selectFilterSubject.next(this.mergeFilters(toInFilter(tableFilters))); } public onCheckboxChange(changed: CheckboxChange): void { @@ -302,7 +291,7 @@ export class TableWidgetRendererComponent case TableControlOptionType.Filter: this.selectFilterSubject.next(this.mergeFilters(changed.option.metaValue)); break; - case TableControlOptionType.UnsetFilter: + case TableControlOptionType.Unset: this.selectFilterSubject.next(this.removeFilters(changed.option.metaValue)); break; default: @@ -317,8 +306,8 @@ export class TableWidgetRendererComponent take(1), map(options => { options.forEach(option => { - if (this.isLabeledOptionMatch(option, changed.option as LabeledTableControlOption)) { - checkboxControlModel.checked = changed.option.value === true; + if (this.isLabeledOptionMatch(option, changed.option)) { + checkboxControlModel.checked = changed.option.value; } }); @@ -333,8 +322,8 @@ export class TableWidgetRendererComponent ); } - private isLabeledOptionMatch(option1: LabeledTableControlOption, option2: LabeledTableControlOption): boolean { - return option1.label === option2.label && option1.value === option2.value; + private isLabeledOptionMatch(option1: TableControlOption, option2: TableControlOption): boolean { + return option1.label === option2.label; } public onSearchChange(text: string): void { diff --git a/projects/observability/src/pages/apis/endpoints/endpoint-list.dashboard.ts b/projects/observability/src/pages/apis/endpoints/endpoint-list.dashboard.ts index b7cbcf2bd..80df7bc97 100644 --- a/projects/observability/src/pages/apis/endpoints/endpoint-list.dashboard.ts +++ b/projects/observability/src/pages/apis/endpoints/endpoint-list.dashboard.ts @@ -26,10 +26,9 @@ export const endpointListDashboard: DashboardDefaultConfiguration = { { type: 'table-widget-select-option', 'unique-values': true, - placeholder: 'All Services', + placeholder: 'Services', data: { type: 'entities-attribute-options-data-source', - 'unset-label': 'All Services', // Use API so we can inherit API filters entity: ObservabilityEntityType.Api, attribute: { diff --git a/projects/observability/src/shared/dashboard/data/graphql/entity/attribute/entities-attribute-options-data-source.model.ts b/projects/observability/src/shared/dashboard/data/graphql/entity/attribute/entities-attribute-options-data-source.model.ts index 086dc9232..c4a13ca65 100644 --- a/projects/observability/src/shared/dashboard/data/graphql/entity/attribute/entities-attribute-options-data-source.model.ts +++ b/projects/observability/src/shared/dashboard/data/graphql/entity/attribute/entities-attribute-options-data-source.model.ts @@ -1,6 +1,5 @@ -import { FilterOperator, TableControlOptionType } from '@hypertrace/components'; -import { LabeledTableControlOption } from '@hypertrace/distributed-tracing'; -import { Model, ModelProperty, STRING_PROPERTY } from '@hypertrace/hyperdash'; +import { FilterOperator, TableControlOptionType, TableSelectControlOption } from '@hypertrace/components'; +import { Model } from '@hypertrace/hyperdash'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { EntitiesAttributeDataSourceModel } from './entities-attribute-data-source.model'; @@ -9,32 +8,19 @@ import { EntitiesAttributeDataSourceModel } from './entities-attribute-data-sour type: 'entities-attribute-options-data-source' }) export class EntitiesAttributeOptionsDataSourceModel extends EntitiesAttributeDataSourceModel { - @ModelProperty({ - key: 'unset-label', - type: STRING_PROPERTY.type, - required: true - }) - public unsetLabel: string = 'All'; - - public getData(): Observable { + public getData(): Observable { return super.getData().pipe( - map((values: unknown[]) => [ - { - type: TableControlOptionType.UnsetFilter, - label: this.unsetLabel, - metaValue: this.specification.name - }, - ...values.map(value => ({ + map((values: unknown[]) => + values.map(value => ({ type: TableControlOptionType.Filter as const, label: String(value), - value: value, metaValue: { field: this.specification.name, operator: FilterOperator.Equals, value: value } })) - ]) + ) ); } } From 04170b0c7f84391bdb91dbe3cf9d259c3b8eea9b Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Wed, 5 May 2021 14:21:18 -0700 Subject: [PATCH 2/5] test: fix --- .../src/table/controls/table-controls-api.ts | 12 +++++------ .../controls/table-controls.component.test.ts | 20 ++++++++++--------- .../controls/table-controls.component.ts | 1 + 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/projects/components/src/table/controls/table-controls-api.ts b/projects/components/src/table/controls/table-controls-api.ts index 1ff4761d5..d3107e29d 100644 --- a/projects/components/src/table/controls/table-controls-api.ts +++ b/projects/components/src/table/controls/table-controls-api.ts @@ -10,23 +10,23 @@ export const enum TableControlOptionType { export type TableControlOption = TableUnsetControlOption | TableFilterControlOption | TablePropertyControlOption; -export type TableFilterControlOption = { +export interface TableFilterControlOption { type: TableControlOptionType.Filter; label: string; metaValue: TableFilter; -}; +} -export type TableUnsetControlOption = { +export interface TableUnsetControlOption { type: TableControlOptionType.Unset; label: string; metaValue: string; -}; +} -export type TablePropertyControlOption = { +export interface TablePropertyControlOption { type: TableControlOptionType.Property; label: string; metaValue: Dictionary; -}; +} /* * Select Control diff --git a/projects/components/src/table/controls/table-controls.component.test.ts b/projects/components/src/table/controls/table-controls.component.test.ts index 7e1f03cfb..0a473b844 100644 --- a/projects/components/src/table/controls/table-controls.component.test.ts +++ b/projects/components/src/table/controls/table-controls.component.test.ts @@ -1,6 +1,6 @@ import { fakeAsync } from '@angular/core/testing'; import { SubscriptionLifecycle } from '@hypertrace/common'; -import { SelectComponent } from '@hypertrace/components'; +import { MultiSelectComponent } from '@hypertrace/components'; import { createHostFactory, mockProvider } from '@ngneat/spectator/jest'; import { MockComponent } from 'ng-mocks'; import { SearchBoxComponent } from '../../search-box/search-box.component'; @@ -14,7 +14,7 @@ describe('Table Controls component', () => { providers: [mockProvider(SubscriptionLifecycle)], declarations: [ MockComponent(SearchBoxComponent), - MockComponent(SelectComponent), + MockComponent(MultiSelectComponent), MockComponent(ToggleGroupComponent) ], template: ` @@ -67,11 +67,11 @@ describe('Table Controls component', () => { options: [ { label: 'first1', - value: 'first-1' + metaValue: 'first-1' }, { label: 'second1', - value: 'second-1' + metaValue: 'second-1' } ] } @@ -79,7 +79,7 @@ describe('Table Controls component', () => { } }); - expect(spectator.query(SelectComponent)?.placeholder).toEqual('test1'); + expect(spectator.query(MultiSelectComponent)?.placeholder).toEqual('test1'); }); test('should emit selection when selected', () => { @@ -106,10 +106,12 @@ describe('Table Controls component', () => { } }); - spectator.triggerEventHandler(SelectComponent, 'selectedChange', { - label: 'first1', - value: 'first-1' - }); + spectator.triggerEventHandler(MultiSelectComponent, 'selectedChange', [ + { + label: 'first1', + value: 'first-1' + } + ]); expect(onChangeSpy).toHaveBeenCalled(); }); diff --git a/projects/components/src/table/controls/table-controls.component.ts b/projects/components/src/table/controls/table-controls.component.ts index 41ff9b3a4..3e0c9d007 100644 --- a/projects/components/src/table/controls/table-controls.component.ts +++ b/projects/components/src/table/controls/table-controls.component.ts @@ -93,6 +93,7 @@ import { }) export class TableControlsComponent implements OnChanges { public readonly DEFAULT_SEARCH_PLACEHOLDER: string = 'Search...'; + @Input() public searchEnabled?: boolean; From d38bb9d794af65d567cb4ba27a162396651e6170 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Thu, 6 May 2021 11:27:01 -0700 Subject: [PATCH 3/5] feat: rename to add Table to beginning of API interfaces --- .../src/table/controls/table-controls-api.ts | 12 +++++----- .../controls/table-controls.component.ts | 22 +++++++++---------- .../table/table-widget-renderer.component.ts | 18 +++++++-------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/projects/components/src/table/controls/table-controls-api.ts b/projects/components/src/table/controls/table-controls-api.ts index d3107e29d..18f587043 100644 --- a/projects/components/src/table/controls/table-controls-api.ts +++ b/projects/components/src/table/controls/table-controls-api.ts @@ -32,13 +32,13 @@ export interface TablePropertyControlOption { * Select Control */ -export interface SelectControl { +export interface TableSelectControl { placeholder: string; options: TableSelectControlOption[]; } -export interface SelectChange { - select: SelectControl; +export interface TableSelectChange { + select: TableSelectControl; values: TableSelectControlOption[]; } @@ -48,14 +48,14 @@ export type TableSelectControlOption = TableFilterControlOption; * Checkbox Control */ -export interface CheckboxControl { +export interface TableCheckboxControl { label: string; value: boolean; options: TableCheckboxOptions; } -export interface CheckboxChange { - checkbox: CheckboxControl; +export interface TableCheckboxChange { + checkbox: TableCheckboxControl; option: TableCheckboxControlOption; } diff --git a/projects/components/src/table/controls/table-controls.component.ts b/projects/components/src/table/controls/table-controls.component.ts index 3e0c9d007..4fb0bc007 100644 --- a/projects/components/src/table/controls/table-controls.component.ts +++ b/projects/components/src/table/controls/table-controls.component.ts @@ -17,10 +17,10 @@ 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 { - CheckboxChange, - CheckboxControl, - SelectChange, - SelectControl, + TableCheckboxChange, + TableCheckboxControl, + TableSelectChange, + TableSelectControl, TableSelectControlOption } from './table-controls-api'; @@ -101,10 +101,10 @@ export class TableControlsComponent implements OnChanges { public searchPlaceholder?: string; @Input() - public selectControls?: SelectControl[] = []; + public selectControls?: TableSelectControl[] = []; @Input() - public checkboxControls?: CheckboxControl[] = []; + public checkboxControls?: TableCheckboxControl[] = []; @Input() public activeFilterItem?: ToggleItem; @@ -119,10 +119,10 @@ export class TableControlsComponent implements OnChanges { public readonly searchChange: EventEmitter = new EventEmitter(); @Output() - public readonly selectChange: EventEmitter = new EventEmitter(); + public readonly selectChange: EventEmitter = new EventEmitter(); @Output() - public readonly checkboxChange: EventEmitter = new EventEmitter(); + public readonly checkboxChange: EventEmitter = new EventEmitter(); @Output() public readonly viewChange: EventEmitter = new EventEmitter(); @@ -187,7 +187,7 @@ export class TableControlsComponent implements OnChanges { this.searchDebounceSubject.next(text); } - public onMultiSelectChange(select: SelectControl, selections: TableSelectControlOption[]): void { + public onMultiSelectChange(select: TableSelectControl, selections: TableSelectControlOption[]): void { this.selectChange.emit({ select: select, values: selections @@ -201,7 +201,7 @@ export class TableControlsComponent implements OnChanges { } diff.forEachAddedItem(addedItem => { - const found: CheckboxControl | undefined = this.checkboxControls?.find( + const found: TableCheckboxControl | undefined = this.checkboxControls?.find( control => control.label === addedItem.item ); if (found) { @@ -213,7 +213,7 @@ export class TableControlsComponent implements OnChanges { }); diff.forEachRemovedItem(removedItem => { - const found: CheckboxControl | undefined = this.checkboxControls?.find( + const found: TableCheckboxControl | undefined = this.checkboxControls?.find( control => control.label === removedItem.item ); if (found) { diff --git a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts index 3ebff3334..e3a472c52 100644 --- a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts +++ b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts @@ -8,12 +8,12 @@ import { PreferenceService } from '@hypertrace/common'; import { - CheckboxChange, - CheckboxControl, + TableCheckboxChange, + TableCheckboxControl, FilterAttribute, FilterOperator, - SelectChange, - SelectControl, + TableSelectChange, + TableSelectControl, StatefulTableRow, TableColumnConfig, TableControlOption, @@ -100,8 +100,8 @@ export class TableWidgetRendererComponent implements OnInit { public viewItems: ToggleItem[] = []; - public selectControls$!: Observable; - public checkboxControls$!: Observable; + public selectControls$!: Observable; + public checkboxControls$!: Observable; public metadata$!: Observable; public columnConfigs$!: Observable; @@ -198,7 +198,7 @@ export class TableWidgetRendererComponent ) ) ).pipe( - tap((checkboxControls: CheckboxControl[]) => { + tap((checkboxControls: TableCheckboxControl[]) => { // Apply initial values for checkboxes checkboxControls.forEach(checkboxControl => { this.onCheckboxChange({ @@ -271,7 +271,7 @@ export class TableWidgetRendererComponent ); } - public onSelectChange(changed: SelectChange): void { + public onSelectChange(changed: TableSelectChange): void { if (changed.values.length === 0) { // tslint:disable-next-line: no-void-expression return this.selectFilterSubject.next(this.removeFilters(changed.select.options[0].metaValue.field)); @@ -283,7 +283,7 @@ export class TableWidgetRendererComponent return this.selectFilterSubject.next(this.mergeFilters(toInFilter(tableFilters))); } - public onCheckboxChange(changed: CheckboxChange): void { + public onCheckboxChange(changed: TableCheckboxChange): void { switch (changed.option.type) { case TableControlOptionType.Property: this.queryPropertiesSubject.next(this.mergeQueryProperties(changed.option.metaValue)); From 2f620878e88a4c2a67bb08489b37e72e1087f588 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Thu, 6 May 2021 11:44:10 -0700 Subject: [PATCH 4/5] feat: add validation to toInFilter --- .../src/table/controls/table-controls-api.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/projects/components/src/table/controls/table-controls-api.ts b/projects/components/src/table/controls/table-controls-api.ts index 18f587043..433719ca6 100644 --- a/projects/components/src/table/controls/table-controls-api.ts +++ b/projects/components/src/table/controls/table-controls-api.ts @@ -70,8 +70,14 @@ export type TableCheckboxOptions = [TableCheckboxControlOption, TableCheck */ export const toInFilter = (tableFilters: TableFilter[]): TableFilter => - tableFilters.reduce((previousValue, currentValue) => ({ - field: previousValue.field, - operator: FilterOperator.In, - value: [...(Array.isArray(previousValue.value) ? previousValue.value : [previousValue.value]), currentValue.value] - })); + tableFilters.reduce((previousValue, currentValue) => { + if (currentValue.operator !== FilterOperator.Equals || previousValue.field !== currentValue.field) { + throw Error('Filters must all contain same field and use = operator'); + } + + return { + field: previousValue.field, + operator: FilterOperator.In, + value: [...(Array.isArray(previousValue.value) ? previousValue.value : [previousValue.value]), currentValue.value] + } + }); From 9f3e276b1905d1d502eca6b167dcd808833f7e5e Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Thu, 6 May 2021 11:58:50 -0700 Subject: [PATCH 5/5] style: lint --- .../components/src/table/controls/table-controls-api.ts | 4 ++-- .../widgets/table/table-widget-renderer.component.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/projects/components/src/table/controls/table-controls-api.ts b/projects/components/src/table/controls/table-controls-api.ts index 433719ca6..1b5cdc995 100644 --- a/projects/components/src/table/controls/table-controls-api.ts +++ b/projects/components/src/table/controls/table-controls-api.ts @@ -59,7 +59,7 @@ export interface TableCheckboxChange { option: TableCheckboxControlOption; } -export type TableCheckboxControlOption = TableControlOption & { +export type TableCheckboxControlOption = TableControlOption & { value: T; }; @@ -79,5 +79,5 @@ export const toInFilter = (tableFilters: TableFilter[]): TableFilter => field: previousValue.field, operator: FilterOperator.In, value: [...(Array.isArray(previousValue.value) ? previousValue.value : [previousValue.value]), currentValue.value] - } + }; }); diff --git a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts index e3a472c52..c94d1ed8e 100644 --- a/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts +++ b/projects/distributed-tracing/src/shared/dashboard/widgets/table/table-widget-renderer.component.ts @@ -8,13 +8,11 @@ import { PreferenceService } from '@hypertrace/common'; import { - TableCheckboxChange, - TableCheckboxControl, FilterAttribute, FilterOperator, - TableSelectChange, - TableSelectControl, StatefulTableRow, + TableCheckboxChange, + TableCheckboxControl, TableColumnConfig, TableControlOption, TableControlOptionType, @@ -22,6 +20,8 @@ import { TableFilter, TableFilterControlOption, TableRow, + TableSelectChange, + TableSelectControl, TableSelectionMode, TableStyle, ToggleItem,