mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-28 17:51:21 +00:00
Rendering sortable generic tables in the UI
Rendering generic table columns Made Type a required attribute for TableTemplate Made generic table sortable on the UI
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import debug from 'debug';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Map as makeMap } from 'immutable';
|
||||
@@ -8,6 +9,7 @@ import { resetDocumentTitle, setDocumentTitle } from '../utils/title-utils';
|
||||
|
||||
import MatchedText from './matched-text';
|
||||
import NodeDetailsControls from './node-details/node-details-controls';
|
||||
import NodeDetailsGenericTable from './node-details/node-details-generic-table';
|
||||
import NodeDetailsHealth from './node-details/node-details-health';
|
||||
import NodeDetailsInfo from './node-details/node-details-info';
|
||||
import NodeDetailsLabels from './node-details/node-details-labels';
|
||||
@@ -15,13 +17,18 @@ import NodeDetailsRelatives from './node-details/node-details-relatives';
|
||||
import NodeDetailsTable from './node-details/node-details-table';
|
||||
import Warning from './warning';
|
||||
|
||||
|
||||
const logError = debug('scope:error');
|
||||
|
||||
function getTruncationText(count) {
|
||||
return 'This section was too long to be handled efficiently and has been truncated'
|
||||
+ ` (${count} extra entries not included). We are working to remove this limitation.`;
|
||||
}
|
||||
|
||||
class NodeDetails extends React.Component {
|
||||
const TABLE_TYPE_PROPERTY_LIST = 'property-list';
|
||||
const TABLE_TYPE_GENERIC = 'multicolumn-table';
|
||||
|
||||
class NodeDetails extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.handleClickClose = this.handleClickClose.bind(this);
|
||||
@@ -214,9 +221,7 @@ class NodeDetails extends React.Component {
|
||||
<Warning text={getTruncationText(table.truncationCount)} />
|
||||
</span>}
|
||||
</div>
|
||||
<NodeDetailsLabels
|
||||
rows={table.rows} controls={table.controls}
|
||||
matches={nodeMatches.get('tables')} />
|
||||
{this.renderTable(table)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -227,6 +232,29 @@ class NodeDetails extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderTable(table) {
|
||||
const { nodeMatches = makeMap() } = this.props;
|
||||
switch (table.type) {
|
||||
case TABLE_TYPE_GENERIC:
|
||||
return (
|
||||
<NodeDetailsGenericTable
|
||||
rows={table.rows} columns={table.columns}
|
||||
matches={nodeMatches.get('tables')}
|
||||
/>
|
||||
);
|
||||
case TABLE_TYPE_PROPERTY_LIST:
|
||||
return (
|
||||
<NodeDetailsLabels
|
||||
rows={table.rows} controls={table.controls}
|
||||
matches={nodeMatches.get('tables')}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
logError(`Undefined type '${table.type}' for table ${table.id}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.updateTitle();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import React from 'react';
|
||||
import { Map as makeMap } from 'immutable';
|
||||
import { sortBy } from 'lodash';
|
||||
|
||||
import MatchedText from '../matched-text';
|
||||
import ShowMore from '../show-more';
|
||||
|
||||
|
||||
function columnStyle(column) {
|
||||
return {
|
||||
textAlign: column.dataType === 'number' ? 'right' : 'left',
|
||||
paddingRight: '10px',
|
||||
maxWidth: '140px'
|
||||
};
|
||||
}
|
||||
|
||||
function sortedRows(rows, sortedByColumn, sortedDesc) {
|
||||
const orderedRows = sortBy(rows, row => row.id);
|
||||
const sorted = sortBy(orderedRows, (row) => {
|
||||
let value = row.entries[sortedByColumn.id];
|
||||
if (sortedByColumn.dataType === 'number') {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
if (!sortedDesc) {
|
||||
sorted.reverse();
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
|
||||
export default class NodeDetailsGenericTable extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.DEFAULT_LIMIT = 5;
|
||||
this.state = {
|
||||
limit: this.DEFAULT_LIMIT,
|
||||
sortedByColumn: props.columns[0],
|
||||
sortedDesc: true
|
||||
};
|
||||
this.handleLimitClick = this.handleLimitClick.bind(this);
|
||||
}
|
||||
|
||||
handleHeaderClick(ev, column) {
|
||||
ev.preventDefault();
|
||||
this.setState({
|
||||
sortedByColumn: column,
|
||||
sortedDesc: this.state.sortedByColumn.id === column.id
|
||||
? !this.state.sortedDesc : true
|
||||
});
|
||||
}
|
||||
|
||||
handleLimitClick() {
|
||||
const limit = this.state.limit ? 0 : this.DEFAULT_LIMIT;
|
||||
this.setState({limit});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { sortedByColumn, sortedDesc } = this.state;
|
||||
const { columns, matches = makeMap() } = this.props;
|
||||
let rows = this.props.rows;
|
||||
let notShown = 0;
|
||||
const limited = rows && this.state.limit > 0 && rows.length > this.state.limit;
|
||||
const expanded = this.state.limit === 0;
|
||||
if (rows && limited) {
|
||||
const hasNotShownMatch = rows.filter((row, index) => index >= this.state.limit
|
||||
&& matches.has(row.id)).length > 0;
|
||||
if (!hasNotShownMatch) {
|
||||
notShown = rows.length - this.DEFAULT_LIMIT;
|
||||
rows = rows.slice(0, this.state.limit);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="node-details-generic-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((column) => {
|
||||
const onHeaderClick = (ev) => {
|
||||
this.handleHeaderClick(ev, column);
|
||||
};
|
||||
const isSorted = column.id === this.state.sortedByColumn.id;
|
||||
const isSortedDesc = isSorted && this.state.sortedDesc;
|
||||
const isSortedAsc = isSorted && !isSortedDesc;
|
||||
const style = Object.assign(columnStyle(column), {
|
||||
cursor: 'pointer',
|
||||
fontSize: '11px'
|
||||
});
|
||||
return (
|
||||
<th
|
||||
className="node-details-generic-table-header"
|
||||
key={column.id} style={style} onClick={onHeaderClick}>
|
||||
{isSortedAsc
|
||||
&& <span className="node-details-table-header-sorter fa fa-caret-up" />}
|
||||
{isSortedDesc
|
||||
&& <span className="node-details-table-header-sorter fa fa-caret-down" />}
|
||||
{column.label}
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{sortedRows(rows, sortedByColumn, sortedDesc).map(row => (
|
||||
<tr className="node-details-generic-table-row" key={row.id}>
|
||||
{columns.map((column) => {
|
||||
const value = row.entries[column.id];
|
||||
const match = matches.get(column.id);
|
||||
return (
|
||||
<td
|
||||
className="node-details-generic-table-field-value truncate"
|
||||
title={value} key={column.id} style={columnStyle(column)}>
|
||||
<MatchedText text={value} match={match} />
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<ShowMore
|
||||
handleClick={this.handleLimitClick} collection={this.props.rows}
|
||||
expanded={expanded} notShown={notShown}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user