From cdf2c210578470dbfca145ff7a4d328966fd5b0c Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Wed, 24 Feb 2016 18:58:26 +0100 Subject: [PATCH 1/2] Replace Show More buttons with carets w/ counts --- .../node-details-health-overflow.js | 5 +- .../node-details/node-details-health.js | 19 +++-- .../node-details/node-details-info.js | 14 ++-- .../node-details/node-details-table.js | 10 +-- client/app/scripts/components/show-more.js | 30 +++++++ client/app/styles/main.less | 78 +++++++------------ 6 files changed, 84 insertions(+), 72 deletions(-) create mode 100644 client/app/scripts/components/show-more.js diff --git a/client/app/scripts/components/node-details/node-details-health-overflow.js b/client/app/scripts/components/node-details/node-details-health-overflow.js index 32624d631e..45e2c3c4ec 100644 --- a/client/app/scripts/components/node-details/node-details-health-overflow.js +++ b/client/app/scripts/components/node-details/node-details-health-overflow.js @@ -7,11 +7,8 @@ export default class NodeDetailsHealthOverflow extends React.Component { const items = this.props.items.slice(0, 4); return ( -
+
{items.map(item => )} -
- Show more -
); } diff --git a/client/app/scripts/components/node-details/node-details-health.js b/client/app/scripts/components/node-details/node-details-health.js index cb7cf1d4db..d389a653af 100644 --- a/client/app/scripts/components/node-details/node-details-health.js +++ b/client/app/scripts/components/node-details/node-details-health.js @@ -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,20 @@ 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 (
- {primeMetrics.map(item => { - return ; - })} - {showOverflow && } - {showLess &&
show less
} +
+ {primeMetrics.map(item => { + return ; + })} + {showOverflow && } +
+ this.handleClickMore()} collection={this.props.metrics} + expanded={this.state.expanded} notShown={notShown} hideNumber />
); } diff --git a/client/app/scripts/components/node-details/node-details-info.js b/client/app/scripts/components/node-details/node-details-info.js index 6df895cded..cf85a58d2f 100644 --- a/client/app/scripts/components/node-details/node-details-info.js +++ b/client/app/scripts/components/node-details/node-details-info.js @@ -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 {
); })} - {showExpand &&
{expandText}
} + this.handleClickMore()} collection={this.props.rows} + expanded={this.state.expanded} notShown={notShown} /> ); } diff --git a/client/app/scripts/components/node-details/node-details-table.js b/client/app/scripts/components/node-details/node-details-table.js index 7164d471b9..0d18772194 100644 --- a/client/app/scripts/components/node-details/node-details-table.js +++ b/client/app/scripts/components/node-details/node-details-table.js @@ -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}); } @@ -122,8 +122,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 +151,7 @@ export default class NodeDetailsTable extends React.Component { })} - {showLimitAction &&
{limitActionText}
} + this.handleLimitClick()} collection={this.props.nodes} expanded={expanded} notShown={notShown} /> ); } diff --git a/client/app/scripts/components/show-more.js b/client/app/scripts/components/show-more.js new file mode 100644 index 0000000000..8da206f33a --- /dev/null +++ b/client/app/scripts/components/show-more.js @@ -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 ; + } + return ( +
+ {limitActionText} +
+ ); + } +} diff --git a/client/app/styles/main.less b/client/app/styles/main.less index 8ca23f6637..68013cd1ad 100644 --- a/client/app/styles/main.less +++ b/client/app/styles/main.less @@ -570,46 +570,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; @@ -649,17 +630,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; align-items: baseline; @@ -749,21 +719,14 @@ 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; } @@ -932,6 +895,25 @@ h2 { } } +.show-more { + .btn-opacity; + border-top: 1px solid lighten(@text-tertiary-color, 30%); + 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; From f411b9c437708178170fdab3bf4e14b437937d5e Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Thu, 25 Feb 2016 14:02:32 +0100 Subject: [PATCH 2/2] Review feedback --- client/app/scripts/components/node-details.js | 10 ++++++---- .../node-details/node-details-health-overflow.js | 13 ++++++++++++- .../components/node-details/node-details-health.js | 3 ++- .../components/node-details/node-details-table.js | 2 ++ client/app/styles/main.less | 13 ++++--------- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/client/app/scripts/components/node-details.js b/client/app/scripts/components/node-details.js index a9ad0dc1b9..44f1b30da6 100644 --- a/client/app/scripts/components/node-details.js +++ b/client/app/scripts/components/node-details.js @@ -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 { }
- {showSummary &&
+ {details.metrics &&
Status
- {details.metrics && } - {details.metadata && } + +
} + {details.metadata &&
+
Info
+
} {details.children && details.children.map(children => { diff --git a/client/app/scripts/components/node-details/node-details-health-overflow.js b/client/app/scripts/components/node-details/node-details-health-overflow.js index 45e2c3c4ec..76fcea7f8c 100644 --- a/client/app/scripts/components/node-details/node-details-health-overflow.js +++ b/client/app/scripts/components/node-details/node-details-health-overflow.js @@ -3,11 +3,22 @@ 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 ( -
+
{items.map(item => )}
); diff --git a/client/app/scripts/components/node-details/node-details-health.js b/client/app/scripts/components/node-details/node-details-health.js index d389a653af..811b195643 100644 --- a/client/app/scripts/components/node-details/node-details-health.js +++ b/client/app/scripts/components/node-details/node-details-health.js @@ -35,7 +35,8 @@ export default class NodeDetailsHealth extends React.Component { {primeMetrics.map(item => { return ; })} - {showOverflow && } + {showOverflow && this.handleClickMore()} />}
this.handleClickMore()} collection={this.props.metrics} expanded={this.state.expanded} notShown={notShown} hideNumber /> diff --git a/client/app/scripts/components/node-details/node-details-table.js b/client/app/scripts/components/node-details/node-details-table.js index 0d18772194..c9e1863a5d 100644 --- a/client/app/scripts/components/node-details/node-details-table.js +++ b/client/app/scripts/components/node-details/node-details-table.js @@ -115,6 +115,8 @@ export default class NodeDetailsTable extends React.Component { } return ; } + // empty cell to complete the row for proper hover + return ; }); } diff --git a/client/app/styles/main.less b/client/app/styles/main.less index 68013cd1ad..a77207103f 100644 --- a/client/app/styles/main.less +++ b/client/app/styles/main.less @@ -546,10 +546,6 @@ h2 { padding: 0 36px 0 36px; overflow-y: auto; - &-info { - margin-top: 16px; - } - &-loading { margin-top: 48px; text-align: center; @@ -628,7 +624,6 @@ h2 { } &-info { - margin: 16px 0; &-field { display: flex; @@ -694,7 +689,7 @@ h2 { table-layout: fixed; &-wrapper { - margin: 24px 0; + margin: 24px -4px; } &-header { @@ -703,7 +698,7 @@ h2 { font-size: 90%; text-align: right; cursor: pointer; - padding: 0; + padding: 0 4px; &-sorted { color: @text-secondary-color; @@ -728,7 +723,7 @@ h2 { } > * { - padding: 0; + padding: 0 4px; } &-link { @@ -897,7 +892,7 @@ h2 { .show-more { .btn-opacity; - border-top: 1px solid lighten(@text-tertiary-color, 30%); + border-top: 1px dotted @border-light-color; padding: 0px 0; margin-top: 4px; text-align: right;