Merge branch 'master' into bugfix/kapacitor-loading-4ever
commit
1724b532c9
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 1.4.3.0
|
||||
current_version = 1.4.3.1
|
||||
files = README.md server/swagger.json
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.(?P<release>\d+)
|
||||
serialize = {major}.{minor}.{patch}.{release}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
Closes #
|
||||
|
||||
_Briefly describe your proposed changes:_
|
||||
_What was the problem?_
|
||||
_What was the solution?_
|
||||
|
||||
- [ ] CHANGELOG.md updated with a link to the PR (not the Issue)
|
||||
- [ ] Rebased/mergable
|
||||
- [ ] Tests pass
|
||||
- [ ] Sign [CLA](https://influxdata.com/community/cla/) (if not already signed)
|
||||
|
||||
Connect #
|
||||
|
||||
### The problem
|
||||
|
||||
### The Solution
|
||||
|
||||
|
||||
- [ ] Sign [CLA](https://influxdata.com/community/cla/) (if not already signed)
|
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -3,7 +3,9 @@
|
|||
### Features
|
||||
|
||||
1. [#2526](https://github.com/influxdata/chronograf/pull/2526): Add support for RS256/JWKS verification, support for id_token parsing (as in ADFS)
|
||||
1. [#3060](https://github.com/influxdata/chronograf/pull/3060): Add ability to set a color palette for Line, Stacked, Step-Plot, and Bar graphs
|
||||
1. [#3103](https://github.com/influxdata/chronograf/pull/3103): Add ability to clone dashboards
|
||||
1. [#3080](https://github.com/influxdata/chronograf/pull/3080): Add tabular data visualization option with features
|
||||
|
||||
### UI Improvements
|
||||
|
||||
|
@ -14,8 +16,15 @@
|
|||
### Bug Fixes
|
||||
|
||||
1. [#2950](https://github.com/influxdata/chronograf/pull/2094): Always save template variables on first edit
|
||||
1. [#3101](https://github.com/influxdata/chronograf/pull/3101): Fix template variables not loading
|
||||
1. [#3109](https://github.com/influxdata/chronograf/pull/3109): Display link to configure Kapacitor on Alerts Page if no configured kapacitor.
|
||||
1. [#3111](https://github.com/influxdata/chronograf/pull/3111): Fix saving of new TICKscripts
|
||||
|
||||
## v1.4.3.1 [2018-04-02]
|
||||
### Bug Fixes
|
||||
|
||||
1. [#3107](https://github.com/influxdata/chronograf/pull/3107): Fixes template variable editing not allowing saving
|
||||
1. [#3094](https://github.com/influxdata/chronograf/pull/3094): Save template variables on first edit
|
||||
1. [#3101](https://github.com/influxdata/chronograf/pull/3101): Fix template variables not loading all values
|
||||
|
||||
## v1.4.3.0 [2018-3-28]
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ option.
|
|||
## Versions
|
||||
|
||||
The most recent version of Chronograf is
|
||||
[v1.4.3.0](https://www.influxdata.com/downloads/).
|
||||
[v1.4.3.1](https://www.influxdata.com/downloads/).
|
||||
|
||||
Spotted a bug or have a feature request? Please open
|
||||
[an issue](https://github.com/influxdata/chronograf/issues/new)!
|
||||
|
@ -178,7 +178,7 @@ By default, chronograf runs on port `8888`.
|
|||
To get started right away with Docker, you can pull down our latest release:
|
||||
|
||||
```sh
|
||||
docker pull chronograf:1.4.3.0
|
||||
docker pull chronograf:1.4.3.1
|
||||
```
|
||||
|
||||
### From Source
|
||||
|
|
|
@ -113,7 +113,7 @@ func HasCorrectAxes(c *chronograf.DashboardCell) error {
|
|||
// HasCorrectColors verifies that the format of each color is correct
|
||||
func HasCorrectColors(c *chronograf.DashboardCell) error {
|
||||
for _, color := range c.CellColors {
|
||||
if !oneOf(color.Type, "max", "min", "threshold", "text", "background") {
|
||||
if !oneOf(color.Type, "max", "min", "threshold", "text", "background", "scale") {
|
||||
return chronograf.ErrInvalidColorType
|
||||
}
|
||||
if len(color.Hex) != 7 {
|
||||
|
|
|
@ -635,7 +635,7 @@ type sourceUsersResponse struct {
|
|||
}
|
||||
|
||||
func (r *sourceUserRequest) ValidUpdate() error {
|
||||
if r.Password == "" && len(r.Permissions) == 0 && len(r.Roles) == 0 {
|
||||
if r.Password == "" && r.Permissions == nil && r.Roles == nil {
|
||||
return fmt.Errorf("No fields to update")
|
||||
}
|
||||
return validPermissions(&r.Permissions)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"info": {
|
||||
"title": "Chronograf",
|
||||
"description": "API endpoints for Chronograf",
|
||||
"version": "1.4.3.0"
|
||||
"version": "1.4.3.1"
|
||||
},
|
||||
"schemes": ["http"],
|
||||
"basePath": "/chronograf/v1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "chronograf-ui",
|
||||
"version": "1.4.3-0",
|
||||
"version": "1.4.3-1",
|
||||
"private": false,
|
||||
"license": "AGPL-3.0",
|
||||
"description": "",
|
||||
|
@ -116,6 +116,7 @@
|
|||
"axios": "^0.13.1",
|
||||
"bignumber.js": "^4.0.2",
|
||||
"calculate-size": "^1.1.1",
|
||||
"chroma-js": "^1.3.6",
|
||||
"classnames": "^2.2.3",
|
||||
"dygraphs": "2.1.0",
|
||||
"eslint-plugin-babel": "^4.1.2",
|
||||
|
@ -147,4 +148,4 @@
|
|||
"rome": "^2.1.22",
|
||||
"uuid": "^3.2.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
|
||||
import MultiSelectDropdown from 'src/shared/components/MultiSelectDropdown'
|
||||
|
||||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
import {User} from 'src/types/influxAdmin'
|
||||
|
||||
interface Props {
|
||||
user: User
|
||||
allPermissions: string[]
|
||||
onUpdatePermissions: (user: User, permissions: any[]) => void
|
||||
}
|
||||
|
||||
class UserPermissionsDropdown extends PureComponent<Props> {
|
||||
public render() {
|
||||
return (
|
||||
<MultiSelectDropdown
|
||||
buttonSize="btn-xs"
|
||||
buttonColor="btn-primary"
|
||||
resetStateOnReceiveProps={false}
|
||||
items={this.allPermissions}
|
||||
label={this.permissionsLabel}
|
||||
customClass={this.permissionsClass}
|
||||
selectedItems={this.selectedPermissions}
|
||||
onApply={this.handleUpdatePermissions}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private handleUpdatePermissions = (permissions): void => {
|
||||
const {onUpdatePermissions, user} = this.props
|
||||
const allowed = permissions.map(p => p.name)
|
||||
onUpdatePermissions(user, [{scope: 'all', allowed}])
|
||||
}
|
||||
|
||||
private get allPermissions() {
|
||||
return this.props.allPermissions.map(p => ({name: p}))
|
||||
}
|
||||
|
||||
private get userPermissions() {
|
||||
return _.get(this.props.user, ['permissions', '0', 'allowed'], [])
|
||||
}
|
||||
|
||||
private get selectedPermissions() {
|
||||
return this.userPermissions.map(p => ({name: p}))
|
||||
}
|
||||
|
||||
private get permissionsLabel() {
|
||||
const {user} = this.props
|
||||
if (user.permissions && user.permissions.length) {
|
||||
return 'Select Permissions'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
private get permissionsClass() {
|
||||
return classnames(`dropdown-${USERS_TABLE.colPermissions}`, {
|
||||
'admin-table--multi-select-empty': !this.props.user.permissions.length,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default UserPermissionsDropdown
|
|
@ -0,0 +1,58 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
import MultiSelectDropdown from 'src/shared/components/MultiSelectDropdown'
|
||||
|
||||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
import {User, UserRole} from 'src/types/influxAdmin'
|
||||
|
||||
interface Props {
|
||||
user: User
|
||||
allRoles: any[]
|
||||
onUpdateRoles: (user: User, roles: UserRole[]) => void
|
||||
}
|
||||
|
||||
class UserRoleDropdown extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {allRoles} = this.props
|
||||
|
||||
return (
|
||||
<MultiSelectDropdown
|
||||
buttonSize="btn-xs"
|
||||
buttonColor="btn-primary"
|
||||
items={allRoles}
|
||||
label={this.rolesLabel}
|
||||
selectedItems={this.roles}
|
||||
customClass={this.rolesClass}
|
||||
onApply={this.handleUpdateRoles}
|
||||
resetStateOnReceiveProps={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private handleUpdateRoles = (roleNames): void => {
|
||||
const {user, allRoles, onUpdateRoles} = this.props
|
||||
const roles = allRoles.filter(r => roleNames.find(rn => rn.name === r.name))
|
||||
|
||||
onUpdateRoles(user, roles)
|
||||
}
|
||||
|
||||
private get roles(): UserRole[] {
|
||||
const roles = _.get(this.props.user, 'roles', []) as UserRole[]
|
||||
return roles.map(({name}) => ({name}))
|
||||
}
|
||||
|
||||
private get rolesClass(): string {
|
||||
return classnames(`dropdown-${USERS_TABLE.colRoles}`, {
|
||||
'admin-table--multi-select-empty': !this.roles.length,
|
||||
})
|
||||
}
|
||||
|
||||
private get rolesLabel(): string {
|
||||
return this.roles.length ? '' : 'Select Roles'
|
||||
}
|
||||
}
|
||||
|
||||
export default UserRoleDropdown
|
|
@ -1,169 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import UserEditName from 'src/admin/components/UserEditName'
|
||||
import UserNewPassword from 'src/admin/components/UserNewPassword'
|
||||
import MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
|
||||
import ConfirmOrCancel from 'shared/components/ConfirmOrCancel'
|
||||
import ConfirmButton from 'shared/components/ConfirmButton'
|
||||
import ChangePassRow from 'src/admin/components/ChangePassRow'
|
||||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
const UserRow = ({
|
||||
user: {name, roles = [], permissions, password},
|
||||
user,
|
||||
allRoles,
|
||||
allPermissions,
|
||||
hasRoles,
|
||||
isNew,
|
||||
isEditing,
|
||||
onEdit,
|
||||
onSave,
|
||||
onCancel,
|
||||
onDelete,
|
||||
onUpdatePermissions,
|
||||
onUpdateRoles,
|
||||
onUpdatePassword,
|
||||
}) => {
|
||||
function handleUpdatePermissions(perms) {
|
||||
const allowed = perms.map(p => p.name)
|
||||
onUpdatePermissions(user, [{scope: 'all', allowed}])
|
||||
}
|
||||
|
||||
function handleUpdateRoles(roleNames) {
|
||||
onUpdateRoles(
|
||||
user,
|
||||
allRoles.filter(r => roleNames.find(rn => rn.name === r.name))
|
||||
)
|
||||
}
|
||||
|
||||
function handleUpdatePassword() {
|
||||
onUpdatePassword(user, password)
|
||||
}
|
||||
|
||||
const perms = _.get(permissions, ['0', 'allowed'], [])
|
||||
|
||||
const wrappedDelete = () => {
|
||||
onDelete(user)
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<tr className="admin-table--edit-row">
|
||||
<UserEditName user={user} onEdit={onEdit} onSave={onSave} />
|
||||
<UserNewPassword
|
||||
user={user}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
isNew={isNew}
|
||||
/>
|
||||
{hasRoles ? <td className="admin-table--left-offset">--</td> : null}
|
||||
<td className="admin-table--left-offset">--</td>
|
||||
<td
|
||||
className="text-right"
|
||||
style={{width: `${USERS_TABLE.colDelete}px`}}
|
||||
>
|
||||
<ConfirmOrCancel
|
||||
item={user}
|
||||
onConfirm={onSave}
|
||||
onCancel={onCancel}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td style={{width: `${USERS_TABLE.colUsername}px`}}>{name}</td>
|
||||
<td style={{width: `${USERS_TABLE.colPassword}px`}}>
|
||||
<ChangePassRow
|
||||
onEdit={onEdit}
|
||||
onApply={handleUpdatePassword}
|
||||
user={user}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</td>
|
||||
{hasRoles ? (
|
||||
<td>
|
||||
<MultiSelectDropdown
|
||||
items={allRoles}
|
||||
selectedItems={roles.map(r => ({name: r.name}))}
|
||||
label={roles.length ? '' : 'Select Roles'}
|
||||
onApply={handleUpdateRoles}
|
||||
buttonSize="btn-xs"
|
||||
buttonColor="btn-primary"
|
||||
customClass={classnames(`dropdown-${USERS_TABLE.colRoles}`, {
|
||||
'admin-table--multi-select-empty': !roles.length,
|
||||
})}
|
||||
resetStateOnReceiveProps={false}
|
||||
/>
|
||||
</td>
|
||||
) : null}
|
||||
<td>
|
||||
{allPermissions && allPermissions.length ? (
|
||||
<MultiSelectDropdown
|
||||
items={allPermissions.map(p => ({name: p}))}
|
||||
selectedItems={perms.map(p => ({name: p}))}
|
||||
label={
|
||||
permissions && permissions.length ? '' : 'Select Permissions'
|
||||
}
|
||||
onApply={handleUpdatePermissions}
|
||||
buttonSize="btn-xs"
|
||||
buttonColor="btn-primary"
|
||||
customClass={classnames(`dropdown-${USERS_TABLE.colPermissions}`, {
|
||||
'admin-table--multi-select-empty': !permissions.length,
|
||||
})}
|
||||
resetStateOnReceiveProps={false}
|
||||
/>
|
||||
) : null}
|
||||
</td>
|
||||
<td className="text-right" style={{width: `${USERS_TABLE.colDelete}px`}}>
|
||||
<ConfirmButton
|
||||
size="btn-xs"
|
||||
type="btn-danger"
|
||||
text="Delete User"
|
||||
confirmAction={wrappedDelete}
|
||||
customClass="table--show-on-row-hover"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
UserRow.propTypes = {
|
||||
user: shape({
|
||||
name: string,
|
||||
roles: arrayOf(
|
||||
shape({
|
||||
name: string,
|
||||
})
|
||||
),
|
||||
permissions: arrayOf(
|
||||
shape({
|
||||
name: string,
|
||||
})
|
||||
),
|
||||
password: string,
|
||||
}).isRequired,
|
||||
allRoles: arrayOf(shape()),
|
||||
allPermissions: arrayOf(string),
|
||||
hasRoles: bool,
|
||||
isNew: bool,
|
||||
isEditing: bool,
|
||||
onCancel: func,
|
||||
onEdit: func,
|
||||
onSave: func,
|
||||
onDelete: func.isRequired,
|
||||
onUpdatePermissions: func,
|
||||
onUpdateRoles: func,
|
||||
onUpdatePassword: func,
|
||||
}
|
||||
|
||||
export default UserRow
|
|
@ -0,0 +1,120 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import UserPermissionsDropdown from 'src/admin/components/UserPermissionsDropdown'
|
||||
import UserRoleDropdown from 'src/admin/components/UserRoleDropdown'
|
||||
import ChangePassRow from 'src/admin/components/ChangePassRow'
|
||||
import ConfirmButton from 'src/shared/components/ConfirmButton'
|
||||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
import UserRowEdit from 'src/admin/components/UserRowEdit'
|
||||
import {User} from 'src/types/influxAdmin'
|
||||
|
||||
interface UserRowProps {
|
||||
user: User
|
||||
allRoles: any[]
|
||||
allPermissions: string[]
|
||||
hasRoles: boolean
|
||||
isNew: boolean
|
||||
isEditing: boolean
|
||||
onCancel: () => void
|
||||
onEdit: () => void
|
||||
onSave: () => void
|
||||
onDelete: (user: User) => void
|
||||
onUpdatePermissions: (user: User, permissions: any[]) => void
|
||||
onUpdateRoles: (user: User, roles: any[]) => void
|
||||
onUpdatePassword: (user: User, password: string) => void
|
||||
}
|
||||
|
||||
class UserRow extends PureComponent<UserRowProps> {
|
||||
public render() {
|
||||
const {
|
||||
user,
|
||||
allRoles,
|
||||
allPermissions,
|
||||
hasRoles,
|
||||
isNew,
|
||||
isEditing,
|
||||
onEdit,
|
||||
onSave,
|
||||
onCancel,
|
||||
onUpdatePermissions,
|
||||
onUpdateRoles,
|
||||
} = this.props
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<UserRowEdit
|
||||
user={user}
|
||||
isNew={isNew}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
onCancel={onCancel}
|
||||
hasRoles={hasRoles}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td style={{width: `${USERS_TABLE.colUsername}px`}}>{user.name}</td>
|
||||
<td style={{width: `${USERS_TABLE.colPassword}px`}}>
|
||||
<ChangePassRow
|
||||
user={user}
|
||||
onEdit={onEdit}
|
||||
buttonSize="btn-xs"
|
||||
onApply={this.handleUpdatePassword}
|
||||
/>
|
||||
</td>
|
||||
{hasRoles && (
|
||||
<td>
|
||||
<UserRoleDropdown
|
||||
user={user}
|
||||
allRoles={allRoles}
|
||||
onUpdateRoles={onUpdateRoles}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
<td>
|
||||
{this.hasPermissions && (
|
||||
<UserPermissionsDropdown
|
||||
user={user}
|
||||
allPermissions={allPermissions}
|
||||
onUpdatePermissions={onUpdatePermissions}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td
|
||||
className="text-right"
|
||||
style={{width: `${USERS_TABLE.colDelete}px`}}
|
||||
>
|
||||
<ConfirmButton
|
||||
size="btn-xs"
|
||||
type="btn-danger"
|
||||
text="Delete User"
|
||||
confirmAction={this.handleDelete}
|
||||
customClass="table--show-on-row-hover"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
private handleDelete = (): void => {
|
||||
const {user, onDelete} = this.props
|
||||
|
||||
onDelete(user)
|
||||
}
|
||||
|
||||
private handleUpdatePassword = (): void => {
|
||||
const {user, onUpdatePassword} = this.props
|
||||
|
||||
onUpdatePassword(user, user.password)
|
||||
}
|
||||
|
||||
private get hasPermissions() {
|
||||
const {allPermissions} = this.props
|
||||
return allPermissions && !!allPermissions.length
|
||||
}
|
||||
}
|
||||
|
||||
export default UserRow
|
|
@ -0,0 +1,47 @@
|
|||
import React, {SFC} from 'react'
|
||||
import UserEditName from 'src/admin/components/UserEditName'
|
||||
import UserNewPassword from 'src/admin/components/UserNewPassword'
|
||||
import ConfirmOrCancel from 'src/shared/components/ConfirmOrCancel'
|
||||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
import {User} from 'src/types/influxAdmin'
|
||||
|
||||
interface UserRowEditProps {
|
||||
user: User
|
||||
onEdit: () => void
|
||||
onSave: () => void
|
||||
onCancel: () => void
|
||||
isNew: boolean
|
||||
hasRoles: boolean
|
||||
}
|
||||
|
||||
const UserRowEdit: SFC<UserRowEditProps> = ({
|
||||
user,
|
||||
onEdit,
|
||||
onSave,
|
||||
onCancel,
|
||||
isNew,
|
||||
hasRoles,
|
||||
}) => (
|
||||
<tr className="admin-table--edit-row">
|
||||
<UserEditName user={user} onEdit={onEdit} onSave={onSave} />
|
||||
<UserNewPassword
|
||||
user={user}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
isNew={isNew}
|
||||
/>
|
||||
{hasRoles ? <td className="admin-table--left-offset">--</td> : null}
|
||||
<td className="admin-table--left-offset">--</td>
|
||||
<td className="text-right" style={{width: `${USERS_TABLE.colDelete}px`}}>
|
||||
<ConfirmOrCancel
|
||||
item={user}
|
||||
onConfirm={onSave}
|
||||
onCancel={onCancel}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
export default UserRowEdit
|
|
@ -57,3 +57,10 @@ export const updateTableOptions = tableOptions => ({
|
|||
tableOptions,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateLineColors = lineColors => ({
|
||||
type: 'UPDATE_LINE_COLORS',
|
||||
payload: {
|
||||
lineColors,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -7,6 +7,7 @@ import OptIn from 'shared/components/OptIn'
|
|||
import Input from 'src/dashboards/components/DisplayOptionsInput'
|
||||
import {Tabber, Tab} from 'src/dashboards/components/Tabber'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import LineGraphColorSelector from 'src/shared/components/LineGraphColorSelector'
|
||||
|
||||
import {
|
||||
AXES_SCALE_OPTIONS,
|
||||
|
@ -102,6 +103,7 @@ class AxesOptions extends Component {
|
|||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<LineGraphColorSelector />
|
||||
<div className="form-group col-sm-6">
|
||||
<label htmlFor="min">Min</label>
|
||||
<OptIn
|
||||
|
|
|
@ -24,7 +24,8 @@ import {
|
|||
import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames'
|
||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
||||
import {AUTO_GROUP_BY} from 'src/shared/constants'
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
|
||||
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
class CellEditorOverlay extends Component {
|
||||
constructor(props) {
|
||||
|
@ -107,7 +108,7 @@ class CellEditorOverlay extends Component {
|
|||
|
||||
handleSaveCell = () => {
|
||||
const {queriesWorkingDraft, staticLegend} = this.state
|
||||
const {cell, thresholdsListColors, gaugeColors} = this.props
|
||||
const {cell, thresholdsListColors, gaugeColors, lineColors} = this.props
|
||||
|
||||
const queries = queriesWorkingDraft.map(q => {
|
||||
const timeRange = q.range || {upper: null, lower: ':dashboardTime:'}
|
||||
|
@ -120,20 +121,12 @@ class CellEditorOverlay extends Component {
|
|||
}
|
||||
})
|
||||
|
||||
let colors = []
|
||||
|
||||
switch (cell.type) {
|
||||
case 'gauge': {
|
||||
colors = stringifyColorValues(gaugeColors)
|
||||
break
|
||||
}
|
||||
case 'single-stat':
|
||||
case 'line-plus-single-stat':
|
||||
case 'table': {
|
||||
colors = stringifyColorValues(thresholdsListColors)
|
||||
break
|
||||
}
|
||||
}
|
||||
const colors = getCellTypeColors({
|
||||
cellType: cell.type,
|
||||
gaugeColors,
|
||||
thresholdsListColors,
|
||||
lineColors,
|
||||
})
|
||||
|
||||
this.props.onSave({
|
||||
...cell,
|
||||
|
@ -390,8 +383,9 @@ CellEditorOverlay.propTypes = {
|
|||
dashboardID: string.isRequired,
|
||||
sources: arrayOf(shape()),
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
gaugeColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
thresholdsListColors: colorsNumberSchema.isRequired,
|
||||
gaugeColors: colorsNumberSchema.isRequired,
|
||||
lineColors: colorsStringSchema.isRequired,
|
||||
}
|
||||
|
||||
CEOBottom.propTypes = {
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
updateGaugeColors,
|
||||
updateAxes,
|
||||
} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
import {colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
class GaugeOptions extends Component {
|
||||
handleAddThreshold = () => {
|
||||
|
@ -219,18 +220,10 @@ class GaugeOptions extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, number, shape, string} = PropTypes
|
||||
const {func, shape} = PropTypes
|
||||
|
||||
GaugeOptions.propTypes = {
|
||||
gaugeColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
gaugeColors: colorsNumberSchema,
|
||||
handleUpdateGaugeColors: func.isRequired,
|
||||
handleUpdateAxes: func.isRequired,
|
||||
axes: shape({}).isRequired,
|
||||
|
|
|
@ -6,7 +6,8 @@ import RefreshingGraph from 'src/shared/components/RefreshingGraph'
|
|||
import buildQueries from 'utils/buildQueriesForGraphs'
|
||||
import VisualizationName from 'src/dashboards/components/VisualizationName'
|
||||
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
|
||||
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
const DashVisualization = (
|
||||
{
|
||||
|
@ -14,6 +15,7 @@ const DashVisualization = (
|
|||
type,
|
||||
templates,
|
||||
timeRange,
|
||||
lineColors,
|
||||
autoRefresh,
|
||||
gaugeColors,
|
||||
queryConfigs,
|
||||
|
@ -26,14 +28,19 @@ const DashVisualization = (
|
|||
},
|
||||
{source: {links: {proxy}}}
|
||||
) => {
|
||||
const colors = type === 'gauge' ? gaugeColors : thresholdsListColors
|
||||
const colors = getCellTypeColors({
|
||||
cellType: type,
|
||||
gaugeColors,
|
||||
thresholdsListColors,
|
||||
lineColors,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="graph">
|
||||
<VisualizationName />
|
||||
<div className="graph-container">
|
||||
<RefreshingGraph
|
||||
colors={stringifyColorValues(colors)}
|
||||
colors={colors}
|
||||
axes={axes}
|
||||
type={type}
|
||||
tableOptions={tableOptions}
|
||||
|
@ -69,24 +76,9 @@ DashVisualization.propTypes = {
|
|||
}),
|
||||
tableOptions: shape({}),
|
||||
resizerTopHeight: number,
|
||||
thresholdsListColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
gaugeColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
thresholdsListColors: colorsNumberSchema,
|
||||
gaugeColors: colorsNumberSchema,
|
||||
lineColors: colorsStringSchema,
|
||||
staticLegend: bool,
|
||||
setDataLabels: func,
|
||||
}
|
||||
|
@ -103,11 +95,13 @@ const mapStateToProps = ({
|
|||
cellEditorOverlay: {
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
lineColors,
|
||||
cell: {type, axes, tableOptions},
|
||||
},
|
||||
}) => ({
|
||||
gaugeColors,
|
||||
thresholdsListColors,
|
||||
lineColors,
|
||||
type,
|
||||
axes,
|
||||
tableOptions,
|
||||
|
|
|
@ -70,7 +70,7 @@ const TemplateVariableRow = ({
|
|||
<Dropdown
|
||||
items={TEMPLATE_TYPES}
|
||||
onChoose={onSelectType}
|
||||
onClick={onStartEdit}
|
||||
onClick={onStartEdit('tempVar')}
|
||||
selected={TEMPLATE_TYPES.find(t => t.type === selectedType).text}
|
||||
className="dropdown-140"
|
||||
/>
|
||||
|
@ -84,7 +84,7 @@ const TemplateVariableRow = ({
|
|||
selectedMeasurement={selectedMeasurement}
|
||||
selectedTagKey={selectedTagKey}
|
||||
onSelectTagKey={onSelectTagKey}
|
||||
onStartEdit={onStartEdit}
|
||||
onStartEdit={onStartEdit('tempVar')}
|
||||
onErrorThrown={onErrorThrown}
|
||||
/>
|
||||
<RowValues
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
|
||||
export const initializeOptions = cellType => {
|
||||
switch (cellType) {
|
||||
|
@ -18,3 +19,33 @@ export const AXES_SCALE_OPTIONS = {
|
|||
|
||||
export const TOOLTIP_Y_VALUE_FORMAT =
|
||||
'<p><strong>K/M/B</strong> = Thousand / Million / Billion<br/><strong>K/M/G</strong> = Kilo / Mega / Giga </p>'
|
||||
|
||||
export const getCellTypeColors = ({
|
||||
cellType,
|
||||
gaugeColors,
|
||||
thresholdsListColors,
|
||||
lineColors,
|
||||
}) => {
|
||||
let colors = []
|
||||
|
||||
switch (cellType) {
|
||||
case 'gauge': {
|
||||
colors = stringifyColorValues(gaugeColors)
|
||||
break
|
||||
}
|
||||
case 'single-stat':
|
||||
case 'line-plus-single-stat':
|
||||
case 'table': {
|
||||
colors = stringifyColorValues(thresholdsListColors)
|
||||
break
|
||||
}
|
||||
case 'bar':
|
||||
case 'line':
|
||||
case 'line-stacked':
|
||||
case 'line-stepplot': {
|
||||
colors = stringifyColorValues(lineColors)
|
||||
}
|
||||
}
|
||||
|
||||
return colors
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import {
|
|||
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
||||
import {notifyDashboardNotFound} from 'shared/copy/notifications'
|
||||
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
const FORMAT_INFLUXQL = 'influxql'
|
||||
const defaultTimeRange = {
|
||||
|
@ -283,6 +284,7 @@ class DashboardPage extends Component {
|
|||
showTemplateControlBar,
|
||||
dashboard,
|
||||
dashboards,
|
||||
lineColors,
|
||||
gaugeColors,
|
||||
autoRefresh,
|
||||
selectedCell,
|
||||
|
@ -399,6 +401,7 @@ class DashboardPage extends Component {
|
|||
thresholdsListType={thresholdsListType}
|
||||
thresholdsListColors={thresholdsListColors}
|
||||
gaugeColors={gaugeColors}
|
||||
lineColors={lineColors}
|
||||
/>
|
||||
) : null}
|
||||
<DashboardHeader
|
||||
|
@ -530,8 +533,9 @@ DashboardPage.propTypes = {
|
|||
handleDismissEditingAnnotation: func.isRequired,
|
||||
selectedCell: shape({}),
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
gaugeColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
thresholdsListColors: colorsNumberSchema.isRequired,
|
||||
gaugeColors: colorsNumberSchema.isRequired,
|
||||
lineColors: colorsStringSchema.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, {params: {dashboardID}}) => {
|
||||
|
@ -549,6 +553,7 @@ const mapStateToProps = (state, {params: {dashboardID}}) => {
|
|||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
lineColors,
|
||||
},
|
||||
} = state
|
||||
|
||||
|
@ -579,6 +584,7 @@ const mapStateToProps = (state, {params: {dashboardID}}) => {
|
|||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
lineColors,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -545,10 +545,9 @@ export const GRAPH_TYPES = [
|
|||
menuOption: 'Gauge',
|
||||
graphic: GRAPH_SVGS.gauge,
|
||||
},
|
||||
// FEATURE FLAG for Table-Graph
|
||||
// {
|
||||
// type: 'table',
|
||||
// menuOption: 'Table',
|
||||
// graphic: GRAPH_SVGS.table,
|
||||
// },
|
||||
{
|
||||
type: 'table',
|
||||
menuOption: 'Table',
|
||||
graphic: GRAPH_SVGS.table,
|
||||
},
|
||||
]
|
||||
|
|
|
@ -9,6 +9,11 @@ import {
|
|||
getThresholdsListType,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
import {
|
||||
DEFAULT_LINE_COLORS,
|
||||
validateLineColors,
|
||||
} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
import {initializeOptions} from 'src/dashboards/constants/cellEditor'
|
||||
|
||||
export const initialState = {
|
||||
|
@ -16,6 +21,7 @@ export const initialState = {
|
|||
thresholdsListType: THRESHOLD_TYPE_TEXT,
|
||||
thresholdsListColors: DEFAULT_THRESHOLDS_LIST_COLORS,
|
||||
gaugeColors: DEFAULT_GAUGE_COLORS,
|
||||
lineColors: DEFAULT_LINE_COLORS,
|
||||
}
|
||||
|
||||
export default function cellEditorOverlay(state = initialState, action) {
|
||||
|
@ -36,12 +42,15 @@ export default function cellEditorOverlay(state = initialState, action) {
|
|||
initializeOptions('table')
|
||||
)
|
||||
|
||||
const lineColors = validateLineColors(colors)
|
||||
|
||||
return {
|
||||
...state,
|
||||
cell: {...cell, tableOptions},
|
||||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
lineColors,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +110,12 @@ export default function cellEditorOverlay(state = initialState, action) {
|
|||
|
||||
return {...state, cell}
|
||||
}
|
||||
|
||||
case 'UPDATE_LINE_COLORS': {
|
||||
const {lineColors} = action.payload
|
||||
|
||||
return {...state, lineColors}
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
|
|
|
@ -55,8 +55,8 @@ const generateTemplateVariableQuery = ({
|
|||
|
||||
export const makeQueryForTemplate = ({influxql, db, measurement, tagKey}) =>
|
||||
influxql
|
||||
.replace(':database:', db)
|
||||
.replace(':measurement:', measurement)
|
||||
.replace(':tagKey:', tagKey)
|
||||
.replace(':database:', `"${db}"`)
|
||||
.replace(':measurement:', `"${measurement}"`)
|
||||
.replace(':tagKey:', `"${tagKey}"`)
|
||||
|
||||
export default generateTemplateVariableQuery
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
|||
|
||||
import Table from './Table'
|
||||
import RefreshingGraph from 'shared/components/RefreshingGraph'
|
||||
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
const VisView = ({
|
||||
axes,
|
||||
|
@ -37,6 +38,7 @@ const VisView = ({
|
|||
|
||||
return (
|
||||
<RefreshingGraph
|
||||
colors={DEFAULT_LINE_COLORS}
|
||||
axes={axes}
|
||||
type={cellType}
|
||||
queries={queries}
|
||||
|
|
|
@ -8,6 +8,8 @@ import underlayCallback from 'src/kapacitor/helpers/ruleGraphUnderlay'
|
|||
|
||||
const RefreshingLineGraph = AutoRefresh(LineGraph)
|
||||
|
||||
import {LINE_COLORS_RULE_GRAPH} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
const {shape, string, func} = PropTypes
|
||||
const RuleGraph = ({
|
||||
query,
|
||||
|
@ -20,7 +22,6 @@ const RuleGraph = ({
|
|||
const autoRefreshMs = 30000
|
||||
const queryText = buildInfluxQLQuery({lower}, query)
|
||||
const queries = [{host: source.links.proxy, text: queryText}]
|
||||
const kapacitorLineColors = ['#4ED8A0']
|
||||
|
||||
if (!queryText) {
|
||||
return (
|
||||
|
@ -47,7 +48,7 @@ const RuleGraph = ({
|
|||
isGraphFilled={false}
|
||||
ruleValues={rule.values}
|
||||
autoRefresh={autoRefreshMs}
|
||||
overrideLineColors={kapacitorLineColors}
|
||||
colors={LINE_COLORS_RULE_GRAPH}
|
||||
underlayCallback={underlayCallback(rule)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -19,7 +19,7 @@ const TickscriptEditorControls = ({
|
|||
{isNewTickscript ? (
|
||||
<TickscriptID onChangeID={onChangeID} id={task.id} />
|
||||
) : (
|
||||
<TickscriptStaticID id={task.name} />
|
||||
<TickscriptStaticID id={task.id} />
|
||||
)}
|
||||
<div className="tickscript-controls--right">
|
||||
<TickscriptType type={task.type} onChangeType={onChangeType} />
|
||||
|
|
|
@ -173,6 +173,7 @@ class TickscriptPage extends Component {
|
|||
response = await updateTask(kapacitor, task, ruleID, router, sourceID)
|
||||
} else {
|
||||
response = await createTask(kapacitor, task, router, sourceID)
|
||||
router.push(`/sources/${sourceID}/tickscript/${response.id}`)
|
||||
}
|
||||
if (response.code) {
|
||||
this.setState({unsavedChanges: true, consoleMessage: response.message})
|
||||
|
@ -227,6 +228,7 @@ class TickscriptPage extends Component {
|
|||
unsavedChanges,
|
||||
consoleMessage,
|
||||
} = this.state
|
||||
|
||||
return (
|
||||
<Tickscript
|
||||
task={task}
|
||||
|
|
|
@ -99,7 +99,7 @@ ColorDropdown.propTypes = {
|
|||
shape({
|
||||
hex: string.isRequired,
|
||||
name: string.isRequired,
|
||||
})
|
||||
}).isRequired
|
||||
).isRequired,
|
||||
stretchToFit: bool,
|
||||
disabled: bool,
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import uuid from 'uuid'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
import {LINE_COLOR_SCALES} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
class ColorScaleDropdown extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
expanded: false,
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleMenu = () => {
|
||||
const {disabled} = this.props
|
||||
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
this.setState({expanded: !this.state.expanded})
|
||||
}
|
||||
|
||||
handleClickOutside = () => {
|
||||
this.setState({expanded: false})
|
||||
}
|
||||
|
||||
handleDropdownClick = colorScale => () => {
|
||||
this.props.onChoose(colorScale)
|
||||
this.setState({expanded: false})
|
||||
}
|
||||
|
||||
generateGradientStyle = colors => ({
|
||||
background: `linear-gradient(to right, ${colors[0].hex} 0%,${
|
||||
colors[1].hex
|
||||
} 50%,${colors[2].hex} 100%)`,
|
||||
})
|
||||
|
||||
render() {
|
||||
const {expanded} = this.state
|
||||
const {selected, disabled, stretchToFit} = this.props
|
||||
|
||||
const dropdownClassNames = classnames('color-dropdown', {
|
||||
open: expanded,
|
||||
'color-dropdown--stretch': stretchToFit,
|
||||
})
|
||||
const toggleClassNames = classnames(
|
||||
'btn btn-sm btn-default color-dropdown--toggle',
|
||||
{active: expanded, 'color-dropdown__disabled': disabled}
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={dropdownClassNames}>
|
||||
<div
|
||||
className={toggleClassNames}
|
||||
onClick={this.handleToggleMenu}
|
||||
disabled={disabled}
|
||||
>
|
||||
<div
|
||||
className="color-dropdown--swatches"
|
||||
style={this.generateGradientStyle(selected)}
|
||||
/>
|
||||
<div className="color-dropdown--name">{selected[0].name}</div>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
{expanded ? (
|
||||
<div className="color-dropdown--menu">
|
||||
<FancyScrollbar autoHide={false} autoHeight={true}>
|
||||
{LINE_COLOR_SCALES.map(colorScale => (
|
||||
<div
|
||||
className={
|
||||
colorScale.name === selected[0].name
|
||||
? 'color-dropdown--item active'
|
||||
: 'color-dropdown--item'
|
||||
}
|
||||
key={uuid.v4()}
|
||||
onClick={this.handleDropdownClick(colorScale)}
|
||||
>
|
||||
<div
|
||||
className="color-dropdown--swatches"
|
||||
style={this.generateGradientStyle(colorScale.colors)}
|
||||
/>
|
||||
<span className="color-dropdown--name">
|
||||
{colorScale.name}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
ColorScaleDropdown.propTypes = {
|
||||
selected: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
}).isRequired
|
||||
).isRequired,
|
||||
onChoose: func.isRequired,
|
||||
stretchToFit: bool,
|
||||
disabled: bool,
|
||||
}
|
||||
|
||||
export default OnClickOutside(ColorScaleDropdown)
|
|
@ -23,11 +23,17 @@ import {
|
|||
LABEL_WIDTH,
|
||||
CHAR_PIXELS,
|
||||
barPlotter,
|
||||
hasherino,
|
||||
highlightSeriesOpts,
|
||||
} from 'src/shared/graphs/helpers'
|
||||
|
||||
import {
|
||||
DEFAULT_LINE_COLORS,
|
||||
getLineColorsHexes,
|
||||
} from 'src/shared/constants/graphColorPalettes'
|
||||
const {LINEAR, LOG, BASE_10, BASE_2} = AXES_SCALE_OPTIONS
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class Dygraph extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
@ -53,7 +59,7 @@ class Dygraph extends Component {
|
|||
fillGraph,
|
||||
logscale: y.scale === LOG,
|
||||
colors: this.getLineColors(),
|
||||
series: this.hashColorDygraphSeries(),
|
||||
series: this.colorDygraphSeries(),
|
||||
plugins: [new Dygraphs.Plugins.Crosshair({direction: 'vertical'})],
|
||||
axes: {
|
||||
y: {
|
||||
|
@ -149,7 +155,7 @@ class Dygraph extends Component {
|
|||
},
|
||||
},
|
||||
colors: this.getLineColors(),
|
||||
series: this.hashColorDygraphSeries(),
|
||||
series: this.colorDygraphSeries(),
|
||||
plotter: isBarGraph ? barPlotter : null,
|
||||
drawCallback: this.annotationsRef.heartbeat,
|
||||
}
|
||||
|
@ -189,6 +195,33 @@ class Dygraph extends Component {
|
|||
onZoom(this.formatTimeRange(lower), this.formatTimeRange(upper))
|
||||
}
|
||||
|
||||
colorDygraphSeries = () => {
|
||||
const {dygraphSeries, children, colors, overrideLineColors} = this.props
|
||||
const numSeries = Object.keys(dygraphSeries).length
|
||||
|
||||
let lineColors = getLineColorsHexes(colors, numSeries)
|
||||
|
||||
if (React.children && React.children.count(children)) {
|
||||
// If graph is line-plus-single-stat then reserve colors for single stat
|
||||
lineColors = getLineColorsHexes(DEFAULT_LINE_COLORS, numSeries)
|
||||
}
|
||||
|
||||
if (overrideLineColors) {
|
||||
lineColors = getLineColorsHexes(overrideLineColors, numSeries)
|
||||
}
|
||||
|
||||
const coloredDygraphSeries = {}
|
||||
|
||||
for (const seriesName in dygraphSeries) {
|
||||
const series = dygraphSeries[seriesName]
|
||||
const color = lineColors[Object.keys(dygraphSeries).indexOf(seriesName)]
|
||||
|
||||
coloredDygraphSeries[seriesName] = {...series, color}
|
||||
}
|
||||
|
||||
return coloredDygraphSeries
|
||||
}
|
||||
|
||||
eventToTimestamp = ({pageX: pxBetweenMouseAndPage}) => {
|
||||
const {left: pxBetweenGraphAndPage} = this.graphRef.getBoundingClientRect()
|
||||
const graphXCoordinate = pxBetweenMouseAndPage - pxBetweenGraphAndPage
|
||||
|
@ -213,21 +246,6 @@ class Dygraph extends Component {
|
|||
this.setState({isHoveringThisGraph: false})
|
||||
}
|
||||
|
||||
hashColorDygraphSeries = () => {
|
||||
const {dygraphSeries} = this.props
|
||||
const colors = this.getLineColors()
|
||||
const hashColorDygraphSeries = {}
|
||||
|
||||
for (const seriesName in dygraphSeries) {
|
||||
const series = dygraphSeries[seriesName]
|
||||
const hashIndex = hasherino(seriesName, colors.length)
|
||||
const color = colors[hashIndex]
|
||||
hashColorDygraphSeries[seriesName] = {...series, color}
|
||||
}
|
||||
|
||||
return hashColorDygraphSeries
|
||||
}
|
||||
|
||||
handleHideLegend = e => {
|
||||
const {top, bottom, left, right} = this.graphRef.getBoundingClientRect()
|
||||
|
||||
|
@ -363,7 +381,7 @@ class Dygraph extends Component {
|
|||
/>
|
||||
{staticLegend && (
|
||||
<StaticLegend
|
||||
dygraphSeries={this.hashColorDygraphSeries()}
|
||||
dygraphSeries={this.colorDygraphSeries()}
|
||||
dygraph={this.dygraph}
|
||||
handleReceiveStaticLegendHeight={
|
||||
this.handleReceiveStaticLegendHeight
|
||||
|
@ -420,7 +438,12 @@ Dygraph.propTypes = {
|
|||
isGraphFilled: bool,
|
||||
isBarGraph: bool,
|
||||
staticLegend: bool,
|
||||
overrideLineColors: array,
|
||||
overrideLineColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
dygraphSeries: shape({}).isRequired,
|
||||
ruleValues: shape({
|
||||
operator: string,
|
||||
|
@ -437,6 +460,7 @@ Dygraph.propTypes = {
|
|||
onZoom: func,
|
||||
mode: string,
|
||||
children: node,
|
||||
colors: colorsStringSchema.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({annotations: {mode}}) => ({
|
||||
|
|
|
@ -10,6 +10,8 @@ import {
|
|||
MIN_THRESHOLDS,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class Gauge extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
@ -325,21 +327,13 @@ class Gauge extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {arrayOf, number, shape, string} = PropTypes
|
||||
const {number, string} = PropTypes
|
||||
|
||||
Gauge.propTypes = {
|
||||
width: string.isRequired,
|
||||
height: string.isRequired,
|
||||
gaugePosition: number.isRequired,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
).isRequired,
|
||||
colors: colorsStringSchema.isRequired,
|
||||
prefix: string.isRequired,
|
||||
suffix: string.isRequired,
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import {DEFAULT_GAUGE_COLORS} from 'src/shared/constants/thresholds'
|
|||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'src/shared/constants'
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class GaugeChart extends PureComponent {
|
||||
render() {
|
||||
const {
|
||||
|
@ -82,15 +84,7 @@ GaugeChart.propTypes = {
|
|||
cellHeight: number,
|
||||
resizerTopHeight: number,
|
||||
resizeCoords: shape(),
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
prefix: string.isRequired,
|
||||
suffix: string.isRequired,
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import {IS_STATIC_LEGEND} from 'src/shared/constants'
|
|||
|
||||
import _ from 'lodash'
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
const getSource = (cell, source, sources, defaultSource) => {
|
||||
const s = _.get(cell, ['queries', '0', 'source'], null)
|
||||
if (!s) {
|
||||
|
@ -134,15 +136,7 @@ const propTypes = {
|
|||
i: string.isRequired,
|
||||
name: string.isRequired,
|
||||
type: string.isRequired,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
}).isRequired,
|
||||
templates: arrayOf(shape()),
|
||||
host: string,
|
||||
|
|
|
@ -6,7 +6,7 @@ import shallowCompare from 'react-addons-shallow-compare'
|
|||
import SingleStat from 'src/shared/components/SingleStat'
|
||||
import timeSeriesToDygraph from 'utils/timeSeriesTransformers'
|
||||
|
||||
import {SINGLE_STAT_LINE_COLORS} from 'src/shared/graphs/helpers'
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class LineGraph extends Component {
|
||||
constructor(props) {
|
||||
|
@ -85,10 +85,6 @@ class LineGraph extends Component {
|
|||
connectSeparatedPoints: true,
|
||||
}
|
||||
|
||||
const lineColors = showSingleStat
|
||||
? SINGLE_STAT_LINE_COLORS
|
||||
: overrideLineColors
|
||||
|
||||
const containerStyle = {
|
||||
width: 'calc(100% - 32px)',
|
||||
height: 'calc(100% - 16px)',
|
||||
|
@ -118,7 +114,8 @@ class LineGraph extends Component {
|
|||
resizeCoords={resizeCoords}
|
||||
dygraphSeries={dygraphSeries}
|
||||
setResolution={setResolution}
|
||||
overrideLineColors={lineColors}
|
||||
colors={colors}
|
||||
overrideLineColors={overrideLineColors}
|
||||
containerStyle={containerStyle}
|
||||
staticLegend={staticLegend}
|
||||
isGraphFilled={showSingleStat ? false : isGraphFilled}
|
||||
|
@ -201,15 +198,7 @@ LineGraph.propTypes = {
|
|||
resizeCoords: shape(),
|
||||
queries: arrayOf(shape({}).isRequired).isRequired,
|
||||
data: arrayOf(shape({}).isRequired).isRequired,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
}
|
||||
|
||||
export default LineGraph
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import ColorScaleDropdown from 'shared/components/ColorScaleDropdown'
|
||||
|
||||
import {updateLineColors} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class LineGraphColorSelector extends Component {
|
||||
handleSelectColors = colorScale => {
|
||||
const {handleUpdateLineColors} = this.props
|
||||
const {colors} = colorScale
|
||||
|
||||
handleUpdateLineColors(colors)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {lineColors} = this.props
|
||||
|
||||
return (
|
||||
<div className="form-group col-xs-12">
|
||||
<label>Line Colors</label>
|
||||
<ColorScaleDropdown
|
||||
onChoose={this.handleSelectColors}
|
||||
stretchToFit={true}
|
||||
selected={lineColors}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func} = PropTypes
|
||||
|
||||
LineGraphColorSelector.propTypes = {
|
||||
lineColors: colorsStringSchema.isRequired,
|
||||
handleUpdateLineColors: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({cellEditorOverlay: {lineColors}}) => ({
|
||||
lineColors,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleUpdateLineColors: bindActionCreators(updateLineColors, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||
LineGraphColorSelector
|
||||
)
|
|
@ -9,6 +9,8 @@ import SingleStat from 'shared/components/SingleStat'
|
|||
import GaugeChart from 'shared/components/GaugeChart'
|
||||
import TableGraph from 'shared/components/TableGraph'
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
const RefreshingLineGraph = AutoRefresh(LineGraph)
|
||||
const RefreshingSingleStat = AutoRefresh(SingleStat)
|
||||
const RefreshingGaugeChart = AutoRefresh(GaugeChart)
|
||||
|
@ -154,15 +156,7 @@ RefreshingGraph.propTypes = {
|
|||
onZoom: func,
|
||||
resizeCoords: shape(),
|
||||
grabDataForDownload: func,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
cellID: string,
|
||||
inView: bool,
|
||||
tableOptions: shape({}),
|
||||
|
|
|
@ -6,6 +6,7 @@ import lastValues from 'shared/parsing/lastValues'
|
|||
import {SMALL_CELL_HEIGHT} from 'shared/graphs/helpers'
|
||||
import {DYGRAPH_CONTAINER_V_MARGIN} from 'shared/constants'
|
||||
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class SingleStat extends PureComponent {
|
||||
render() {
|
||||
|
@ -78,15 +79,7 @@ SingleStat.propTypes = {
|
|||
data: arrayOf(shape()).isRequired,
|
||||
isFetchingInitially: bool,
|
||||
cellHeight: number,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
prefix: string,
|
||||
suffix: string,
|
||||
lineGraph: bool,
|
||||
|
|
|
@ -86,10 +86,6 @@ class StaticLegend extends Component {
|
|||
key={uuid.v4()}
|
||||
onMouseDown={this.handleClick(i)}
|
||||
>
|
||||
<div
|
||||
className="static-legend--dot"
|
||||
style={{backgroundColor: colors[i]}}
|
||||
/>
|
||||
<span style={{color: colors[i]}}>{removeMeasurement(v)}</span>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from 'src/shared/constants/tableGraph'
|
||||
|
||||
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class TableGraph extends Component {
|
||||
constructor(props) {
|
||||
|
@ -432,15 +433,7 @@ TableGraph.propTypes = {
|
|||
}),
|
||||
hoverTime: string,
|
||||
onSetHoverTime: func,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
setDataLabels: func,
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import Threshold from 'src/dashboards/components/Threshold'
|
|||
import ColorDropdown from 'shared/components/ColorDropdown'
|
||||
|
||||
import {updateThresholdsListColors} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
import {colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
import {
|
||||
THRESHOLD_COLORS,
|
||||
|
@ -166,22 +167,14 @@ class ThresholdsList extends Component {
|
|||
)
|
||||
}
|
||||
}
|
||||
const {arrayOf, bool, func, number, shape, string} = PropTypes
|
||||
const {bool, func, string} = PropTypes
|
||||
|
||||
ThresholdsList.defaultProps = {
|
||||
showListHeading: false,
|
||||
}
|
||||
ThresholdsList.propTypes = {
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
thresholdsListColors: colorsNumberSchema.isRequired,
|
||||
handleUpdateThresholdsListColors: func.isRequired,
|
||||
onResetFocus: func.isRequired,
|
||||
showListHeading: bool,
|
||||
|
|
|
@ -1,36 +1,21 @@
|
|||
import _ from 'lodash'
|
||||
import chroma from 'chroma-js'
|
||||
|
||||
import {
|
||||
THRESHOLD_COLORS,
|
||||
THRESHOLD_TYPE_BASE,
|
||||
THRESHOLD_TYPE_TEXT,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
const hexToRgb = hex => {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16),
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
const averageRgbValues = valuesObject => {
|
||||
const {r, g, b} = valuesObject
|
||||
return (r + g + b) / 3
|
||||
}
|
||||
|
||||
const trueNeutralGrey = 128
|
||||
const luminanceThreshold = 0.5
|
||||
|
||||
const getLegibleTextColor = bgColorHex => {
|
||||
const averageBackground = averageRgbValues(hexToRgb(bgColorHex))
|
||||
const isBackgroundLight = averageBackground > trueNeutralGrey
|
||||
|
||||
const darkText = '#292933'
|
||||
const lightText = '#ffffff'
|
||||
|
||||
return isBackgroundLight ? darkText : lightText
|
||||
return chroma(bgColorHex).luminance() < luminanceThreshold
|
||||
? darkText
|
||||
: lightText
|
||||
}
|
||||
|
||||
const findNearestCrossedThreshold = (colors, lastValue) => {
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
import chroma from 'chroma-js'
|
||||
|
||||
const COLOR_TYPE_SCALE = 'scale'
|
||||
|
||||
// Color Palettes
|
||||
export const LINE_COLORS_A = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#31C0F6',
|
||||
id: '0',
|
||||
name: 'Nineteen Eighty Four',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#A500A5',
|
||||
id: '0',
|
||||
name: 'Nineteen Eighty Four',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#FF7E27',
|
||||
id: '0',
|
||||
name: 'Nineteen Eighty Four',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_B = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#74D495',
|
||||
id: '1',
|
||||
name: 'Atlantis',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#3F3FBA',
|
||||
id: '1',
|
||||
name: 'Atlantis',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#FF4D9E',
|
||||
id: '1',
|
||||
name: 'Atlantis',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_C = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#8F8AF4',
|
||||
id: '1',
|
||||
name: 'Do Androids Dream of Electric Sheep?',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#A51414',
|
||||
id: '1',
|
||||
name: 'Do Androids Dream of Electric Sheep?',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#F4CF31',
|
||||
id: '1',
|
||||
name: 'Do Androids Dream of Electric Sheep?',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_D = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#FD7A5D',
|
||||
id: '1',
|
||||
name: 'Delorean',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#5F1CF2',
|
||||
id: '1',
|
||||
name: 'Delorean',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#4CE09A',
|
||||
id: '1',
|
||||
name: 'Delorean',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_E = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#FDC44F',
|
||||
id: '1',
|
||||
name: 'Cthulhu',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#007C76',
|
||||
id: '1',
|
||||
name: 'Cthulhu',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#8983FF',
|
||||
id: '1',
|
||||
name: 'Cthulhu',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_F = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#DA6FF1',
|
||||
id: '1',
|
||||
name: 'Ectoplasm',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#00717A',
|
||||
id: '1',
|
||||
name: 'Ectoplasm',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#ACFF76',
|
||||
id: '1',
|
||||
name: 'Ectoplasm',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_G = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#F6F6F8',
|
||||
id: '1',
|
||||
name: 'T-Max 400 Film',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#A4A8B6',
|
||||
id: '1',
|
||||
name: 'T-Max 400 Film',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#545667',
|
||||
id: '1',
|
||||
name: 'T-Max 400 Film',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_RULE_GRAPH = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#4ED8A0',
|
||||
id: '1',
|
||||
name: 'Rainforest',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const DEFAULT_LINE_COLORS = LINE_COLORS_A
|
||||
|
||||
export const LINE_COLOR_SCALES = [
|
||||
LINE_COLORS_A,
|
||||
LINE_COLORS_B,
|
||||
LINE_COLORS_C,
|
||||
LINE_COLORS_D,
|
||||
LINE_COLORS_E,
|
||||
LINE_COLORS_F,
|
||||
LINE_COLORS_G,
|
||||
].map(colorScale => {
|
||||
const name = colorScale[0].name
|
||||
const colors = colorScale
|
||||
const id = colorScale[0].id
|
||||
|
||||
return {name, colors, id}
|
||||
})
|
||||
|
||||
export const validateLineColors = colors => {
|
||||
if (!colors || colors.length === 0) {
|
||||
return DEFAULT_LINE_COLORS
|
||||
}
|
||||
|
||||
const testColorsTypes =
|
||||
colors.filter(color => color.type === COLOR_TYPE_SCALE).length ===
|
||||
colors.length
|
||||
|
||||
return testColorsTypes ? colors : DEFAULT_LINE_COLORS
|
||||
}
|
||||
|
||||
export const getLineColorsHexes = (colors, numSeries) => {
|
||||
const validatedColors = validateLineColors(colors) // ensures safe defaults
|
||||
const colorsHexArray = validatedColors.map(color => color.hex)
|
||||
|
||||
if (numSeries === 1 || numSeries === 0) {
|
||||
return [colorsHexArray[0]]
|
||||
}
|
||||
if (numSeries === 2) {
|
||||
return [colorsHexArray[0], colorsHexArray[1]]
|
||||
}
|
||||
return chroma
|
||||
.scale(colorsHexArray)
|
||||
.mode('lch')
|
||||
.colors(numSeries)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import PropTypes from 'prop-types'
|
||||
|
||||
const {shape, string} = PropTypes
|
||||
const {arrayOf, number, shape, string} = PropTypes
|
||||
|
||||
export const annotation = shape({
|
||||
id: string.isRequired,
|
||||
|
@ -9,3 +9,23 @@ export const annotation = shape({
|
|||
text: string.isRequired,
|
||||
type: string.isRequired,
|
||||
})
|
||||
|
||||
export const colorsStringSchema = arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
)
|
||||
|
||||
export const colorsNumberSchema = arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
export const fixtureStatusPageCells = [
|
||||
{
|
||||
i: 'alerts-bar-graph',
|
||||
|
@ -8,6 +10,7 @@ export const fixtureStatusPageCells = [
|
|||
h: 4,
|
||||
legend: {},
|
||||
name: 'Alert Events per Day – Last 30 Days',
|
||||
colors: DEFAULT_LINE_COLORS,
|
||||
queries: [
|
||||
{
|
||||
query:
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
*/
|
||||
|
||||
$color-dropdown--circle: 14px;
|
||||
$color-dropdown--bar: 104px;
|
||||
$color-dropdown--bar-height: 10px;
|
||||
$color-dropdown--left-padding: 11px;
|
||||
$color-dropdown--name-padding: 20px;
|
||||
|
||||
.color-dropdown {
|
||||
width: 140px;
|
||||
|
@ -31,11 +35,11 @@ $color-dropdown--circle: 14px;
|
|||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
z-index: 5;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.7);
|
||||
@include gradient-h($g0-obsidian,$g2-kevlar);
|
||||
@include gradient-h($g0-obsidian, $g2-kevlar);
|
||||
}
|
||||
.color-dropdown--item {
|
||||
@include no-user-select();
|
||||
|
@ -43,9 +47,7 @@ $color-dropdown--circle: 14px;
|
|||
height: 28px;
|
||||
position: relative;
|
||||
color: $g11-sidewalk;
|
||||
transition:
|
||||
color 0.25s ease,
|
||||
background-color 0.25s ease;
|
||||
transition: color 0.25s ease, background-color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: $g4-onyx;
|
||||
|
@ -67,6 +69,7 @@ $color-dropdown--circle: 14px;
|
|||
}
|
||||
}
|
||||
.color-dropdown--swatch,
|
||||
.color-dropdown--swatches,
|
||||
.color-dropdown--name {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
@ -76,21 +79,35 @@ $color-dropdown--circle: 14px;
|
|||
width: $color-dropdown--circle;
|
||||
height: $color-dropdown--circle;
|
||||
border-radius: 50%;
|
||||
left: 11px;
|
||||
left: $color-dropdown--left-padding;
|
||||
}
|
||||
.color-dropdown--swatches {
|
||||
width: $color-dropdown--bar;
|
||||
height: $color-dropdown--bar-height;
|
||||
border-radius: $color-dropdown--bar-height / 2;
|
||||
left: $color-dropdown--left-padding;
|
||||
}
|
||||
.color-dropdown--name {
|
||||
text-align: left;
|
||||
right: 11px;
|
||||
left: 34px;
|
||||
right: $color-dropdown--name-padding;
|
||||
left: $color-dropdown--circle + $color-dropdown--name-padding;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-transform: capitalize;
|
||||
|
||||
.color-dropdown--swatches + & {
|
||||
left: $color-dropdown--bar + $color-dropdown--name-padding;
|
||||
}
|
||||
}
|
||||
.color-dropdown .color-dropdown--menu .fancy-scroll--container .fancy-scroll--track-v .fancy-scroll--thumb-v {
|
||||
@include gradient-v($g9-mountain,$g7-graphite);
|
||||
.color-dropdown
|
||||
.color-dropdown--menu
|
||||
.fancy-scroll--container
|
||||
.fancy-scroll--track-v
|
||||
.fancy-scroll--thumb-v {
|
||||
@include gradient-v($g9-mountain, $g7-graphite);
|
||||
}
|
||||
.color-dropdown--toggle.color-dropdown__disabled {
|
||||
color: $g7-graphite;
|
||||
|
|
|
@ -16,17 +16,9 @@
|
|||
flex-wrap: wrap;
|
||||
max-height: 50%;
|
||||
overflow: auto;
|
||||
@include custom-scrollbar($g3-castle,$g6-smoke);
|
||||
}
|
||||
.static-legend--dot {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 4px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: $g20-white;
|
||||
@include custom-scrollbar($g3-castle, $g6-smoke);
|
||||
}
|
||||
|
||||
.static-legend--item,
|
||||
.static-legend--single {
|
||||
height: 22px;
|
||||
|
@ -43,8 +35,7 @@
|
|||
.static-legend--item {
|
||||
transition: background-color 0.25s ease, color 0.25s ease;
|
||||
|
||||
span,
|
||||
.static-legend--dot {
|
||||
span {
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
@ -53,8 +44,7 @@
|
|||
cursor: pointer;
|
||||
background-color: $g6-smoke;
|
||||
|
||||
span,
|
||||
.static-legend--dot {
|
||||
span {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
@ -62,16 +52,14 @@
|
|||
background-color: $g1-raven;
|
||||
font-style: italic;
|
||||
|
||||
span,
|
||||
.static-legend--dot {
|
||||
span {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $g2-kevlar;
|
||||
|
||||
span,
|
||||
.static-legend--dot {
|
||||
span {
|
||||
opacity: 0.65;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
.tab {
|
||||
font-weight: 500 !important;
|
||||
border-radius: $radius $radius 0 0 !important;
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
color 0.25s ease !important;
|
||||
transition: background-color 0.25s ease, color 0.25s ease !important;
|
||||
border: 0 !important;
|
||||
text-align: left;
|
||||
height: 60px !important;
|
||||
|
@ -41,16 +39,24 @@
|
|||
}
|
||||
}
|
||||
.admin-tabs--content {
|
||||
.panel {border-top-left-radius: 0;}
|
||||
.panel-heading {height: 60px;}
|
||||
.panel {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
.panel-heading {
|
||||
height: 60px;
|
||||
}
|
||||
.panel-title {
|
||||
font-size: 17px;
|
||||
font-weight: 400 !important;
|
||||
color: $g12-forge;
|
||||
padding: 6px 0;
|
||||
}
|
||||
.panel-body {min-height: 300px;}
|
||||
.panel-heading + .panel-body {padding-top: 0;}
|
||||
.panel-body {
|
||||
min-height: 300px;
|
||||
}
|
||||
.panel-heading + .panel-body {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -69,7 +75,9 @@
|
|||
border-radius: $radius 0 0 $radius !important;
|
||||
padding: 0 0 0 16px !important;
|
||||
}
|
||||
& + div {padding-left: 0;}
|
||||
& + div {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,8 +90,13 @@
|
|||
font-weight: 600;
|
||||
color: $g14-chromium;
|
||||
transition: none !important;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.caret {opacity: 0;}
|
||||
.caret {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.admin-table--multi-select-empty .dropdown-toggle {
|
||||
color: $g8-storm;
|
||||
|
@ -92,7 +105,9 @@
|
|||
color: $g20-white !important;
|
||||
background-color: $c-pool;
|
||||
|
||||
.caret {opacity: 1;}
|
||||
.caret {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transition: background-color 0.25s ease;
|
||||
|
@ -100,7 +115,9 @@
|
|||
}
|
||||
}
|
||||
table > tbody > tr > td.admin-table--left-offset,
|
||||
table > thead > tr > th.admin-table--left-offset {padding-left: 15px;}
|
||||
table > thead > tr > th.admin-table--left-offset {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
table > tbody > tr.admin-table--edit-row,
|
||||
table > tbody > tr.admin-table--edit-row:hover,
|
||||
|
@ -141,9 +158,15 @@ pre.admin-table--query {
|
|||
.db-manager {
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:last-child {margin-bottom: 0;}
|
||||
.db-manager-header--actions {visibility: hidden;}
|
||||
&:hover .db-manager-header--actions {visibility: visible;}
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.db-manager-header--actions {
|
||||
visibility: hidden;
|
||||
}
|
||||
&:hover .db-manager-header--actions {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
.db-manager-header {
|
||||
padding: 0 11px;
|
||||
|
@ -182,7 +205,9 @@ pre.admin-table--query {
|
|||
padding: 9px 11px;
|
||||
border-radius: 0 0 $radius-small $radius-small;
|
||||
|
||||
.table-highlight > tbody > tr:hover {background-color: $g5-pepper;}
|
||||
.table-highlight > tbody > tr:hover {
|
||||
background-color: $g5-pepper;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
export interface UserRole {
|
||||
name: string
|
||||
}
|
||||
|
||||
interface UserPermission {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface User {
|
||||
name: string
|
||||
roles: UserRole[]
|
||||
permissions: UserPermission[]
|
||||
password: string
|
||||
}
|
|
@ -8,6 +8,7 @@ import {
|
|||
updateThresholdsListColors,
|
||||
updateThresholdsListType,
|
||||
updateGaugeColors,
|
||||
updateLineColors,
|
||||
updateAxes,
|
||||
} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
validateThresholdsListColors,
|
||||
getThresholdsListType,
|
||||
} from 'shared/constants/thresholds'
|
||||
import {validateLineColors} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
const defaultCellType = 'line'
|
||||
const defaultCellName = 'defaultCell'
|
||||
|
@ -45,6 +47,7 @@ const defaultThresholdsListColors = validateThresholdsListColors(
|
|||
defaultThresholdsListType
|
||||
)
|
||||
const defaultGaugeColors = validateGaugeColors(defaultCell.colors)
|
||||
const defaultLineColors = validateLineColors(defaultCell.colors)
|
||||
|
||||
describe('Dashboards.Reducers.cellEditorOverlay', () => {
|
||||
it('should show cell editor overlay', () => {
|
||||
|
@ -117,4 +120,11 @@ describe('Dashboards.Reducers.cellEditorOverlay', () => {
|
|||
|
||||
expect(actual.cell.axes).toBe(expected)
|
||||
})
|
||||
|
||||
it('should update the cell line graph colors', () => {
|
||||
const actual = reducer(initialState, updateLineColors(defaultLineColors))
|
||||
const expected = defaultLineColors
|
||||
|
||||
expect(actual.lineColors).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1686,6 +1686,10 @@ chownr@^1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
|
||||
|
||||
chroma-js@^1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/chroma-js/-/chroma-js-1.3.6.tgz#22dd7220ef6b55dcfcb8ef92982baaf55dced45d"
|
||||
|
||||
ci-info@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.2.tgz#03561259db48d0474c8bdc90f5b47b068b6bbfb4"
|
||||
|
|
Loading…
Reference in New Issue