mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
178 lines
5.2 KiB
TypeScript
178 lines
5.2 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import React, { Component } from 'react';
|
|
|
|
import {
|
|
DataFrame,
|
|
FieldMatcherID,
|
|
getDataSourceRef,
|
|
getFrameDisplayName,
|
|
PanelProps,
|
|
SelectableValue,
|
|
} from '@grafana/data';
|
|
import { PanelDataErrorView } from '@grafana/runtime';
|
|
import { Select, Table } from '@grafana/ui';
|
|
import { FilterItem, TableSortByFieldState } from '@grafana/ui/src/components/Table/types';
|
|
import { config } from 'app/core/config';
|
|
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
|
|
|
import { getDashboardSrv } from '../../../features/dashboard/services/DashboardSrv';
|
|
import { applyFilterFromTable } from '../../../features/variables/adhoc/actions';
|
|
import { dispatch } from '../../../store/store';
|
|
|
|
import { getFooterCells } from './footer';
|
|
import { PanelOptions } from './models.gen';
|
|
|
|
interface Props extends PanelProps<PanelOptions> {}
|
|
|
|
export class TablePanel extends Component<Props> {
|
|
constructor(props: Props) {
|
|
super(props);
|
|
}
|
|
|
|
onColumnResize = (fieldDisplayName: string, width: number) => {
|
|
const { fieldConfig } = this.props;
|
|
const { overrides } = fieldConfig;
|
|
|
|
const matcherId = FieldMatcherID.byName;
|
|
const propId = 'custom.width';
|
|
|
|
// look for existing override
|
|
const override = overrides.find((o) => o.matcher.id === matcherId && o.matcher.options === fieldDisplayName);
|
|
|
|
if (override) {
|
|
// look for existing property
|
|
const property = override.properties.find((prop) => prop.id === propId);
|
|
if (property) {
|
|
property.value = width;
|
|
} else {
|
|
override.properties.push({ id: propId, value: width });
|
|
}
|
|
} else {
|
|
overrides.push({
|
|
matcher: { id: matcherId, options: fieldDisplayName },
|
|
properties: [{ id: propId, value: width }],
|
|
});
|
|
}
|
|
|
|
this.props.onFieldConfigChange({
|
|
...fieldConfig,
|
|
overrides,
|
|
});
|
|
};
|
|
|
|
onSortByChange = (sortBy: TableSortByFieldState[]) => {
|
|
this.props.onOptionsChange({
|
|
...this.props.options,
|
|
sortBy,
|
|
});
|
|
};
|
|
|
|
onChangeTableSelection = (val: SelectableValue<number>) => {
|
|
this.props.onOptionsChange({
|
|
...this.props.options,
|
|
frameIndex: val.value || 0,
|
|
});
|
|
|
|
// Force a redraw -- but no need to re-query
|
|
this.forceUpdate();
|
|
};
|
|
|
|
onCellFilterAdded = (filter: FilterItem) => {
|
|
const { key, value, operator } = filter;
|
|
const panelModel = getDashboardSrv().getCurrent()?.getPanelById(this.props.id);
|
|
if (!panelModel) {
|
|
return;
|
|
}
|
|
|
|
// When the datasource is null/undefined (for a default datasource), we use getInstanceSettings
|
|
// to find the real datasource ref for the default datasource.
|
|
const datasourceInstance = getDatasourceSrv().getInstanceSettings(panelModel.datasource);
|
|
const datasourceRef = datasourceInstance && getDataSourceRef(datasourceInstance);
|
|
if (!datasourceRef) {
|
|
return;
|
|
}
|
|
|
|
dispatch(applyFilterFromTable({ datasource: datasourceRef, key, operator, value }));
|
|
};
|
|
|
|
renderTable(frame: DataFrame, width: number, height: number) {
|
|
const { options } = this.props;
|
|
const footerValues = options.footer?.show ? getFooterCells(frame, options.footer) : undefined;
|
|
|
|
return (
|
|
<Table
|
|
height={height}
|
|
width={width}
|
|
data={frame}
|
|
noHeader={!options.showHeader}
|
|
showTypeIcons={options.showTypeIcons}
|
|
resizable={true}
|
|
initialSortBy={options.sortBy}
|
|
onSortByChange={this.onSortByChange}
|
|
onColumnResize={this.onColumnResize}
|
|
onCellFilterAdded={this.onCellFilterAdded}
|
|
footerValues={footerValues}
|
|
enablePagination={options.footer?.enablePagination}
|
|
/>
|
|
);
|
|
}
|
|
|
|
getCurrentFrameIndex(frames: DataFrame[], options: PanelOptions) {
|
|
return options.frameIndex > 0 && options.frameIndex < frames.length ? options.frameIndex : 0;
|
|
}
|
|
|
|
render() {
|
|
const { data, height, width, options, fieldConfig, id } = this.props;
|
|
|
|
const frames = data.series;
|
|
const count = frames?.length;
|
|
const hasFields = frames[0]?.fields.length;
|
|
|
|
if (!count || !hasFields) {
|
|
return <PanelDataErrorView panelId={id} fieldConfig={fieldConfig} data={data} />;
|
|
}
|
|
|
|
if (count > 1) {
|
|
const inputHeight = config.theme.spacing.formInputHeight;
|
|
const padding = 8 * 2;
|
|
const currentIndex = this.getCurrentFrameIndex(frames, options);
|
|
const names = frames.map((frame, index) => {
|
|
return {
|
|
label: getFrameDisplayName(frame),
|
|
value: index,
|
|
};
|
|
});
|
|
|
|
return (
|
|
<div className={tableStyles.wrapper}>
|
|
{this.renderTable(data.series[currentIndex], width, height - inputHeight - padding)}
|
|
<div className={tableStyles.selectWrapper}>
|
|
<Select options={names} value={names[currentIndex]} onChange={this.onChangeTableSelection} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return this.renderTable(data.series[0], width, height);
|
|
}
|
|
}
|
|
|
|
const tableStyles = {
|
|
wrapper: css`
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
height: 100%;
|
|
`,
|
|
noData: css`
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
`,
|
|
selectWrapper: css`
|
|
padding: 8px;
|
|
`,
|
|
};
|