diff --git a/packages/grafana-ui/src/components/Table/Table.story.tsx b/packages/grafana-ui/src/components/Table/Table.story.tsx
index a494f01c56d..8cd97dcefa9 100644
--- a/packages/grafana-ui/src/components/Table/Table.story.tsx
+++ b/packages/grafana-ui/src/components/Table/Table.story.tsx
@@ -1,40 +1,10 @@
-import React, { FunctionComponent } from 'react';
+// import React from 'react';
import { storiesOf } from '@storybook/react';
import { Table } from './Table';
import { migratedTestTable, migratedTestStyles, simpleTable } from './examples';
-import { ScopedVars } from '../../types/index';
-
-import { renderComponentWithTheme } from '../../utils/storybook/withTheme';
-import { AutoSizer } from 'react-virtualized';
-
-const CenteredStory: FunctionComponent<{}> = ({ children }) => {
- return (
-
-
- {({ width, height }) => (
-
-
- Need to pass {width}/{height} to the table?
-
- {children}
-
- )}
-
-
- );
-};
+import { ScopedVars, TableData } from '../../types/index';
+import { withFullSizeStory } from '../../utils/storybook/withFullSizeStory';
const replaceVariables = (value: any, scopedVars: ScopedVars | undefined) => {
// if (scopedVars) {
@@ -47,24 +17,49 @@ const replaceVariables = (value: any, scopedVars: ScopedVars | undefined) => {
};
storiesOf('UI - Alpha/Table', module)
- .addDecorator(story => {story()})
.add('basic', () => {
- return renderComponentWithTheme(Table, {
+ return withFullSizeStory(Table, {
styles: [],
data: simpleTable,
replaceVariables,
showHeader: true,
- width: 500,
- height: 300,
});
})
.add('Test Configuration', () => {
- return renderComponentWithTheme(Table, {
+ return withFullSizeStory(Table, {
styles: migratedTestStyles,
data: migratedTestTable,
replaceVariables,
showHeader: true,
- width: 500,
- height: 300,
+ });
+ })
+ .add('Lots of cells', () => {
+ const data = {
+ columns: [],
+ rows: [],
+ type: 'table',
+ columnMap: {},
+ } as TableData;
+ for (let i = 0; i < 20; i++) {
+ data.columns.push({
+ text: 'Column ' + i,
+ });
+ }
+ for (let r = 0; r < 500; r++) {
+ const row = [];
+ for (let i = 0; i < 20; i++) {
+ row.push(r + i);
+ }
+ data.rows.push(row);
+ }
+ console.log('DATA:', data);
+
+ return withFullSizeStory(Table, {
+ styles: simpleTable,
+ data,
+ replaceVariables,
+ showHeader: true,
+ fixedColumnCount: 1,
+ fixedRowCount: 1,
});
});
diff --git a/packages/grafana-ui/src/components/Table/Table.test.ts b/packages/grafana-ui/src/components/Table/Table.test.ts
index 93cf53b2adb..e53df39750e 100644
--- a/packages/grafana-ui/src/components/Table/Table.test.ts
+++ b/packages/grafana-ui/src/components/Table/Table.test.ts
@@ -3,9 +3,9 @@ import _ from 'lodash';
import { getColorDefinitionByName } from '@grafana/ui';
import { ScopedVars } from '@grafana/ui/src/types';
import { getTheme } from '../../themes';
-import Table from './Table';
import { migratedTestTable, migratedTestStyles } from './examples';
+import TableXXXX from './TableXXXX';
// TODO: this is commented out with *x* describe!
// Essentially all the elements need to replace the with
@@ -28,7 +28,7 @@ xdescribe('when rendering table', () => {
};
const table = migratedTestTable;
- const renderer = new Table({
+ const renderer = new TableXXXX({
styles: migratedTestStyles,
data: migratedTestTable,
replaceVariables,
diff --git a/packages/grafana-ui/src/components/Table/Table.tsx b/packages/grafana-ui/src/components/Table/Table.tsx
index f208b7651e8..cbc0b50ebc0 100644
--- a/packages/grafana-ui/src/components/Table/Table.tsx
+++ b/packages/grafana-ui/src/components/Table/Table.tsx
@@ -1,22 +1,19 @@
// Libraries
import _ from 'lodash';
-import React, { Component, CSSProperties, ReactNode } from 'react';
+import React, { Component, ReactNode } from 'react';
import {
- Table as RVTable,
SortDirectionType,
SortIndicator,
- Column as RVColumn,
- TableHeaderProps,
- TableCellProps,
+ MultiGrid,
+ CellMeasurerCache,
+ CellMeasurer,
+ GridCellProps,
} from 'react-virtualized';
import { Themeable } from '../../types/theme';
import { sortTableData } from '../../utils/processTimeSeries';
-import moment from 'moment';
-
-import { getValueFormat, TableData, getColorFromHexRgbOrName, InterpolateFunction, Column } from '@grafana/ui';
-import { Index } from 'react-virtualized';
+import { TableData, InterpolateFunction } from '@grafana/ui';
import { ColumnStyle } from './Table';
// APP Imports!!!
@@ -27,7 +24,7 @@ export interface ColumnStyle {
pattern?: string;
alias?: string;
- colorMode?: string;
+ colorMode?: 'cell' | 'value';
colors?: any[];
decimals?: number;
thresholds?: any[];
@@ -61,6 +58,8 @@ interface ColumnInfo {
interface Props extends Themeable {
data?: TableData;
showHeader: boolean;
+ fixedColumnCount: number;
+ fixedRowCount: number;
styles: ColumnStyle[];
replaceVariables: InterpolateFunction;
width: number;
@@ -78,8 +77,12 @@ export class Table extends Component {
columns: ColumnInfo[] = [];
colorState: any;
+ _cache: CellMeasurerCache;
+
static defaultProps = {
showHeader: true,
+ fixedRowCount: 1,
+ fixedColumnCount: 0,
};
constructor(props: Props) {
@@ -89,6 +92,11 @@ export class Table extends Component {
data: props.data,
};
+ this._cache = new CellMeasurerCache({
+ defaultHeight: 30,
+ defaultWidth: 150,
+ });
+
this.initRenderer();
}
@@ -109,314 +117,17 @@ export class Table extends Component {
}
}
- initRenderer() {
- const { styles } = this.props;
- const { data } = this.state;
- this.colorState = {};
- if (!data || !data.columns) {
- this.columns = [];
- return;
- }
- this.columns = data.columns.map((col, index) => {
- let title = col.text;
- let style; // ColumnStyle
-
- // Find the style based on the text
- for (let i = 0; i < styles.length; i++) {
- const s = styles[i];
- const regex = 'XXX'; //kbn.stringToJsRegex(s.pattern);
- if (title.match(regex)) {
- style = s;
- if (s.alias) {
- title = title.replace(regex, s.alias);
- }
- break;
- }
- }
-
- return {
- header: title,
- accessor: col.text, // unique?
- style: style,
- formatter: this.createColumnFormatter(col, style),
- };
- });
- }
-
- //----------------------------------------------------------------------
- // renderer.ts copy (taken from angular version!!!)
- //----------------------------------------------------------------------
-
- getColorForValue(value: any, style: ColumnStyle) {
- if (!style.thresholds || !style.colors) {
- return null;
- }
- const { theme } = this.props;
-
- for (let i = style.thresholds.length; i > 0; i--) {
- if (value >= style.thresholds[i - 1]) {
- return getColorFromHexRgbOrName(style.colors[i], theme.type);
- }
- }
- return getColorFromHexRgbOrName(_.first(style.colors), theme.type);
- }
-
- defaultCellFormatter(v: any, style?: ColumnStyle): string {
- if (v === null || v === void 0 || v === undefined) {
- return '';
- }
-
- if (_.isArray(v)) {
- v = v.join(', ');
- }
-
- return v; // react will sanitize
- }
-
- createColumnFormatter(schema: Column, style?: ColumnStyle): CellFormatter {
- if (!style) {
- return this.defaultCellFormatter;
- }
-
- if (style.type === 'hidden') {
- return v => {
- return undefined;
- };
- }
-
- if (style.type === 'date') {
- return v => {
- if (v === undefined || v === null) {
- return '-';
- }
-
- if (_.isArray(v)) {
- v = v[0];
- }
- let date = moment(v);
- if (this.props.isUTC) {
- date = date.utc();
- }
- return date.format(style.dateFormat);
- };
- }
-
- if (style.type === 'string') {
- return v => {
- if (_.isArray(v)) {
- v = v.join(', ');
- }
-
- const mappingType = style.mappingType || 0;
-
- if (mappingType === 1 && style.valueMaps) {
- for (let i = 0; i < style.valueMaps.length; i++) {
- const map = style.valueMaps[i];
-
- if (v === null) {
- if (map.value === 'null') {
- return map.text;
- }
- continue;
- }
-
- // Allow both numeric and string values to be mapped
- if ((!_.isString(v) && Number(map.value) === Number(v)) || map.value === v) {
- this.setColorState(v, style);
- return this.defaultCellFormatter(map.text, style);
- }
- }
- }
-
- if (mappingType === 2 && style.rangeMaps) {
- for (let i = 0; i < style.rangeMaps.length; i++) {
- const map = style.rangeMaps[i];
-
- if (v === null) {
- if (map.from === 'null' && map.to === 'null') {
- return map.text;
- }
- continue;
- }
-
- if (Number(map.from) <= Number(v) && Number(map.to) >= Number(v)) {
- this.setColorState(v, style);
- return this.defaultCellFormatter(map.text, style);
- }
- }
- }
-
- if (v === null || v === void 0) {
- return '-';
- }
-
- this.setColorState(v, style);
- return this.defaultCellFormatter(v, style);
- };
- }
-
- if (style.type === 'number') {
- const valueFormatter = getValueFormat(style.unit || schema.unit || 'none');
-
- return v => {
- if (v === null || v === void 0) {
- return '-';
- }
-
- if (_.isString(v) || _.isArray(v)) {
- return this.defaultCellFormatter(v, style);
- }
-
- this.setColorState(v, style);
- return valueFormatter(v, style.decimals, null);
- };
- }
-
- return value => {
- return this.defaultCellFormatter(value, style);
- };
- }
-
- setColorState(value: any, style: ColumnStyle) {
- if (!style.colorMode) {
- return;
- }
-
- if (value === null || value === void 0 || _.isArray(value)) {
- return;
- }
-
- if (_.isNaN(value)) {
- return;
- }
- const numericValue = Number(value);
- this.colorState[style.colorMode] = this.getColorForValue(numericValue, style);
- }
-
- renderRowVariables(rowIndex: number) {
- const scopedVars: any = {};
- const row = this.rowGetter({ index: rowIndex });
- for (let i = 0; i < row.length; i++) {
- scopedVars[`__cell_${i}`] = { value: row[i] };
- }
- return scopedVars;
- }
-
- renderCell(columnIndex: number, rowIndex: number, value: any): ReactNode {
- const column = this.columns[columnIndex];
- if (column.formatter) {
- value = column.formatter(value, column.style);
- }
-
- const style: CSSProperties = {};
- const cellClasses = [];
- let cellClass = '';
-
- if (this.colorState.cell) {
- style.backgroundColor = this.colorState.cell;
- style.color = 'white';
- this.colorState.cell = null;
- } else if (this.colorState.value) {
- style.color = this.colorState.value;
- this.colorState.value = null;
- }
-
- if (value === undefined) {
- style.display = 'none';
- column.hidden = true;
- } else {
- column.hidden = false;
- }
-
- if (column.style && column.style.preserveFormat) {
- cellClasses.push('table-panel-cell-pre');
- }
-
- let columnHtml: JSX.Element;
- if (column.style && column.style.link) {
- // Render cell as link
- const { replaceVariables } = this.props;
- const scopedVars = this.renderRowVariables(rowIndex);
- scopedVars['__cell'] = { value: value };
-
- const cellLink = replaceVariables(column.style.linkUrl, scopedVars, encodeURIComponent);
- const cellLinkTooltip = replaceVariables(column.style.linkTooltip, scopedVars);
- const cellTarget = column.style.linkTargetBlank ? '_blank' : '';
-
- cellClasses.push('table-panel-cell-link');
- columnHtml = (
-
- {value}
-
- );
- } else {
- columnHtml = {value};
- }
-
- let filterLink: JSX.Element | null = null;
- if (column.filterable) {
- cellClasses.push('table-panel-cell-filterable');
- filterLink = (
-
-
-
-
-
-
-
-
- );
- }
-
- if (cellClasses.length) {
- cellClass = cellClasses.join(' ');
- }
-
- style.width = '100%';
- style.height = '100%';
- columnHtml = (
-
- {columnHtml}
- {filterLink}
-
- );
- return columnHtml;
- }
+ initRenderer() {}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
- rowGetter = ({ index }: Index) => {
- return this.state.data!.rows[index];
- };
-
- doSort = (info: any) => {
- let dir = info.sortDirection;
- let sort = info.sortBy;
- if (sort !== this.state.sortBy) {
+ doSort = (columnIndex: number) => {
+ let sort: any = this.state.sortBy;
+ let dir = this.state.sortDirection;
+ if (sort !== columnIndex) {
dir = 'DESC';
+ sort = columnIndex;
} else if (dir === 'DESC') {
dir = 'ASC';
} else {
@@ -425,58 +136,107 @@ export class Table extends Component {
this.setState({ sortBy: sort, sortDirection: dir });
};
- headerRenderer = (header: TableHeaderProps): ReactNode => {
- const dataKey = header.dataKey as any; // types say string, but it is number!
+ handelClick = (rowIndex: number, columnIndex: number) => {
+ const { showHeader } = this.props;
+ const { data } = this.state;
+ const realRowIndex = rowIndex - (showHeader ? 1 : 0);
+ if (realRowIndex < 0) {
+ this.doSort(columnIndex);
+ } else {
+ const row = data!.rows[realRowIndex];
+ const value = row[columnIndex];
+ console.log('CLICK', rowIndex, columnIndex, value);
+ }
+ };
+
+ headerRenderer = (columnIndex: number): ReactNode => {
const { data, sortBy, sortDirection } = this.state;
- const col = data!.columns[dataKey];
+ const col = data!.columns[columnIndex];
+ const sorting = sortBy === columnIndex;
return (
- {col.text} {sortBy === dataKey && }
+ {col.text}{' '}
+ {sorting && (
+
+ {sortDirection}
+
+
+ )}
);
};
- cellRenderer = (cell: TableCellProps) => {
- const { columnIndex, rowIndex } = cell;
- const row = this.state.data!.rows[rowIndex];
- const val = row[columnIndex];
- return this.renderCell(columnIndex, rowIndex, val);
+ cellRenderer = (props: GridCellProps): React.ReactNode => {
+ const { rowIndex, columnIndex, key, parent, style } = props;
+ const { showHeader } = this.props;
+ const { data } = this.state;
+ if (!data) {
+ return ? ;
+ }
+
+ const realRowIndex = rowIndex - (showHeader ? 1 : 0);
+
+ let classNames = 'gf-table-cell';
+ let content = null;
+
+ if (realRowIndex < 0) {
+ content = this.headerRenderer(columnIndex);
+ classNames = 'gf-table-header';
+ } else {
+ const row = data.rows[realRowIndex];
+ const value = row[columnIndex];
+ content = (
+
+ {rowIndex}/{columnIndex}: {value}
+
+ );
+ }
+
+ return (
+
+ this.handelClick(rowIndex, columnIndex)}
+ className={classNames}
+ style={{
+ ...style,
+ whiteSpace: 'nowrap',
+ }}
+ >
+ {content}
+
+
+ );
};
render() {
- const { width, height, showHeader } = this.props;
- const { data } = this.props;
+ const { data, showHeader, width, height, fixedColumnCount, fixedRowCount } = this.props;
if (!data) {
return NO Data ;
}
return (
-
- {data.columns.map((col, index) => {
- return (
-
- );
- })}
-
+ height={height}
+ fixedColumnCount={fixedColumnCount}
+ fixedRowCount={fixedRowCount}
+ classNameTopLeftGrid="gf-table-fixed-row-and-column"
+ classNameTopRightGrid="gf-table-fixed-row"
+ classNameBottomLeftGrid="gf-table-fixed-column"
+ classNameBottomRightGrid="gf-table-normal-cell"
+ />
);
}
}
diff --git a/packages/grafana-ui/src/components/Table/TableXXXX.tsx b/packages/grafana-ui/src/components/Table/TableXXXX.tsx
new file mode 100644
index 00000000000..4c78c3b336b
--- /dev/null
+++ b/packages/grafana-ui/src/components/Table/TableXXXX.tsx
@@ -0,0 +1,456 @@
+// Libraries
+import _ from 'lodash';
+import React, { Component, CSSProperties, ReactNode } from 'react';
+import {
+ Table as RVTable,
+ SortDirectionType,
+ SortIndicator,
+ Column as RVColumn,
+ TableHeaderProps,
+ TableCellProps,
+} from 'react-virtualized';
+import { Themeable } from '../../types/theme';
+
+import { sortTableData } from '../../utils/processTimeSeries';
+
+import moment from 'moment';
+
+import { getValueFormat, TableData, getColorFromHexRgbOrName, InterpolateFunction, Column } from '@grafana/ui';
+import { Index } from 'react-virtualized';
+import { ColumnStyle } from './Table';
+
+type CellFormatter = (v: any, style?: ColumnStyle) => ReactNode;
+
+interface ColumnInfo {
+ header: string;
+ accessor: string; // the field name
+ style?: ColumnStyle;
+ hidden?: boolean;
+ formatter: CellFormatter;
+ filterable?: boolean;
+}
+
+interface Props extends Themeable {
+ data?: TableData;
+ showHeader: boolean;
+ styles: ColumnStyle[];
+ replaceVariables: InterpolateFunction;
+ width: number;
+ height: number;
+ isUTC?: boolean;
+}
+
+interface State {
+ sortBy?: number;
+ sortDirection?: SortDirectionType;
+ data?: TableData;
+}
+
+export class TableXXXX extends Component {
+ columns: ColumnInfo[] = [];
+ colorState: any;
+
+ static defaultProps = {
+ showHeader: true,
+ };
+
+ constructor(props: Props) {
+ super(props);
+
+ this.state = {
+ data: props.data,
+ };
+
+ this.initRenderer();
+ }
+
+ componentDidUpdate(prevProps: Props, prevState: State) {
+ const { data, styles } = this.props;
+ const { sortBy, sortDirection } = this.state;
+ const dataChanged = data !== prevProps.data;
+
+ // Update the renderer if options change
+ if (dataChanged || styles !== prevProps.styles) {
+ this.initRenderer();
+ }
+
+ // Update the data when data or sort changes
+ if (dataChanged || sortBy !== prevState.sortBy || sortDirection !== prevState.sortDirection) {
+ const sorted = data ? sortTableData(data, sortBy, sortDirection === 'DESC') : data;
+ this.setState({ data: sorted });
+ }
+ }
+
+ initRenderer() {
+ const { styles } = this.props;
+ const { data } = this.state;
+ this.colorState = {};
+ if (!data || !data.columns) {
+ this.columns = [];
+ return;
+ }
+ this.columns = data.columns.map((col, index) => {
+ let title = col.text;
+ let style; // ColumnStyle
+
+ // Find the style based on the text
+ for (let i = 0; i < styles.length; i++) {
+ const s = styles[i];
+ const regex = 'XXX'; //kbn.stringToJsRegex(s.pattern);
+ if (title.match(regex)) {
+ style = s;
+ if (s.alias) {
+ title = title.replace(regex, s.alias);
+ }
+ break;
+ }
+ }
+
+ return {
+ header: title,
+ accessor: col.text, // unique?
+ style: style,
+ formatter: this.createColumnFormatter(col, style),
+ };
+ });
+ }
+
+ //----------------------------------------------------------------------
+ // renderer.ts copy (taken from angular version!!!)
+ //----------------------------------------------------------------------
+
+ getColorForValue(value: any, style: ColumnStyle) {
+ if (!style.thresholds || !style.colors) {
+ return null;
+ }
+ const { theme } = this.props;
+
+ for (let i = style.thresholds.length; i > 0; i--) {
+ if (value >= style.thresholds[i - 1]) {
+ return getColorFromHexRgbOrName(style.colors[i], theme.type);
+ }
+ }
+ return getColorFromHexRgbOrName(_.first(style.colors), theme.type);
+ }
+
+ defaultCellFormatter(v: any, style?: ColumnStyle): string {
+ if (v === null || v === void 0 || v === undefined) {
+ return '';
+ }
+
+ if (_.isArray(v)) {
+ v = v.join(', ');
+ }
+
+ return v; // react will sanitize
+ }
+
+ createColumnFormatter(schema: Column, style?: ColumnStyle): CellFormatter {
+ if (!style) {
+ return this.defaultCellFormatter;
+ }
+
+ if (style.type === 'hidden') {
+ return v => {
+ return undefined;
+ };
+ }
+
+ if (style.type === 'date') {
+ return v => {
+ if (v === undefined || v === null) {
+ return '-';
+ }
+
+ if (_.isArray(v)) {
+ v = v[0];
+ }
+ let date = moment(v);
+ if (this.props.isUTC) {
+ date = date.utc();
+ }
+ return date.format(style.dateFormat);
+ };
+ }
+
+ if (style.type === 'string') {
+ return v => {
+ if (_.isArray(v)) {
+ v = v.join(', ');
+ }
+
+ const mappingType = style.mappingType || 0;
+
+ if (mappingType === 1 && style.valueMaps) {
+ for (let i = 0; i < style.valueMaps.length; i++) {
+ const map = style.valueMaps[i];
+
+ if (v === null) {
+ if (map.value === 'null') {
+ return map.text;
+ }
+ continue;
+ }
+
+ // Allow both numeric and string values to be mapped
+ if ((!_.isString(v) && Number(map.value) === Number(v)) || map.value === v) {
+ this.setColorState(v, style);
+ return this.defaultCellFormatter(map.text, style);
+ }
+ }
+ }
+
+ if (mappingType === 2 && style.rangeMaps) {
+ for (let i = 0; i < style.rangeMaps.length; i++) {
+ const map = style.rangeMaps[i];
+
+ if (v === null) {
+ if (map.from === 'null' && map.to === 'null') {
+ return map.text;
+ }
+ continue;
+ }
+
+ if (Number(map.from) <= Number(v) && Number(map.to) >= Number(v)) {
+ this.setColorState(v, style);
+ return this.defaultCellFormatter(map.text, style);
+ }
+ }
+ }
+
+ if (v === null || v === void 0) {
+ return '-';
+ }
+
+ this.setColorState(v, style);
+ return this.defaultCellFormatter(v, style);
+ };
+ }
+
+ if (style.type === 'number') {
+ const valueFormatter = getValueFormat(style.unit || schema.unit || 'none');
+
+ return v => {
+ if (v === null || v === void 0) {
+ return '-';
+ }
+
+ if (_.isString(v) || _.isArray(v)) {
+ return this.defaultCellFormatter(v, style);
+ }
+
+ this.setColorState(v, style);
+ return valueFormatter(v, style.decimals, null);
+ };
+ }
+
+ return value => {
+ return this.defaultCellFormatter(value, style);
+ };
+ }
+
+ setColorState(value: any, style: ColumnStyle) {
+ if (!style.colorMode) {
+ return;
+ }
+
+ if (value === null || value === void 0 || _.isArray(value)) {
+ return;
+ }
+
+ if (_.isNaN(value)) {
+ return;
+ }
+ const numericValue = Number(value);
+ this.colorState[style.colorMode] = this.getColorForValue(numericValue, style);
+ }
+
+ renderRowVariables(rowIndex: number) {
+ const scopedVars: any = {};
+ const row = this.rowGetter({ index: rowIndex });
+ for (let i = 0; i < row.length; i++) {
+ scopedVars[`__cell_${i}`] = { value: row[i] };
+ }
+ return scopedVars;
+ }
+
+ renderCell(columnIndex: number, rowIndex: number, value: any): ReactNode {
+ const column = this.columns[columnIndex];
+ if (column.formatter) {
+ value = column.formatter(value, column.style);
+ }
+
+ const style: CSSProperties = {};
+ const cellClasses = [];
+ let cellClass = '';
+
+ if (this.colorState.cell) {
+ style.backgroundColor = this.colorState.cell;
+ style.color = 'white';
+ this.colorState.cell = null;
+ } else if (this.colorState.value) {
+ style.color = this.colorState.value;
+ this.colorState.value = null;
+ }
+
+ if (value === undefined) {
+ style.display = 'none';
+ column.hidden = true;
+ } else {
+ column.hidden = false;
+ }
+
+ if (column.style && column.style.preserveFormat) {
+ cellClasses.push('table-panel-cell-pre');
+ }
+
+ let columnHtml: JSX.Element;
+ if (column.style && column.style.link) {
+ // Render cell as link
+ const { replaceVariables } = this.props;
+ const scopedVars = this.renderRowVariables(rowIndex);
+ scopedVars['__cell'] = { value: value };
+
+ const cellLink = replaceVariables(column.style.linkUrl, scopedVars, encodeURIComponent);
+ const cellLinkTooltip = replaceVariables(column.style.linkTooltip, scopedVars);
+ const cellTarget = column.style.linkTargetBlank ? '_blank' : '';
+
+ cellClasses.push('table-panel-cell-link');
+ columnHtml = (
+
+ {value}
+
+ );
+ } else {
+ columnHtml = {value};
+ }
+
+ let filterLink: JSX.Element | null = null;
+ if (column.filterable) {
+ cellClasses.push('table-panel-cell-filterable');
+ filterLink = (
+
+
+
+
+
+
+
+
+ );
+ }
+
+ if (cellClasses.length) {
+ cellClass = cellClasses.join(' ');
+ }
+
+ style.width = '100%';
+ style.height = '100%';
+ columnHtml = (
+
+ {columnHtml}
+ {filterLink}
+
+ );
+ return columnHtml;
+ }
+
+ //----------------------------------------------------------------------
+ //----------------------------------------------------------------------
+
+ rowGetter = ({ index }: Index) => {
+ return this.state.data!.rows[index];
+ };
+
+ doSort = (info: any) => {
+ let dir = info.sortDirection;
+ let sort = info.sortBy;
+ if (sort !== this.state.sortBy) {
+ dir = 'DESC';
+ } else if (dir === 'DESC') {
+ dir = 'ASC';
+ } else {
+ sort = null;
+ }
+ this.setState({ sortBy: sort, sortDirection: dir });
+ };
+
+ headerRenderer = (header: TableHeaderProps): ReactNode => {
+ const dataKey = header.dataKey as any; // types say string, but it is number!
+ const { data, sortBy, sortDirection } = this.state;
+ const col = data!.columns[dataKey];
+
+ return (
+
+ {col.text} {sortBy === dataKey && }
+
+ );
+ };
+
+ cellRenderer = (cell: TableCellProps) => {
+ const { columnIndex, rowIndex } = cell;
+ const row = this.state.data!.rows[rowIndex];
+ const val = row[columnIndex];
+ return this.renderCell(columnIndex, rowIndex, val);
+ };
+
+ render() {
+ const { width, height, showHeader } = this.props;
+ const { data } = this.props;
+ if (!data) {
+ return NO Data ;
+ }
+
+ return (
+
+ {data.columns.map((col, index) => {
+ return (
+
+ );
+ })}
+
+ );
+ }
+}
+
+export default TableXXXX;
diff --git a/packages/grafana-ui/src/components/Table/_Table.scss b/packages/grafana-ui/src/components/Table/_Table.scss
index b0aa2d6b742..f9cb0271561 100644
--- a/packages/grafana-ui/src/components/Table/_Table.scss
+++ b/packages/grafana-ui/src/components/Table/_Table.scss
@@ -9,12 +9,6 @@
display: flex;
flex-direction: row;
align-items: left;
-
- background: $list-item-bg;
- border-top: 2px solid $body-bg;
- border-bottom: 2px solid $body-bg;
-
- color: $blue;
}
.ReactVirtualized__Table__row {
display: flex;
@@ -37,12 +31,6 @@
margin-right: 10px;
min-width: 0px;
}
-.ReactVirtualized__Table__rowColumn {
- text-overflow: ellipsis;
- white-space: nowrap;
-
- border-right: 2px solid $body-bg;
-}
.ReactVirtualized__Table__headerColumn:first-of-type,
.ReactVirtualized__Table__rowColumn:first-of-type {
@@ -62,3 +50,25 @@
width: 1em;
fill: currentColor;
}
+
+.gf-table-header {
+ padding: 3px 10px;
+
+ background: $list-item-bg;
+ border-top: 2px solid $body-bg;
+ border-bottom: 2px solid $body-bg;
+
+ cursor: pointer;
+
+ color: $blue;
+}
+
+.gf-table-cell {
+ padding: 3px 10px;
+
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+ border-right: 2px solid $body-bg;
+ border-bottom: 2px solid $body-bg;
+}
diff --git a/packages/grafana-ui/src/utils/storybook/withFullSizeStory.tsx b/packages/grafana-ui/src/utils/storybook/withFullSizeStory.tsx
new file mode 100644
index 00000000000..c6efbee462f
--- /dev/null
+++ b/packages/grafana-ui/src/utils/storybook/withFullSizeStory.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { AutoSizer } from 'react-virtualized';
+
+export const withFullSizeStory = (component: React.ComponentType, props: any) => (
+
+
+ {({ width, height }) => (
+ <>
+ {React.createElement(component, {
+ ...props,
+ width,
+ height,
+ })}
+ >
+ )}
+
+
+);
|