Skip to content

Commit 31308b1

Browse files
committed
[Time Conductor] Addressed code review comments. Fixes #1287
1 parent db6386e commit 31308b1

9 files changed

+255
-42
lines changed

platform/features/conductor-v2/conductor/res/templates/time-conductor.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
<div class="l-data-visualization-holder l-row-elem flex-elem"
8888
ng-controller="ConductorTOIController as toi">
8989
<a class="l-page-button s-icon-button icon-pointer-left"></a>
90-
<div class="l-data-visualization" ng-click="toi.click($event)">
90+
<div class="l-data-visualization" ng-click="toi.setTOIFromPosition($event)">
9191
<mct-include key="'time-of-interest'"
9292
class="l-toi-holder show-val"
9393
ng-class="{ pinned: toi.pinned, 'val-to-left': toi.left > 80 }"

platform/features/conductor-v2/conductor/src/ui/ConductorAxisController.js

+37-8
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ define(
2828
var PADDING = 1;
2929

3030
/**
31-
* The mct-conductor-axis renders a horizontal axis with regular
32-
* labelled 'ticks'. It requires 'start' and 'end' integer values to
33-
* be specified as attributes.
31+
* Controller that renders a horizontal time scale spanning the current bounds defined in the time conductor.
32+
* Used by the mct-conductor-axis directive
33+
* @constructor
3434
*/
3535
function ConductorAxisController(openmct, formatService, conductorViewService, scope, element) {
3636
// Dependencies
@@ -54,6 +54,9 @@ define(
5454
this.initialize(element);
5555
}
5656

57+
/**
58+
* @private
59+
*/
5760
ConductorAxisController.prototype.destroy = function () {
5861
this.conductor.off('timeSystem', this.changeTimeSystem);
5962
this.conductor.off('bounds', this.changeBounds);
@@ -62,9 +65,7 @@ define(
6265
};
6366

6467
/**
65-
* Set defaults, and apply d3 axis to the
66-
* @param scope
67-
* @param element
68+
* @private
6869
*/
6970
ConductorAxisController.prototype.initialize = function (element) {
7071
this.target = element[0].firstChild;
@@ -95,6 +96,9 @@ define(
9596
this.conductorViewService.on("zoom-stop", this.onZoomStop);
9697
};
9798

99+
/**
100+
* @private
101+
*/
98102
ConductorAxisController.prototype.changeBounds = function (bounds) {
99103
this.bounds = bounds;
100104
if (!this.zooming) {
@@ -127,8 +131,7 @@ define(
127131
};
128132

129133
/**
130-
* When the time system changes, update the scale and formatter used
131-
* for showing times.
134+
* When the time system changes, update the scale and formatter used for showing times.
132135
* @param timeSystem
133136
*/
134137
ConductorAxisController.prototype.changeTimeSystem = function (timeSystem) {
@@ -164,28 +167,51 @@ define(
164167
}
165168
};
166169

170+
/**
171+
* The user has stopped panning the time conductor scale element.
172+
* @event panStop
173+
*/
174+
/**
175+
* Called on release of mouse button after dragging the scale left or right.
176+
* @fires platform.features.conductor.ConductorAxisController~panStop
177+
*/
167178
ConductorAxisController.prototype.panStop = function () {
168179
//resync view bounds with time conductor bounds
169180
this.conductorViewService.emit("pan-stop");
170181
this.conductor.bounds(this.bounds);
171182
};
172183

184+
/**
185+
* Rescales the axis when the user zooms. Although zoom ultimately results in a bounds change once the user
186+
* releases the zoom slider, dragging the slider will not immediately change the conductor bounds. It will
187+
* however immediately update the scale and the bounds displayed in the UI.
188+
* @private
189+
* @param {ZoomLevel}
190+
*/
173191
ConductorAxisController.prototype.onZoom = function (zoom) {
174192
this.zooming = true;
175193

176194
this.bounds = zoom.bounds;
177195
this.setScale();
178196
};
179197

198+
/**
199+
* @private
200+
*/
180201
ConductorAxisController.prototype.onZoomStop = function (zoom) {
181202
this.zooming = false;
182203
};
183204

205+
/**
206+
* @event platform.features.conductor.ConductorAxisController~pan
207+
* Fired when the time conductor is panned
208+
*/
184209
/**
185210
* Initiate panning via a click + drag gesture on the time conductor
186211
* scale. Panning triggers a "pan" event
187212
* @param {number} delta the offset from the original click event
188213
* @see TimeConductorViewService#
214+
* @fires platform.features.conductor.ConductorAxisController~pan
189215
*/
190216
ConductorAxisController.prototype.pan = function (delta) {
191217
if (!this.conductor.follow()) {
@@ -202,6 +228,9 @@ define(
202228
}
203229
};
204230

231+
/**
232+
* Invoked on element resize. Will rebuild the scale based on the new dimensions of the element.
233+
*/
205234
ConductorAxisController.prototype.resize = function () {
206235
this.setScale();
207236
};

platform/features/conductor-v2/conductor/src/ui/ConductorTOIController.js

+22-8
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ define(
2525
function ($) {
2626

2727
/**
28-
* The mct-conductor-axis renders a horizontal axis with regular
29-
* labelled 'ticks'. It requires 'start' and 'end' integer values to
30-
* be specified as attributes.
28+
* Controller for the Time of Interest indicator in the conductor itself. Sets the horizontal position of the
29+
* TOI indicator based on the current value of the TOI, and the width of the TOI conductor.
30+
* @memberof platform.features.conductor
3131
*/
3232
function ConductorTOIController($scope, openmct, conductorViewService) {
3333
this.conductor = openmct.conductor;
@@ -41,7 +41,7 @@ define(
4141
}.bind(this));
4242

4343
this.conductor.on('timeOfInterest', this.changeTimeOfInterest);
44-
this.conductorViewService.on('zoom', this.setOffsetFromBounds);
44+
this.conductorViewService.on('zoom', this.setOffsetFromZoom);
4545
this.conductorViewService.on('pan', this.setOffsetFromBounds);
4646

4747
var timeOfInterest = this.conductor.timeOfInterest();
@@ -50,12 +50,14 @@ define(
5050
}
5151

5252
$scope.$on('$destroy', this.destroy);
53-
5453
}
5554

55+
/**
56+
* @private
57+
*/
5658
ConductorTOIController.prototype.destroy = function () {
5759
this.conductor.off('timeOfInterest', this.changeTimeOfInterest);
58-
this.conductorViewService.off('zoom', this.setOffsetFromBounds);
60+
this.conductorViewService.off('zoom', this.setOffsetFromZoom);
5961
this.conductorViewService.off('pan', this.setOffsetFromBounds);
6062
};
6163

@@ -80,6 +82,17 @@ define(
8082
}
8183
};
8284

85+
/**
86+
* @private
87+
*/
88+
ConductorTOIController.prototype.setOffsetFromZoom = function (zoom) {
89+
return this.setOffsetFromBounds(zoom.bounds);
90+
};
91+
92+
/**
93+
* Invoked when time of interest changes. Will set the horizontal offset of the TOI indicator.
94+
* @private
95+
*/
8396
ConductorTOIController.prototype.changeTimeOfInterest = function () {
8497
var bounds = this.conductor.bounds();
8598
if (bounds) {
@@ -88,10 +101,11 @@ define(
88101
};
89102

90103
/**
91-
* Set time of interest
104+
* On a mouse click event within the TOI element, convert position within element to a time of interest, and
105+
* set the time of interest on the conductor.
92106
* @param e The angular $event object
93107
*/
94-
ConductorTOIController.prototype.click = function (e) {
108+
ConductorTOIController.prototype.setTOIFromPosition = function (e) {
95109
//TOI is set using the alt key modified + primary click
96110
if (e.altKey) {
97111
var element = $(e.currentTarget);

platform/features/conductor-v2/conductor/src/ui/ConductorTOIControllerSpec.js

+119-6
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,127 @@ define([
2727
) {
2828
var mockConductor;
2929
var mockConductorViewService;
30+
var mockScope;
31+
var mockAPI;
32+
var conductorTOIController;
33+
34+
function getNamedCallback(thing, name) {
35+
return thing.calls.filter(function (call) {
36+
return call.args[0] === name;
37+
}).map(function (call) {
38+
return call.args;
39+
})[0][1];
40+
}
3041

3142
describe("The ConductorTOIController", function () {
32-
mockConductor = jasmine.createSpyObj("conductor", [
33-
"on"
34-
]);
35-
mockConductorViewService = jasmine.createSpyObj("conductorViewService", [
36-
"on"
37-
]);
43+
beforeEach(function () {
44+
mockConductor = jasmine.createSpyObj("conductor", [
45+
"bounds",
46+
"timeOfInterest",
47+
"on",
48+
"off"
49+
]);
50+
mockAPI = {conductor: mockConductor};
51+
52+
mockConductorViewService = jasmine.createSpyObj("conductorViewService", [
53+
"on",
54+
"off"
55+
]);
56+
57+
mockScope = jasmine.createSpyObj("openMCT", [
58+
"$on"
59+
]);
60+
61+
conductorTOIController = new ConductorTOIController(mockScope, mockAPI, mockConductorViewService);
62+
});
63+
64+
it("listens to changes in the time of interest on the conductor", function () {
65+
expect(mockConductor.on).toHaveBeenCalledWith("timeOfInterest", jasmine.any(Function));
66+
});
67+
68+
describe("when responding to changes in the time of interest", function () {
69+
var toiCallback;
70+
beforeEach(function () {
71+
var bounds = {
72+
start: 0,
73+
end: 200
74+
};
75+
mockConductor.bounds.andReturn(bounds);
76+
toiCallback = getNamedCallback(mockConductor.on, "timeOfInterest");
77+
});
78+
79+
it("calculates the correct horizontal offset based on bounds and current TOI", function () {
80+
//Expect time of interest position to be 50% of element width
81+
mockConductor.timeOfInterest.andReturn(100);
82+
toiCallback();
83+
expect(conductorTOIController.left).toBe(50);
84+
85+
//Expect time of interest position to be 25% of element width
86+
mockConductor.timeOfInterest.andReturn(50);
87+
toiCallback();
88+
expect(conductorTOIController.left).toBe(25);
89+
90+
//Expect time of interest position to be 0% of element width
91+
mockConductor.timeOfInterest.andReturn(0);
92+
toiCallback();
93+
expect(conductorTOIController.left).toBe(0);
94+
95+
//Expect time of interest position to be 100% of element width
96+
mockConductor.timeOfInterest.andReturn(200);
97+
toiCallback();
98+
expect(conductorTOIController.left).toBe(100);
99+
});
100+
101+
it("renders the TOI indicator visible", function () {
102+
expect(conductorTOIController.pinned).toBeFalsy();
103+
mockConductor.timeOfInterest.andReturn(100);
104+
toiCallback();
105+
expect(conductorTOIController.pinned).toBe(true);
106+
});
107+
});
108+
109+
it("responds to zoom events", function () {
110+
var mockZoom = {
111+
bounds: {
112+
start: 500,
113+
end: 1000
114+
}
115+
};
116+
expect(mockConductorViewService.on).toHaveBeenCalledWith("zoom", jasmine.any(Function));
117+
118+
// Should correspond to horizontal offset of 50%
119+
mockConductor.timeOfInterest.andReturn(750);
120+
var zoomCallback = getNamedCallback(mockConductorViewService.on, "zoom");
121+
zoomCallback(mockZoom);
122+
expect(conductorTOIController.left).toBe(50);
123+
});
124+
125+
it("responds to pan events", function () {
126+
var mockPanBounds = {
127+
start: 1000,
128+
end: 3000
129+
};
130+
131+
expect(mockConductorViewService.on).toHaveBeenCalledWith("pan", jasmine.any(Function));
132+
133+
// Should correspond to horizontal offset of 25%
134+
mockConductor.timeOfInterest.andReturn(1500);
135+
var panCallback = getNamedCallback(mockConductorViewService.on, "pan");
136+
panCallback(mockPanBounds);
137+
expect(conductorTOIController.left).toBe(25);
138+
});
139+
140+
141+
it("Cleans up all listeners when controller destroyed", function () {
142+
var zoomCB = getNamedCallback(mockConductorViewService.on, "zoom");
143+
var panCB = getNamedCallback(mockConductorViewService.on, "pan");
144+
var toiCB = getNamedCallback(mockConductor.on, "timeOfInterest");
38145

146+
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", jasmine.any(Function));
147+
getNamedCallback(mockScope.$on, "$destroy")();
148+
expect(mockConductorViewService.off).toHaveBeenCalledWith("zoom", zoomCB);
149+
expect(mockConductorViewService.off).toHaveBeenCalledWith("pan", panCB);
150+
expect(mockConductor.off).toHaveBeenCalledWith("timeOfInterest", toiCB);
151+
});
39152
});
40153
});

0 commit comments

Comments
 (0)