Merge pull request #1029 from weaveworks/show-moar

Replace Show More buttons with carets w/ counts
This commit is contained in:
David
2016-02-25 14:03:03 +01:00
7 changed files with 107 additions and 84 deletions

View File

@@ -130,7 +130,6 @@ export default class NodeDetails extends React.Component {
renderDetails() {
const details = this.props.details;
const showSummary = (details.metadata || details.metrics || details.docker_labels) !== undefined;
const showControls = details.controls && details.controls.length > 0;
const nodeColor = getNodeColorDark(details.rank, details.label, details.pseudo);
const {error, pending} = (this.props.nodeControlStatus || {});
@@ -166,10 +165,13 @@ export default class NodeDetails extends React.Component {
</div>}
<div className="node-details-content">
{showSummary && <div className="node-details-content-section">
{details.metrics && <div className="node-details-content-section">
<div className="node-details-content-section-header">Status</div>
{details.metrics && <NodeDetailsHealth metrics={details.metrics} />}
{details.metadata && <NodeDetailsInfo rows={details.metadata} />}
<NodeDetailsHealth metrics={details.metrics} />
</div>}
{details.metadata && <div className="node-details-content-section">
<div className="node-details-content-section-header">Info</div>
<NodeDetailsInfo rows={details.metadata} />
</div>}
{details.children && details.children.map(children => {

View File

@@ -3,15 +3,23 @@ import React from 'react';
import NodeDetailsHealthOverflowItem from './node-details-health-overflow-item';
export default class NodeDetailsHealthOverflow extends React.Component {
constructor(props, context) {
super(props, context);
this.handleClick = this.handleClick.bind(this);
}
handleClick(ev) {
ev.preventDefault();
this.props.handleClick();
}
render() {
const items = this.props.items.slice(0, 4);
return (
<div className="node-details-health-overflow" onClick={this.props.handleClickMore}>
<div className="node-details-health-overflow" onClick={this.handleClick}>
{items.map(item => <NodeDetailsHealthOverflowItem key={item.id} {...item} />)}
<div className="node-details-health-overflow-expand">
Show more
</div>
</div>
);
}

View File

@@ -1,5 +1,6 @@
import React from 'react';
import ShowMore from '../show-more';
import NodeDetailsHealthOverflow from './node-details-health-overflow';
import NodeDetailsHealthItem from './node-details-health-item';
@@ -13,8 +14,7 @@ export default class NodeDetailsHealth extends React.Component {
this.handleClickMore = this.handleClickMore.bind(this);
}
handleClickMore(ev) {
ev.preventDefault();
handleClickMore() {
const expanded = !this.state.expanded;
this.setState({expanded});
}
@@ -25,17 +25,21 @@ export default class NodeDetailsHealth extends React.Component {
const primeMetrics = metrics.slice(0, primeCutoff);
const overflowMetrics = metrics.slice(primeCutoff);
const showOverflow = overflowMetrics.length > 0 && !this.state.expanded;
const showLess = this.state.expanded;
const flexWrap = showOverflow || !this.state.expanded ? 'nowrap' : 'wrap';
const justifyContent = showOverflow || !this.state.expanded ? 'space-around' : 'flex-start';
const notShown = overflowMetrics.length;
return (
<div className="node-details-health" style={{flexWrap, justifyContent}}>
{primeMetrics.map(item => {
return <NodeDetailsHealthItem key={item.id} {...item} />;
})}
{showOverflow && <NodeDetailsHealthOverflow items={overflowMetrics} handleClickMore={this.handleClickMore} />}
{showLess && <div className="node-details-health-expand" onClick={this.handleClickMore}>show less</div>}
<div className="node-details-health-wrapper">
{primeMetrics.map(item => {
return <NodeDetailsHealthItem key={item.id} {...item} />;
})}
{showOverflow && <NodeDetailsHealthOverflow items={overflowMetrics}
handleClick={() => this.handleClickMore()} />}
</div>
<ShowMore handleClick={() => this.handleClickMore()} collection={this.props.metrics}
expanded={this.state.expanded} notShown={notShown} hideNumber />
</div>
);
}

View File

@@ -1,5 +1,7 @@
import React from 'react';
import ShowMore from '../show-more';
export default class NodeDetailsInfo extends React.Component {
constructor(props, context) {
@@ -10,8 +12,7 @@ export default class NodeDetailsInfo extends React.Component {
this.handleClickMore = this.handleClickMore.bind(this);
}
handleClickMore(ev) {
ev.preventDefault();
handleClickMore() {
const expanded = !this.state.expanded;
this.setState({expanded});
}
@@ -19,11 +20,9 @@ export default class NodeDetailsInfo extends React.Component {
render() {
let rows = (this.props.rows || []);
const prime = rows.filter(row => row.prime);
let expandText = 'Show less';
let showExpand = this.state.expanded;
let notShown = 0;
if (!this.state.expanded && prime.length < rows.length) {
expandText = 'Show more';
showExpand = true;
notShown = rows.length - prime.length;
rows = prime;
}
return (
@@ -40,7 +39,8 @@ export default class NodeDetailsInfo extends React.Component {
</div>
);
})}
{showExpand && <div className="node-details-info-expand" onClick={this.handleClickMore}>{expandText}</div>}
<ShowMore handleClick={() => this.handleClickMore()} collection={this.props.rows}
expanded={this.state.expanded} notShown={notShown} />
</div>
);
}

View File

@@ -1,6 +1,7 @@
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';
@@ -25,8 +26,7 @@ export default class NodeDetailsTable extends React.Component {
this.setState({sortBy, sortedDesc});
}
handleLimitClick(ev) {
ev.preventDefault();
handleLimitClick() {
const limit = this.state.limit ? 0 : this.DEFAULT_LIMIT;
this.setState({limit});
}
@@ -115,6 +115,8 @@ export default class NodeDetailsTable extends React.Component {
}
return <NodeDetailsTableNodeMetric key={field.id} {...field} />;
}
// empty cell to complete the row for proper hover
return <td className="node-details-table-node-value" key={id} />;
});
}
@@ -122,8 +124,8 @@ export default class NodeDetailsTable extends React.Component {
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 showLimitAction = nodes && (limited || (this.state.limit === 0 && nodes.length > this.DEFAULT_LIMIT));
const limitActionText = limited ? 'Show more' : 'Show less';
const expanded = this.state.limit === 0;
const notShown = nodes.length - this.DEFAULT_LIMIT;
if (this.state.sortedDesc) {
nodes.reverse();
}
@@ -151,7 +153,7 @@ export default class NodeDetailsTable extends React.Component {
})}
</tbody>
</table>
{showLimitAction && <div className="node-details-table-more" onClick={this.handleLimitClick}>{limitActionText}</div>}
<ShowMore handleClick={() => this.handleLimitClick()} collection={this.props.nodes} expanded={expanded} notShown={notShown} />
</div>
);
}

View File

@@ -0,0 +1,30 @@
import React from 'react';
export default class ShowMore extends React.Component {
constructor(props, context) {
super(props, context);
this.handleClick = this.handleClick.bind(this);
}
handleClick(ev) {
ev.preventDefault();
this.props.handleClick();
}
render() {
const { collection, notShown, expanded, hideNumber } = this.props;
const showLimitAction = collection && (expanded || notShown > 0);
const limitActionText = !hideNumber && !expanded && notShown > 0 ? `+${notShown}` : '';
const limitActionIcon = !expanded && notShown > 0 ? 'fa fa-caret-down' : 'fa fa-caret-up';
if (!showLimitAction) {
return <span/>;
}
return (
<div className="show-more" onClick={this.handleClick}>
{limitActionText} <span className={'show-more-icon ' + limitActionIcon}/>
</div>
);
}
}

View File

@@ -546,10 +546,6 @@ h2 {
padding: 0 36px 0 36px;
overflow-y: auto;
&-info {
margin-top: 16px;
}
&-loading {
margin-top: 48px;
text-align: center;
@@ -570,46 +566,27 @@ h2 {
}
&-health {
display: flex;
justify-content: space-around;
align-content: center;
text-align: center;
&-expand {
.btn-opacity;
margin: 4px 16px 0;
border-top: 1px solid @border-light-color;
text-transform: uppercase;
font-size: 80%;
color: @text-secondary-color;
width: 100%;
cursor: pointer;
opacity: @link-opacity-default;
&-wrapper {
display: flex;
justify-content: space-around;
align-content: center;
text-align: center;
flex-wrap: wrap;
}
&-overflow {
.btn-opacity;
flex-basis: 33%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
border-left: 1px solid @border-light-color;
opacity: 0.85;
cursor: pointer;
position: relative;
padding-bottom: 16px;
&-expand {
text-transform: uppercase;
font-size: 70%;
color: @text-secondary-color;
position: absolute;
bottom: -2px;
left: 0;
right: 0;
font-weight: bold;
}
&-item {
padding: 4px 8px;
line-height: 1.2;
@@ -647,18 +624,6 @@ h2 {
}
&-info {
margin: 16px 0;
&-expand {
.btn-opacity;
text-align: center;
text-transform: uppercase;
font-size: 80%;
cursor: pointer;
color: @text-secondary-color;
padding: 2px 0;
font-weight: bold;
}
&-field {
display: flex;
@@ -724,7 +689,7 @@ h2 {
table-layout: fixed;
&-wrapper {
margin: 24px 0;
margin: 24px -4px;
}
&-header {
@@ -733,7 +698,7 @@ h2 {
font-size: 90%;
text-align: right;
cursor: pointer;
padding: 0;
padding: 0 4px;
&-sorted {
color: @text-secondary-color;
@@ -749,23 +714,16 @@ h2 {
}
}
&-more {
.btn-opacity;
padding: 2px 0;
text-align: center;
text-transform: uppercase;
cursor: pointer;
color: @text-secondary-color;
font-size: 80%;
font-weight: bold;
}
&-node {
font-size: 105%;
line-height: 1.5;
&:hover {
background-color: lighten(@background-color, 5%);
}
> * {
padding: 0;
padding: 0 4px;
}
&-link {
@@ -932,6 +890,25 @@ h2 {
}
}
.show-more {
.btn-opacity;
border-top: 1px dotted @border-light-color;
padding: 0px 0;
margin-top: 4px;
text-align: right;
text-transform: uppercase;
cursor: pointer;
color: @text-secondary-color;
font-size: 90%;
&-icon {
color: @text-tertiary-color;
font-size: 120%;
position: relative;
top: 1px;
}
}
.status {
text-transform: uppercase;