Merge branch 'master' into presentational-page-components
commit
9798f0ef0c
|
@ -0,0 +1,138 @@
|
|||
workflows:
|
||||
version: 2
|
||||
main:
|
||||
jobs:
|
||||
- build
|
||||
- deploy-nightly:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- deploy-pre-release:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /^[0-9]+(\.[0-9]+)*(\S*)([a|rc|beta]([0-9]+))+$/
|
||||
- deploy-release:
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /^[0-9]+(\.[0-9]+)*$/
|
||||
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
environment:
|
||||
DOCKER_TAG: chronograf-20180327
|
||||
machine: true
|
||||
steps:
|
||||
- checkout
|
||||
- run: |
|
||||
ls -lah
|
||||
pwd
|
||||
- run: ./etc/scripts/docker/pull.sh
|
||||
- run:
|
||||
name: "Run Tests"
|
||||
command: >
|
||||
./etc/scripts/docker/run.sh
|
||||
--debug
|
||||
--test
|
||||
--no-build
|
||||
- persist_to_workspace:
|
||||
root: /home/circleci
|
||||
paths:
|
||||
- project
|
||||
|
||||
deploy-nightly:
|
||||
environment:
|
||||
DOCKER_TAG: chronograf-20180327
|
||||
machine: true
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci
|
||||
- run: |
|
||||
./etc/scripts/docker/run.sh \
|
||||
--debug \
|
||||
--clean \
|
||||
--package \
|
||||
--platform all \
|
||||
--arch all \
|
||||
--upload \
|
||||
--nightly \
|
||||
--bucket=dl.influxdata.com/chronograf/releases
|
||||
cp build/linux/static_amd64/chronograf .
|
||||
cp build/linux/static_amd64/chronoctl .
|
||||
docker build -t chronograf .
|
||||
docker login -u "$QUAY_USER" -p $QUAY_PASS quay.io
|
||||
docker tag chronograf quay.io/influxdb/chronograf:nightly
|
||||
docker push quay.io/influxdb/chronograf:nightly
|
||||
- store_artifacts:
|
||||
path: ./build/
|
||||
|
||||
deploy-pre-release:
|
||||
environment:
|
||||
DOCKER_TAG: chronograf-20180327
|
||||
machine: true
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci
|
||||
- run: |
|
||||
./etc/scripts/docker/run.sh \
|
||||
--clean \
|
||||
--debug \
|
||||
--release \
|
||||
--package \
|
||||
--platform all \
|
||||
--arch all \
|
||||
--upload-overwrite \
|
||||
--upload \
|
||||
--bucket dl.influxdata.com/chronograf/releases
|
||||
cp build/linux/static_amd64/chronograf .
|
||||
cp build/linux/static_amd64/chronoctl .
|
||||
docker build -t chronograf .
|
||||
docker login -u "$QUAY_USER" -p $QUAY_PASS quay.io
|
||||
docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
docker push quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_TAG}
|
||||
docker push quay.io/influxdb/chronograf:${CIRCLE_TAG}
|
||||
- store_artifacts:
|
||||
path: ./build/
|
||||
|
||||
deploy-release:
|
||||
environment:
|
||||
DOCKER_TAG: chronograf-20180327
|
||||
machine: true
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci
|
||||
- run: |
|
||||
./etc/scripts/docker/run.sh \
|
||||
--clean \
|
||||
--debug \
|
||||
--release \
|
||||
--package \
|
||||
--platform all \
|
||||
--arch all \
|
||||
--upload-overwrite \
|
||||
--upload \
|
||||
--bucket dl.influxdata.com/chronograf/releases
|
||||
cp build/linux/static_amd64/chronograf .
|
||||
cp build/linux/static_amd64/chronoctl .
|
||||
docker build -t chronograf .
|
||||
docker login -u "$QUAY_USER" -p $QUAY_PASS quay.io
|
||||
docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
docker push quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_TAG}
|
||||
docker push quay.io/influxdb/chronograf:${CIRCLE_TAG}
|
||||
docker tag chronograf quay.io/influxdb/chronograf:latest
|
||||
docker push quay.io/influxdb/chronograf:latest
|
||||
- store_artifacts:
|
||||
path: ./build/
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
1. [#3474](https://github.com/influxdata/chronograf/pull/3474): Sort task table on Manage Alert page alphabetically
|
||||
1. [#3590](https://github.com/influxdata/chronograf/pull/3590): Redesign icons in side navigation
|
||||
1. [#3696](https://github.com/influxdata/chronograf/pull/3696): Add ability to delete entire queries in Flux Editor
|
||||
1. [#3671](https://github.com/influxdata/chronograf/pull/3671): Remove Snip functionality in hover legend
|
||||
1. [#3659](https://github.com/influxdata/chronograf/pull/3659): Upgrade Data Explorer query text field with syntax highlighting and partial multi-line support
|
||||
1. [#3663](https://github.com/influxdata/chronograf/pull/3663): Truncate message preview in Alert Rules table
|
||||
|
|
91
circle.yml
91
circle.yml
|
@ -1,91 +0,0 @@
|
|||
---
|
||||
machine:
|
||||
services:
|
||||
- docker
|
||||
environment:
|
||||
DOCKER_TAG: chronograf-20180327
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- ./etc/scripts/docker/pull.sh
|
||||
|
||||
test:
|
||||
override:
|
||||
- >
|
||||
./etc/scripts/docker/run.sh
|
||||
--debug
|
||||
--test
|
||||
--no-build
|
||||
|
||||
deployment:
|
||||
master:
|
||||
branch: master
|
||||
commands:
|
||||
- >
|
||||
./etc/scripts/docker/run.sh
|
||||
--debug
|
||||
--clean
|
||||
--package
|
||||
--platform all
|
||||
--arch all
|
||||
--upload
|
||||
--nightly
|
||||
--bucket=dl.influxdata.com/chronograf/releases
|
||||
- sudo chown -R ubuntu:ubuntu /home/ubuntu
|
||||
- cp build/linux/static_amd64/chronograf .
|
||||
- cp build/linux/static_amd64/chronoctl .
|
||||
- docker build -t chronograf .
|
||||
- docker login -e $QUAY_EMAIL -u "$QUAY_USER" -p $QUAY_PASS quay.io
|
||||
- docker tag chronograf quay.io/influxdb/chronograf:nightly
|
||||
- docker push quay.io/influxdb/chronograf:nightly
|
||||
- mv ./build/* $CIRCLE_ARTIFACTS
|
||||
pre-release:
|
||||
tag: /^[0-9]+(\.[0-9]+)*(\S*)([a|rc|beta]([0-9]+))+$/
|
||||
commands:
|
||||
- >
|
||||
./etc/scripts/docker/run.sh
|
||||
--clean
|
||||
--debug
|
||||
--release
|
||||
--package
|
||||
--platform all
|
||||
--arch all
|
||||
--upload-overwrite
|
||||
--upload
|
||||
--bucket dl.influxdata.com/chronograf/releases
|
||||
- sudo chown -R ubuntu:ubuntu /home/ubuntu
|
||||
- cp build/linux/static_amd64/chronograf .
|
||||
- cp build/linux/static_amd64/chronoctl .
|
||||
- docker build -t chronograf .
|
||||
- docker login -e $QUAY_EMAIL -u "$QUAY_USER" -p $QUAY_PASS quay.io
|
||||
- docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
- docker push quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
- docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_TAG}
|
||||
- docker push quay.io/influxdb/chronograf:${CIRCLE_TAG}
|
||||
- mv ./build/* $CIRCLE_ARTIFACTS
|
||||
release:
|
||||
tag: /^[0-9]+(\.[0-9]+)*$/
|
||||
commands:
|
||||
- >
|
||||
./etc/scripts/docker/run.sh
|
||||
--clean
|
||||
--debug
|
||||
--release
|
||||
--package
|
||||
--platform all
|
||||
--arch all
|
||||
--upload-overwrite
|
||||
--upload
|
||||
--bucket dl.influxdata.com/chronograf/releases
|
||||
- sudo chown -R ubuntu:ubuntu /home/ubuntu
|
||||
- cp build/linux/static_amd64/chronograf .
|
||||
- cp build/linux/static_amd64/chronoctl .
|
||||
- docker build -t chronograf .
|
||||
- docker login -e $QUAY_EMAIL -u "$QUAY_USER" -p $QUAY_PASS quay.io
|
||||
- docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
- docker push quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
- docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_TAG}
|
||||
- docker push quay.io/influxdb/chronograf:${CIRCLE_TAG}
|
||||
- docker tag chronograf quay.io/influxdb/chronograf:latest
|
||||
- docker push quay.io/influxdb/chronograf:latest
|
||||
- mv ./build/* $CIRCLE_ARTIFACTS
|
|
@ -12,7 +12,7 @@ import {
|
|||
applyMasks,
|
||||
insertTempVar,
|
||||
unMask,
|
||||
} from 'src/dashboards/constants'
|
||||
} from 'src/tempVars/constants'
|
||||
|
||||
@ErrorHandling
|
||||
class QueryTextArea extends Component {
|
||||
|
|
|
@ -32,7 +32,7 @@ export const generateURLQueryParamsFromTempVars = (
|
|||
const selected = values.find(value => value.selected === true)
|
||||
const strippedTempVar = stripTempVar(tempVar)
|
||||
|
||||
urlQueryParams[strippedTempVar] = selected.value
|
||||
urlQueryParams[strippedTempVar] = _.get(selected, 'value', '')
|
||||
})
|
||||
|
||||
return urlQueryParams
|
||||
|
|
|
@ -3,12 +3,13 @@ import _ from 'lodash'
|
|||
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import ExpressionNode from 'src/flux/components/ExpressionNode'
|
||||
import VariableName from 'src/flux/components/VariableName'
|
||||
import VariableNode from 'src/flux/components/VariableNode'
|
||||
import FuncSelector from 'src/flux/components/FuncSelector'
|
||||
import BodyDelete from 'src/flux/components/BodyDelete'
|
||||
import {funcNames} from 'src/flux/constants'
|
||||
|
||||
import {Service} from 'src/types'
|
||||
import {FlatBody, Suggestion} from 'src/types/flux'
|
||||
import {Body, Suggestion} from 'src/types/flux'
|
||||
|
||||
interface Props {
|
||||
service: Service
|
||||
|
@ -16,21 +17,25 @@ interface Props {
|
|||
suggestions: Suggestion[]
|
||||
onAppendFrom: () => void
|
||||
onAppendJoin: () => void
|
||||
}
|
||||
|
||||
interface Body extends FlatBody {
|
||||
id: string
|
||||
onDeleteBody: (bodyID: string) => void
|
||||
}
|
||||
|
||||
class BodyBuilder extends PureComponent<Props> {
|
||||
public render() {
|
||||
const bodybuilder = this.props.body.map((b, i) => {
|
||||
const {body, onDeleteBody} = this.props
|
||||
|
||||
const bodybuilder = body.map((b, i) => {
|
||||
if (b.declarations.length) {
|
||||
return b.declarations.map(d => {
|
||||
if (d.funcs) {
|
||||
return (
|
||||
<div className="declaration" key={i}>
|
||||
<VariableName name={d.name} assignedToQuery={true} />
|
||||
<div className="func-node--wrapper">
|
||||
<VariableNode name={d.name} assignedToQuery={true} />
|
||||
<div className="func-node--menu">
|
||||
<BodyDelete bodyID={b.id} onDeleteBody={onDeleteBody} />
|
||||
</div>
|
||||
</div>
|
||||
<ExpressionNode
|
||||
bodyID={b.id}
|
||||
declarationID={d.id}
|
||||
|
@ -38,6 +43,7 @@ class BodyBuilder extends PureComponent<Props> {
|
|||
funcs={d.funcs}
|
||||
declarationsFromBody={this.declarationsFromBody}
|
||||
isLastBody={this.isLastBody(i)}
|
||||
onDeleteBody={onDeleteBody}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -45,7 +51,16 @@ class BodyBuilder extends PureComponent<Props> {
|
|||
|
||||
return (
|
||||
<div className="declaration" key={i}>
|
||||
<VariableName name={b.source} assignedToQuery={false} />
|
||||
<div className="func-node--wrapper">
|
||||
<VariableNode name={b.source} assignedToQuery={false} />
|
||||
<div className="func-node--menu">
|
||||
<BodyDelete
|
||||
bodyID={b.id}
|
||||
type="variable"
|
||||
onDeleteBody={onDeleteBody}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
@ -59,6 +74,7 @@ class BodyBuilder extends PureComponent<Props> {
|
|||
funcNames={this.funcNames}
|
||||
declarationsFromBody={this.declarationsFromBody}
|
||||
isLastBody={this.isLastBody(i)}
|
||||
onDeleteBody={onDeleteBody}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import ConfirmButton from 'src/shared/components/ConfirmButton'
|
||||
|
||||
type BodyType = 'variable' | 'query'
|
||||
|
||||
interface Props {
|
||||
bodyID: string
|
||||
type?: BodyType
|
||||
onDeleteBody: (bodyID: string) => void
|
||||
}
|
||||
|
||||
class BodyDelete extends PureComponent<Props> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
type: 'query',
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {type} = this.props
|
||||
|
||||
if (type === 'variable') {
|
||||
return (
|
||||
<button
|
||||
className="btn btn-sm btn-square btn-danger"
|
||||
title="Delete Variable"
|
||||
onClick={this.handleDelete}
|
||||
>
|
||||
<span className="icon remove" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmButton
|
||||
icon="trash"
|
||||
type="btn-danger"
|
||||
confirmText="Delete Query"
|
||||
square={true}
|
||||
confirmAction={this.handleDelete}
|
||||
position="right"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private handleDelete = (): void => {
|
||||
this.props.onDeleteBody(this.props.bodyID)
|
||||
}
|
||||
}
|
||||
|
||||
export default BodyDelete
|
|
@ -15,6 +15,7 @@ interface Props {
|
|||
declarationID?: string
|
||||
declarationsFromBody: string[]
|
||||
isLastBody: boolean
|
||||
onDeleteBody: (bodyID: string) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -42,6 +43,7 @@ class ExpressionNode extends PureComponent<Props, State> {
|
|||
funcNames,
|
||||
funcs,
|
||||
declarationsFromBody,
|
||||
onDeleteBody,
|
||||
} = this.props
|
||||
|
||||
const {nonYieldableIndexesToggled} = this.state
|
||||
|
@ -106,6 +108,7 @@ class ExpressionNode extends PureComponent<Props, State> {
|
|||
onGenerateScript={onGenerateScript}
|
||||
declarationsFromBody={declarationsFromBody}
|
||||
onToggleYieldWithLast={this.handleToggleYieldWithLast}
|
||||
onDeleteBody={onDeleteBody}
|
||||
/>
|
||||
)
|
||||
|
||||
|
@ -152,6 +155,7 @@ class ExpressionNode extends PureComponent<Props, State> {
|
|||
onGenerateScript={onGenerateScript}
|
||||
declarationsFromBody={declarationsFromBody}
|
||||
onToggleYieldWithLast={this.handleToggleYieldWithLast}
|
||||
onDeleteBody={onDeleteBody}
|
||||
/>
|
||||
<YieldFuncNode
|
||||
index={i}
|
||||
|
|
|
@ -5,9 +5,11 @@ import {tagKeys as fetchTagKeys} from 'src/shared/apis/flux/metaQueries'
|
|||
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
||||
import FilterTagList from 'src/flux/components/FilterTagList'
|
||||
import Walker from 'src/flux/ast/walker'
|
||||
import {makeCancelable} from 'src/utils/promises'
|
||||
|
||||
import {Service} from 'src/types'
|
||||
import {Links, OnChangeArg, Func, FilterNode} from 'src/types/flux'
|
||||
import {WrappedCancelablePromise} from 'src/types/promises'
|
||||
|
||||
interface Props {
|
||||
links: Links
|
||||
|
@ -28,6 +30,8 @@ interface State {
|
|||
}
|
||||
|
||||
class FilterArgs extends PureComponent<Props, State> {
|
||||
private fetchTagKeysResponse?: WrappedCancelablePromise<string>
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
|
@ -52,15 +56,24 @@ class FilterArgs extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
const {db, service} = this.props
|
||||
|
||||
try {
|
||||
this.convertStringToNodes()
|
||||
const response = await fetchTagKeys(service, db, [])
|
||||
const response = await this.getTagKeys()
|
||||
const tagKeys = parseValuesColumn(response)
|
||||
this.setState({tagKeys})
|
||||
|
||||
this.setState({
|
||||
tagKeys,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
if (!error.isCanceled) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
if (this.fetchTagKeysResponse) {
|
||||
this.fetchTagKeysResponse.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,6 +104,14 @@ class FilterArgs extends PureComponent<Props, State> {
|
|||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private getTagKeys(): Promise<string> {
|
||||
const {db, service} = this.props
|
||||
|
||||
this.fetchTagKeysResponse = makeCancelable(fetchTagKeys(service, db, []))
|
||||
|
||||
return this.fetchTagKeysResponse.promise
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({links}) => {
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, {PureComponent, MouseEvent} from 'react'
|
|||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
|
||||
import BodyDelete from 'src/flux/components/BodyDelete'
|
||||
import FuncArgs from 'src/flux/components/FuncArgs'
|
||||
import FuncArgsPreview from 'src/flux/components/FuncArgsPreview'
|
||||
import {
|
||||
|
@ -28,6 +29,7 @@ interface Props {
|
|||
declarationsFromBody: string[]
|
||||
isYielding: boolean
|
||||
isYieldable: boolean
|
||||
onDeleteBody: (bodyID: string) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -53,14 +55,16 @@ export default class FuncNode extends PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={this.nodeClassName}
|
||||
onClick={this.handleToggleEdit}
|
||||
title="Edit function arguments"
|
||||
>
|
||||
<div className="func-node--connector" />
|
||||
<div className="func-node--name">{func.name}</div>
|
||||
<FuncArgsPreview func={func} />
|
||||
<div className="func-node--wrapper">
|
||||
<div
|
||||
className={this.nodeClassName}
|
||||
onClick={this.handleToggleEdit}
|
||||
title="Edit function arguments"
|
||||
>
|
||||
<div className="func-node--connector" />
|
||||
<div className="func-node--name">{func.name}</div>
|
||||
<FuncArgsPreview func={func} />
|
||||
</div>
|
||||
{this.funcMenu}
|
||||
</div>
|
||||
{this.funcArgs}
|
||||
|
@ -103,13 +107,7 @@ export default class FuncNode extends PureComponent<Props, State> {
|
|||
return (
|
||||
<div className="func-node--menu">
|
||||
{this.yieldToggleButton}
|
||||
<button
|
||||
className="btn btn-sm btn-square btn-danger"
|
||||
onClick={this.handleDelete}
|
||||
title="Delete this Function"
|
||||
>
|
||||
<span className="icon trash" />
|
||||
</button>
|
||||
{this.deleteButton}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -140,6 +138,24 @@ export default class FuncNode extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private get deleteButton(): JSX.Element {
|
||||
const {func, bodyID, onDeleteBody} = this.props
|
||||
|
||||
if (func.name === 'from') {
|
||||
return <BodyDelete onDeleteBody={onDeleteBody} bodyID={bodyID} />
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className="btn btn-sm btn-square btn-danger"
|
||||
onClick={this.handleDelete}
|
||||
title="Delete this Function"
|
||||
>
|
||||
<span className="icon remove" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
private get nodeClassName(): string {
|
||||
const {isYielding} = this.props
|
||||
const {editing} = this.state
|
||||
|
@ -147,14 +163,14 @@ export default class FuncNode extends PureComponent<Props, State> {
|
|||
return classnames('func-node', {active: isYielding || editing})
|
||||
}
|
||||
|
||||
private handleDelete = (e: MouseEvent<HTMLElement>): void => {
|
||||
e.stopPropagation()
|
||||
private handleDelete = (): void => {
|
||||
const {func, bodyID, declarationID} = this.props
|
||||
|
||||
this.props.onDelete({funcID: func.id, bodyID, declarationID})
|
||||
}
|
||||
|
||||
private handleToggleEdit = (): void => {
|
||||
private handleToggleEdit = (e: MouseEvent<HTMLElement>): void => {
|
||||
e.stopPropagation()
|
||||
this.setState({editing: !this.state.editing})
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
Suggestion,
|
||||
OnChangeScript,
|
||||
OnSubmitScript,
|
||||
OnDeleteBody,
|
||||
FlatBody,
|
||||
ScriptStatus,
|
||||
} from 'src/types/flux'
|
||||
|
@ -22,6 +23,7 @@ interface Props {
|
|||
status: ScriptStatus
|
||||
suggestions: Suggestion[]
|
||||
onChangeScript: OnChangeScript
|
||||
onDeleteBody: OnDeleteBody
|
||||
onSubmitScript: OnSubmitScript
|
||||
onAppendFrom: () => void
|
||||
onAppendJoin: () => void
|
||||
|
@ -63,7 +65,14 @@ class TimeMachine extends PureComponent<Props> {
|
|||
}
|
||||
|
||||
private get builder() {
|
||||
const {body, service, suggestions, onAppendFrom, onAppendJoin} = this.props
|
||||
const {
|
||||
body,
|
||||
service,
|
||||
suggestions,
|
||||
onAppendFrom,
|
||||
onDeleteBody,
|
||||
onAppendJoin,
|
||||
} = this.props
|
||||
|
||||
return {
|
||||
name: 'Build',
|
||||
|
@ -75,6 +84,7 @@ class TimeMachine extends PureComponent<Props> {
|
|||
body={body}
|
||||
service={service}
|
||||
suggestions={suggestions}
|
||||
onDeleteBody={onDeleteBody}
|
||||
onAppendFrom={onAppendFrom}
|
||||
onAppendJoin={onAppendJoin}
|
||||
/>
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
export const emptyAST = {
|
||||
type: 'Program',
|
||||
location: {
|
||||
start: {
|
||||
line: 1,
|
||||
column: 1,
|
||||
},
|
||||
end: {
|
||||
line: 1,
|
||||
column: 1,
|
||||
},
|
||||
source: '',
|
||||
},
|
||||
body: [],
|
||||
}
|
||||
|
||||
export const ast = {
|
||||
type: 'File',
|
||||
start: 0,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {ast} from 'src/flux/constants/ast'
|
||||
import {ast, emptyAST} from 'src/flux/constants/ast'
|
||||
import * as editor from 'src/flux/constants/editor'
|
||||
import * as argTypes from 'src/flux/constants/argumentTypes'
|
||||
import * as funcNames from 'src/flux/constants/funcNames'
|
||||
|
@ -10,6 +10,7 @@ const MAX_RESPONSE_BYTES = 1e7 // 10 MB
|
|||
|
||||
export {
|
||||
ast,
|
||||
emptyAST,
|
||||
funcNames,
|
||||
argTypes,
|
||||
editor,
|
||||
|
|
|
@ -15,7 +15,7 @@ import {UpdateScript} from 'src/flux/actions'
|
|||
|
||||
import {bodyNodes} from 'src/flux/helpers'
|
||||
import {getSuggestions, getAST, getTimeSeries} from 'src/flux/apis'
|
||||
import {builder, argTypes} from 'src/flux/constants'
|
||||
import {builder, argTypes, emptyAST} from 'src/flux/constants'
|
||||
|
||||
import {Source, Service, Notification, FluxTable} from 'src/types'
|
||||
import {
|
||||
|
@ -114,6 +114,7 @@ export class FluxPage extends PureComponent<Props, State> {
|
|||
onAppendJoin={this.handleAppendJoin}
|
||||
onChangeScript={this.handleChangeScript}
|
||||
onSubmitScript={this.handleSubmitScript}
|
||||
onDeleteBody={this.handleDeleteBody}
|
||||
/>
|
||||
</div>
|
||||
</KeyboardShortcuts>
|
||||
|
@ -331,6 +332,13 @@ export class FluxPage extends PureComponent<Props, State> {
|
|||
this.getASTResponse(script)
|
||||
}
|
||||
|
||||
private handleDeleteBody = (bodyID: string): void => {
|
||||
const newBody = this.state.body.filter(b => b.id !== bodyID)
|
||||
const script = this.getBodyToScript(newBody)
|
||||
|
||||
this.getASTResponse(script)
|
||||
}
|
||||
|
||||
private handleScriptUpToYield = (
|
||||
bodyID: string,
|
||||
declarationID: string,
|
||||
|
@ -601,7 +609,8 @@ export class FluxPage extends PureComponent<Props, State> {
|
|||
const {links} = this.props
|
||||
|
||||
if (!script) {
|
||||
return
|
||||
this.props.updateScript(script)
|
||||
return this.setState({ast: emptyAST, body: []})
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -45,6 +45,10 @@ class AnnotationInput extends Component<Props, State> {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
public handleClickOutside = () => {
|
||||
this.props.onConfirmUpdate()
|
||||
this.setState({isEditing: false})
|
||||
}
|
||||
|
||||
private handleInputClick = () => {
|
||||
this.setState({isEditing: true})
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
type Position = 'top' | 'bottom' | 'left' | 'right'
|
||||
|
||||
interface Props {
|
||||
text?: string
|
||||
confirmText?: string
|
||||
|
@ -12,6 +15,7 @@ interface Props {
|
|||
icon?: string
|
||||
disabled?: boolean
|
||||
customClass?: string
|
||||
position?: Position
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -47,10 +51,11 @@ class ConfirmButton extends PureComponent<Props, State> {
|
|||
className={this.className}
|
||||
onClick={this.handleButtonClick}
|
||||
ref={r => (this.buttonDiv = r)}
|
||||
title={confirmText}
|
||||
>
|
||||
{icon && <span className={`icon ${icon}`} />}
|
||||
{text && text}
|
||||
<div className={`confirm-button--tooltip ${this.calculatedPosition}`}>
|
||||
<div className={this.tooltipClassName}>
|
||||
<div
|
||||
className="confirm-button--confirmation"
|
||||
onClick={this.handleConfirmClick}
|
||||
|
@ -64,18 +69,6 @@ class ConfirmButton extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private get className() {
|
||||
const {type, size, square, disabled, customClass} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
const customClassString = customClass ? ` ${customClass}` : ''
|
||||
const squareString = square ? ' btn-square' : ''
|
||||
const expandedString = expanded ? ' active' : ''
|
||||
const disabledString = disabled ? ' disabled' : ''
|
||||
|
||||
return `confirm-button btn ${type} ${size}${customClassString}${squareString}${expandedString}${disabledString}`
|
||||
}
|
||||
|
||||
private handleButtonClick = () => {
|
||||
if (this.props.disabled) {
|
||||
return
|
||||
|
@ -92,9 +85,27 @@ class ConfirmButton extends PureComponent<Props, State> {
|
|||
this.setState({expanded: false})
|
||||
}
|
||||
|
||||
private get calculatedPosition() {
|
||||
private get className(): string {
|
||||
const {type, size, square, disabled, customClass} = this.props
|
||||
const {expanded} = this.state
|
||||
|
||||
return classnames(`confirm-button btn ${type} ${size}`, {
|
||||
[customClass]: customClass,
|
||||
'btn-square': square,
|
||||
active: expanded,
|
||||
disabled,
|
||||
})
|
||||
}
|
||||
|
||||
private get tooltipClassName(): string {
|
||||
const {position} = this.props
|
||||
|
||||
if (position) {
|
||||
return `confirm-button--tooltip ${position}`
|
||||
}
|
||||
|
||||
if (!this.buttonDiv || !this.tooltipDiv) {
|
||||
return ''
|
||||
return 'confirm-button--tooltip bottom'
|
||||
}
|
||||
|
||||
const windowWidth = window.innerWidth
|
||||
|
@ -104,10 +115,10 @@ class ConfirmButton extends PureComponent<Props, State> {
|
|||
const rightGap = windowWidth - buttonRect.right
|
||||
|
||||
if (tooltipRect.width / 2 > rightGap) {
|
||||
return 'left'
|
||||
return 'confirm-button--tooltip left'
|
||||
}
|
||||
|
||||
return 'bottom'
|
||||
return 'confirm-button--tooltip bottom'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,13 @@ class NewAnnotation extends Component<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
public handleClickOutside = () => {
|
||||
const {onDismissAddingAnnotation, isTempHovering} = this.props
|
||||
if (!isTempHovering) {
|
||||
onDismissAddingAnnotation()
|
||||
}
|
||||
}
|
||||
|
||||
private clampWithinGraphTimerange = (timestamp: number): number => {
|
||||
const [xRangeStart] = this.props.dygraph.xAxisRange()
|
||||
return Math.max(xRangeStart, timestamp)
|
||||
|
|
|
@ -11,18 +11,26 @@
|
|||
position: absolute;
|
||||
z-index: 1;
|
||||
|
||||
|
||||
&.top {
|
||||
bottom: calc(100% + 4px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
&.bottom {
|
||||
top: calc(100% + 4px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&.left {
|
||||
top: 50%;
|
||||
right: calc(100% + 4px);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
&.right {
|
||||
top: 50%;
|
||||
left: calc(100% + 4px);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.confirm-button--confirmation {
|
||||
|
@ -43,11 +51,7 @@
|
|||
&:after {
|
||||
content: '';
|
||||
border: 8px solid transparent;
|
||||
border-bottom-color: $c-curacao;
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
transition: border-color 0.25s ease;
|
||||
z-index: 100;
|
||||
}
|
||||
|
@ -56,28 +60,35 @@
|
|||
background-color: $c-dreamsicle;
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover:after {
|
||||
border-bottom-color: $c-dreamsicle;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-button--tooltip.bottom .confirm-button--confirmation:after {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
border-bottom-color: $c-curacao;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.confirm-button--tooltip.bottom .confirm-button--confirmation:hover:after {
|
||||
border-bottom-color: $c-dreamsicle;
|
||||
}
|
||||
.confirm-button--tooltip.left .confirm-button--confirmation:after {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
border-left-color: $c-curacao;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.confirm-button--tooltip.left .confirm-button--confirmation:hover:after {
|
||||
border-left-color: $c-dreamsicle;
|
||||
.top &:after {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-top-color: $c-curacao;
|
||||
}
|
||||
.top &:hover:after {border-top-color: $c-dreamsicle;}
|
||||
.bottom &:after {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-bottom-color: $c-curacao;
|
||||
}
|
||||
.bottom &:hover:after {border-bottom-color: $c-dreamsicle;}
|
||||
.left &:after {
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
transform: translateY(-50%);
|
||||
border-left-color: $c-curacao;
|
||||
}
|
||||
.left &:hover:after {border-left-color: $c-dreamsicle;}
|
||||
.right &:after {
|
||||
top: 50%;
|
||||
right: 100%;
|
||||
transform: translateY(-50%);
|
||||
border-right-color: $c-curacao;
|
||||
}
|
||||
.right &:hover:after {border-right-color: $c-dreamsicle;}
|
||||
}
|
||||
|
||||
.confirm-button.active {
|
||||
|
|
|
@ -144,6 +144,8 @@ $flux-invalid-hover: $c-dreamsicle;
|
|||
|
||||
&:hover,
|
||||
&.active {
|
||||
cursor: pointer !important;
|
||||
|
||||
.func-node--preview {
|
||||
color: $g20-white;
|
||||
}
|
||||
|
@ -196,7 +198,7 @@ $flux-invalid-hover: $c-dreamsicle;
|
|||
}
|
||||
|
||||
// When a query exists unassigned to a variable
|
||||
.func-node:first-child {
|
||||
.func-node--wrapper:first-child .func-node {
|
||||
margin-left: 0;
|
||||
padding-left: $flux-node-gap;
|
||||
.func-node--connector {
|
||||
|
@ -248,24 +250,26 @@ $flux-invalid-hover: $c-dreamsicle;
|
|||
}
|
||||
}
|
||||
|
||||
.func-node--wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.func-node--menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
transform: translate(100%, -50%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s ease;
|
||||
|
||||
> button.btn {
|
||||
> button.btn,
|
||||
> .confirm-button {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.func-node:hover .func-node--menu,
|
||||
.func-node.editing .func-node--menu,
|
||||
.func-node.active .func-node--menu {
|
||||
.func-node--wrapper:hover .func-node--menu,
|
||||
.func-node.editing + .func-node--menu,
|
||||
.func-node.active + .func-node--menu {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
|
|||
values: [],
|
||||
type: TemplateType.CSV,
|
||||
label: '',
|
||||
query: {},
|
||||
}
|
||||
},
|
||||
[TemplateType.TagKeys]: () => {
|
||||
|
|
|
@ -21,6 +21,7 @@ export type ScriptUpToYield = (
|
|||
yieldNodeIndex: number,
|
||||
isYieldable: boolean
|
||||
) => string
|
||||
export type OnDeleteBody = (bodyID: string) => void
|
||||
|
||||
export interface ScriptStatus {
|
||||
type: string
|
||||
|
@ -105,6 +106,9 @@ export interface FlatBody {
|
|||
funcs?: Func[]
|
||||
declarations?: FlatDeclaration[]
|
||||
}
|
||||
export interface Body extends FlatBody {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface Func {
|
||||
type: string
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export interface WrappedCancelablePromise<T> {
|
||||
promise: Promise<T>
|
||||
cancel: () => void
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import {WrappedCancelablePromise} from 'src/types/promises'
|
||||
|
||||
export const makeCancelable = <T>(
|
||||
promise: Promise<T>
|
||||
): WrappedCancelablePromise<T> => {
|
||||
let isCanceled = false
|
||||
|
||||
const wrappedPromise = new Promise<T>(async (resolve, reject) => {
|
||||
try {
|
||||
const value = await promise
|
||||
|
||||
if (isCanceled) {
|
||||
reject({isCanceled})
|
||||
} else {
|
||||
resolve(value)
|
||||
}
|
||||
} catch (error) {
|
||||
if (isCanceled) {
|
||||
reject({isCanceled})
|
||||
} else {
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
promise: wrappedPromise,
|
||||
cancel() {
|
||||
isCanceled = true
|
||||
},
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ const setup = () => {
|
|||
onValidate: () => {},
|
||||
onAppendFrom: () => {},
|
||||
onAppendJoin: () => {},
|
||||
onDeleteBody: () => {},
|
||||
status: {type: '', text: ''},
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue