The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/plugins/panel/table-old/module.ts

272 lines
7.4 KiB

import _ from 'lodash';
import $ from 'jquery';
import { MetricsPanelCtrl } from 'app/plugins/sdk';
import config from 'app/core/config';
import { transformDataToTable } from './transformers';
import { tablePanelEditor } from './editor';
import { columnOptionsTab } from './column_options';
import { TableRenderer } from './renderer';
import { isTableData, PanelEvents, PanelPlugin, PanelProps } from '@grafana/data';
Variables: migrates ad hoc variable type to react/redux. (#22784) * Refactor: moves all the newVariables part to features/variables directory * Feature: adds datasource type * Tests: adds reducer tests * Tests: covers data source actions with tests * Chore: reduces strict null errors * boilerplate that will be replaced by real code. * added old editor template. * added initial version of ad hoc editor. * added working (apart from add) version of the editor. * Added placeholder for picker. * Have a working UI. Need to connect it so we refresh the variables on changes. * variable should be updated now. * removed console.log * made the url work. * cleaned up the adapter. * added possiblity to create filter directly from table. * moved infotext from general reducer to extended value of adhoc. * fixed strict null errors. * fixed strict null errors. * fixed issue where remove was displayed before being added. * fixed issue with fragment key. * changed so template_src is using the redux variables. * minor refactorings. * moved adhoc picker to adhoc variable. * adding tests for reducer and fixed bug. * added tests or urlparser. * added tests for ad hoc actions. * added more tests. * added more tests. * fixed strict null error. * fixed copy n pase error. * added utilit for getting new variable index. * removed console.log * added location to reducerTester type and created a module type for it. * changed so we only have one builder pattern. * fixed tests to use static expected values. * fixed strict errors. * fixed more strict errors. Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
5 years ago
import { dispatch } from 'app/store/store';
import { ComponentType } from 'react';
Variables: migrates ad hoc variable type to react/redux. (#22784) * Refactor: moves all the newVariables part to features/variables directory * Feature: adds datasource type * Tests: adds reducer tests * Tests: covers data source actions with tests * Chore: reduces strict null errors * boilerplate that will be replaced by real code. * added old editor template. * added initial version of ad hoc editor. * added working (apart from add) version of the editor. * Added placeholder for picker. * Have a working UI. Need to connect it so we refresh the variables on changes. * variable should be updated now. * removed console.log * made the url work. * cleaned up the adapter. * added possiblity to create filter directly from table. * moved infotext from general reducer to extended value of adhoc. * fixed strict null errors. * fixed strict null errors. * fixed issue where remove was displayed before being added. * fixed issue with fragment key. * changed so template_src is using the redux variables. * minor refactorings. * moved adhoc picker to adhoc variable. * adding tests for reducer and fixed bug. * added tests or urlparser. * added tests for ad hoc actions. * added more tests. * added more tests. * fixed strict null error. * fixed copy n pase error. * added utilit for getting new variable index. * removed console.log * added location to reducerTester type and created a module type for it. * changed so we only have one builder pattern. * fixed tests to use static expected values. * fixed strict errors. * fixed more strict errors. Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
5 years ago
import { applyFilterFromTable } from 'app/features/variables/adhoc/actions';
export class TablePanelCtrl extends MetricsPanelCtrl {
static templateUrl = 'module.html';
pageIndex: number;
dataRaw: any;
table: any;
renderer: any;
panelHasRowColorMode: boolean;
panelHasLinks: boolean;
panelDefaults: any = {
targets: [{}],
transform: 'timeseries_to_columns',
pageSize: null,
showHeader: true,
styles: [
{
type: 'date',
pattern: 'Time',
alias: 'Time',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
align: 'auto',
},
{
unit: 'short',
type: 'number',
alias: '',
decimals: 2,
colors: ['rgba(245, 54, 54, 0.9)', 'rgba(237, 129, 40, 0.89)', 'rgba(50, 172, 45, 0.97)'],
colorMode: null,
pattern: '/.*/',
thresholds: [],
align: 'right',
},
],
columns: [],
fontSize: '100%',
sort: { col: 0, desc: true },
};
/** @ngInject */
constructor($scope: any, $injector: any, private annotationsSrv: any, private $sanitize: any) {
super($scope, $injector);
this.pageIndex = 0;
if (this.panel.styles === void 0) {
this.panel.styles = this.panel.columns;
this.panel.columns = this.panel.fields;
delete this.panel.columns;
delete this.panel.fields;
}
_.defaults(this.panel, this.panelDefaults);
this.panelHasRowColorMode = Boolean(this.panel.styles.find((style: any) => style.colorMode === 'row'));
this.panelHasLinks = Boolean(this.panel.styles.find((style: any) => style.link));
this.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this));
this.events.on(PanelEvents.dataSnapshotLoad, this.onDataReceived.bind(this));
this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this));
}
onInitEditMode() {
this.addEditorTab('Options', tablePanelEditor, 2);
this.addEditorTab('Column Styles', columnOptionsTab, 3);
}
migrateToPanel(type: string) {
this.onPluginTypeChange(config.panels[type]);
}
issueQueries(datasource: any) {
this.pageIndex = 0;
if (this.panel.transform === 'annotations') {
return this.annotationsSrv
.getAnnotations({
dashboard: this.dashboard,
panel: this.panel,
range: this.range,
})
.then((anno: any) => {
this.loading = false;
this.dataRaw = anno;
this.pageIndex = 0;
this.render();
return { data: this.dataRaw }; // Not used
});
}
return super.issueQueries(datasource);
}
onDataReceived(dataList: any) {
this.dataRaw = dataList;
this.pageIndex = 0;
// automatically correct transform mode based on data
if (this.dataRaw && this.dataRaw.length) {
if (isTableData(this.dataRaw[0])) {
this.panel.transform = 'table';
} else {
if (this.dataRaw[0].type === 'docs') {
this.panel.transform = 'json';
} else {
if (this.panel.transform === 'table' || this.panel.transform === 'json') {
this.panel.transform = 'timeseries_to_rows';
}
}
}
}
this.render();
}
render() {
this.table = transformDataToTable(this.dataRaw, this.panel);
this.table.sort(this.panel.sort);
this.renderer = new TableRenderer(
this.panel,
this.table,
DateTime: adding support to select preferred timezone for presentation of date and time values. (#23586) * added moment timezone package. * added a qnd way of selecting timezone. * added a first draft to display how it can be used. * fixed failing tests. * made moment.local to be in utc when running tests. * added tests to verify that the timeZone support works as expected. * Fixed so we use the formatter in the graph context menu. * changed so we will format d3 according to timeZone. * changed from class base to function based for easier consumption. * fixed so tests got green. * renamed to make it shorter. * fixed formatting in logRow. * removed unused value. * added time formatter to flot. * fixed failing tests. * changed so history will use the formatting with support for timezone. * added todo. * added so we append the correct abbrivation behind time. * added time zone abbrevation in timepicker. * adding timezone in rangeutil tool. * will use timezone when formatting range. * changed so we use new functions to format date so timezone is respected. * wip - dashboard settings. * changed so the time picker settings is in react. * added force update. * wip to get the react graph to work. * fixed formatting and parsing on the timepicker. * updated snap to be correct. * fixed so we format values properly in time picker. * make sure we pass timezone on all the proper places. * fixed so we use correct timeZone in explore. * fixed failing tests. * fixed so we always parse from local to selected timezone. * removed unused variable. * reverted back. * trying to fix issue with directive. * fixed issue. * fixed strict null errors. * fixed so we still can select default. * make sure we reads the time zone from getTimezone
5 years ago
this.dashboard.getTimezone(),
this.$sanitize,
this.templateSrv,
config.theme
);
return super.render(this.table);
}
toggleColumnSort(col: any, colIndex: any) {
// remove sort flag from current column
if (this.table.columns[this.panel.sort.col]) {
this.table.columns[this.panel.sort.col].sort = false;
}
if (this.panel.sort.col === colIndex) {
if (this.panel.sort.desc) {
this.panel.sort.desc = false;
} else {
this.panel.sort.col = null;
}
} else {
this.panel.sort.col = colIndex;
this.panel.sort.desc = true;
}
this.render();
}
link(scope: any, elem: JQuery, attrs: any, ctrl: TablePanelCtrl) {
let data: any;
const panel = ctrl.panel;
let pageCount = 0;
function getTableHeight() {
let panelHeight = ctrl.height;
if (pageCount > 1) {
panelHeight -= 26;
}
return panelHeight - 31 + 'px';
}
function appendTableRows(tbodyElem: JQuery) {
ctrl.renderer.setTable(data);
tbodyElem.empty();
tbodyElem.html(ctrl.renderer.render(ctrl.pageIndex));
}
function switchPage(e: any) {
const el = $(e.currentTarget);
ctrl.pageIndex = parseInt(el.text(), 10) - 1;
renderPanel();
}
function appendPaginationControls(footerElem: JQuery) {
footerElem.empty();
const pageSize = panel.pageSize || 100;
pageCount = Math.ceil(data.rows.length / pageSize);
if (pageCount === 1) {
return;
}
const startPage = Math.max(ctrl.pageIndex - 3, 0);
const endPage = Math.min(pageCount, startPage + 9);
const paginationList = $('<ul></ul>');
for (let i = startPage; i < endPage; i++) {
const activeClass = i === ctrl.pageIndex ? 'active' : '';
const pageLinkElem = $(
'<li><a class="table-panel-page-link pointer ' + activeClass + '">' + (i + 1) + '</a></li>'
);
paginationList.append(pageLinkElem);
}
footerElem.append(paginationList);
}
function renderPanel() {
const panelElem = elem.parents('.panel-content');
const rootElem = elem.find('.table-panel-scroll');
const tbodyElem = elem.find('tbody');
const footerElem = elem.find('.table-panel-footer');
elem.css({ 'font-size': panel.fontSize });
panelElem.addClass('table-panel-content');
appendTableRows(tbodyElem);
appendPaginationControls(footerElem);
rootElem.css({ 'max-height': getTableHeight() });
}
// hook up link tooltips
elem.tooltip({
selector: '[data-link-tooltip]',
});
function addFilterClicked(e: any) {
const filterData = $(e.currentTarget).data();
const options = {
datasource: panel.datasource,
key: data.columns[filterData.column].text,
value: data.rows[filterData.row][filterData.column],
operator: filterData.operator,
};
dispatch(applyFilterFromTable(options));
}
elem.on('click', '.table-panel-page-link', switchPage);
elem.on('click', '.table-panel-filter-link', addFilterClicked);
const unbindDestroy = scope.$on('$destroy', () => {
elem.off('click', '.table-panel-page-link');
elem.off('click', '.table-panel-filter-link');
unbindDestroy();
});
ctrl.events.on(PanelEvents.render, (renderData: any) => {
data = renderData || data;
if (data) {
renderPanel();
}
ctrl.renderingCompleted();
});
}
}
export const plugin = new PanelPlugin((null as unknown) as ComponentType<PanelProps<any>>);
plugin.angularPanelCtrl = TablePanelCtrl;
plugin.setNoPadding();