import _ from 'lodash'; import React from 'react'; import ShowMore from '../show-more'; import NodeDetailsTableNodeLink from './node-details-table-node-link'; import NodeDetailsTableNodeMetric from './node-details-table-node-metric'; export default class NodeDetailsTable extends React.Component { constructor(props, context) { super(props, context); this.DEFAULT_LIMIT = 5; this.state = { limit: this.DEFAULT_LIMIT, sortedDesc: true, sortBy: null }; this.handleLimitClick = this.handleLimitClick.bind(this); this.getValueForSortBy = this.getValueForSortBy.bind(this); } handleHeaderClick(ev, headerId) { ev.preventDefault(); const sortedDesc = headerId === this.state.sortBy ? !this.state.sortedDesc : this.state.sortedDesc; const sortBy = headerId; this.setState({sortBy, sortedDesc}); } handleLimitClick() { const limit = this.state.limit ? 0 : this.DEFAULT_LIMIT; this.setState({limit}); } getDefaultSortBy() { // first metric return _.get(this.props.nodes, [0, 'metrics', 0, 'id']); } getMetaDataSorters() { // returns an array of sorters that will take a node return _.get(this.props.nodes, [0, 'metadata'], []).map((field, index) => { return node => node.metadata[index] ? node.metadata[index].value : null; }); } getValueForSortBy(node) { // return the node's value based on the sortBy field const sortBy = this.state.sortBy || this.getDefaultSortBy(); if (sortBy !== null) { const field = _.union(node.metrics, node.metadata).find(f => f.id === sortBy); if (field) { return field.value; } } return -1e-10; // just under 0 to treat missing values differently from 0 } getValuesForNode(node) { const values = {}; ['metrics', 'metadata'].forEach(collection => { if (node[collection]) { node[collection].forEach(field => { field.valueType = collection; values[field.id] = field; }); } }); return values; } renderHeaders() { if (this.props.nodes && this.props.nodes.length > 0) { const columns = this.props.columns || []; const headers = [{id: 'label', label: this.props.label}].concat(columns); const defaultSortBy = this.getDefaultSortBy(); return ( {headers.map(header => { const headerClasses = ['node-details-table-header', 'truncate']; const onHeaderClick = ev => { this.handleHeaderClick(ev, header.id); }; // sort by first metric by default const isSorted = this.state.sortBy !== null ? header.id === this.state.sortBy : header.id === defaultSortBy; const isSortedDesc = isSorted && this.state.sortedDesc; const isSortedAsc = isSorted && !isSortedDesc; if (isSorted) { headerClasses.push('node-details-table-header-sorted'); } return ( {isSortedAsc && } {isSortedDesc && } {header.label} ); })} ); } return ''; } renderValues(node) { const fields = this.getValuesForNode(node); const columns = this.props.columns || []; return columns.map(({id}) => { const field = fields[id]; if (field) { if (field.valueType === 'metadata') { return ( {field.value} ); } return ; } // empty cell to complete the row for proper hover return ; }); } render() { const headers = this.renderHeaders(); let nodes = _.sortByAll(this.props.nodes, this.getValueForSortBy, 'label', this.getMetaDataSorters()); const limited = nodes && this.state.limit > 0 && nodes.length > this.state.limit; const expanded = this.state.limit === 0; const notShown = nodes.length - this.DEFAULT_LIMIT; if (this.state.sortedDesc) { nodes.reverse(); } if (nodes && limited) { nodes = nodes.slice(0, this.state.limit); } return (
{headers} {nodes && nodes.map(node => { const values = this.renderValues(node); return ( {values} ); })}
this.handleLimitClick()} collection={this.props.nodes} expanded={expanded} notShown={notShown} />
); } }