Merge remote-tracking branch 'origin/master' into feature/status_page-1556

pull/10616/head
Jared Scheib 2017-06-14 19:27:24 -07:00
commit b84d23e7e0
21 changed files with 498 additions and 1983 deletions

View File

@ -2,6 +2,8 @@
### Bug Fixes
1. [#1512](https://github.com/influxdata/chronograf/pull/1512): Prevent legend from flowing over window bottom bound
1. [#1600](https://github.com/influxdata/chronograf/pull/1600): Prevent Kapacitor configurations from having the same name
1. [#1600](https://github.com/influxdata/chronograf/pull/1600): Limit Kapacitor configuration names to 33 characters to fix display bug
### Features
1. [#1512](https://github.com/influxdata/chronograf/pull/1512): Synchronize vertical crosshair at same time across all graphs in a dashboard
@ -10,6 +12,8 @@
### UI Improvements
1. [#1512](https://github.com/influxdata/chronograf/pull/1512): When dashboard time range is changed, reset graphs that are zoomed in
1. [#1599](https://github.com/influxdata/chronograf/pull/1599): Bar graph option added to dashboard
1. [#1600](https://github.com/influxdata/chronograf/pull/1600): Redesign source management table to be more intuitive
1. [#1600](https://github.com/influxdata/chronograf/pull/1600): Redesign Line + Single Stat cells to appear more like a sparkline, and improve legibility
## v1.3.2.1 [2017-06-06]

View File

@ -99,7 +99,7 @@ const DashboardsPage = React.createClass({
<tbody>
{dashboards.map(dashboard =>
<tr key={dashboard.id} className="">
<td className="monotype">
<td>
<Link
to={`${dashboardLink}/dashboards/${dashboard.id}`}
>

View File

@ -51,7 +51,7 @@ const Header = React.createClass({
<div className="page-header__right">
<GraphTips />
<SourceIndicator sourceName={this.context.source.name} />
<div className="btn btn-sm btn-info" onClick={showWriteForm}>
<div className="btn btn-sm btn-default" onClick={showWriteForm}>
<span className="icon pencil" />
Write Data
</div>

View File

@ -298,12 +298,12 @@ Dygraph.Plugins.Crosshair = (function() {
const gradient = ctx.createLinearGradient(0, 0, 0, height)
gradient.addColorStop(0, 'rgba(255, 255, 255, 0.0)')
gradient.addColorStop(0.2, 'rgba(255, 255, 255, 1.0)')
gradient.addColorStop(0.8, 'rgba(255, 255, 255, 1.0)')
gradient.addColorStop(0.11, 'rgba(255, 255, 255, 1.0)')
gradient.addColorStop(0.89, 'rgba(255, 255, 255, 1.0)')
gradient.addColorStop(1, 'rgba(255, 255, 255, 0.0)')
ctx.strokeStyle = gradient
ctx.lineWidth = 2
ctx.lineWidth = 1.5
// If graphs have different time ranges, it's possible to select a point on
// one graph that doesn't exist in another, resulting in an exception.

View File

@ -1,10 +1,10 @@
import React, {PropTypes} from 'react'
import {Link} from 'react-router'
import _ from 'lodash'
import shallowCompare from 'react-addons-shallow-compare'
import classnames from 'classnames'
import _ from 'lodash'
import {HOSTS_TABLE} from 'src/hosts/constants/tableSizing'
const {arrayOf, bool, number, shape, string} = PropTypes
@ -101,6 +101,7 @@ const HostsTable = React.createClass({
sortDirection
)
const hostCount = sortedHosts.length
const {colName, colStatus, colCPU, colLoad} = HOSTS_TABLE
let hostsTitle
@ -128,27 +129,28 @@ const HostsTable = React.createClass({
<th
onClick={() => this.updateSort('name')}
className={this.sortableClasses('name')}
style={{width: colName}}
>
Host
</th>
<th
onClick={() => this.updateSort('deltaUptime')}
className={this.sortableClasses('deltaUptime')}
style={{width: '74px'}}
style={{width: colStatus}}
>
Status
</th>
<th
onClick={() => this.updateSort('cpu')}
className={this.sortableClasses('cpu')}
style={{width: '70px'}}
style={{width: colCPU}}
>
CPU
</th>
<th
onClick={() => this.updateSort('load')}
className={this.sortableClasses('load')}
style={{width: '68px'}}
style={{width: colLoad}}
>
Load
</th>
@ -195,13 +197,14 @@ const HostRow = React.createClass({
render() {
const {host, source} = this.props
const {name, cpu, load, apps = []} = host
const {colName, colStatus, colCPU, colLoad} = HOSTS_TABLE
return (
<tr>
<td className="monotype">
<td style={{width: colName}}>
<Link to={`/sources/${source.id}/hosts/${name}`}>{name}</Link>
</td>
<td style={{width: '74px'}}>
<td style={{width: colStatus}}>
<div
className={classnames(
'table-dot',
@ -209,13 +212,13 @@ const HostRow = React.createClass({
)}
/>
</td>
<td className="monotype" style={{width: '70px'}}>
<td style={{width: colCPU}} className="monotype">
{isNaN(cpu) ? 'N/A' : `${cpu.toFixed(2)}%`}
</td>
<td className="monotype" style={{width: '68px'}}>
<td style={{width: colLoad}} className="monotype">
{isNaN(load) ? 'N/A' : `${load.toFixed(2)}`}
</td>
<td className="monotype">
<td>
{apps.map((app, index) => {
return (
<span key={app}>

View File

@ -0,0 +1,6 @@
export const HOSTS_TABLE = {
colName: '40%',
colStatus: '74px',
colCPU: '70px',
colLoad: '68px',
}

View File

@ -52,6 +52,7 @@ class KapacitorForm extends Component {
value={name}
onChange={onInputChange}
spellCheck="false"
maxLength="33"
/>
</div>
<div className="form-group">

View File

@ -71,6 +71,15 @@ class KapacitorPage extends Component {
const {addFlashMessage, source, params, router} = this.props
const {kapacitor} = this.state
const kapNames = source.kapacitors.map(k => k.name)
if (kapNames.includes(kapacitor.name)) {
addFlashMessage({
type: 'error',
text: `There is already a Kapacitor configuration named "${kapacitor.name}"`,
})
return
}
if (params.id) {
updateKapacitor(kapacitor)
.then(({data}) => {
@ -142,7 +151,7 @@ class KapacitorPage extends Component {
}
}
const {func, shape, string} = PropTypes
const {array, func, shape, string} = PropTypes
KapacitorPage.propTypes = {
addFlashMessage: func,
@ -155,6 +164,7 @@ KapacitorPage.propTypes = {
source: shape({
id: string.isRequired,
url: string.isRequired,
kapacitors: array.isRequired,
}),
}

View File

@ -9,6 +9,8 @@ import lastValues from 'shared/parsing/lastValues'
const {array, arrayOf, bool, func, number, shape, string} = PropTypes
const SMALL_CELL_HEIGHT = 1
export default React.createClass({
displayName: 'LineGraph',
propTypes: {
@ -37,6 +39,7 @@ export default React.createClass({
}),
isInDataExplorer: bool,
synchronizer: func,
cellHeight: number,
},
getDefaultProps() {
@ -91,6 +94,7 @@ export default React.createClass({
ruleValues,
synchronizer,
timeRange,
cellHeight,
} = this.props
const {labels, timeSeries, dygraphSeries} = this._timeSeries
@ -112,7 +116,7 @@ export default React.createClass({
title,
rightGap: 0,
yRangePad: 10,
axisLabelWidth: 38,
axisLabelWidth: 60,
drawAxesAtZero: true,
underlayCallback,
ylabel: _.get(queries, ['0', 'label'], ''),
@ -120,6 +124,32 @@ export default React.createClass({
...displayOptions,
}
const singleStatOptions = {
labels,
connectSeparatedPoints: true,
labelsKMB: true,
axes: {
x: {
drawGrid: false,
drawAxis: false,
},
y: {
drawGrid: false,
drawAxis: false,
},
},
title,
rightGap: 0,
strokeWidth: 1.5,
drawAxesAtZero: true,
underlayCallback,
...displayOptions,
highlightSeriesOpts: {
strokeWidth: 1.5,
},
}
const singleStatLineColor = ['#7A65F2']
let roundedValue
if (showSingleStat) {
const lastValue = lastValues(data)[1]
@ -138,12 +168,14 @@ export default React.createClass({
{isRefreshing ? this.renderSpinner() : null}
<Dygraph
containerStyle={{width: '100%', height: '100%'}}
overrideLineColors={overrideLineColors}
isGraphFilled={isGraphFilled}
overrideLineColors={
showSingleStat ? singleStatLineColor : overrideLineColors
}
isGraphFilled={showSingleStat ? false : isGraphFilled}
isBarGraph={isBarGraph}
timeSeries={timeSeries}
labels={labels}
options={options}
options={showSingleStat ? singleStatOptions : options}
dygraphSeries={dygraphSeries}
ranges={ranges || this.getRanges()}
ruleValues={ruleValues}
@ -151,8 +183,14 @@ export default React.createClass({
timeRange={timeRange}
/>
{showSingleStat
? <div className="graph-single-stat single-stat">
<span className="single-stat--value">{roundedValue}</span>
? <div className="single-stat single-stat-line">
<span
className={classnames('single-stat--value', {
'single-stat--small': cellHeight === SMALL_CELL_HEIGHT,
})}
>
<span className="single-stat--shadow">{roundedValue}</span>
</span>
</div>
: null}
</div>

View File

@ -8,4 +8,5 @@
{defaultGroupBy: '30m', seconds: 172800, inputValue: 'Past 2 days', lower: 'now() - 2d', upper: null, menuOption: 'Past 2 days'},
{defaultGroupBy: '1h', seconds: 604800, inputValue: 'Past 7 days', lower: 'now() - 7d', upper: null, menuOption: 'Past 7 days'},
{defaultGroupBy: '6h', seconds: 2592000, inputValue: 'Past 30 days', lower: 'now() - 30d', upper: null, menuOption: 'Past 30 days'},
{defaultGroupBy: '12h', seconds: 7776000, inputValue: 'Past 90 days', lower: 'now() - 90d', upper: null, menuOption: 'Past 90 days'},
]

View File

@ -9,6 +9,8 @@ import {
NavListItem,
} from 'src/side_nav/components/NavItems'
import {DEFAULT_HOME_PAGE} from 'shared/constants'
const {bool, shape, string} = PropTypes
const SideNav = React.createClass({
@ -39,7 +41,7 @@ const SideNav = React.createClass({
? null
: <NavBar location={location}>
<div className="sidebar__logo">
<Link to={`${sourcePrefix}/status`}>
<Link to={`${sourcePrefix}/${DEFAULT_HOME_PAGE}`}>
<span className="icon cubo-uniform" />
</Link>
</div>

View File

@ -2,6 +2,7 @@ import React, {PropTypes} from 'react'
import {Link, withRouter} from 'react-router'
import Dropdown from 'shared/components/Dropdown'
import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip'
const kapacitorDropdown = (
kapacitors,
@ -12,7 +13,12 @@ const kapacitorDropdown = (
) => {
if (!kapacitors || kapacitors.length === 0) {
return (
<Link to={`/sources/${source.id}/kapacitors/new`}>Add Kapacitor</Link>
<Link
to={`/sources/${source.id}/kapacitors/new`}
className="btn btn-xs btn-default"
>
Add Config
</Link>
)
}
const kapacitorItems = kapacitors.map(k => {
@ -35,7 +41,7 @@ const kapacitorDropdown = (
return (
<Dropdown
className="dropdown-260"
buttonColor="btn-default"
buttonColor="btn-primary"
buttonSize="btn-xs"
items={kapacitorItems}
onChoose={item => setActiveKapacitor(item.kapacitor)}
@ -90,27 +96,59 @@ const InfluxTable = ({
<table className="table v-center margin-bottom-zero table-highlight">
<thead>
<tr>
<th>Name</th>
<th>Host</th>
<th>Kapacitor</th>
<th className="source-table--connect-col" />
<th>Source Name & Host</th>
<th className="text-right" />
<th>
Active Kapacitor{' '}
<QuestionMarkTooltip
tipID="kapacitor-node-helper"
tipContent="Kapacitor Configurations are scoped per InfluxDB Source. Only one can be active at a time"
/>
</th>
</tr>
</thead>
<tbody>
{sources.map(s => {
return (
<tr key={s.id}>
<tr
key={s.id}
className={s.id === source.id ? 'highlight' : null}
>
<td>
<Link to={`${location.pathname}/${s.id}/edit`}>
{s.name}
</Link>
{' '}
{s.default
? <span className="default-source-label">Default</span>
: null}
{s.id === source.id
? <div className="btn btn-success btn-xs source-table--connect">
Connected
</div>
: <Link
className="btn btn-default btn-xs source-table--connect"
to={`/sources/${s.id}/hosts`}
>
Connect
</Link>}
</td>
<td className="monotype">{s.url}</td>
<td>
<h5 className="margin-zero">
<Link
to={`${location.pathname}/${s.id}/edit`}
className={s.id === source.id ? 'link-success' : null}
>
<strong>{s.name}</strong>
{s.default ? ' (Default)' : null}
</Link>
</h5>
<span>{s.url}</span>
</td>
<td className="text-right">
<a
className="btn btn-xs btn-danger table--show-on-row-hover"
href="#"
onClick={() => handleDeleteSource(s)}
>
Delete Source
</a>
</td>
<td className="source-table--kapacitor">
{kapacitorDropdown(
s.kapacitors,
s,
@ -119,24 +157,6 @@ const InfluxTable = ({
handleDeleteKapacitor
)}
</td>
<td className="text-right">
{s.id === source.id
? <span className="currently-connected-source">
<span className="icon checkmark" /> Connected
</span>
: <Link
className="btn btn-success btn-xs"
to={`/sources/${s.id}/hosts`}
>
Connect
</Link>}
<button
className="btn btn-danger btn-xs btn-square"
onClick={() => handleDeleteSource(s)}
>
<span className="icon trash" />
</button>
</td>
</tr>
)
})}

View File

@ -3,7 +3,7 @@ import timeRanges from 'hson!shared/data/timeRanges.hson'
import * as actionTypes from 'src/status/constants/actionTypes'
const {lower, upper} = timeRanges.find(tr => tr.lower === 'now() - 30d')
const {lower, upper} = timeRanges.find(tr => tr.lower === 'now() - 90d')
const initialState = {
autoRefresh: AUTOREFRESH_DEFAULT,

View File

@ -128,10 +128,10 @@
.graph--hasYLabel {
.dygraph-axis-label-y {
padding: 0 1px 0 10px !important;
padding: 0 1px 0 16px !important;
}
.dygraph-axis-label-y2 {
padding: 0 10px 0 1px !important;
padding: 0 16px 0 1px !important;
}
}
@ -153,13 +153,37 @@
top: calc(50% - 15px);
left: 50%;
transform: translate(-50%,-50%);
width: calc(100% - 32px);
// overflow: hidden;
text-align: center;
// text-overflow: ellipsis;
font-size: 54px;
line-height: 54px;
font-weight: 300;
color: $c-pool;
color: $c-laser;
z-index: 1;
&.single-stat--small {
font-size: 38px;
line-height: 38px;
font-weight: 400;
font-size: 34px;
line-height: 34px;
}
}
.single-stat--shadow {
position: relative;
display: inline-block;
}
.single-stat--shadow:after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 110%;
height: 0;
transform: translate(-50%,-50%);
box-shadow: fade-out($g2-kevlar, 0.3) 0 0 50px 30px;
z-index: -1;
}
.single-stat--small .single-stat--shadow:after {
box-shadow: fade-out($g2-kevlar, 0.3) 0 0 30px 10px;
}

View File

@ -167,6 +167,11 @@ $table-tab-scrollbar-height: 6px;
border-radius: 0 $radius-small $radius-small $radius-small;
}
.table > tbody > tr.highlight,
.table.table-highlight > tbody > tr.highlight {
background-color: $g4-onyx;
}
/*
Responsive Tables
----------------------------------------------

View File

@ -149,7 +149,7 @@ pre.admin-table--query {
font-size: 16px;
font-family: $code-font;
padding-left: 6px;
width: auto;
@include no-user-select();
}
}
.db-manager-header--edit {

View File

@ -204,6 +204,7 @@ input.form-control.dash-graph--name-edit {
transition-property: all;
> li {
@include no-user-select;
position: relative;
width: 100%;
height: 28px;
@ -241,7 +242,7 @@ input.form-control.dash-graph--name-edit {
&:hover {
cursor: pointer;
background-color: $g6-smoke;
background-color: $g7-graphite;
color: $g20-white;
}
}

View File

@ -24,7 +24,6 @@ $rule-builder--radius-lg: 5px;
align-items: stretch;
}
.rule-builder p {
width: auto;
margin: 0 ($rule-builder--padding-sm - 2px);
font-weight: 600;
color: $g15-platinum;

View File

@ -54,7 +54,6 @@ $overlay-z: 100;
margin: 0 0 0 5px;
}
p {
width: auto;
font-weight: 600;
color: $g13-mist;
margin: 0 6px 0 0;
@ -63,7 +62,6 @@ $overlay-z: 100;
}
}
.overlay--graph-name {
width: auto;
margin: 0;
font-size: 17px;
font-weight: 400;

File diff suppressed because it is too large Load Diff

View File

@ -158,6 +158,17 @@ br {
@include gradient-v($g2-kevlar, $g0-obsidian);
}
.source-table--connect {
width: 74px;
}
.source-table--connect-col {
width: 90px;
}
.source-table--kapacitor {
border-left: 2px solid $g5-pepper;
width: 278px;
}
/*
Styles for the Status Dashboard
-----------------------------------------------------------------------------