]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5207 Add breadcrumbs. Zooming in both directions.
authorStas Vilchik <vilchiks@gmail.com>
Tue, 8 Jul 2014 11:21:44 +0000 (17:21 +0600)
committerStas Vilchik <vilchiks@gmail.com>
Tue, 8 Jul 2014 11:21:53 +0000 (17:21 +0600)
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_treemap.html.erb
sonar-server/src/main/coffee/widgets/treemap.coffee
sonar-server/src/main/less/icons.less
sonar-server/src/main/less/style.less

index 67d2bd13cfb46594f10ee91a5cef04b15243cd06..36334a8e333a84094fba550a630ece728ce96cd5 100644 (file)
@@ -42,6 +42,7 @@
         .metricsPriority(metrics)
         .options({
           heightInPercents: '<%= widget_properties['heightInPercents'] -%>',
+          maxItems: <%= maxItems -%>,
           maxItemsReachedMessage: '<%= message("widget.measure_filter_histogram.max_items_reached", :params => [maxItems]) -%>',
           baseUrl: baseUrl + '/dashboard/index/',
           noData: '<%= message('no_data') -%>'
index 91ebfa623e1b5d3f7840b116b7dc287b87489208..6b2ce67acca7a0cd75fbf5bc64e7daeeaa8eb291 100644 (file)
@@ -1,7 +1,7 @@
 class Treemap extends window.SonarWidgets.BaseWidget
   sizeLow: 11
   sizeMedium: 13
-  sizeHigh: 24
+  sizeHigh: 20
 
 
   constructor: ->
@@ -31,19 +31,23 @@ class Treemap extends window.SonarWidgets.BaseWidget
     @cellsInner = @box.selectAll('.treemap-inner').data nodes
     @cellsInner.text (d) -> d.longName
 
-#    cellsLink = cellsEnter.append('a').classed 'treemap-dashboard', true
-#    cellsLink.attr 'href', (d) =>
-#      url = @options().baseUrl + encodeURIComponent(d.key)
-#      url += '?metric=' + encodeURIComponent(@colorMetric.key) if d.qualifier == 'CLA' || d.qualifier == 'FIL'
-#      url
-#    cellsLink.append('i').attr 'class', (d) -> "icon-qualifier-#{d.qualifier.toLowerCase()}"
-
     @attachEvents cellsEnter
 
+    # Show maxResultsReached message
+    @maxResultsReachedLabel.style 'display', if @maxResultsReached() then 'block' else 'none'
+
+
+  updateTreemap: (components, maxResultsReached) ->
+    @components components
+    @maxResultsReached maxResultsReached
+
+    @renderTreemap()
+    @positionCells()
+
 
   attachEvents: (cells) ->
     cells.on 'click', (d) =>
-      @requestChildren d.key
+      @requestChildren d
 
 
   positionCells: ->
@@ -53,8 +57,6 @@ class Treemap extends window.SonarWidgets.BaseWidget
     @cells.style 'height', (d) -> "#{d.dy}px"
     @cells.style 'line-height', (d) -> "#{d.dy}px"
     @cells.style 'font-size', (d) => "#{@size d.dx}px"
-#    @cells.classed 'treemap-cell-small', (d) -> d.dy < 60
-#    @cells.classed 'treemap-cell-very-small', (d) -> d.dx < 20 || d.dy < 20
 
 
   renderLegend: (box) ->
@@ -64,10 +66,43 @@ class Treemap extends window.SonarWidgets.BaseWidget
     @legend.append('span').classed('legend-text', true).html "Size: <span class='legend-text-main'>#{@sizeMetric.name}</span>"
     @legend.append('span').classed('legend-text', true).html "Color: <span class='legend-text-main'>#{@colorMetric.name}</span>"
 
-    # Show maxResultsReached message
-    if @maxResultsReached()
-      maxResultsReachedLabel = box.append('div').text @options().maxItemsReachedMessage
-      maxResultsReachedLabel.classed 'max-results-reached-message', true
+
+  renderBreadcrumbs: (box) ->
+    @breadcrumbsBox = box.append('div').classed 'treemap-breadcrumbs', true
+    @breadcrumbs = []
+    d = name: '<i class="icon-home"></i>', components: @components(), maxResultsReached: @maxResultsReached()
+    @addToBreadcrumbs d
+
+
+  updateBreadcrumbs: ->
+    breadcrumbs = @breadcrumbsBox.selectAll('.treemap-breadcrumbs-item').data @breadcrumbs
+    breadcrumbs.exit().remove()
+    breadcrumbsEnter = breadcrumbs.enter().append('span').classed 'treemap-breadcrumbs-item', true
+    breadcrumbsEnter.append('i').classed('icon-chevron-right', true).style 'display', (d, i) ->
+      if i > 0 then 'inline' else 'none'
+    breadcrumbsEnter.append('i').attr 'class', (d) ->
+      if d.qualifier? then "icon-qualifier-#{d.qualifier.toLowerCase()}" else ''
+    breadcrumbsEnterLinks = breadcrumbsEnter.append 'a'
+    breadcrumbsEnterLinks.classed 'underlined-link', (d, i) -> i > 0
+    breadcrumbsEnterLinks.html (d) -> d.name
+    breadcrumbsEnterLinks.on 'click', (d) =>
+      @updateTreemap d.components, d.maxResultsReached
+      @cutBreadcrumbs d
+    @breadcrumbsBox.style 'display', if @breadcrumbs.length < 2 then 'none' else 'block'
+
+
+  addToBreadcrumbs: (d) ->
+    @breadcrumbs.push d
+    @updateBreadcrumbs()
+
+
+  cutBreadcrumbs: (lastElement) ->
+    index = null
+    @breadcrumbs.forEach (d, i) ->
+      index = i if d.key == lastElement.key
+    if index?
+      @breadcrumbs = _.initial @breadcrumbs, @breadcrumbs.length - index - 1
+      @updateBreadcrumbs()
 
 
   render: (container) ->
@@ -90,7 +125,11 @@ class Treemap extends window.SonarWidgets.BaseWidget
     @treemap = d3.layout.treemap()
     @treemap.value (d) => @sizeMetric.value d
 
+    @maxResultsReachedLabel = box.append('div').text @options().maxItemsReachedMessage
+    @maxResultsReachedLabel.classed 'max-results-reached-message', true
+
     @renderLegend box
+    @renderBreadcrumbs box
     @renderTreemap()
     super
 
@@ -107,29 +146,34 @@ class Treemap extends window.SonarWidgets.BaseWidget
   stopDrilldown: ->
 
 
+  formatComponents: (data) ->
+    components = _.filter data, (component) =>
+      hasSizeMetric = => _.findWhere component.msr, key: @sizeMetric.key
+      _.isArray(component.msr) && component.msr.length > 0 && hasSizeMetric()
+
+    if _.isArray(components) && components.length > 0
+      components.map (component) =>
+        measures = {}
+        component.msr.forEach (measure) ->
+          measures[measure.key] = val: measure.val, fval: measure.frmt_val
+
+        key: component.key
+        name: component.name
+        longName: component.lname
+        qualifier: component.qualifier
+        measures: measures
+
 
-  requestChildren: (key) ->
+  requestChildren: (d) ->
     metrics = @metricsPriority().join ','
     RESOURCES_URL = "#{baseUrl}/api/resources/index"
-    jQuery.get(RESOURCES_URL, resource: key, depth: 1, metrics: metrics).done (r) =>
-      components = _.filter r, (component) =>
-        hasSizeMetric = => _.findWhere component.msr, key: @sizeMetric.key
-        _.isArray(component.msr) && component.msr.length > 0 && hasSizeMetric()
-
-      if _.isArray(components) && components.length > 0
-        components = components.map (component) =>
-          measures = {}
-          component.msr.forEach (measure) ->
-            measures[measure.key] = val: measure.val, fval: measure.frmt_val
-
-          key: component.key
-          name: component.name
-          longName: component.lname
-          qualifier: component.qualifier
-          measures: measures
-        @components components
-        @renderTreemap()
-        @positionCells()
+    jQuery.get(RESOURCES_URL, resource: d.key, depth: 1, metrics: metrics).done (r) =>
+      components = @formatComponents r
+      if components?
+        components = _.sortBy components, (d) => @sizeMetric.value d
+        components = _.initial components, components.length - @options().maxItems - 1
+        @updateTreemap components, components.length > @options().maxItems
+        @addToBreadcrumbs _.extend d, components: components, maxResultsReached: @maxResultsReached()
       else @stopDrilldown()
 
 
index cb6edff97ab17d3e248d2953dbab5fb6c220ea8f..c7ff41c31c317a26a8034304e7cbf0a79afedcd1 100644 (file)
@@ -473,6 +473,10 @@ a[class^="icon-"], a[class*=" icon-"] {
   content: "\f069";
   font-size: @iconFontSize;
 }
+.icon-home:before {
+  content: "\f016";
+  font-size: @iconFontSize;
+}
 
 
 /*
index 325989f14965cc80999b11461b5986a32905751b..4d62543798765750f790f71211bae9f6184ce8aa 100644 (file)
@@ -2859,6 +2859,24 @@ div.rule-title {
   .treemap-dashboard { display: none; }
 }
 
+.sonar-d3 .treemap-breadcrumbs {
+  margin-top: 10px;
+  padding-top: 7px;
+  border-top: 1px solid #E6E6E6;
+}
+
+.sonar-d3 .treemap-breadcrumbs-item {
+  color: @secondFontColor;
+
+  & > [class^="icon-qualifier-"] { margin-right: 4px; }
+}
+
+.sonar-d3 .treemap-breadcrumbs-item + .treemap-breadcrumbs-item {
+  margin-left: 10px;
+
+  & > .icon-chevron-right { margin-right: 10px; }
+}
+
 
 /* ------------------- Admin pages ------------------- */