Dashboard Table cleanups (#1793)

This branch includes some small appearance tweaks for tables in the app.

- Removes the restrictions on the MetricsTables for Authorities Grafana 
links (Authorities Grafana dashboards were added in #1772)
- Fixes the tables overflowing their containers on the Overview page
- Allows tables to be denser, allowing for more data on screen
- Fixes the colour of the meshed status bar in the ServiceMesh page
- Rixes the ErrorModal icon alignment and colour
- Small appearance tweaks to the things in the table e.g. icons
This commit is contained in:
Risha Mars 2018-10-24 11:13:28 -07:00 committed by GitHub
parent 1922dc0a0b
commit 98ee36344e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 51 deletions

View File

@ -20,11 +20,11 @@ const styles = theme => ({
});
function BaseTable(props) {
const { classes, tableRows, tableColumns, tableClassName, rowKey} = props;
const { classes, tableRows, tableColumns, tableClassName, rowKey, padding} = props;
return (
<Paper className={classes.root}>
<Table className={`${classes.table} ${tableClassName}`}>
<Table className={`${classes.table} ${tableClassName}`} padding={padding}>
<TableHead>
<TableRow>
{ _.map(tableColumns, c => (
@ -60,6 +60,7 @@ function BaseTable(props) {
BaseTable.propTypes = {
classes: PropTypes.shape({}).isRequired,
padding: PropTypes.string,
rowKey: PropTypes.func,
tableClassName: PropTypes.string,
tableColumns: PropTypes.arrayOf(PropTypes.shape({
@ -71,9 +72,10 @@ BaseTable.propTypes = {
};
BaseTable.defaultProps = {
padding: "default",
rowKey: null,
tableClassName: "",
tableRows: []
tableRows: [],
};
export default withStyles(styles)(BaseTable);

View File

@ -79,7 +79,7 @@ class ErrorModal extends React.Component {
}
return _.map(errorsByContainer, (errors, container) => (
<div key={`error-${container}`} className="container-error">
<div key={`error-${container}`}>
<Grid
container
direction="row"
@ -95,7 +95,7 @@ class ErrorModal extends React.Component {
</Grid>
</Grid>
<div className="error-text">
<div>
{
_.map(errors, (er, i) => {
if (_.size(er.message) === 0) {
@ -121,8 +121,8 @@ class ErrorModal extends React.Component {
renderPodErrors = errors => {
return _.map(errors, err => {
return (
<div className="controller-pod-error" key={err.pod}>
<Typography variant="title" gutterBottom>{err.pod}</Typography>
<div key={err.pod}>
<Typography variant="h6" gutterBottom>{err.pod}</Typography>
{this.renderContainerErrors(err.pod, err.byContainer)}
</div>
);
@ -145,7 +145,9 @@ class ErrorModal extends React.Component {
<Tooltip title="Pods are initializing"><CircularProgress size={20} thickness={4} /></Tooltip>
);
} else {
return <ErrorIcon onClick={this.handleClickOpen} />;
return (
<ErrorIcon color="error" fontSize="small" onClick={this.handleClickOpen} />
);
}
}
@ -153,7 +155,7 @@ class ErrorModal extends React.Component {
let errors = this.processErrorData(this.props.errors);
return (
<div>
<React.Fragment>
{this.renderStatusIcon(errors)}
<Dialog
open={this.state.open}
@ -183,7 +185,7 @@ class ErrorModal extends React.Component {
<Button onClick={this.handleClose} color="primary">Close</Button>
</DialogActions>
</Dialog>
</div>
</React.Fragment>
);
}
}

View File

@ -1,5 +1,6 @@
import BaseTable from './BaseTable.jsx';
import ErrorModal from './ErrorModal.jsx';
import Grid from '@material-ui/core/Grid';
import PropTypes from 'prop-types';
import React from 'react';
import { StyledProgress } from './util/Progress.jsx';
@ -24,10 +25,12 @@ const namespacesColumns = PrefixedLink => [
render: d => {
return (
<React.Fragment>
<PrefixedLink to={"/namespaces/" + d.namespace}>{d.namespace}</PrefixedLink>
{ _.isEmpty(d.errors) ? null :
<ErrorModal errors={d.errors} resourceName={d.namespace} resourceType="namespace" />
<Grid container alignItems="center" spacing={8}>
<Grid item><PrefixedLink to={"/namespaces/" + d.namespace}>{d.namespace}</PrefixedLink></Grid>
{ _.isEmpty(d.errors) ? null :
<Grid item><ErrorModal errors={d.errors} resourceName={d.namespace} resourceType="namespace" /></Grid>
}
</Grid>
</React.Fragment>
);
}

View File

@ -21,21 +21,6 @@ const columnDefinitions = (resource, showNamespaceColumn, PrefixedLink) => {
}
];
let grafanaLinkColumn = [
{
title: "Grafana Dashboard",
key: "grafanaDashboard",
isNumeric: true,
render: row => !row.added || _.get(row, "pods.totalPods") === "0" ? null : (
<GrafanaLink
name={row.name}
namespace={row.namespace}
resource={resource}
PrefixedLink={PrefixedLink} />
)
}
];
let meshedColumn = {
title: "Meshed",
key: "meshed",
@ -63,7 +48,7 @@ const columnDefinitions = (resource, showNamespaceColumn, PrefixedLink) => {
}
return (
<React.Fragment>
{nameContents}
{nameContents}&nbsp;&nbsp;
{ _.isEmpty(d.errors) ? null : <ErrorModal errors={d.errors} resourceName={d.name} resourceType={resource} /> }
</React.Fragment>
);
@ -104,13 +89,30 @@ const columnDefinitions = (resource, showNamespaceColumn, PrefixedLink) => {
key: "has_tls",
isNumeric: true,
render: d => _.isNil(d.tlsRequestPercent) || d.tlsRequestPercent.get() === -1 ? "---" : d.tlsRequestPercent.prettyRate()
},
{
title: "Grafana Dashboard",
key: "grafanaDashboard",
isNumeric: true,
render: row => {
if (!isAuthorityTable && (!row.added || _.get(row, "pods.totalPods") === "0") ) {
return null;
}
return (
<GrafanaLink
name={row.name}
namespace={row.namespace}
resource={resource}
PrefixedLink={PrefixedLink} />
);
}
}
];
// don't add the meshed column on a Authority MetricsTable
// don't add the meshed column on a Authority MetricsTable
if (!isAuthorityTable) {
columns.splice(1, 0, meshedColumn);
columns = _.concat(columns, grafanaLinkColumn);
}
if (!showNamespaceColumn) {
@ -159,7 +161,8 @@ class MetricsTable extends React.Component {
<BaseTable
tableRows={rows}
tableColumns={columns}
tableClassName="metric-table" />
tableClassName="metric-table"
padding="dense" />
);
}
}

View File

@ -52,13 +52,13 @@ describe('Tests for <MetricsTable>', () => {
expect(table.props().tableColumns).toHaveLength(9);
});
it('omits meshed column and grafana column for authority resource', () => {
it('omits meshed column for an authority resource', () => {
let extraProps = _.merge({}, defaultProps, { metrics: [], resource: "authority"});
const component = mount(routerWrap(MetricsTable, extraProps));
const table = component.find("BaseTable");
expect(table).toBeDefined();
expect(table.props().tableColumns).toHaveLength(8);
expect(table.props().tableColumns).toHaveLength(9);
});
});

View File

@ -5,6 +5,7 @@ import { processMultiResourceRollup, processSingleResourceRollup } from './util/
import Accordion from './util/Accordion.jsx';
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
import ErrorBanner from './ErrorBanner.jsx';
import Grid from '@material-ui/core/Grid';
import MetricsTable from './MetricsTable.jsx';
import PropTypes from 'prop-types';
import React from 'react';
@ -17,7 +18,7 @@ import { withContext } from './util/AppContext.jsx';
const isMeshedTooltip = (
<Tooltip title="Namespace is meshed" placement="right-start">
<CheckCircleIcon />
<CheckCircleIcon color="primary" />
</Tooltip>
);
class NamespaceLanding extends React.Component {
@ -124,14 +125,18 @@ class NamespaceLanding extends React.Component {
return null;
}
return (
<div className="page-section">
<br />
<Typography variant="h5">{friendlyTitle(resource).plural}</Typography>
<MetricsTable
resource={resource}
metrics={metrics}
showNamespaceColumn={false} />
</div>
<Grid container direction="column" justify="center">
<Grid item>
<Typography variant="h5">{friendlyTitle(resource).plural}</Typography>
</Grid>
<Grid item>
<MetricsTable
resource={resource}
metrics={metrics}
showNamespaceColumn={false} />
</Grid>
</Grid>
);
}
@ -144,14 +149,15 @@ class NamespaceLanding extends React.Component {
let noMetrics = _.isEmpty(metrics.pod);
return (
<div>
<Typography variant="h4">Namespace: {namespace}</Typography>
{ noMetrics ? <div>No resources detected.</div> : null}
<Grid container direction="column">
<Grid item><Typography variant="h4">Namespace: {namespace}</Typography></Grid>
<Grid item>{ noMetrics ? <div>No resources detected.</div> : null}</Grid>
{this.renderResourceSection("deployment", metrics.deployment)}
{this.renderResourceSection("replicationcontroller", metrics.replicationcontroller)}
{this.renderResourceSection("pod", metrics.pod)}
{this.renderResourceSection("authority", metrics.authority)}
</div>
</Grid>
);
}
@ -159,19 +165,20 @@ class NamespaceLanding extends React.Component {
let panelData = _.map(this.state.namespaces, ns => {
return {
id: ns.name,
header: <React.Fragment>{ns.name} {!ns.added ? null : isMeshedTooltip}</React.Fragment>,
header: <React.Fragment><Typography variant="subtitle1">{ns.name}</Typography> {!ns.added ? null : isMeshedTooltip}</React.Fragment>,
body: ns.name === this.state.selectedNs || ns.name === this.state.defaultOpenNs.name ?
this.renderNamespaceSection(ns.name) : null
};
});
return (
<React.Fragment>
<Grid container>
<Accordion
onChange={this.onNamespaceChange}
panels={panelData}
defaultOpenPanel={_.get(this.state.defaultOpenNs, 'name', null)} />
</React.Fragment>
</Grid>
);
}

View File

@ -1,4 +1,5 @@
import LinearProgress from '@material-ui/core/LinearProgress';
import grey from '@material-ui/core/colors/grey';
import { withStyles } from '@material-ui/core/styles';
const colorLookup = {
@ -6,10 +7,14 @@ const colorLookup = {
colorPrimary: '#c8e6c9', // background bar color (lighter)
barColorPrimary: '#388e3c', // inner bar color (darker)
},
neutral: {
warning: {
colorPrimary: '#ffcc80',
barColorPrimary: '#ef6c00',
},
neutral: {
colorPrimary: grey[200],
barColorPrimary: grey[500],
},
poor: {
colorPrimary: '#ffebee',
barColorPrimary: '#d32f2f',