Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(explorer): add save as variable overlay #12523

Merged
merged 1 commit into from
Mar 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
1. [12524](https://github.com/influxdata/influxdb/pull/12524): Add ability to import a dashboard from org view
1. [12531](https://github.com/influxdata/influxdb/pull/12531): Add ability to export a dashboard and a task

1. [12523](https://github.com/influxdata/influxdb/pull/12523): Add ability to save a query as a variable from the Data Explorer.

### Bug Fixes

### UI Improvements
Expand Down
12 changes: 12 additions & 0 deletions ui/src/dataExplorer/components/SaveAsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, {PureComponent} from 'react'
// Components
import SaveAsCellForm from 'src/dataExplorer/components/SaveAsCellForm'
import SaveAsTaskForm from 'src/dataExplorer/components/SaveAsTaskForm'
import SaveAsVariable from 'src/dataExplorer/components/SaveAsVariable'
import {IconFont, Button, ComponentColor} from '@influxdata/clockface'
import {Radio, Overlay} from 'src/clockface'

Expand All @@ -13,6 +14,7 @@ import 'src/dataExplorer/components/SaveAsButton.scss'
enum SaveAsOption {
Dashboard = 'dashboard',
Task = 'task',
Variable = 'variable',
}

interface Props {}
Expand Down Expand Up @@ -65,6 +67,14 @@ class SaveAsButton extends PureComponent<Props, State> {
>
Task
</Radio.Button>
<Radio.Button
active={saveAsOption === SaveAsOption.Variable}
value={SaveAsOption.Variable}
onClick={this.handleSetSaveAsOption}
data-testid="variable-radio-button"
>
Variable
</Radio.Button>
</Radio>
</div>
{this.saveAsForm}
Expand All @@ -82,6 +92,8 @@ class SaveAsButton extends PureComponent<Props, State> {
return <SaveAsCellForm dismiss={this.handleHideOverlay} />
} else if (saveAsOption === SaveAsOption.Task) {
return <SaveAsTaskForm dismiss={this.handleHideOverlay} />
} else if (saveAsOption === SaveAsOption.Variable) {
return <SaveAsVariable onHideOverlay={this.handleHideOverlay} />
}
}

Expand Down
64 changes: 64 additions & 0 deletions ui/src/dataExplorer/components/SaveAsVariable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Libraries
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'

// Components
import VariableForm from 'src/organizations/components/VariableForm'

// Utils
import {getActiveOrg} from 'src/organizations/selectors'
import {createVariable} from 'src/variables/actions'

// Types
import {AppState} from 'src/types/v2'
import {Variable} from '@influxdata/influx'
import {getActiveQuery} from 'src/timeMachine/selectors'

interface OwnProps {
onHideOverlay: () => void
}

interface DispatchProps {
onCreateVariable: (variable: Variable) => void
}

interface StateProps {
initialScript?: string
orgID: string
}

type Props = StateProps & DispatchProps & OwnProps

class SaveAsVariable extends PureComponent<Props> {
render() {
const {orgID, onHideOverlay, onCreateVariable, initialScript} = this.props

return (
<VariableForm
orgID={orgID}
onHideOverlay={onHideOverlay}
onCreateVariable={onCreateVariable}
initialScript={initialScript}
/>
)
}
}

const mstp = (state: AppState): StateProps => {
const activeQuery = getActiveQuery(state)
const activeOrgID = getActiveOrg(state).id

return {
orgID: activeOrgID,
initialScript: activeQuery.text,
}
}

const mdtp = {
onCreateVariable: createVariable,
}

export default connect<StateProps, DispatchProps, OwnProps>(
mstp,
mdtp
)(SaveAsVariable)
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ exports[`SaveAsButton rendering renders 1`] = `
>
Task
</RadioButton>
<RadioButton
active={false}
data-testid="variable-radio-button"
disabled={false}
disabledTitleText="This option is disabled"
onClick={[Function]}
testID="radio-button"
value="variable"
>
Variable
</RadioButton>
</Radio>
</div>
<Connect(SaveAsCellForm)
Expand Down
118 changes: 16 additions & 102 deletions ui/src/organizations/components/CreateVariableOverlay.tsx
Original file line number Diff line number Diff line change
@@ -1,125 +1,39 @@
// Libraries
import React, {PureComponent, ChangeEvent} from 'react'
import React, {PureComponent} from 'react'

// Styles
import 'src/organizations/components/CreateVariableOverlay.scss'

// Components
import {Form, Input, Overlay} from 'src/clockface'
import {Button} from '@influxdata/clockface'
import FluxEditor from 'src/shared/components/FluxEditor'
import {Overlay} from 'src/clockface'
import VariableForm from 'src/organizations/components/VariableForm'

// Types
import {Variable} from '@influxdata/influx'
import {
ComponentColor,
ComponentStatus,
ButtonType,
} from '@influxdata/clockface'

interface Props {
onCreateVariable: (variable: Variable) => void
onCloseModal: () => void
onHideOverlay: () => void
orgID: string
initialScript?: string
}

interface State {
name: string
script: string
nameInputStatus: ComponentStatus
errorMessage: string
}

export default class CreateOrgOverlay extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
name: '',
script: '',
nameInputStatus: ComponentStatus.Default,
errorMessage: '',
}
}

export default class CreateVariableOverlay extends PureComponent<Props> {
public render() {
const {onCloseModal} = this.props
const {nameInputStatus, name, script} = this.state
const {onHideOverlay, onCreateVariable, orgID, initialScript} = this.props

return (
<Overlay.Container maxWidth={1000}>
<Overlay.Heading
title="Create Variable"
onDismiss={this.props.onCloseModal}
/>

<Form onSubmit={this.handleSubmit}>
<Overlay.Body>
<div className="overlay-flux-editor--spacing">
<Form.Element label="Name">
<Input
placeholder="Give your variable a name"
name="name"
autoFocus={true}
value={name}
onChange={this.handleChangeInput}
status={nameInputStatus}
/>
</Form.Element>
</div>

<Form.Element label="Value">
<div className="overlay-flux-editor">
<FluxEditor
script={script}
onChangeScript={this.handleChangeScript}
visibility="visible"
suggestions={[]}
/>
</div>
</Form.Element>

<Overlay.Footer>
<Button
text="Cancel"
color={ComponentColor.Danger}
onClick={onCloseModal}
/>
<Button
text="Create"
type={ButtonType.Submit}
color={ComponentColor.Primary}
/>
</Overlay.Footer>
</Overlay.Body>
</Form>
<Overlay.Heading title="Create Variable" onDismiss={onHideOverlay} />
<Overlay.Body>
<VariableForm
onCreateVariable={onCreateVariable}
onHideOverlay={onHideOverlay}
orgID={orgID}
initialScript={initialScript}
/>
</Overlay.Body>
</Overlay.Container>
)
}

private handleSubmit = (): void => {
const {onCreateVariable, orgID, onCloseModal} = this.props

onCreateVariable({
name: this.state.name,
orgID,
arguments: {
type: 'query',
values: {query: this.state.script, language: 'flux'},
},
})

onCloseModal()
}

private handleChangeInput = (e: ChangeEvent<HTMLInputElement>) => {
const {value, name} = e.target

const newState = {...this.state}
newState[name] = value
this.setState(newState)
}

private handleChangeScript = (script: string): void => {
this.setState({script})
}
}
125 changes: 125 additions & 0 deletions ui/src/organizations/components/VariableForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Libraries
import React, {PureComponent, ChangeEvent} from 'react'

// Styles
import 'src/organizations/components/CreateVariableOverlay.scss'

// Components
import {Form, Input, Grid} from 'src/clockface'
import {Button} from '@influxdata/clockface'
import FluxEditor from 'src/shared/components/FluxEditor'

// Types
import {Variable} from '@influxdata/influx'
import {
ComponentColor,
ComponentStatus,
ButtonType,
} from '@influxdata/clockface'

interface Props {
onCreateVariable: (variable: Variable) => void
onHideOverlay?: () => void
orgID: string
initialScript?: string
}

interface State {
name: string
script: string
nameInputStatus: ComponentStatus
errorMessage: string
}

export default class CreateOrgOverlay extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
name: '',
script: this.props.initialScript || '',
nameInputStatus: ComponentStatus.Default,
errorMessage: '',
}
}

public render() {
const {onHideOverlay} = this.props
const {nameInputStatus, name, script} = this.state

return (
<Form onSubmit={this.handleSubmit}>
<Grid>
<Grid.Row>
<Grid.Column>
<div className="overlay-flux-editor--spacing">
<Form.Element label="Name">
<Input
placeholder="Give your variable a name"
name="name"
autoFocus={true}
value={name}
onChange={this.handleChangeInput}
status={nameInputStatus}
/>
</Form.Element>
</div>
</Grid.Column>
<Grid.Column>
<Form.Element label="Value">
<div className="overlay-flux-editor">
<FluxEditor
script={script}
onChangeScript={this.handleChangeScript}
visibility="visible"
suggestions={[]}
/>
</div>
</Form.Element>
</Grid.Column>
<Grid.Column>
<Form.Footer>
<Button
text="Cancel"
color={ComponentColor.Danger}
onClick={onHideOverlay}
/>
<Button
text="Create"
type={ButtonType.Submit}
color={ComponentColor.Primary}
/>
</Form.Footer>
</Grid.Column>
</Grid.Row>
</Grid>
</Form>
)
}

private handleSubmit = (): void => {
const {onCreateVariable, orgID, onHideOverlay} = this.props

onCreateVariable({
name: this.state.name,
orgID,
arguments: {
type: 'query',
values: {query: this.state.script, language: 'flux'},
},
})

onHideOverlay()
}

private handleChangeInput = (e: ChangeEvent<HTMLInputElement>) => {
const {value, name} = e.target

const newState = {...this.state}
newState[name] = value
this.setState(newState)
}

private handleChangeScript = (script: string): void => {
this.setState({script})
}
}
Loading