Skip to content

Commit c1f4840

Browse files
committed
Unify styled filter inputs into reusable widget
1 parent 6eac3d3 commit c1f4840

15 files changed

+148
-26
lines changed

tensorboard/webapp/metrics/views/main_view/BUILD

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ tf_ng_module(
9595
"//tensorboard/webapp/types:ui",
9696
"//tensorboard/webapp/util:string",
9797
"//tensorboard/webapp/util:types",
98+
"//tensorboard/webapp/widgets/filter_input",
9899
"@npm//@angular/common",
99100
"@npm//@angular/core",
100101
"@npm//@ngrx/store",
@@ -117,7 +118,6 @@ tf_ts_library(
117118
"//tensorboard/webapp/angular:expect_angular_core_testing",
118119
"//tensorboard/webapp/angular:expect_angular_material_autocomplete",
119120
"//tensorboard/webapp/angular:expect_angular_material_button",
120-
"//tensorboard/webapp/angular:expect_angular_material_input",
121121
"//tensorboard/webapp/angular:expect_angular_platform_browser_animations",
122122
"//tensorboard/webapp/angular:expect_ngrx_store_testing",
123123
"//tensorboard/webapp/core/store",
@@ -131,6 +131,7 @@ tf_ts_library(
131131
"//tensorboard/webapp/testing:mat_icon",
132132
"//tensorboard/webapp/testing:material",
133133
"//tensorboard/webapp/types:ui",
134+
"//tensorboard/webapp/widgets/filter_input",
134135
"@npm//@angular/core",
135136
"@npm//@angular/platform-browser",
136137
"@npm//@ngrx/store",

tensorboard/webapp/metrics/views/main_view/filter_input_component.ng.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,13 @@
1616
-->
1717
<div class="tag-filter">
1818
<mat-icon svgIcon="search_24px"></mat-icon>
19-
<input
20-
matInput
21-
autocomplete="off"
19+
<tb-filter-input
2220
placeholder="Filter tags (regex)"
2321
[value]="regexFilterValue"
2422
(input)="onRegexFilterValueChange.emit($event.target.value)"
2523
(keyup)="onFilterKeyUp($event)"
2624
[matAutocomplete]="filterMatches"
27-
/>
25+
></tb-filter-input>
2826
<mat-icon
2927
svgIcon="error_24px"
3028
*ngIf="!isRegexFilterValid"

tensorboard/webapp/metrics/views/main_view/filter_input_component.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ mat-icon {
2525
margin-right: 5px;
2626
}
2727

28+
tb-filter-input {
29+
flex-grow: 1;
30+
}
31+
2832
:host {
2933
color: mat-color($tb-foreground, text);
3034
font-size: 13px;

tensorboard/webapp/metrics/views/main_view/filter_input_component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import {
2121
Output,
2222
ViewChild,
2323
} from '@angular/core';
24-
import {MatAutocompleteTrigger} from '@angular/material/autocomplete';
2524

2625
import {escapeForRegex} from '../../../util/string';
26+
import {FilterInputComponent} from '../../../widgets/filter_input/filter_input_component';
2727

2828
@Component({
2929
selector: 'metrics-tag-filter-component',
@@ -37,12 +37,12 @@ export class MetricsFilterInputComponent {
3737
@Input() completions!: string[];
3838
@Output() onRegexFilterValueChange = new EventEmitter<string>();
3939

40-
@ViewChild(MatAutocompleteTrigger, {static: true})
41-
autocompleteTrigger!: MatAutocompleteTrigger;
40+
@ViewChild(FilterInputComponent)
41+
filterInput!: FilterInputComponent;
4242

4343
onFilterKeyUp(event: KeyboardEvent) {
4444
if (event.key === 'Enter') {
45-
this.autocompleteTrigger.closePanel();
45+
this.filterInput.getAutocompleteTrigger().closePanel();
4646
}
4747
}
4848

tensorboard/webapp/metrics/views/main_view/filter_input_test.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ import {NO_ERRORS_SCHEMA} from '@angular/core';
1717
import {TestBed} from '@angular/core/testing';
1818
import {MatAutocompleteModule} from '@angular/material/autocomplete';
1919
import {MatButtonModule} from '@angular/material/button';
20-
import {MatInputModule} from '@angular/material/input';
2120
import {By} from '@angular/platform-browser';
2221
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
2322
import {Action, Store} from '@ngrx/store';
2423
import {MockStore, provideMockStore} from '@ngrx/store/testing';
2524

2625
import {State} from '../../../app_state';
27-
import {sendKeys} from '../../../testing/dom';
26+
import {KeyType, sendKey, sendKeys} from '../../../testing/dom';
2827
import {MatIconTestingModule} from '../../../testing/mat_icon_module';
2928
import {getAutocompleteOptions} from '../../../testing/material';
29+
import {FilterInputModule} from '../../../widgets/filter_input/filter_input_module';
3030
import {metricsTagFilterChanged} from '../../actions';
3131
import {PluginType} from '../../data_source';
3232
import * as selectors from '../../store/metrics_selectors';
@@ -47,7 +47,7 @@ describe('metrics filter input', () => {
4747
MatAutocompleteModule,
4848
MatButtonModule,
4949
MatIconTestingModule,
50-
MatInputModule,
50+
FilterInputModule,
5151
],
5252
declarations: [MetricsFilterInputComponent, MetricsFilterInputContainer],
5353
providers: [
@@ -239,9 +239,12 @@ describe('metrics filter input', () => {
239239
const options = getAutocompleteOptions(overlayContainer);
240240
expect(options.length).toBeGreaterThan(1);
241241

242-
input.nativeElement.dispatchEvent(
243-
new KeyboardEvent('keyup', {key: 'Enter'})
244-
);
242+
sendKey(fixture, input, {
243+
type: KeyType.SPECIAL,
244+
prevString: input.properties.value,
245+
key: 'Enter',
246+
startingCursorIndex: input.properties.selectionStart,
247+
});
245248

246249
const options2 = getAutocompleteOptions(overlayContainer);
247250
expect(options2.length).toBe(0);

tensorboard/webapp/metrics/views/main_view/main_view_module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {MatButtonModule} from '@angular/material/button';
2020
import {MatIconModule} from '@angular/material/icon';
2121
import {MatInputModule} from '@angular/material/input';
2222

23+
import {FilterInputModule} from '../../../widgets/filter_input/filter_input_module';
2324
import {CardRendererModule} from '../card_renderer/card_renderer_module';
2425
import {RightPaneModule} from '../right_pane/right_pane_module';
2526

@@ -55,6 +56,7 @@ import {PinnedViewContainer} from './pinned_view_container';
5556
imports: [
5657
CardRendererModule,
5758
CommonModule,
59+
FilterInputModule,
5860
MatAutocompleteModule,
5961
MatButtonModule,
6062
MatIconModule,

tensorboard/webapp/runs/views/runs_table/BUILD

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ tf_ng_module(
2929
"//tensorboard/webapp/angular:expect_angular_material_button",
3030
"//tensorboard/webapp/angular:expect_angular_material_checkbox",
3131
"//tensorboard/webapp/angular:expect_angular_material_icon",
32-
"//tensorboard/webapp/angular:expect_angular_material_input",
3332
"//tensorboard/webapp/angular:expect_angular_material_menu",
3433
"//tensorboard/webapp/angular:expect_angular_material_paginator",
3534
"//tensorboard/webapp/angular:expect_angular_material_progress_spinner",
@@ -43,6 +42,7 @@ tf_ng_module(
4342
"//tensorboard/webapp/runs/store:types",
4443
"//tensorboard/webapp/types",
4544
"//tensorboard/webapp/types:ui",
45+
"//tensorboard/webapp/widgets/filter_input",
4646
"//tensorboard/webapp/widgets/range_input",
4747
"@npm//@angular/common",
4848
"@npm//@angular/core",
@@ -86,6 +86,7 @@ tf_ts_library(
8686
"//tensorboard/webapp/testing:mat_icon",
8787
"//tensorboard/webapp/types",
8888
"//tensorboard/webapp/types:ui",
89+
"//tensorboard/webapp/widgets/filter_input",
8990
"//tensorboard/webapp/widgets/range_input",
9091
"@npm//@angular/core",
9192
"@npm//@angular/platform-browser",

tensorboard/webapp/runs/views/runs_table/runs_table_component.ng.html

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,10 @@
1717
<div class="filter-row">
1818
<div class="run-filter">
1919
<mat-icon svgIcon="search_24px"></mat-icon>
20-
<input
21-
#filter
22-
matInput
23-
autocomplete="off"
20+
<tb-filter-input
2421
(keyup)="onFilterKeyUp($event)"
2522
placeholder="Filter runs (regex)"
26-
/>
23+
></tb-filter-input>
2724
</div>
2825
</div>
2926
<div class="table-container">

tensorboard/webapp/runs/views/runs_table/runs_table_component.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ $_border-color: #0000001f;
140140
margin-left: 5px;
141141
margin-right: 5px;
142142
}
143+
144+
tb-filter-input {
145+
flex-grow: 1;
146+
}
143147
}
144148

145149
// Prevents the table column for checkbox and run_color from growing beyond

tensorboard/webapp/runs/views/runs_table/runs_table_module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {NgModule} from '@angular/core';
2121
import {MatButtonModule} from '@angular/material/button';
2222
import {MatCheckboxModule} from '@angular/material/checkbox';
2323
import {MatIconModule} from '@angular/material/icon';
24-
import {MatInputModule} from '@angular/material/input';
2524
import {MatMenuModule} from '@angular/material/menu';
2625
import {MatPaginatorModule} from '@angular/material/paginator';
2726
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
@@ -30,6 +29,7 @@ import {MatTableModule} from '@angular/material/table';
3029
import {ColorPickerModule} from 'ngx-color-picker';
3130

3231
import {AlertModule} from '../../../alert/alert_module';
32+
import {FilterInputModule} from '../../../widgets/filter_input/filter_input_module';
3333
import {RangeInputModule} from '../../../widgets/range_input/range_input_module';
3434
import {RunsTableComponent} from './runs_table_component';
3535
import {RunsTableContainer} from './runs_table_container';
@@ -38,10 +38,10 @@ import {RunsTableContainer} from './runs_table_container';
3838
imports: [
3939
ColorPickerModule,
4040
CommonModule,
41+
FilterInputModule,
4142
MatButtonModule,
4243
MatCheckboxModule,
4344
MatIconModule,
44-
MatInputModule,
4545
MatMenuModule,
4646
MatPaginatorModule,
4747
MatProgressSpinnerModule,

tensorboard/webapp/runs/views/runs_table/runs_table_test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ import {sendKeys} from '../../../testing/dom';
6868
import {MatIconTestingModule} from '../../../testing/mat_icon_module';
6969
import {DataLoadState} from '../../../types/data';
7070
import {SortDirection} from '../../../types/ui';
71+
import {FilterInputModule} from '../../../widgets/filter_input/filter_input_module';
7172
import {RangeInputModule} from '../../../widgets/range_input/range_input_module';
7273
import {
7374
runColorChanged,
@@ -180,6 +181,7 @@ describe('runs_table', () => {
180181
MatSortModule,
181182
MatTableModule,
182183
NoopAnimationsModule,
184+
FilterInputModule,
183185
RangeInputModule,
184186
],
185187
declarations: [

tensorboard/webapp/testing/dom.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,16 @@ export function sendKey<T>(
126126
el.setSelectionRange(startingCursorIndex, startingCursorIndex);
127127
}
128128

129+
// Use to override default options on programmatic events to be closer to
130+
// user-initiated events, which bubble and cancel.
131+
const baseEventOptions = {
132+
bubbles: true,
133+
cancellable: true,
134+
};
135+
129136
// Convert typing to object. Sadly, because keyCode is deprecated, it is not
130137
// typed properly.
131-
const keyboardEventArg = {key, keyCode} as {};
138+
const keyboardEventArg = {...baseEventOptions, key, keyCode} as {};
132139
el.dispatchEvent(new KeyboardEvent('keydown', keyboardEventArg));
133140

134141
// This is technically incorrect. For modifier key event, the keydown triggers
@@ -144,10 +151,10 @@ export function sendKey<T>(
144151
}
145152

146153
if (emitKeyPressAndInput) {
147-
el.dispatchEvent(new InputEvent('input', {data: key}));
154+
el.dispatchEvent(new InputEvent('input', {...baseEventOptions, data: key}));
148155
}
149156

150-
document.dispatchEvent(new Event('selectionchange'));
157+
document.dispatchEvent(new Event('selectionchange', baseEventOptions));
151158

152159
el.dispatchEvent(new KeyboardEvent('keyup', keyboardEventArg));
153160
fixture.detectChanges();
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
load("//tensorboard/defs:defs.bzl", "tf_ng_module")
2+
3+
package(default_visibility = ["//tensorboard:internal"])
4+
5+
licenses(["notice"])
6+
7+
tf_ng_module(
8+
name = "filter_input",
9+
srcs = [
10+
"filter_input_component.ts",
11+
"filter_input_module.ts",
12+
],
13+
deps = [
14+
"//tensorboard/webapp/angular:expect_angular_material_autocomplete",
15+
"@npm//@angular/common",
16+
"@npm//@angular/core",
17+
],
18+
)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
import {Component, Input, ViewChild} from '@angular/core';
16+
import {MatAutocompleteTrigger} from '@angular/material/autocomplete';
17+
18+
/**
19+
* A text input field intended for filtering items.
20+
*/
21+
@Component({
22+
selector: 'tb-filter-input',
23+
template: `
24+
<input
25+
type="text"
26+
autocomplete="off"
27+
[placeholder]="placeholder"
28+
[matAutocomplete]="matAutocomplete"
29+
[matAutocompleteDisabled]="!matAutocomplete"
30+
/>
31+
`,
32+
styles: [
33+
`
34+
:host {
35+
display: flex;
36+
}
37+
38+
input {
39+
font: inherit;
40+
border: none;
41+
outline: none;
42+
padding: 0;
43+
width: 100%;
44+
}
45+
`,
46+
],
47+
})
48+
export class FilterInputComponent {
49+
@Input() value!: string;
50+
@Input() matAutocomplete?: string;
51+
@Input() placeholder?: string;
52+
53+
@ViewChild(MatAutocompleteTrigger)
54+
autocompleteTrigger!: MatAutocompleteTrigger;
55+
56+
getAutocompleteTrigger(): MatAutocompleteTrigger {
57+
return this.autocompleteTrigger;
58+
}
59+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
import {CommonModule} from '@angular/common';
16+
import {NgModule} from '@angular/core';
17+
import {MatAutocompleteModule} from '@angular/material/autocomplete';
18+
19+
import {FilterInputComponent} from './filter_input_component';
20+
21+
@NgModule({
22+
declarations: [FilterInputComponent],
23+
exports: [FilterInputComponent],
24+
imports: [CommonModule, MatAutocompleteModule],
25+
})
26+
export class FilterInputModule {}

0 commit comments

Comments
 (0)