From 29fe9d1119d3cbc772a96fa3b46a381b02f4e42d Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 24 Apr 2018 10:54:17 -0700 Subject: [PATCH] Fix delete to work with variable declarations --- ui/src/ifql/components/BodyBuilder.tsx | 3 +- ui/src/ifql/components/ExpressionNode.tsx | 4 +- ui/src/ifql/components/FuncNode.tsx | 13 ++++-- ui/src/ifql/containers/IFQLPage.tsx | 53 +++++++++++++++++------ ui/src/types/ifql.ts | 8 +++- 5 files changed, 61 insertions(+), 20 deletions(-) diff --git a/ui/src/ifql/components/BodyBuilder.tsx b/ui/src/ifql/components/BodyBuilder.tsx index 0acd7e308c..a6d8d6f647 100644 --- a/ui/src/ifql/components/BodyBuilder.tsx +++ b/ui/src/ifql/components/BodyBuilder.tsx @@ -22,8 +22,9 @@ class BodyBuilder extends PureComponent { if (d.funcs) { return ( diff --git a/ui/src/ifql/components/ExpressionNode.tsx b/ui/src/ifql/components/ExpressionNode.tsx index a76f0c7e38..2b57b2cfd4 100644 --- a/ui/src/ifql/components/ExpressionNode.tsx +++ b/ui/src/ifql/components/ExpressionNode.tsx @@ -10,12 +10,13 @@ interface Props { funcNames: any[] bodyID: string funcs: Func[] + declarationID?: string } // an Expression is a group of one or more functions class ExpressionNode extends PureComponent { public render() { - const {bodyID, funcNames, funcs} = this.props + const {declarationID, bodyID, funcNames, funcs} = this.props return ( {({onDeleteFuncNode, onAddNode, onChangeArg, onGenerateScript}) => { @@ -35,6 +36,7 @@ class ExpressionNode extends PureComponent { bodyID={bodyID} onChangeArg={onChangeArg} onDelete={onDeleteFuncNode} + declarationID={declarationID} onGenerateScript={onGenerateScript} /> ))} diff --git a/ui/src/ifql/components/FuncNode.tsx b/ui/src/ifql/components/FuncNode.tsx index 47f049f6e6..a8c52f78a5 100644 --- a/ui/src/ifql/components/FuncNode.tsx +++ b/ui/src/ifql/components/FuncNode.tsx @@ -1,12 +1,13 @@ import React, {PureComponent, MouseEvent} from 'react' import FuncArgs from 'src/ifql/components/FuncArgs' -import {OnChangeArg, Func} from 'src/types/ifql' +import {OnDeleteFuncNode, OnChangeArg, Func} from 'src/types/ifql' import {ErrorHandling} from 'src/shared/decorators/errors' interface Props { func: Func bodyID: string - onDelete: (funcID: string, bodyID: string) => void + declarationID?: string + onDelete: OnDeleteFuncNode onChangeArg: OnChangeArg onGenerateScript: () => void } @@ -17,6 +18,10 @@ interface State { @ErrorHandling export default class FuncNode extends PureComponent { + public static defaultProps: Partial = { + declarationID: '', + } + constructor(props) { super(props) this.state = { @@ -49,7 +54,9 @@ export default class FuncNode extends PureComponent { } private handleDelete = (): void => { - this.props.onDelete(this.props.func.id, this.props.bodyID) + const {func, bodyID, declarationID} = this.props + + this.props.onDelete({funcID: func.id, bodyID, declarationID}) } private handleClick = (e: MouseEvent): void => { diff --git a/ui/src/ifql/containers/IFQLPage.tsx b/ui/src/ifql/containers/IFQLPage.tsx index 662e48d7d7..2b970f6100 100644 --- a/ui/src/ifql/containers/IFQLPage.tsx +++ b/ui/src/ifql/containers/IFQLPage.tsx @@ -5,7 +5,7 @@ import {connect} from 'react-redux' import TimeMachine from 'src/ifql/components/TimeMachine' import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts' import {Suggestion, FlatBody} from 'src/types/ifql' -import {InputArg, Handlers} from 'src/types/ifql' +import {InputArg, Handlers, DeleteFuncNodeArgs} from 'src/types/ifql' import {bodyNodes} from 'src/ifql/helpers' import {getSuggestions, getAST} from 'src/ifql/apis' @@ -196,35 +196,60 @@ export class IFQLPage extends PureComponent { this.getASTResponse(script) } - private handleDeleteFuncNode = (funcID: string, bodyID: string): void => { - // TODO: export this and test functionality + private handleDeleteFuncNode = (ids: DeleteFuncNodeArgs): void => { + const {funcID, declarationID = '', bodyID} = ids + const script = this.state.body .map((body, bodyIndex) => { if (body.id !== bodyID) { return body.source } - const funcs = body.funcs.filter(f => f.id !== funcID) - const source = funcs.reduce((acc, f, i) => { - if (i === 0) { - return `${f.source}` + const isLast = bodyIndex === this.state.body.length - 1 + + if (declarationID) { + const declaration = body.declarations.find( + d => d.id === declarationID + ) + + if (!declaration) { + return } - return `${acc}\n\t${f.source}` - }, '') - - const isLast = bodyIndex === this.state.body.length - 1 - if (isLast) { - return `${source}` + const functions = declaration.funcs.filter(f => f.id !== funcID) + const s = this.funcsToSource(functions) + return `${declaration.name} = ${this.parseLastSource(s, isLast)}` } - return `${source}\n\n` + const funcs = body.funcs.filter(f => f.id !== funcID) + const source = this.funcsToSource(funcs) + return this.parseLastSource(source, isLast) }) .join('') this.getASTResponse(script) } + // formats the last line of a body string to include two new lines + private parseLastSource = (source: string, isLast: boolean): string => { + if (isLast) { + return `${source}` + } + + return `${source}\n\n` + } + + // funcsToSource takes a list of funtion nodes and returns an ifql script + private funcsToSource = (funcs): string => { + return funcs.reduce((acc, f, i) => { + if (i === 0) { + return `${f.source}` + } + + return `${acc}\n\t${f.source}` + }, '') + } + private getASTResponse = async (script: string) => { const {links} = this.props diff --git a/ui/src/types/ifql.ts b/ui/src/types/ifql.ts index e8b725bffd..289609efeb 100644 --- a/ui/src/types/ifql.ts +++ b/ui/src/types/ifql.ts @@ -1,5 +1,5 @@ // function definitions -export type OnDeleteFuncNode = (funcID: string, bodyID: string) => void +export type OnDeleteFuncNode = (ids: DeleteFuncNodeArgs) => void export type OnChangeArg = (inputArg: InputArg) => void export type OnAddNode = (bodyID: string, funcName: string) => void export type OnGenerateScript = (script: string) => void @@ -15,6 +15,12 @@ export interface Handlers { onGenerateScript: OnGenerateScript } +export interface DeleteFuncNodeArgs { + funcID: string + bodyID: string + declarationID?: string +} + export interface InputArg { funcID: string bodyID: string