Skip to content

Commit 31938e8

Browse files
authored
Linked Time: Render a prospective fob on the scalar card when the feature is enabled (#6026)
Blocked by #6022 ## Motivation for features / changes We believe the many check boxes in the settings panel to not be very discoverable and a generally poor user experience. To avoid needing as many check boxes we're adding a "Prospective Fob" which will be shown when hovering the axis of a chart. This PR just starts rendering the feature while #6017 will add the click interactions. ## Screenshots of UI changes ![image](https://user-images.githubusercontent.com/78179109/200031105-23967eaf-a6a0-427b-8b16-3d2b250e58d5.png) ![image](https://user-images.githubusercontent.com/78179109/200031168-34641531-0ec3-482f-9f2f-5270b37cae08.png) ## Detailed steps to verify changes work correctly (as executed by you) 1) Start tensorboard 2) Nagivate to http://localhost:6006/?enableDataTable=true&allowRangeSelection=true&enableProspectiveFob=true#timeseries 3) Hover a scalar card chart 4) Assert a prospective fob appears
1 parent 4767950 commit 31938e8

File tree

4 files changed

+116
-25
lines changed

4 files changed

+116
-25
lines changed

tensorboard/webapp/feature_flag/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@ export interface FeatureFlags {
4949
// Adds check box in settings which allows users to enter step selection range.
5050
allowRangeSelection: boolean;
5151
// In Linked Time, if enabled, show a prospective fob user to turn on the feature or select a step.
52+
// If this is removed update the `getCurrentFob` method of tensorboard/webapp/widgets/card_fob/card_fob_controller_component.ts
5253
enabledProspectiveFob: boolean;
5354
}

tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3238,6 +3238,33 @@ describe('scalar card', () => {
32383238
expect(testController).toBeTruthy();
32393239
}));
32403240

3241+
it('renders prospective fob', fakeAsync(() => {
3242+
store.overrideSelector(selectors.getMetricsLinkedTimeSelection, null);
3243+
store.overrideSelector(selectors.getMetricsStepSelectorEnabled, false);
3244+
store.overrideSelector(
3245+
selectors.getIsLinkedTimeProspectiveFobEnabled,
3246+
true
3247+
);
3248+
3249+
const fixture = createComponent('card1');
3250+
fixture.detectChanges();
3251+
3252+
const cardFobController = fixture.debugElement.query(
3253+
By.directive(CardFobControllerComponent)
3254+
).componentInstance;
3255+
expect(cardFobController).toBeDefined();
3256+
3257+
cardFobController.onPrespectiveStepChanged.emit(1);
3258+
fixture.detectChanges();
3259+
3260+
const prospectiveFob = fixture.debugElement.query(
3261+
By.directive(CardFobComponent)
3262+
).componentInstance;
3263+
expect(prospectiveFob).toBeDefined();
3264+
expect(cardFobController.prospectiveFobWrapper).toBeDefined();
3265+
expect(cardFobController.prospectiveStep).toEqual(1);
3266+
}));
3267+
32413268
it('Does not render fobs when axis type is RELATIVE', fakeAsync(() => {
32423269
store.overrideSelector(
32433270
selectors.getIsLinkedTimeProspectiveFobEnabled,

tensorboard/webapp/widgets/card_fob/card_fob_controller_component.ng.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@
2525
<div *ngIf="showExtendedLine" class="extended-line"></div>
2626
<card-fob
2727
[ngClass]="isVertical() ? 'vertical-fob' : 'horizontal-fob'"
28+
[allowRemoval]="false"
29+
[step]="prospectiveStep"
2830
></card-fob>
2931
</div>
3032
<div
3133
class="prospective-fob-area"
3234
[ngClass]="isVertical() ? 'vertical-prospective-area' : 'horizontal-prospective-area'"
35+
(mousemove)="mouseOver($event)"
3336
(mouseleave)="onProspectiveAreaMouseLeave()"
3437
></div>
3538
<div

tensorboard/webapp/widgets/card_fob/card_fob_controller_component.ts

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
Output,
2323
ViewChild,
2424
} from '@angular/core';
25+
import {CardFobComponent} from './card_fob_component';
2526
import {
2627
AxisDirection,
2728
CardFobGetStepFromPositionHelper,
@@ -61,7 +62,7 @@ export class CardFobControllerComponent {
6162
@Input() lowestStep!: number;
6263
@Input() showExtendedLine?: Boolean = false;
6364
@Input() isProspectiveFobFeatureEnabled?: Boolean = false;
64-
@Input() prospectiveStep?: number | null = null;
65+
@Input() prospectiveStep: number | null = null;
6566
@Input() prospectiveStepAxisPosition?: number | null = null;
6667

6768
@Output() onTimeSelectionChanged =
@@ -200,21 +201,30 @@ export class CardFobControllerComponent {
200201
return newTimeSelection;
201202
}
202203

203-
mouseMove(event: MouseEvent) {
204-
if (this.currentDraggingFob === Fob.NONE) return;
205-
204+
getNewStepFromMouseEvent(event: MouseEvent): number | null {
206205
let newStep: number | null = null;
207206
const mousePosition = this.getMousePositionFromEvent(event);
208207
const movement =
209208
this.axisDirection === AxisDirection.VERTICAL
210209
? event.movementY
211210
: event.movementX;
212-
if (this.isDraggingHigher(mousePosition, movement)) {
211+
if (this.isMovingHigher(mousePosition, movement)) {
213212
newStep = this.cardFobHelper.getStepHigherThanAxisPosition(mousePosition);
214-
} else if (this.isDraggingLower(mousePosition, movement)) {
213+
} else if (this.isMovingLower(mousePosition, movement)) {
215214
newStep = this.cardFobHelper.getStepLowerThanAxisPosition(mousePosition);
216215
}
217216

217+
if (newStep === null) {
218+
return null;
219+
}
220+
221+
return newStep;
222+
}
223+
224+
mouseMove(event: MouseEvent) {
225+
if (this.currentDraggingFob === Fob.NONE) return;
226+
227+
const newStep = this.getNewStepFromMouseEvent(event);
218228
if (newStep === null || !this.timeSelection) {
219229
return;
220230
}
@@ -229,19 +239,53 @@ export class CardFobControllerComponent {
229239
this.hasFobMoved = true;
230240
}
231241

232-
isDraggingLower(position: number, movement: number): boolean {
242+
mouseOver(event: MouseEvent) {
243+
if (
244+
this.timeSelection?.end !== null &&
245+
this.timeSelection?.end !== undefined
246+
) {
247+
return;
248+
}
249+
250+
const newStep = this.getNewStepFromMouseEvent(event);
251+
if (newStep === null) {
252+
return;
253+
}
254+
255+
this.onPrespectiveStepChanged.emit(newStep);
256+
}
257+
258+
isMovingLower(position: number, movement: number): boolean {
259+
if (this.currentDraggingFob === Fob.NONE && this.prospectiveStep === null) {
260+
return true;
261+
}
262+
263+
const currentStep = this.getCurrentFobStep();
264+
if (currentStep === undefined) {
265+
return false;
266+
}
267+
233268
return (
234269
position < this.getDraggingFobCenter() &&
235270
movement < 0 &&
236-
this.getDraggingFobStep() > this.lowestStep
271+
currentStep > this.lowestStep
237272
);
238273
}
239274

240-
isDraggingHigher(position: number, movement: number): boolean {
275+
isMovingHigher(position: number, movement: number): boolean {
276+
if (this.currentDraggingFob === Fob.NONE && this.prospectiveStep === null) {
277+
return true;
278+
}
279+
280+
const currentStep = this.getCurrentFobStep();
281+
if (currentStep === undefined) {
282+
return false;
283+
}
284+
241285
return (
242286
position > this.getDraggingFobCenter() &&
243287
movement > 0 &&
244-
this.getDraggingFobStep() < this.highestStep
288+
currentStep < this.highestStep
245289
);
246290
}
247291

@@ -252,27 +296,43 @@ export class CardFobControllerComponent {
252296
// the element's natural position(using translateY(-50%)). While in the
253297
// horizontal direction the fob's center is actually rendered over the left
254298
// of the element's natural position (using translateX(-50%)).
299+
const currentFob = this.getCurrentFob()?.nativeElement;
300+
if (!currentFob) {
301+
return 0;
302+
}
303+
let fobTopPosition = currentFob.getBoundingClientRect().top;
304+
let fobLeftPosition = currentFob.getBoundingClientRect().left;
305+
255306
if (this.axisDirection === AxisDirection.VERTICAL) {
256307
return (
257-
(this.currentDraggingFob !== Fob.END
258-
? this.startFobWrapper.nativeElement.getBoundingClientRect().top
259-
: this.endFobWrapper.nativeElement.getBoundingClientRect().top) -
260-
this.root.nativeElement.getBoundingClientRect().top
261-
);
262-
} else {
263-
return (
264-
(this.currentDraggingFob !== Fob.END
265-
? this.startFobWrapper.nativeElement.getBoundingClientRect().left
266-
: this.endFobWrapper.nativeElement.getBoundingClientRect().left) -
267-
this.root.nativeElement.getBoundingClientRect().left
308+
fobTopPosition - this.root.nativeElement.getBoundingClientRect().top
268309
);
269310
}
311+
return (
312+
fobLeftPosition - this.root.nativeElement.getBoundingClientRect().left
313+
);
314+
}
315+
316+
getCurrentFob(): ElementRef<CardFobComponent & HTMLElement> | null {
317+
switch (this.currentDraggingFob) {
318+
case Fob.START:
319+
return this.startFobWrapper;
320+
case Fob.END:
321+
return this.endFobWrapper;
322+
case Fob.NONE:
323+
return this.prospectiveFobWrapper;
324+
}
270325
}
271326

272-
getDraggingFobStep(): number {
273-
return this.currentDraggingFob !== Fob.END
274-
? this.timeSelection!.start.step
275-
: this.timeSelection!.end!.step;
327+
getCurrentFobStep(): number | undefined {
328+
switch (this.currentDraggingFob) {
329+
case Fob.START:
330+
return this.timeSelection?.start.step;
331+
case Fob.END:
332+
return this.timeSelection?.end?.step;
333+
case Fob.NONE:
334+
return this.prospectiveStep ?? undefined;
335+
}
276336
}
277337

278338
getMousePositionFromEvent(event: MouseEvent): number {

0 commit comments

Comments
 (0)