mistercrunch closed pull request #3375: Improved Datatables View (and some 
other minor improvements as well)
URL: https://github.com/apache/incubator-superset/pull/3375
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/setup.py b/setup.py
index 0133596835..fa64677de6 100644
--- a/setup.py
+++ b/setup.py
@@ -59,7 +59,7 @@ def get_git_sha():
         'humanize==0.5.1',
         'gunicorn==19.7.1',
         'markdown==2.6.8',
-        'pandas==0.20.2',
+        'pandas==0.20.3',
         'parsedatetime==2.0.0',
         'pydruid==0.3.1',
         'PyHive>=0.4.0',
diff --git a/superset/assets/backendSync.json b/superset/assets/backendSync.json
index 71e7130328..e1285c6fa2 100644
--- a/superset/assets/backendSync.json
+++ b/superset/assets/backendSync.json
@@ -2006,6 +2006,18 @@
       "default": false,
       "description": "Whether to include a time filter"
     },
+    "filter_label": {
+      "type": "CheckboxControl",
+      "label": "Display Label",
+      "default": true,
+      "description": "Whether to display a label above each filter"
+    },
+    "display_metric": {
+      "type": "CheckboxControl",
+      "label": "Display Metric",
+      "default": false,
+      "description": "Whether to display the metric number next to the filter 
option"
+    },
     "show_datatable": {
       "type": "CheckboxControl",
       "label": "Data Table",
@@ -2019,6 +2031,12 @@
       "default": false,
       "description": "Whether to include a client side search box"
     },
+    "csv_button": {
+      "type": "CheckboxControl",
+      "label": "CSV Button",
+      "default": false,
+      "description": "Whether to include a button to export filtered table to 
csv"
+    },
     "table_filter": {
       "type": "CheckboxControl",
       "label": "Table Filter",
@@ -2371,4 +2389,4 @@
       "description": "The number of seconds before expiring the cache"
     }
   }
-}
\ No newline at end of file
+}
diff --git a/superset/assets/javascripts/explore/stores/controls.jsx 
b/superset/assets/javascripts/explore/stores/controls.jsx
index ce5ee43fb4..a899399ad2 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -971,6 +971,20 @@ export const controls = {
     description: 'Whether to include a time filter',
   },
 
+  filter_label: {
+    type: 'CheckboxControl',
+    label: 'Display Label',
+    default: true,
+    description: 'Whether to display a label above each filter',
+  },
+
+  display_metric: {
+    type: 'CheckboxControl',
+    label: 'Display Metric',
+    default: false,
+    description: 'Whether to display the metric number next to the filter 
option',
+  },
+
   show_datatable: {
     type: 'CheckboxControl',
     label: 'Data Table',
@@ -986,6 +1000,13 @@ export const controls = {
     description: 'Whether to include a client side search box',
   },
 
+  csv_button: {
+    type: 'CheckboxControl',
+    label: 'CSV Button',
+    default: false,
+    description: 'Whether to include a button to export filtered table to csv',
+  },
+
   table_filter: {
     type: 'CheckboxControl',
     label: 'Table Filter',
diff --git a/superset/assets/javascripts/explore/stores/visTypes.js 
b/superset/assets/javascripts/explore/stores/visTypes.js
index d3ee320f07..964bcbab2e 100644
--- a/superset/assets/javascripts/explore/stores/visTypes.js
+++ b/superset/assets/javascripts/explore/stores/visTypes.js
@@ -342,6 +342,7 @@ export const visTypes = {
           ['table_timestamp_format'],
           ['row_limit', 'page_length'],
           ['include_search', 'table_filter'],
+          ['csv_button'],
         ],
       },
     ],
@@ -868,7 +869,9 @@ export const visTypes = {
         expanded: true,
         controlSetRows: [
           ['groupby'],
+          ['filter_label'],
           ['metric'],
+          ['display_metric'],
         ],
       },
       {
diff --git a/superset/assets/javascripts/modules/utils.js 
b/superset/assets/javascripts/modules/utils.js
index 61949c7e2b..d8e3accb7f 100644
--- a/superset/assets/javascripts/modules/utils.js
+++ b/superset/assets/javascripts/modules/utils.js
@@ -123,7 +123,12 @@ export function toggleCheckbox(apiUrlPrefix, selector) {
  */
 export const fixDataTableBodyHeight = function ($tableDom, height) {
   const headHeight = $tableDom.find('.dataTables_scrollHead').height();
-  $tableDom.find('.dataTables_scrollBody').css('max-height', height - 
headHeight);
+  const headerFooterHeight = $tableDom.find('.table-header').height() +
+      $tableDom.find('.table-footer').height();
+  $tableDom.find('.dataTables_scrollBody').css('max-height', height - 
(headerFooterHeight + headHeight));
+  $tableDom.find('.table-body').css('max-height', height - headerFooterHeight);
+  $tableDom.find('.dataTables_scroll').css('max-height', height - 
headerFooterHeight);
+  $tableDom.find('.table-data').css('max-height', height - headerFooterHeight);
 };
 
 export function d3format(format, number) {
diff --git a/superset/assets/package.json b/superset/assets/package.json
index f39b253781..1eb9338e22 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -54,6 +54,10 @@
     "datatables-bootstrap3-plugin": "^0.5.0",
     "datatables.net": "^1.10.13",
     "datatables.net-bs": "^1.10.12",
+    "datatables.net-buttons": "^1.3.1",
+    "datatables.net-buttons-bs": "^1.3.1",
+    "gridster": "^0.5.6",
+    "immutability-helper": "^2.0.0",
     "immutable": "^3.8.1",
     "jquery": "^3.2.1",
     "lodash.throttle": "^4.1.1",
diff --git a/superset/assets/stylesheets/buttons.dataTables.min.css 
b/superset/assets/stylesheets/buttons.dataTables.min.css
new file mode 100644
index 0000000000..112078b3b7
--- /dev/null
+++ b/superset/assets/stylesheets/buttons.dataTables.min.css
@@ -0,0 +1 @@
+@keyframes dtb-spinner{100%{transform:rotate(360deg)}}@-o-keyframes 
dtb-spinner{100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes
 
dtb-spinner{100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes
 
dtb-spinner{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes
 
dtb-spinner{100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}div.dt-button-info{position:fixed;top:50%;left:50%;width:400px;margin-top:-100px;margin-left:-200px;background-color:white;border:2px
 solid #111;box-shadow:3px 3px 8px 
rgba(0,0,0,0.3);border-radius:3px;text-align:center;z-index:21}div.dt-button-info
 h2{padding:0.5em;margin:0;font-weight:normal;border-bottom:1px solid 
#ddd;background-color:#f3f3f3}div.dt-button-info>div{padding:1em}button.dt-button,div.dt-button,a.dt-button{position:relative;display:inline-block;box-sizing:border-box;margin-right:0.333em;padding:0.5em
 1em;border:1px solid 
#999;border-radius:2px;cursor:pointer;font-size:0.88em;color:black;white-space:nowrap;overflow:hidden;background-color:#e9e9e9;background-image:-webkit-linear-gradient(top,
 #fff 0%, #e9e9e9 100%);background-image:-moz-linear-gradient(top, #fff 0%, 
#e9e9e9 100%);background-image:-ms-linear-gradient(top, #fff 0%, #e9e9e9 
100%);background-image:-o-linear-gradient(top, #fff 0%, #e9e9e9 
100%);background-image:linear-gradient(to bottom, #fff 0%, #e9e9e9 
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='white',
 
EndColorStr='#e9e9e9');-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none;outline:none}button.dt-button.disabled,div.dt-button.disabled,a.dt-button.disabled{color:#999;border:1px
 solid 
#d0d0d0;cursor:default;background-color:#f9f9f9;background-image:-webkit-linear-gradient(top,
 #fff 0%, #f9f9f9 100%);background-image:-moz-linear-gradient(top, #fff 0%, 
#f9f9f9 100%);background-image:-ms-linear-gradient(top, #fff 0%, #f9f9f9 
100%);background-image:-o-linear-gradient(top, #fff 0%, #f9f9f9 
100%);background-image:linear-gradient(to bottom, #fff 0%, #f9f9f9 
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#ffffff',
 
EndColorStr='#f9f9f9')}button.dt-button:active:not(.disabled),button.dt-button.active:not(.disabled),div.dt-button:active:not(.disabled),div.dt-button.active:not(.disabled),a.dt-button:active:not(.disabled),a.dt-button.active:not(.disabled){background-color:#e2e2e2;background-image:-webkit-linear-gradient(top,
 #f3f3f3 0%, #e2e2e2 100%);background-image:-moz-linear-gradient(top, #f3f3f3 
0%, #e2e2e2 100%);background-image:-ms-linear-gradient(top, #f3f3f3 0%, #e2e2e2 
100%);background-image:-o-linear-gradient(top, #f3f3f3 0%, #e2e2e2 
100%);background-image:linear-gradient(to bottom, #f3f3f3 0%, #e2e2e2 
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f3f3f3',
 EndColorStr='#e2e2e2');box-shadow:inset 1px 1px 3px 
#999999}button.dt-button:active:not(.disabled):hover:not(.disabled),button.dt-button.active:not(.disabled):hover:not(.disabled),div.dt-button:active:not(.disabled):hover:not(.disabled),div.dt-button.active:not(.disabled):hover:not(.disabled),a.dt-button:active:not(.disabled):hover:not(.disabled),a.dt-button.active:not(.disabled):hover:not(.disabled){box-shadow:inset
 1px 1px 3px 
#999999;background-color:#cccccc;background-image:-webkit-linear-gradient(top, 
#eaeaea 0%, #ccc 100%);background-image:-moz-linear-gradient(top, #eaeaea 0%, 
#ccc 100%);background-image:-ms-linear-gradient(top, #eaeaea 0%, #ccc 
100%);background-image:-o-linear-gradient(top, #eaeaea 0%, #ccc 
100%);background-image:linear-gradient(to bottom, #eaeaea 0%, #ccc 
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#eaeaea',
 
EndColorStr='#cccccc')}button.dt-button:hover,div.dt-button:hover,a.dt-button:hover{text-decoration:none}button.dt-button:hover:not(.disabled),div.dt-button:hover:not(.disabled),a.dt-button:hover:not(.disabled){border:1px
 solid 
#666;background-color:#e0e0e0;background-image:-webkit-linear-gradient(top, 
#f9f9f9 0%, #e0e0e0 100%);background-image:-moz-linear-gradient(top, #f9f9f9 
0%, #e0e0e0 100%);background-image:-ms-linear-gradient(top, #f9f9f9 0%, #e0e0e0 
100%);background-image:-o-linear-gradient(top, #f9f9f9 0%, #e0e0e0 
100%);background-image:linear-gradient(to bottom, #f9f9f9 0%, #e0e0e0 
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f9f9f9',
 
EndColorStr='#e0e0e0')}button.dt-button:focus:not(.disabled),div.dt-button:focus:not(.disabled),a.dt-button:focus:not(.disabled){border:1px
 solid #426c9e;text-shadow:0 1px 0 
#c4def1;outline:none;background-color:#79ace9;background-image:-webkit-linear-gradient(top,
 #bddef4 0%, #79ace9 100%);background-image:-moz-linear-gradient(top, #bddef4 
0%, #79ace9 100%);background-image:-ms-linear-gradient(top, #bddef4 0%, #79ace9 
100%);background-image:-o-linear-gradient(top, #bddef4 0%, #79ace9 
100%);background-image:linear-gradient(to bottom, #bddef4 0%, #79ace9 
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#bddef4',
 EndColorStr='#79ace9')}.dt-button 
embed{outline:none}div.dt-buttons{position:relative;float:left}div.dt-buttons.buttons-right{float:right}div.dt-button-collection{position:absolute;top:0;left:0;width:150px;margin-top:3px;padding:8px
 8px 4px 8px;border:1px solid #ccc;border:1px solid 
rgba(0,0,0,0.4);background-color:white;overflow:hidden;z-index:2002;border-radius:5px;box-shadow:3px
 3px 5px 
rgba(0,0,0,0.3);z-index:2002;-webkit-column-gap:8px;-moz-column-gap:8px;-ms-column-gap:8px;-o-column-gap:8px;column-gap:8px}div.dt-button-collection
 button.dt-button,div.dt-button-collection 
div.dt-button,div.dt-button-collection 
a.dt-button{position:relative;left:0;right:0;display:block;float:none;margin-bottom:4px;margin-right:0}div.dt-button-collection
 button.dt-button:active:not(.disabled),div.dt-button-collection 
button.dt-button.active:not(.disabled),div.dt-button-collection 
div.dt-button:active:not(.disabled),div.dt-button-collection 
div.dt-button.active:not(.disabled),div.dt-button-collection 
a.dt-button:active:not(.disabled),div.dt-button-collection 
a.dt-button.active:not(.disabled){background-color:#dadada;background-image:-webkit-linear-gradient(top,
 #f0f0f0 0%, #dadada 100%);background-image:-moz-linear-gradient(top, #f0f0f0 
0%, #dadada 100%);background-image:-ms-linear-gradient(top, #f0f0f0 0%, #dadada 
100%);background-image:-o-linear-gradient(top, #f0f0f0 0%, #dadada 
100%);background-image:linear-gradient(to bottom, #f0f0f0 0%, #dadada 
100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f0f0f0',
 EndColorStr='#dadada');box-shadow:inset 1px 1px 3px 
#666}div.dt-button-collection.fixed{position:fixed;top:50%;left:50%;margin-left:-75px;border-radius:0}div.dt-button-collection.fixed.two-column{margin-left:-150px}div.dt-button-collection.fixed.three-column{margin-left:-225px}div.dt-button-collection.fixed.four-column{margin-left:-300px}div.dt-button-collection>*{-webkit-column-break-inside:avoid;break-inside:avoid}div.dt-button-collection.two-column{width:300px;padding-bottom:1px;-webkit-column-count:2;-moz-column-count:2;-ms-column-count:2;-o-column-count:2;column-count:2}div.dt-button-collection.three-column{width:450px;padding-bottom:1px;-webkit-column-count:3;-moz-column-count:3;-ms-column-count:3;-o-column-count:3;column-count:3}div.dt-button-collection.four-column{width:600px;padding-bottom:1px;-webkit-column-count:4;-moz-column-count:4;-ms-column-count:4;-o-column-count:4;column-count:4}div.dt-button-background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center,
 ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 
100%);background:-moz-radial-gradient(center, ellipse farthest-corner, 
rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, 
ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 
100%);background:-webkit-gradient(radial, center center, 0, center center, 497, 
color-stop(0, rgba(0,0,0,0.3)), color-stop(1, 
rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse 
farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 
100%);background:radial-gradient(ellipse farthest-corner at center, 
rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:2001}@media screen and 
(max-width: 640px){div.dt-buttons{float:none 
!important;text-align:center}}button.dt-button.processing,div.dt-button.processing,a.dt-button.processing{color:rgba(0,0,0,0.2)}button.dt-button.processing:after,div.dt-button.processing:after,a.dt-button.processing:after{position:absolute;top:50%;left:50%;width:16px;height:16px;margin:-8px
 0 0 -8px;box-sizing:border-box;display:block;content:' ';border:2px solid 
#282828;border-radius:50%;border-left-color:transparent;border-right-color:transparent;animation:dtb-spinner
 1500ms infinite linear;-o-animation:dtb-spinner 1500ms infinite 
linear;-ms-animation:dtb-spinner 1500ms infinite 
linear;-webkit-animation:dtb-spinner 1500ms infinite 
linear;-moz-animation:dtb-spinner 1500ms infinite linear}
diff --git a/superset/assets/stylesheets/dashboard.css 
b/superset/assets/stylesheets/dashboard.css
index ad84807aa1..bbd9537c50 100644
--- a/superset/assets/stylesheets/dashboard.css
+++ b/superset/assets/stylesheets/dashboard.css
@@ -130,3 +130,8 @@ div.widget .chart-controls {
 .slice_container .alert {
     margin: 10px;
 }
+
+.row {
+    margin-left: 0px;
+    margin-right: 0px;
+}
diff --git a/superset/assets/stylesheets/superset.less 
b/superset/assets/stylesheets/superset.less
index 5c0c4d619c..8d9fc14b86 100644
--- a/superset/assets/stylesheets/superset.less
+++ b/superset/assets/stylesheets/superset.less
@@ -195,6 +195,10 @@ div.widget .slice_container {
     margin-bottom: 0;
 }
 
+.navbar-brand-image {
+  height: 100%;
+}
+
 .table-condensed {
     font-size: 12px;
 }
diff --git a/superset/assets/visualizations/filter_box.jsx 
b/superset/assets/visualizations/filter_box.jsx
index a5de26a810..60a48d7d0a 100644
--- a/superset/assets/visualizations/filter_box.jsx
+++ b/superset/assets/visualizations/filter_box.jsx
@@ -10,18 +10,22 @@ import { TIME_CHOICES } from './constants';
 import './filter_box.css';
 
 const propTypes = {
-  origSelectedValues: PropTypes.object,
-  instantFiltering: PropTypes.bool,
   filtersChoices: PropTypes.object,
   onChange: PropTypes.func,
   showDateFilter: PropTypes.bool,
   datasource: PropTypes.object.isRequired,
+  showLabel: PropTypes.bool,
+  showMetricNumber: PropTypes.bool,
+  origSelectedValues: PropTypes.object,
+  instantFiltering: PropTypes.bool,
 };
 
 const defaultProps = {
-  origSelectedValues: {},
   onChange: () => {},
   showDateFilter: false,
+  showLabel: true,
+  showMetricNumber: false,
+  origSelectedValues: {},
   instantFiltering: true,
 };
 
@@ -100,7 +104,7 @@ class FilterBox extends React.Component {
       });
       return (
         <div key={filter} className="m-b-5">
-          {this.props.datasource.verbose_map[filter] || filter}
+          {this.props.showLabel && (this.props.datasource.verbose_map[filter] 
|| filter)}
           <Select.Creatable
             placeholder={`Select [${filter}]`}
             key={filter}
@@ -116,7 +120,13 @@ class FilterBox extends React.Component {
                 backgroundImage,
                 padding: '2px 5px',
               };
-              return { value: opt.id, label: opt.id, style };
+              let label;
+              if (this.props.showMetricNumber) {
+                label = opt.id.concat(' [', opt.metric, ']');
+              } else {
+                label = opt.id;
+              }
+              return { value: opt.id, label, style };
             })}
             onChange={this.changeFilter.bind(this, filter)}
           />
@@ -163,6 +173,8 @@ function filterBox(slice, payload) {
       onChange={slice.addFilter}
       showDateFilter={fd.date_filter}
       datasource={slice.datasource}
+      showLabel={fd.filter_label}
+      showMetricNumber={fd.display_metric}
       origSelectedValues={slice.getFilters() || {}}
       instantFiltering={fd.instant_filtering}
     />,
diff --git a/superset/assets/visualizations/table.css 
b/superset/assets/visualizations/table.css
index 84c7e27351..9de4ec9c83 100644
--- a/superset/assets/visualizations/table.css
+++ b/superset/assets/visualizations/table.css
@@ -27,3 +27,27 @@ table.table thead th.sorting:after, table.table thead 
th.sorting_asc:after, tabl
 .like-pre {
   white-space: pre-wrap;
 }
+
+.dataTables_length {
+  display: inline-block;
+  padding: 4px 10px;
+  border: 1px;
+  margin: 0px;
+}
+
+.buttons-html5 {
+  padding: 4px 10px;
+}
+
+.dt-buttons {
+  margin: 0px;
+}
+
+.dataTables_filter {
+  margin: 4px;
+}
+
+.dataTables_scroll {
+  height: 100%;
+}
+
diff --git a/superset/assets/visualizations/table.js 
b/superset/assets/visualizations/table.js
index 82b0305864..d444c576ff 100644
--- a/superset/assets/visualizations/table.js
+++ b/superset/assets/visualizations/table.js
@@ -1,14 +1,12 @@
 import d3 from 'd3';
 import 'datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css';
-import 'datatables.net';
-import dt from 'datatables.net-bs';
-
-import { fixDataTableBodyHeight, d3TimeFormatPreset } from 
'../javascripts/modules/utils';
+import { fixDataTableBodyHeight } from '../javascripts/modules/utils';
 import './table.css';
 
 const $ = require('jquery');
-
-dt(window, $);
+require('datatables.net-bs')(window, $);
+require('datatables.net-buttons-bs')(window, $);
+require('datatables.net-buttons/js/buttons.html5.js')(window, $);
 
 function tableVis(slice, payload) {
   const container = $(slice.selector);
@@ -33,17 +31,26 @@ function tableVis(slice, payload) {
     maxes[metrics[i]] = d3.max(col(metrics[i]));
   }
 
-  const tsFormatter = d3TimeFormatPreset(fd.table_timestamp_format);
-
   const div = d3.select(slice.selector);
   div.html('');
   const table = div.append('table')
     .classed(
       'dataframe dataframe table table-striped table-bordered ' +
-      'table-condensed table-hover dataTable no-footer', true)
+      'table-condensed table-hover dataTable', true)
     .attr('width', '100%');
 
   const cols = data.columns.map(c => slice.datasource.verbose_map[c] || c);
+  const height = slice.height();
+  let paging = false;
+  let pageLength;
+  if (fd.page_length && fd.page_length > 0) {
+    paging = true;
+    pageLength = parseInt(fd.page_length, 10);
+  }
+  const buttons = [];
+  if (fd.csv_button) {
+    buttons.push('csvHtml5');
+  }
 
   table.append('thead').append('tr')
     .selectAll('th')
@@ -54,89 +61,106 @@ function tableVis(slice, payload) {
       return d;
     });
 
-  table.append('tbody')
-    .selectAll('tr')
-    .data(data.records)
-    .enter()
-    .append('tr')
-    .selectAll('td')
-    .data(row => data.columns.map((c) => {
-      const val = row[c];
-      let html;
-      const isMetric = metrics.indexOf(c) >= 0;
-      if (c === '__timestamp') {
-        html = tsFormatter(val);
-      }
-      if (typeof (val) === 'string') {
-        html = `<span class="like-pre">${val}</span>`;
-      }
-      if (isMetric) {
-        html = slice.d3format(c, val);
-      }
-      return {
-        col: c,
-        val,
-        html,
-        isMetric,
-      };
-    }))
-    .enter()
-    .append('td')
-    .style('background-image', function (d) {
-      if (d.isMetric) {
-        const perc = Math.round((d.val / maxes[d.col]) * 100);
-        // The 0.01 to 0.001 is a workaround for what appears to be a
-        // CSS rendering bug on flat, transparent colors
-        return (
-          `linear-gradient(to left, rgba(0,0,0,0.2), rgba(0,0,0,0.2) ${perc}%, 
` +
-          `rgba(0,0,0,0.01) ${perc}%, rgba(0,0,0,0.001) 100%)`
-        );
-      }
-      return null;
-    })
-    .classed('text-right', d => d.isMetric)
-    .attr('title', (d) => {
-      if (!isNaN(d.val)) {
-        return fC(d.val);
-      }
-      return null;
-    })
-    .attr('data-sort', function (d) {
-      return (d.isMetric) ? d.val : null;
-    })
-    .on('click', function (d) {
-      if (!d.isMetric && fd.table_filter) {
-        const td = d3.select(this);
-        if (td.classed('filtered')) {
-          slice.removeFilter(d.col, [d.val]);
-          d3.select(this).classed('filtered', false);
-        } else {
-          d3.select(this).classed('filtered', true);
-          slice.addFilter(d.col, [d.val]);
+  let datatable;
+
+  if ((!data.columns.find(c => metrics.indexOf(c) >= 0))) {
+    const columns = data.columns.map(c => ({ data: c }));
+    datatable = container.find('.dataTable').DataTable({
+      data: data.records,
+      columns,
+      paging,
+      scrollY: true,
+      deferRender: true,
+      pageLength,
+      searching: fd.include_search,
+      dom: '<"row table-header"<"col-sm-6"lB><"col-sm-6"f>>' +
+           '<"row table-body"<"col-sm-12 table-data"tr>>' +
+           '<"row table-footer"<"col-sm-5"i><"col-sm-7"p>>',
+      buttons,
+    });
+  } else {
+    table.append('tbody')
+      .selectAll('tr')
+      .data(data.records)
+      .enter()
+      .append('tr')
+      .selectAll('td')
+      .data(row => data.columns.map((c) => {
+        const val = row[c];
+        let html;
+        const isMetric = metrics.indexOf(c) >= 0;
+        if (c === 'timestamp') {
+          html = timestampFormatter(val);
         }
-      }
-    })
-    .style('cursor', function (d) {
-      return (!d.isMetric) ? 'pointer' : '';
-    })
-    .html(d => d.html ? d.html : d.val);
-  const height = slice.height();
-  let paging = false;
-  let pageLength;
-  if (fd.page_length && fd.page_length > 0) {
-    paging = true;
-    pageLength = parseInt(fd.page_length, 10);
+        if (typeof (val) === 'string') {
+          html = `<span class="like-pre">${val}</span>`;
+        }
+        if (isMetric) {
+          html = slice.d3format(c, val);
+        }
+        return {
+          col: c,
+          val,
+          html,
+          isMetric,
+        };
+      }))
+      .enter()
+      .append('td')
+      .style('background-image', function (d) {
+        if (d.isMetric) {
+          const perc = Math.round((d.val / maxes[d.col]) * 100);
+          // The 0.01 to 0.001 is a workaround for what appears to be a
+          // CSS rendering bug on flat, transparent colors
+          return (
+            `linear-gradient(to right, rgba(0,0,0,0.2), rgba(0,0,0,0.2) 
${perc}%, ` +
+            `rgba(0,0,0,0.01) ${perc}%, rgba(0,0,0,0.001) 100%)`
+          );
+        }
+        return null;
+      })
+      .attr('title', (d) => {
+        if (!isNaN(d.val)) {
+          return fC(d.val);
+        }
+        return null;
+      })
+      .attr('data-sort', function (d) {
+        return (d.isMetric) ? d.val : null;
+      })
+      .on('click', function (d) {
+        if (!d.isMetric && fd.table_filter) {
+          const td = d3.select(this);
+          if (td.classed('filtered')) {
+            slice.removeFilter(d.col, [d.val]);
+            d3.select(this).classed('filtered', false);
+          } else {
+            d3.select(this).classed('filtered', true);
+            slice.addFilter(d.col, [d.val]);
+          }
+        }
+      })
+      .style('cursor', function (d) {
+        return (!d.isMetric) ? 'pointer' : '';
+      })
+      .html(d => d.html ? d.html : d.val);
+
+    datatable = container.find('.dataTable').DataTable({
+      paging,
+      scrollY: true,
+      deferRender: true,
+      pageLength,
+      aaSorting: [],
+      searching: fd.include_search,
+      bInfo: false,
+      buttons,
+      dom: '<"row table-header"<"col-sm-6"lB><"col-sm-6"f>>' +
+           '<"row table-body"<"col-sm-12 table-data"tr>>' +
+           '<"row table-footer"<"col-sm-5"i><"col-sm-7"p>>',
+    });
   }
-  const datatable = container.find('.dataTable').DataTable({
-    paging,
-    pageLength,
-    aaSorting: [],
-    searching: fd.include_search,
-    bInfo: false,
-    scrollY: height + 'px',
-    scrollCollapse: true,
-    scrollX: true,
-  });
+  datatable.buttons().container().appendTo('.dataTables_wrapper 
.col-sm-6:eq(0)');
+
   fixDataTableBodyHeight(
       container.find('.dataTables_wrapper'), height);
   // Sorting table by main column
diff --git a/superset/templates/appbuilder/navbar.html 
b/superset/templates/appbuilder/navbar.html
index 0ea2daec5f..bee0ccc741 100644
--- a/superset/templates/appbuilder/navbar.html
+++ b/superset/templates/appbuilder/navbar.html
@@ -12,6 +12,7 @@
       </button>
       <a class="navbar-brand" href="/superset/profile/{{ current_user.username 
}}/">
         <img
+          class="navbar-brand-image"
           width="126" src="{{ appbuilder.app_icon }}"
           alt="{{ appbuilder.app_name }}"
         />
diff --git a/superset/views/core.py b/superset/views/core.py
index 65d3feb500..53bdf6d66e 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -61,7 +61,6 @@
 USER_MISSING_ERR = __("The user seems to have been deleted")
 DATASOURCE_ACCESS_ERR = __("You don't have access to this datasource")
 
-
 def get_database_access_error_msg(database_name):
     return __("This view requires the database %(name)s or "
               "`all_datasource_access` permission", name=database_name)
@@ -2229,7 +2228,7 @@ def show_traceback(self):
     def welcome(self):
         """Personalized welcome page"""
         if not g.user or not g.user.get_id():
-            return redirect(appbuilder.get_url_for_login)
+            return appbuilder.sm.auth_view.login()
         return self.render_template(
             'superset/welcome.html', entry='welcome', utils=utils)
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to