1import React, { PureComponent } from 'react'; 2import { connect, ConnectedProps } from 'react-redux'; 3import { ValueLinkConfig, applyFieldOverrides, TimeZone } from '@grafana/data'; 4import { Collapse, Table } from '@grafana/ui'; 5import { ExploreId, ExploreItemState } from 'app/types/explore'; 6import { StoreState } from 'app/types'; 7import { splitOpen } from './state/main'; 8import { config } from 'app/core/config'; 9import { PANEL_BORDER } from 'app/core/constants'; 10import { MetaInfoText } from './MetaInfoText'; 11import { FilterItem } from '@grafana/ui/src/components/Table/types'; 12import { getFieldLinksForExplore } from './utils/links'; 13 14interface TableContainerProps { 15 ariaLabel?: string; 16 exploreId: ExploreId; 17 width: number; 18 timeZone: TimeZone; 19 onCellFilterAdded?: (filter: FilterItem) => void; 20} 21 22function mapStateToProps(state: StoreState, { exploreId }: TableContainerProps) { 23 const explore = state.explore; 24 // @ts-ignore 25 const item: ExploreItemState = explore[exploreId]; 26 const { loading: loadingInState, tableResult, range } = item; 27 const loading = tableResult && tableResult.length > 0 ? false : loadingInState; 28 return { loading, tableResult, range }; 29} 30 31const mapDispatchToProps = { 32 splitOpen, 33}; 34 35const connector = connect(mapStateToProps, mapDispatchToProps); 36 37type Props = TableContainerProps & ConnectedProps<typeof connector>; 38 39export class TableContainer extends PureComponent<Props> { 40 getTableHeight() { 41 const { tableResult } = this.props; 42 43 if (!tableResult || tableResult.length === 0) { 44 return 200; 45 } 46 47 // tries to estimate table height 48 return Math.max(Math.min(600, tableResult.length * 35) + 35); 49 } 50 51 render() { 52 const { loading, onCellFilterAdded, tableResult, width, splitOpen, range, ariaLabel, timeZone } = this.props; 53 const height = this.getTableHeight(); 54 const tableWidth = width - config.theme.panelPadding * 2 - PANEL_BORDER; 55 56 let dataFrame = tableResult; 57 58 if (dataFrame?.length) { 59 dataFrame = applyFieldOverrides({ 60 data: [dataFrame], 61 timeZone, 62 theme: config.theme2, 63 replaceVariables: (v: string) => v, 64 fieldConfig: { 65 defaults: {}, 66 overrides: [], 67 }, 68 })[0]; 69 // Bit of code smell here. We need to add links here to the frame modifying the frame on every render. 70 // Should work fine in essence but still not the ideal way to pass props. In logs container we do this 71 // differently and sidestep this getLinks API on a dataframe 72 for (const field of dataFrame.fields) { 73 field.getLinks = (config: ValueLinkConfig) => { 74 return getFieldLinksForExplore({ field, rowIndex: config.valueRowIndex!, splitOpenFn: splitOpen, range }); 75 }; 76 } 77 } 78 79 return ( 80 <Collapse label="Table" loading={loading} isOpen> 81 {dataFrame?.length ? ( 82 <Table 83 ariaLabel={ariaLabel} 84 data={dataFrame} 85 width={tableWidth} 86 height={height} 87 onCellFilterAdded={onCellFilterAdded} 88 /> 89 ) : ( 90 <MetaInfoText metaItems={[{ value: '0 series returned' }]} /> 91 )} 92 </Collapse> 93 ); 94 } 95} 96 97export default connector(TableContainer); 98