Merge pull request #3458 from influxdata/ts-party/misc-shared-components
Convert a Handful of Components to TypeScriptpull/10616/head
commit
9fc462abae
|
@ -223,7 +223,7 @@ class DashboardPage extends Component {
|
|||
dashboardActions.addDashboardCellAsync(dashboard)
|
||||
}
|
||||
|
||||
handleCloneCell = cell => () => {
|
||||
handleCloneCell = cell => {
|
||||
const {dashboardActions, dashboard} = this.props
|
||||
dashboardActions.cloneDashboardCellAsync(dashboard, cell)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Query {
|
||||
rawText: string
|
||||
}
|
||||
import {QueryConfig} from 'src/types/query'
|
||||
|
||||
interface Props {
|
||||
isActive: boolean
|
||||
query: Query
|
||||
query: QueryConfig
|
||||
onSelect: (index: number) => void
|
||||
onDelete: (index: number) => void
|
||||
queryTabText: string
|
||||
|
|
|
@ -48,6 +48,7 @@ import DeprecationWarning from 'src/admin/components/DeprecationWarning'
|
|||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {Source, Kapacitor} from 'src/types'
|
||||
import {Notification} from 'src/types/notifications'
|
||||
import {ServiceProperties, SpecificConfigOptions} from 'src/types/kapacitor'
|
||||
import SlackConfigs from 'src/kapacitor/components/config/SlackConfigs'
|
||||
import {
|
||||
|
@ -101,14 +102,6 @@ interface Sections {
|
|||
victorops: Section
|
||||
}
|
||||
|
||||
interface Notification {
|
||||
id?: string
|
||||
type: string
|
||||
icon: string
|
||||
duration: number
|
||||
message: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
source: Source
|
||||
kapacitor: Kapacitor
|
||||
|
|
|
@ -2,9 +2,11 @@ import React, {SFC} from 'react'
|
|||
import _ from 'lodash'
|
||||
|
||||
import {TEMP_VAR_DASHBOARD_TIME} from 'src/shared/constants'
|
||||
import {QueryConfig} from 'src/types/query'
|
||||
|
||||
interface Query {
|
||||
query: string
|
||||
config: QueryConfig
|
||||
text: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
@ -12,7 +14,9 @@ interface Props {
|
|||
}
|
||||
|
||||
const CustomTimeIndicator: SFC<Props> = ({queries}) => {
|
||||
const q = queries.find(({query}) => !query.includes(TEMP_VAR_DASHBOARD_TIME))
|
||||
const q = queries.find(
|
||||
query => query.text.includes(TEMP_VAR_DASHBOARD_TIME) === false
|
||||
)
|
||||
const customLower = _.get(q, ['queryConfig', 'range', 'lower'], null)
|
||||
const customUpper = _.get(q, ['queryConfig', 'range', 'upper'], null)
|
||||
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import _ from 'lodash'
|
||||
|
||||
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
|
||||
|
||||
import LayoutCellMenu from 'shared/components/LayoutCellMenu'
|
||||
import LayoutCellHeader from 'shared/components/LayoutCellHeader'
|
||||
import {notify} from 'src/shared/actions/notifications'
|
||||
import {notifyCSVDownloadFailed} from 'src/shared/copy/notifications'
|
||||
import download from 'src/external/download.js'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {dataToCSV} from 'src/shared/parsing/dataToCSV'
|
||||
import {timeSeriesToTableGraph} from 'src/utils/timeSeriesTransformers'
|
||||
|
||||
@ErrorHandling
|
||||
class LayoutCell extends Component {
|
||||
handleDeleteCell = cell => () => {
|
||||
this.props.onDeleteCell(cell)
|
||||
}
|
||||
|
||||
handleSummonOverlay = cell => () => {
|
||||
this.props.onSummonOverlayTechnologies(cell)
|
||||
}
|
||||
|
||||
handleCSVDownload = cell => () => {
|
||||
const joinedName = cell.name.split(' ').join('_')
|
||||
const {cellData} = this.props
|
||||
const {data} = timeSeriesToTableGraph(cellData)
|
||||
|
||||
try {
|
||||
download(dataToCSV(data), `${joinedName}.csv`, 'text/plain')
|
||||
} catch (error) {
|
||||
notify(notifyCSVDownloadFailed())
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {cell, children, isEditable, cellData, onCloneCell} = this.props
|
||||
|
||||
const queries = _.get(cell, ['queries'], [])
|
||||
|
||||
// Passing the cell ID into the child graph so that further along
|
||||
// we can detect if "this cell is the one being resized"
|
||||
const child = children.length ? children[0] : children
|
||||
const layoutCellGraph = React.cloneElement(child, {cellID: cell.i})
|
||||
|
||||
return (
|
||||
<div className="dash-graph">
|
||||
<Authorized requiredRole={EDITOR_ROLE}>
|
||||
<LayoutCellMenu
|
||||
cell={cell}
|
||||
queries={queries}
|
||||
dataExists={!!cellData.length}
|
||||
isEditable={isEditable}
|
||||
onDelete={this.handleDeleteCell}
|
||||
onEdit={this.handleSummonOverlay}
|
||||
onClone={onCloneCell}
|
||||
onCSVDownload={this.handleCSVDownload}
|
||||
/>
|
||||
</Authorized>
|
||||
<LayoutCellHeader cellName={cell.name} isEditable={isEditable} />
|
||||
<div className="dash-graph--container">
|
||||
{queries.length ? (
|
||||
layoutCellGraph
|
||||
) : (
|
||||
<div className="graph-empty">
|
||||
<Authorized requiredRole={EDITOR_ROLE}>
|
||||
<button
|
||||
className="no-query--button btn btn-md btn-primary"
|
||||
onClick={this.handleSummonOverlay(cell)}
|
||||
>
|
||||
<span className="icon plus" /> Add Data
|
||||
</button>
|
||||
</Authorized>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, node, number, shape, string} = PropTypes
|
||||
|
||||
LayoutCell.propTypes = {
|
||||
cell: shape({
|
||||
i: string.isRequired,
|
||||
name: string.isRequired,
|
||||
isEditing: bool,
|
||||
x: number.isRequired,
|
||||
y: number.isRequired,
|
||||
queries: arrayOf(shape()),
|
||||
}).isRequired,
|
||||
children: node.isRequired,
|
||||
onDeleteCell: func,
|
||||
onCloneCell: func,
|
||||
onSummonOverlayTechnologies: func,
|
||||
isEditable: bool,
|
||||
onCancelEditCell: func,
|
||||
cellData: arrayOf(shape({})),
|
||||
}
|
||||
|
||||
export default LayoutCell
|
|
@ -0,0 +1,119 @@
|
|||
import React, {Component, ReactElement} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
|
||||
|
||||
import LayoutCellMenu from 'src/shared/components/LayoutCellMenu'
|
||||
import LayoutCellHeader from 'src/shared/components/LayoutCellHeader'
|
||||
import {notify} from 'src/shared/actions/notifications'
|
||||
import {notifyCSVDownloadFailed} from 'src/shared/copy/notifications'
|
||||
import download from 'src/external/download.js'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {dataToCSV} from 'src/shared/parsing/dataToCSV'
|
||||
import {timeSeriesToTableGraph} from 'src/utils/timeSeriesTransformers'
|
||||
import {Cell, CellQuery} from 'src/types/dashboard'
|
||||
|
||||
interface Series {
|
||||
columns: string[]
|
||||
name: string
|
||||
values: number[][]
|
||||
}
|
||||
|
||||
interface Result {
|
||||
statement_id: number
|
||||
series: Series[]
|
||||
}
|
||||
|
||||
interface Response {
|
||||
results: Result[]
|
||||
}
|
||||
|
||||
interface Data {
|
||||
response: Response[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
cell: Cell
|
||||
children: ReactElement<any>
|
||||
onDeleteCell: (cell: Cell) => void
|
||||
onCloneCell: (cell: Cell) => void
|
||||
onSummonOverlayTechnologies: (cell: Cell) => void
|
||||
isEditable: boolean
|
||||
onCancelEditCell: () => void
|
||||
cellData: Data[]
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
export default class LayoutCell extends Component<Props> {
|
||||
public render() {
|
||||
const {cell, isEditable, cellData, onDeleteCell, onCloneCell} = this.props
|
||||
|
||||
return (
|
||||
<div className="dash-graph">
|
||||
<Authorized requiredRole={EDITOR_ROLE}>
|
||||
<LayoutCellMenu
|
||||
cell={cell}
|
||||
queries={this.queries}
|
||||
dataExists={!!cellData.length}
|
||||
isEditable={isEditable}
|
||||
onDelete={onDeleteCell}
|
||||
onEdit={this.handleSummonOverlay}
|
||||
onClone={onCloneCell}
|
||||
onCSVDownload={this.handleCSVDownload}
|
||||
/>
|
||||
</Authorized>
|
||||
<LayoutCellHeader cellName={cell.name} isEditable={isEditable} />
|
||||
<div className="dash-graph--container">{this.renderGraph}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get queries(): CellQuery[] {
|
||||
const {cell} = this.props
|
||||
return _.get(cell, ['queries'], [])
|
||||
}
|
||||
|
||||
private get renderGraph(): JSX.Element {
|
||||
const {cell, children} = this.props
|
||||
|
||||
if (this.queries.length) {
|
||||
const child = React.Children.only(children)
|
||||
return React.cloneElement(child, {cellID: cell.id})
|
||||
}
|
||||
|
||||
return this.emptyGraph
|
||||
}
|
||||
|
||||
private get emptyGraph(): JSX.Element {
|
||||
return (
|
||||
<div className="graph-empty">
|
||||
<Authorized requiredRole={EDITOR_ROLE}>
|
||||
<button
|
||||
className="no-query--button btn btn-md btn-primary"
|
||||
onClick={this.handleSummonOverlay}
|
||||
>
|
||||
<span className="icon plus" /> Add Data
|
||||
</button>
|
||||
</Authorized>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private handleSummonOverlay = (): void => {
|
||||
const {cell, onSummonOverlayTechnologies} = this.props
|
||||
onSummonOverlayTechnologies(cell)
|
||||
}
|
||||
|
||||
private handleCSVDownload = (): void => {
|
||||
const {cellData, cell} = this.props
|
||||
const joinedName = cell.name.split(' ').join('_')
|
||||
const {data} = timeSeriesToTableGraph(cellData)
|
||||
|
||||
try {
|
||||
download(dataToCSV(data), `${joinedName}.csv`, 'text/plain')
|
||||
} catch (error) {
|
||||
notify(notifyCSVDownloadFailed())
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import classnames from 'classnames'
|
||||
|
||||
import MenuTooltipButton from 'src/shared/components/MenuTooltipButton'
|
||||
import CustomTimeIndicator from 'src/shared/components/CustomTimeIndicator'
|
||||
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
|
||||
import {EDITING} from 'src/shared/annotations/helpers'
|
||||
import {cellSupportsAnnotations} from 'src/shared/constants/index'
|
||||
|
||||
import {
|
||||
addingAnnotation,
|
||||
editingAnnotation,
|
||||
dismissEditingAnnotation,
|
||||
} from 'src/shared/actions/annotations'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
@ErrorHandling
|
||||
class LayoutCellMenu extends Component {
|
||||
state = {
|
||||
subMenuIsOpen: false,
|
||||
}
|
||||
|
||||
handleToggleSubMenu = () => {
|
||||
this.setState({subMenuIsOpen: !this.state.subMenuIsOpen})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {subMenuIsOpen} = this.state
|
||||
const {
|
||||
mode,
|
||||
cell,
|
||||
onEdit,
|
||||
onClone,
|
||||
queries,
|
||||
onDelete,
|
||||
isEditable,
|
||||
dataExists,
|
||||
onCSVDownload,
|
||||
onStartAddingAnnotation,
|
||||
onStartEditingAnnotation,
|
||||
onDismissEditingAnnotation,
|
||||
} = this.props
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
text: 'Configure',
|
||||
action: onEdit(cell),
|
||||
},
|
||||
{
|
||||
text: 'Add Annotation',
|
||||
action: onStartAddingAnnotation,
|
||||
disabled: !cellSupportsAnnotations(cell.type),
|
||||
},
|
||||
{
|
||||
text: 'Edit Annotations',
|
||||
action: onStartEditingAnnotation,
|
||||
disabled: !cellSupportsAnnotations(cell.type),
|
||||
},
|
||||
{
|
||||
text: 'Download CSV',
|
||||
action: onCSVDownload(cell),
|
||||
disabled: !dataExists,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('dash-graph-context', {
|
||||
'dash-graph-context__open': subMenuIsOpen,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={`${
|
||||
isEditable
|
||||
? 'dash-graph--custom-indicators dash-graph--draggable'
|
||||
: 'dash-graph--custom-indicators'
|
||||
}`}
|
||||
>
|
||||
{queries && <CustomTimeIndicator queries={queries} />}
|
||||
</div>
|
||||
{isEditable &&
|
||||
mode !== EDITING && (
|
||||
<div className="dash-graph-context--buttons">
|
||||
{queries.length ? (
|
||||
<MenuTooltipButton
|
||||
icon="pencil"
|
||||
menuOptions={menuOptions}
|
||||
informParent={this.handleToggleSubMenu}
|
||||
/>
|
||||
) : null}
|
||||
<Authorized requiredRole={EDITOR_ROLE}>
|
||||
<MenuTooltipButton
|
||||
icon="duplicate"
|
||||
menuOptions={[{text: 'Clone Cell', action: onClone(cell)}]}
|
||||
informParent={this.handleToggleSubMenu}
|
||||
/>
|
||||
</Authorized>
|
||||
<MenuTooltipButton
|
||||
icon="trash"
|
||||
theme="danger"
|
||||
menuOptions={[{text: 'Confirm', action: onDelete(cell)}]}
|
||||
informParent={this.handleToggleSubMenu}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{mode === 'editing' &&
|
||||
cellSupportsAnnotations(cell.type) && (
|
||||
<div className="dash-graph-context--buttons">
|
||||
<div
|
||||
className="btn btn-xs btn-success"
|
||||
onClick={onDismissEditingAnnotation}
|
||||
>
|
||||
Done Editing
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
LayoutCellMenu.propTypes = {
|
||||
mode: string,
|
||||
onEdit: func,
|
||||
onClone: func,
|
||||
onDelete: func,
|
||||
cell: shape(),
|
||||
isEditable: bool,
|
||||
dataExists: bool,
|
||||
onCSVDownload: func,
|
||||
queries: arrayOf(shape()),
|
||||
onStartAddingAnnotation: func.isRequired,
|
||||
onStartEditingAnnotation: func.isRequired,
|
||||
onDismissEditingAnnotation: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({annotations: {mode}}) => ({
|
||||
mode,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onStartAddingAnnotation: bindActionCreators(addingAnnotation, dispatch),
|
||||
onStartEditingAnnotation: bindActionCreators(editingAnnotation, dispatch),
|
||||
onDismissEditingAnnotation: bindActionCreators(
|
||||
dismissEditingAnnotation,
|
||||
dispatch
|
||||
),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(LayoutCellMenu)
|
|
@ -0,0 +1,214 @@
|
|||
import React, {Component} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import classnames from 'classnames'
|
||||
|
||||
import MenuTooltipButton, {
|
||||
MenuItem,
|
||||
} from 'src/shared/components/MenuTooltipButton'
|
||||
import CustomTimeIndicator from 'src/shared/components/CustomTimeIndicator'
|
||||
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
|
||||
import {EDITING} from 'src/shared/annotations/helpers'
|
||||
import {cellSupportsAnnotations} from 'src/shared/constants/index'
|
||||
import {Cell} from 'src/types/dashboard'
|
||||
import {QueryConfig} from 'src/types/query'
|
||||
|
||||
import {
|
||||
addingAnnotation,
|
||||
editingAnnotation,
|
||||
dismissEditingAnnotation,
|
||||
} from 'src/shared/actions/annotations'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Query {
|
||||
text: string
|
||||
config: QueryConfig
|
||||
}
|
||||
|
||||
interface Props {
|
||||
cell: Cell
|
||||
isEditable: boolean
|
||||
dataExists: boolean
|
||||
mode: string
|
||||
onEdit: () => void
|
||||
onClone: (cell: Cell) => void
|
||||
onDelete: (cell: Cell) => void
|
||||
onCSVDownload: () => void
|
||||
onStartAddingAnnotation: () => void
|
||||
onStartEditingAnnotation: () => void
|
||||
onDismissEditingAnnotation: () => void
|
||||
queries: Query[]
|
||||
}
|
||||
|
||||
interface State {
|
||||
subMenuIsOpen: boolean
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class LayoutCellMenu extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
subMenuIsOpen: false,
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {queries} = this.props
|
||||
|
||||
return (
|
||||
<div className={this.contextMenuClassname}>
|
||||
<div className={this.customIndicatorsClassname}>
|
||||
{queries && <CustomTimeIndicator queries={queries} />}
|
||||
</div>
|
||||
{this.renderMenu}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get renderMenu(): JSX.Element {
|
||||
const {isEditable, mode, cell, onDismissEditingAnnotation} = this.props
|
||||
|
||||
if (mode === EDITING && cellSupportsAnnotations(cell.type)) {
|
||||
return (
|
||||
<div className="dash-graph-context--buttons">
|
||||
<div
|
||||
className="btn btn-xs btn-success"
|
||||
onClick={onDismissEditingAnnotation}
|
||||
>
|
||||
Done Editing
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (isEditable && mode !== EDITING) {
|
||||
return (
|
||||
<div className="dash-graph-context--buttons">
|
||||
{this.pencilMenu}
|
||||
<Authorized requiredRole={EDITOR_ROLE}>
|
||||
<MenuTooltipButton
|
||||
icon="duplicate"
|
||||
menuItems={this.cloneMenuItems}
|
||||
informParent={this.handleToggleSubMenu}
|
||||
/>
|
||||
</Authorized>
|
||||
<MenuTooltipButton
|
||||
icon="trash"
|
||||
theme="danger"
|
||||
menuItems={this.deleteMenuItems}
|
||||
informParent={this.handleToggleSubMenu}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private get pencilMenu(): JSX.Element {
|
||||
const {queries} = this.props
|
||||
|
||||
if (!queries.length) {
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuTooltipButton
|
||||
icon="pencil"
|
||||
menuItems={this.editMenuItems}
|
||||
informParent={this.handleToggleSubMenu}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private get contextMenuClassname(): string {
|
||||
const {subMenuIsOpen} = this.state
|
||||
|
||||
return classnames('dash-graph-context', {
|
||||
'dash-graph-context__open': subMenuIsOpen,
|
||||
})
|
||||
}
|
||||
private get customIndicatorsClassname(): string {
|
||||
const {isEditable} = this.props
|
||||
|
||||
return classnames('dash-graph--custom-indicators', {
|
||||
'dash-graph--draggable': isEditable,
|
||||
})
|
||||
}
|
||||
|
||||
private get editMenuItems(): MenuItem[] {
|
||||
const {
|
||||
cell,
|
||||
dataExists,
|
||||
onStartAddingAnnotation,
|
||||
onStartEditingAnnotation,
|
||||
onCSVDownload,
|
||||
} = this.props
|
||||
|
||||
return [
|
||||
{
|
||||
text: 'Configure',
|
||||
action: this.handleEditCell,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: 'Add Annotation',
|
||||
action: onStartAddingAnnotation,
|
||||
disabled: !cellSupportsAnnotations(cell.type),
|
||||
},
|
||||
{
|
||||
text: 'Edit Annotations',
|
||||
action: onStartEditingAnnotation,
|
||||
disabled: !cellSupportsAnnotations(cell.type),
|
||||
},
|
||||
{
|
||||
text: 'Download CSV',
|
||||
action: onCSVDownload,
|
||||
disabled: !dataExists,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
private get cloneMenuItems(): MenuItem[] {
|
||||
return [{text: 'Clone Cell', action: this.handleCloneCell, disabled: false}]
|
||||
}
|
||||
|
||||
private get deleteMenuItems(): MenuItem[] {
|
||||
return [{text: 'Confirm', action: this.handleDeleteCell, disabled: false}]
|
||||
}
|
||||
|
||||
private handleEditCell = (): void => {
|
||||
const {onEdit} = this.props
|
||||
onEdit()
|
||||
}
|
||||
|
||||
private handleDeleteCell = (): void => {
|
||||
const {onDelete, cell} = this.props
|
||||
onDelete(cell)
|
||||
}
|
||||
|
||||
private handleCloneCell = (): void => {
|
||||
const {onClone, cell} = this.props
|
||||
onClone(cell)
|
||||
}
|
||||
|
||||
private handleToggleSubMenu = (): void => {
|
||||
this.setState({subMenuIsOpen: !this.state.subMenuIsOpen})
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({annotations: {mode}}) => ({
|
||||
mode,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onStartAddingAnnotation: bindActionCreators(addingAnnotation, dispatch),
|
||||
onStartEditingAnnotation: bindActionCreators(editingAnnotation, dispatch),
|
||||
onDismissEditingAnnotation: bindActionCreators(
|
||||
dismissEditingAnnotation,
|
||||
dispatch
|
||||
),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(LayoutCellMenu)
|
|
@ -1,102 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import OnClickOutside from 'react-onclickoutside'
|
||||
import classnames from 'classnames'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
@ErrorHandling
|
||||
class MenuTooltipButton extends Component {
|
||||
state = {
|
||||
expanded: false,
|
||||
}
|
||||
|
||||
handleButtonClick = () => {
|
||||
const {informParent} = this.props
|
||||
|
||||
this.setState({expanded: !this.state.expanded})
|
||||
informParent()
|
||||
}
|
||||
|
||||
handleMenuItemClick = menuItemAction => () => {
|
||||
const {informParent} = this.props
|
||||
|
||||
this.setState({expanded: false})
|
||||
menuItemAction()
|
||||
informParent()
|
||||
}
|
||||
|
||||
handleClickOutside = () => {
|
||||
const {informParent} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
if (expanded === false) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({expanded: false})
|
||||
informParent()
|
||||
}
|
||||
|
||||
renderMenuOptions = () => {
|
||||
const {menuOptions} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
if (expanded === false) {
|
||||
return null
|
||||
}
|
||||
|
||||
return menuOptions.map((option, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`dash-graph-context--menu-item${
|
||||
option.disabled ? ' disabled' : ''
|
||||
}`}
|
||||
onClick={
|
||||
option.disabled ? null : this.handleMenuItemClick(option.action)
|
||||
}
|
||||
>
|
||||
{option.text}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
|
||||
render() {
|
||||
const {icon, theme} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('dash-graph-context--button', {active: expanded})}
|
||||
onClick={this.handleButtonClick}
|
||||
>
|
||||
<span className={`icon ${icon}`} />
|
||||
{expanded ? (
|
||||
<div className={`dash-graph-context--menu ${theme}`}>
|
||||
{this.renderMenuOptions()}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
MenuTooltipButton.defaultProps = {
|
||||
theme: 'default',
|
||||
}
|
||||
|
||||
MenuTooltipButton.propTypes = {
|
||||
theme: string, // accepted values: default, primary, warning, success, danger
|
||||
icon: string.isRequired,
|
||||
menuOptions: arrayOf(
|
||||
shape({
|
||||
text: string.isRequired,
|
||||
action: func.isRequired,
|
||||
disabled: bool,
|
||||
})
|
||||
).isRequired,
|
||||
informParent: func,
|
||||
}
|
||||
|
||||
export default OnClickOutside(MenuTooltipButton)
|
|
@ -0,0 +1,117 @@
|
|||
import React, {Component} from 'react'
|
||||
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
||||
import classnames from 'classnames'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
type MenuItemAction = () => void
|
||||
|
||||
export interface MenuItem {
|
||||
text: string
|
||||
action: MenuItemAction
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
interface Props {
|
||||
theme?: string
|
||||
icon: string
|
||||
informParent: () => void
|
||||
menuItems: MenuItem[]
|
||||
}
|
||||
|
||||
interface State {
|
||||
expanded: boolean
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
export default class MenuTooltipButton extends Component<Props, State> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
theme: 'default',
|
||||
}
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
expanded: false,
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {expanded} = this.state
|
||||
|
||||
return (
|
||||
<ClickOutside onClickOutside={this.handleClickOutside}>
|
||||
<div className={this.className} onClick={this.handleButtonClick}>
|
||||
{this.icon}
|
||||
{expanded && this.renderMenu}
|
||||
</div>
|
||||
</ClickOutside>
|
||||
)
|
||||
}
|
||||
|
||||
private handleButtonClick = (): void => {
|
||||
const {informParent} = this.props
|
||||
|
||||
this.setState({expanded: !this.state.expanded})
|
||||
informParent()
|
||||
}
|
||||
|
||||
private handleMenuItemClick = (action: MenuItemAction) => (): void => {
|
||||
const {informParent} = this.props
|
||||
|
||||
this.setState({expanded: false})
|
||||
action()
|
||||
informParent()
|
||||
}
|
||||
|
||||
private handleClickOutside = (): void => {
|
||||
const {informParent} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
if (expanded === false) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({expanded: false})
|
||||
informParent()
|
||||
}
|
||||
|
||||
private get className(): string {
|
||||
const {expanded} = this.state
|
||||
|
||||
return classnames('dash-graph-context--button', {active: expanded})
|
||||
}
|
||||
|
||||
private get icon(): JSX.Element {
|
||||
const {icon} = this.props
|
||||
|
||||
return <span className={`icon ${icon}`} />
|
||||
}
|
||||
|
||||
private get renderMenu(): JSX.Element {
|
||||
const {menuItems, theme} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
if (expanded === false) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`dash-graph-context--menu ${theme}`}>
|
||||
{menuItems.map((option, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`dash-graph-context--menu-item${
|
||||
option.disabled ? ' disabled' : ''
|
||||
}`}
|
||||
onClick={
|
||||
option.disabled ? null : this.handleMenuItemClick(option.action)
|
||||
}
|
||||
>
|
||||
{option.text}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import classnames from 'classnames'
|
||||
|
||||
import {dismissNotification as dismissNotificationAction} from 'shared/actions/notifications'
|
||||
|
||||
import {NOTIFICATION_TRANSITION} from 'shared/constants/index'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
@ErrorHandling
|
||||
class Notification extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
opacity: 1,
|
||||
height: 0,
|
||||
dismissed: false,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {
|
||||
notification: {duration},
|
||||
} = this.props
|
||||
|
||||
this.updateHeight()
|
||||
|
||||
if (duration >= 0) {
|
||||
// Automatically dismiss notification after duration prop
|
||||
this.dismissTimer = setTimeout(this.handleDismiss, duration)
|
||||
}
|
||||
}
|
||||
|
||||
updateHeight() {
|
||||
if (this.notificationRef) {
|
||||
const {height} = this.notificationRef.getBoundingClientRect()
|
||||
this.setState({height})
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.dismissTimer)
|
||||
clearTimeout(this.deleteTimer)
|
||||
}
|
||||
|
||||
handleDismiss = () => {
|
||||
const {
|
||||
notification: {id},
|
||||
dismissNotification,
|
||||
} = this.props
|
||||
|
||||
this.setState({dismissed: true})
|
||||
this.deleteTimer = setTimeout(
|
||||
() => dismissNotification(id),
|
||||
NOTIFICATION_TRANSITION
|
||||
)
|
||||
}
|
||||
|
||||
onNotificationRef = ref => {
|
||||
this.notificationRef = ref
|
||||
this.updateHeight()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
notification: {type, message, icon},
|
||||
} = this.props
|
||||
const {height, dismissed} = this.state
|
||||
|
||||
const notificationContainerClass = classnames('notification-container', {
|
||||
show: !!height,
|
||||
'notification-dismissed': dismissed,
|
||||
})
|
||||
const notificationClass = `notification notification-${type}`
|
||||
const notificationMargin = 4
|
||||
const style = {height: height + notificationMargin}
|
||||
|
||||
return (
|
||||
<div className={notificationContainerClass} style={style}>
|
||||
<div className={notificationClass} ref={this.onNotificationRef}>
|
||||
<span className={`icon ${icon}`} />
|
||||
<div className="notification-message">{message}</div>
|
||||
<button className="notification-close" onClick={this.handleDismiss} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func, number, shape, string} = PropTypes
|
||||
|
||||
Notification.propTypes = {
|
||||
notification: shape({
|
||||
id: string.isRequired,
|
||||
type: string.isRequired,
|
||||
message: string.isRequired,
|
||||
duration: number.isRequired,
|
||||
icon: string.isRequired,
|
||||
}).isRequired,
|
||||
dismissNotification: func.isRequired,
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
dismissNotification: bindActionCreators(dismissNotificationAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(Notification)
|
|
@ -0,0 +1,132 @@
|
|||
import React, {Component, CSSProperties} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
import {Notification as NotificationType} from 'src/types/notifications'
|
||||
|
||||
import classnames from 'classnames'
|
||||
|
||||
import {dismissNotification as dismissNotificationAction} from 'src/shared/actions/notifications'
|
||||
|
||||
import {NOTIFICATION_TRANSITION} from 'src/shared/constants/index'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Props {
|
||||
notification: NotificationType
|
||||
dismissNotification: (id: string) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
opacity: number
|
||||
height: number
|
||||
dismissed: boolean
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class Notification extends Component<Props, State> {
|
||||
private notificationRef: HTMLElement
|
||||
private dismissalTimer: number
|
||||
private deletionTimer: number
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
opacity: 1,
|
||||
height: 0,
|
||||
dismissed: false,
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
const {
|
||||
notification: {duration},
|
||||
} = this.props
|
||||
|
||||
this.updateHeight()
|
||||
|
||||
if (duration >= 0) {
|
||||
// Automatically dismiss notification after duration prop
|
||||
this.dismissalTimer = window.setTimeout(this.handleDismiss, duration)
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
clearTimeout(this.dismissalTimer)
|
||||
clearTimeout(this.deletionTimer)
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
notification: {message, icon},
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div className={this.containerClassname} style={this.notificationStyle}>
|
||||
<div
|
||||
className={this.notificationClassname}
|
||||
ref={this.handleNotificationRef}
|
||||
>
|
||||
<span className={`icon ${icon}`} />
|
||||
<div className="notification-message">{message}</div>
|
||||
<button className="notification-close" onClick={this.handleDismiss} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get notificationClassname(): string {
|
||||
const {
|
||||
notification: {type},
|
||||
} = this.props
|
||||
|
||||
return `notification notification-${type}`
|
||||
}
|
||||
|
||||
private get containerClassname(): string {
|
||||
const {height, dismissed} = this.state
|
||||
|
||||
return classnames('notification-container', {
|
||||
show: !!height,
|
||||
'notification-dismissed': dismissed,
|
||||
})
|
||||
}
|
||||
|
||||
private get notificationStyle(): CSSProperties {
|
||||
const {height} = this.state
|
||||
|
||||
const NOTIFICATION_MARGIN = 4
|
||||
|
||||
return {height: height + NOTIFICATION_MARGIN}
|
||||
}
|
||||
|
||||
private updateHeight = (): void => {
|
||||
if (this.notificationRef) {
|
||||
const {height} = this.notificationRef.getBoundingClientRect()
|
||||
this.setState({height})
|
||||
}
|
||||
}
|
||||
|
||||
private handleDismiss = (): void => {
|
||||
const {
|
||||
notification: {id},
|
||||
dismissNotification,
|
||||
} = this.props
|
||||
|
||||
this.setState({dismissed: true})
|
||||
this.deletionTimer = window.setTimeout(
|
||||
() => dismissNotification(id),
|
||||
NOTIFICATION_TRANSITION
|
||||
)
|
||||
}
|
||||
|
||||
private handleNotificationRef = (ref: HTMLElement): void => {
|
||||
this.notificationRef = ref
|
||||
this.updateHeight()
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
dismissNotification: bindActionCreators(dismissNotificationAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(null, mapDispatchToProps)(Notification)
|
|
@ -1,44 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import Notification from 'shared/components/Notification'
|
||||
|
||||
const Notifications = ({notifications, inPresentationMode}) => (
|
||||
<div
|
||||
className={`${
|
||||
inPresentationMode
|
||||
? 'notification-center__presentation-mode'
|
||||
: 'notification-center'
|
||||
}`}
|
||||
>
|
||||
{notifications.map(n => <Notification key={n.id} notification={n} />)}
|
||||
</div>
|
||||
)
|
||||
|
||||
const {arrayOf, bool, number, shape, string} = PropTypes
|
||||
|
||||
Notifications.propTypes = {
|
||||
notifications: arrayOf(
|
||||
shape({
|
||||
id: string.isRequired,
|
||||
type: string.isRequired,
|
||||
message: string.isRequired,
|
||||
duration: number.isRequired,
|
||||
icon: string,
|
||||
})
|
||||
),
|
||||
inPresentationMode: bool,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
notifications,
|
||||
app: {
|
||||
ephemeral: {inPresentationMode},
|
||||
},
|
||||
}) => ({
|
||||
notifications,
|
||||
inPresentationMode,
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, null)(Notifications)
|
|
@ -0,0 +1,47 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {Notification as NotificationType} from 'src/types/notifications'
|
||||
import Notification from 'src/shared/components/Notification'
|
||||
|
||||
interface Props {
|
||||
inPresentationMode?: boolean
|
||||
notifications: NotificationType[]
|
||||
}
|
||||
|
||||
class Notifications extends PureComponent<Props> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
inPresentationMode: false,
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {notifications} = this.props
|
||||
|
||||
return (
|
||||
<div className={this.className}>
|
||||
{notifications.map(n => <Notification key={n.id} notification={n} />)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get className(): string {
|
||||
const {inPresentationMode} = this.props
|
||||
|
||||
if (inPresentationMode) {
|
||||
return 'notification-center__presentation-mode'
|
||||
}
|
||||
|
||||
return 'notification-center'
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
notifications,
|
||||
app: {
|
||||
ephemeral: {inPresentationMode},
|
||||
},
|
||||
}): Props => ({
|
||||
notifications,
|
||||
inPresentationMode,
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, null)(Notifications)
|
|
@ -1,51 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import QueryMakerTab from 'src/data_explorer/components/QueryMakerTab'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
|
||||
const QueryTabList = ({
|
||||
queries,
|
||||
timeRange,
|
||||
onAddQuery,
|
||||
onDeleteQuery,
|
||||
activeQueryIndex,
|
||||
setActiveQueryIndex,
|
||||
}) => (
|
||||
<div className="query-maker--tabs">
|
||||
{queries.map((q, i) => (
|
||||
<QueryMakerTab
|
||||
isActive={i === activeQueryIndex}
|
||||
key={i}
|
||||
queryIndex={i}
|
||||
query={q}
|
||||
onSelect={setActiveQueryIndex}
|
||||
onDelete={onDeleteQuery}
|
||||
queryTabText={
|
||||
q.rawText || buildInfluxQLQuery(timeRange, q) || `Query ${i + 1}`
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="query-maker--new btn btn-sm btn-primary"
|
||||
onClick={onAddQuery}
|
||||
>
|
||||
<span className="icon plus" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const {arrayOf, func, number, shape, string} = PropTypes
|
||||
|
||||
QueryTabList.propTypes = {
|
||||
queries: arrayOf(shape({})).isRequired,
|
||||
timeRange: shape({
|
||||
upper: string,
|
||||
lower: string,
|
||||
}).isRequired,
|
||||
onAddQuery: func.isRequired,
|
||||
onDeleteQuery: func.isRequired,
|
||||
activeQueryIndex: number.isRequired,
|
||||
setActiveQueryIndex: func.isRequired,
|
||||
}
|
||||
|
||||
export default QueryTabList
|
|
@ -0,0 +1,55 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import QueryMakerTab from 'src/data_explorer/components/QueryMakerTab'
|
||||
import buildInfluxQLQuery from 'src/utils/influxql'
|
||||
import {QueryConfig, TimeRange} from 'src/types/query'
|
||||
|
||||
interface Props {
|
||||
queries: QueryConfig[]
|
||||
onAddQuery: () => void
|
||||
onDeleteQuery: (index: number) => void
|
||||
activeQueryIndex: number
|
||||
setActiveQueryIndex: (index: number) => void
|
||||
timeRange: TimeRange
|
||||
}
|
||||
|
||||
export default class QueryTabList extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {
|
||||
queries,
|
||||
onAddQuery,
|
||||
onDeleteQuery,
|
||||
activeQueryIndex,
|
||||
setActiveQueryIndex,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div className="query-maker--tabs">
|
||||
{queries.map((q, i) => (
|
||||
<QueryMakerTab
|
||||
key={i}
|
||||
isActive={i === activeQueryIndex}
|
||||
query={q}
|
||||
onSelect={setActiveQueryIndex}
|
||||
onDelete={onDeleteQuery}
|
||||
queryTabText={this.queryTabText(i, q)}
|
||||
queryIndex={i}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="query-maker--new btn btn-sm btn-primary"
|
||||
onClick={onAddQuery}
|
||||
>
|
||||
<span className="icon plus" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private queryTabText = (i: number, query: QueryConfig): string => {
|
||||
const {timeRange} = this.props
|
||||
|
||||
return (
|
||||
query.rawText || buildInfluxQLQuery(timeRange, query) || `Query ${i + 1}`
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
|
@ -8,36 +7,36 @@ import {updateThresholdsListType} from 'src/dashboards/actions/cellEditorOverlay
|
|||
import {
|
||||
THRESHOLD_TYPE_TEXT,
|
||||
THRESHOLD_TYPE_BG,
|
||||
} from 'shared/constants/thresholds'
|
||||
} from 'src/shared/constants/thresholds'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface PropsFromRedux {
|
||||
thresholdsListType: string
|
||||
}
|
||||
interface PropsFromParent {
|
||||
containerClass: string
|
||||
handleUpdateThresholdsListType: (newType: string) => void
|
||||
}
|
||||
|
||||
type Props = PropsFromRedux & PropsFromParent
|
||||
|
||||
@ErrorHandling
|
||||
class ThresholdsListTypeToggle extends Component {
|
||||
handleToggleThresholdsListType = newType => () => {
|
||||
const {handleUpdateThresholdsListType} = this.props
|
||||
|
||||
handleUpdateThresholdsListType(newType)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {thresholdsListType, containerClass} = this.props
|
||||
class ThresholdsListTypeToggle extends Component<Props> {
|
||||
public render() {
|
||||
const {containerClass} = this.props
|
||||
|
||||
return (
|
||||
<div className={containerClass}>
|
||||
<label>Threshold Coloring</label>
|
||||
<ul className="nav nav-tablist nav-tablist-sm">
|
||||
<li
|
||||
className={`${
|
||||
thresholdsListType === THRESHOLD_TYPE_BG ? 'active' : ''
|
||||
}`}
|
||||
className={this.bgTabClassName}
|
||||
onClick={this.handleToggleThresholdsListType(THRESHOLD_TYPE_BG)}
|
||||
>
|
||||
Background
|
||||
</li>
|
||||
<li
|
||||
className={`${
|
||||
thresholdsListType === THRESHOLD_TYPE_TEXT ? 'active' : ''
|
||||
}`}
|
||||
className={this.textTabClassName}
|
||||
onClick={this.handleToggleThresholdsListType(THRESHOLD_TYPE_TEXT)}
|
||||
>
|
||||
Text
|
||||
|
@ -46,16 +45,37 @@ class ThresholdsListTypeToggle extends Component {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
const {func, string} = PropTypes
|
||||
|
||||
ThresholdsListTypeToggle.propTypes = {
|
||||
thresholdsListType: string.isRequired,
|
||||
handleUpdateThresholdsListType: func.isRequired,
|
||||
containerClass: string.isRequired,
|
||||
private get bgTabClassName(): string {
|
||||
const {thresholdsListType} = this.props
|
||||
|
||||
if (thresholdsListType === THRESHOLD_TYPE_BG) {
|
||||
return 'active'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
private get textTabClassName(): string {
|
||||
const {thresholdsListType} = this.props
|
||||
|
||||
if (thresholdsListType === THRESHOLD_TYPE_TEXT) {
|
||||
return 'active'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
private handleToggleThresholdsListType = (newType: string) => (): void => {
|
||||
const {handleUpdateThresholdsListType} = this.props
|
||||
|
||||
handleUpdateThresholdsListType(newType)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({cellEditorOverlay: {thresholdsListType}}) => ({
|
||||
const mapStateToProps = ({
|
||||
cellEditorOverlay: {thresholdsListType},
|
||||
}): PropsFromRedux => ({
|
||||
thresholdsListType,
|
||||
})
|
||||
|
|
@ -57,8 +57,6 @@
|
|||
text-align: right !important;
|
||||
user-select: none;
|
||||
}
|
||||
.graph-container > div > div > div > div {
|
||||
}
|
||||
|
||||
/* Vertical Axis Labels */
|
||||
.dygraph-ylabel,
|
||||
|
@ -171,7 +169,7 @@
|
|||
display: block !important;
|
||||
position: absolute;
|
||||
padding: 11px;
|
||||
z-index: 500;
|
||||
z-index: $dygraph-legend-z;
|
||||
border-radius: 3px;
|
||||
min-width: 350px;
|
||||
user-select: text;
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
Page Layout
|
||||
----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$dygraph-legend-z: 500;
|
||||
$dash-ceo-z: $dygraph-legend-z + 10;
|
||||
|
||||
.chronograf-root {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
$overlay-controls-height: 60px;
|
||||
$overlay-controls-bg: $g2-kevlar;
|
||||
$overlay-z: 100;
|
||||
|
||||
|
||||
// Make Overlay Technology full screen
|
||||
|
@ -26,7 +25,7 @@ $overlay-z: 100;
|
|||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: $overlay-z;
|
||||
z-index: $dash-ceo-z;
|
||||
padding: 0 30px;
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue