Allow sharing of selected item by page URL.
This patch adds the ability for users to share a selected timeline item via the page URL. When an item is selected, the page address is updated to include the current test name as a parameter. If this link is shared, the test named in the URL will automatically be highlighed when the page is loaded. Change-Id: I228d58e68ee986f621a3763bba1a565300c79023
This commit is contained in:
parent
bf35250595
commit
756d281ee4
app
@ -5,7 +5,7 @@ var controllersModule = require('./_index');
|
|||||||
/**
|
/**
|
||||||
* @ngInject
|
* @ngInject
|
||||||
*/
|
*/
|
||||||
function TimelineCtrl($stateParams, datasetService) {
|
function TimelineCtrl($scope, $location, $stateParams, datasetService) {
|
||||||
|
|
||||||
// ViewModel
|
// ViewModel
|
||||||
var vm = this;
|
var vm = this;
|
||||||
@ -19,6 +19,19 @@ function TimelineCtrl($stateParams, datasetService) {
|
|||||||
vm.hoveredItem = null;
|
vm.hoveredItem = null;
|
||||||
vm.selectedItem = null;
|
vm.selectedItem = null;
|
||||||
|
|
||||||
|
vm.preselect = $location.search().test;
|
||||||
|
|
||||||
|
$scope.$watch(function() {
|
||||||
|
return vm.selectedItem;
|
||||||
|
}, function(value) {
|
||||||
|
if (value) {
|
||||||
|
$location.search({ test: value.name });
|
||||||
|
vm.preselect = null;
|
||||||
|
} else if (vm.preselect === null) {
|
||||||
|
$location.search({ test: null });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
controllersModule.controller('TimelineCtrl', TimelineCtrl);
|
controllersModule.controller('TimelineCtrl', TimelineCtrl);
|
||||||
|
@ -134,11 +134,11 @@ function timeline($log, datasetService) {
|
|||||||
.append('clipPath')
|
.append('clipPath')
|
||||||
.attr('id', 'clip')
|
.attr('id', 'clip')
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('width', width); // TODO: set height later
|
.attr('width', width);
|
||||||
|
|
||||||
var main = chart.append('g')
|
var main = chart.append('g')
|
||||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
|
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
|
||||||
.attr('width', width); // TODO: set height later
|
.attr('width', width);
|
||||||
|
|
||||||
var laneLines = main.append('g');
|
var laneLines = main.append('g');
|
||||||
var laneLabels = main.append('g');
|
var laneLabels = main.append('g');
|
||||||
@ -280,31 +280,8 @@ function timeline($log, datasetService) {
|
|||||||
})
|
})
|
||||||
.on('click', function(d) {
|
.on('click', function(d) {
|
||||||
var self = d3.select(this);
|
var self = d3.select(this);
|
||||||
if (selectedRect) {
|
selectItem(self, d);
|
||||||
if (selectedRect.attr('data-old-fill')) {
|
|
||||||
selectedRect.attr('fill', selectedRect.attr('data-old-fill'));
|
|
||||||
selectedRect.attr('data-old-fill', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope.selectedItem.name === d.name) {
|
|
||||||
scope.selectedItem = null;
|
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
|
|
||||||
selectedRect = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.selectedItem = d;
|
|
||||||
scope.$apply();
|
|
||||||
|
|
||||||
selectedRect = self;
|
|
||||||
|
|
||||||
if (!self.attr('data-old-fill')) {
|
|
||||||
self.attr('data-old-fill', self.attr('fill'));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.attr('fill', 'goldenrod');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
rects.exit().remove();
|
rects.exit().remove();
|
||||||
@ -371,6 +348,30 @@ function timeline($log, datasetService) {
|
|||||||
groups.exit().remove();
|
groups.exit().remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var selectItem = function(element, datum) {
|
||||||
|
if (selectedRect) {
|
||||||
|
if (selectedRect.attr('data-old-fill')) {
|
||||||
|
selectedRect.attr('fill', selectedRect.attr('data-old-fill'));
|
||||||
|
selectedRect.attr('data-old-fill', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope.selectedItem.name === datum.name) {
|
||||||
|
scope.selectedItem = null;
|
||||||
|
selectedRect = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.selectedItem = datum;
|
||||||
|
selectedRect = element;
|
||||||
|
|
||||||
|
if (!element.attr('data-old-fill')) {
|
||||||
|
element.attr('data-old-fill', element.attr('fill'));
|
||||||
|
}
|
||||||
|
|
||||||
|
element.attr('fill', 'goldenrod');
|
||||||
|
};
|
||||||
|
|
||||||
var initChart = function() {
|
var initChart = function() {
|
||||||
// determine lanes available based on current data
|
// determine lanes available based on current data
|
||||||
dstatLanes = getDstatLanes(dstat.entries, dstat.minimums, dstat.maximums);
|
dstatLanes = getDstatLanes(dstat.entries, dstat.minimums, dstat.maximums);
|
||||||
@ -413,6 +414,7 @@ function timeline($log, datasetService) {
|
|||||||
.on('brush', update);
|
.on('brush', update);
|
||||||
|
|
||||||
var brushElement = mini.append('g')
|
var brushElement = mini.append('g')
|
||||||
|
.attr('class', 'brush')
|
||||||
.call(brush)
|
.call(brush)
|
||||||
.selectAll('rect')
|
.selectAll('rect')
|
||||||
.attr('y', 1)
|
.attr('y', 1)
|
||||||
@ -481,6 +483,7 @@ function timeline($log, datasetService) {
|
|||||||
// find data extents
|
// find data extents
|
||||||
var minStart = null;
|
var minStart = null;
|
||||||
var maxEnd = null;
|
var maxEnd = null;
|
||||||
|
var preselect = null;
|
||||||
|
|
||||||
raw.forEach(function(d) {
|
raw.forEach(function(d) {
|
||||||
d.startDate = new Date(d.timestamps[0]);
|
d.startDate = new Date(d.timestamps[0]);
|
||||||
@ -492,6 +495,10 @@ function timeline($log, datasetService) {
|
|||||||
if (maxEnd === null || d.endDate > maxEnd) {
|
if (maxEnd === null || d.endDate > maxEnd) {
|
||||||
maxEnd = d.endDate;
|
maxEnd = d.endDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (scope.preselect && d.name === scope.preselect) {
|
||||||
|
preselect = d;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// define a nested data structure with groups by worker, and fill using
|
// define a nested data structure with groups by worker, and fill using
|
||||||
@ -513,6 +520,32 @@ function timeline($log, datasetService) {
|
|||||||
timeExtents = [ minStart, maxEnd ];
|
timeExtents = [ minStart, maxEnd ];
|
||||||
|
|
||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
|
if (preselect) {
|
||||||
|
// determine desired viewport and item sizes to center view on
|
||||||
|
var extentSize = (maxEnd - minStart) / 8;
|
||||||
|
var itemLength = (preselect.endDate - preselect.startDate);
|
||||||
|
var itemMid = preselect.startDate.getTime() + (itemLength / 2);
|
||||||
|
|
||||||
|
// find real begin, end values, but don't exceed min/max time extents
|
||||||
|
var begin = Math.max(minStart.getTime(), itemMid - (extentSize / 2));
|
||||||
|
begin = Math.min(begin, maxEnd.getTime() - extentSize);
|
||||||
|
var end = begin + extentSize;
|
||||||
|
|
||||||
|
// update the brush extent and redraw
|
||||||
|
brush.extent([ begin, end ]);
|
||||||
|
mini.select('.brush').call(brush);
|
||||||
|
|
||||||
|
// update items to reflect the new viewport bounds
|
||||||
|
update();
|
||||||
|
|
||||||
|
// find + select the actual dom element
|
||||||
|
itemGroups.selectAll('rect').each(function(d) {
|
||||||
|
if (d.name === preselect.name) {
|
||||||
|
selectItem(d3.select(this), d);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
chart.on('mouseout', function() {
|
chart.on('mouseout', function() {
|
||||||
@ -548,7 +581,6 @@ function timeline($log, datasetService) {
|
|||||||
defs.attr('width', width);
|
defs.attr('width', width);
|
||||||
main.attr('width', width);
|
main.attr('width', width);
|
||||||
mini.attr('width', width);
|
mini.attr('width', width);
|
||||||
// TODO: dstat?
|
|
||||||
|
|
||||||
laneLines.selectAll('.laneLine').attr('x2', width);
|
laneLines.selectAll('.laneLine').attr('x2', width);
|
||||||
|
|
||||||
@ -588,7 +620,8 @@ function timeline($log, datasetService) {
|
|||||||
scope: {
|
scope: {
|
||||||
'dataset': '=',
|
'dataset': '=',
|
||||||
'hoveredItem': '=',
|
'hoveredItem': '=',
|
||||||
'selectedItem': '='
|
'selectedItem': '=',
|
||||||
|
'preselect': '='
|
||||||
},
|
},
|
||||||
link: link
|
link: link
|
||||||
};
|
};
|
||||||
|
@ -14,9 +14,10 @@ function OnConfig($stateProvider, $locationProvider, $urlRouterProvider) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$stateProvider.state('timeline', {
|
$stateProvider.state('timeline', {
|
||||||
url: '/{datasetId:int}/timeline',
|
url: '/{datasetId:int}/timeline?test',
|
||||||
controller: 'TimelineCtrl as timeline',
|
controller: 'TimelineCtrl as timeline',
|
||||||
templateUrl: 'timeline.html',
|
templateUrl: 'timeline.html',
|
||||||
|
reloadOnSearch: false,
|
||||||
title: 'Timeline'
|
title: 'Timeline'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
<timeline class="panel-body"
|
<timeline class="panel-body"
|
||||||
dataset="timeline.dataset"
|
dataset="timeline.dataset"
|
||||||
hovered-item="timeline.hoveredItem"
|
hovered-item="timeline.hoveredItem"
|
||||||
selected-item="timeline.selectedItem"></timeline>
|
selected-item="timeline.selectedItem"
|
||||||
|
preselect="timeline.preselect"></timeline>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user