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