rd_ui: sync filters with location.search [closes #146]

This commit is contained in:
Amir Nissim
2014-08-06 16:18:05 +03:00
parent 9592610f8b
commit 948e2247e4
8 changed files with 177 additions and 12 deletions

View File

@@ -159,4 +159,4 @@
</script> </script>
</body> </body>
</html> </html>

View File

@@ -55,7 +55,7 @@
}]; }];
}; };
var VisualizationRenderer = function (Visualization) { var VisualizationRenderer = function ($location, Visualization) {
return { return {
restrict: 'E', restrict: 'E',
scope: { scope: {
@@ -68,12 +68,47 @@
template: '<filters></filters>\n' + Visualization.renderVisualizationsTemplate, template: '<filters></filters>\n' + Visualization.renderVisualizationsTemplate,
replace: false, replace: false,
link: function (scope) { link: function (scope) {
scope.select2Options = { scope.select2Options = {
width: '50%' width: '50%'
};
function readURL() {
var searchFilters = angular.fromJson($location.search().filters);
if (searchFilters) {
_.forEach(scope.filters, function(filter) {
var value = searchFilters[filter.friendlyName];
if (value) {
filter.current = value;
}
});
}
} }
function updateURL(filters) {
var current = {};
_.each(filters, function(filter) {
if (filter.current) {
current[filter.friendlyName] = filter.current;
}
});
var newSearch = angular.extend($location.search(), {
filters: angular.toJson(current)
});
$location.search(newSearch);
}
scope.$watch('queryResult && queryResult.getFilters()', function (filters) { scope.$watch('queryResult && queryResult.getFilters()', function (filters) {
if (filters) { if (filters) {
scope.filters = filters; scope.filters = filters;
if (filters.length) {
readURL();
// start watching for changes and update URL
scope.$watch('filters', updateURL, true);
}
} }
}); });
} }
@@ -172,7 +207,7 @@
angular.module('redash.visualization', []) angular.module('redash.visualization', [])
.provider('Visualization', VisualizationProvider) .provider('Visualization', VisualizationProvider)
.directive('visualizationRenderer', ['Visualization', VisualizationRenderer]) .directive('visualizationRenderer', ['$location', 'Visualization', VisualizationRenderer])
.directive('visualizationOptionsEditor', ['Visualization', VisualizationOptionsEditor]) .directive('visualizationOptionsEditor', ['Visualization', VisualizationOptionsEditor])
.directive('filters', Filters) .directive('filters', Filters)
.directive('editVisulatizationForm', ['Events', 'Visualization', 'growl', EditVisualizationForm]) .directive('editVisulatizationForm', ['Events', 'Visualization', 'growl', EditVisualizationForm])

View File

@@ -171,7 +171,7 @@
<edit-visulatization-form visualization="vis" query="query" query-result="queryResult" ng-show="canEdit"></edit-visulatization-form> <edit-visulatization-form visualization="vis" query="query" query-result="queryResult" ng-show="canEdit"></edit-visulatization-form>
</div> </div>
<div ng-show="selectedTab == 'add'"> <div ng-if="canEdit" ng-show="selectedTab == 'add'">
<visualization-renderer visualization="newVisualization" query-result="queryResult"></visualization-renderer> <visualization-renderer visualization="newVisualization" query-result="queryResult"></visualization-renderer>
<edit-visulatization-form visualization="newVisualization" query="query" query-result="queryResult" ng-show="canEdit" open-editor="true" on-new-success="setVisualizationTab"></edit-visulatization-form> <edit-visulatization-form visualization="newVisualization" query="query" query-result="queryResult" ng-show="canEdit" open-editor="true" on-new-success="setVisualizationTab"></edit-visulatization-form>
</div> </div>

View File

@@ -29,7 +29,8 @@
"karma-jasmine": "~0.1.5", "karma-jasmine": "~0.1.5",
"grunt-karma": "~0.8.3", "grunt-karma": "~0.8.3",
"karma-phantomjs-launcher": "~0.1.4", "karma-phantomjs-launcher": "~0.1.4",
"karma": "~0.12.19" "karma": "~0.12.19",
"karma-ng-html2js-preprocessor": "~0.1.0"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"

View File

@@ -75,10 +75,17 @@ module.exports = function(config) {
'app/scripts/directives/dashboard_directives.js', 'app/scripts/directives/dashboard_directives.js',
'app/scripts/filters.js', 'app/scripts/filters.js',
'app/views/**/*.html',
'test/mocks/*.js', 'test/mocks/*.js',
'test/unit/*.js' 'test/unit/*.js'
], ],
// generate js files from html templates
preprocessors: {
'app/views/**/*.html': 'ng-html2js'
},
// list of files / patterns to exclude // list of files / patterns to exclude
exclude: [], exclude: [],
@@ -100,7 +107,8 @@ module.exports = function(config) {
// Which plugins to enable // Which plugins to enable
plugins: [ plugins: [
'karma-phantomjs-launcher', 'karma-phantomjs-launcher',
'karma-jasmine' 'karma-jasmine',
'karma-ng-html2js-preprocessor'
], ],
// Continuous Integration mode // Continuous Integration mode

View File

@@ -16,9 +16,8 @@ currentUser = {
angular.module('redashMocks', []) angular.module('redashMocks', [])
.value('mockData', { .value('MockData', {
query: { query: {
"ttl": -1, "ttl": -1,
"query": "select name from users;", "query": "select name from users;",
"id": 1803, "id": 1803,
@@ -72,5 +71,38 @@ angular.module('redashMocks', [])
"data_source_id": 1 "data_source_id": 1
} }
},
queryResult: {
"job": {},
"query_result": {
"retrieved_at": "2014-08-04T13:33:45.563486+03:00",
"query_hash": "9951c38c9cf00e6ee8aecce026b51c19",
"query": "select name as \"name::filter\" from users",
"runtime": 0.00896096229553223,
"data": {
"rows": [],
"columns": [{
"friendly_name": "name::filter",
"type": null,
"name": "name::filter"
}]
},
"id": 106673,
"data_source_id": 1
},
"status": "done",
"filters": [],
"filterFreeze": "test@example.com",
"updatedAt": "2014-08-05T13:13:40.833Z",
"columnNames": ["name::filter"],
"filteredData": [{
"name::filter": "test@example.com"
}],
"columns": [{
"friendly_name": "name::filter",
"type": null,
"name": "name::filter"
}]
} }
}); });

View File

@@ -2,18 +2,18 @@
describe('QueryViewCtrl', function() { describe('QueryViewCtrl', function() {
var scope; var scope;
var mockData; var MockData;
beforeEach(module('redash', 'redashMocks')); beforeEach(module('redash', 'redashMocks'));
beforeEach(inject(function($injector, $controller, $rootScope, Query, _mockData_) { beforeEach(inject(function($injector, $controller, $rootScope, Query, _MockData_) {
mockData = _mockData_; MockData = _MockData_;
scope = $rootScope.$new(); scope = $rootScope.$new();
var route = { var route = {
current: { current: {
locals: { locals: {
query: new Query(mockData.query) query: new Query(MockData.query)
} }
} }
}; };

View File

@@ -0,0 +1,89 @@
'use strict';
describe('VisualizationRenderer', function() {
var element;
var scope;
var filters = [{
"name": "name::filter",
"friendlyName": "Name",
"values": ["test@example.com", "amirn@example.com"],
"multiple": false
}];
beforeEach(module('redash', 'redashMocks'));
// loading templates
beforeEach(module('app/views/grid_renderer.html',
'app/views/visualizations/filters.html'));
// serving templates
beforeEach(inject(function($httpBackend, $templateCache) {
$httpBackend.whenGET('/views/grid_renderer.html')
.respond($templateCache.get('app/views/grid_renderer.html'));
$httpBackend.whenGET('/views/visualizations/filters.html')
.respond($templateCache.get('app/views/visualizations/filters.html'));
}));
// directive setup
beforeEach(inject(function($rootScope, $compile, MockData, QueryResult) {
var qr = new QueryResult(MockData.queryResult)
qr.filters = filters;
$rootScope.queryResult = qr;
element = angular.element(
'<visualization-renderer query-result="queryResult">' +
'</visualization-renderer>');
}));
describe('scope', function() {
beforeEach(inject(function($rootScope, $compile) {
$compile(element)($rootScope);
// we will test the isolated scope of the directive
scope = element.isolateScope();
scope.$digest();
}));
it('should have filters', function() {
expect(scope.filters).toBeDefined();
});
});
describe('URL binding', function() {
beforeEach(inject(function($rootScope, $compile, $location) {
spyOn($location, 'search').andCallThrough();
// set initial search
var initialSearch = {};
initialSearch[filters[0].friendlyName] = filters[0].values[0];
$location.search('filters', initialSearch);
$compile(element)($rootScope);
// we will test the isolated scope of the directive
scope = element.isolateScope();
scope.$digest();
}));
it('should update scope from URL',
inject(function($location) {
expect($location.search).toHaveBeenCalled();
expect(scope.filters[0].current).toEqual(filters[0].values[0]);
}));
it('should update URL from scope',
inject(function($location) {
scope.filters[0].current = 'newValue';
scope.$digest();
var searchFilters = angular.fromJson($location.search().filters);
expect(searchFilters[filters[0].friendlyName]).toEqual('newValue');
}));
});
});