Skip to content

HParams: Create filter dialog component #6493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tensorboard/webapp/hparams/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ tf_ts_library(
deps = [
"//tensorboard/webapp/runs/data_source",
"//tensorboard/webapp/runs/data_source:backend_types",
"//tensorboard/webapp/widgets/data_table:types",
],
)

Expand Down
24 changes: 2 additions & 22 deletions tensorboard/webapp/hparams/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
import {
HparamValue,
MetricValue,
DiscreteHparamValues,
DomainType,
HparamSpec,
MetricSpec,
} from '../runs/data_source/runs_data_source_types';

export {DiscreteFilter, IntervalFilter} from '../widgets/data_table/types';

export {
DatasetType,
DiscreteHparamValue,
Expand Down Expand Up @@ -50,21 +48,3 @@ export interface HparamAndMetricSpec {
hparams: HparamSpec[];
metrics: MetricSpec[];
}

export interface DiscreteFilter {
type: DomainType.DISCRETE;
includeUndefined: boolean;
possibleValues: DiscreteHparamValues;
// Subset of `possibleValues`
filterValues: DiscreteHparamValues;
}

export interface IntervalFilter {
type: DomainType.INTERVAL;
includeUndefined: boolean;
minValue: number;
maxValue: number;
// Filter values have to be in between min and max values (inclusive).
filterLowerValue: number;
filterUpperValue: number;
}
1 change: 1 addition & 0 deletions tensorboard/webapp/runs/data_source/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ tf_ng_module(
deps = [
":backend_types",
"//tensorboard/webapp/webapp_data_source:http_client",
"//tensorboard/webapp/widgets/data_table:types",
"@npm//@angular/core",
"@npm//rxjs",
],
Expand Down
2 changes: 1 addition & 1 deletion tensorboard/webapp/runs/data_source/runs_data_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import {
TBHttpClient,
} from '../../webapp_data_source/tb_http_client';
import * as backendTypes from './runs_backend_types';
import {DomainType} from '../../widgets/data_table/types';
import {
Domain,
DomainType,
HparamsAndMetadata,
HparamSpec,
HparamValue,
Expand Down
8 changes: 3 additions & 5 deletions tensorboard/webapp/runs/data_source/runs_data_source_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import * as backendTypes from './runs_backend_types';

import {DomainType} from '../../widgets/data_table/types';
export {DomainType} from '../../widgets/data_table/types';

export {
BackendHparamsValueType as HparamsValueType,
DatasetType,
Expand All @@ -41,11 +44,6 @@ export interface RunToHparamsAndMetrics {
};
}

export enum DomainType {
DISCRETE,
INTERVAL,
}

interface IntervalDomain {
type: DomainType.INTERVAL;
minValue: number;
Expand Down
40 changes: 40 additions & 0 deletions tensorboard/webapp/widgets/data_table/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ tf_sass_binary(
],
)

tf_sass_binary(
name = "filter_dialog_styles",
src = "filter_dialog_component.scss",
strict_deps = False,
deps = [
"//tensorboard/webapp:angular_material_sass_deps",
"//tensorboard/webapp/theme",
],
)

tf_ng_module(
name = "data_table",
srcs = [
Expand All @@ -71,12 +81,14 @@ tf_ng_module(
deps = [
":column_selector",
":data_table_header",
":filter_dialog",
":types",
"//tensorboard/webapp/angular:expect_angular_material_button",
"//tensorboard/webapp/angular:expect_angular_material_icon",
"//tensorboard/webapp/metrics/views/card_renderer:scalar_card_types",
"//tensorboard/webapp/widgets/custom_modal",
"//tensorboard/webapp/widgets/line_chart_v2/lib:formatter",
"//tensorboard/webapp/widgets/range_input:types",
"@npm//@angular/common",
"@npm//@angular/core",
"@npm//rxjs",
Expand Down Expand Up @@ -123,6 +135,27 @@ tf_ng_module(
],
)

tf_ng_module(
name = "filter_dialog",
srcs = [
"filter_dialog_component.ts",
"filter_dialog_module.ts",
],
assets = [
"filter_dialog_component.ng.html",
":filter_dialog_styles",
],
deps = [
":types",
"//tensorboard/webapp/angular:expect_angular_material_checkbox",
"//tensorboard/webapp/widgets/filter_input",
"//tensorboard/webapp/widgets/range_input",
"//tensorboard/webapp/widgets/range_input:types",
"@npm//@angular/common",
"@npm//@angular/core",
],
)

tf_ts_library(
name = "types",
srcs = [
Expand All @@ -137,16 +170,23 @@ tf_ts_library(
"column_selector_test.ts",
"content_cell_component_test.ts",
"data_table_test.ts",
"filter_dialog_test.ts",
"header_cell_component_test.ts",
],
deps = [
":column_selector",
":data_table",
":filter_dialog",
":types",
"//tensorboard/webapp/angular:expect_angular_cdk_testing",
"//tensorboard/webapp/angular:expect_angular_cdk_testing_testbed",
"//tensorboard/webapp/angular:expect_angular_core_testing",
"//tensorboard/webapp/angular:expect_angular_material_checkbox",
"//tensorboard/webapp/angular:expect_angular_platform_browser_animations",
"//tensorboard/webapp/testing:mat_icon",
"//tensorboard/webapp/widgets/custom_modal",
"//tensorboard/webapp/widgets/range_input",
"//tensorboard/webapp/widgets/range_input:types",
"@npm//@angular/core",
"@npm//@angular/forms",
"@npm//@angular/platform-browser",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!--
@license
Copyright 2023 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="filter-dialog">
<div
*ngIf="filter.type === DomainType.DISCRETE"
class="discrete-filters-area"
>
<tb-filter-input
*ngIf="filter.possibleValues"
[value]="discreteValueFilter"
(keyup)="discreteValueKeyUp($event)"
placeholder="Filter Discrete Values (regex)"
></tb-filter-input>
<div *ngIf="!getPossibleValues().length" class="no-matches">
No Matching Values
</div>
<div
*ngFor="let value of getPossibleValues(); index"
(click)="$event.stopPropagation()"
>
<mat-checkbox
[checked]="filter.filterValues.includes(value)"
(change)="discreteFilterChanged.emit(value)"
><span>{{ value }}</span></mat-checkbox
>
</div>
</div>

<div
*ngIf="filter.type === DomainType.INTERVAL"
(click)="$event.stopPropagation()"
class="range-input-container"
disableRipple
>
<tb-range-input
[min]="filter.minValue"
[max]="filter.maxValue"
[lowerValue]="filter.filterLowerValue"
[upperValue]="filter.filterUpperValue"
(rangeValuesChanged)="intervalFilterChanged.emit($event)"
></tb-range-input>
</div>

<mat-checkbox
[checked]="filter.includeUndefined"
(change)="includeUndefinedToggled.emit()"
>Include Undefined</mat-checkbox
>
</div>
44 changes: 44 additions & 0 deletions tensorboard/webapp/widgets/data_table/filter_dialog_component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
@use '@angular/material' as mat;
@import 'tensorboard/webapp/theme/tb_theme';

.filter-dialog {
padding: 8px;
border-radius: 4px;
border: 1px solid;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
border-color: mat.get-color-from-palette($tb-foreground, border);
background-color: mat.get-color-from-palette($tb-background, background);

@include tb-dark-theme {
border-color: mat.get-color-from-palette($tb-dark-foreground, border);
background-color: mat.get-color-from-palette(
$tb-dark-background,
'background'
);
}

.discrete-filters-area {
max-height: 100px;
overflow-y: auto;
}

.no-matches {
// 12px is the width of the checkbox so the text should match the
// indentation of the selectable filters.
padding: 8px 12px;
}
}
56 changes: 56 additions & 0 deletions tensorboard/webapp/widgets/data_table/filter_dialog_component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {
DomainType,
DiscreteFilter,
IntervalFilter,
DiscreteFilterValue,
} from './types';
import {RangeValues} from '../range_input/types';

@Component({
selector: 'tb-data-table-filter',
templateUrl: 'filter_dialog_component.ng.html',
styleUrls: ['filter_dialog_component.css'],
})
export class FilterDialog {
DomainType = DomainType;

discreteValueFilter: string = '';

@Input() filter!: DiscreteFilter | IntervalFilter;

@Output() discreteFilterChanged = new EventEmitter<DiscreteFilterValue>();

@Output() intervalFilterChanged = new EventEmitter<RangeValues>();

@Output() includeUndefinedToggled = new EventEmitter<void>();

getPossibleValues() {
const values: DiscreteFilterValue[] =
(this.filter as DiscreteFilter).possibleValues ?? [];
if (!this.discreteValueFilter) {
return values;
}
return values.filter((value) =>
value.toString().match(this.discreteValueFilter)
);
}

discreteValueKeyUp(event: KeyboardEvent) {
this.discreteValueFilter = (event.target! as HTMLInputElement).value;
}
}
33 changes: 33 additions & 0 deletions tensorboard/webapp/widgets/data_table/filter_dialog_module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

import {NgModule} from '@angular/core';
import {FilterDialog} from './filter_dialog_component';
import {CommonModule} from '@angular/common';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {RangeInputModule} from '../range_input/range_input_module';
import {FilterInputModule} from '../filter_input/filter_input_module';

@NgModule({
declarations: [FilterDialog],
imports: [
CommonModule,
MatCheckboxModule,
FilterInputModule,
RangeInputModule,
],
exports: [FilterDialog],
})
export class FilterDialogModule {}
Loading