mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-02 17:50:39 +00:00
Merge pull request #1029 from weaveworks/show-moar
Replace Show More buttons with carets w/ counts
This commit is contained in:
@@ -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 => {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
30
client/app/scripts/components/show-more.js
Normal file
30
client/app/scripts/components/show-more.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user