React UI: More conversions to Function Components (#6259)

* React UI: More conversions to Function Components

Signed-off-by: Julius Volz <julius.volz@gmail.com>

* Address chat feedback over Riot

Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
Julius Volz 2019-11-02 21:45:22 +01:00 committed by GitHub
parent f7446778f3
commit fffb5ca1e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 130 deletions

View file

@ -1,4 +1,4 @@
import React, { PureComponent, ReactNode } from 'react'; import React, { FC, ReactNode } from 'react';
import { Alert, Table } from 'reactstrap'; import { Alert, Table } from 'reactstrap';
@ -41,96 +41,92 @@ interface Metric {
type SampleValue = [number, string]; type SampleValue = [number, string];
class DataTable extends PureComponent<QueryResult> { const limitSeries = <S extends InstantSample | RangeSamples>(series: S[]): S[] => {
limitSeries(series: InstantSample[] | RangeSamples[]): InstantSample[] | RangeSamples[] { const maxSeries = 10000;
const maxSeries = 10000;
if (series.length > maxSeries) { if (series.length > maxSeries) {
return series.slice(0, maxSeries); return series.slice(0, maxSeries);
} }
return series; return series;
};
const DataTable: FC<QueryResult> = ({ data }) => {
if (data === null) {
return <Alert color="light">No data queried yet</Alert>;
} }
render() { if (data.result === null || data.result.length === 0) {
const data = this.props.data; return <Alert color="secondary">Empty query result</Alert>;
}
if (data === null) { let rows: ReactNode[] = [];
return <Alert color="light">No data queried yet</Alert>; let limited = false;
} switch (data.resultType) {
case 'vector':
if (data.result === null || data.result.length === 0) { rows = (limitSeries(data.result) as InstantSample[]).map(
return <Alert color="secondary">Empty query result</Alert>; (s: InstantSample, index: number): ReactNode => {
}
let rows: ReactNode[] = [];
let limited = false;
switch (data.resultType) {
case 'vector':
rows = (this.limitSeries(data.result) as InstantSample[]).map(
(s: InstantSample, index: number): ReactNode => {
return (
<tr key={index}>
<td>
<SeriesName labels={s.metric} format={false} />
</td>
<td>{s.value[1]}</td>
</tr>
);
}
);
limited = rows.length !== data.result.length;
break;
case 'matrix':
rows = (this.limitSeries(data.result) as RangeSamples[]).map((s, index) => {
const valueText = s.values
.map(v => {
return [1] + ' @' + v[0];
})
.join('\n');
return ( return (
<tr style={{ whiteSpace: 'pre' }} key={index}> <tr key={index}>
<td> <td>
<SeriesName labels={s.metric} format={false} /> <SeriesName labels={s.metric} format={false} />
</td> </td>
<td>{valueText}</td> <td>{s.value[1]}</td>
</tr> </tr>
); );
}); }
limited = rows.length !== data.result.length; );
break; limited = rows.length !== data.result.length;
case 'scalar': break;
rows.push( case 'matrix':
<tr key="0"> rows = (limitSeries(data.result) as RangeSamples[]).map((s, index) => {
<td>scalar</td> const valueText = s.values
<td>{data.result[1]}</td> .map(v => {
return [1] + ' @' + v[0];
})
.join('\n');
return (
<tr style={{ whiteSpace: 'pre' }} key={index}>
<td>
<SeriesName labels={s.metric} format={false} />
</td>
<td>{valueText}</td>
</tr> </tr>
); );
break; });
case 'string': limited = rows.length !== data.result.length;
rows.push( break;
<tr key="0"> case 'scalar':
<td>scalar</td> rows.push(
<td>{data.result[1]}</td> <tr key="0">
</tr> <td>scalar</td>
); <td>{data.result[1]}</td>
break; </tr>
default: );
return <Alert color="danger">Unsupported result value type</Alert>; break;
} case 'string':
rows.push(
return ( <tr key="0">
<> <td>scalar</td>
{limited && ( <td>{data.result[1]}</td>
<Alert color="danger"> </tr>
<strong>Warning:</strong> Fetched {data.result.length} metrics, only displaying first {rows.length}. );
</Alert> break;
)} default:
<Table hover size="sm" className="data-table"> return <Alert color="danger">Unsupported result value type</Alert>;
<tbody>{rows}</tbody>
</Table>
</>
);
} }
}
return (
<>
{limited && (
<Alert color="danger">
<strong>Warning:</strong> Fetched {data.result.length} metrics, only displaying first {rows.length}.
</Alert>
)}
<Table hover size="sm" className="data-table">
<tbody>{rows}</tbody>
</Table>
</>
);
};
export default DataTable; export default DataTable;

View file

@ -1,4 +1,4 @@
import React, { PureComponent } from 'react'; import React, { FC } from 'react';
import SeriesName from './SeriesName'; import SeriesName from './SeriesName';
@ -6,31 +6,23 @@ interface LegendProps {
series: any; // TODO: Type this. series: any; // TODO: Type this.
} }
class Legend extends PureComponent<LegendProps> { const Legend: FC<LegendProps> = ({ series }) => {
renderLegendItem(s: any) { return (
return ( <table className="graph-legend">
<tr key={s.index} className="legend-item"> <tbody>
<td> {series.map((s: any) => (
<div className="legend-swatch" style={{ backgroundColor: s.color }}></div> <tr key={s.index} className="legend-item">
</td> <td>
<td> <div className="legend-swatch" style={{ backgroundColor: s.color }}></div>
<SeriesName labels={s.labels} format={true} /> </td>
</td> <td>
</tr> <SeriesName labels={s.labels} format={true} />
); </td>
} </tr>
))}
render() { </tbody>
return ( </table>
<table className="graph-legend"> );
<tbody> };
{this.props.series.map((s: any) => {
return this.renderLegendItem(s);
})}
</tbody>
</table>
);
}
}
export default Legend; export default Legend;

View file

@ -1,4 +1,4 @@
import React, { PureComponent } from 'react'; import React, { FC } from 'react';
import metricToSeriesName from './MetricFormat'; import metricToSeriesName from './MetricFormat';
interface SeriesNameProps { interface SeriesNameProps {
@ -6,11 +6,9 @@ interface SeriesNameProps {
format: boolean; format: boolean;
} }
class SeriesName extends PureComponent<SeriesNameProps> { const SeriesName: FC<SeriesNameProps> = ({ labels, format }) => {
renderFormatted(): React.ReactNode { const renderFormatted = (): React.ReactElement => {
const labels = this.props.labels!; const labelNodes: React.ReactElement[] = [];
const labelNodes: React.ReactNode[] = [];
let first = true; let first = true;
for (const label in labels) { for (const label in labels) {
if (label === '__name__') { if (label === '__name__') {
@ -31,31 +29,24 @@ class SeriesName extends PureComponent<SeriesNameProps> {
return ( return (
<> <>
<span className="legend-metric-name">{labels.__name__ || ''}</span> <span className="legend-metric-name">{labels!.__name__ || ''}</span>
<span className="legend-label-brace">{'{'}</span> <span className="legend-label-brace">{'{'}</span>
{labelNodes} {labelNodes}
<span className="legend-label-brace">{'}'}</span> <span className="legend-label-brace">{'}'}</span>
</> </>
); );
};
if (labels === null) {
return <>scalar</>;
} }
renderPlain() { if (format) {
const labels = this.props.labels!; return renderFormatted();
return metricToSeriesName(labels);
} }
// Return a simple text node. This is much faster to scroll through
render() { // for longer lists (hundreds of items).
if (this.props.labels === null) { return <>{metricToSeriesName(labels!)}</>;
return 'scalar'; };
}
if (this.props.format) {
return this.renderFormatted();
}
// Return a simple text node. This is much faster to scroll through
// for longer lists (hundreds of items).
return this.renderPlain();
}
}
export default SeriesName; export default SeriesName;